| 1 | // Wires the signup form to the solver. |
| 2 | // |
| 3 | // The submit button is rendered disabled, with an explanation inside |
| 4 | // <noscript>, and this module enables it once it has loaded. That ordering is |
| 5 | // the only one that fails safe: JavaScript cannot enable a control that lives |
| 6 | // inside <noscript>, and a module that throws on an old browser leaves the |
| 7 | // honest disabled state rather than a form that looks live and silently does |
| 8 | // nothing. |
| 9 | // |
| 10 | // Signing up genuinely requires JavaScript here — the proof of work is |
| 11 | // mandatory. A visitor who cannot run it deserves to learn that immediately, |
| 12 | // and from us. |
| 13 | |
| 14 | // This module does no hashing itself — the worker imports sha256.js. |
| 15 | |
| 16 | const form = document.querySelector("[data-pow-form]"); |
| 17 | |
| 18 | if (form) { |
| 19 | const submit = form.querySelector("[data-pow-submit]"); |
| 20 | const email = form.querySelector("[data-pow-email]"); |
| 21 | const counter = form.querySelector("[data-pow-counter]"); |
| 22 | const status = form.querySelector("[data-pow-status]"); |
| 23 | const workerURL = form.dataset.powWorker; |
| 24 | const difficulty = parseInt(form.dataset.powDifficulty, 10); |
| 25 | const nonce = form.dataset.powNonce; |
| 26 | |
| 27 | const idleLabel = submit.dataset.label || submit.textContent; |
| 28 | const workingLabel = submit.dataset.workingLabel || idleLabel; |
| 29 | |
| 30 | // Everything above resolved, so the form can be used. |
| 31 | submit.disabled = false; |
| 32 | |
| 33 | let solved = false; |
| 34 | |
| 35 | form.addEventListener("submit", (event) => { |
| 36 | if (solved) return; // second pass: the counter is in, let it through. |
| 37 | |
| 38 | event.preventDefault(); |
| 39 | if (!email.value.trim()) { |
| 40 | email.reportValidity(); |
| 41 | return; |
| 42 | } |
| 43 | |
| 44 | submit.disabled = true; |
| 45 | submit.textContent = workingLabel; |
| 46 | if (status) status.hidden = false; |
| 47 | |
| 48 | // The worker is addressed by a fingerprinted URL the template supplies, |
| 49 | // because static assets are content-hashed and this module cannot guess |
| 50 | // the name. Module type, so it can import sha256.js the same way. |
| 51 | const worker = new Worker(workerURL, { type: "module" }); |
| 52 | |
| 53 | worker.onmessage = (e) => { |
| 54 | if (!e.data.done) { |
| 55 | if (status) status.textContent = String(e.data.tried); |
| 56 | return; |
| 57 | } |
| 58 | worker.terminate(); |
| 59 | counter.value = e.data.counter; |
| 60 | solved = true; |
| 61 | form.requestSubmit ? form.requestSubmit() : form.submit(); |
| 62 | }; |
| 63 | |
| 64 | worker.onerror = () => { |
| 65 | worker.terminate(); |
| 66 | // Nothing clever to do: without a solution the server will refuse the |
| 67 | // submission anyway, so say so here rather than posting something we |
| 68 | // know will be rejected. |
| 69 | submit.disabled = false; |
| 70 | submit.textContent = idleLabel; |
| 71 | if (status) status.textContent = ""; |
| 72 | form.dispatchEvent(new CustomEvent("pow:failed")); |
| 73 | }; |
| 74 | |
| 75 | worker.postMessage({ |
| 76 | nonce, |
| 77 | email: email.value, |
| 78 | difficulty, |
| 79 | }); |
| 80 | }); |
| 81 | } |
| 82 | |