Created
March 13, 2026 19:17
-
-
Save Nooshu/dd608f9df6e21ad5e2ec3a37e1e12264 to your computer and use it in GitHub Desktop.
This is the client-side script for adding Cloudflare Turnstile to my blog.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (function () { | |
| const SITE_KEY = "0x4AAAAAACpYvR2v9J0i1tr_"; | |
| const form = document.getElementById("fs-frm"); | |
| if (!form) return; | |
| const tokenInputName = "cf-turnstile-response"; | |
| let tokenInput = form.querySelector(`input[name="${tokenInputName}"]`); | |
| if (!tokenInput) { | |
| tokenInput = document.createElement("input"); | |
| tokenInput.type = "hidden"; | |
| tokenInput.name = tokenInputName; | |
| form.appendChild(tokenInput); | |
| } | |
| const errorSummary = document.getElementById("form-error-summary"); | |
| const formStatus = document.getElementById("form-status"); | |
| function showFormError(message) { | |
| if (errorSummary) { | |
| errorSummary.textContent = message; | |
| errorSummary.hidden = false; | |
| errorSummary.focus(); | |
| } | |
| } | |
| function clearFormError() { | |
| if (errorSummary) { | |
| errorSummary.textContent = ""; | |
| errorSummary.hidden = true; | |
| } | |
| } | |
| function showFieldError(fieldId, message) { | |
| const field = document.getElementById(fieldId); | |
| const errorEl = document.getElementById(fieldId + "-error"); | |
| if (field) { | |
| field.setAttribute("aria-invalid", "true"); | |
| field.setAttribute("aria-describedby", fieldId + "-error"); | |
| } | |
| if (errorEl) { | |
| errorEl.textContent = message; | |
| errorEl.hidden = false; | |
| } | |
| } | |
| function clearFieldError(fieldId) { | |
| const field = document.getElementById(fieldId); | |
| const errorEl = document.getElementById(fieldId + "-error"); | |
| if (field) { | |
| field.removeAttribute("aria-invalid"); | |
| field.removeAttribute("aria-describedby"); | |
| } | |
| if (errorEl) { | |
| errorEl.textContent = ""; | |
| errorEl.hidden = true; | |
| } | |
| } | |
| function clearAllErrors() { | |
| clearFormError(); | |
| ["name", "email", "message"].forEach(clearFieldError); | |
| } | |
| function setStatus(message) { | |
| if (formStatus) { | |
| formStatus.textContent = message; | |
| } | |
| } | |
| function validateForm() { | |
| clearAllErrors(); | |
| const errors = []; | |
| const nameField = form.querySelector("#name"); | |
| const emailField = form.querySelector("#email"); | |
| const messageField = form.querySelector("#message"); | |
| if (!nameField.value.trim()) { | |
| showFieldError("name", "Please enter your full name."); | |
| errors.push(nameField); | |
| } | |
| if (!emailField.value.trim()) { | |
| showFieldError("email", "Please enter your email address."); | |
| errors.push(emailField); | |
| } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailField.value.trim())) { | |
| showFieldError("email", "Please enter a valid email address."); | |
| errors.push(emailField); | |
| } | |
| if (!messageField.value.trim()) { | |
| showFieldError("message", "Please enter a message."); | |
| errors.push(messageField); | |
| } | |
| if (errors.length > 0) { | |
| const summary = | |
| errors.length === 1 | |
| ? "There is 1 error in the form." | |
| : "There are " + errors.length + " errors in the form."; | |
| showFormError(summary); | |
| errors[0].focus(); | |
| return false; | |
| } | |
| return true; | |
| } | |
| let widgetId = null; | |
| let pendingSubmit = false; | |
| let submitButton = null; | |
| function onTokenReceived(token) { | |
| tokenInput.value = token; | |
| if (pendingSubmit) { | |
| pendingSubmit = false; | |
| form.submit(); | |
| } | |
| } | |
| function onTokenError(errorCode) { | |
| tokenInput.value = ""; | |
| pendingSubmit = false; | |
| setStatus(""); | |
| console.error("Turnstile error:", errorCode); | |
| showFormError( | |
| "Verification failed. Please try again. If you use an ad blocker, you may need to allow Cloudflare on this site." | |
| ); | |
| if (submitButton) { | |
| submitButton.disabled = false; | |
| submitButton.textContent = "Send Message"; | |
| } | |
| if (widgetId !== null && typeof turnstile !== "undefined") { | |
| turnstile.reset(widgetId); | |
| } | |
| } | |
| function renderTurnstile() { | |
| const container = document.getElementById("turnstile-container"); | |
| if (!container || widgetId !== null) return; | |
| if (typeof turnstile !== "undefined") { | |
| widgetId = turnstile.render(container, { | |
| sitekey: SITE_KEY, | |
| size: "invisible", | |
| execution: "execute", | |
| callback: onTokenReceived, | |
| "error-callback": onTokenError, | |
| "expired-callback": function () { | |
| tokenInput.value = ""; | |
| }, | |
| }); | |
| } | |
| } | |
| function waitForTurnstile() { | |
| return new Promise((resolve) => { | |
| if (typeof turnstile !== "undefined") { | |
| resolve(); | |
| return; | |
| } | |
| let attempts = 0; | |
| const checkInterval = setInterval(() => { | |
| attempts++; | |
| if (typeof turnstile !== "undefined") { | |
| clearInterval(checkInterval); | |
| resolve(); | |
| } else if (attempts > 100) { | |
| clearInterval(checkInterval); | |
| resolve(); | |
| } | |
| }, 100); | |
| }); | |
| } | |
| waitForTurnstile().then(() => { | |
| renderTurnstile(); | |
| }); | |
| form.addEventListener("submit", async function (e) { | |
| e.preventDefault(); | |
| if (!validateForm()) return; | |
| submitButton = form.querySelector('button[type="submit"]'); | |
| if (submitButton) { | |
| submitButton.disabled = true; | |
| submitButton.textContent = "Sending…"; | |
| } | |
| setStatus("Verifying, please wait."); | |
| await waitForTurnstile(); | |
| if (widgetId === null) { | |
| renderTurnstile(); | |
| await new Promise((resolve) => setTimeout(resolve, 100)); | |
| } | |
| if (tokenInput.value) { | |
| form.submit(); | |
| return; | |
| } | |
| if (widgetId !== null && typeof turnstile !== "undefined") { | |
| pendingSubmit = true; | |
| turnstile.execute(widgetId); | |
| } else { | |
| console.error("Turnstile widget not available"); | |
| onTokenError("widget-not-available"); | |
| } | |
| }); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment