1/** 2 * Chile Consiente â banner de consentimiento (vanilla JS, sin dependencias). 3 * 4 * Cargado como archivo externo (sin inline) para no disparar HDR-CSP-WEAK. 5 * El estado se resuelve en cliente desde la cookie â funciona con caché de página. 6 * 7 * Flujo: 8 * 1. Lee la cookie. Si hay decisión vigente (misma versión de polÃtica) â 9 * aplica el estado (desbloquea scripts, Consent Mode update, wp_set_consent) 10 * y no muestra el banner. 11 * 2. Si no â muestra el banner. Aceptar/Rechazar/Configurar. 12 * 3. Al decidir â POST al endpoint (cookie autoritativa + log) y aplica el 13 * estado en memoria sin recargar. 14 */ 15( function () { 16 'use strict'; 17 18 var cfg = window.chileConsiente; 19 if ( !cfg || !cfg.categories ) { return; } 20 21 // CategorÃa (nuestra) â señales de Google Consent Mode v2. 22 var SIGNALS = { 23 analiticas: { analytics_storage: 1, personalization_storage: 1 }, 24 publicitarias: { ad_storage: 1, ad_user_data: 1, ad_personalization: 1 }, 25 funcionales: { functionality_storage: 1, personalization_storage: 1 } 26 }; 27 28 // ââ utilidades ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 29 function readCookie( name ) { 30 var m = document.cookie.match( '(?:^|; )' + name.replace( /([.$?*|{}()\[\]\\\/+^])/g, '\\$1' ) + '=([^;]*)' ); 31 if ( !m ) { return null; } 32 try { return JSON.parse( decodeURIComponent( m[1] ) ); } catch ( e ) { return null; } 33 } 34 35 function currentDecision() { 36 var c = readCookie( cfg.cookieName ); 37 if ( c && String( c.v ) === String( cfg.policyVersion ) && c.cats ) { return c; } 38 return null; 39 } 40 41 function el( tag, attrs, text ) { 42 var n = document.createElement( tag ); 43 if ( attrs ) { Object.keys( attrs ).forEach( function ( k ) { n.setAttribute( k, attrs[k] ); } ); } 44 if ( text != null ) { n.textContent = text; } 45 return n; 46 } 47 48 // ââ aplicar estado ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 49 function unblockScripts( cats ) { 50 var nodes = document.querySelectorAll( 'script[type="text/plain"][' + cfg.catAttr + ']' ); 51 nodes.forEach( function ( old ) { 52 var cat = old.getAttribute( cfg.catAttr ); 53 if ( !cats[cat] ) { return; } 54 var s = document.createElement( 'script' ); 55 for ( var i = 0; i < old.attributes.length; i++ ) { 56 var a = old.attributes[i]; 57 if ( a.name === 'type' || a.name === cfg.catAttr ) { continue; } 58 s.setAttribute( a.name, a.value ); 59 } 60 if ( old.textContent ) { s.textContent = old.textContent; } 61 old.parentNode.replaceChild( s, old ); 62 } ); 63 } 64 65 function consentModeUpdate( cats ) { 66 if ( typeof window.gtag !== 'function' ) { return; } 67 var upd = {}; 68 Object.keys( SIGNALS ).forEach( function ( cat ) { 69 var granted = !!cats[cat]; 70 Object.keys( SIGNALS[cat] ).forEach( function ( sig ) { 71 if ( granted ) { upd[sig] = 'granted'; } 72 else if ( !( sig in upd ) ) { upd[sig] = 'denied'; } 73 } ); 74 } ); 75 window.gtag( 'consent', 'update', upd ); 76 } 77 78 function wpConsentApi( cats ) { 79 if ( typeof window.wp_set_consent !== 'function' ) { return; } 80 Object.keys( cfg.catMap ).forEach( function ( cat ) { 81 var mapped = cfg.catMap[cat]; 82 var val = ( cat === 'necesarias' || cats[cat] ) ? 'allow' : 'deny'; 83 try { window.wp_set_consent( mapped, val ); } catch ( e ) {} 84 } ); 85 } 86 87 function applyState( cats ) { 88 consentModeUpdate( cats ); 89 wpConsentApi( cats ); 90 unblockScripts( cats ); 91 } 92 93 // ââ persistencia ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 94 // Aplica el estado en el acto y guarda la decisión en el servidor (cookie 95 // autoritativa + log). Transporte: REST y, si falla (sitios que restringen 96 // la REST a usuarios autenticados), fallback a admin-ajax. 97 function persist( cats ) { 98 applyState( cats ); 99 sendConsent( cats ); 100 hideBanner(); 101 } 102 103 function postJson( url, body ) { 104 try { 105 return fetch( url, { 106 method: 'POST', 107 credentials: 'same-origin', 108 headers: { 'Content-Type': 'application/json' }, 109 body: body 110 } ).then( function ( r ) { return !!( r && r.ok ); } ).catch( function () { return false; } ); 111 } catch ( e ) { 112 return Promise.resolve( false ); 113 } 114 } 115 116 function sendConsent( cats ) { 117 var body = JSON.stringify( { choices: cats } ); 118 postJson( cfg.restUrl, body ).then( function ( ok ) { 119 if ( ok || !cfg.ajaxUrl ) { return; } 120 var sep = cfg.ajaxUrl.indexOf( '?' ) === -1 ? '?' : '&'; 121 var url = cfg.ajaxUrl + sep + 'action=' + encodeURIComponent( cfg.ajaxAction || 'chc_consent' ); 122 postJson( url, body ); 123 } ); 124 } 125 126 function allCats( value ) { 127 var c = {};
128 cfg.categories.forEach( function ( cat ) { c[cat.key] = cat.required ? true : value; } ); 129 return c; 130 } 131 132 // ââ UI ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 133 var root = document.getElementById( 'chc-root' ); 134 135 function hideBanner() { if ( root ) { root.classList.add( 'chc-hidden' ); root.innerHTML = ''; } } 136 137 function buildPanel() { 138 var rows = el( 'div', { 'class': 'chc-cats' } ); 139 cfg.categories.forEach( function ( cat ) { 140 var row = el( 'label', { 'class': 'chc-cat' } ); 141 var cb = el( 'input', { type: 'checkbox', value: cat.key } ); 142 if ( cat.required ) { cb.checked = true; cb.disabled = true; } 143 row.appendChild( cb ); 144 var txt = el( 'span', { 'class': 'chc-cat-txt' } ); 145 txt.appendChild( el( 'strong', null, cat.label ) ); 146 txt.appendChild( el( 'span', { 'class': 'chc-cat-desc' }, cat.desc ) ); 147 row.appendChild( txt ); 148 rows.appendChild( row ); 149 } ); 150 return rows; 151 } 152 153 function showBanner() { 154 if ( !root ) { return; } 155 root.innerHTML = ''; 156 var box = el( 'div', { 'class': 'chc-banner', role: 'document' } ); 157 box.appendChild( el( 'h2', { 'class': 'chc-title' }, cfg.texts.title ) ); 158 var body = el( 'p', { 'class': 'chc-body' }, cfg.texts.body ); 159 if ( cfg.policyUrl ) { 160 body.appendChild( document.createTextNode( ' ' ) ); 161 body.appendChild( el( 'a', { 'class': 'chc-policy-link', href: cfg.policyUrl }, cfg.texts.policy ) ); 162 } 163 box.appendChild( body ); 164 165 var panel = buildPanel(); 166 panel.classList.add( 'chc-hidden' ); 167 box.appendChild( panel ); 168 169 var actions = el( 'div', { 'class': 'chc-actions' } ); 170 var bReject = el( 'button', { type: 'button', 'class': 'chc-btn chc-btn--reject' }, cfg.texts.reject ); 171 var bConfig = el( 'button', { type: 'button', 'class': 'chc-btn chc-btn--config' }, cfg.texts.config ); 172 var bAccept = el( 'button', { type: 'button', 'class': 'chc-btn chc-btn--accept' }, cfg.texts.accept ); 173 var bSave = el( 'button', { type: 'button', 'class': 'chc-btn chc-btn--save chc-hidden' }, cfg.texts.save ); 174 175 bAccept.addEventListener( 'click', function () { persist( allCats( true ) ); } ); 176 bReject.addEventListener( 'click', function () { persist( allCats( false ) ); } ); 177 bConfig.addEventListener( 'click', function () { 178 panel.classList.toggle( 'chc-hidden' ); 179 bSave.classList.toggle( 'chc-hidden' ); 180 } ); 181 bSave.addEventListener( 'click', function () { 182 var c = {}; 183 cfg.categories.forEach( function ( cat ) { 184 if ( cat.required ) { c[cat.key] = true; return; } 185 var cb = panel.querySelector( 'input[value="' + cat.key + '"]' ); 186 c[cat.key] = !!( cb && cb.checked ); 187 } ); 188 persist( c ); 189 } ); 190 191 actions.appendChild( bReject ); 192 actions.appendChild( bConfig ); 193 actions.appendChild( bAccept ); 194 actions.appendChild( bSave ); 195 box.appendChild( actions ); 196 197 root.appendChild( box ); 198 root.classList.remove( 'chc-hidden' ); 199 } 200 201 // Reabrir desde el shortcode/enlace de footer. 202 document.addEventListener( 'click', function ( e ) { 203 var t = e.target; 204 if ( t && t.closest && t.closest( '[data-chc-reopen]' ) ) { e.preventDefault(); showBanner(); } 205 } ); 206 207 // ââ arranque ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 208 var decision = currentDecision(); 209 if ( decision ) { applyState( decision.cats ); } 210 else { showBanner(); } 211} )();
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.