/* UTM Builder UI. © 2026 CyberMax. All rights reserved. */
(function () {
"use strict";
var U = window.UtmCore;
var el = function (id) { return document.getElementById(id); };
var KEY = "cybermax-utm:v1:";
function load(k, d) { try { var v = localStorage.getItem(KEY + k); return v ? JSON.parse(v) : d; } catch (e) { return d; } }
function save(k, v) { try { localStorage.setItem(KEY + k, JSON.stringify(v)); } catch (e) { /* ignore */ } }
function esc(s) { return String(s).replace(/[&<>"]/g, function (c) { return { "&": "&", "<": "<", ">": ">", '"': """ }[c]; }); }
var toastT;
function toast(m) { var t = el("toast"); t.textContent = m; t.classList.add("show"); clearTimeout(toastT); toastT = setTimeout(function () { t.classList.remove("show"); }, 1600); }
function copy(text, msg) {
var done = function () { toast(msg || "Copied"); };
if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(text).then(done, function () { fallback(text); done(); });
else { fallback(text); done(); }
}
function fallback(text) { var ta = document.createElement("textarea"); ta.value = text; document.body.appendChild(ta); ta.select(); try { document.execCommand("copy"); } catch (e) { /* ignore */ } ta.remove(); }
function download(name, text, type) {
var a = document.createElement("a");
a.href = URL.createObjectURL(new Blob([text], { type: type }));
a.download = name; document.body.appendChild(a); a.click(); a.remove();
setTimeout(function () { URL.revokeObjectURL(a.href); }, 1000);
}
// ---------- tabs ----------
function showTab(name) {
document.querySelectorAll(".tab").forEach(function (t) { t.setAttribute("aria-selected", String(t.dataset.tab === name)); });
document.querySelectorAll(".panel").forEach(function (p) { p.hidden = p.id !== "panel-" + name; });
if (name === "history") renderHistory();
}
document.querySelectorAll(".tab").forEach(function (t) { t.onclick = function () { showTab(t.dataset.tab); }; });
// ---------- builder ----------
var fields = ["url"].concat(U.KEYS);
function params() { var p = {}; U.KEYS.forEach(function (k) { p[k] = el("f-" + k).value; }); return p; }
function opts() { return { lower: el("o-lower").checked, space: el("o-space").value }; }
var current = "";
var presets = el("presets");
Object.keys(U.PRESETS).forEach(function (name) {
var b = document.createElement("button");
b.type = "button"; b.className = "chip"; b.textContent = name;
b.onclick = function () {
var p = U.PRESETS[name];
for (var k in p) el("f-" + k).value = p[k];
presets.querySelectorAll(".chip").forEach(function (c) { c.classList.toggle("on", c === b); });
update();
};
presets.appendChild(b);
});
function update() {
var r = U.build(el("f-url").value, params(), opts());
var out = el("out");
if (r.error || !r.tagged) {
current = "";
out.textContent = el("f-url").value.trim() ? (r.error || "Add a source, medium and campaign.") : "Fill in the URL, source, medium and campaign.";
} else {
current = r.url;
out.textContent = r.url;
}
el("warn").innerHTML = (r.warnings || []).filter(function () { return !!current; }).map(function (w) { return "⚠ " + esc(w); }).join("
");
el("btn-copy").disabled = !current;
el("btn-qr").disabled = !current;
var open = el("btn-open");
if (current) { open.href = current; open.removeAttribute("aria-disabled"); } else { open.removeAttribute("href"); open.setAttribute("aria-disabled", "true"); }
drawQr(current);
var state = {}; fields.forEach(function (f) { state[f] = el("f-" + f).value; });
state.lower = el("o-lower").checked; state.space = el("o-space").value;
save("form", state);
}
function drawQr(text) {
var c = el("qr"), ctx = c.getContext("2d");
ctx.fillStyle = "#fff"; ctx.fillRect(0, 0, c.width, c.height);
if (!text) {
ctx.fillStyle = "#9aa4b6"; ctx.font = "14px system-ui, sans-serif"; ctx.textAlign = "center";
ctx.fillText("QR code appears here", c.width / 2, c.height / 2);
return;
}
try {
var qr = window.qrcode(0, "M");
qr.addData(text); qr.make();
var n = qr.getModuleCount(), quiet = 4, size = Math.floor(c.width / (n + quiet * 2));
var off = Math.floor((c.width - size * n) / 2);
ctx.fillStyle = "#000";
for (var r = 0; r < n; r++) for (var col = 0; col < n; col++) if (qr.isDark(r, col)) ctx.fillRect(off + col * size, off + r * size, size, size);
} catch (e) {
ctx.fillStyle = "#b25e00"; ctx.font = "13px system-ui, sans-serif"; ctx.textAlign = "center";
ctx.fillText("Link too long for a QR code", c.width / 2, c.height / 2);
}
}
function qrPng() {
// Re-render at 1024px for print quality.
var qr = window.qrcode(0, "M"); qr.addData(current); qr.make();
var n = qr.getModuleCount(), cell = Math.floor(1024 / (n + 8)), size = cell * (n + 8);
var c = document.createElement("canvas"); c.width = c.height = size;
var ctx = c.getContext("2d"); ctx.fillStyle = "#fff"; ctx.fillRect(0, 0, size, size); ctx.fillStyle = "#000";
for (var r = 0; r < n; r++) for (var col = 0; col < n; col++) if (qr.isDark(r, col)) ctx.fillRect((col + 4) * cell, (r + 4) * cell, cell, cell);
var a = document.createElement("a"); a.href = c.toDataURL("image/png");
var camp = (el("f-utm_campaign").value || "link").toLowerCase().replace(/[^a-z0-9_-]+/g, "-");
a.download = "qr-" + camp + ".png"; document.body.appendChild(a); a.click(); a.remove();
}
fields.forEach(function (f) { el("f-" + f).addEventListener("input", update); });
el("o-lower").addEventListener("change", update);
el("o-space").addEventListener("change", update);
el("btn-copy").onclick = function () { if (!current) return; copy(current, "Link copied"); addHistory(current); };
el("btn-qr").onclick = function () { if (current) qrPng(); };
el("btn-reset").onclick = function () { fields.forEach(function (f) { el("f-" + f).value = ""; }); presets.querySelectorAll(".chip").forEach(function (c) { c.classList.remove("on"); }); update(); el("f-url").focus(); };
// ---------- bulk ----------
var bulkRows = [];
el("btn-bulk").onclick = function () {
bulkRows = U.bulk(el("bulk-in").value, params(), opts());
var tb = el("bulk-table").querySelector("tbody");
tb.innerHTML = bulkRows.map(function (r, i) {
return "