*
* If no #mcl-market-ticker element exists, the widget inserts itself right
* after this
var sel = thisScript && thisScript.getAttribute("data-target");
if (sel) {
var t = document.querySelector(sel);
if (t) return t;
}
// 2) A dedicated mount element the host page provides.
var el = document.getElementById(CONFIG.targetId);
if (el) return el;
// 3) Fallback: insert right after this script tag.
el = document.createElement("div");
el.id = CONFIG.targetId;
if (thisScript && thisScript.parentNode) {
thisScript.parentNode.insertBefore(el, thisScript.nextSibling);
} else {
document.body.appendChild(el);
}
return el;
}
// ---- Data pipeline (identical logic to the standalone banner) ------------
function apiUrl(path) {
if (!path) return path;
if (/^https?:\/\//i.test(path)) return path;
return CONFIG.apiBase + (path.charAt(0) === "/" ? "" : "/") + path;
}
function toNum(v) { var n = parseFloat(String(v == null ? "" : v).replace(/,/g, "")); return isFinite(n) ? n : null; }
function fmtNum(v) {
var n = parseFloat(String(v).replace(/,/g, ""));
if (!isFinite(n)) return String(v == null ? "" : v);
var d = Math.abs(n) >= 1 ? 2 : 4;
return n.toLocaleString("en-US", { minimumFractionDigits: d, maximumFractionDigits: d });
}
function changeClass(d) { if (d == null || Math.abs(d) < 1e-9) return "flat"; return d > 0 ? "up" : "down"; }
function arrow(d) { if (d == null || Math.abs(d) < 1e-9) return "▬"; return d > 0 ? "▲" : "▼"; }
async function fetchFiles(category, count) {
var res = await fetch(apiUrl("/api/files?category=" + category));
if (!res.ok) throw new Error("list " + category + " " + res.status);
var list = await res.json();
return Array.isArray(list) ? list.slice(0, count || 1) : [];
}
async function parseSheet(meta) {
var res = await fetch(apiUrl(meta.url));
if (!res.ok) throw new Error("download " + res.status);
var buf = await res.arrayBuffer();
var wb = XLSX.read(buf, { type: "array" });
return XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], { defval: "" });
}
function buildStocks(rows) {
var items = [];
rows.forEach(function (r) {
var sym = r["Symbol"] || r["symbol"]; if (!sym) return;
var close = toNum(r["Close"]); if (close == null) return;
var prev = toNum(r["Prev Close"] || r["Previous Close"]);
var delta = prev != null ? close - prev : null;
var pct = (delta != null && prev) ? (delta / prev) * 100 : null;
items.push({ sym: sym, val: fmtNum(close), delta: delta, pct: pct });
});
return items;
}
function buildForex(rows, prevRows) {
var pmean = {};
(prevRows || []).forEach(function (r) { if (r["Currency"]) pmean[r["Currency"]] = toNum(r["Mean"]); });
var items = [];
rows.forEach(function (r) {
var cur = r["Currency"]; if (!cur) return;
var mean = toNum(r["Mean"]), buy = toNum(r["Buying"]), sell = toNum(r["Selling"]);
if (buy == null && sell == null && mean == null) return;
var pv = pmean[cur];
var ref = mean != null ? mean : (buy != null && sell != null ? (buy + sell) / 2 : (buy != null ? buy : sell));
var delta = pv != null ? ref - pv : null;
var pct = (delta != null && pv) ? (delta / pv) * 100 : null;
items.push({ sym: cur, buy: buy != null ? fmtNum(buy) : "—", sell: sell != null ? fmtNum(sell) : "—", delta: delta, pct: pct });
});
var order = CONFIG.majorCurrencies;
items.sort(function (a, b) {
var ia = order.indexOf(a.sym), ib = order.indexOf(b.sym);
if (ia === -1) ia = 999; if (ib === -1) ib = 999;
if (ia !== ib) return ia - ib;
return a.sym < b.sym ? -1 : 1;
});
return items;
}
function chgHTML(it) {
var cls = changeClass(it.delta);
if (it.delta == null) return '