1/*! 2 * hs-newsletter.js â Clearleft newsletter signup â HubSpot Forms API 3 * Portal 148482298 (EU hublet). One file, drops into every site unchanged. 4 * 5 * Markup contract â put these on the <form>: 6 * data-hs-form marks the form for this script 7 * data-hs-portal=""148482298"" 8 * data-hs-id=""<form GUID>"" 9 * data-hs-newsletter=""<value>"" writes the newsletter_subscriptions property 10 * 11 * And on each input you want submitted: 12 * data-hs=""0-1/email"" ""<objectTypeId>/<internal name>"" 13 * 0-1 = Contact, 0-2 = Company 14 * 15 * Adding a field later is a markup change only â no edit to this file. 16 * If a CSP is ever added to a site, connect-src must include api-eu1.hsforms.com 17 */ 18(function () { 19 "use strict"; 20 21 var ENDPOINT_BASE = 22 "https://api-eu1.hsforms.com/submissions/v3/integration/submit/"; 23 24 function hutk() { 25 var m = document.cookie.match(/(^|;)\s*hubspotutk=([^;]*)/); 26 return m ? m[2] : undefined; // undefined keys are dropped by JSON.stringify 27 } 28 29 function textFrom(html) { 30 var d = document.createElement("div"); 31 d.innerHTML = html || ""; 32 return d.textContent.trim(); 33 } 34 35 function init(form) { 36 var portal = form.dataset.hsPortal; 37 var formId = form.dataset.hsId; 38 var newsletter = form.dataset.hsNewsletter; 39 if (!portal || !formId) return; 40 41 var status = form.querySelector("[data-hs-status]"); 42 var button = form.querySelector("button[type=submit], input[type=submit]"); 43 var honey = form.querySelector("[data-hs-honeypot]"); 44 45 function say(msg) { 46 if (status) status.textContent = msg; 47 } 48 49 form.addEventListener("submit", function (e) { 50 e.preventDefault(); 51 if (honey && honey.value) return; // bot 52 53 var fields = []; 54 var missing = false; 55 56 form.querySelectorAll("[data-hs]").forEach(function (input) { 57 var parts = input.getAttribute("data-hs").split("/"); 58 var value = (input.value || "").trim(); 59 if (!value) { 60 if (input.required) missing = true; 61 return; 62 } 63 fields.push({ objectTypeId: parts[0], name: parts[1], value: value }); 64 }); 65 66 if (missing || !fields.length) { 67 say("Please fill in the required fields."); 68 return; 69 } 70 71 // Tag which newsletter this signup came from. Without this every brand's 72 // signups land in HubSpot indistinguishable from each other. 73 if (newsletter) { 74 fields.push({ 75 objectTypeId: "0-1", 76 name: "newsletter_subscriptions", 77 value: newsletter, 78 }); 79 } 80 81 if (button) button.disabled = true; 82 say("Subscribingâ¦"); 83 84 fetch(ENDPOINT_BASE + portal + "/" + formId, { 85 method: "POST", 86 headers: { "Content-Type": "application/json" }, 87 body: JSON.stringify({ 88 fields: fields, 89 context: { 90 hutk: hutk(), 91 pageUri: window.location.href, 92 pageName: document.title, 93 }, 94 95 // ââ CONSENT âââââââââââââââââââââââââââââââââââââââââââââââââââââ 96 // GDPR is currently OFF in portal 148482298, so no consent record 97 // is stored. See README before deploying. When it is switched on, 98 // add here and surface matching copy/checkbox in the markup: 99 // 100 // ,legalConsentOptions: { 101 // consent: { 102 // consentToProcess: true, 103 // text: 'I agree to allow Clearleft to store and process my personal data.', 104 // communications: [{ 105 // value: true, 106 // subscriptionTypeId: 0, // real ID per brand 107 // text: 'I agree to receive this newsletter.' 108 // }] 109 // } 110 // } 111 // ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 112 }), 113 }) 114 .then(function (res) { 115 return res.json().then(function (body) { 116 if (!res.ok) throw new Error(JSON.stringify(body)); 117 return body; 118 }); 119 }) 120 .then(function (data) { 121 if (data.redirectUri) { 122 window.location.assign(data.redirectUri); 123 return; 124 } 125 say(textFrom(data.inlineMessage) || "Thanks â youâre on the list."); 126 var group = form.querySelector("[data-hs-fields]"); 127 if (group) group.hidden = true; 128 }) 129 .catch(function (err) { 130 if (window.console) console.error("[hs-newsletter]", err); 131 say("Sorry, something went wrong. Please try again."); 132 if (button) button.disabled = false;
133 }); 134 }); 135 } 136 137 function boot() { 138 document.querySelectorAll("[data-hs-form]").forEach(init); 139 } 140 if (document.readyState === "loading") { 141 document.addEventListener("DOMContentLoaded", boot); 142 } else { 143 boot(); 144 } 145})();
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.