1(function() { 2 'use strict'; 3 4 // Abort if config missing 5 if (typeof grimfasteConfig === 'undefined') return; 6 7 var config = grimfasteConfig; 8 var queue = []; 9 10 // Scroll depth tracking state 11 var maxScrollDepth = 0; 12 13 // Time-on-page tracking state 14 var activeTimeStart = Date.now(); 15 var totalActiveTime = 0; 16 var isPageVisible = true; 17 18 // Adaptive intervals based on traffic tier (configured from server) 19 // Defaults to 'default' tier (30 min) if not specified 20 var TRAFFIC_TIER = config.trafficTier || 'default'; 21 var TIER_SETTINGS = { 22 realtime: { interval: 5000, batchSize: 10, flushOnUnload: true }, // 5s, real-time tracking 23 high: { interval: 30000, batchSize: 5, flushOnUnload: true }, // 30s, high traffic 24 medium: { interval: 60000, batchSize: 3, flushOnUnload: true }, // 60s, medium traffic 25 low: { interval: 120000, batchSize: 2, flushOnUnload: true }, // 2min, low traffic 26 default: { interval: 1800000, batchSize: 1, flushOnUnload: true } // 30min, default (cost-optimized) 27 }; 28 29 var tierConfig = TIER_SETTINGS[TRAFFIC_TIER] || TIER_SETTINGS.default; 30 var BATCH_INTERVAL = tierConfig.interval; 31 var BATCH_SIZE = tierConfig.batchSize; 32 33 // Generate or retrieve session ID 34 function getSessionId() { 35 var sessionId = sessionStorage.getItem('grimfaste_session_id'); 36 if (!sessionId) { 37 sessionId = 'sess_' + Date.now().toString(36) + '_' + Math.random().toString(36).substr(2, 9); 38 sessionStorage.setItem('grimfaste_session_id', sessionId); 39 } 40 return sessionId; 41 } 42 43 var sessionId = getSessionId(); 44 45 function getCookie(name) { 46 var parts = document.cookie ? document.cookie.split('; ') : []; 47 for (var i = 0; i < parts.length; i++) { 48 var part = parts[i]; 49 var eq = part.indexOf('='); 50 var key = eq >= 0 ? part.slice(0, eq) : part; 51 if (key === name) { 52 return decodeURIComponent(eq >= 0 ? part.slice(eq + 1) : ''); 53 } 54 } 55 return ''; 56 } 57 58 function setCookie(name, value, days) { 59 var cookie = name + '=' + encodeURIComponent(value) + '; path=/; SameSite=Lax'; 60 if (days && days > 0) { 61 cookie += '; max-age=' + Math.round(days * 86400); 62 } 63 if (window.location.protocol === 'https:') { 64 cookie += '; Secure'; 65 } 66 document.cookie = cookie; 67 } 68 69 function campaignCookieKey(prefix, externalKey) { 70 return prefix + encodeURIComponent(externalKey || '').replace(/%/g, '_'); 71 } 72 73 // Amazon domains 74 var amazonDomains = [ 75 'amazon.com', 'amazon.de', 'amazon.co.uk', 'amazon.fr', 76 'amazon.it', 'amazon.es', 'amazon.ca', 'amazon.com.au', 77 'amazon.co.jp', 'amazon.in', 'amzn.to', 'amzn.eu', 'a.co' 78 ]; 79 80 // Check if URL is external 81 function isExternalLink(url) { 82 try { 83 var linkHost = new URL(url).hostname; 84 return linkHost !== window.location.hostname; 85 } catch (e) { 86 return false; 87 } 88 } 89 90 // Check if URL is Amazon 91 function isAmazonLink(url) { 92 try { 93 var hostname = new URL(url).hostname.toLowerCase(); 94 for (var i = 0; i < amazonDomains.length; i++) { 95 if (hostname === amazonDomains[i] || hostname.endsWith('.' + amazonDomains[i])) { 96 return true; 97 } 98 } 99 return false; 100 } catch (e) { 101 return false; 102 } 103 } 104 105 // Get common event data 106 function getCommonData() { 107 return { 108 url: window.location.href, 109 title: document.title, 110 referrer: document.referrer || undefined, 111 userAgent: navigator.userAgent, 112 sessionId: sessionId 113 }; 114 } 115 116 // Throttle helper 117 function throttle(fn, delay) { 118 var lastCall = 0; 119 return function() { 120 var now = Date.now(); 121 if (now - lastCall >= delay) {
122 lastCall = now; 123 fn(); 124 } 125 }; 126 } 127 128 // Update max scroll depth 129 var updateScrollDepth = throttle(function() { 130 var scrollTop = window.pageYOffset || document.documentElement.scrollTop || 0; 131 var windowHeight = window.innerHeight || document.documentElement.clientHeight || 0; 132 var docHeight = Math.max( 133 document.body.scrollHeight || 0, 134 document.documentElement.scrollHeight || 0, 135 document.body.offsetHeight || 0, 136 document.documentElement.offsetHeight || 0 137 ); 138 if (docHeight > 0) { 139 var depth = Math.round((scrollTop + windowHeight) / docHeight * 100); 140 if (depth > maxScrollDepth) { 141 maxScrollDepth = Math.min(depth, 100); 142 } 143 } 144 }, 200); 145 146 // Get accumulated active time in milliseconds 147 function getActiveTime() { 148 var elapsed = totalActiveTime; 149 if (isPageVisible) { 150 elapsed += Date.now() - activeTimeStart; 151 } 152 return elapsed; 153 } 154 155 // Pause/resume active time tracking 156 function pauseActiveTime() { 157 if (isPageVisible) { 158 totalActiveTime += Date.now() - activeTimeStart; 159 isPageVisible = false; 160 } 161 } 162 163 function resumeActiveTime() { 164 if (!isPageVisible) { 165 activeTimeStart = Date.now(); 166 isPageVisible = true; 167 } 168 } 169 170 // Extract WordPress search query from URL 171 function getSearchQuery() { 172 try { 173 var params = new URLSearchParams(window.location.search); 174 return params.get('s') || undefined; 175 } catch (e) { 176 return undefined; 177 } 178 } 179 180 // Extract the newsletter campaign tag (utm_campaign) from the landing URL. 181 // Owned-audience (#03): only meaningful for email-sourced visits, but we send 182 // it whenever present so the backend can join an email landing view to a 183 // specific newsletter campaign. Additive + optional. 184 function getUtmCampaign() { 185 try { 186 var params = new URLSearchParams(window.location.search); 187 var campaign = params.get('utm_campaign'); 188 return campaign ? campaign.slice(0, 200) : undefined; 189 } catch (e) { 190 return undefined; 191 } 192 } 193 194 // Convert internal event shape to backend batch format 195 function serializeEvent(eventType, data) { 196 var normalizedType = eventType === 'pageview' ? 'pageView' : eventType; 197 var event = { 198 type: normalizedType, 199 url: data.url, 200 title: data.title, 201 referrer: data.referrer, 202 userAgent: data.userAgent, 203 sessionId: data.sessionId, 204 timestamp: Date.now() 205 }; 206 if (config.isTest) { 207 event.isTest = true; // A6: Owner-Testmodus (gf_debug) 208 } 209 210 if (normalizedType === 'pageView') { 211 var searchQuery = getSearchQuery(); 212 if (searchQuery) { 213 event.searchQuery = searchQuery; 214 } 215 var utmCampaign = getUtmCampaign(); 216 if (utmCampaign) { 217 event.utmCampaign = utmCampaign; 218 } 219 } 220 221 if (normalizedType === 'click') { 222 event.clickedUrl = data.clickedUrl; 223 event.isAmazon = !!data.isAmazon; 224 event.amazonAsin = data.amazonAsin; 225 if (data.campaignExternalKey) { 226 event.campaignExternalKey = data.campaignExternalKey; 227 event.campaignBoxType = data.campaignBoxType || data.boxType || ''; 228 } 229 if (data.boxType) { 230 event.boxType = data.boxType; 231 event.boxAsin = data.boxAsin || ''; 232 event.boxTemplate = data.boxTemplate || ''; 233 } 234 if (data.boxSource) event.boxSource = data.boxSource; 235 if (data.boxPosition) event.boxPosition = data.boxPosition; 236 } 237 238 if (normalizedType === 'update') { 239 event.scrollDepth = data.scrollDepth; 240 event.timeOnPage = data.timeOnPage; 241 } 242 243 return event; 244 } 245 246 // Send batched events 247 function sendBatch(events, useBeacon) { 248 var url = config.apiUrl.replace(/\/$/, '') + '/api/track'; 249 var payload = JSON.stringify({ 250 siteApiKey: config.apiKey, 251 events: events 252 }); 253 254 // Use beacon on unload paths when available. 255 if (useBeacon && typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') { 256 try { 257 var blob = new Blob([payload], { type: 'text/plain' }); 258 if (navigator.sendBeacon(url, blob)) { 259 return; 260 } 261 } catch (e) {} 262 } 263 264 if (typeof fetch !== 'undefined') { 265 fetch(url, { 266 method: 'POST', 267 headers: { 'Content-Type': 'application/json' }, 268 body: payload, 269 keepalive: true 270 }).catch(function() {}); 271 } 272 } 273 274 // Queue event 275 function queueEvent(type, data) { 276 var eventData = Object.assign({}, getCommonData(), data); 277 queue.push(serializeEvent(type, eventData)); 278 279 if (queue.length >= BATCH_SIZE) { 280 flushQueue(false); 281 } 282 } 283 284 // Flush queue 285 function flushQueue(useBeacon) { 286 if (queue.length === 0) return; 287 288 var events = queue.splice(0, queue.length); 289 sendBatch(events, useBeacon); 290 } 291 292 // Queue a final update event with scroll depth and time on page 293 function queueUpdateEvent() { 294 queueEvent('update', { 295 scrollDepth: maxScrollDepth, 296 timeOnPage: getActiveTime() 297 }); 298 } 299 300 // Track pageview 301 function trackPageview() { 302 queueEvent('pageview', {}); 303 } 304 305 var campaignGeoRequested = false;
306 var campaignImpressionKeys = {}; 307 var campaignImpressionQueue = {}; 308 var campaignImpressionTimer = null; 309 var campaignObserver = null; 310 311 function getCampaignBoxes() { 312 return Array.prototype.slice.call(document.querySelectorAll('.grimfaste-campaign-wrap[data-gf-campaign]')); 313 } 314 315 function getCampaignExternalKey(box) { 316 return box ? (box.getAttribute('data-gf-campaign') || '') : ''; 317 } 318 319 function closestCampaignBox(node) { 320 while (node && node.getAttribute) { 321 if (node.classList && node.classList.contains('grimfaste-campaign-wrap')) { 322 return node; 323 } 324 node = node.parentElement; 325 } 326 return null; 327 } 328 329 function isCampaignSuppressed(box) { 330 return box && box.getAttribute('data-gf-suppressed') === '1'; 331 } 332 333 function setCampaignSuppressed(box) { 334 if (!box) return; 335 box.setAttribute('data-gf-suppressed', '1'); 336 box.hidden = true; 337 } 338 339 function isCampaignVisible(box) { 340 if (!box || box.hidden || isCampaignSuppressed(box)) return false; 341 if (!box.getClientRects || box.getClientRects().length === 0) return false; 342 var style = window.getComputedStyle ? window.getComputedStyle(box) : null; 343 return !style || (style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0'); 344 } 345 346 function applyCampaignDismissState(box) { 347 var key = getCampaignExternalKey(box); 348 if (!key) return; 349 350 if (getCookie(campaignCookieKey('gf_camp_', key))) { 351 setCampaignSuppressed(box); 352 return; 353 } 354 355 var cap = parseInt(box.getAttribute('data-gf-seen-cap') || '0', 10); 356 if (cap > 0) { 357 var seen = parseInt(getCookie(campaignCookieKey('gf_camp_seen_', key)) || '0', 10); 358 if (seen >= cap) { 359 setCampaignSuppressed(box); 360 } 361 } 362 } 363 364 function recordCampaignSeen(box) { 365 var key = getCampaignExternalKey(box); 366 if (!key) return; 367 368 var cap = parseInt(box.getAttribute('data-gf-seen-cap') || '0', 10); 369 if (cap <= 0) return; 370 371 var days = parseInt(box.getAttribute('data-gf-seen-days') || '7', 10); 372 var cookieName = campaignCookieKey('gf_camp_seen_', key); 373 var seen = parseInt(getCookie(cookieName) || '0', 10); 374 setCookie(cookieName, String(seen + 1), days > 0 ? days : 7); 375 } 376 377 function applyCampaignGeo(country) { 378 var boxes = getCampaignBoxes(); 379 country = (country || '').toUpperCase(); 380 381 for (var i = 0; i < boxes.length; i++) { 382 var box = boxes[i]; 383 var geo = box.getAttribute('data-gf-geo'); 384 if (!geo || isCampaignSuppressed(box)) continue; 385 386 var allowed = geo.toUpperCase().split(/\s*,\s*/); 387 if (country && allowed.indexOf(country) !== -1) { 388 box.hidden = false; 389 } else { 390 box.hidden = true; 391 } 392 } 393 } 394 395 function initCampaignGeo() { 396 var geoBoxes = document.querySelectorAll('.grimfaste-campaign-wrap[data-gf-geo]'); 397 if (!geoBoxes.length) return; 398 399 var cachedCountry = getCookie('gf_geo_country'); 400 if (cachedCountry) { 401 applyCampaignGeo(cachedCountry); 402 return; 403 } 404 405 if (campaignGeoRequested || typeof fetch === 'undefined') return; 406 campaignGeoRequested = true; 407 408 fetch(config.apiUrl.replace(/\/$/, '') + '/api/geo', { 409 method: 'GET', 410 credentials: 'omit' 411 }).then(function(response) { 412 return response && response.ok ? response.json() : null; 413 }).then(function(body) { 414 var country = body && body.country ? String(body.country).toUpperCase() : ''; 415 if (!country) return; 416 setCookie('gf_geo_country', country); 417 applyCampaignGeo(country); 418 }).catch(function() {}); 419 } 420 421 function queueCampaignImpression(box) { 422 var key = getCampaignExternalKey(box); 423 if (!key || campaignImpressionKeys[key] || !isCampaignVisible(box)) return; 424 425 campaignImpressionKeys[key] = true; 426 campaignImpressionQueue[key] = (campaignImpressionQueue[key] || 0) + 1; 427 recordCampaignSeen(box); 428 429 if (!campaignImpressionTimer) { 430 campaignImpressionTimer = setTimeout(function() { 431 flushCampaignImpressions(false); 432 }, 250); 433 } 434 } 435 436 function flushCampaignImpressions(useBeacon) { 437 if (campaignImpressionTimer) { 438 clearTimeout(campaignImpressionTimer); 439 campaignImpressionTimer = null; 440 } 441 442 var impressions = []; 443 for (var key in campaignImpressionQueue) { 444 if (Object.prototype.hasOwnProperty.call(campaignImpressionQueue, key)) { 445 impressions.push({ externalKey: key, count: campaignImpressionQueue[key] }); 446 } 447 } 448 449 if (!impressions.length) return; 450 campaignImpressionQueue = {}; 451 452 var url = config.apiUrl.replace(/\/$/, '') + '/api/campaigns/impression'; 453 var payload = JSON.stringify({ impressions: impressions }); 454 455 if (typeof fetch !== 'undefined') { 456 fetch(url, { 457 method: 'POST', 458 headers: { 459 'Content-Type': 'application/json', 460 'X-API-Key': config.apiKey 461 }, 462 body: payload, 463 keepalive: true 464 }).catch(function() {}); 465 return; 466 } 467 468 if (useBeacon && typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') { 469 try { 470 navigator.sendBeacon(url, new Blob([payload], { type: 'text/plain' })); 471 } catch (e) {} 472 } 473 } 474 475 function initCampaignImpressions() { 476 var boxes = getCampaignBoxes(); 477 if (!boxes.length) return; 478 479 if (typeof IntersectionObserver === 'undefined') { 480 setTimeout(function() { 481 for (var i = 0; i < boxes.length; i++) { 482 queueCampaignImpression(boxes[i]); 483 } 484 }, 500); 485 return; 486 } 487 488 campaignObserver = new IntersectionObserver(function(entries) { 489 for (var i = 0; i < entries.length; i++) { 490 var entry = entries[i]; 491 if (entry.isIntersecting && entry.intersectionRatio > 0) { 492 queueCampaignImpression(entry.target); 493 if (campaignImpressionKeys[getCampaignExternalKey(entry.target)]) { 494 campaignObserver.unobserve(entry.target); 495 } 496 } 497 } 498 }, { threshold: 0.25 }); 499 500 for (var j = 0; j < boxes.length; j++) { 501 campaignObserver.observe(boxes[j]); 502 } 503 } 504 505 function initCampaignBoxes() { 506 var boxes = getCampaignBoxes(); 507 if (!boxes.length) return; 508 509 for (var i = 0; i < boxes.length; i++) { 510 applyCampaignDismissState(boxes[i]); 511 } 512 513 document.addEventListener('click', function(event) { 514 var target = event.target; 515 while (target && (!target.getAttribute || !target.getAttribute('data-gf-dismiss-campaign'))) { 516 target = target.parentElement; 517 } 518 if (!target || !target.getAttribute) return; 519 520 var key = target.getAttribute('data-gf-dismiss-campaign'); 521 var box = target.closest ? target.closest('.grimfaste-campaign-wrap') : closestCampaignBox(target); 522 if (key) {
523 setCookie(campaignCookieKey('gf_camp_', key), '1', 30); 524 } 525 setCampaignSuppressed(box); 526 }, true); 527 528 initCampaignGeo(); 529 initCampaignImpressions(); 530 } 531 532 /* ââ Wave-1 A1 (2026-07-27): product-box impressions ââââââââââââââââââ 533 * Mirrors the campaign impression beacon for ALL Grimfaste product-box 534 * wrappers, keyed by template|source|position. This is the denominator 535 * that makes box CTR computable server-side (boxImpressionDaily). */ 536 var boxImpressionQueue = {}; 537 var boxImpressionTimer = null; 538 var boxObserver = null; 539 540 function getProductBoxes() { 541 return Array.prototype.slice.call( 542 document.querySelectorAll('.grimfaste-products-wrap') 543 ); 544 } 545 546 function getBoxTemplate(box) { 547 var m = (box.className || '').match(/grimfaste-template-([a-z0-9_-]+)/); 548 return m ? m[1] : 'unknown'; 549 } 550 551 function queueBoxImpression(box) { 552 if (!box || box.getAttribute('data-gf-imp') === '1') return; 553 box.setAttribute('data-gf-imp', '1'); 554 555 var key = getBoxTemplate(box) 556 + '|' + (box.getAttribute('data-gf-source') || 'unknown') 557 + '|' + (box.getAttribute('data-gf-pos') || 'unknown'); 558 boxImpressionQueue[key] = (boxImpressionQueue[key] || 0) + 1; 559 560 if (!boxImpressionTimer) { 561 boxImpressionTimer = setTimeout(function() { 562 flushBoxImpressions(false); 563 }, 250); 564 } 565 } 566 567 function flushBoxImpressions(useBeacon) { 568 if (boxImpressionTimer) { 569 clearTimeout(boxImpressionTimer); 570 boxImpressionTimer = null; 571 } 572 573 var impressions = []; 574 for (var key in boxImpressionQueue) { 575 if (Object.prototype.hasOwnProperty.call(boxImpressionQueue, key)) { 576 var parts = key.split('|'); 577 impressions.push({ 578 template: parts[0], 579 source: parts[1], 580 position: parts[2], 581 count: boxImpressionQueue[key] 582 }); 583 } 584 } 585 586 if (!impressions.length) return; 587 boxImpressionQueue = {}; 588 589 var url = config.apiUrl.replace(/\/$/, '') + '/api/boxes/impression'; 590 var payload = JSON.stringify({ impressions: impressions }); 591 592 if (typeof fetch !== 'undefined') { 593 fetch(url, { 594 method: 'POST', 595 headers: { 596 'Content-Type': 'application/json', 597 'X-API-Key': config.apiKey 598 }, 599 body: payload, 600 keepalive: true 601 }).catch(function() {}); 602 return; 603 } 604 605 if (useBeacon && typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') { 606 try { 607 navigator.sendBeacon(url, new Blob([payload], { type: 'text/plain' })); 608 } catch (e) {} 609 } 610 } 611 612 function initBoxImpressions() { 613 var boxes = getProductBoxes(); 614 if (!boxes.length) return; 615 616 if (typeof IntersectionObserver === 'undefined') { 617 setTimeout(function() { 618 for (var i = 0; i < boxes.length; i++) { 619 queueBoxImpression(boxes[i]); 620 } 621 }, 500); 622 return; 623 } 624 625 boxObserver = new IntersectionObserver(function(entries) { 626 for (var i = 0; i < entries.length; i++) { 627 var entry = entries[i]; 628 if (entry.isIntersecting && entry.intersectionRatio > 0) { 629 queueBoxImpression(entry.target); 630 boxObserver.unobserve(entry.target); 631 } 632 } 633 }, { threshold: 0.25 }); 634 635 for (var j = 0; j < boxes.length; j++) { 636 boxObserver.observe(boxes[j]); 637 } 638 } 639 640 // Handle click events 641 function handleClick(event) { 642 var target = event.target; 643 644 // Find anchor element 645 while (target && target.tagName !== 'A') { 646 target = target.parentElement; 647 } 648 649 if (!target || !target.href) return; 650 651 var url = target.href; 652 653 // Only track external links 654 if (!isExternalLink(url)) return; 655 656 var isAmazon = isAmazonLink(url); 657 var eventData = { 658 articleUrl: window.location.href, 659 clickedUrl: url, 660 isAmazon: isAmazon 661 }; 662 663 var boxType = target.getAttribute('data-grimfaste-box'); 664 if (boxType) { 665 eventData.boxType = boxType; 666 eventData.boxAsin = target.getAttribute('data-grimfaste-asin') || ''; 667 eventData.boxTemplate = target.getAttribute('data-grimfaste-template') || ''; 668 eventData.campaignExternalKey = target.getAttribute('data-grimfaste-campaign') || ''; 669 eventData.campaignBoxType = target.getAttribute('data-grimfaste-campaign-type') || boxType; 670 // Wave-1 A2: cascade source + placement live on the box wrapper. 671 var gfWrap = target.closest ? target.closest('.grimfaste-products-wrap') : null; 672 if (gfWrap) { 673 eventData.boxSource = gfWrap.getAttribute('data-gf-source') || ''; 674 eventData.boxPosition = gfWrap.getAttribute('data-gf-pos') || ''; 675 } 676 677 // Fire local tracking pixel for box clicks 678 if (config.localTrackUrl) { 679 var img = new Image(); 680 img.src = config.localTrackUrl 681 + '&nonce=' + encodeURIComponent(config.localTrackNonce || '') 682 + '&block_type=' + encodeURIComponent(boxType) 683 + '&asin=' + encodeURIComponent(eventData.boxAsin) 684 + '&template=' + encodeURIComponent(eventData.boxTemplate) 685 + '&post_id=' + encodeURIComponent(config.pageData.postId || ''); 686 } 687 } 688 689 queueEvent('click', eventData); 690 691 // Flush immediately for Amazon links 692 if (isAmazon) { 693 flushQueue(true); 694 } 695 } 696 697 // Initialize tracker
698 function init() { 699 initCampaignBoxes(); 700 initBoxImpressions(); 701 702 // Track pageview 703 trackPageview(); 704 705 // Listen for clicks 706 document.addEventListener('click', handleClick, true); 707 708 // Listen for scroll to track max depth 709 window.addEventListener('scroll', updateScrollDepth, { passive: true }); 710 // Capture initial scroll depth (e.g. page loaded scrolled or short page) 711 updateScrollDepth(); 712 713 // Periodic flush 714 setInterval(function() { 715 flushQueue(false); 716 }, BATCH_INTERVAL); 717 718 // Flush on visibility change + time tracking 719 document.addEventListener('visibilitychange', function() { 720 if (document.visibilityState === 'hidden') { 721 pauseActiveTime(); 722 queueUpdateEvent(); 723 flushQueue(true); 724 flushCampaignImpressions(true); 725 flushBoxImpressions(true); 726 } else { 727 resumeActiveTime(); 728 } 729 }); 730 731 // Flush on page unload 732 window.addEventListener('pagehide', function() { 733 pauseActiveTime(); 734 queueUpdateEvent(); 735 flushQueue(true); 736 flushCampaignImpressions(true); 737 flushBoxImpressions(true); 738 }); 739 740 window.addEventListener('beforeunload', function() { 741 pauseActiveTime(); 742 queueUpdateEvent(); 743 flushQueue(true); 744 flushCampaignImpressions(true); 745 flushBoxImpressions(true); 746 }); 747 } 748 749 // Start tracking 750 if (document.readyState === 'loading') { 751 document.addEventListener('DOMContentLoaded', init); 752 } else { 753 init(); 754 } 755})();
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.