UNPKG

ag-charts-core

Version:

Advanced Charting / Charts supporting Javascript / Typescript / React / Angular / Vue

1,628 lines (1,601 loc) 278 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __decorateClass = (decorators, target, key, kind) => { var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result; if (kind && result) __defProp(target, key, result); return result; }; // packages/ag-charts-core/src/globals/debug-logger.ts var debug_logger_exports = {}; __export(debug_logger_exports, { Time: () => Time, check: () => check, create: () => create, inDevelopmentMode: () => inDevelopmentMode }); // packages/ag-charts-core/src/utils/arrays.ts function toArray(value) { if (value === void 0) { return []; } return Array.isArray(value) ? value : [value]; } function unique(array2) { return Array.from(new Set(array2)); } function groupBy(array2, iteratee) { return array2.reduce((result, item) => { const groupKey = iteratee(item); result[groupKey] ?? (result[groupKey] = []); result[groupKey].push(item); return result; }, {}); } function arraysEqual(a, b) { if (a == null || b == null || a.length !== b.length) { return false; } for (let i = 0; i < a.length; i++) { if (Array.isArray(a[i]) && Array.isArray(b[i])) { if (!arraysEqual(a[i], b[i])) { return false; } } else if (a[i] !== b[i]) { return false; } } return true; } function circularSliceArray(data, size, offset = 0) { if (data.length === 0) { return []; } const result = []; for (let i = 0; i < size; i++) { result.push(data.at((i + offset) % data.length)); } return result; } function sortBasedOnArray(baseArray, orderArray) { const orderMap = /* @__PURE__ */ new Map(); for (const [index, item] of orderArray.entries()) { orderMap.set(item, index); } return baseArray.sort((a, b) => { const indexA = orderMap.get(a) ?? Infinity; const indexB = orderMap.get(b) ?? Infinity; return indexA - indexB; }); } function dropFirstWhile(array2, cond) { let i = 0; while (i < array2.length && cond(array2[i])) { i += 1; } const deleteCount = i; if (deleteCount !== 0) array2.splice(0, deleteCount); } function dropLastWhile(array2, cond) { let i = array2.length - 1; while (i >= 0 && cond(array2[i])) { i -= 1; } const deleteCount = array2.length - 1 - i; if (deleteCount !== 0) array2.splice(array2.length - deleteCount, deleteCount); } function distribute(min, max, maxCount) { const values = [min]; const step = Math.round((max - min) / (maxCount - 1)); if (step > 0) { for (let i = min + step; i < max; i += step) { const length2 = values.push(i); if (length2 >= maxCount - 1) break; } } values.push(max); return values; } // packages/ag-charts-core/src/utils/dom/globalsProxy.ts var verifiedGlobals = {}; if (typeof globalThis.window !== "undefined") { verifiedGlobals.window = globalThis.window; } if (typeof document !== "undefined") { verifiedGlobals.document = document; } else if (typeof globalThis.global !== "undefined") { verifiedGlobals.document = globalThis.document; } function getDocument(propertyName) { return propertyName ? verifiedGlobals.document?.[propertyName] : verifiedGlobals.document; } function getWindow(propertyName) { return propertyName ? verifiedGlobals.window?.[propertyName] : verifiedGlobals.window; } function setDocument(document2) { verifiedGlobals.document = document2; } function setWindow(window) { verifiedGlobals.window = window; } // packages/ag-charts-core/src/utils/dom/domElements.ts function createElement(tagName, className, style2) { const element = getDocument().createElement(tagName); if (typeof className === "object") { style2 = className; className = void 0; } if (className) { for (const name of className.split(" ")) { element.classList.add(name); } } if (style2) { Object.assign(element.style, style2); } return element; } function createSvgElement(elementName) { return getDocument().createElementNS("http://www.w3.org/2000/svg", elementName); } // packages/ag-charts-core/src/utils/dom/domDownload.ts function downloadUrl(dataUrl, fileName) { const body = getDocument("body"); const element = createElement("a", { display: "none" }); element.href = dataUrl; element.download = fileName; body.appendChild(element); element.click(); setTimeout(() => element.remove()); } // packages/ag-charts-core/src/utils/dom/domEvents.ts function attachListener(element, eventName, handler, options) { element.addEventListener(eventName, handler, options); return () => element.removeEventListener(eventName, handler, options); } // packages/ag-charts-core/src/utils/dom/domUtils.ts var style; function parseColor(color2) { if (style == null) { const OptionConstructor = getWindow("Option"); style = new OptionConstructor().style; } style.color = color2; const result = style.color || null; style.color = ""; return result; } // packages/ag-charts-core/src/globals/logger.ts var logger_exports = {}; __export(logger_exports, { error: () => error, errorOnce: () => errorOnce, log: () => log, logGroup: () => logGroup, reset: () => reset, table: () => table, warn: () => warn, warnOnce: () => warnOnce }); var doOnceCache = /* @__PURE__ */ new Set(); function log(...logContent) { console.log(...logContent); } function warn(message, ...logContent) { console.warn(`AG Charts - ${message}`, ...logContent); } function error(message, ...logContent) { if (typeof message === "object") { console.error(`AG Charts error`, message, ...logContent); } else { console.error(`AG Charts - ${message}`, ...logContent); } } function table(...logContent) { console.table(...logContent); } function guardOnce(messageOrError, prefix, cb) { let message; if (messageOrError instanceof Error) { message = messageOrError.message; } else if (typeof messageOrError === "string") { message = messageOrError; } else if (typeof messageOrError === "object") { message = JSON.stringify(messageOrError); } else { message = String(messageOrError); } const cacheKey = `${prefix}: ${message}`; if (doOnceCache.has(cacheKey)) return; cb(messageOrError); doOnceCache.add(cacheKey); } function warnOnce(messageOrError, ...logContent) { guardOnce(messageOrError, "Logger.warn", (message) => warn(message, ...logContent)); } function errorOnce(messageOrError, ...logContent) { guardOnce(messageOrError, "Logger.error", (message) => error(message, ...logContent)); } function reset() { doOnceCache.clear(); } function logGroup(name, cb) { console.groupCollapsed(name); try { return cb(); } finally { console.groupEnd(); } } // packages/ag-charts-core/src/globals/debug-logger.ts var LongTimePeriodThreshold = 2e3; var timeOfLastLog = Date.now(); function logTimeGap() { const timeSinceLastLog = Date.now() - timeOfLastLog; if (timeSinceLastLog > LongTimePeriodThreshold) { const prettyDuration = (Math.floor(timeSinceLastLog / 100) / 10).toFixed(1); log(`**** ${prettyDuration}s since last log message ****`); } timeOfLastLog = Date.now(); } function create(...debugSelectors) { const resultFn = (...logContent) => { if (check(...debugSelectors)) { if (typeof logContent[0] === "function") { logContent = toArray(logContent[0]()); } logTimeGap(); log(...logContent); } }; return Object.assign(resultFn, { check: () => check(...debugSelectors), group: (name, cb) => { if (check(...debugSelectors)) { return logGroup(name, cb); } return cb(); } }); } function check(...debugSelectors) { if (debugSelectors.length === 0) { debugSelectors.push(true); } const chartDebug = toArray(getWindow("agChartsDebug")); return chartDebug.some((selector) => debugSelectors.includes(selector)); } function inDevelopmentMode(fn) { if (check("dev")) { return fn(); } } function Time(name, opts = {}) { const { logResult = true, logStack = false, logArgs = false, logData } = opts; return function(_target, _propertyKey, descriptor) { const method = descriptor.value; descriptor.value = function(...args) { const start2 = performance.now(); const result = method.apply(this, args); const duration = performance.now() - start2; const logMessage = { duration }; if (logResult) logMessage.result = result; if (logArgs) logMessage.args = args; if (logStack) logMessage.stack = new Error("Stack trace for timing debug").stack; if (logData) logMessage.logData = logData(this); log(name, logMessage); return result; }; }; } // packages/ag-charts-core/src/globals/debug-metrics.ts var debug_metrics_exports = {}; __export(debug_metrics_exports, { flush: () => flush, record: () => record }); var metrics = /* @__PURE__ */ new Map(); function record(key, value) { if (!check("scene:stats:verbose")) return; metrics.set(key, value); } function flush() { const result = Object.fromEntries(metrics); metrics.clear(); return result; } // packages/ag-charts-core/src/globals/enterpriseRegistry.ts var enterpriseRegistry = {}; // packages/ag-charts-core/src/globals/moduleRegistry.ts var moduleRegistry_exports = {}; __export(moduleRegistry_exports, { RegistryMode: () => RegistryMode, getAxisModule: () => getAxisModule, getChartModule: () => getChartModule, getPresetModule: () => getPresetModule, getSeriesModule: () => getSeriesModule, hasModule: () => hasModule, isEnterprise: () => isEnterprise, isIntegrated: () => isIntegrated, isModuleType: () => isModuleType, isUmd: () => isUmd, listModules: () => listModules, listModulesByType: () => listModulesByType, register: () => register, registerModules: () => registerModules, reset: () => reset2, setRegistryMode: () => setRegistryMode }); // packages/ag-charts-core/src/interfaces/moduleDefinition.ts var ModuleType = /* @__PURE__ */ ((ModuleType2) => { ModuleType2["Chart"] = "chart"; ModuleType2["Axis"] = "axis"; ModuleType2["Series"] = "series"; ModuleType2["Plugin"] = "plugin"; ModuleType2["AxisPlugin"] = "axis:plugin"; ModuleType2["SeriesPlugin"] = "series:plugin"; ModuleType2["Preset"] = "preset"; return ModuleType2; })(ModuleType || {}); // packages/ag-charts-core/src/globals/moduleRegistry.ts var RegistryMode = /* @__PURE__ */ ((RegistryMode2) => { RegistryMode2["Enterprise"] = "enterprise"; RegistryMode2["Integrated"] = "integrated"; RegistryMode2["UMD"] = "umd"; return RegistryMode2; })(RegistryMode || {}); var registeredModes = /* @__PURE__ */ new Set(); var registeredModules = /* @__PURE__ */ new Map(); function registerModuleDefinition(def) { registeredModules.set(def.name, def); if (def.dependencies) { for (const dependency of def.dependencies) { register(dependency); } } } function register(def) { const existingDefinition = registeredModules.get(def.name); if (!existingDefinition) { registerModuleDefinition(def); return; } if (existingDefinition.version === def.version) { if (!existingDefinition.enterprise && def.enterprise) { registerModuleDefinition(def); } return; } throw new Error( [ `AG Charts - Module '${def.name}' already registered with different version:`, `${existingDefinition.version} vs ${def.version}`, ``, `Check your package.json for conflicting dependencies - depending on your package manager`, `one of these commands may help:`, `- npm ls ag-charts-community`, `- yarn why ag-charts-community` ].join("\n") ); } function registerModules(definitions) { for (const definition of definitions.flat()) { register(definition); } } function reset2() { registeredModes.clear(); registeredModules.clear(); } function hasModule(moduleName) { return registeredModules.has(moduleName); } function* listModules() { for (const definition of registeredModules.values()) { yield definition; } } function* listModulesByType(moduleType) { for (const definition of registeredModules.values()) { if (isModuleType(moduleType, definition)) { yield definition; } } } function getAxisModule(moduleName) { const definition = registeredModules.get(moduleName); if (isModuleType("axis" /* Axis */, definition)) { return definition; } } function getChartModule(moduleName) { const definition = registeredModules.get(moduleName); if (isModuleType("chart" /* Chart */, definition)) { return definition; } throw new Error( `AG Charts - Unknown chart type; Check options are correctly structured and series types are specified` ); } function getPresetModule(moduleName) { const definition = registeredModules.get(moduleName); if (isModuleType("preset" /* Preset */, definition)) { return definition; } } function getSeriesModule(moduleName) { const definition = registeredModules.get(moduleName); if (isModuleType("series" /* Series */, definition)) { return definition; } } function setRegistryMode(registryFlag) { registeredModes.add(registryFlag); } function isEnterprise() { return registeredModes.has("enterprise" /* Enterprise */); } function isIntegrated() { return registeredModes.has("integrated" /* Integrated */); } function isUmd() { return registeredModes.has("umd" /* UMD */); } function isModuleType(moduleType, definition) { return definition?.type === moduleType; } // packages/ag-charts-core/src/utils/cleanupRegistry.ts var CleanupRegistry = class { constructor() { this.callbacks = /* @__PURE__ */ new Set(); } flush() { for (const cb of this.callbacks) { cb(); } this.callbacks.clear(); } merge(registry) { for (const cb of registry.callbacks) { this.callbacks.add(cb); } } register(...callbacks) { for (const cb of callbacks) { if (!cb) continue; this.callbacks.add(cb); } } }; // packages/ag-charts-core/src/globals/moduleInstance.ts var AbstractModuleInstance = class { constructor() { this.cleanup = new CleanupRegistry(); } destroy() { this.cleanup.flush(); } }; // packages/ag-charts-core/src/interfaces/scaleTypes.ts function extractDomain(value) { return value.domain; } var ScaleAlignment = /* @__PURE__ */ ((ScaleAlignment2) => { ScaleAlignment2[ScaleAlignment2["Leading"] = 0] = "Leading"; ScaleAlignment2[ScaleAlignment2["Trailing"] = 1] = "Trailing"; ScaleAlignment2[ScaleAlignment2["Interpolate"] = 2] = "Interpolate"; return ScaleAlignment2; })(ScaleAlignment || {}); // packages/ag-charts-core/src/classes/eventEmitter.ts var EventEmitter = class { constructor() { this.events = /* @__PURE__ */ new Map(); } /** * Registers an event listener. * @param eventName The event name to listen for. * @param listener The callback to be invoked on the event. * @returns A function to unregister the listener. */ on(eventName, listener) { if (!this.events.has(eventName)) { this.events.set(eventName, /* @__PURE__ */ new Set()); } this.events.get(eventName)?.add(listener); return () => this.off(eventName, listener); } /** * Unregisters an event listener. * @param eventName The event name to stop listening for. * @param listener The callback to be removed. */ off(eventName, listener) { const eventListeners = this.events.get(eventName); if (eventListeners) { eventListeners.delete(listener); if (eventListeners.size === 0) { this.events.delete(eventName); } } } /** * Emits an event to all registered listeners. * @param eventName The name of the event to emit. * @param event The event payload. */ emit(eventName, event) { const listeners = this.events.get(eventName); if (listeners) { for (const callback2 of listeners) { callback2(event); } } } /** * Clears all listeners for a specific event or all events if no event name is provided. * @param eventName (Optional) The name of the event to clear listeners for. If not provided, all listeners for all events are cleared. */ clear(eventName) { if (eventName) { this.events.delete(eventName); } else { this.events.clear(); } } }; // packages/ag-charts-core/src/classes/lruCache.ts var LRUCache = class { constructor(maxCacheSize) { this.maxCacheSize = maxCacheSize; this.store = /* @__PURE__ */ new Map(); if (maxCacheSize <= 0) { throw new Error("LRUCache size must be greater than 0"); } } get(key) { if (!this.store.has(key)) return; const value = this.store.get(key); this.store.delete(key); this.store.set(key, value); return value; } has(key) { return this.store.has(key); } set(key, value) { this.store.set(key, value); if (this.store.size > this.maxCacheSize) { this.store.delete(this.store.keys().next().value); } return value; } clear() { this.store.clear(); } }; // packages/ag-charts-core/src/utils/numbers.ts function clamp(min, value, max) { return Math.min(max, Math.max(min, value)); } function inRange(value, range2, epsilon = 1e-10) { return value >= range2[0] - epsilon && value <= range2[1] + epsilon; } function isNumberEqual(a, b, epsilon = 1e-10) { return a === b || Math.abs(a - b) < epsilon; } function isNegative(value) { return Math.sign(value) === -1 || Object.is(value, -0); } function isInteger(value) { return value % 1 === 0; } function roundTo(value, decimals = 2) { const base = 10 ** decimals; return Math.round(value * base) / base; } function modulus(n, m) { return Math.floor(n % m + (n < 0 ? Math.abs(m) : 0)); } function countFractionDigits(value) { if (Math.floor(value) === value) { return 0; } let valueString = String(value); let exponent = 0; if (value < 1e-6 || value >= 1e21) { let exponentString; [valueString, exponentString] = valueString.split("e"); if (exponentString != null) { exponent = Number(exponentString); } } const decimalPlaces2 = valueString.split(".")[1]?.length ?? 0; return Math.max(decimalPlaces2 - exponent, 0); } // packages/ag-charts-core/src/utils/typeGuards.ts function isDefined(val) { return val != null; } function isArray(value) { return Array.isArray(value); } function isBoolean(value) { return typeof value === "boolean"; } function isDate(value) { return value instanceof Date; } function isValidDate(value) { return isDate(value) && !Number.isNaN(Number(value)); } function isRegExp(value) { return value instanceof RegExp; } function isFunction(value) { return typeof value === "function"; } function isObject(value) { return typeof value === "object" && value !== null && !isArray(value); } function isObjectLike(value) { return isArray(value) || isPlainObject(value); } function isPlainObject(value) { return typeof value === "object" && value !== null && value.constructor?.name === "Object"; } function isEmptyObject(value) { if (typeof value !== "object" || value === null) return false; for (const _ in value) { return false; } return true; } function isString(value) { return typeof value === "string"; } function isNumber(value) { return typeof value === "number"; } function isFiniteNumber(value) { return Number.isFinite(value); } function isHtmlElement(value) { return typeof globalThis.window !== "undefined" && value instanceof HTMLElement; } function isEnumKey(enumObject, enumKey) { return isString(enumKey) && Object.keys(enumObject).includes(enumKey); } function isEnumValue(enumObject, enumValue) { return Object.values(enumObject).includes(enumValue); } function isSymbol(value) { return typeof value === "symbol"; } function isColor(value) { return isString(value) && (value === "none" || parseColor(value) != null); } function isKeyOf(value, container) { return value in container; } // packages/ag-charts-core/src/modules/format/numberFormat.ts var formatRegEx = /^(?:(.)?([<>=^]))?([+\-( ])?([$€£¥₣₹#])?(0)?(\d+)?(,)?(?:\.(\d+))?(~)?([%a-z])?$/i; var surroundedRegEx = /^((?:[^#]|#[^{])*)#{([^}]+)}(.*)$/; function isValidNumberFormat(value) { if (!isString(value)) return false; const match = surroundedRegEx.exec(value); return formatRegEx.test(match ? match[2] : value); } function parseNumberFormat(format) { let prefix; let suffix; const surrounded = surroundedRegEx.exec(format); if (surrounded) { [, prefix, format, suffix] = surrounded; } const match = formatRegEx.exec(format); if (!match) { logger_exports.warnOnce(`The number formatter is invalid: ${format}`); return; } const [, fill, align, sign, symbol, zero, width2, comma, precision, trim, type] = match; return { fill, align, sign, symbol, zero, width: Number.parseInt(width2), comma, precision: Number.parseInt(precision), trim: Boolean(trim), type, prefix, suffix }; } function createNumberFormatter(format) { const options = typeof format === "string" ? parseNumberFormat(format) : format; if (options == null) return; const { fill, align, sign = "-", symbol, zero, width: width2, comma, type, prefix = "", suffix = "", precision } = options; let { trim } = options; const precisionIsNaN = precision == null || Number.isNaN(precision); let formatBody; if (!type) { formatBody = decimalTypes["g"]; trim = true; } else if (type in decimalTypes && type in integerTypes) { formatBody = precisionIsNaN ? integerTypes[type] : decimalTypes[type]; } else if (type in decimalTypes) { formatBody = decimalTypes[type]; } else if (type in integerTypes) { formatBody = integerTypes[type]; } else { throw new Error(`The number formatter type is invalid: ${type}`); } const defaultFormatterPrecision = type ? 6 : 12; let formatterPrecision; if (!precisionIsNaN) { formatterPrecision = precision; } let padAlign = align; let padFill = fill; if (zero) { padFill ?? (padFill = "0"); padAlign ?? (padAlign = "="); } return (n, fractionDigits) => { let effectivePrecision; if (formatterPrecision != null) { effectivePrecision = formatterPrecision; } else if (type === "f" || type === "%") { effectivePrecision = fractionDigits ?? defaultFormatterPrecision; } else if (type) { effectivePrecision = defaultFormatterPrecision; } else { effectivePrecision = fractionDigits ?? defaultFormatterPrecision; } let result = formatBody(n, effectivePrecision); if (trim) { result = removeTrailingZeros(result); } if (comma) { result = insertSeparator(result, comma); } const symbolPrefix = getSymbolPrefix(symbol, type); const symbolPrefixLength = symbolPrefix?.length ?? 0; if (symbolPrefix) { result = `${symbolPrefix}${result}`; } if (type === "s") { result = `${result}${getSIPrefix(n)}`; } if (type === "%" || type === "p") { result = `${result}%`; } const { value: signedResult, prefixLength: signPrefixLength } = addSign(n, result, sign); const totalPrefixLength = signPrefixLength + symbolPrefixLength; let output = signedResult; if (width2 != null && !Number.isNaN(width2)) { output = addPadding(output, width2, padFill ?? " ", padAlign, totalPrefixLength); } output = `${prefix}${output}${suffix}`; return output; }; } var integerTypes = { b: (n) => absFloor(n).toString(2), c: (n) => String.fromCodePoint(n), d: (n) => Math.round(Math.abs(n)).toFixed(0), o: (n) => absFloor(n).toString(8), x: (n) => absFloor(n).toString(16), X: (n) => integerTypes.x(n).toUpperCase(), n: (n) => integerTypes.d(n), "%": (n) => `${absFloor(n * 100).toFixed(0)}` }; var decimalTypes = { e: (n, f) => Math.abs(n).toExponential(f), E: (n, f) => decimalTypes.e(n, f).toUpperCase(), f: (n, f) => Math.abs(n).toFixed(f), F: (n, f) => decimalTypes.f(n, f).toUpperCase(), g: (n, f) => { if (n === 0) { return "0"; } const a = Math.abs(n); const p = Math.floor(Math.log10(a)); if (p >= -4 && p < f) { return a.toFixed(f - 1 - p); } return a.toExponential(f - 1); }, G: (n, f) => decimalTypes.g(n, f).toUpperCase(), n: (n, f) => decimalTypes.g(n, f), p: (n, f) => decimalTypes.r(n * 100, f), r: (n, f) => { if (n === 0) { return "0"; } const a = Math.abs(n); const p = Math.floor(Math.log10(a)); const q = p - (f - 1); if (q <= 0) { return a.toFixed(-q); } const x = 10 ** q; return (Math.round(a / x) * x).toFixed(); }, s: (n, f) => { const p = getSIPrefixPower(n); return decimalTypes.r(n / 10 ** p, f); }, "%": (n, f) => decimalTypes.f(n * 100, f) }; var minSIPrefix = -24; var maxSIPrefix = 24; var siPrefixes = { [minSIPrefix]: "y", [-21]: "z", [-18]: "a", [-15]: "f", [-12]: "p", [-9]: "n", [-6]: "\xB5", [-3]: "m", [0]: "", [3]: "k", [6]: "M", [9]: "G", [12]: "T", [15]: "P", [18]: "E", [21]: "Z", [maxSIPrefix]: "Y" }; var minusSign = "\u2212"; function absFloor(n) { return Math.floor(Math.abs(n)); } function removeTrailingZeros(numString) { if (!numString.endsWith("0") || !numString.includes(".")) return numString; let endIndex = numString.length - 1; while (endIndex > 0) { if (numString[endIndex] == "0") { endIndex -= 1; } else if (numString[endIndex] == ".") { endIndex -= 1; break; } else { break; } } return numString.substring(0, endIndex + 1); } function insertSeparator(numString, separator) { let dotIndex = numString.indexOf("."); if (dotIndex < 0) { dotIndex = numString.length; } const integerChars = numString.substring(0, dotIndex).split(""); const fractionalPart = numString.substring(dotIndex); for (let i = integerChars.length - 3; i > 0; i -= 3) { integerChars.splice(i, 0, separator); } return `${integerChars.join("")}${fractionalPart}`; } function getSIPrefix(n) { return siPrefixes[getSIPrefixPower(n)]; } function getSIPrefixPower(n) { return clamp(minSIPrefix, n ? Math.floor(Math.log10(Math.abs(n)) / 3) * 3 : 0, maxSIPrefix); } function addSign(num, numString, signType = "") { if (signType === "(") { if (num >= 0) { return { value: numString, prefixLength: 0 }; } return { value: `(${numString})`, prefixLength: 1 }; } let signPrefix = ""; if (num < 0) { signPrefix = minusSign; } else if (signType === "+") { signPrefix = "+"; } else if (signType === " ") { signPrefix = " "; } return { value: `${signPrefix}${numString}`, prefixLength: signPrefix.length }; } function addPadding(numString, width2, fill = " ", align = ">", prefixLength = 0) { const padSize = width2 - numString.length; if (padSize <= 0) { return numString; } const padding2 = fill.repeat(padSize); if (align === "=") { const clampedPrefix = Math.min(Math.max(prefixLength, 0), numString.length); const start2 = numString.slice(0, clampedPrefix); const rest = numString.slice(clampedPrefix); return `${start2}${padding2}${rest}`; } if (align === ">" || !align) { return padding2 + numString; } else if (align === "<") { return `${numString}${padding2}`; } else if (align === "^") { const padLeft = Math.ceil(padSize / 2); const padRight = Math.floor(padSize / 2); return `${fill.repeat(padLeft)}${numString}${fill.repeat(padRight)}`; } return padding2 + numString; } function getSymbolPrefix(symbol, type) { if (symbol === "#") { switch (type) { case "b": return "0b"; case "o": return "0o"; case "x": return "0x"; case "X": return "0X"; default: return ""; } } return symbol ?? ""; } // packages/ag-charts-core/src/modules/format/timeFormat.ts var CONSTANTS = { periods: ["AM", "PM"], days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] }; function dayOfYear(date2, startOfYear = new Date(date2.getFullYear(), 0, 1)) { const startOffset = date2.getTimezoneOffset() - startOfYear.getTimezoneOffset(); const timeDiff = date2.getTime() - startOfYear.getTime() + startOffset * 6e4; const timeOneDay = 36e5 * 24; return Math.floor(timeDiff / timeOneDay); } function weekOfYear(date2, startDay) { const startOfYear = new Date(date2.getFullYear(), 0, 1); const startOfYearDay = startOfYear.getDay(); const firstWeekStartOffset = (startDay - startOfYearDay + 7) % 7; const startOffset = new Date(date2.getFullYear(), 0, firstWeekStartOffset + 1); if (startOffset <= date2) { return Math.floor(dayOfYear(date2, startOffset) / 7) + 1; } return 0; } var SUNDAY = 0; var MONDAY = 1; var THURSDAY = 4; function isoWeekOfYear(date2, year = date2.getFullYear()) { const firstOfYear = new Date(year, 0, 1); const firstOfYearDay = firstOfYear.getDay(); const firstThursdayOffset = (THURSDAY - firstOfYearDay + 7) % 7; const startOffset = new Date(year, 0, firstThursdayOffset - (THURSDAY - MONDAY) + 1); if (startOffset <= date2) { return Math.floor(dayOfYear(date2, startOffset) / 7) + 1; } return isoWeekOfYear(date2, year - 1); } function timezone(date2) { const offset = date2.getTimezoneOffset(); const unsignedOffset = Math.abs(offset); const sign = offset > 0 ? "-" : "+"; return `${sign}${pad(Math.floor(unsignedOffset / 60), 2, "0")}${pad(Math.floor(unsignedOffset % 60), 2, "0")}`; } var FORMATTERS = { a: (d) => CONSTANTS.shortDays[d.getDay()], A: (d) => CONSTANTS.days[d.getDay()], b: (d) => CONSTANTS.shortMonths[d.getMonth()], B: (d) => CONSTANTS.months[d.getMonth()], c: "%x, %X", d: (d, p) => pad(d.getDate(), 2, p ?? "0"), e: "%_d", f: (d, p) => pad(d.getMilliseconds() * 1e3, 6, p ?? "0"), H: (d, p) => pad(d.getHours(), 2, p ?? "0"), I: (d, p) => { const hours = d.getHours() % 12; return hours === 0 ? "12" : pad(hours, 2, p ?? "0"); }, j: (d, p) => pad(dayOfYear(d) + 1, 3, p ?? "0"), m: (d, p) => pad(d.getMonth() + 1, 2, p ?? "0"), M: (d, p) => pad(d.getMinutes(), 2, p ?? "0"), L: (d, p) => pad(d.getMilliseconds(), 3, p ?? "0"), p: (d) => d.getHours() < 12 ? "AM" : "PM", Q: (d) => String(d.getTime()), s: (d) => String(Math.floor(d.getTime() / 1e3)), S: (d, p) => pad(d.getSeconds(), 2, p ?? "0"), u: (d) => { let day = d.getDay(); if (day < 1) day += 7; return String(day % 7); }, U: (d, p) => pad(weekOfYear(d, SUNDAY), 2, p ?? "0"), V: (d, p) => pad(isoWeekOfYear(d), 2, p ?? "0"), w: (d, p) => pad(d.getDay(), 2, p ?? "0"), W: (d, p) => pad(weekOfYear(d, MONDAY), 2, p ?? "0"), x: "%-m/%-d/%Y", X: "%-I:%M:%S %p", y: (d, p) => pad(d.getFullYear() % 100, 2, p ?? "0"), Y: (d, p) => pad(d.getFullYear(), 4, p ?? "0"), Z: (d) => timezone(d), "%": () => "%" }; var PADS = { _: " ", "0": "0", "-": "" }; function pad(value, size, padChar) { const output = String(Math.floor(value)); if (output.length >= size) { return output; } return `${padChar.repeat(size - output.length)}${output}`; } function buildDateFormatter(formatString) { const formatParts = []; while (formatString.length > 0) { let nextEscapeIdx = formatString.indexOf("%"); if (nextEscapeIdx !== 0) { const literalPart = nextEscapeIdx > 0 ? formatString.substring(0, nextEscapeIdx) : formatString; formatParts.push(literalPart); } if (nextEscapeIdx < 0) break; const maybePadSpecifier = formatString[nextEscapeIdx + 1]; const maybePad = PADS[maybePadSpecifier]; if (maybePad != null) { nextEscapeIdx++; } const maybeFormatterSpecifier = formatString[nextEscapeIdx + 1]; const maybeFormatter = FORMATTERS[maybeFormatterSpecifier]; if (typeof maybeFormatter === "function") { formatParts.push([maybeFormatter, maybePad]); } else if (typeof maybeFormatter === "string") { const formatter2 = buildDateFormatter(maybeFormatter); formatParts.push([formatter2, maybePad]); } else { formatParts.push(`${maybePad ?? ""}${maybeFormatterSpecifier}`); } formatString = formatString.substring(nextEscapeIdx + 2); } return (dateTime) => { const dateTimeAsDate = typeof dateTime === "number" ? new Date(dateTime) : dateTime; return formatParts.map((c) => typeof c === "string" ? c : c[0](dateTimeAsDate, c[1])).join(""); }; } // packages/ag-charts-core/src/utils/functions.ts function debounce(callback2, waitMs = 0, options) { const { leading = false, trailing = true, maxWait = Infinity } = options ?? {}; let timerId; let startTime; if (maxWait < waitMs) { throw new Error("Value of maxWait cannot be lower than waitMs."); } function debounceCallback(...args) { if (leading && !startTime) { startTime = Date.now(); timerId = setTimeout(() => startTime = null, waitMs); callback2(...args); return; } let adjustedWaitMs = waitMs; if (maxWait !== Infinity && startTime) { const elapsedTime = Date.now() - startTime; if (waitMs > maxWait - elapsedTime) { adjustedWaitMs = maxWait - elapsedTime; } } clearTimeout(timerId); startTime ?? (startTime = Date.now()); timerId = setTimeout(() => { startTime = null; if (trailing) { callback2(...args); } }, adjustedWaitMs); } return Object.assign(debounceCallback, { cancel() { clearTimeout(timerId); startTime = null; } }); } function throttle(callback2, waitMs, options) { const { leading = true, trailing = true } = options ?? {}; let timerId; let lastArgs; let shouldWait = false; function timeoutHandler() { if (trailing && lastArgs) { timerId = setTimeout(timeoutHandler, waitMs); callback2(...lastArgs); } else { shouldWait = false; } lastArgs = null; } function throttleCallback(...args) { if (shouldWait) { lastArgs = args; } else { shouldWait = true; timerId = setTimeout(timeoutHandler, waitMs); if (leading) { callback2(...args); } else { lastArgs = args; } } } return Object.assign(throttleCallback, { cancel() { clearTimeout(timerId); shouldWait = false; lastArgs = null; } }); } function safeCall(callback2, args, errorPath = "") { try { return callback2(...args); } catch (error2) { const postfix = errorPath ? ` \`${errorPath}\`` : ""; warnOnce(`Uncaught exception in user callback${postfix}`, error2); } } // packages/ag-charts-core/src/utils/textUtils.ts var EllipsisChar = "\u2026"; var LineSplitter = /\r?\n/g; var TrimEdgeGuard = "\u200B"; function toFontString({ fontSize, fontStyle, fontWeight, fontFamily }) { let fontString = ""; if (fontStyle && fontStyle !== "normal") { fontString += `${fontStyle} `; } if (fontWeight && fontWeight !== "normal" && fontWeight !== 400) { fontString += `${fontWeight} `; } fontString += `${fontSize}px`; fontString += ` ${fontFamily}`; return fontString; } function calcLineHeight(fontSize, lineHeightRatio = 1.15) { return Math.round(fontSize * lineHeightRatio); } function toTextString(value) { return String(value ?? ""); } var TrimCharsRegex = /[\s.,;:-]{1,5}$/; function appendEllipsis(text) { return text.replace(TrimCharsRegex, "") + EllipsisChar; } function guardTextEdges(str) { return TrimEdgeGuard + str + TrimEdgeGuard; } function unguardTextEdges(str) { return str.replaceAll(TrimEdgeGuard, ""); } function isTruncated(value) { return isArray(value) ? isSegmentTruncated(value.at(-1)) : isTextTruncated(toTextString(value)); } function isTextTruncated(str) { return str.endsWith(EllipsisChar); } function isSegmentTruncated(segment) { return toTextString(segment?.text).endsWith(EllipsisChar); } // packages/ag-charts-core/src/utils/strings.ts function joinFormatted(values, conjunction = "and", format = String, maxItems = Infinity) { if (values.length === 0) { return ""; } else if (values.length === 1) { return format(values[0]); } values = values.map(format); const lastValue = values.pop(); if (values.length >= maxItems) { const remainingCount = values.length - (maxItems - 1); return `${values.slice(0, maxItems - 1).join(", ")}, and ${remainingCount} more ${conjunction} ${lastValue}`; } return `${values.join(", ")} ${conjunction} ${lastValue}`; } function stringifyValue(value, maxLength = Infinity) { if (typeof value === "number") { if (Number.isNaN(value)) { return "NaN"; } else if (value === Infinity) { return "Infinity"; } else if (value === -Infinity) { return "-Infinity"; } } const strValue = JSON.stringify(value) ?? typeof value; if (strValue.length > maxLength) { return `${strValue.slice(0, maxLength)}... (+${strValue.length - maxLength} characters)`; } return strValue; } function countLines(text) { let count = 1; for (let i = 0; i < text.length; i++) { if (text.codePointAt(i) === 10) { count++; } } return count; } function levenshteinDistance(a, b) { if (a === b) return 0; const [shorter, longer] = a.length < b.length ? [a, b] : [b, a]; const m = shorter.length; const n = longer.length; let prevRow = new Array(m + 1).fill(0).map((_, i) => i); let currRow = new Array(m + 1); for (let i = 1; i <= n; i++) { currRow[0] = i; for (let j = 1; j <= m; j++) { const cost = longer[i - 1] === shorter[j - 1] ? 0 : 1; currRow[j] = Math.min( prevRow[j] + 1, // Deletion currRow[j - 1] + 1, // Insertion prevRow[j - 1] + cost // Substitution ); } [prevRow, currRow] = [currRow, prevRow]; } return prevRow[m]; } function kebabCase(a) { return a.replaceAll(KEBAB_CASE_REGEX, (match, offset) => (offset > 0 ? "-" : "") + match.toLowerCase()); } var KEBAB_CASE_REGEX = /[A-Z]+(?![a-z])|[A-Z]/g; function toPlainText(text, fallback = "") { if (text == null) { return fallback; } else if (isArray(text)) { return text.map((segment) => toTextString(segment.text)).join(""); } else if (isString(text)) { return text; } else { return String(text); } } // packages/ag-charts-core/src/utils/validation.ts var descriptionSymbol = Symbol("description"); var requiredSymbol = Symbol("required"); var markedSymbol = Symbol("marked"); var undocumentedSymbol = Symbol("undocumented"); var unionSymbol = Symbol("union"); var similarOptionsMap = [ ["placement", "position"], ["padding", "spacing", "gap"], ["color", "fill", "stroke"], ["whisker", "wick"], ["nodeClick", "seriesNodeClick"], ["nodeDoubleClick", "seriesNodeDoubleClick"], ["src", "url"] ].reduce((map, words) => { for (const word of words) { map.set(word.toLowerCase(), new Set(words.filter((w) => w !== word))); } return map; }, /* @__PURE__ */ new Map()); var ErrorType = /* @__PURE__ */ ((ErrorType2) => { ErrorType2["Invalid"] = "invalid"; ErrorType2["Required"] = "required"; ErrorType2["Unknown"] = "unknown"; return ErrorType2; })(ErrorType || {}); function extendPath(path, key) { if (isFiniteNumber(key)) { return `${path}[${key}]`; } return path ? `${path}.${key}` : key; } var ValidationError = class { constructor(type, description, value, path, key) { this.type = type; this.description = description; this.value = value; this.path = path; this.key = key; } setUnionType(unionType, path) { if (this.path.startsWith(path)) { const suffix = this.path.slice(path.length); this.altPath = `${path}[type=${unionType}]${suffix}`; } } getPrefix() { const { altPath: path = this.path, key } = this; if (!path && !key) return "Value"; return `Option \`${key ? extendPath(path, key) : path}\``; } toString() { const { description = "unknown", type, value } = this; if (type === "required" /* Required */ && value == null) { return `${this.getPrefix()} is required and has not been provided; expecting ${description}, ignoring.`; } return `${this.getPrefix()} cannot be set to \`${stringifyValue(value, 50)}\`; expecting ${description}, ignoring.`; } }; var UnknownError = class extends ValidationError { constructor(suggestions, value, path, key) { super("unknown" /* Unknown */, void 0, value, path, key); this.suggestions = suggestions; this.key = key; } getPrefix() { return `Unknown option \`${extendPath(this.altPath ?? this.path, this.key)}\``; } getPostfix() { const suggestions = joinFormatted(findSuggestions(this.key, this.suggestions), "or", (val) => `\`${val}\``); return suggestions ? `; Did you mean ${suggestions}? Ignoring.` : ", ignoring."; } toString() { return `${this.getPrefix()}${this.getPostfix()}`; } }; function validate(options, optionsDefs2, path = "") { if (!isObject(options)) { return { cleared: null, invalid: [new ValidationError("required" /* Required */, "an object", options, path)] }; } const cleared = {}; const invalid = []; const optionsKeys = new Set(Object.keys(options)); const unusedKeys = []; if (unionSymbol in optionsDefs2) { const validTypes = Object.keys(optionsDefs2); const defaultType = optionsDefs2[unionSymbol]; if (options.type != null && validTypes.includes(options.type) || options.type == null && defaultType != null) { const { type = defaultType, ...rest } = options; const nestedResult = validate(rest, optionsDefs2[type], path); Object.assign(cleared, { type }, nestedResult.cleared); for (const error2 of nestedResult.invalid) { error2.setUnionType(type, path); } invalid.push(...nestedResult.invalid); } else { const keywords = joinFormatted(validTypes, "or", (val) => `'${val}'`); invalid.push( new ValidationError("required" /* Required */, `a keyword such as ${keywords}`, options.type, path, "type") ); } return { cleared, invalid }; } for (const key of Object.keys(optionsDefs2)) { const validatorOrDefs = optionsDefs2[key]; const required3 = validatorOrDefs[requiredSymbol]; const value = options[key]; optionsKeys.delete(key); if (value === void 0) { if (!validatorOrDefs[undocumentedSymbol]) { unusedKeys.push(key); } if (!required3) continue; } const keyPath = extendPath(path, key); if (isFunction(validatorOrDefs)) { const context = { options, path: keyPath }; const validatorResult = validatorOrDefs(value, context); const objectResult = typeof validatorResult === "object"; if (objectResult) { invalid.push(...validatorResult.invalid); if (validatorResult.valid) { cleared[key] = validatorResult.cleared; continue; } else if (hasRequiredInPath(validatorResult.invalid, keyPath)) { continue; } } else if (validatorResult) { cleared[key] = value; continue; } invalid.push( new ValidationError( required3 ? "required" /* Required */ : "invalid" /* Invalid */, validatorOrDefs[descriptionSymbol], value, path, key ) ); } else { const nestedResult = validate(value, validatorOrDefs, keyPath); if (nestedResult.cleared != null) { cleared[key] = nestedResult.cleared; } invalid.push(...nestedResult.invalid); } } for (const key of optionsKeys) { const value = options[key]; if (value === void 0) continue; invalid.push(new UnknownError(unusedKeys, value, path, key)); } return { cleared, invalid }; } function findSuggestions(value, suggestions, maxDistance = 2) { const lowerCaseValue = value.toLowerCase(); const similarValues = similarOptionsMap.get(lowerCaseValue); return suggestions.filter((key) => { const lowerCaseKey = key.toLowerCase(); return similarValues?.has(key) === true || lowerCaseKey.includes(lowerCaseValue) || levenshteinDistance(lowerCaseValue, lowerCaseKey) <= maxDistance; }); } function attachDescription(validatorOrDefs, description) { if (isFunction(validatorOrDefs)) { let clonedValidator2 = function(value, context) { return validatorOrDefs(value, context); }; var clonedValidator = clonedValidator2; clonedValidator2[descriptionSymbol] = description; return clonedValidator2; } else { return { ...validatorOrDefs, [descriptionSymbol]: description }; } } function required(validatorOrDefs) { return Object.assign( isFunction(validatorOrDefs) ? (value, context) => validatorOrDefs(value, context) : optionsDefs(validatorOrDefs), { [requiredSymbol]: true, [descriptionSymbol]: validatorOrDefs[descriptionSymbol] } ); } function undocumented(validatorOrDefs) { return Object.assign( isFunction(validatorOrDefs) ? (value, context) => validatorOrDefs(value, context) : optionsDefs(validatorOrDefs), { [undocumentedSymbol]: true, [descriptionSymbol]: validatorOrDefs[descriptionSymbol] } ); } var optionsDefs = (defs, description = "an object", failAll = false) => attachDescription((value, context) => { const result = validate(value, defs, context.path); const valid = !hasRequiredInPath(result.invalid, context.path); return { valid, cleared: valid || !failAll ? result.cleared : null, invalid: result.invalid }; }, description); var typeUnion = (defs, description, defaultType) => ({ ...defs, [descriptionSymbol]: description, [unionSymbol]: defaultType }); var and = (...validators) => attachDescription( (value, context) => { const invalid = []; for (const validator of validators) { const result = validator(value, context); if (typeof result === "object") { invalid.push(...result.invalid); if (!result.valid) { return { valid: false, cleared: value, invalid }; } value = result.cleared; } else if (!result) { return false; } } return { valid: true, cleared: value, invalid }; }, joinFormatted( validators.filter((v) => !v[undocumentedSymbol]).map((v) => v[descriptionSymbol]).filter(isDefined), "and" ) ); var or = (...validators) => attachDescription( (value, context) => { for (const validator of validators) { const result = validator(value, context); if (typeof result === "object" ? result.valid : result) { return result; } } return false; }, joinFormatted( validators.filter((v) => !v[undocumentedSymbol]).map((v) => v[descriptionSymbol]).filter(isDefined), "or" ) ); var isComparable = (value) => isFiniteNumber(value) || isValidDate(value); var isValidDateValue = (value) => isDate(value) || (isFiniteNumber(value) || isString(value)) && isValidDate(new Date(value)); var array = attachDescription(isArray, "an array"); var boolean = attachDescription(isBoolean, "a boolean"); var callback = attachDescription(isFunction, "a function"); var color = attachDescription(isColor, "a color string"); var date = attachDescription(isValidDateValue, "a date"); var defined = attachDescription(isDefined, "a defined value"); var number = attachDescription(isFiniteNumber, "a number"); var object = attachDescription(isObject, "an object"); var string = attachDescription(isString, "a string"); var htmlElement = attachDescription( (value) => typeof HTMLElement === "undefined" || value instanceof HTMLElement, "an html element" ); var arrayLength = (minLength, maxLength = Infinity) => { let message; if (maxLength === Infinity) { message = `an array of at least ${minLength} items`; } else if (minLength === maxLength) { message = `an array of exactly ${minLength} items`; } else if (minLength === 0) { message = `an array of no more than ${maxLength} items`; } else { message = `an array of at least ${minLength} and no more than ${maxLength} items`; } return attachDescription( (value) => isArray(value) && value.length >= minLength && value.length <= maxLength, message ); }; var stringLength = (minLength, maxLength = Infinity) => { let message; if (maxLength === Infinity) { message = `a string of at least ${minLength} characters`; } else if (minLength === maxLength) { message = `an string of exactly ${minLength} characters`; } else if (minLength === 0) { message = `an string of no more than ${maxLength} characters`; } else { message = `an string of at least ${minLength} and no more than ${maxLength} characters`; } return attachDescription( (value) => isString(value) && value.length >= minLength && value.length <= maxLength, message ); }; var numberMin = (min, inclusive = true) => attachDescription( (value) => isFiniteNumber(value) && (value > min || inclusive && value === min), `a number greater than ${inclusive ? "or equal to " : ""}${min}` ); var numberRange = (min, max) => attachDescription( (value) => isFiniteNumber(value) && value >= min && value <= max, `a number betwe