PageSourceSearch

https://www.nashjewellers.com//skeleton/1.0/view/js/cmp-bridge.js?v=1790126023

js nashjewellers.com collected 2026-09-29 08:16:55 UTC 21,113 bytes, 462 lines download raw bytes

1/**
2 * RS 2026-06-19 CMP Bridge
3 *
4 * Universal, provider-agnostic consent listener. Sits between the active CMP
5 * (CookieYes, OneTrust, Cookiebot, …) and the site's tracking layer.
6 *
7 *  - Resolves the visitor's consent using the SAME category_map the server uses
8 *    to tag scripts (emitted as window.cmpBridgeConfig by getCMPScripts()), so
9 *    there are NO hard-coded provider category IDs. The map is internal -> the
10 *    provider's own category id/key:
11 *        { analytics:'2', advertisement:'4', personalisation:'3' }   // OneTrust
12 *        { analytics:'analytics', advertisement:'advertisement', personalisation:'functional' } // CookieYes
13 *  - Syncs Google Consent Mode v2 (gtag) and Facebook Pixel (fbq) on every change.
14 *  - Drives the Rolex / Adobe (Content Square) consent signals: rlx-consent and
15 *    rlx-marketing cookies + the Rolex iframe / _satellite events.
16 *
17 * Script LAUNCHING per category is the provider SDK's job via the blocking
18 * attributes rendered in conversion_codes.php (e.g. OneTrust's
19 * `type="text/plain" class="optanon-category-2"`); the bridge propagates the
20 * consent signals the SDK does not, and launches the tags it left blocked
21 * (RS 2026-09-02, see activateBlockedScripts).
22 *
23 * Rolex / Adobe consent (rlx-consent) defaults to the ANALYTICS category, and
24 * marketing (rlx-marketing) defaults to the ADVERTISEMENT category. If the admin
25 * configures a dedicated provider category under category_map.rolex /
26 * category_map.marketing, that takes precedence — so a separate OneTrust group
27 * for the Rolex/Adobe section works without code changes.
28 *
29 * Load order: MUST be included BEFORE the provider SDK (see getCMPScripts()) so
30 * OptanonWrapper and the CookieYes/Cookiebot listeners register in time.
31 */
32(function (window, document) {
33    'use strict';
34
35    if (window.__cmpBridgeInitialized) return;
36    window.__cmpBridgeInitialized = true;
37
38    var cfg = window.cmpBridgeConfig || {};
39    var provider = cfg.provider || 'internal';
40    var rolexEnabled = !!cfg.rolexEnabled;
41    var ROLEX_ORIGIN = 'https://cornersv7.Rolex.com';
42    var RLX_CONSENT = 'rlx-consent';
43    var RLX_MARKETING = 'rlx-marketing';
44    var COOKIE_DAYS = 365;
45
46    // --- gtag / dataLayer ---
47    window.dataLayer = window.dataLayer || [];
48    function gtag() { window.dataLayer.push(arguments); }
49
50    // GPC (Global Privacy Control) — "do not sell/share"; under CPRA governs
51    // advertising. When present we force advertising/marketing off.
52    function gpcActive() {
53        return navigator.globalPrivacyControl === true || window.__gpcSignal === true;
54    }
55
56    // ------------------------------------------------------------
57    // category_map: internal category -> provider's own category id/key.
58    // Falls back to sensible per-provider defaults when not configured.
59    // ------------------------------------------------------------
60    var PROVIDER_DEFAULT_MAP = {
61        cookieyes: { analytics: 'analytics', advertisement: 'advertisement', personalisation: 'functional' },
62        cookiebot: { analytics: 'statistics', advertisement: 'marketing', personalisation: 'preferences' }
63        // onetrust: intentionally none — group ids are account-specific (see info.txt),
64        // so they MUST come from the admin-configured category_map.
65    };
66
67    function buildCategoryMap() {
68        var map = {};
69        var configured = cfg.categoryMap || {};
70        var defaults = PROVIDER_DEFAULT_MAP[provider] || {};
71        // start from defaults, then let configured values win
72        var k;
73        for (k in defaults) { if (defaults.hasOwnProperty(k)) map[k] = String(defaults[k]); }
74        for (k in configured) {
75            if (configured.hasOwnProperty(k) && configured[k] !== '' && configured[k] != null) {
76                map[k] = String(configured[k]);
77            }
78        }
79        // 'functional' is the legacy alias of 'personalisation'
80        if (!map.personalisation && configured.functional) map.personalisation = String(configured.functional);
81        // Rolex/Adobe defaults to analytics; marketing defaults to advertisement.
82        if (!map.rolex && map.analytics) map.rolex = map.analytics;
83        if (!map.marketing && map.advertisement) map.marketing = map.advertisement;
84        return map;
85    }
86    var categoryMap = buildCategoryMap();
87    // True when marketing has no dedicated category and simply follows advertising;
88    // used to decide whether GPC ("do-not-sell") should also disable rlx-marketing.
89    var marketingFollowsAd = !!categoryMap.advertisement && categoryMap.marketing === categoryMap.advertisement;
90
91    // Given the set of provider categories the visitor has consented to, resolve
92    // the internal granted flags via category_map.
93    function resolveGranted(activeSet) {
94        var g = { advertisement: false, analytics: false, personalisation: false, marketing: false, rolex: false };
95        for (var internalKey in categoryMap) {
96            if (categoryMap.hasOwnProperty(internalKey) && activeSet[categoryMap[internalKey]]) {
97                g[internalKey] = true;
98            }
99        }
100        return g;
101    }
102
103    // ------------------------------------------------------------
104    // Cookie helpers
105    // ------------------------------------------------------------
106    function getCookie(name) {
107        var parts = ('; ' + document.cookie).split('; ' + name + '=');
108        return parts.length === 2 ? parts.pop().split(';').shift() : null;
109    }
110    function setCookie(name, value, days) {
111        var str = name + '=' + value + '; path=/; SameSite=Lax';
112        if (days) {
113            var d = new Date();
114            d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000);
115            str += '; expires=' + d.toUTCString();
116        }
117        document.cookie = str;
118    }
119
120    // ------------------------------------------------------------
121    // Rolex / Adobe (Content Square) events — mirrored from cmp.js
122    // ------------------------------------------------------------
123    function rolexCmpEvents(enable) {
124        var iframe = document.getElementById('rlx-corner');
125        if (enable) {
126            if (iframe && iframe.contentWindow) {
127                iframe.contentWindow.postMessage('consentTrue', ROLEX_ORIGIN);
128                iframe.contentWindow.postMessage('consentValidation', ROLEX_ORIGIN);
129            } else if (document.querySelector('.rolex_page')) {
130                if (typeof _satellite !== 'undefined' && _satellite.track) {
131                    _satellite.track('PageView');
132                }
133            }
134        } else if (iframe && iframe.contentWindow) {
135            iframe.contentWindow.postMessage('consentFalse', ROLEX_ORIGIN);
136        }
137    }
138
139    // ------------------------------------------------------------
140    // RS 2026-09-02
141    // SDKs re-enable only tags they blocked by URL, so inline tags stay dead after
142    // consent. The delay lets an SDK that activates its own tags go first.
143    // ------------------------------------------------------------
144    var ACTIVATION_DELAY = 1000;
145    var ACTIVATED_FLAG = 'data-cmp-activated';
146    var blockAttr = cfg.blockingAttribute || '';
147    var blockFormat = cfg.blockingFormat || '{CATEGORY}';
148    var srcAttr = cfg.srcAttribute || 'data-src';
149
150    function isExecutableType(type) {
151        if (!type) return true;
152        type = type.toLowerCase();
153        return type.indexOf('javascript') !== -1 || type === 'module';
154    }
155
156    // `class` (OneTrust: class="optanon-category-C0008") can hold several tokens.
157    function carriesToken(node, token) {
158        var value = node.getAttribute(blockAttr);
159        if (!value) return false;
160        return value === token || (' ' + value + ' ').indexOf(' ' + token + ' ') !== -1;
161    }
162
163    function activateNode(node, token) {
164        var replacement = document.createElement('script');
165        var attrs = node.attributes;
166        for (var i = 0; i < attrs.length; i++) {
167            if (attrs[i].name === 'type' || attrs[i].name === srcAttr) continue;
168            try { replacement.setAttribute(attrs[i].name, attrs[i].value); } catch (e) {}
169        }
170        var suppressedSrc = node.getAttribute(srcAttr);
171        if (suppressedSrc) replacement.src = suppressedSrc;
172        else if (node.src) replacement.src = node.src;
173        else replacement.text = node.text;
174        node.setAttribute(ACTIVATED_FLAG, '1');
175        // Drop the token so an in-place SDK pass cannot run this node again.
176        var left = (' ' + node.getAttribute(blockAttr) + ' ').replace(' ' + token + ' ', ' ').trim();
177        if (left) node.setAttribute(blockAttr, left);
178        else node.removeAttribute(blockAttr);
179        if (node.parentNode) node.parentNode.insertBefore(replacement, node.nextSibling);
180    }
181
182    function activateBlockedScripts(granted) {
183        if (!blockAttr) return;
184        var tokens = [];
185        for (var internalKey in categoryMap) {
186            if (categoryMap.hasOwnProperty(internalKey) && granted[internalKey]) {
187                tokens.push(blockFormat.replace('{CATEGORY}', categoryMap[internalKey]));
188            }
189        }
190        if (!tokens.length) return;
191        var nodes = document.getElementsByTagName('script');
192        var pending = [];
193        for (var i = 0; i < nodes.length; i++) {
194            if (nodes[i].hasAttribute(ACTIVATED_FLAG)) continue;
195            if (isExecutableType(nodes[i].getAttribute('type'))) continue;
196            for (var t = 0; t < tokens.length; t++) {
197                if (carriesToken(nodes[i], tokens[t])) { pending.push([nodes[i], tokens[t]]); break; }
198            }
199        }
200        for (var j = 0; j < pending.length; j++) {
201            try { activateNode(pending[j][0], pending[j][1]); } catch (e) {}
202        }
203    }
204
205    // ------------------------------------------------------------
206    // RS 2026-09-01
207    // GA4 sends one page_view, at gtag("config") time, and never repeats it on a consent
208    // change. The CMP reactivates blocked tags synchronously but dispatches its consent
209    // event a task later, so config always runs before the grant reaches gtag.
210    // ------------------------------------------------------------
211    function consentDefaults() {
212        var dl = window.dataLayer || [];
213        for (var i = 0; i < dl.length; i++) {
214            var a = dl[i];
215            if (a && a[0] === 'consent' && a[1] === 'default' && a[2]) return a[2];
216        }
217        return null;
218    }
219
220    function matchesDefaults(update) {
221        var defaults = consentDefaults();
222        if (!defaults) return false;
223        for (var k in update) {
224            if (update.hasOwnProperty(k) && defaults[k] !== update[k]) return false;
225        }
226        return true;
227    }
228
229    // gtag counts wait_for_update from when the tag loads, not from navigation start.
230    // 0 means the tag has not loaded and no page_view can have been sent.
231    function consentWaitEndsAt() {
232        var defaults = consentDefaults();
233        var wait = (defaults && typeof defaults.wait_for_update === 'number') ? defaults.wait_for_update : 0;
234        var loadedAt = 0;
235        try {
236            var res = performance.getEntriesByType('resource');
237            for (var i = 0; i < res.length; i++) {
238                if (/googletagmanager\.com\/(gtag\/js|gtm\.js)/.test(res[i].name)) {
239                    loadedAt = Math.max(loadedAt, res[i].responseEnd);
240                }
241            }
242        } catch (e) {}
243        return loadedAt ? loadedAt + wait : 0;
244    }
245
246    function ga4Configured() {
247        var dl = window.dataLayer || [];
248        for (var i = 0; i < dl.length; i++) {
249            if (dl[i] && dl[i][0] === 'config') return true;
250        }
251        return false;
252    }
253
254    // Inside the window gtag still holds the original page_view and will send it granted,
255    // so re-sending there would double count.
256    function resendPageViewIfLost() {
257        if (!ga4Configured()) return;
258        var defaults = consentDefaults();
259        if (!defaults || defaults.analytics_storage !== 'denied') return;
260        var endsAt = consentWaitEndsAt();
261        if (!endsAt) return;
262        try {
263            if (performance.now() <= endsAt) return;
264        } catch (e) { return; }
265        gtag('event', 'page_view');
266    }
267
268    // ------------------------------------------------------------
269    // Core: apply resolved consent across every layer
270    // ------------------------------------------------------------
271    var lastSig = null;
272    var initialApplyDone = false;
273    var analyticsGranted = false;
274
275    function applyConsent(granted) {
276        granted = granted || {};
277        var ad = !!granted.advertisement;
278        var an = !!granted.analytics;
279        var fn = !!granted.personalisation;
280        var mkt = (granted.marketing !== undefined) ? !!granted.marketing : ad;
281        var rlx = (granted.rolex !== undefined) ? !!granted.rolex : an;
282
283        // GPC ("do-not-sell/share", CPRA) governs advertising/targeting only. It
284        // forces the advertisement signal off, but NOT analytics-natured consent
285        // such as the Adobe/Content Square (Rolex) category that drives rlx-*.
286        if (gpcActive()) {
287            ad = false;
288            // If marketing follows the advertisement category (the default), honour
289            // GPC for it too; if it is mapped to its own (e.g. Adobe/Content Square,
290            // analytics-natured) category, GPC does not apply.
291            if (marketingFollowsAd) mkt = false;
292        }
293
294        var sig = [ad, an, fn, mkt, rlx].join('|');
295        if (sig === lastSig) return; // CMPs often fire several events per change
296        lastSig = sig;
297        var firstApply = !initialApplyDone;
298        initialApplyDone = true;
299
300        // 1. Google Consent Mode v2
301        var update = {
302            ad_storage: ad ? 'granted' : 'denied',
303            ad_user_data: ad ? 'granted' : 'denied',
304            ad_personalization: ad ? 'granted' : 'denied',
305            analytics_storage: an ? 'granted' : 'denied',
306            functionality_storage: fn ? 'granted' : 'denied',
307            personalization_storage: fn ? 'granted' : 'denied',
308            security_storage: 'granted'
309        };
310        // An update that only restates the defaults spends the wait window for nothing.
311        if (!(firstApply && matchesDefaults(update))) {
312            gtag('consent', 'update', update);
313            gtag('set', 'ads_data_redaction', ad ? false : true);
314        }
315        if (an && !analyticsGranted) resendPageViewIfLost();
316        analyticsGranted = an;
317
318        // 2. Facebook Pixel
319        if (typeof window.fbq === 'function') {
320            try { window.fbq('consent', ad ? 'grant' : 'revoke'); } catch (e) {}
321        }
322
323        // 3. Rolex / Adobe (only on Rolex-enabled sites)
324        if (rolexEnabled) {
325            setCookie(RLX_MARKETING, mkt ? 'true' : 'false', COOKIE_DAYS);
326            setCookie(RLX_CONSENT, rlx ? 'true' : 'false', COOKIE_DAYS);
327            rolexCmpEvents(rlx);
328        }
329
330        // 4. Let the rest of the site react (lazy widgets, Rolex iframe, etc.)
331        var resolved = { advertisement: ad, analytics: an, personalisation: fn, marketing: mkt, rolex: rlx };
332        try {
333            document.dispatchEvent(new CustomEvent('cmp_bridge_consent', {
334                detail: { granted: resolved }
335            }));
336        } catch (e) {}
337
338        // 5. Launch the tags the SDK left blocked - RS 2026-09-02
339        window.setTimeout(function () { activateBlockedScripts(resolved); }, ACTIVATION_DELAY);
340    }
341
342    function applyFromActiveSet(activeSet) {
343        applyConsent(resolveGranted(activeSet));
344    }
345
346    // Turn a list of provider categories into a lookup object.
347    function toSet(list) {
348        var s = {};
349        if (list && list.length !== undefined) {
350            for (var i = 0; i < list.length; i++) {
351                var v = ('' + list[i]).replace(/:.*/, '').trim(); // strip OneTrust ":1" sub-state
352                if (v) s[v] = true;
353            }
354        }
355        return s;
356    }
357
358    // ============================================================
359    // Adapter: CookieYes
360    // ============================================================
361    function cookieYesActiveSet(detail) {
362        // Live API is most reliable on returning visits.
363        if (typeof window.getCkyConsent === 'function') {
364            var c = window.getCkyConsent();
365            var cats = (c && c.categories) || {};
366            var s = {};
367            for (var key in cats) { if (cats.hasOwnProperty(key) && cats[key]) s[key] = true; }
368            return s;
369        }
370        // Fall back to event payload: detail.accepted is an array of CY keys.
371        if (detail && detail.accepted) return toSet(detail.accepted);
372        return null;
373    }
374    function wireCookieYes() {
375        function handler(e) {
376            var s = cookieYesActiveSet(e && e.detail);
377            if (s) applyFromActiveSet(s);
378        }
379        document.addEventListener('cookieyes_consent_update', handler);
380        document.addEventListener('cookieyes_banner_load', handler);
381    }
382
383    // ============================================================
384    // Adapter: OneTrust
385    // ============================================================
386    function oneTrustActiveSet(source) {
387        if (Object.prototype.toString.call(source) === '[object Array]') return toSet(source);
388        if (typeof source === 'string') return toSet(source.split(','));
389        if (typeof window.OnetrustActiveGroups === 'string') return toSet(window.OnetrustActiveGroups.split(','));
390        return {};
391    }
392    function wireOneTrust() {
393        // Fires on load and on every consent change; detail = active-group array.
394        window.addEventListener('OneTrustGroupsUpdated', function (e) {
395            applyFromActiveSet(oneTrustActiveSet(e && e.detail));
396        });
397        // OptanonWrapper is OneTrust's load + consent-change callback. Chain it so
398        // we don't clobber whatever the manual SDK snippet defined (info.txt shows
399        // the snippet defines an empty OptanonWrapper).
400        var previous = window.OptanonWrapper;
401        window.OptanonWrapper = function () {
402            if (typeof previous === 'function') {
403                try { previous.apply(this, arguments); } catch (e) {}
404            }
405            applyFromActiveSet(oneTrustActiveSet(window.OnetrustActiveGroups));
406        };
407    }
408
409    // ============================================================
410    // Adapter: Cookiebot (best-effort)
411    // ============================================================
412    function cookiebotActiveSet() {
413        var c = window.Cookiebot && window.Cookiebot.consent;
414        if (!c) return null;
415        var s = {};
416        ['preferences', 'statistics', 'marketing'].forEach(function (k) { if (c[k]) s[k] = true; });
417        return s;
418    }
419    function wireCookiebot() {
420        function handler() {
421            var s = cookiebotActiveSet();
422            if (s) applyFromActiveSet(s);
423        }
424        window.addEventListener('CookiebotOnAccept', handler);
425        window.addEventListener('CookiebotOnDecline', handler);
426        window.addEventListener('CookiebotOnConsentReady', handler);
427    }
428
429    // ============================================================
430    // Public manual API (internal/manual provider or custom code)
431    // ============================================================
432    window.cmpBridge = {
433        // accepts internal granted flags directly
434        update: function (granted) { lastSig = null; applyConsent(granted); },
435        grantAll: function () { lastSig = null; applyConsent({ advertisement: true, analytics: true, personalisation: true, marketing: true, rolex: true }); },
436        denyAll: function () { lastSig = null; applyConsent({ advertisement: false, analytics: false, personalisation: false, marketing: false, rolex: false }); },
437        // accepts a list of the active PROVIDER categories (e.g. ['2','3'])
438        fromActiveCategories: function (list) { applyFromActiveSet(toSet(list)); }
439    };
440
441    // ============================================================
442    // Init
443    // ============================================================
444    // Register listeners synchronously — the bridge loads before the SDK, so
445    // every adapter is armed before its CMP can fire its first event.
446    wireCookieYes();
447    wireOneTrust();
448    wireCookiebot();
449
450    // Fallback initial read, in case stored consent was restored and the load
451    // event fired before this script attached (defensive).
452    function initialSync() {
453        var s = null;
454        if (typeof window.getCkyConsent === 'function') s = cookieYesActiveSet(null);
455        else if (typeof window.OnetrustActiveGroups === 'string' && window.OnetrustActiveGroups.replace(/,/g, '').length) s = oneTrustActiveSet(window.OnetrustActiveGroups);
456        else if (window.Cookiebot && window.Cookiebot.consent) s = cookiebotActiveSet();
457        if (s) applyFromActiveSet(s);
458    }
459
460    if (document.readyState === 'complete') initialSync();
461    else window.addEventListener('load', initialSync);
462})(window, document);

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.