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.