PageSourceSearch

https://www.nelomanga.net/vendor/widget/loader.js

js nelomanga.net collected 2026-10-06 17:50:49 UTC 4,003 bytes, 113 lines download raw bytes

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.