PageSourceSearch

https://adaas.ai/shared/js/adaas-api.js

js adaas.ai collected 2026-10-05 14:36:33 UTC 5,663 bytes, 126 lines download raw bytes

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 { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[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.