PageSourceSearch

https://www.iab.com/wp-content/plugins/iab-utm-persist/iab-utm-persist.js?ver=1778782504

js iab.com collected 2026-09-24 08:03:18 UTC 4,509 bytes, 144 lines download raw bytes

1document.addEventListener("DOMContentLoaded", function () {
2  const STORAGE_KEY = "iab_utm_values";
3  const utmParams = [
4    "utm_id",
5    "utm_source",
6    "utm_medium",
7    "utm_campaign",
8    "utm_term",
9    "utm_content",
10  ];
11
12  function readStoredUTMs() {
13    try {
14      const raw = localStorage.getItem(STORAGE_KEY);
15      if (!raw) return {};
16      const parsed = JSON.parse(raw);
17      if (!parsed || typeof parsed !== "object") return {};
18      const filtered = {};
19      utmParams.forEach(function (key) {
20        if (parsed[key]) filtered[key] = parsed[key];
21      });
22      return filtered;
23    } catch (e) {
24      return {};
25    }
26  }
27
28  function writeStoredUTMs(utms) {
29    try {
30      localStorage.setItem(STORAGE_KEY, JSON.stringify(utms));
31    } catch (e) {
32      // localStorage unavailable or quota exceeded — silent
33    }
34  }
35
36  function readCurrentUTMs() {
37    const params = new URLSearchParams(window.location.search);
38    const out = {};
39    utmParams.forEach(function (key) {
40      if (!params.has(key)) return;
41      const value = params.get(key);
42      if (value) out[key] = value;
43    });
44    return out;
45  }
46
47  const storedUTMs = readStoredUTMs();
48  const currentUTMs = readCurrentUTMs();
49  const utmsToApply = Object.assign({}, storedUTMs, currentUTMs);
50
51  if (Object.keys(currentUTMs).length > 0) {
52    writeStoredUTMs(utmsToApply);
53  }
54
55  // Blackthorn links
56  document.querySelectorAll('a[href*="blackthorn.io"]').forEach(function (link) {
57    try {
58      const url = new URL(link.getAttribute("href"), window.location.href);
59      Object.keys(utmsToApply).forEach(function (key) {
60        url.searchParams.set(key, utmsToApply[key]);
61      });
62      link.href = url.toString();
63    } catch (e) {
64      console.error("iab-utm-persist: error modifying Blackthorn link:", e);
65    }
66  });
67
68  // HubSpot embedded forms — same-origin iframe contentDocument access
69  // HubSpot renders forms inside iframes via srcdoc/document.write (no src
70  // attribute). Iframes are same-origin so we can reach in and populate
71  // hidden utm_* fields. Overwrite-always: utmsToApply already has URL
72  // values merged over stored, so overwriting can't clobber a fresh URL
73  // UTM, but it does override stale HubSpot returning-visitor defaults.
74  function applyToIframeForm(iframe) {
75    try {
76      const doc = iframe.contentDocument;
77      if (!doc) return;
78      const form = doc.querySelector("form.hs-form");
79      if (!form) return;
80      Object.keys(utmsToApply).forEach(function (key) {
81        const input = form.querySelector('input[name="' + key + '"]');
82        if (!input) return;
83        if (input.value === utmsToApply[key]) return;
84        input.value = utmsToApply[key];
85        input.dispatchEvent(new Event("input", { bubbles: true }));
86        input.dispatchEvent(new Event("change", { bubbles: true }));
87      });
88    } catch (e) {
89      // contentDocument may be cross-origin or not yet ready — silent
90    }
91  }
92
93  function watchHubSpotIframe(iframe) {
94    if (iframe.dataset.utmWatched) return;
95    iframe.dataset.utmWatched = "1";
96
97    let innerObserver = null;
98    function attachInnerObserver() {
99      if (innerObserver) {
100        try { innerObserver.disconnect(); } catch (e) {}
101        innerObserver = null;
102      }
103      try {
104        const doc = iframe.contentDocument;
105        if (!doc) return;
106        innerObserver = new MutationObserver(function () { applyToIframeForm(iframe); });
107        innerObserver.observe(doc, { childList: true, subtree: true });
108      } catch (e) {
109        // contentDocument not accessible yet
110      }
111    }
112
113    applyToIframeForm(iframe);
114    attachInnerObserver();
115    iframe.addEventListener("load", function () {
116      applyToIframeForm(iframe);
117      attachInnerObserver();
118    });
119  }
120
121  function scanForHubSpotIframes(root) {
122    if (!root) return;
123    if (root.matches && root.matches("iframe.hs-form-iframe")) {
124      watchHubSpotIframe(root);
125      return;
126    }
127    if (root.querySelectorAll) {
128      root.querySelectorAll("iframe.hs-form-iframe").forEach(watchHubSpotIframe);
129    }
130  }
131
132  scanForHubSpotIframes(document.body);
133
134  const observer = new MutationObserver(function (mutations) {
135    mutations.forEach(function (mutation) {
136      if (mutation.type !== "childList") return;
137      mutation.addedNodes.forEach(function (node) {
138        if (node.nodeType !== 1) return;
139        scanForHubSpotIframes(node);
140      });
141    });
142  });
143  observer.observe(document.body, { childList: true, subtree: true });
144});

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.