1/* =========================================================================== 2 ADAAS shared client. Loaded on every interior (non-landing) page. 3 - Resolves the API base (api.adaas.org, or window.ADAAS_API in dev). 4 - Thin fetch helpers per content type. 5 - Theme persistence + toggle wiring. 6 - Loader hide + SEO meta injection from API payloads. 7 =========================================================================== */ 8(function () { 9 "use strict"; 10 11 // API base: <meta name="adaas-api"> wins, then window.ADAAS_API, then prod. 12 var meta = document.querySelector('meta[name="adaas-api"]'); 13 var BASE = 14 (meta && meta.content) || 15 window.ADAAS_API || 16 "https://api.adaas.org"; 17 BASE = BASE.replace(/\/$/, ""); 18 19 function get(path) { 20 return fetch(BASE + path, { headers: { Accept: "application/json" } }).then( 21 function (r) { 22 if (!r.ok) throw new Error("HTTP " + r.status + " for " + path); 23 return r.json(); 24 } 25 ); 26 } 27 28 var api = { 29 base: BASE, 30 network: function () { return get("/v1/network"); }, 31 services: function (category) { 32 return get("/v1/services" + (category ? "?category=" + encodeURIComponent(category) : "")); 33 }, 34 service: function (code) { return get("/v1/services/" + encodeURIComponent(code)); }, 35 blog: function (domain) { return get("/v1/blog?domain=" + encodeURIComponent(domain || "adaas.org")); }, 36 post: function (slug, domain) { 37 return get("/v1/blog/" + encodeURIComponent(slug) + "?domain=" + encodeURIComponent(domain || "adaas.org")); 38 }, 39 products: function () { return get("/v1/products"); }, 40 product: function (id) { return get("/v1/products/" + encodeURIComponent(id)); }, 41 courses: function () { return get("/v1/courses"); }, 42 course: function (id) { return get("/v1/courses/" + encodeURIComponent(id)); }, 43 // Next round-robin ad for a placement, filtered by site + size + type. 44 // Pass { pinned: 1 } to fetch a featured creative instead. 45 ad: function (opts) { 46 opts = opts || {}; 47 var q = []; 48 if (opts.site) q.push("site=" + encodeURIComponent(opts.site)); 49 if (opts.size) q.push("size=" + encodeURIComponent(opts.size)); 50 if (opts.type) q.push("type=" + encodeURIComponent(opts.type)); 51 if (opts.pinned) q.push("pinned=1"); 52 return get("/v1/ads" + (q.length ? "?" + q.join("&") : "")); 53 } 54 }; 55 56 // ---- Theme ------------------------------------------------------------- 57 function applyTheme(t) { 58 document.documentElement.setAttribute("data-theme", t); 59 try { localStorage.setItem("adaas-theme", t); } catch (e) {} 60 } 61 function initTheme() { 62 var saved; 63 try { saved = localStorage.getItem("adaas-theme"); } catch (e) {} 64 var t = saved || (window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark"); 65 applyTheme(t); 66 document.addEventListener("click", function (e) { 67 var btn = e.target.closest && e.target.closest(".tbtn"); 68 if (btn) applyTheme(btn.getAttribute("data-set")); 69 }); 70 } 71 72 // ---- Loader ------------------------------------------------------------ 73 function hideLoader() { 74 var l = document.getElementById("app-loader"); 75 if (l) setTimeout(function () { l.classList.add("is-hidden"); }, 120); 76 } 77 78 // ---- SEO injection ----------------------------------------------------- 79 function setMeta(seo, fallbackTitle) { 80 if (!seo) seo = {}; 81 if (seo.title || fallbackTitle) document.title = (seo.title || fallbackTitle) + " · ADAAS"; 82 function tag(sel, attr, val) { 83 if (!val) return; 84 var el = document.head.querySelector(sel); 85 if (!el) { 86 el = document.createElement("meta"); 87 var parts = sel.replace(/meta\[|\]/g, "").split("="); 88 el.setAttribute(parts[0], parts[1].replace(/["']/g, "")); 89 document.head.appendChild(el); 90 } 91 el.setAttribute(attr, val); 92 } 93 tag('meta[name="description"]', "content", seo.description); 94 if (seo.keywords && seo.keywords.length) tag('meta[name="keywords"]', "content", seo.keywords.join(", ")); 95 tag('meta[property="og:title"]', "content", seo.title || fallbackTitle); 96 tag('meta[property="og:description"]', "content", seo.description); 97 if (seo.ogImage) tag('meta[property="og:image"]', "content", seo.ogImage); 98 } 99 100 // ---- Error rendering --------------------------------------------------- 101 function fail(container, msg) { 102 if (!container) return; 103 container.innerHTML = 104 '<div style="padding:40px 0;color:var(--ink-dim);font-family:var(--font-body)">' + 105 '<p style="font-family:var(--font-display);font-weight:700;font-size:20px;color:var(--ink-strong);margin:0 0 8px">Couldn\u2019t load this yet</p>' + 106 '<p style="margin:0">' + (msg || "The content service didn\u2019t respond.") + 107 ' Refresh to try again.</p></div>'; 108 } 109 110 function esc(s) { 111 return String(s == null ? "" : s).replace(/[&<>"']/g, function (c) { 112 return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]; 113 }); 114 } 115 116 function money(p) { 117 if (!p) return "On request"; 118 if (p.model === "quote") return "On request"; 119 var n = new Intl.NumberFormat("en-US", { style: "currency", currency: p.currency || "USD", maximumFractionDigits: 0 }); 120 var pre = p.model === "from" ? "From " : p.model === "retainer" ? "Retainer · from " : ""; 121 return pre + n.format(p.from) + (p.to ? "\u2013" + n.format(p.to) : ""); 122 } 123 124 window.ADAAS = { api: api, applyTheme: applyTheme, initTheme: initTheme, hideLoader: hideLoader, setMeta: setMeta, fail: fail, esc: esc, money: money }; 125 initTheme(); 126})();
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.