1;(function () { 2 function setupSocialShare() { 3 var pageUrl = window.location.href 4 var pageTitle = document.title 5 6 document 7 .querySelectorAll("a[fs-socialshare-element]") 8 .forEach(function (anchor) { 9 var socialType = anchor.getAttribute("fs-socialshare-element") 10 var shareUrl = "" 11 12 if (socialType === "linkedin") { 13 shareUrl = 14 "https://www.linkedin.com/sharing/share-offsite/?url=" + 15 encodeURIComponent(pageUrl) 16 } else if (socialType === "facebook") { 17 shareUrl = 18 "https://www.facebook.com/sharer/sharer.php?u=" + 19 encodeURIComponent(pageUrl) 20 } else if (socialType === "x") { 21 shareUrl = 22 "https://twitter.com/intent/tweet?url=" + 23 encodeURIComponent(pageUrl) + 24 "&text=" + 25 encodeURIComponent(pageTitle) 26 } 27 28 if (!shareUrl) { 29 return 30 } 31 32 anchor.setAttribute("href", shareUrl) 33 anchor.setAttribute("target", "_blank") 34 anchor.setAttribute("rel", "noopener noreferrer") 35 }) 36 } 37 38 // API bases and dev hostnames come from window.__DS_MARKETING_CONFIG, 39 // injected by BaseLayout.astro from src/config/marketing.config.ts 40 // (values in apps/web/marketing/.env). This file is served raw from 41 // public/ and cannot read import.meta.env. 42 var CONFIG = window.__DS_MARKETING_CONFIG 43 44 function isDevHost() { 45 return CONFIG.devHosts.indexOf(window.location.hostname) !== -1 46 } 47 48 function getFormApiBase() { 49 return isDevHost() ? CONFIG.devApiBase : CONFIG.apiBase 50 } 51 52 function slugifyFormName(formName) { 53 return formName 54 .toLowerCase() 55 .replace(/[^a-z0-9]+/g, "-") 56 .replace(/^-+|-+$/g, "") 57 } 58 59 function collectFormFields(form) { 60 var fields = {} 61 var inputs = form.querySelectorAll("input, textarea, select") 62 inputs.forEach(function (input) { 63 var name = input.getAttribute("name") 64 if (!name) return 65 if (input.type === "submit" || input.type === "hidden") return 66 67 if (input.type === "radio") { 68 if (input.checked) { 69 var label = input.closest("label") 70 var labelText = label 71 ? label.querySelector(".w-form-label,.radio-button-label") 72 : null 73 fields[name] = labelText ? labelText.textContent.trim() : input.value 74 } 75 return 76 } 77 78 if (input.type === "checkbox") { 79 if (input.checked) { 80 fields[name] = input.value || "Yes" 81 } 82 return 83 } 84 85 if (input.value) { 86 fields[name] = input.value 87 } 88 }) 89 return fields 90 } 91 92 function showFormError(form, message) { 93 var errorBlock = form.parentElement 94 ? form.parentElement.querySelector(".w-form-fail") 95 : null 96 if (errorBlock) { 97 var errorText = errorBlock.querySelector(".text-error, div") 98 if (errorText) { 99 errorText.textContent = 100 message || "Something went wrong. Please try again." 101 } 102 errorBlock.style.display = "block" 103 } 104 } 105 106 function showFormSuccess(form) { 107 var successBlock = form.parentElement 108 ? form.parentElement.querySelector(".w-form-done") 109 : null 110 if (successBlock) { 111 successBlock.style.display = "block" 112 } 113 form.style.display = "none" 114 } 115 116 function setupFormRedirects() { 117 if (!CONFIG) { 118 console.error( 119 "marketing-runtime: window.__DS_MARKETING_CONFIG missing â " + 120 "BaseLayout.astro must inject it before this script. Forms disabled." 121 ) 122 return 123 } 124 var apiBase = getFormApiBase() 125 var forms = document.querySelectorAll("form[data-form-name]") 126 if (!forms.length) return 127 128 forms.forEach(function (form) { 129 var formName = form.getAttribute("data-form-name") 130 if (!formName) return 131 var formSlug = slugifyFormName(formName) 132 133 form.addEventListener("submit", function (event) { 134 event.preventDefault() 135 136 // Hide any previous error 137 var errorBlock = form.parentElement 138 ? form.parentElement.querySelector(".w-form-fail") 139 : null 140 if (errorBlock) errorBlock.style.display = "none" 141 142 var fields = collectFormFields(form) 143 144 var submitBtn = form.querySelector('input[type="submit"]') 145 var originalValue = submitBtn ? submitBtn.value : "" 146 var waitText = submitBtn ? submitBtn.getAttribute("data-wait") : null 147 if (submitBtn && waitText) { 148 submitBtn.value = waitText 149 submitBtn.disabled = true 150 } 151 152 fetch(apiBase + "/v1/forms/" + formSlug + "/submissions", { 153 method: "POST", 154 headers: { "Content-Type": "application/json" }, 155 body: JSON.stringify({ 156 fields: fields, 157 website: "", 158 }), 159 }) 160 .then(function (response) { 161 if (!response.ok) { 162 return response.json().then(function (data) { 163 throw new Error(data.message || "Submission failed") 164 }) 165 } 166 return response.json() 167 }) 168 .then(function () { 169 var redirectTo = 170 form.getAttribute("data-redirect") || 171 form.getAttribute("redirect") 172 // Only navigate to a same-origin path. Reject absolute URLs, protocol-relative
173 // '//host', and non-path schemes (e.g. javascript:) so a DOM-sourced value can't 174 // be reinterpreted as a dangerous navigation. (CodeQL js/xss-through-dom #106) 175 if ( 176 redirectTo && 177 redirectTo.startsWith("/") && 178 !redirectTo.startsWith("//") 179 ) { 180 if ( 181 !redirectTo.endsWith("/") && 182 !redirectTo.includes("?") && 183 !redirectTo.includes("#") && 184 !/\.[a-zA-Z0-9]+$/.test(redirectTo) 185 ) { 186 redirectTo = redirectTo + "/" 187 } 188 window.location.href = redirectTo 189 } else { 190 showFormSuccess(form) 191 } 192 }) 193 .catch(function (err) { 194 showFormError(form, err.message) 195 }) 196 .finally(function () { 197 if (submitBtn) { 198 submitBtn.value = originalValue 199 submitBtn.disabled = false 200 } 201 }) 202 }) 203 }) 204 } 205 206 function setupVimeoFadeIn() { 207 var iframes = Array.prototype.slice.call( 208 document.querySelectorAll('iframe[src*="player.vimeo.com"]') 209 ) 210 if (!iframes.length) return 211 212 // Reveal only once Vimeo has actually painted the video. `ready` means 213 // "player chrome loaded" but the video buffer may not be rendering yet, 214 // so revealing on `ready` exposes the iframe's white default canvas. 215 // For autoplaying loops (the flashing case) we wait for `play`. For 216 // non-autoplay videos (e.g. Welcome video, hero) we accept `ready` as a 217 // signal that the player thumbnail is up â those have controls/poster 218 // so the chrome itself is dark. 219 function revealForSource(source) { 220 for (var i = 0; i < iframes.length; i++) { 221 if (iframes[i].contentWindow === source) { 222 iframes[i].classList.add("vimeo-loaded") 223 break 224 } 225 } 226 } 227 228 window.addEventListener("message", function (e) { 229 if (e.origin !== "https://player.vimeo.com") return 230 var data 231 try { 232 data = JSON.parse(e.data) 233 } catch (ex) { 234 return 235 } 236 237 // On ready, subscribe to play. For non-autoplay videos, also reveal 238 // now so the user sees the player thumbnail/controls. 239 if (data.event === "ready") { 240 e.source.postMessage( 241 JSON.stringify({ method: "addEventListener", value: "play" }), 242 e.origin 243 ) 244 e.source.postMessage( 245 JSON.stringify({ method: "addEventListener", value: "playing" }), 246 e.origin 247 ) 248 // Determine if this iframe is an autoplay loop. If not, reveal now. 249 for (var i = 0; i < iframes.length; i++) { 250 if (iframes[i].contentWindow === e.source) { 251 if (iframes[i].src.indexOf("autoplay=1") === -1) { 252 iframes[i].classList.add("vimeo-loaded") 253 } 254 break 255 } 256 } 257 } 258 259 if (data.event === "play" || data.event === "playing") { 260 revealForSource(e.source) 261 } 262 }) 263 264 // Fallback so iframes never stay hidden if the ready event is missed 265 // (network failure, blocked embed, postMessage error, etc). 266 iframes.forEach(function (iframe) { 267 setTimeout(function () { 268 iframe.classList.add("vimeo-loaded") 269 }, 3000) 270 }) 271 } 272 273 setupSocialShare() 274 setupFormRedirects() 275 setupVimeoFadeIn() 276})()
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.