1// Shared two-step submit-confirm. Replaces duplicated IIFEs in 2// drafts.js, notes.js, canaries.js, and bugs/index.html (inline). 3// 4// Auto-attaches to any ``<form data-confirm-delete>`` OR 5// ``<form data-confirm-submit>`` on the page. The delete variant is 6// the historical name (most use-sites are deletions); the submit 7// variant is the same behaviour with a friendlier attribute for 8// non-destructive but irreversible actions (e.g. submitting a 9// marketplace review, which can't be edited or withdrawn after). 10// 11// First submit click flips the form's submit button into a red 12// "Confirm delete" state; second click within 4 seconds (or before 13// the user clicks elsewhere) submits the form normally. Per-page 14// CSS styles the button via the ``[data-confirming="1"]`` selector, 15// so visual treatment stays page-specific. 16// 17// Per-button customisation (set on the submit button, not the form): 18// data-confirm-label="Confirm retract" // default: "Confirm delete" 19// data-idle-label="Retract" // default: button's initial textContent 20// 21// Icon-preserving label swap: if the button has a child with 22// ``data-label`` (typically a ``<span data-label>`` next to an SVG), 23// the helper swaps that span's textContent instead of the whole 24// button. Lets buttons keep their icon while only the label flips. 25// 26// Auto-resets after 4 s or on click outside the pending button. 27// Works under CSP (no inline handlers) and is safe against text-node 28// click targets via the closestEl walk-up. 29(function () { 30 'use strict'; 31 32 var pendingBtn = null; 33 var pendingTimer = null; 34 35 function labelTarget(btn) { 36 // Icon-preserving label swap. ``<span data-label>`` next to an 37 // SVG keeps the icon intact while only the text flips. 38 return btn.querySelector('[data-label]') || btn; 39 } 40 41 function reset() { 42 if (pendingBtn) { 43 pendingBtn.dataset.confirming = '0'; 44 var idle = pendingBtn.dataset.idleLabel; 45 if (idle != null) labelTarget(pendingBtn).textContent = idle; 46 } 47 pendingBtn = null; 48 if (pendingTimer) clearTimeout(pendingTimer); 49 pendingTimer = null; 50 } 51 52 document.addEventListener('submit', function (e) { 53 var form = e.target; 54 if (!form || form.nodeType !== 1) return; 55 if (!form.matches('[data-confirm-delete], [data-confirm-submit]')) return; 56 57 // Prefer the actual submitter (modern browsers); fall back to 58 // the first submit button on the form. 59 var btn = e.submitter 60 || form.querySelector('button[type="submit"]') 61 || form.querySelector('button:not([type])'); 62 if (!btn) return; 63 64 if (btn.dataset.confirming === '1') { 65 // Second click â let the submit proceed normally. 66 reset(); 67 return; 68 } 69 e.preventDefault(); 70 reset(); 71 pendingBtn = btn; 72 var target = labelTarget(btn); 73 if (btn.dataset.idleLabel == null) { 74 // Capture the initial text so we can restore it on reset. 75 btn.dataset.idleLabel = target.textContent; 76 } 77 btn.dataset.confirming = '1'; 78 target.textContent = btn.dataset.confirmLabel || 'Confirm delete'; 79 pendingTimer = setTimeout(reset, 4000); 80 }); 81 82 // Click outside the pending button -> reset. Walk through text-node 83 // targets safely (mirrors closestEl in colony.js). 84 document.addEventListener('click', function (e) { 85 if (!pendingBtn) return; 86 var t = e.target; 87 while (t && t.nodeType !== 1) t = t.parentNode || null; 88 if (t && pendingBtn.contains(t)) return; 89 reset(); 90 }); 91})();
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.