Spaces:
Running
Running
Download utm-builder/app.js from CyberMax-tools/cybermax: direct link, hf CLI and curl.
- Browser
- Download file 9.83 kB
-
https://huggingface.co/spaces/CyberMax-tools/cybermax/resolve/main/utm-builder/app.js
- Command line
-
hf download hf://spaces/CyberMax-tools/cybermax/utm-builder/app.js
-
curl -L -o app.js https://huggingface.co/spaces/CyberMax-tools/cybermax/resolve/main/utm-builder/app.js
9.83 kB
| /* 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("<br>"); | |
| 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 "<tr><td class='num'>" + (i + 1) + "</td><td style='word-break:break-all'>" + esc(r.url || r.input) + "</td><td>" + | |
| (r.error ? "<span style='color:var(--warn)'>" + esc(r.error) + "</span>" : esc(r.warnings.filter(function (w) { return !/^Added https/.test(w); }).slice(0, 2).join(" "))) + "</td></tr>"; | |
| }).join(""); | |
| el("bulk-table").hidden = !bulkRows.length; | |
| var okN = bulkRows.filter(function (r) { return !r.error; }).length; | |
| el("btn-bulk-copy").disabled = el("btn-bulk-csv").disabled = !okN; | |
| toast(bulkRows.length ? okN + " of " + bulkRows.length + " links built" : "Paste some URLs first"); | |
| }; | |
| el("btn-bulk-copy").onclick = function () { copy(bulkRows.filter(function (r) { return !r.error; }).map(function (r) { return r.url; }).join("\n"), "All links copied"); }; | |
| el("btn-bulk-csv").onclick = function () { download("utm-links.csv", U.toCsv(bulkRows), "text/csv"); }; | |
| // ---------- decode ---------- | |
| var decoded = null; | |
| el("dec-in").addEventListener("input", function () { | |
| var v = el("dec-in").value.trim(), tb = el("dec-table").querySelector("tbody"); | |
| decoded = v ? U.parse(v) : null; | |
| if (!decoded || decoded.error) { tb.innerHTML = v ? "<tr><td>" + esc(decoded.error) + "</td></tr>" : ""; el("dec-clean").textContent = ""; return; } | |
| var keys = Object.keys(decoded.utm); | |
| tb.innerHTML = keys.length ? keys.map(function (k) { return "<tr><th>" + esc(k) + "</th><td>" + esc(decoded.utm[k]) + "</td></tr>"; }).join("") | |
| : "<tr><td>No UTM parameters in this link.</td></tr>"; | |
| el("dec-clean").textContent = decoded.clean; | |
| }); | |
| el("btn-dec-edit").onclick = function () { | |
| if (!decoded || decoded.error) return; | |
| el("f-url").value = decoded.clean; | |
| U.KEYS.forEach(function (k) { el("f-" + k).value = decoded.utm[k] || ""; }); | |
| showTab("single"); update(); | |
| }; | |
| // ---------- history ---------- | |
| function addHistory(url) { | |
| var h = load("history", []).filter(function (x) { return x.url !== url; }); | |
| h.unshift({ url: url, at: new Date().toISOString().slice(0, 16).replace("T", " ") }); | |
| save("history", h.slice(0, (window.CMXPro && CMXPro.isPro()) ? 500 : 25)); // Pro (pro.js): 500 | |
| } | |
| function renderHistory() { | |
| var h = load("history", []), tb = el("hist-table").querySelector("tbody"); | |
| tb.innerHTML = h.length ? h.map(function (x, i) { | |
| return "<tr><td style='white-space:nowrap;color:var(--muted)'>" + esc(x.at) + "</td><td style='word-break:break-all'>" + esc(x.url) + "</td><td><button class='chip' data-i='" + i + "'>Copy</button></td></tr>"; | |
| }).join("") : "<tr><td>Links you copy will appear here.</td></tr>"; | |
| tb.querySelectorAll("button").forEach(function (b) { b.onclick = function () { copy(h[+b.dataset.i].url, "Link copied"); }; }); | |
| } | |
| el("btn-hist-clear").onclick = function () { save("history", []); renderHistory(); }; | |
| // ---------- restore ---------- | |
| var st = load("form", null); | |
| if (st) { | |
| fields.forEach(function (f) { if (st[f]) el("f-" + f).value = st[f]; }); | |
| el("o-lower").checked = st.lower !== false; | |
| if (st.space) el("o-space").value = st.space; | |
| } | |
| update(); | |
| window.UtmApp = { update: update, current: function () { return current; } }; | |
| if ("serviceWorker" in navigator && location.protocol === "https:") navigator.serviceWorker.register("sw.js").catch(function () {}); | |
| })(); | |