PageSourceSearch

https://dejvicka34.cz/bundles/dejvicka/scripts/consent-guard.js?v=3

js dejvicka34.cz collected 2026-10-05 01:34:30 UTC 9,479 bytes, 225 lines download raw bytes

1/**
2 * Hlídač souhlasu pro skripty, které injektuje Google Tag Manager
3 * – dejvicka34.cz
4 *
5 * PROČ EXISTUJE
6 * Facebook Pixel se do stránky nedostává z našeho kódu ani z databáze, ale
7 * z kontejneru GTM za běhu. Nereaguje na Google Consent Mode, takže bez
8 * zásahu nastaví cookie _fbp ještě dřív, než návštěvník cokoli odklikne.
9 * Do GTM nemáme přístup, takže se to musí ošetřit tady.
10 *
11 * JAK TO FUNGUJE
12 * Hlídáme vkládání <script> tagů (insertBefore / appendChild) a element
13 * mířící na Facebook zadržíme, dokud není souhlas s marketingem. Zadržuje se
14 * celý element, ne jen jeho URL – GTM na něj věší onload callback, ve kterém
15 * teprve volá fbq('init', …). Po udělení souhlasu vložíme původní element do
16 * hlavičky, callback doběhne a Pixel se chová normálně.
17 *
18 * Tenhle kontejner vkládá Pixel vlastním injektorem (script má nonce), takže
19 * obvyklý trik s `if (f.fbq) return;` na něj nestačí. Stub fbq níž je jen
20 * pojistka pro případ, že by někdo do GTM doplnil standardní base snippet.
21 *
22 * Sklik navíc po udělení souhlasu dostane tentýž zásah ještě jednou,
23 * už s consent: 1 – jeho tag v GTM se totiž spouští na All Pages a sám
24 * by se po souhlasu už nespustil.
25 *
26 * OVĚŘENO: bez tohoto souboru vzniká cookie _fbp ještě před jakoukoli volbou
27 * a přežije i „Odmítnout vše“.
28 *
29 * MUSÍ SE NAČÍST SYNCHRONNĚ A PŘED GTM. Bez `defer`, bez `async`.
30 */
31(function (window, document) {
32    'use strict';
33
34    // Hostitelé, ze kterých se bez souhlasu s marketingem nesmí načíst skript.
35    // Sklik (c.seznam.cz) tu schválně NENÍ – ten se má odeslat i bez souhlasu,
36    // jen s hodnotou consent: 0. Řeší se níž v sekci Sklik.
37    var BLOCKED_HOSTS = ['connect.facebook.net', 'facebook.net', 'facebook.com'];
38
39    function hostBlocked(src) {
40        try {
41            var host = new URL(src, location.href).hostname.toLowerCase();
42            for (var i = 0; i < BLOCKED_HOSTS.length; i++) {
43                var b = BLOCKED_HOSTS[i];
44                if (host === b || host.slice(-(b.length + 1)) === '.' + b) return true;
45            }
46        } catch (e) { /* relativní nebo rozbitá URL – nechat projít */ }
47        return false;
48    }
49
50    var FB_SRC = 'https://connect.facebook.net/en_US/fbevents.js';
51
52    var state = { marketing: false, fbLoaded: false };
53    var held = [];
54
55    // ---------------------------------------------------------------- souhlas
56    // Přečteme uloženou volbu rovnou z cookie cookieconsentu, ať vracející se
57    // návštěvník se souhlasem nečeká, než nabootuje lišta.
58    function storedMarketingConsent() {
59        try {
60            var m = document.cookie.match(/(?:^|;\s*)cc_cookie=([^;]*)/);
61            if (!m) return false;
62            var data = JSON.parse(decodeURIComponent(m[1]));
63            return !!(data && data.level && data.level.indexOf('marketing') !== -1);
64        } catch (e) {
65            return false;
66        }
67    }
68
69    // ------------------------------------------------------------ fbq stub
70    // Tvarem odpovídá oficiálnímu base kódu Meta Pixelu, jen bez vložení
71    // skriptu. Díky tomu snippet z GTM skončí na `if (f.fbq) return;`.
72    if (!window.fbq) {
73        var fbq = window.fbq = function () {
74            fbq.callMethod
75                ? fbq.callMethod.apply(fbq, arguments)
76                : fbq.queue.push(arguments);
77        };
78        if (!window._fbq) window._fbq = fbq;
79        fbq.push = fbq;
80        fbq.loaded = true;
81        fbq.version = '2.0';
82        fbq.queue = [];
83    }
84
85    function loadFacebook() {
86        if (state.fbLoaded) return;
87        state.fbLoaded = true;
88        var s = document.createElement('script');
89        s.async = true;
90        s.src = FB_SRC;
91        (document.head || document.documentElement).appendChild(s);
92    }
93
94    // ------------------------------------------------- pojistka: vkládání DOM
95    var nativeInsertBefore = Node.prototype.insertBefore;
96    var nativeAppendChild = Node.prototype.appendChild;
97
98    function blocks(node) {
99        return !state.marketing
100            && node
101            && node.nodeType === 1
102            && node.tagName === 'SCRIPT'
103            && node.src
104            && hostBlocked(node.src);
105    }
106
107    // Zadržený element si schováváme celý, ne jen jeho src. GTM na něj věší
108    // onload callback a v něm volá fbq('init', …) – kdybychom po souhlasu
109    // vytvořili nový element, callback by se nespustil a Pixel by se nikdy
110    // neinicializoval.
111    Node.prototype.insertBefore = function (node) {
112        if (blocks(node)) { held.push(node); return node; }
113        return nativeInsertBefore.apply(this, arguments);
114    };
115
116    Node.prototype.appendChild = function (node) {
117        if (blocks(node)) { held.push(node); return node; }
118        return nativeAppendChild.apply(this, arguments);
119    };
120
121    function releaseHeld() {
122        Node.prototype.insertBefore = nativeInsertBefore;
123        Node.prototype.appendChild = nativeAppendChild;
124        var target = document.head || document.documentElement;
125        held.splice(0).forEach(function (node) {
126            if (node.src && node.src.indexOf('fbevents.js') !== -1) state.fbLoaded = true;
127            nativeAppendChild.call(target, node);
128        });
129    }
130
131    // ------------------------------------------------------------------ Sklik
132    // Sklik čte souhlas z pole `consent` (1 = souhlas, 0 = bez souhlasu,
133    // -1 = nezjištěno). Tag v GTM ho plní proměnnou, kterou tam nikdo
134    // nenastavil, takže odesílal -1. Zabalíme retargetingHit tak, aby tam
135    // vždycky doplnil skutečnou hodnotu – bez ohledu na to, co pošle GTM.
136    var _rc;
137    var lastHit = null;        // poslední conf, se kterým GTM volalo retargetingHit
138    var hitReplayed = false;   // přehrát smíme jen jednou za načtení stránky
139
140    function shallowCopy(o) {
141        var out = {};
142        for (var k in o) {
143            if (Object.prototype.hasOwnProperty.call(o, k)) out[k] = o[k];
144        }
145        return out;
146    }
147
148    function wrapRc(obj) {
149        if (obj && typeof obj.retargetingHit === 'function' && !obj.retargetingHit.__dot11) {
150            var original = obj.retargetingHit;
151            var patched = function (conf) {
152                try {
153                    conf = conf || {};
154                    conf.consent = state.marketing ? 1 : 0;
155                    lastHit = shallowCopy(conf);   // ať víme, co přehrát
156                } catch (e) { /* radši odeslat, než spadnout */ }
157                return original.call(this, conf);
158            };
159            patched.__dot11 = true;
160            obj.retargetingHit = patched;
161        }
162        return obj;
163    }
164
165    /**
166     * Sklik tag má v GTM trigger All Pages, takže odpálí hned při načtení –
167     * v tu chvíli souhlas ještě není a odejde consent: 0. Když návštěvník
168     * vzápětí klikne na „Přijmout vše“, GTM už nic znovu nespustí a Seznam
169     * by z téhle návštěvy jedničku nikdy nedostal. Přehrajeme mu proto
170     * tentýž zásah jednou znovu, už se souhlasem.
171     *
172     * Přehrává se jen když nějaký zásah opravdu proběhl – rtgId si
173     * nevymýšlíme. Vracející se návštěvník se souhlasem sem nedojde:
174     * setMarketing(true) u něj běží dřív, než GTM tag vůbec spustí, takže
175     * první a jediný zásah rovnou nese jedničku.
176     */
177    function replaySklikHit() {
178        if (hitReplayed || !lastHit) return;
179        var rc = window.rc;
180        if (!rc || typeof rc.retargetingHit !== 'function') return;
181
182        hitReplayed = true;
183        var conf = shallowCopy(lastHit);
184        conf.consent = 1;   // wrapper to přepíše podle stavu, tady jen explicitně
185        try { rc.retargetingHit(conf); } catch (e) { /* nevadí, jde o bonus */ }
186    }
187    try {
188        Object.defineProperty(window, 'rc', {
189            configurable: true,
190            get: function () { return wrapRc(_rc); },
191            set: function (value) { _rc = value; }
192        });
193    } catch (e) { /* starý prohlížeč – Sklik pojede jako dřív */ }
194
195    // ------------------------------------------------------------- veřejné API
196    window.dot11Consent = {
197        /**
198         * Volá se z cookieconsent-config.js při každé změně souhlasu.
199         * @param {boolean} marketing
200         */
201        setMarketing: function (marketing) {
202            var granted = !!marketing;
203            if (granted === state.marketing && state.fbLoaded === granted) return;
204            state.marketing = granted;
205
206            if (granted) {
207                if (window.fbq) window.fbq('consent', 'grant');
208                releaseHeld();
209                loadFacebook();   // pojistka, kdyby GTM nic nezadrzel
210                replaySklikHit();
211            } else {
212                if (window.fbq && state.fbLoaded) window.fbq('consent', 'revoke');
213                // _fbp a _fbc maže cookieconsent přes autoclear_cookies,
214                // protože jsou vypsané v tabulce marketingové kategorie.
215            }
216        },
217        isMarketingAllowed: function () { return state.marketing; }
218    };
219
220    // Vracející se návštěvník se souhlasem – nastavíme rovnou, ať Pixel
221    // nečeká na doběhnutí lišty.
222    if (storedMarketingConsent()) {
223        window.dot11Consent.setMarketing(true);
224    }
225})(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.