daemon-sec-lotl

DÆMONBins: GTFOBins × LOLBAS × WADComs × LOOBins in one filterable catalog
git clone https://git.daemon-sec.xyz/daemon-sec-lotl.git
Log | Files | Refs | Submodules | README | LICENSE

copy.ts (1842B)


      1 // Copy-to-clipboard for every [data-copy] button on the site — the per-tool
      2 // pages and the catalog island share this one path. Delegated on `document`
      3 // so rows rendered later (the catalog fetch, "Show more") need no re-wiring.
      4 // The button copies the text of the nearest `[data-cmdbar] code`.
      5 
      6 export function copyText(text: string, btn: HTMLElement): void {
      7   const restore = btn.textContent;
      8   const flash = (label: string) => {
      9     btn.textContent = label;
     10     btn.classList.toggle('is-copied', label === 'copied');
     11     setTimeout(() => { btn.textContent = restore; btn.classList.remove('is-copied'); }, 1400);
     12   };
     13   const done = () => flash('copied');
     14   const failed = () => flash('failed');
     15   if (navigator.clipboard?.writeText) {
     16     navigator.clipboard.writeText(text).then(done).catch(() => fallback(text, done, failed));
     17   } else {
     18     fallback(text, done, failed);
     19   }
     20 }
     21 
     22 function fallback(text: string, done: () => void, failed: () => void): void {
     23   const ta = document.createElement('textarea');
     24   ta.value = text;
     25   ta.setAttribute('readonly', '');
     26   ta.style.position = 'fixed';
     27   ta.style.opacity = '0';
     28   document.body.appendChild(ta);
     29   ta.select();
     30   let ok = false;
     31   try { ok = document.execCommand('copy'); } catch { ok = false; }
     32   document.body.removeChild(ta);
     33   if (ok) done(); else failed();
     34 }
     35 
     36 function onClick(e: Event): void {
     37   const btn = (e.target as HTMLElement | null)?.closest<HTMLElement>('[data-copy]');
     38   if (!btn) return;
     39   const bar = btn.closest('[data-cmdbar]');
     40   const code = bar?.querySelector('code');
     41   copyText(code?.textContent || '', btn);
     42 }
     43 
     44 // Modules run once per JS context (the ClientRouter keeps it across
     45 // navigations), so a module-level flag is the right double-bind guard.
     46 let bound = false;
     47 if (!bound) {
     48   bound = true;
     49   document.addEventListener('click', onClick);
     50 }