UNPKG

apexcharts

Version:

A JavaScript Chart Library

811 lines (810 loc) 30.7 kB
var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a2, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a2, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a2, prop, b[prop]); } return a2; }; var __spreadProps = (a2, b) => __defProps(a2, __getOwnPropDescs(b)); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e2) { reject(e2); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e2) { reject(e2); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; /*! * ApexCharts v7.0.0 * (c) 2018-2026 ApexCharts */ import * as ApexCharts from "apexcharts/core"; import ApexCharts__default from "apexcharts/core"; import { default as default2 } from "apexcharts/core"; const Utils = ApexCharts.__apex_Utils; const Environment = ApexCharts.__apex_Environment_Environment; const VIEWSTATE_VERSION = 1; function axisWindow(min, max) { const hasMin = min !== void 0 && min !== null; const hasMax = max !== void 0 && max !== null; if (!hasMin && !hasMax) return null; return { min: hasMin ? min : null, max: hasMax ? max : null }; } function cloneSelection(sel) { if (!Array.isArray(sel)) return []; return sel.map((a2) => Array.isArray(a2) ? a2.slice() : a2); } function annotationKind(method, ctx) { if (typeof method !== "function" || !ctx) return null; if (method === ctx.addXaxisAnnotation) return "xaxis"; if (method === ctx.addYaxisAnnotation) return "yaxis"; if (method === ctx.addPointAnnotation) return "point"; return null; } function addMethodName(kind) { switch (kind) { case "xaxis": return "addXaxisAnnotation"; case "yaxis": return "addYaxisAnnotation"; case "point": return "addPointAnnotation"; default: return null; } } function captureAnnotations(w, ctx) { const staticAnno = w.config.annotations ? Utils.clone(w.config.annotations) : null; const dynamic = []; const mem = w.globals.memory && w.globals.memory.methodsToExec || []; for (const entry of mem) { if (!entry || entry.label !== "addAnnotation") continue; const kind = annotationKind(entry.method, ctx); if (!kind) continue; dynamic.push({ kind, params: Utils.clone(entry.params) }); } return { static: staticAnno, dynamic }; } function captureMeasure(ctx) { const m = ctx && ctx.measure; if (!m || typeof m.getPins !== "function") return null; const pins = m.getPins(); return Array.isArray(pins) && pins.length ? { pins } : null; } function captureViewState(w, ctx) { var _a, _b; const cfgX = w.config.xaxis || {}; const cfgYArr = Array.isArray(w.config.yaxis) ? w.config.yaxis : w.config.yaxis ? [w.config.yaxis] : []; const yWindows = cfgYArr.map( (y) => axisWindow(y && y.min, y && y.max) ); const anyY = yWindows.some((yw) => yw !== null); const theme = w.config.theme; const drilldown = ctx && ctx.drilldown; return { v: VIEWSTATE_VERSION, window: { xaxis: axisWindow(cfgX.min, cfgX.max), yaxis: anyY ? yWindows : null }, zoomed: !!w.interact.zoomed, collapsed: (w.globals.collapsedSeriesIndices || []).slice(), ancillaryCollapsed: (w.globals.ancillaryCollapsedSeriesIndices || []).slice(), selectedDataPoints: cloneSelection(w.interact.selectedDataPoints), theme: theme ? { mode: (_a = theme.mode) != null ? _a : null, palette: (_b = theme.palette) != null ? _b : null } : null, locale: w.config.chart && w.config.chart.defaultLocale || null, annotations: captureAnnotations(w, ctx), drill: drilldown && drilldown.depth > 0 ? { path: drilldown.path.slice() } : null, measure: captureMeasure(ctx) }; } function applyCollapsedSet(ctx, targetCollapsed, targetAncillary) { const w = ctx.w; if (targetCollapsed == null && targetAncillary == null) return; const names = w.globals.seriesNames || []; const target = /* @__PURE__ */ new Set([ ...targetCollapsed || [], ...targetAncillary || [] ]); const current = /* @__PURE__ */ new Set([ ...w.globals.collapsedSeriesIndices || [], ...w.globals.ancillaryCollapsedSeriesIndices || [] ]); for (let realIndex = 0; realIndex < names.length; realIndex++) { const name = names[realIndex]; if (name == null) continue; const shouldCollapse = target.has(realIndex); const isCollapsed = current.has(realIndex); if (shouldCollapse && !isCollapsed) { ctx.hideSeries(name); } else if (!shouldCollapse && isCollapsed) { ctx.showSeries(name); } } } function restoreSelection(ctx, selectedDataPoints) { if (!Array.isArray(selectedDataPoints)) return; ctx.w.interact.selectedDataPoints = cloneSelection(selectedDataPoints); } function applyViewState(ctx, view, { animate = true, mergeOptions } = {}) { var _a, _b; if (!ctx || !view) return; if (typeof view.v === "number" && view.v > VIEWSTATE_VERSION) { console.warn( `[apexcharts] ViewState v${view.v} is newer than this build understands (v${VIEWSTATE_VERSION}); applying best-effort.` ); } ctx.clearAnnotations(); const options = mergeOptions ? Utils.clone(mergeOptions) : {}; const xw = view.window && view.window.xaxis; options.xaxis = Object.assign( {}, options.xaxis, xw ? { min: (_a = xw.min) != null ? _a : void 0, max: (_b = xw.max) != null ? _b : void 0 } : { min: void 0, max: void 0 } ); const yw = view.window && view.window.yaxis; if (Array.isArray(yw)) { options.yaxis = yw.map( (y) => { var _a2, _b2; return y ? { min: (_a2 = y.min) != null ? _a2 : void 0, max: (_b2 = y.max) != null ? _b2 : void 0 } : {}; } ); } if (view.theme) { const theme = {}; if (view.theme.mode != null) theme.mode = view.theme.mode; if (view.theme.palette != null) theme.palette = view.theme.palette; if (Object.keys(theme).length) { options.theme = Object.assign({}, options.theme, theme); } } if (view.annotations && view.annotations.static) { options.annotations = Utils.clone(view.annotations.static); } ctx.updateOptions( options, false, animate, false, false ); applyViewInteraction(ctx, view); } function applyViewInteraction(ctx, view) { if (!ctx || !view) return; const w = ctx.w; w.interact.zoomed = !!view.zoomed; applyCollapsedSet(ctx, view.collapsed, view.ancillaryCollapsed); if (view.annotations && Array.isArray(view.annotations.dynamic)) { view.annotations.dynamic.forEach((a2) => { const method = addMethodName(a2.kind); if (method && typeof ctx[method] === "function") { ctx[method](a2.params, true); } }); } restoreSelection(ctx, view.selectedDataPoints); if (view.locale && view.locale !== w.config.chart.defaultLocale) { ctx.setLocale(view.locale); } if (ctx.measure && typeof ctx.measure.setPins === "function") { ctx.measure.setPins(view.measure && view.measure.pins || []); } } const e = globalThis.console; function t(t2) { e.error(t2); } function s(t2) { e.warn(t2); } const i = "APEX-", n = /* @__PURE__ */ new Date("2027-07-31T00:00:00Z"), r = "__apex_license_v1__"; function a() { const e2 = globalThis; let t2 = e2[r]; return t2 || (t2 = { key: null, listeners: /* @__PURE__ */ new Set(), result: null }, e2[r] = t2), t2; } const l = class { static get licenseKey() { return a().key; } static set licenseKey(e2) { a().key = e2; } static get listeners() { return a().listeners; } static get validationResult() { return a().result; } static set validationResult(e2) { a().result = e2; } static getKey() { return this.licenseKey; } static getLicenseStatus() { return this.licenseKey ? (this.validationResult = this.validateKey(this.licenseKey), this.validationResult) : { expired: false, signatureVerified: false, valid: false }; } static isKeyValid(e2) { return !!e2 && this.validateKey(e2).valid; } static isLicenseValid() { return this.getLicenseStatus().valid; } static onChange(e2) { return this.listeners.add(e2), () => { this.listeners.delete(e2); }; } static setLicense(e2) { var _a; var i2; if (!e2) return this.licenseKey = null, void this.publish({ expired: false, signatureVerified: false, valid: false }); const n2 = this.validateKey(e2); n2.valid || e2 === this.licenseKey || !(null == (i2 = this.validationResult) ? void 0 : i2.valid) ? (this.licenseKey = e2, this.publish(n2), n2.valid || t(`[Apex] ${n2.message}`)) : s(`[Apex] Ignoring license key: ${(_a = n2.message) != null ? _a : "it is not valid"} A valid license is already active on this page.`); } static validateKey(e2) { const t2 = this.parseKey(e2), s2 = this.validateStructure(e2, t2); if (!s2.valid || !(null == t2 ? void 0 : t2.signature)) return s2; const i2 = this.verdicts.get(e2); return false === i2 ? { data: t2.data, expired: false, message: "Invalid license key. The license signature does not verify.", signatureVerified: true, valid: false } : (void 0 === i2 && this.verifySignature(e2, t2, s2), __spreadProps(__spreadValues({}, s2), { signatureVerified: true === i2 })); } static _resetSignatureState() { this.verdicts.clear(), this.verifying.clear(), this.warnedUnverifiable = false, this.epoch++; } static base64ToBytes(e2) { const t2 = e2.replace(/-/g, "+").replace(/_/g, "/"), s2 = t2.padEnd(4 * Math.ceil(t2.length / 4), "="), i2 = globalThis.atob; if ("function" != typeof i2) throw new Error("no base64 decoder available"); const n2 = i2(s2), r2 = new Uint8Array(n2.length); for (let e3 = 0; e3 < n2.length; e3++) r2[e3] = n2.charCodeAt(e3); return r2; } static canonicalPayload(e2) { const t2 = e2.domains && e2.domains.length > 0 ? e2.domains.join(",") : ""; return `v1|${e2.issueDate}|${e2.expiryDate}|${e2.plan}|${t2}`; } static notify(e2) { for (const t2 of this.listeners) try { t2(e2); } catch (e3) { } } static parseKey(e2) { if ("string" != typeof e2 || !e2.startsWith(i)) return null; const t2 = e2.slice(5); if (!t2) return null; try { const e3 = new TextDecoder().decode(this.base64ToBytes(t2)), s2 = JSON.parse(e3); return s2.issueDate && s2.expiryDate && s2.plan ? { data: { domains: Array.isArray(s2.domains) ? s2.domains : void 0, expiryDate: s2.expiryDate, issueDate: s2.issueDate, plan: s2.plan, valid: true }, signature: "string" == typeof s2.sig && s2.sig ? s2.sig : null } : null; } catch (e3) { return null; } } static publish(e2) { this.validationResult = e2, this.notify(e2); } static validateStructure(e2, t2) { const s2 = (e3) => ({ expired: false, message: e3, signatureVerified: false, valid: false }); if ("string" != typeof e2 || !e2.startsWith(i)) return s2('Invalid license key format. License key must start with "APEX-".'); if (!t2) return s2("Invalid license key. Unable to decode license data."); const { data: r2, signature: a2 } = t2; if (!a2 && /* @__PURE__ */ new Date() >= n) return s2("This license key is in the old unsigned format, which is no longer accepted. Please request a replacement key."); if (new Date(r2.expiryDate) < /* @__PURE__ */ new Date()) return { data: r2, expired: true, message: `License expired on ${r2.expiryDate}. Please renew your license.`, signatureVerified: false, valid: false }; if (r2.domains && r2.domains.length > 0) { const e3 = "undefined" == typeof location ? "" : location.hostname; if (!r2.domains.some(((t3) => e3 === t3 || e3.endsWith(`.${t3}`)))) return { data: r2, expired: false, message: `License is not valid for this domain (${e3}). Allowed domains: ${r2.domains.join(", ")}.`, signatureVerified: false, valid: false }; } return { data: r2, expired: false, signatureVerified: false, valid: true }; } static verifySignature(e2, i2, n2) { return __async(this, null, function* () { var r2; if (this.verifying.has(e2) || this.verdicts.has(e2)) return; this.verifying.add(e2); const a2 = this.epoch, l2 = null == (r2 = globalThis.crypto) ? void 0 : r2.subtle; if (!l2 || 0 === this.publicKeysSpki.length) return this.verifying.delete(e2), void (this.warnedUnverifiable || (this.warnedUnverifiable = true, s(l2 ? "[Apex] No license signing key is configured in this build, so license signatures cannot be verified." : "[Apex] Web Crypto is unavailable (a secure context is required), so the license signature cannot be verified."))); const o2 = new TextEncoder().encode(this.canonicalPayload(i2.data)); let c2 = false; for (const e3 of this.publicKeysSpki) { try { const t2 = yield l2.importKey("spki", this.base64ToBytes(e3), { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"]); c2 = yield l2.verify({ hash: "SHA-256", name: "ECDSA" }, t2, this.base64ToBytes(i2.signature), o2); } catch (e4) { c2 = false; } if (c2) break; } if (this.verifying.delete(e2), this.epoch !== a2) return; if (this.verdicts.set(e2, c2), c2) { const t2 = __spreadProps(__spreadValues({}, n2), { signatureVerified: true }); return void (this.licenseKey === e2 ? this.publish(t2) : this.notify(t2)); } const h2 = "Invalid license key. The license signature does not verify.", d = { data: i2.data, expired: false, message: h2, signatureVerified: true, valid: false }; this.licenseKey === e2 ? this.publish(d) : this.notify(d), t(`[Apex] ${h2}`); }); } }; l.publicKeysSpki = ["MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQIaK9UMD6n0oR/FIy8QdL0uSzKMQlf1BB+tOrji4/WuHsyRNxeDhVykoSsNURozMi1xhmqWvBH1L//xIfugTPA=="], l.verdicts = /* @__PURE__ */ new Map(), l.verifying = /* @__PURE__ */ new Set(), l.warnedUnverifiable = false, l.epoch = 0; let o = l; const c = class { static applyStyles(e2) { Object.assign(e2.style, this.CRITICAL_STYLES, { backgroundImage: this.createWatermarkPattern(), backgroundRepeat: "repeat" }); } static node(e2) { return e2 ? e2.querySelector(`[${this.WATERMARK_ATTR}]`) : null; } static add(e2, t2) { return e2 && "undefined" != typeof document ? (this.setManaged(e2, t2), this.paint(e2)) : null; } static exists(e2) { return !!this.node(e2); } static remove(e2, t2) { e2 && (this.setManaged(e2, t2), this.erase(e2)); } static untrack(e2) { this.managed.delete(e2); } static createWatermarkPattern() { const e2 = this.WATERMARK_TEXT; return `url("data:image/svg+xml,${encodeURIComponent(` <svg xmlns="http://www.w3.org/2000/svg" width="300" height="200"> <text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" font-family="-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Arial, sans-serif" font-size="18" font-weight="600" fill="rgba(134, 134, 134, 0.1)" transform="rotate(-35, 100, 60)" >${e2}</text> </svg> `.trim())}")`; } static erase(e2) { var t2; null == (t2 = this.node(e2)) || t2.remove(); } static paint(e2) { let t2 = this.node(e2); return t2 || (t2 = document.createElement("div"), t2.setAttribute(this.WATERMARK_ATTR, ""), e2.appendChild(t2)), this.applyStyles(t2), "function" == typeof getComputedStyle && "static" === getComputedStyle(e2).position && (e2.style.position = "relative"), t2; } static reconcile() { const e2 = o.isLicenseValid(); for (const t2 of this.managed) t2.isConnected ? e2 ? this.erase(t2) : this.paint(t2) : this.managed.delete(t2); } static setManaged(e2, t2) { false !== (null == t2 ? void 0 : t2.manage) ? this.track(e2) : this.managed.delete(e2); } static track(e2) { this.managed.add(e2), this.subscribed || (this.subscribed = true, o.onChange((() => { this.reconcile(); }))); } }; c.WATERMARK_ATTR = "data-apexcharts-watermark", c.WATERMARK_TEXT = "APEXCHARTS", c.ATTR = "data-apexcharts-watermark", c.CRITICAL_STYLES = { bottom: "0", display: "block", left: "0", msUserSelect: "none", opacity: "1", pointerEvents: "none", position: "absolute", right: "0", top: "0", userSelect: "none", visibility: "visible", webkitUserSelect: "none", zIndex: "10000" }, c.managed = /* @__PURE__ */ new Set(), c.subscribed = false; let h = c; const PRICING_URL = "https://apexcharts.com/pricing"; let _perspectivesTokenDecoded = false; const enforced = /* @__PURE__ */ new Set(); function markPerspectivesTokenDecoded() { _perspectivesTokenDecoded = true; reevaluateLicenseAcrossCharts(); } function premiumFeaturesInUse(w, ctx) { const chart = w && w.config && w.config.chart || {}; const used = []; if (chart.type === "unit") used.push("unit"); if (ctx.trellis && typeof ctx.trellis.isActive === "function" && ctx.trellis.isActive()) { used.push("trellis"); } if (ctx.storyboard && ctx.storyboard._used) used.push("storyboard"); const link = chart.link; if (ctx.linkedViews && link && (link.enabled === true || typeof link.dimension === "function")) { used.push("link"); } if (ctx.ink && chart.ink && chart.ink.enabled === true) used.push("ink"); if (ctx.measure && chart.measure && chart.measure.enabled === true) { used.push("measure"); } if (ctx.contextMenu && chart.contextMenu && chart.contextMenu.enabled === true) { used.push("context-menu"); } if (ctx.perspectives && (ctx.perspectives._used || _perspectivesTokenDecoded)) { used.push("perspectives"); } if (ctx.history && chart.history && chart.history.enabled === true) { used.push("history"); } return used; } function resolveKey(w) { const perChart = w && w.config && w.config.chart && w.config.chart.license; if (perChart) return perChart; const singleton = o.getKey(); if (singleton) return singleton; const apex = Environment.getApex(); if (apex && apex.license) return apex.license; return null; } const PREMIUM_PLANS = /* @__PURE__ */ new Set(["premium", "enterprise"]); function licensedForPremium(key) { if (!key) return false; const result = o.validateKey(key); if (!result.valid) return false; const plan = result.data && result.data.plan; return typeof plan === "string" && PREMIUM_PLANS.has(plan.toLowerCase()); } function reinstateWatermark(ctx, elWrap) { const node = h.add(elWrap, { manage: false }); if (!node || typeof MutationObserver === "undefined") return; if (ctx._wmNodeObserver && ctx._wmObservedNode === node) return; if (ctx._wmNodeObserver) ctx._wmNodeObserver.disconnect(); const nodeObs = new MutationObserver(() => { const n2 = h.node(elWrap); if (!n2) return; nodeObs.disconnect(); h.applyStyles(n2); nodeObs.takeRecords(); nodeObs.observe(n2, { attributes: true, attributeFilter: ["style"] }); }); nodeObs.observe(node, { attributes: true, attributeFilter: ["style"] }); ctx._wmNodeObserver = nodeObs; ctx._wmObservedNode = node; } function addWatermark(ctx, elWrap) { reinstateWatermark(ctx, elWrap); if (typeof MutationObserver === "undefined" || ctx._wmWrapObserver) return; const wrapObs = new MutationObserver(() => { if (!h.node(elWrap)) reinstateWatermark(ctx, elWrap); }); wrapObs.observe(elWrap, { childList: true }); ctx._wmWrapObserver = wrapObs; } function teardownWatermark(ctx, elWrap) { if (ctx._wmWrapObserver) { ctx._wmWrapObserver.disconnect(); ctx._wmWrapObserver = null; } if (ctx._wmNodeObserver) { ctx._wmNodeObserver.disconnect(); ctx._wmNodeObserver = null; } ctx._wmObservedNode = null; const wrap = elWrap || ctx.w && ctx.w.dom && ctx.w.dom.elWrap; if (wrap) h.remove(wrap, { manage: false }); } function notifyTrial(ctx, key, features) { if (ctx._premiumLicenseNotified) return; ctx._premiumLicenseNotified = true; const many = features.length > 1; if (!key) { console.warn( `[ApexCharts] Premium feature${many ? "s" : ""} in use (${features.join(", ")}) without a license. Running in trial mode with a watermark. Get a license: ${PRICING_URL}` ); return; } const result = o.validateKey(key); if (result.valid) { const plan = result.data && result.data.plan || "current"; console.warn( `[ApexCharts] Premium feature${many ? "s" : ""} in use (${features.join(", ")}) require a Premium or Enterprise license; the ${plan} plan does not include ${many ? "them" : "it"}. Running in trial mode with a watermark. Upgrade: ${PRICING_URL}` ); return; } if (key !== o.getKey()) { console.error(`[Apex] ${result.message}`); } } function enforceLicense(w, ctx) { try { if (!Environment.isBrowser()) return; if (w && w.globals && w.globals.isDestroyed) { enforced.delete(ctx); return; } const elWrap = w && w.dom && w.dom.elWrap; if (!elWrap) return; const features = premiumFeaturesInUse(w, ctx); if (features.length === 0) { enforced.delete(ctx); teardownWatermark(ctx, elWrap); return; } enforced.add(ctx); const key = resolveKey(w); if (licensedForPremium(key)) { teardownWatermark(ctx, elWrap); return; } addWatermark(ctx, elWrap); notifyTrial(ctx, key, features); } catch (e2) { } } function reevaluateLicenseAcrossCharts() { if (!Environment.isBrowser()) return; const visited = /* @__PURE__ */ new Set(); const apex = Environment.getApex(); const instances = apex && apex._chartInstances; if (Array.isArray(instances)) { instances.forEach((entry) => { const chart = entry && entry.chart; if (chart && chart.w && !chart.w.globals.isDestroyed) { visited.add(chart); enforceLicense(chart.w, chart); } }); } Array.from(enforced).forEach((ctx) => { const w = ctx && ctx.w; const elWrap = w && w.dom && w.dom.elWrap; if (!w || w.globals.isDestroyed || !elWrap || elWrap.isConnected === false) { enforced.delete(ctx); return; } if (visited.has(ctx)) return; enforceLicense(w, ctx); }); } o.onChange(reevaluateLicenseAcrossCharts); const PERSPECTIVE_VERSION = 1; const HASH_KEY = "apex"; function toBase64(str) { if (typeof Buffer !== "undefined") { return Buffer.from(str, "utf-8").toString("base64"); } const bytes = new TextEncoder().encode(str); let bin = ""; for (let i2 = 0; i2 < bytes.length; i2++) bin += String.fromCharCode(bytes[i2]); return btoa(bin); } function fromBase64(b64) { if (typeof Buffer !== "undefined") { return Buffer.from(b64, "base64").toString("utf-8"); } const bin = atob(b64); const bytes = new Uint8Array(bin.length); for (let i2 = 0; i2 < bin.length; i2++) bytes[i2] = bin.charCodeAt(i2); return new TextDecoder().decode(bytes); } function base64urlEncode(str) { return toBase64(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); } function base64urlDecode(b64url) { let b64 = b64url.replace(/-/g, "+").replace(/_/g, "/"); while (b64.length % 4) b64 += "="; return fromBase64(b64); } function stripFunctions(obj) { try { return JSON.parse(JSON.stringify(obj)); } catch (e2) { return void 0; } } class Perspectives { /** * @param {import('../../types/internal').ChartStateW} w * @param {import('../../types/internal').ChartContext} ctx */ constructor(w, ctx) { this.w = w; this.ctx = ctx; this._saved = []; this._counter = 0; this._used = false; } /** * Capture the current chart view as a Perspective token. * @returns {{ v: number, view: object, options?: Record<string, any> }} */ capture() { const view = captureViewState(this.w, this.ctx); const token = ( /** @type {any} */ { v: PERSPECTIVE_VERSION, view } ); const options = this._serializableDelta(); if (options && Object.keys(options).length) token.options = options; return token; } /** * Build the whitelisted, function-free option override recorded in the token. * @returns {Record<string, any>} * @private */ _serializableDelta() { const cfg = this.w.config; const whitelist = cfg.chart && cfg.chart.perspectives && cfg.chart.perspectives.serializeOptions || ["theme", "xaxis", "yaxis", "title", "subtitle"]; const delta = {}; whitelist.forEach((path) => { if (cfg[path] !== void 0) { const stripped = stripFunctions(cfg[path]); if (stripped !== void 0) delta[path] = stripped; } }); return delta; } /** * Encode a token (or the current capture) to a compact base64url string. * JSON.stringify drops any functions embedded in annotation params / option * overrides by construction: a shared link carries data; the opening page * supplies its own functions from config. * @param {any} [token] * @returns {string} */ encode(token) { const t2 = token || this.capture(); return base64urlEncode(JSON.stringify(t2)); } /** * Decode a base64url token string. Never throws: returns null on any error * or version mismatch (with a console warning). * @param {string} str * @returns {any | null} */ decode(str) { return Perspectives.decode(str); } /** * Encode the current capture into a `#apex=<token>` URL hash fragment on the * current location. Browser-only; returns '' under SSR. * @returns {string} */ toURL() { if (!Environment.isBrowser()) return ""; const encoded = this.encode(this.capture()); const url = new URL(window.location.href); url.hash = `${HASH_KEY}=${encoded}`; return url.toString(); } /** * Restore a perspective. Accepts a token object or an encoded string. When * the chart is grouped, applies to every synced chart. * * The token's option overrides and `opts.mergeOptions` are folded into the * view restore's ONE updateOptions call (mergeOptions wins over * token.options; the view's own fields win over both). A single render is * deliberate: a second immediate updateOptions would kill the first one's * animation mid-flight, and a chart.type change applied this way morphs * (morph feature) inside the same re-render instead of being re-rendered * over. Consumed by Storyboard for per-beat option payloads. * * @param {any} tokenOrString * @param {{ animate?: boolean, mergeOptions?: Record<string, any> }} [opts] */ apply(tokenOrString, opts = {}) { const token = typeof tokenOrString === "string" ? Perspectives.decode(tokenOrString) : tokenOrString; if (!token || !token.view) return; this._used = true; enforceLicense(this.w, this.ctx); const animate = opts.animate !== void 0 ? opts.animate : true; const combined = Utils.extend( token.options ? Utils.clone(token.options) : {}, opts.mergeOptions || {} ); const mergeOptions = Object.keys(combined).length ? combined : void 0; const targets = this.w.config.chart.group ? this.ctx.getSyncedCharts() : [this.ctx]; targets.forEach((chart) => { applyViewState(chart, token.view, { animate, mergeOptions }); }); } /** * Save the current view under a name in the in-memory registry. * @param {string} name * @returns {string} generated id */ save(name) { const id = `perspective-${++this._counter}`; this._saved.push({ id, name: name || id, token: this.capture() }); this._used = true; enforceLicense(this.w, this.ctx); return id; } /** * List saved perspectives. * @returns {{ id: string, name: string, token: any }[]} */ list() { return this._saved.map((s2) => ({ id: s2.id, name: s2.name, token: s2.token })); } /** * Delete a saved perspective by id. * @param {string} id */ delete(id) { const i2 = this._saved.findIndex((s2) => s2.id === id); if (i2 > -1) this._saved.splice(i2, 1); } /** Drop the saved-views registry (called on full destroy). */ teardown() { this._saved = []; this._counter = 0; } // ── static, pure helpers (available once the feature is imported) ───────── /** * @param {string} str base64url token * @returns {any | null} */ static decode(str) { if (typeof str !== "string" || !str) return null; try { const token = JSON.parse(base64urlDecode(str)); if (!token || typeof token !== "object") return null; if (token.v !== PERSPECTIVE_VERSION) { console.warn( `apexcharts: unsupported perspective version ${token.v} (expected ${PERSPECTIVE_VERSION}).` ); return null; } return token; } catch (e2) { console.warn("apexcharts: failed to decode perspective token.", e2); return null; } } /** * Parse a `#apex=<token>` fragment out of an href (or the current location in * a browser). Pure and Node-safe when given an explicit href. * @param {string} [href] * @returns {any | null} */ static fromURL(href) { try { const target = href || (Environment.isBrowser() ? window.location.href : ""); if (!target) return null; const url = new URL(target); const hash = url.hash.replace(/^#/, ""); if (!hash) return null; const pair = hash.split("&").map((p) => p.split("=")).find((p) => p[0] === HASH_KEY); if (!pair || pair[1] == null) return null; return Perspectives.decode(decodeURIComponent(pair[1])); } catch (e2) { return null; } } } ApexCharts__default.registerFeatures({ perspectives: Perspectives }); ApexCharts__default.perspectives = { /** @param {string} str */ decode: (str) => { markPerspectivesTokenDecoded(); return Perspectives.decode(str); }, /** @param {string} [href] */ fromURL: (href) => { markPerspectivesTokenDecoded(); return Perspectives.fromURL(href); } }; export { default2 as default };