PageSourceSearch

https://devswarm.ai/scripts/marketing-runtime.js

js devswarm.ai collected 2026-10-04 02:08:22 UTC 8,788 bytes, 276 lines download raw bytes

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.