1/* DAOP - tracciamento dei clic che GA4 da solo non vede. 2 * 3 * Perche' esiste come file esterno e non come blocco inline: il blocco inline 4 * viveva nelle pagine scritte a mano, e sulle ~280 pagine generate ricopiarlo 5 * avrebbe voluto dire 2 KB in piu' su ognuna e dodici copie da tenere 6 * allineate. Cosi' e' una richiesta sola, in cache dalla seconda pagina in poi. 7 * Dal 12/08/2026 le copie inline non ci sono piu': questo file e' l'unico 8 * posto in cui si scrivono i clic, come cookie-consent.js e' l'unico posto in 9 * cui si inizializza GA4. 10 * 11 * Cosa NON serve tracciare qui: la navigazione interna qualunque. Ogni pagina 12 * del sito manda gia' un page_view (lo fa cookie-consent.js dopo il consenso), 13 * quindi "da questa scheda dove vanno dopo" si legge gia' in GA4 con 14 * un'esplorazione di percorso. Qui stanno i clic che NON producono un 15 * page_view: quelli che portano fuori dal sito o che aprono un'app. 16 * 17 * L'ECCEZIONE, dal 14/09/2026: i blocchi marcati `data-cta`. Il page_view dice 18 * da quale PAGINA si arriva, non da quale BLOCCO, e non dice quante volte quel 19 * blocco e' stato visto - quindi "il link ai corsi in coda alla scheda rende 20 * o no?" non aveva risposta. Per quei blocchi, e solo per quelli, partono 21 * `internal_cta_view` (una volta per pagina, quando il blocco e' a schermo) e 22 * `internal_cta_click`. I nomi sono quelli proposti da Giovanni nell'analisi 23 * del 14/09, apposta: chi legge i report e' lui. 24 * 25 * gtag esiste solo dopo il consenso ai cookie: ogni chiamata e' protetta, e 26 * senza consenso lo script gira a vuoto senza errori. 27 */ 28(function () { 29 'use strict'; 30 31 if (window.__daopTrackAttivo) return; // due include = un solo tracciamento 32 window.__daopTrackAttivo = true; 33 34 /* ââ Contesto della pagina âââââââââââââââââââââââââââââââââââââââââââââ 35 I generatori stampano tre meta sulle pagine che parlano di un evento o 36 di un comune. Dove non ci sono (home, rubriche, libri...) i campi 37 restano vuoti e semplicemente non vengono mandati: GA4 non riceve 38 parametri con valore vuoto, cosi' i report non si riempiono di "(not 39 set)" sulle pagine per cui la domanda non ha senso. */ 40 function meta(nome) { 41 var m = document.querySelector('meta[name="' + nome + '"]'); 42 var v = m && m.getAttribute('content'); 43 return v ? v.trim() : ''; 44 } 45 46 var CTX = { 47 event_title: meta('daop:evento'), 48 event_city: meta('daop:citta'), 49 event_province: meta('daop:provincia') 50 }; 51 52 /* ââ Contesto della RIGA ââââââââââââââââââââââââââââââââââââââââââââââââ 53 I meta qui sopra descrivono la PAGINA, e su quasi tutto il sito basta: 54 una scheda evento parla di un evento solo. Su /corsi.html no â in una 55 pagina sola convivono N societa' e N corsi, quindi "chi ha ricevuto 56 questo clic" e' una domanda per elemento, non per documento. 57 58 La risposta sta gia' nel DOM: il generatore stampa `data-org` sia sulla 59 card del corso sia sulla scheda della realta' (e' lo stesso slug 60 dell'ancora #r-pgs-roccavione che si manda su WhatsApp), piu' 61 `data-org-nome` per il nome leggibile e `data-codice` per il codice del 62 foglio. Qui si risale l'albero e basta: nessun elenco da tenere 63 allineato, e un link che NON sta dentro una di quelle scatole â l'invito 64 alle societa' in fondo, per dire â non prende l'attribuzione di nessuno, 65 che e' il comportamento giusto. 66 67 A cosa serve: a rispondere a una realta' "quanti hanno aperto la tua 68 scheda, quanti sono andati sul tuo sito, quanti ti hanno chiamato" 69 senza ricostruire ogni volta l'organizzatore da `destination_url`. 70 71 `organizer_id` e' lo slug e NON deve mai cambiare: e' anche l'ancora che 72 gira nei messaggi, e in GA4 un id nuovo vuol dire una serie storica che 73 riparte da zero. Se nel foglio c'e' un CODICE, quello vince come 74 `course_id` proprio per questo â un corso che si rinomina non perde il 75 suo storico. */ 76 function contesto_riga(el) { 77 var out = {}; 78 if (!el || !el.closest) return out; 79 var box = el.closest('[data-org]'); 80 if (box) { 81 var oid = box.getAttribute('data-org') || ''; 82 var onome = box.getAttribute('data-org-nome') || ''; 83 if (oid) out.organizer_id = oid; 84 if (onome) out.organizer_name = onome; 85 } 86 var card = el.closest('.event-card[data-org]'); 87 if (card) { 88 var cid = card.getAttribute('data-codice') || card.id || ''; 89 if (cid) out.course_id = cid; 90 var n = card.querySelector('.ev-name'); 91 var testo = n && (n.textContent || '').trim(); 92 if (testo) out.course_name = testo; 93 } 94 /* Le schede di /luoghi.html. Stessi parametri dei corsi, e non una 95 dimensione nuova, perche' la domanda e' la stessa - "a quale realta' 96 va questo clic" - e le quattro dei corsi sono gia' registrate in GA4. 97 L'id non si stampa in un attributo: e' l'id della riga, che esiste gia' 98 (#lg-...), quindi 890 righe non pagano un byte in piu'. Le copie nel 99 riquadro Sponsorizzati hanno "-ev" in coda: si toglie, se no lo stesso 100 posto sarebbe due righe nei report. `posizione` dice da dove e' partito 101 il clic, ed e' la prova di quanto vale il riquadro a chi lo compra. */ 102 var lg = !box && el.closest('.lg-row'); 103 if (lg) { 104 var lid = (lg.id || '').replace(/-ev$/, ''); 105 if (lid) out.organizer_id = lid; 106 var ln = lg.querySelector('.lg-nome'); 107 var lt = ln && (ln.textContent || '').trim(); 108 if (lt) out.organizer_name = lt; 109 out.posizione = lg.closest('.lg-vetrina') ? 'sponsorizzati' : 'elenco'; 110 } 111 // La riga "Sponsorizzato" in coda a una scheda evento: il posto e' quello 112 // dell'ancora, cosi' il report lo mette sulla stessa riga delle aperture. 113 var sp = el.closest('.ev-spons'); 114 if (sp) { 115 var sa = sp.querySelector('a[href*="#lg-"]'); 116 var sid = sa && (sa.getAttribute('href') || '').split('#')[1]; 117 if (sid) out.organizer_id = sid; 118 if (sa && sa.textContent) out.organizer_name = sa.textContent.trim(); 119 out.posizione = 'scheda_evento'; 120 } 121 return out; 122 } 123 124 // Stesso motivo di cookie-consent.js: "/index.html" e "/" sono la stessa 125 // pagina e devono contare come una sola riga anche negli eventi di clic. 126 function percorso() { 127 return location.pathname.replace(/\/index\.html$/i, '/') || '/'; 128 } 129 130 /* In quale famiglia del sito porta un link interno, per `destination_area`. 131 `destination_url` c'e' gia' e dice la pagina esatta; questo raggruppa, cosi' 132 "quanti vanno ai corsi" non chiede un filtro scritto a mano in GA4. Torna 133 '' per un link che esce dal sito: quelli hanno gia' il loro evento. 134 `evento` e' la scheda singola, `eventi` qualunque elenco di eventi. */ 135 function area_destinazione(href) { 136 var u; 137 try { u = new URL(href, location.href); } catch (e) { return ''; } 138 if (u.host !== location.host && u.host.indexOf('daop.it') === -1) return ''; 139 // Aperta da file:// su Windows, "/corsi.html" si risolve in /C:/corsi.html 140 // (la trappola gia' scritta in tests/_aiuto.js): online non capita mai, ma 141 // senza questa riga la prova vedrebbe "altro" dove il sito dice "corsi". 142 var p = u.pathname.replace(/^\/[A-Za-z]:(?=\/)/, ''); 143 if (/^\/corsi(\.html$|\/)/.test(p)) return 'corsi'; 144 if (p === '/luoghi.html' || p === '/piscine.html') return 'luoghi'; 145 if (p === '/ginetto.html') return 'ginetto'; 146 if (p.indexOf('/centri-') === 0) return 'centri'; 147 if (p.indexOf('/eventi/comune/') === 0 || 148 /^\/eventi\/(oggi|weekend)[^/]*\.html$/.test(p)) return 'eventi'; 149 if (p.indexOf('/eventi/') === 0) return 'evento'; 150 if (p === '/eventi.html' || /^\/(sagre|eventi)-provincia-/.test(p)) return 'eventi'; 151 return 'altro'; 152 } 153 154 /* ââ Invio âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 155 `event_name` non viene mandato come parametro: in GA4 e' gia' il nome 156 dell'evento stesso (la dimensione "Nome evento"), e un parametro 157 personalizzato con lo stesso nome andrebbe a sbattere contro quella 158 dimensione. Chi legge il report lo vede comunque, in colonna. 159 `link_url` resta accanto a `destination_url` per non spezzare la serie 160 storica: le esplorazioni gia' salvate in GA4 leggono quel nome. */ 161 function track(nome, href, el) { 162 // `gtag` esiste sempre (e' lo stub che accoda): la domanda vera e' se 163 // l'utente ha acconsentito. Il flag lo mette cookie-consent.js. 164 if (!window.daopConsensoAnalytics || typeof gtag !== 'function') return; 165 var p = { page_path: percorso() }; 166 // `apri_corso` non porta da nessuna parte: mandare una destinazione vuota 167 // riempirebbe il report di "(not set)" sulla meta' degli eventi. 168 if (href) { p.destination_url = href; p.link_url = href; } 169 for (var k in CTX) { if (CTX[k]) p[k] = CTX[k]; } 170 var riga = contesto_riga(el); 171 for (var j in riga) { p[j] = riga[j]; } 172 /* Da quale blocco marcato e' partito il clic (vedi internal_cta_view in
173 avvia()). Vale anche per `apri_ginetto` e `click_sponsorizzato`: la 174 domanda "da quale posto della pagina" e' la stessa. */ 175 var blocco = el && el.closest && el.closest('[data-cta]'); 176 if (blocco) p.cta_id = blocco.getAttribute('data-cta'); 177 if (nome === 'internal_cta_click') p.destination_area = area_destinazione(href); 178 /* Quale guida. Sta in un attributo e non si deduce dal nome del file: 179 "estivi-2027.pdf" andrebbe spezzato in due qui dentro, e il giorno che il 180 nome cambia il report cambia in silenzio. Stessa ragione di 181 `organizer_name`. Va registrata in GA4 come dimensione evento prima di 182 pubblicare: non e' retroattiva. */ 183 if (el && el.getAttribute) { 184 var g = el.getAttribute('data-guida'); 185 if (g) { p.stagione = g; } 186 } 187 gtag('event', nome, p); 188 } 189 190 /* ââ Che cosa e' stato cliccato ââââââââââââââââââââââââââââââââââââââââ 191 L'ordine conta: i casi specifici stanno prima di quello generico, se no 192 una locandina su Supabase finirebbe in "sito organizzatore". */ 193 var IMMAGINE = /\.(jpe?g|png|webp|avif|gif)(\?.*)?$/i; 194 195 function nome_evento(href) { 196 if (href.indexOf('mailto:') === 0) return 'click_email'; 197 if (href.indexOf('tel:') === 0) return 'click_telefono'; 198 if (href.indexOf('ginettoapp.it') !== -1) return 'apri_ginetto'; 199 if (href.indexOf('gioco.sane-italia.it') !== -1) return 'gioca_ora'; 200 if (href.indexOf('instagram.com') !== -1 || 201 href.indexOf('facebook.com') !== -1 || 202 href.indexOf('youtube.com') !== -1) return 'click_social'; 203 // I tre sotto valgono soprattutto sulle pagine evento: dicono se la 204 // scheda ha fatto il suo mestiere. Chi apre la mappa o si segna la data 205 // sul calendario ci sta andando davvero, ed e' un segnale piu' forte di 206 // qualunque tempo sulla pagina. 207 if (href.indexOf('google.com/maps') !== -1) return 'click_come_arrivare'; 208 if (href.indexOf('calendar.google.com') !== -1) return 'aggiungi_calendario'; 209 if (href.indexOf('/storage/v1/object/public/locandine/') !== -1 || 210 href.indexOf('/assets/miniature/') !== -1) return 'click_locandina'; 211 /* La guida stagionale ha un nome suo e sta PRIMA del ramo generico dei 212 PDF. Nel secchio `scarica_materiale` finirebbe insieme a qualunque 213 allegato di qualunque organizzatore, e l'unica domanda che la guida pone 214 â quanti se la portano via ogni mille visite â si potrebbe rispondere 215 solo filtrando `destination_url` a mano, cioe' mai. E' esattamente il 216 difetto che l'invito al canale ha avuto per una settimana. */ 217 if (href.indexOf('/guide/') !== -1 && 218 href.slice(-4).toLowerCase() === '.pdf') return 'scarica_guida'; 219 if (href.slice(-4).toLowerCase() === '.pdf') return 'scarica_materiale'; 220 if (href.indexOf('amazon.') !== -1 || href.indexOf('amzn.') !== -1) return 'click_amazon'; 221 // Generico: qualunque altro link che porta fuori da daop.it. Sulle schede 222 // evento e' il sito di chi organizza, scritto nella descrizione o nei 223 // recapiti; e' l'unico posto da cui puo' arrivare un link esterno. 224 if (/^https?:\/\//i.test(href) && href.indexOf('daop.it') === -1) { 225 if (IMMAGINE.test(href)) return 'click_locandina'; 226 return 'click_sito_organizzatore'; 227 } 228 return null; 229 } 230 231 /* ââ Antirimbalzo ââââââââââââââââââââââââââââââââââââââââââââââââââââââ 232 Un clic solo deve valere un evento solo. Serve perche' su alcuni link 233 convivono piu' comportamenti (locandina.js apre il riquadro, il link 234 resta un href vero) e perche' un doppio clic involontario e' comune sui 235 telefoni. Stesso evento + stessa destinazione entro 800 ms: si scarta. */ 236 var ultimo = { chiave: '', quando: 0 }; 237 function invia(nome, href, el) { 238 // L'id della scatola entra nella chiave perche' `apri_corso` non ha una 239 // destinazione: senza, due corsi aperti in fila entro 800 ms sarebbero la 240 // stessa chiave e il secondo si perderebbe. 241 var box = el && el.closest && (el.closest('[data-org]') || el.closest('.lg-row')); 242 var chiave = nome + '|' + href + '|' + ((box && box.id) || ''); 243 var ora = Date.now(); 244 if (chiave === ultimo.chiave && ora - ultimo.quando < 800) return; 245 ultimo.chiave = chiave; 246 ultimo.quando = ora; 247 track(nome, href, el); 248 } 249 250 function avvia() { 251 /* Un ascoltatore solo, in delega sul document, invece di uno per ogni 252 <a>: su eventi.html i link sono ~1.500 e attaccarli a mano era lavoro 253 al caricamento sulla pagina piu' pesante del sito (11.000 nodi, il
254 punto dolente documentato in CLAUDE.md). In delega il costo e' zero e 255 vale anche per i link che compaiono dopo, senza riagganciare niente. */ 256 document.addEventListener('click', function (ev) { 257 var a = ev.target.closest && ev.target.closest('a[href]'); 258 if (a) { 259 var href = a.getAttribute('href') || ''; 260 // L'unico link INTERNO che si conta, e il perche' e' commerciale: e' 261 // uno spazio venduto, e a chi lo compra si deve poter dire quante 262 // volte e' stato toccato. Il page_view di /luoghi.html che segue non 263 // lo direbbe - non sa da quale riga di quale scheda si e' arrivati. 264 if (a.closest('.ev-spons')) { invia('click_sponsorizzato', href, a); return; } 265 var nome = nome_evento(href); 266 if (nome) { invia(nome, href, a); return; } 267 // Un link interno dentro un blocco marcato. Fuori da quei blocchi la 268 // navigazione interna resta affidata ai page_view, vedi in testa. 269 if (a.closest('[data-cta]') && area_destinazione(href)) { 270 invia('internal_cta_click', href, a); 271 } 272 return; 273 } 274 /* L'APERTURA DI UNA SCHEDA LUOGO, il denominatore che mancava a 275 /luoghi.html: GA4 contava i clic DALLA scheda (mappe, telefono, sito) 276 ma non quante volte era stata aperta, quindi a un cliente si poteva 277 dire "47 hanno chiesto le indicazioni" e non su quante. Stessa regola 278 di apri_corso qui sotto: al momento del capture il <details> e' ancora 279 nello stato vecchio, quindi `open` falso vuol dire che si sta aprendo, 280 e richiudere non conta. */ 281 var sum = ev.target.closest && ev.target.closest('.lg-row > summary'); 282 if (sum) { 283 if (!sum.parentElement.open) invia('apri_luogo', '', sum.parentElement); 284 return; 285 } 286 /* L'APERTURA DI UNA SCHEDA CORSO, che e' il denominatore di tutto il 287 resto. /corsi.html e' UNA pagina: il suo page_view dice "qualcuno ha 288 aperto l'elenco", non "qualcuno ha guardato i corsi della PGS 289 Roccavione". Senza questo evento a una realta' si potrebbe dire "3 290 clic al tuo sito" ma non su quante volte, che e' l'unica forma in cui 291 quel numero vuol dire qualcosa â lo stesso buco gia' scritto in 292 CLAUDE.md per le schede di luoghi.html. 293 294 Si conta solo l'APERTURA: al momento del capture `aria-expanded` ha 295 ancora il valore vecchio, quindi "false" vuol dire che sta aprendo. 296 Chiudere una riga non e' un secondo interessamento. 297 298 Il selettore chiede `data-org`, che oggi lo stampa il solo 299 genera_corsi.py: le ~300 schede evento hanno le stesse `.ev-row` e 300 contarle qui vorrebbe dire moltiplicare gli eventi su tutto il sito 301 per una domanda che li' nessuno ha fatto. */ 302 var apri = ev.target.closest && ev.target.closest('.event-card[data-org] .ev-row'); 303 if (apri) { 304 if (apri.getAttribute('aria-expanded') === 'false') invia('apri_corso', '', apri); 305 return; 306 } 307 // Sulle schede evento la locandina non e' dentro un link: e' l'immagine 308 // grande in colonna, che locandina.js apre nel riquadro. Senza questo 309 // ramo il clic piu' significativo della scheda non si vedrebbe. 310 var img = ev.target.closest && ev.target.closest('img.ev-loc'); 311 if (img) invia('click_locandina', img.getAttribute('src') || '', img); 312 }, true); 313 314 /* LA VISTA DI UN BLOCCO MARCATO (`data-cta`), una volta per pagina. 315 E' il denominatore del clic: 20 clic sul link ai corsi non dicono niente 316 se non si sa se il blocco l'hanno visto in cento o in diecimila. Prima 317 quel numero si stimava da scroll_depth e dall'altezza misurata a mano 318 del blocco; cosi' si legge. 319 320 "Visto" vuol dire meta' del blocco a schermo, oppure - per un blocco 321 piu' alto dello schermo - una fetta pari al 40% dello schermo. 322 323 Senza consenso non si manda e NON si segna come visto: il blocco resta 324 osservato, e al prossimo passaggio di soglia (basta scorrere) si 325 riprova. Segnarlo lo stesso vorrebbe dire perdere per sempre la vista di 326 chi accetta il banner dopo aver gia' visto il blocco, cioe' contare clic 327 senza la loro vista. 328 329 Costa zero sulle pagine che non hanno blocchi marcati: senza elementi 330 l'osservatore non nasce nemmeno. */ 331 var blocchi = document.querySelectorAll('[data-cta]'); 332 if (blocchi.length && 'IntersectionObserver' in window) { 333 var visti = {}; 334 var oss = new IntersectionObserver(function (voci) { 335 voci.forEach(function (v) { 336 if (!v.isIntersecting) return; 337 if (v.intersectionRatio < 0.5 && 338 v.intersectionRect.height < window.innerHeight * 0.4) return; 339 var id = v.target.getAttribute('data-cta'); 340 if (visti[id]) { oss.unobserve(v.target); return; } 341 if (!window.daopConsensoAnalytics || typeof gtag !== 'function') return; 342 visti[id] = true; 343 oss.unobserve(v.target); 344 var p = { page_path: percorso(), cta_id: id }; 345 for (var k in CTX) { if (CTX[k]) p[k] = CTX[k]; } 346 gtag('event', 'internal_cta_view', p); 347 }); 348 }, { threshold: [0, 0.25, 0.5, 0.75, 1] }); 349 [].forEach.call(blocchi, function (b) { oss.observe(b); }); 350 } 351 352 /* "Vicino a me". Il filtro per distanza (daop-vicino.js) non chiama gtag 353 da solo: emette un evento DOM e lo raccogliamo qui, cosi' gtag resta 354 scritto in un posto solo - la stessa regola per cui cookie-consent.js e' 355 l'unico posto in cui GA4 si inizializza. 356 357 NON si manda mai la posizione. Partono due cose sole: COME e' stato
358 scelto il centro (gps, comune, gradino) e QUANTI chilometri. Le 359 coordinate restano nel browser, che e' anche quello che la pagina 360 promette a chi la usa: mandarle a GA4 renderebbe quella riga una bugia. 361 362 A cosa serve misurarlo: a decidere se la funzione vale su altre pagine, 363 e soprattutto se chi la usa poi apre una scheda. Il resto del percorso 364 si legge gia' con i page_view. */ 365 document.addEventListener('daop:vicino', function (ev) { 366 var d = (ev && ev.detail) || {}; 367 var p = { page_path: percorso() }; 368 if (d.metodo) p.metodo_posizione = d.metodo; 369 if (d.raggio) p.raggio_km = d.raggio; 370 for (var k in CTX) { if (CTX[k]) p[k] = CTX[k]; } 371 // Stesso antirimbalzo dei clic: chi prova tre gradini di fila e' una 372 // persona che sta scegliendo, non tre eventi. 373 var chiave = 'vicino|' + d.metodo + '|' + d.raggio; 374 var ora = Date.now(); 375 if (chiave === ultimo.chiave && ora - ultimo.quando < 800) return; 376 ultimo.chiave = chiave; 377 ultimo.quando = ora; 378 if (!window.daopConsensoAnalytics || typeof gtag !== 'function') return; 379 gtag('event', 'vicino_a_me', p); 380 }); 381 382 /* La mappa di luoghi.html (daop-mappa.js). Come "vicino a me", il modulo 383 non chiama gtag: emette un evento DOM. Parte una volta per pagina, alla 384 prima apertura: e' il numero che dice se la mappa serve, e va letto 385 contro i page_view di /luoghi.html con la regola dei 2,5 per utente. */ 386 document.addEventListener('daop:mappa', function (ev) { 387 if (!window.daopConsensoAnalytics || typeof gtag !== 'function') return; 388 var d = (ev && ev.detail) || {}; 389 var p = { page_path: percorso() }; 390 if (d.tipo) p.destination_area = d.tipo; 391 for (var k in CTX) { if (CTX[k]) p[k] = CTX[k]; } 392 gtag('event', 'apri_mappa', p); 393 }); 394 395 /* Profondita' di scroll: evento a 25/50/75/100%. Su una scheda evento 396 dice se sono arrivati in fondo, dove stanno la firma e i link alle 397 landing. E' voluto e NON e' un doppione dell'evento automatico 398 `scroll` di GA4: quello scatta una volta sola, al 90%, e non distingue 399 chi si ferma a meta'. Se in GA4 si vogliono vedere solo questi, si 400 toglie "Scorrimenti" da Misurazione avanzata. */ 401 var soglie = [25, 50, 75, 100], raggiunte = {}, inCoda = false; 402 window.addEventListener('scroll', function () { 403 // Il calcolo legge scrollHeight/clientHeight, che forzano il layout: 404 // farlo a ogni evento di scroll su un documento da 11.000 nodi si 405 // sente. Una lettura per frame basta e avanza. 406 if (inCoda) return; 407 inCoda = true; 408 requestAnimationFrame(function () { 409 inCoda = false; 410 var h = document.documentElement; 411 var scrollabile = h.scrollHeight - h.clientHeight; 412 if (scrollabile <= 0) return; 413 var perc = (h.scrollTop || document.body.scrollTop) / scrollabile * 100; 414 soglie.forEach(function (s) { 415 if (perc >= s && !raggiunte[s]) { 416 raggiunte[s] = true; 417 if (window.daopConsensoAnalytics && typeof gtag === 'function') { 418 gtag('event', 'scroll_depth', { percent_scroll: s, page_path: percorso() }); 419 } 420 } 421 }); 422 }); 423 }, { passive: true }); 424 } 425 426 // Lo script e' caricato con defer, quindi il DOM c'e' gia'; il ramo 427 // DOMContentLoaded resta per sicurezza se qualcuno lo includesse senza defer. 428 if (document.readyState === 'loading') { 429 document.addEventListener('DOMContentLoaded', avvia); 430 } else { 431 avvia(); 432 } 433})();
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.