/* CyberMax Pro unlock with plans, for static web apps (copied into each product's web/ folder; each product stays * self-contained). Extends pro.js with several plans (monthly, yearly, one-time) and a re-check for subscriptions. * Config before this script: * window.CMX_PRO = { id, name, perks: [..], plans: [{ offer, label, price, per, note, best }], free: "one line" } * Buy: counted link https://data.cybermaxtools.com/buy/?s=app- -> Stripe Payment Link. * After checkout Stripe sends the buyer to data.cybermaxtools.com/unlock/?session_id=..., which checks the * Checkout Session (and, for a subscription, that it is still active) and returns here with #cmx-pro=. * The app checks that code once (/unlock/?json=1&session_id=..) and remembers Pro in this browser; it re-checks every * 3 days, so a cancelled subscription turns off by itself (one tiny request per buyer every 3 days, none for free users). * The same code (shown on the receipt page and in the Pro box) unlocks Pro on another device. No account, no tracking. * (c) 2026 CyberMax. All rights reserved. */ (function () { "use strict"; var C = window.CMX_PRO || {}; if (!C.id) return; var DATA = "https://data.cybermaxtools.com"; var KEY = "cmx-pro:" + C.id; var SID = /^(cmxk1\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+|cs_(live|test)_[A-Za-z0-9]{10,200}|[A-Z0-9]{8}-[A-Z0-9]{8}-[A-Z0-9]{8}-[A-Z0-9]{8})$/i; // Stripe order code or Gumroad licence key var RECHECK = 3 * 864e5; var listeners = []; function get() { try { return JSON.parse(localStorage.getItem(KEY) || "null"); } catch (e) { return null; } } function set(v) { try { if (v) localStorage.setItem(KEY, JSON.stringify(v)); else localStorage.removeItem(KEY); } catch (e) { /* private mode */ } mem = v; } var mem = get(); function isPro() { return !!(mem && mem.code); } // Offline Pro keys (17 Oct 2026): "cmxk1.." from data.cybermaxtools.com/unlock is verified // here with WebCrypto and the public key below, so unlocking needs no Worker call. Expired, foreign-signed or unverifiable // (no Ed25519 in this browser) keys fall back to the online check of the order code inside them, as before. var CMX_PUB = "u0FDxbu0d9B9Sz5-yLT6AuL5IagyTlFcbBgEDqZxWtk"; function b64d(s) { s = s.replace(/-/g, "+").replace(/_/g, "/"); while (s.length % 4) s += "="; var b = atob(s), u = new Uint8Array(b.length); for (var i = 0; i < b.length; i++) u[i] = b.charCodeAt(i); return u; } function cmxVerify(k) { var parts = k.split("."), p = null; try { p = JSON.parse(new TextDecoder().decode(b64d(parts[1]))); } catch (e) { return Promise.resolve({ p: null, ok: false }); } if (!(window.crypto && crypto.subtle)) return Promise.resolve({ p: p, ok: null }); return crypto.subtle.importKey("raw", b64d(CMX_PUB), { name: "Ed25519" }, false, ["verify"]) .then(function (key) { return crypto.subtle.verify({ name: "Ed25519" }, key, b64d(parts[2]), new TextEncoder().encode(parts[0] + "." + parts[1])); }) .then(function (ok) { return { p: p, ok: !!ok }; }, function () { return { p: p, ok: null }; }); } function check(code) { if (!/^cmxk1\./.test(code)) return checkOnline(code); return cmxVerify(code).then(function (r) { var p = r.p; if (!p || p.a !== C.id) return false; if (r.ok && (!p.x || p.x * 1000 > Date.now())) return true; return p.c ? checkOnline(p.c) : false; }); } function checkOnline(code) { return fetch(DATA + "/unlock/" + encodeURIComponent(C.id) + "?json=1&session_id=" + encodeURIComponent(code)) .then(function (r) { return r.json(); }) .then(function (j) { return j && typeof j.pro === "boolean" ? j.pro : null; }) .catch(function () { return null; }); } function unlock(code) { return check(code).then(function (ok) { if (ok) { set({ code: code, at: new Date().toISOString(), checked: Date.now() }); apply(); toast("Pro unlocked. Thank you for choosing " + C.name + "!"); } else if (ok === false) toast("That code is not an active " + C.name + " Pro order."); else toast("Could not reach the unlock check. Try again in a minute."); return ok; }); } function recheck() { if (!isPro() || Date.now() - (mem.checked || 0) < RECHECK) return; check(mem.code).then(function (ok) { if (ok === false) { set(null); apply(); toast(C.name + " Pro has ended in this browser. Everything free is still here."); } else if (ok) set(Object.assign({}, mem, { checked: Date.now() })); }); } function toast(msg) { var t = document.getElementById("toast"); if (t) { t.textContent = msg; t.hidden = false; t.classList.add("show"); setTimeout(function () { t.classList.remove("show"); }, 3500); } var s = document.getElementById("cmx-pro-msg"); if (s) s.textContent = msg; } function esc(s) { return String(s).replace(/[&<>"]/g, function (c) { return { "&": "&", "<": "<", ">": ">", '"': """ }[c]; }); } function buyUrl(offer, from) { return DATA + "/buy/" + offer + "?s=app-" + C.id + (from ? "-" + from : ""); } // Convert kit (funnel beacons, real proof, email capture, phone buy bar), loaded from the CyberMax Store; optional: // if it can't load (offline), the app and the Pro box work exactly the same. var KIT = "https://cybermaxtools.com/store/convert.js", kitQ = []; function conv(fn) { if (window.CMXConvert) return fn(window.CMXConvert); kitQ.push(fn); if (kitQ.length > 1 || !navigator.onLine) return; var sc = document.createElement("script"); sc.src = KIT; sc.async = true; sc.onload = function () { var q = kitQ.splice(0); q.forEach(function (f) { try { f(window.CMXConvert); } catch (e) { /* */ } }); }; document.head.appendChild(sc); } function box() { var el = document.getElementById("cmx-pro"); if (!el) { el = document.createElement("section"); el.id = "cmx-pro"; var anchor = document.querySelector("footer.more") || document.querySelector("main footer") || document.querySelector("footer"); if (anchor && anchor.parentNode) anchor.parentNode.insertBefore(el, anchor); else (document.querySelector("main") || document.body).appendChild(el); } el.className = "cmx-pro"; return el; } function render() { var el = box(); if (isPro()) { el.innerHTML = '

' + esc(C.name) + ' Pro on

Thank you! Pro is on in this browser: ' + C.perks.map(esc).join(" · ") + '.

Your unlock code (paste it on another device): ' + esc(mem.code) + '

' + ((C.plans || []).some(function (p) { return /\//.test(p.per || ""); }) ? '

Monthly or yearly plan? Cancel any time from the link in your Stripe receipt email.

' : ""); return; } var subs = (C.plans || []).some(function (p) { return /\//.test(p.per || ""); }); var plans = (C.plans || []).map(function (p) { return '' + (p.best ? "Best value" : "") + "" + esc(p.label) + '' + esc(p.price) + "" + esc(p.per || "") + '' + esc(p.note || "") + ""; }).join(""); el.innerHTML = '

' + esc(C.name) + ' Pro

' + (C.free ? '

Free: ' + esc(C.free) + "

" : "") + "
    " + C.perks.map(function (p) { return "
  • " + esc(p) + "
  • "; }).join("") + '
' + plans + '

Secure Stripe checkout. Pro turns on the moment you pay; no account needed.' + (subs ? " Subscriptions cancel any time from your receipt email." : " One payment, no subscription.") + ' 14-day promise: if it doesn\'t work as described, we fix it or refund you in full (terms). Questions: cybermax.tools@gmail.com

' + '
Already bought Pro?

'; Array.prototype.forEach.call(el.querySelectorAll("a.cmx-plan"), function (a) { a.addEventListener("click", function () { conv(function (K) { K.event("upgrade-click", C.id); }); }); }); conv(function (K) { K.proof(el.querySelector(".cmx-pro-proof"), C.id); K.capture(el.querySelector(".cmx-pro-cap"), { list: "weekly-" + C.id, src: "app-" + C.id, label: "Not ready for Pro? Email me " + C.name + " tips and new features (about once a week)" }); }); var f = document.getElementById("cmx-pro-form"); if (f) f.onsubmit = function (e) { e.preventDefault(); var c = (document.getElementById("cmx-pro-code").value || "").trim(); if (SID.test(c)) unlock(c); else toast("Paste the code from your receipt page (it starts with cs_live_) or your Gumroad licence key."); }; } function apply() { document.documentElement.classList.toggle("cmx-pro", isPro()); render(); listeners.forEach(function (fn) { try { fn(isPro()); } catch (e) { /* app hook */ } }); } /** Scroll to the Pro box (call when a free user hits a limit); `from` tags the counted buy links. */ function upsell(reason, from) { var el = box(); if (from) Array.prototype.forEach.call(el.querySelectorAll("a.cmx-plan"), function (a) { a.href = buyUrl(a.getAttribute("data-offer"), from); }); if (reason) toast(reason); // A real limit: count it, and on phones keep one buy button in reach (no timer; closes with one tap). conv(function (K) { K.event("limit", C.id); K.event("upgrade-view", C.id); var best = (C.plans || []).filter(function (p) { return p.best; })[0] || (C.plans || [])[0]; if (best && !isPro()) K.sticky({ text: C.name + " Pro: " + best.price + (best.per || ""), cta: "Go Pro", href: buyUrl(best.offer, (from || "limit") + "-bar"), product: C.id }); }); el.scrollIntoView({ behavior: "smooth", block: "start" }); el.classList.add("cmx-pro-flash"); setTimeout(function () { el.classList.remove("cmx-pro-flash"); }, 1600); } /** Free monthly allowance kept in this browser: quota("pdf", 5) -> {used, left, take()}. */ function quota(name, limit) { var k = "cmx-quota:" + C.id + ":" + name, month = new Date().toISOString().slice(0, 7), q; try { q = JSON.parse(localStorage.getItem(k) || "null"); } catch (e) { q = null; } if (!q || q.m !== month) q = { m: month, n: 0 }; return { used: q.n, left: Math.max(0, limit - q.n), limit: limit, take: function () { if (isPro()) return true; if (q.n >= limit) return false; q.n++; try { localStorage.setItem(k, JSON.stringify(q)); } catch (e) { /* */ } this.used = q.n; this.left = Math.max(0, limit - q.n); return true; }, }; } function download(name, data, type) { var a = document.createElement("a"); a.href = URL.createObjectURL(data instanceof Blob ? data : new Blob([data], { type: type || "text/csv;charset=utf-8" })); a.download = name; document.body.appendChild(a); a.click(); a.remove(); setTimeout(function () { URL.revokeObjectURL(a.href); }, 2000); } window.CMXPro = { isPro: isPro, onChange: function (fn) { listeners.push(fn); fn(isPro()); }, upsell: upsell, quota: quota, download: download, toast: toast }; var m = /[#&]cmx-key=([^&]+)/.exec(location.hash || "") || /[#&]cmx-pro=([^&]+)/.exec(location.hash || ""); function start() { if (m && SID.test(decodeURIComponent(m[1]))) { var code = decodeURIComponent(m[1]); history.replaceState(null, "", location.pathname + location.search); apply(); unlock(code); } else { apply(); recheck(); } } if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", start); else start(); })(); /* Direct checkout (17 Oct 2026, Workers cap fix): a click on a data.cybermaxtools.com/buy/ link goes straight to the Stripe/Polar checkout (map: https://cybermaxtools.com/store/buy-direct.json, a static file) and the click is counted by a background beacon. Without the map the link works as before. */ (function () { var MAP = null, U = "https://cybermaxtools.com/store/buy-direct.json"; try { MAP = JSON.parse(sessionStorage.getItem("cmx-bd") || "null"); } catch (e) { /* none */ } if (!MAP && window.fetch) fetch(U).then(function (r) { return r.json(); }).then(function (j) { MAP = j; try { sessionStorage.setItem("cmx-bd", JSON.stringify(j)); } catch (e) { /* full */ } }).catch(function () {}); document.addEventListener("click", function (e) { var a = e.target && e.target.closest && e.target.closest('a[href*="data.cybermaxtools.com/buy/"]'); if (!a || !MAP || !MAP.offers) return; try { var u = new URL(a.href), k = /^\/buy\/([a-z0-9-]+)\/?$/.exec(u.pathname), t = k && MAP.offers[k[1]]; if (!t) return; var s = String(u.searchParams.get("s") || "unknown").toLowerCase().replace(/[^a-z0-9-]/g, "").slice(0, 80) || "unknown"; a.href = t.split(MAP.ph).join(s); if (!navigator.webdriver) navigator.sendBeacon(u.href + (u.search ? "&" : "?") + "b=1"); } catch (x) { /* keep the counted link */ } }, true); })();