PageSourceSearch

https://www.autobutler.dk/assets/controllers/shared/copy_to_clipboard_controller-d8db8e6b.js

js autobutler.dk collected 2026-10-05 04:43:09 UTC 2,749 bytes, 79 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2
3// Tiny copy-to-clipboard helper for sysadmin hint pills and inline
4// snippets. Reads the text to copy from `data-copy-to-clipboard-text-value`
5// (preferred) or, failing that, the element's own textContent. On success,
6// briefly swaps in the "copied" slot content (marked by
7// `data-copy-to-clipboard-target="copied"`) so the admin sees feedback.
8//
9// Usage:
10//   <button
11//     data-controller="copy-to-clipboard"
12//     data-copy-to-clipboard-text-value="bin/rake entity_hubs:seed"
13//     data-action="click->copy-to-clipboard#copy">
14//     <span data-copy-to-clipboard-target="default">Copy</span>
15//     <span data-copy-to-clipboard-target="copied" hidden>Copied!</span>
16//   </button>
17//
18// Prefers `navigator.clipboard.writeText` (secure contexts), falls back to
19// the textarea + execCommand trick so the action still works over plain HTTP
20// in dev / on LAN boxes.
21export default class extends Controller {
22  static targets = ["default", "copied"]
23  static values  = {
24    text: String,
25    // How long (ms) to keep the "Copied!" state visible before reverting.
26    timeout: { type: Number, default: 1500 }
27  }
28
29  // Private fields must be declared in the class body before first use —
30  // otherwise Chrome parses this.#revertHandle below as a syntax error
31  // ("Private field '#revertHandle' must be declared in an enclosing class").
32  #revertHandle = null
33
34  copy(event) {
35    event?.preventDefault?.()
36    event?.stopPropagation?.()
37
38    const text = this.textValue?.length ? this.textValue : this.element.textContent.trim()
39    if (!text) return
40
41    const done = () => this.#flashCopied()
42
43    if (navigator.clipboard?.writeText) {
44      navigator.clipboard.writeText(text).then(done).catch(() => this.#fallbackCopy(text, done))
45    } else {
46      this.#fallbackCopy(text, done)
47    }
48  }
49
50  // Legacy fallback — hidden textarea + document.execCommand('copy'). Used
51  // when navigator.clipboard isn't available (non-HTTPS, older browsers).
52  #fallbackCopy(text, done) {
53    const ta = document.createElement("textarea")
54    ta.value = text
55    ta.setAttribute("readonly", "")
56    ta.style.position = "absolute"
57    ta.style.left = "-9999px"
58    document.body.appendChild(ta)
59    ta.select()
60    try {
61      document.execCommand("copy")
62      done()
63    } finally {
64      document.body.removeChild(ta)
65    }
66  }
67
68  #flashCopied() {
69    if (this.hasCopiedTarget && this.hasDefaultTarget) {
70      this.defaultTarget.hidden = true
71      this.copiedTarget.hidden  = false
72      clearTimeout(this.#revertHandle)
73      this.#revertHandle = setTimeout(() => {
74        this.defaultTarget.hidden = false
75        this.copiedTarget.hidden  = true
76      }, this.timeoutValue)
77    }
78  }
79}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.