1/** 2 * Widget loader â first-party browser script for the publisher site. 3 * 4 * Fetches the site's current payload from the local data API exactly once, 5 * exposes each known type on the global window var its renderer core expects, 6 * then injects those cores (same-origin) so they render. Identifiers are 7 * deliberately neutral â no ad/banner tokens â to dodge adblock lists. 8 * 9 * Embed (cores are auto-injected â do NOT also include widget-core.js / 10 * popunder-core.js manually): 11 * 12 * <script src="/vendor/widget/loader.js" data-base="/api/widget" defer></script> 13 * 14 * Contract served by GET /api/widget/data: 15 * { version: <int>, items: { banner: {...}|null, popunder: {...}|null, ... } } 16 */ 17(function () { 18 "use strict"; 19 20 var self = document.currentScript; 21 var apiBase = ((self && self.getAttribute("data-base")) || "/api/widget").replace(/\/+$/, ""); 22 // Directory this script lives in â the renderer cores sit beside it. 23 var here = self ? self.src.replace(/\/[^/]+$/, "") : ""; 24 25 // Known types -> the global window var the matching renderer core reads. 26 // A metadata renderer can be appended here when that pipeline lands. 27 var types = [ 28 { key: "banner", global: "__WidgetConfig", src: here + "/widget-core.js" }, 29 { key: "popunder", global: "__PopConfig", src: here + "/popunder-core.js" } 30 ]; 31 32 function injectCores() { 33 for (var i = 0; i < types.length; i++) { 34 var s = document.createElement("script"); 35 s.src = types[i].src; 36 // Preserve declaration order so each core reads its window var 37 // after we have set it; the cores manage their own DOM scanning. 38 s.async = false; 39 document.body.appendChild(s); 40 } 41 } 42 43 function whenReady(fn) { 44 if (document.readyState !== "loading") { 45 fn(); 46 } else { 47 document.addEventListener("DOMContentLoaded", fn); 48 } 49 } 50 51 function apply(res) { 52 var items = res && res.items ? res.items : {}; 53 for (var i = 0; i < types.length; i++) { 54 var item = items[types[i].key]; 55 // A null/absent payload means "no ads of this type" â leave the 56 // window var unset so the core reads `window.__X || {}` and no-ops. 57 if (item) { 58 window[types[i].global] = item; 59 } 60 } 61 } 62 63 // Stale-while-revalidate: cache the last good payload so a transient 64 // /data failure (5xx, network blip) falls back to it instead of blanking 65 // the ad slots. Keyed by apiBase so distinct prefixes never collide. 66 var cacheKey = "__widget_data:" + apiBase; 67 68 function persist(res) { 69 try { 70 if (res && typeof res === "object") { 71 localStorage.setItem(cacheKey, JSON.stringify(res)); 72 } 73 } catch (e) { 74 // Private mode / quota exceeded â caching is best-effort. 75 } 76 } 77 78 function recall() { 79 try { 80 var raw = localStorage.getItem(cacheKey); 81 return raw ? JSON.parse(raw) : null; 82 } catch (e) { 83 return null; 84 } 85 } 86 87 fetch(apiBase + "/data", { credentials: "omit", cache: "default" }) 88 .then(function (r) { 89 return r.ok ? r.json() : null; 90 }) 91 .then(function (res) { 92 if (res) { 93 apply(res); 94 persist(res); 95 } else { 96 // Non-2xx: fall back to the last good payload (stale-while-revalidate). 97 var cached = recall(); 98 if (cached) { 99 apply(cached); 100 } 101 } 102 }) 103 .catch(function () { 104 // Network hiccup: same stale fallback before the cores run. 105 var cached = recall(); 106 if (cached) { 107 apply(cached); 108 } 109 }) 110 .finally(function () { 111 whenReady(injectCores); 112 }); 113})();
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.