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