PageSourceSearch

https://shop.mysticgardenfestival.nl/link-tracking.js

js mysticgardenfestival.nl collected 2026-10-04 13:32:10 UTC 6,486 bytes, 159 lines download raw bytes

1/*!
2 * tckt link tracking
3 *
4 * Runs on a client's own website (not in the shop). Remembers the campaign
5 * params a visitor landed with and adds them to links to the client's tckt
6 * ticket shop, so the shop can attribute the order.
7 *
8 * Install in <head> of the client's website (or as GTM Custom HTML tag):
9 *   <script src="https://shop.<client-domain>/link-tracking.js" data-cookieconsent="ignore" async></script>
10 * data-cookieconsent="ignore" keeps Cookiebot auto-blocking from blocking it;
11 * the script checks marketing consent itself before it sets a cookie.
12 *
13 * Rules (same as the shop's TrackingService):
14 *   - Only allowlisted params are kept: utm_*, gclid, gbraid, wbraid, ttclid.
15 *   - A landing URL with any of them replaces the complete stored touch, so
16 *     params from different visits never mix. A landing without them keeps it.
17 *   - Stored in sessionStorage always; in a first-party cookie only while
18 *     Cookiebot marketing consent is given (deleted on decline). The cookie
19 *     expires 90 days after it was last written.
20 *   - On a link to the shop, a stored touch replaces all tracking params already
21 *     on the link as one set. Other params on the link (e.g. GA's _gl) are kept.
22 *   - A link to the shop without its own referrerpolicy gets
23 *     strict-origin-when-cross-origin (the browser default), so the shop sees the
24 *     site's origin as referrer even when the site's Referrer-Policy (e.g.
25 *     same-origin) would send none. Only the origin is sent, never the path.
26 *     Shop buttons that open the shop with JavaScript are not covered.
27 *
28 * Shop links are links to shop.<site domain> (www. stripped), to our platform
29 * domain shop.ihavegotaticket.com, or to the host this script was loaded from.
30 *
31 * window.tcktLinkTracking.decorate(url) is available for custom buttons that
32 * open the shop with JavaScript instead of a link.
33 */
34(function () {
35  'use strict';
36  if (window.tcktLinkTracking) return;
37
38  var STORAGE_KEY = 'tckt_touch';
39  var COOKIE_MAX_AGE = 90 * 24 * 60 * 60;
40  var CLICK_IDS = ['gclid', 'gbraid', 'wbraid', 'ttclid'];
41  var SHOP_LINK_REFERRER_POLICY = 'strict-origin-when-cross-origin';
42
43  var shopHosts = ['shop.' + location.hostname.replace(/^www\./, ''), 'shop.ihavegotaticket.com'];
44  var script = document.currentScript;
45  if (script && script.src) {
46    var scriptHost = new URL(script.src, location.href).hostname;
47    // When the script is self-hosted the site itself is not a shop
48    if (scriptHost !== location.hostname && shopHosts.indexOf(scriptHost) === -1) shopHosts.push(scriptHost);
49  }
50
51  function isTrackingParam(key) {
52    return key.indexOf('utm_') === 0 || CLICK_IDS.indexOf(key) !== -1;
53  }
54
55  function parseTouch(raw) {
56    if (!raw) return null;
57    try {
58      var touch = JSON.parse(raw);
59      return touch && typeof touch === 'object' && Object.keys(touch).length ? touch : null;
60    } catch (e) {
61      return null;
62    }
63  }
64
65  function readSession() {
66    try { return parseTouch(sessionStorage.getItem(STORAGE_KEY)); } catch (e) { return null; }
67  }
68
69  function writeSession(touch) {
70    try { sessionStorage.setItem(STORAGE_KEY, JSON.stringify(touch)); } catch (e) {}
71  }
72
73  function readCookie() {
74    var match = document.cookie.match(new RegExp('(?:^|; )' + STORAGE_KEY + '=([^;]*)'));
75    if (!match) return null;
76    try { return parseTouch(decodeURIComponent(match[1])); } catch (e) { return null; }
77  }
78
79  function writeCookie(touch) {
80    document.cookie = STORAGE_KEY + '=' + encodeURIComponent(JSON.stringify(touch)) +
81      '; max-age=' + COOKIE_MAX_AGE + '; path=/; SameSite=Lax' +
82      (location.protocol === 'https:' ? '; Secure' : '');
83  }
84
85  function deleteCookie() {
86    document.cookie = STORAGE_KEY + '=; max-age=0; path=/';
87  }
88
89  // This session's touch is always the newest; the cookie carries one over from an earlier visit
90  function readTouch() {
91    return readSession() || readCookie();
92  }
93
94  // null until Cookiebot has loaded and the visitor's choice is known
95  function marketingConsent() {
96    var consent = window.Cookiebot && window.Cookiebot.consent;
97    return consent && typeof consent.marketing === 'boolean' ? consent.marketing : null;
98  }
99
100  function capture() {
101    var touch = {};
102    new URLSearchParams(location.search).forEach(function (value, key) {
103      if (value && isTrackingParam(key)) touch[key] = value;
104    });
105    if (!Object.keys(touch).length) return;
106    writeSession(touch);
107    if (marketingConsent() === true) writeCookie(touch);
108  }
109
110  function applyConsent() {
111    var consent = marketingConsent();
112    if (consent === true) {
113      var touch = readSession();
114      if (touch) writeCookie(touch);
115    } else if (consent === false) {
116      deleteCookie();
117    }
118  }
119
120  function isShopUrl(href) {
121    try { return shopHosts.indexOf(new URL(href, location.href).hostname) !== -1; } catch (e) { return false; }
122  }
123
124  function decorate(href) {
125    var touch = readTouch();
126    if (!touch || !isShopUrl(href)) return href;
127    var url = new URL(href, location.href);
128
129    var existing = [];
130    url.searchParams.forEach(function (value, key) { existing.push(key); });
131    existing.forEach(function (key) { if (isTrackingParam(key)) url.searchParams.delete(key); });
132    Object.keys(touch).forEach(function (key) {
133      if (isTrackingParam(key)) url.searchParams.set(key, touch[key]);
134    });
135    return url.toString();
136  }
137
138  // Rewrites the link just before the browser follows it, which also covers
139  // links added later, middle/ctrl-click and "open in new tab" from the context menu
140  function onLinkInteraction(event) {
141    var link = event.target && event.target.closest ? event.target.closest('a[href]') : null;
142    // SVG links have a non-string href
143    if (!link || typeof link.href !== 'string' || !isShopUrl(link.href)) return;
144    if (!link.referrerPolicy) link.referrerPolicy = SHOP_LINK_REFERRER_POLICY;
145    var decorated = decorate(link.href);
146    if (decorated !== link.href) link.href = decorated;
147  }
148
149  capture();
150  applyConsent();
151  ['CookiebotOnConsentReady', 'CookiebotOnLoad', 'CookiebotOnAccept', 'CookiebotOnDecline'].forEach(function (name) {
152    window.addEventListener(name, applyConsent);
153  });
154  ['mousedown', 'touchstart', 'click', 'auxclick', 'contextmenu'].forEach(function (name) {
155    document.addEventListener(name, onLinkInteraction, { capture: true, passive: true });
156  });
157
158  window.tcktLinkTracking = { decorate: decorate, touch: readTouch, shopHosts: shopHosts };
159})();

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.