Spaces:
Running
Running
Download utm-builder/utm.js from CyberMax-tools/cybermax: direct link, hf CLI and curl.
- Browser
- Download file 6.95 kB
-
https://huggingface.co/spaces/CyberMax-tools/cybermax/resolve/main/utm-builder/utm.js
- Command line
-
hf download hf://spaces/CyberMax-tools/cybermax/utm-builder/utm.js
-
curl -L -o utm.js https://huggingface.co/spaces/CyberMax-tools/cybermax/resolve/main/utm-builder/utm.js
6.95 kB
| /* UTM Builder core (pure, no DOM). © 2026 CyberMax. All rights reserved. */ | |
| (function (root) { | |
| "use strict"; | |
| var KEYS = ["utm_source", "utm_medium", "utm_campaign", "utm_id", "utm_term", "utm_content"]; | |
| // Mediums that GA4's default channel groups recognise (patterns from Google's published rules, simplified). | |
| var KNOWN_MEDIUM = /^(.*cp.*|ppc|retargeting|paid.*|e[-_ ]?mail|display|banner|expandable|interstitial|affiliate|referral|app|link|social|social[-_ ]network|social[-_ ]media|sm|organic|video|audio|sms|push|mobile|notification|qr|print|offline)$/; | |
| var PRESETS = { | |
| "Google Ads": { utm_source: "google", utm_medium: "cpc" }, | |
| "Microsoft Ads": { utm_source: "bing", utm_medium: "cpc" }, | |
| "Meta Ads": { utm_source: "facebook", utm_medium: "paid_social" }, | |
| "Instagram bio": { utm_source: "instagram", utm_medium: "social" }, | |
| "LinkedIn post": { utm_source: "linkedin", utm_medium: "social" }, | |
| "X / Twitter": { utm_source: "twitter", utm_medium: "social" }, | |
| "TikTok": { utm_source: "tiktok", utm_medium: "social" }, | |
| "YouTube": { utm_source: "youtube", utm_medium: "video" }, | |
| "Newsletter": { utm_source: "newsletter", utm_medium: "email" }, | |
| "QR / print": { utm_source: "qr", utm_medium: "print" }, | |
| "Affiliate": { utm_source: "partner", utm_medium: "affiliate" } | |
| }; | |
| function clean(v, opts) { | |
| v = String(v == null ? "" : v).trim(); | |
| if (opts.lower) v = v.toLowerCase(); | |
| if (opts.space && opts.space !== "keep") v = v.replace(/\s+/g, opts.space); | |
| return v; | |
| } | |
| function normalizeBase(base) { | |
| base = String(base || "").trim(); | |
| if (!base) return { error: "Enter the page URL you want to track." }; | |
| var added = false; | |
| if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(base)) { base = "https://" + base; added = true; } | |
| var u; | |
| try { u = new URL(base); } catch (e) { return { error: "That doesn't look like a valid URL." }; } | |
| if (!/^https?:$/.test(u.protocol)) return { error: "Use an http:// or https:// URL." }; | |
| if (!/\./.test(u.hostname) && u.hostname !== "localhost") return { error: "That URL has no domain name." }; | |
| return { url: u, added: added }; | |
| } | |
| // Build a tagged URL. params: {utm_source, ...}; opts: {lower: bool, space: "-"|"_"|"%20"|"keep"}. | |
| function build(base, params, opts) { | |
| opts = opts || { lower: true, space: "-" }; | |
| var warnings = []; | |
| var nb = normalizeBase(base); | |
| if (nb.error) return { url: "", error: nb.error, warnings: [] }; | |
| var u = nb.url; | |
| if (nb.added) warnings.push("Added https:// to the URL."); | |
| var replaced = []; | |
| var kept = []; | |
| u.search.replace(/^\?/, "").split("&").forEach(function (pair) { | |
| if (!pair) return; | |
| var k = decodeURIComponent(pair.split("=")[0].replace(/\+/g, " ")).toLowerCase(); | |
| if (KEYS.indexOf(k) >= 0) replaced.push(k); else kept.push(pair); | |
| }); | |
| var tags = []; | |
| KEYS.forEach(function (k) { | |
| var raw = params[k]; | |
| var v = clean(raw, opts); | |
| if (v) tags.push(k + "=" + encodeURIComponent(v).replace(/%2F/gi, "/")); | |
| if (raw && /\s/.test(String(raw).trim()) && opts.space === "keep") warnings.push(k + " contains spaces; they'll be encoded as %20."); | |
| if (raw && !opts.lower && /[A-Z]/.test(raw)) warnings.push(k + " has capitals. Analytics treats \"Facebook\" and \"facebook\" as different sources."); | |
| }); | |
| ["utm_source", "utm_medium", "utm_campaign"].forEach(function (k) { | |
| if (!clean(params[k], opts)) warnings.push(k + " is empty. GA4 needs source, medium and campaign to report the visit properly."); | |
| }); | |
| var med = clean(params.utm_medium, opts); | |
| if (med && !KNOWN_MEDIUM.test(med.toLowerCase())) warnings.push("utm_medium \"" + med + "\" isn't one GA4's default channel groups recognise, so these visits may show as \"Unassigned\". Try cpc, email, social, paid_social, display, affiliate or referral."); | |
| if (replaced.length) warnings.push("Replaced existing " + replaced.join(", ") + " in the URL."); | |
| var search = kept.concat(tags).join("&"); | |
| var out = u.origin + u.pathname + (search ? "?" + search : "") + u.hash; | |
| if (out.length > 2000) warnings.push("The URL is over 2,000 characters; some platforms truncate long links."); | |
| return { url: out, warnings: warnings, tagged: tags.length > 0 }; | |
| } | |
| function parse(url) { | |
| var nb = normalizeBase(url); | |
| if (nb.error) return { error: nb.error }; | |
| var u = nb.url, utm = {}, other = []; | |
| u.searchParams.forEach(function (v, k) { | |
| if (/^utm_/i.test(k)) utm[k.toLowerCase()] = v; else other.push(k + "=" + v); | |
| }); | |
| var cleanUrl = u.origin + u.pathname; | |
| var rest = []; | |
| u.search.replace(/^\?/, "").split("&").forEach(function (p) { if (p && !/^utm_/i.test(p)) rest.push(p); }); | |
| if (rest.length) cleanUrl += "?" + rest.join("&"); | |
| cleanUrl += u.hash; | |
| return { utm: utm, clean: cleanUrl, other: other }; | |
| } | |
| // CSV line splitter with quotes. | |
| function splitCsv(line) { | |
| var out = [], cur = "", q = false; | |
| for (var i = 0; i < line.length; i++) { | |
| var c = line[i]; | |
| if (q) { if (c === '"' && line[i + 1] === '"') { cur += '"'; i++; } else if (c === '"') q = false; else cur += c; } | |
| else if (c === '"') q = true; | |
| else if (c === "," || c === "\t") { out.push(cur); cur = ""; } | |
| else cur += c; | |
| } | |
| out.push(cur); | |
| return out.map(function (s) { return s.trim(); }); | |
| } | |
| // Bulk: each line "url" or "url,source,medium,campaign,term,content". Blank columns use defaults. | |
| var BULK_COLS = ["url", "utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"]; | |
| function bulk(text, defaults, opts) { | |
| var lines = String(text || "").split(/\r?\n/).map(function (l) { return l.trim(); }).filter(Boolean); | |
| var cols = BULK_COLS; | |
| if (lines.length && /^url\b/i.test(lines[0])) { | |
| cols = splitCsv(lines.shift()).map(function (h) { h = h.toLowerCase().replace(/^utm_/, ""); return h === "url" ? "url" : "utm_" + h; }); | |
| } | |
| return lines.slice(0, 5000).map(function (line) { | |
| var cells = splitCsv(line), p = {}; | |
| KEYS.forEach(function (k) { p[k] = defaults[k] || ""; }); | |
| var url = ""; | |
| cols.forEach(function (c, i) { if (c === "url") url = cells[i] || ""; else if (cells[i]) p[c] = cells[i]; }); | |
| var r = build(url, p, opts); | |
| return { input: url, url: r.url, error: r.error || "", warnings: r.warnings || [] }; | |
| }); | |
| } | |
| function csvCell(s) { s = String(s == null ? "" : s); return /[",\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s; } | |
| function toCsv(rows) { | |
| return "input_url,tagged_url,error\n" + rows.map(function (r) { return [r.input, r.url, r.error].map(csvCell).join(","); }).join("\n") + "\n"; | |
| } | |
| var api = { KEYS: KEYS, PRESETS: PRESETS, build: build, parse: parse, bulk: bulk, toCsv: toCsv, splitCsv: splitCsv }; | |
| root.UtmCore = api; | |
| if (typeof module !== "undefined") module.exports = api; | |
| })(typeof window !== "undefined" ? window : globalThis); | |