rastrillo / fiesta Public

Clone
git clone https://amadan.net/rastrillo/fiesta

Plain git — no account needed to clone.

Download

Download this file

1/*! Fiesta 1.0.0 | MPL-2.0 | https://fiesta.rastrillo.org */
2(function () {
3 "use strict";
4
5 var HOLD_MS = 650;
6
7 function busyOff(form) {
8 form.removeAttribute("aria-busy");
9 document.querySelectorAll('[aria-busy="true"]').forEach(function (b) {
10 if (b.form !== form && !form.contains(b)) return;
11 b.disabled = false;
12 b.removeAttribute("aria-busy");
13 var spin = b.querySelector("[rst-spin]");
14 if (spin) spin.remove();
15 var idle = b.getAttribute("data-idle-label");
16 if (idle === null) return;
17 if (b.tagName === "INPUT") { b.value = idle; } else { b.textContent = idle; }
18 b.removeAttribute("data-idle-label");
19 });
20 }
21
22 function busySubmit(e) {
23 var form = e.target;
24 if (!form || form.tagName !== "FORM") return;
25 if (form.hasAttribute("data-pow-form") && !form.hasAttribute("data-pow-released")) return;
26 if (form.getAttribute("data-busy") === "false") return;
27 var btn = e.submitter;
28 var to = (btn && btn.getAttribute("formtarget")) || form.getAttribute("target");
29 if (to && to !== "_self") return;
30 if (/^dialog$/i.test(btn && btn.getAttribute("formmethod") ||
31 form.getAttribute("method"))) return;
32 if (form === releasing) return;
33 if (form.getAttribute("aria-busy") === "true") { e.preventDefault(); return; }
34 form.setAttribute("aria-busy", "true");
35 if (!btn || btn.getAttribute("data-busy") === "false") btn = null;
36 var label = btn && (btn.getAttribute("data-busy-label") ||
37 form.getAttribute("data-busy-label"));
38 if (btn) {
39 btn.setAttribute("aria-busy", "true");
40 if (btn.tagName === "BUTTON") {
41 if (label) {
42 btn.setAttribute("data-idle-label", btn.textContent);
43 btn.textContent = label;
44 }
45 var spin = document.createElement("span");
46 spin.setAttribute("rst-spin", "");
47 spin.setAttribute("aria-hidden", "true");
48 btn.insertBefore(spin, btn.firstChild);
49 }
50 }
51 setTimeout(function () {
52 if (e.defaultPrevented && !held.has(e)) { busyOff(form); return; }
53 if (!btn) return;
54 if (label && btn.tagName === "INPUT") {
55 btn.setAttribute("data-idle-label", btn.value);
56 btn.value = label;
57 }
58 btn.disabled = true;
59 }, 0);
60 }
61
62 document.addEventListener("submit", busySubmit, true);
63
64 var held = new WeakSet();
65 var pending = new Map();
66 var releasing = null;
67 window.addEventListener("submit", function (e) {
68 var form = e.target;
69 if (!form || form.tagName !== "FORM") return;
70 if (form === releasing) { releasing = null; return; }
71 if (form.hasAttribute("data-pow-form")) return;
72 if (e.defaultPrevented || form.getAttribute("aria-busy") !== "true") return;
73 var btn = e.submitter || null;
74 if (!btn || btn.getAttribute("aria-busy") !== "true") return;
75 e.preventDefault();
76 held.add(e);
77 pending.set(form, setTimeout(function () { release(form, btn); }, HOLD_MS));
78 if (pending.size === 1) window.addEventListener("beforeunload", dropHeld);
79 });
80
81 function release(form, btn) {
82 pending.delete(form);
83 if (pending.size === 0) window.removeEventListener("beforeunload", dropHeld);
84 if (!form.isConnected) return;
85 var skip = form.hasAttribute("novalidate") ||
86 (btn && btn.hasAttribute("formnovalidate"));
87 if (!skip && !form.checkValidity()) {
88 busyOff(form);
89 form.reportValidity();
90 return;
91 }
92 if (btn && (!btn.isConnected || btn.form !== form)) {
93 busyOff(form);
94 return;
95 }
96 var busyValue = null;
97 var wasDisabled = btn ? btn.disabled : false;
98 if (btn) {
99 btn.disabled = false;
100 var idle = btn.getAttribute("data-idle-label");
101 if (btn.tagName === "INPUT" && idle !== null) {
102 busyValue = btn.value;
103 btn.value = idle;
104 }
105 }
106 releasing = form;
107 try {
108 form.requestSubmit(btn || undefined);
109 } finally {
110 releasing = null;
111 if (btn) {
112 btn.disabled = wasDisabled;
113 if (busyValue !== null) btn.value = busyValue;
114 }
115 }
116 }
117
118 function dropHeld() {
119 pending.forEach(function (timer, form) {
120 clearTimeout(timer);
121 busyOff(form);
122 });
123 pending.clear();
124 window.removeEventListener("beforeunload", dropHeld);
125 }
126
127 window.addEventListener("pagehide", function () { dropHeld(); });
128
129 window.addEventListener("pageshow", function (e) {
130 if (e.persisted) document.querySelectorAll("form[aria-busy]").forEach(busyOff);
131 });
132})();
133