// SPDX-License-Identifier: LicenseRef-Highcharts
/**
 * @license Highcharts Dashboards v4.2.0 (2026-05-12)
 * @module dashboards/dashboards
 *
 * (c) 2009-2026 Highsoft AS
 *
 * A commercial license may be required depending on use,
 * see www.highcharts.com/license
 */
(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		(root["_Dashboards"] = factory(),module.exports = root["_Dashboards"]);
	else if(typeof define === 'function' && define.amd)
		define("dashboards/dashboards", [], factory);
	else if(typeof exports === 'object')
		(root["_Dashboards"] = factory(),exports["dashboards/dashboards"] = root["_Dashboards"]);
	else
		((root["Dashboards"] && root["Dashboards"].error(16, true)), root["Dashboards"] = factory());
})(typeof window === 'undefined' ? this : window, () => {
return /******/ (() => { // webpackBootstrap
/******/ 	"use strict";
/******/ 	// The require scope
/******/ 	var __webpack_require__ = {};
/******/ 	
/************************************************************************/
/******/ 	/* webpack/runtime/define property getters */
/******/ 	(() => {
/******/ 		// define getter functions for harmony exports
/******/ 		__webpack_require__.d = (exports, definition) => {
/******/ 			for(var key in definition) {
/******/ 				if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ 					Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ 				}
/******/ 			}
/******/ 		};
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/hasOwnProperty shorthand */
/******/ 	(() => {
/******/ 		__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ 	})();
/******/ 	
/************************************************************************/
var __webpack_exports__ = {};

// EXPORTS
__webpack_require__.d(__webpack_exports__, {
  "default": () => (/* binding */ dashboards_src)
});

;// ./code/dashboards/es-modules/Core/Globals.js
/* *
 *
 *  (c) 2010-2026 Highsoft AS
 *  Author: Torstein Hønsi
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 * */

/* *
 *
 *  Namespace
 *
 * */
/**
 * Shared Highcharts properties.
 * @internal
 */
var Globals;
(function (Globals) {
    /* *
     *
     *  Constants
     *
     * */
    Globals.SVG_NS = 'http://www.w3.org/2000/svg', Globals.product = 'Highcharts', Globals.version = '4.2.0', Globals.win = (typeof window !== 'undefined' ?
        window :
        {}), // eslint-disable-line node/no-unsupported-features/es-builtins
    Globals.doc = Globals.win.document, Globals.svg = !!Globals.doc?.createElementNS?.(Globals.SVG_NS, 'svg')?.createSVGRect, Globals.pageLang = Globals.doc?.documentElement?.closest('[lang]')?.lang, Globals.userAgent = Globals.win.navigator?.userAgent || '', Globals.isChrome = Globals.win.chrome, Globals.isFirefox = Globals.userAgent.indexOf('Firefox') !== -1, Globals.isMS = /(edge|msie|trident)/i.test(Globals.userAgent) && !Globals.win.opera, Globals.isSafari = !Globals.isChrome && Globals.userAgent.indexOf('Safari') !== -1, Globals.isTouchDevice = /(Mobile|Android|Windows Phone)/.test(Globals.userAgent), Globals.isWebKit = Globals.userAgent.indexOf('AppleWebKit') !== -1, Globals.deg2rad = Math.PI * 2 / 360, Globals.marginNames = [
        'plotTop',
        'marginRight',
        'marginBottom',
        'plotLeft'
    ], Globals.noop = function () { }, Globals.supportsPassiveEvents = (function () {
        // Checks whether the browser supports passive events, (#11353).
        let supportsPassive = false;
        // Object.defineProperty doesn't work on IE as well as passive
        // events - instead of using polyfill, we can exclude IE totally.
        if (!Globals.isMS) {
            const opts = Object.defineProperty({}, 'passive', {
                get: function () {
                    supportsPassive = true;
                }
            });
            if (Globals.win.addEventListener && Globals.win.removeEventListener) {
                Globals.win.addEventListener('testPassive', Globals.noop, opts);
                Globals.win.removeEventListener('testPassive', Globals.noop, opts);
            }
        }
        return supportsPassive;
    }());
    /**
     * An array containing the current chart objects in the page. A chart's
     * position in the array is preserved throughout the page's lifetime. When
     * a chart is destroyed, the array item becomes `undefined`.
     *
     * @name Highcharts.charts
     * @type {Array<Highcharts.Chart|undefined>}
     */
    Globals.charts = [];
    /**
     * A shared registry between all bundles to keep track of applied
     * compositions.
     * @internal
     */
    Globals.composed = [];
    /**
     * A hook for defining additional date format specifiers. New
     * specifiers are defined as key-value pairs by using the
     * specifier as key, and a function which takes the timestamp as
     * value. This function returns the formatted portion of the
     * date.
     *
     * Using `dateFormats` is also a convenient way to define new keys for
     * complex locale-aware date formats compatible with the
     * [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat)
     * browser API, whenever the built-in formats are not sufficient.
     *
     * @sample highcharts/global/dateformats/
     *         Adding support for week number
     * @sample highcharts/global/dateformats-object/
     *         A locale-aware date format using `Intl.DateTimeFormat`
     *
     * @name Highcharts.dateFormats
     * @type {Record<string, Highcharts.TimeFormatCallbackFunction>}
     */
    Globals.dateFormats = {};
    /**
     * @internal
     * @deprecated
     * @todo Use only `Core/Series/SeriesRegistry.seriesTypes`
     */
    Globals.seriesTypes = {};
    /** @internal */
    Globals.symbolSizes = {};
    /* *
     *
     *  Properties
     *
     * */
    // eslint-disable-next-line prefer-const
    Globals.chartCount = 0;
})(Globals || (Globals = {}));
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Core_Globals = (Globals);
/* *
 *
 *  API Declarations
 *
 * */
/**
 * Theme options that should get applied to the chart. In module mode it
 * might not be possible to change this property because of read-only
 * restrictions, instead use {@link Highcharts.setOptions}.
 *
 * @deprecated
 * @name Highcharts.theme
 * @type {Highcharts.Options}
 */
(''); // Keeps doclets above in JS file

;// ./code/dashboards/es-modules/Shared/Utilities.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 * */

const { doc, win } = Core_Globals;
/**
 * Add an event listener.
 *
 * @function Highcharts.addEvent<T>
 *
 * @param  {Highcharts.Class<T>|T} el
 *         The element or object to add a listener to. It can be a
 *         {@link HTMLDOMElement}, an {@link SVGElement} or any other object.
 *
 * @param  {string} type
 *         The event type.
 *
 * @param  {Highcharts.EventCallbackFunction<T>|Function} fn
 *         The function callback to execute when the event is fired.
 *
 * @param  {Highcharts.EventOptionsObject} [options]
 *         Options for adding the event.
 *
 * @sample highcharts/members/addevent
 *         Use a general `render` event to draw shapes on a chart
 *
 * @return {Function}
 *         A callback function to remove the added event.
 */
function addEvent(el, type, fn, options = {}) {
    // Add hcEvents to either the prototype (in case we're running addEvent on a
    // class) or the instance. If hasOwnProperty('hcEvents') is false, it is
    // inherited down the prototype chain, in which case we need to set the
    // property on this instance (which may itself be a prototype).
    const owner = typeof el === 'function' && el.prototype || el;
    if (!Object.hasOwnProperty.call(owner, 'hcEvents')) {
        owner.hcEvents = {};
    }
    const events = owner.hcEvents;
    // Allow click events added to points, otherwise they will be prevented by
    // the TouchPointer.pinch function after a pinch zoom operation (#7091).
    if (Core_Globals.Point && // Without H a dependency loop occurs
        el instanceof Core_Globals.Point &&
        el.series &&
        el.series.chart) {
        el.series.chart.runTrackerClick = true;
    }
    // Handle DOM events
    // If the browser supports passive events, add it to improve performance
    // on touch events (#11353).
    const addEventListener = el.addEventListener;
    if (addEventListener) {
        addEventListener.call(el, type, fn, Core_Globals.supportsPassiveEvents ? {
            passive: options.passive === void 0 ?
                type.indexOf('touch') !== -1 : options.passive,
            capture: false
        } : false);
    }
    if (!events[type]) {
        events[type] = [];
    }
    const eventObject = {
        fn,
        order: typeof options.order === 'number' ? options.order : Infinity
    };
    events[type].push(eventObject);
    // Order the calls
    events[type].sort((a, b) => a.order - b.order);
    // Return a function that can be called to remove this event.
    return function () {
        removeEvent(el, type, fn);
    };
}
/**
 * Non-recursive method to find the lowest member of an array. `Math.min` raises
 * a maximum call stack size exceeded error in Chrome when trying to apply more
 * than 150.000 points. This method is slightly slower, but safe.
 *
 * @function Highcharts.arrayMin
 *
 * @param {Array<*>} data
 *        An array of numbers.
 *
 * @return {number}
 *         The lowest number.
 */
function arrayMin(data) {
    let i = data.length, min = data[0];
    while (i--) {
        if (data[i] < min) {
            min = data[i];
        }
    }
    return min;
}
/**
 * Non-recursive method to find the lowest member of an array. `Math.max` raises
 * a maximum call stack size exceeded error in Chrome when trying to apply more
 * than 150.000 points. This method is slightly slower, but safe.
 *
 * @function Highcharts.arrayMax
 *
 * @param {Array<*>} data
 *        An array of numbers.
 *
 * @return {number}
 *         The highest number.
 */
function arrayMax(data) {
    let i = data.length, max = data[0];
    while (i--) {
        if (data[i] > max) {
            max = data[i];
        }
    }
    return max;
}
/**
 * Set or get an attribute or an object of attributes.
 *
 * To use as a setter, pass a key and a value, or let the second argument be a
 * collection of keys and values. When using a collection, passing a value of
 * `null` or `undefined` will remove the attribute.
 *
 * To use as a getter, pass only a string as the second argument.
 *
 * @function Highcharts.attr
 *
 * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} elem
 *        The DOM element to receive the attribute(s).
 *
 * @param {string|Highcharts.HTMLAttributes|Highcharts.SVGAttributes} [keyOrAttribs]
 *        The property or an object of key-value pairs.
 *
 * @param {number|string} [value]
 *        The value if a single property is set.
 *
 * @return {string|null|undefined}
 *         When used as a getter, return the value.
 */
function attr(elem, keyOrAttribs, value) {
    const isGetter = isString(keyOrAttribs) && !defined(value);
    let ret;
    const attrSingle = (value, key) => {
        // Set the value
        if (defined(value)) {
            elem.setAttribute(key, value);
            // Get the value
        }
        else if (isGetter) {
            ret = elem.getAttribute(key);
            // IE7 and below cannot get class through getAttribute (#7850)
            if (!ret && key === 'class') {
                ret = elem.getAttribute(key + 'Name');
            }
            // Remove the value
        }
        else {
            elem.removeAttribute(key);
        }
    };
    // If keyOrAttribs is a string
    if (isString(keyOrAttribs)) {
        attrSingle(value, keyOrAttribs);
        // Else if keyOrAttribs is defined, it is a hash of key/value pairs
    }
    else {
        objectEach(keyOrAttribs, attrSingle);
    }
    return ret;
}
/**
 * Constrain a value to within a lower and upper threshold.
 *
 * @internal
 * @param {number} value The initial value
 * @param {number} min The lower threshold
 * @param {number} max The upper threshold
 * @return {number} Returns a number value within min and max.
 */
function clamp(value, min, max) {
    return value > min ? value < max ? value : max : min;
}
/**
 * Fix JS round off float errors.
 *
 * @function Highcharts.correctFloat
 *
 * @param {number} num
 *        A float number to fix.
 *
 * @param {number} [prec=14]
 *        The precision.
 *
 * @return {number}
 *         The corrected float number.
 */
function correctFloat(num, prec) {
    // When the number is higher than 1e14 use the number (#16275)
    return num > 1e14 ? num : parseFloat(num.toPrecision(prec || 14));
}
/**
 * Utility function to create an HTML element with attributes and styles.
 *
 * @function Highcharts.createElement
 *
 * @param {string} tag
 *        The HTML tag.
 *
 * @param {Highcharts.HTMLAttributes} [attribs]
 *        Attributes as an object of key-value pairs.
 *
 * @param {Highcharts.CSSObject} [styles]
 *        Styles as an object of key-value pairs.
 *
 * @param {Highcharts.HTMLDOMElement} [parent]
 *        The parent HTML object.
 *
 * @param {boolean} [nopad=false]
 *        If true, remove all padding, border and margin.
 *
 * @return {Highcharts.HTMLDOMElement}
 *         The created DOM element.
 */
function createElement(tag, attribs, styles, parent, nopad) {
    const el = doc.createElement(tag);
    if (attribs) {
        extend(el, attribs);
    }
    if (nopad) {
        css(el, { padding: '0', border: 'none', margin: '0' });
    }
    if (styles) {
        css(el, styles);
    }
    if (parent) {
        parent.appendChild(el);
    }
    return el;
}
/**
 * Utility for crisping a line position to the nearest full pixel depending on
 * the line width.
 *
 * @internal
 * @param {number} value       The raw pixel position
 * @param {number} lineWidth   The line width
 * @param {boolean} [inverted] Whether the containing group is inverted.
 *                             Crisping round numbers on the y-scale need to go
 *                             to the other side because the coordinate system
 *                             is flipped (scaleY is -1)
 * @return {number}            The pixel position to use for a crisp display
 */
function crisp(value, lineWidth = 0, inverted) {
    const mod = lineWidth % 2 / 2, inverter = inverted ? -1 : 1;
    return (Math.round(value * inverter - mod) + mod) * inverter;
}
/**
 * Set CSS on a given element.
 *
 * @function Highcharts.css
 *
 * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} el
 *        An HTML DOM element.
 *
 * @param {Highcharts.CSSObject} styles
 *        Style object with camel case property names.
 *
 * @return {void}
 */
function css(el, styles) {
    extend(el.style, styles);
}
/**
 * Check if an object is null or undefined.
 *
 * @function Highcharts.defined
 *
 * @param {*} obj
 *        The object to check.
 *
 * @return {boolean}
 *         False if the object is null or undefined, otherwise true.
 */
function defined(obj) {
    return typeof obj !== 'undefined' && obj !== null;
}
/**
 * Utility method that destroys any SVGElement instances that are properties on
 * the given object. It loops all properties and invokes destroy if there is a
 * destroy method. The property is then delete.
 *
 * @function Highcharts.destroyObjectProperties
 *
 * @param {*} obj
 *        The object to destroy properties on.
 *
 * @param {*} [except]
 *        Exception, do not destroy this property, only delete it.
 */
function destroyObjectProperties(obj, except, destructablesOnly) {
    objectEach(obj, function (val, n) {
        // If the object is non-null and destroy is defined
        if (val !== except && val?.destroy) {
            // Invoke the destroy
            val.destroy();
        }
        // Delete the property from the object
        if (val?.destroy || !destructablesOnly) {
            delete obj[n];
        }
    });
}
/**
 * Discard a HTML element
 *
 * @function Highcharts.discardElement
 *
 * @param {Highcharts.HTMLDOMElement} element
 *        The HTML node to discard.
 */
function discardElement(element) {
    element?.parentElement?.removeChild(element);
}
// eslint-disable-next-line valid-jsdoc
/**
 * Return the deep difference between two objects. It can either return the new
 * properties, or optionally return the old values of new properties.
 * @internal
 */
function diffObjects(newer, older, keepOlder, collectionsWithUpdate) {
    const ret = {};
    /**
     * Recurse over a set of options and its current values, and store the
     * current values in the ret object.
     */
    function diff(newer, older, ret, depth) {
        const keeper = keepOlder ? older : newer;
        objectEach(newer, function (newerVal, key) {
            if (!depth &&
                collectionsWithUpdate &&
                collectionsWithUpdate.indexOf(key) > -1 &&
                older[key]) {
                newerVal = splat(newerVal);
                ret[key] = [];
                // Iterate over collections like series, xAxis or yAxis and map
                // the items by index.
                for (let i = 0; i < Math.max(newerVal.length, older[key].length); i++) {
                    // Item exists in current data (#6347)
                    if (older[key][i]) {
                        // If the item is missing from the new data, we need to
                        // save the whole config structure. Like when
                        // responsively updating from a dual axis layout to a
                        // single axis and back (#13544).
                        if (newerVal[i] === void 0) {
                            ret[key][i] = older[key][i];
                            // Otherwise, proceed
                        }
                        else {
                            ret[key][i] = {};
                            diff(newerVal[i], older[key][i], ret[key][i], depth + 1);
                        }
                    }
                }
            }
            else if (isObject(newerVal, true) &&
                !newerVal.nodeType // #10044
            ) {
                ret[key] = isArray(newerVal) ? [] : {};
                diff(newerVal, older[key] || {}, ret[key], depth + 1);
                // Delete empty nested objects
                if (Object.keys(ret[key]).length === 0 &&
                    // Except colorAxis which is a special case where the empty
                    // object means it is enabled. Which is unfortunate and we
                    // should try to find a better way.
                    !(key === 'colorAxis' && depth === 0)) {
                    delete ret[key];
                }
            }
            else if (newer[key] !== older[key] ||
                // If the newer key is explicitly undefined, keep it (#10525)
                (key in newer && !(key in older))) {
                if (key !== '__proto__' && key !== 'constructor') {
                    ret[key] = keeper[key];
                }
            }
        });
    }
    diff(newer, older, ret, 0);
    return ret;
}
/**
 * Remove the last occurrence of an item from an array.
 *
 * @function Highcharts.erase
 *
 * @param {Array<*>} arr
 *        The array.
 *
 * @param {*} item
 *        The item to remove.
 *
 * @return {void}
 */
function erase(arr, item) {
    let i = arr.length;
    while (i--) {
        if (arr[i] === item) {
            arr.splice(i, 1);
            break;
        }
    }
}
/**
 * Utility function to extend an object with the members of another.
 *
 * @function Highcharts.extend<T>
 *
 * @param {T|undefined} a
 *        The object to be extended.
 *
 * @param {Partial<T>} b
 *        The object to add to the first one.
 *
 * @return {T}
 *         Object a, the original object.
 */
function extend(a, b) {
    let n;
    if (!a) {
        a = {};
    }
    for (n in b) { // eslint-disable-line guard-for-in
        a[n] = b[n];
    }
    return a;
}
// eslint-disable-next-line valid-jsdoc
/**
 * Extend a prototyped class by new members.
 *
 * @deprecated
 * @function Highcharts.extendClass<T>
 *
 * @param {Highcharts.Class<T>} parent
 *        The parent prototype to inherit.
 *
 * @param {Highcharts.Dictionary<*>} members
 *        A collection of prototype members to add or override compared to the
 *        parent prototype.
 *
 * @return {Highcharts.Class<T>}
 *         A new prototype.
 */
function extendClass(parent, members) {
    const obj = (function () { });
    obj.prototype = new parent(); // eslint-disable-line new-cap
    extend(obj.prototype, members);
    return obj;
}
/**
 * Fire an event that was registered with {@link Highcharts#addEvent}.
 *
 * @function Highcharts.fireEvent<T>
 *
 * @param {T} el
 *        The object to fire the event on. It can be a {@link HTMLDOMElement},
 *        an {@link SVGElement} or any other object.
 *
 * @param {string} type
 *        The type of event.
 *
 * @param {Highcharts.Dictionary<*>|Event} [eventArguments]
 *        Custom event arguments that are passed on as an argument to the event
 *        handler.
 *
 * @param {Highcharts.EventCallbackFunction<T>|Function} [defaultFunction]
 *        The default function to execute if the other listeners haven't
 *        returned false.
 *
 * @return {void}
 */
function fireEvent(el, type, eventArguments, defaultFunction) {
    eventArguments = eventArguments || {};
    if (doc?.createEvent &&
        (el.dispatchEvent ||
            (el.fireEvent &&
                // Enable firing events on Highcharts instance.
                el !== Core_Globals))) {
        const e = doc.createEvent('Events');
        e.initEvent(type, true, true);
        eventArguments = extend(e, eventArguments);
        if (el.dispatchEvent) {
            el.dispatchEvent(eventArguments);
        }
        else {
            el.fireEvent(type, eventArguments);
        }
    }
    else if (el.hcEvents) {
        if (!eventArguments.target) {
            // We're running a custom event
            extend(eventArguments, {
                // Attach a simple preventDefault function to skip
                // default handler if called. The built-in
                // defaultPrevented property is not overwritable (#5112)
                preventDefault: function () {
                    eventArguments.defaultPrevented = true;
                },
                // Setting target to native events fails with clicking
                // the zoom-out button in Chrome.
                target: el,
                // If the type is not set, we're running a custom event
                // (#2297). If it is set, we're running a browser event.
                type: type
            });
        }
        const events = [];
        let object = el;
        let multilevel = false;
        // Recurse up the inheritance chain and collect hcEvents set as own
        // objects on the prototypes.
        while (object.hcEvents) {
            if (Object.hasOwnProperty.call(object, 'hcEvents') &&
                object.hcEvents[type]) {
                if (events.length) {
                    multilevel = true;
                }
                events.unshift.apply(events, object.hcEvents[type]);
            }
            object = Object.getPrototypeOf(object);
        }
        // For performance reasons, only sort the event handlers in case we are
        // dealing with multiple levels in the prototype chain. Otherwise, the
        // events are already sorted in the addEvent function.
        if (multilevel) {
            // Order the calls
            events.sort((a, b) => a.order - b.order);
        }
        // Call the collected event handlers
        events.forEach((obj) => {
            // If the event handler returns false, prevent the default handler
            // from executing
            if (obj.fn.call(el, eventArguments, el) === false) {
                eventArguments.preventDefault();
            }
        });
    }
    // Run the default if not prevented
    if (defaultFunction && !eventArguments.defaultPrevented) {
        defaultFunction.call(el, eventArguments);
    }
}
/**
 * Convenience function to get the align factor, used several places for
 * computing positions
 * @internal
 */
const getAlignFactor = (align = '') => ({
    center: 0.5,
    right: 1,
    middle: 0.5,
    bottom: 1
}[align] || 0);
/**
 * Find the closest distance between two values of a two-dimensional array
 * @internal
 * @function Highcharts.getClosestDistance
 *
 * @param {Array<Array<number>>} arrays
 *          An array of arrays of numbers
 *
 * @return {number | undefined}
 *          The closest distance between values
 */
function getClosestDistance(arrays, onError) {
    const allowNegative = !onError;
    let closest, loopLength, distance, i;
    arrays.forEach((xData) => {
        if (xData.length > 1) {
            loopLength = xData.length - 1;
            for (i = loopLength; i > 0; i--) {
                distance = xData[i] - xData[i - 1];
                if (distance < 0 && !allowNegative) {
                    onError?.();
                    // Only one call
                    onError = void 0;
                }
                else if (distance && (typeof closest === 'undefined' || distance < closest)) {
                    closest = distance;
                }
            }
        }
    });
    return closest;
}
/**
 * Get the magnitude of a number.
 *
 * @function Highcharts.getMagnitude
 *
 * @param {number} num
 *        The number.
 *
 * @return {number}
 *         The magnitude, where 1-9 are magnitude 1, 10-99 magnitude 2 etc.
 */
function getMagnitude(num) {
    return Math.pow(10, Math.floor(Math.log(num) / Math.LN10));
}
/**
 * Returns the value of a property path on a given object.
 *
 * @internal
 * @function getNestedProperty
 *
 * @param {string} path
 * Path to the property, for example `custom.myValue`.
 *
 * @param {unknown} parent
 * Instance containing the property on the specific path.
 *
 * @return {unknown}
 * The unknown property value.
 */
function getNestedProperty(path, parent) {
    const pathElements = path.split('.');
    while (pathElements.length && defined(parent)) {
        const pathElement = pathElements.shift();
        // Filter on the key
        if (typeof pathElement === 'undefined' ||
            pathElement === '__proto__') {
            return; // Undefined
        }
        if (pathElement === 'this') {
            let thisProp;
            if (isObject(parent)) {
                thisProp = parent['@this'];
            }
            return thisProp ?? parent;
        }
        const child = parent[pathElement.replace(/[\\'"]/g, '')];
        // Filter on the child
        if (!defined(child) ||
            typeof child === 'function' ||
            typeof child.nodeType === 'number' ||
            child === win) {
            return; // Undefined
        }
        // Else, proceed
        parent = child;
    }
    return parent;
}
/**
 * Get the computed CSS value for given element and property, only for numerical
 * properties. For width and height, the dimension of the inner box (excluding
 * padding) is returned. Used for fitting the chart within the container.
 *
 * @function Highcharts.getStyle
 *
 * @param {Highcharts.HTMLDOMElement} el
 * An HTML element.
 *
 * @param {string} prop
 * The property name.
 *
 * @param {boolean} [toInt=true]
 * Parse to integer.
 *
 * @return {number|string|undefined}
 * The style value.
 */
function getStyle(el, prop, toInt) {
    let style;
    // For width and height, return the actual inner pixel size (#4913)
    if (prop === 'width') {
        let offsetWidth = Math.min(el.offsetWidth, el.scrollWidth);
        // In flex boxes, we need to use getBoundingClientRect and floor it,
        // because scrollWidth doesn't support subpixel precision (#6427) ...
        const boundingClientRectWidth = el.getBoundingClientRect?.().width;
        // ...unless if the containing div or its parents are transform-scaled
        // down, in which case the boundingClientRect can't be used as it is
        // also scaled down (#9871, #10498).
        if (boundingClientRectWidth < offsetWidth &&
            boundingClientRectWidth >= offsetWidth - 1) {
            offsetWidth = Math.floor(boundingClientRectWidth);
        }
        return Math.max(0, // #8377
        (offsetWidth -
            (getStyle(el, 'padding-left', true) || 0) -
            (getStyle(el, 'padding-right', true) || 0)));
    }
    if (prop === 'height') {
        return Math.max(0, // #8377
        (Math.min(el.offsetHeight, el.scrollHeight) -
            (getStyle(el, 'padding-top', true) || 0) -
            (getStyle(el, 'padding-bottom', true) || 0)));
    }
    // Otherwise, get the computed style
    const css = win.getComputedStyle(el, void 0); // eslint-disable-line no-undefined
    if (css) {
        style = css.getPropertyValue(prop);
        if (Utilities_pick(toInt, prop !== 'opacity')) {
            style = pInt(style);
        }
    }
    return style;
}
/**
 * Return the value of the first element in the array that satisfies the
 * provided testing function.
 *
 * @function Highcharts.find<T>
 *
 * @param {Array<T>} arr
 *        The array to test.
 *
 * @param {Function} callback
 *        The callback function. The function receives the item as the first
 *        argument. Return `true` if this item satisfies the condition.
 *
 * @return {T|undefined}
 *         The value of the element.
 */
const find = Array.prototype.find ?
    function (arr, callback) {
        return arr.find(callback);
    } :
    // Legacy implementation. PhantomJS, IE <= 11 etc. #7223.
    function (arr, callback) {
        let i;
        const length = arr.length;
        for (i = 0; i < length; i++) {
            if (callback(arr[i], i)) { // eslint-disable-line node/callback-return
                return arr[i];
            }
        }
    };
/**
 * Internal clear timeout. The function checks that the `id` was not removed
 * (e.g. by `chart.destroy()`). For the details see
 * [issue #7901](https://github.com/highcharts/highcharts/issues/7901).
 *
 * @internal
 *
 * @function Highcharts.clearTimeout
 *
 * @param {number|undefined} id
 * Id of a timeout.
 */
function internalClearTimeout(id) {
    if (defined(id)) {
        clearTimeout(id);
    }
}
/**
 * Utility function to check if an Object is a HTML Element.
 *
 * @function Highcharts.isDOMElement
 *
 * @param {*} obj
 *        The item to check.
 *
 * @return {boolean}
 *         True if the argument is a HTML Element.
 */
function isDOMElement(obj) {
    return isObject(obj) && typeof obj.nodeType === 'number';
}
/**
 * Utility function to check if an Object is a class.
 *
 * @function Highcharts.isClass
 *
 * @param {object|undefined} obj
 *        The item to check.
 *
 * @return {boolean}
 *         True if the argument is a class.
 */
function isClass(obj) {
    const c = obj?.constructor;
    return !!(isObject(obj, true) &&
        !isDOMElement(obj) &&
        (c?.name && c.name !== 'Object'));
}
/**
 * Utility function to check if an item is a number and it is finite (not NaN,
 * Infinity or -Infinity).
 *
 * @function Highcharts.isNumber
 *
 * @param {*} n
 *        The item to check.
 *
 * @return {boolean}
 *         True if the item is a finite number
 */
function Utilities_isNumber(n) {
    return typeof n === 'number' && !isNaN(n) && n < Infinity && n > -Infinity;
}
/**
 * Utility function to check for string type.
 *
 * @function Highcharts.isString
 *
 * @param {*} s
 *        The item to check.
 *
 * @return {boolean}
 *         True if the argument is a string.
 */
function isString(s) {
    return typeof s === 'string';
}
/**
 * Utility function to check if an item is an array.
 *
 * @function Highcharts.isArray
 *
 * @param {*} obj
 *        The item to check.
 *
 * @return {boolean}
 *         True if the argument is an array.
 */
function isArray(obj) {
    const str = Object.prototype.toString.call(obj);
    return str === '[object Array]' || str === '[object Array Iterator]';
}
/**
 * Utility function to check if object is a function.
 *
 * @function Highcharts.isFunction
 *
 * @param {*} obj
 *        The item to check.
 *
 * @return {boolean}
 *         True if the argument is a function.
 */
function isFunction(obj) {
    return typeof obj === 'function';
}
/**
 * Utility function to check if an item is of type object.
 *
 * @function Highcharts.isObject
 *
 * @param {*} obj
 *        The item to check.
 *
 * @param {boolean} [strict=false]
 *        Also checks that the object is not an array.
 *
 * @return {boolean}
 *         True if the argument is an object.
 */
function isObject(obj, strict) {
    return (!!obj &&
        typeof obj === 'object' &&
        (!strict || !isArray(obj))); // eslint-disable-line @typescript-eslint/no-explicit-any
}
/**
 * Utility function to deep merge two or more objects and return a third object.
 * If the first argument is true, the contents of the second object is copied
 * into the first object. The merge function can also be used with a single
 * object argument to create a deep copy of an object.
 *
 * @function Highcharts.merge<T>
 *
 * @param {true | T} extendOrSource
 *        Whether to extend the left-side object,
 *        or the first object to merge as a deep copy.
 *
 * @param {...Array<object|undefined>} [sources]
 *        Object(s) to merge into the previous one.
 *
 * @return {T}
 *         The merged object. If the first argument is true, the return is the
 *         same as the second argument.
 */
function merge(extendOrSource, ...sources) {
    let i, args = [extendOrSource, ...sources], ret = {};
    const doCopy = function (copy, original) {
        // An object is replacing a primitive
        if (typeof copy !== 'object') {
            copy = {};
        }
        objectEach(original, function (value, key) {
            // Prototype pollution (#14883)
            if (key === '__proto__' || key === 'constructor') {
                return;
            }
            // Copy the contents of objects, but not arrays or DOM nodes
            if (isObject(value, true) &&
                !isClass(value) &&
                !isDOMElement(value)) {
                copy[key] = doCopy(copy[key] || {}, value);
                // Primitives and arrays are copied over directly
            }
            else {
                copy[key] = original[key];
            }
        });
        return copy;
    };
    // If first argument is true, copy into the existing object. Used in
    // setOptions.
    if (extendOrSource === true) {
        ret = args[1];
        args = Array.prototype.slice.call(args, 2);
    }
    // For each argument, extend the return
    const len = args.length;
    for (i = 0; i < len; i++) {
        ret = doCopy(ret, args[i]);
    }
    return ret;
}
/**
 * Take an interval and normalize it to multiples of round numbers.
 *
 * @deprecated
 * @function Highcharts.normalizeTickInterval
 *
 * @param {number} interval
 *        The raw, un-rounded interval.
 *
 * @param {Array<*>} [multiples]
 *        Allowed multiples.
 *
 * @param {number} [magnitude]
 *        The magnitude of the number.
 *
 * @param {boolean} [allowDecimals]
 *        Whether to allow decimals.
 *
 * @param {boolean} [hasTickAmount]
 *        If it has tickAmount, avoid landing on tick intervals lower than
 *        original.
 *
 * @return {number}
 *         The normalized interval.
 *
 * @todo
 * Move this function to the Axis prototype. It is here only for historical
 * reasons.
 */
function normalizeTickInterval(interval, multiples, magnitude, allowDecimals, hasTickAmount) {
    let i, retInterval = interval;
    // Round to a tenfold of 1, 2, 2.5 or 5
    magnitude = Utilities_pick(magnitude, getMagnitude(interval));
    const normalized = interval / magnitude;
    // Multiples for a linear scale
    if (!multiples) {
        multiples = hasTickAmount ?
            // Finer grained ticks when the tick amount is hard set, including
            // when alignTicks is true on multiple axes (#4580).
            [1, 1.2, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10] :
            // Else, let ticks fall on rounder numbers
            [1, 2, 2.5, 5, 10];
        // The allowDecimals option
        if (allowDecimals === false) {
            if (magnitude === 1) {
                multiples = multiples.filter(function (num) {
                    return num % 1 === 0;
                });
            }
            else if (magnitude <= 0.1) {
                multiples = [1 / magnitude];
            }
        }
    }
    // Normalize the interval to the nearest multiple
    for (i = 0; i < multiples.length; i++) {
        retInterval = multiples[i];
        // Only allow tick amounts smaller than natural
        if ((hasTickAmount &&
            retInterval * magnitude >= interval) ||
            (!hasTickAmount &&
                (normalized <=
                    (multiples[i] +
                        (multiples[i + 1] || multiples[i])) / 2))) {
            break;
        }
    }
    // Multiply back to the correct magnitude. Correct floats to appropriate
    // precision (#6085).
    retInterval = correctFloat(retInterval * magnitude, -Math.round(Math.log(0.001) / Math.LN10));
    return retInterval;
}
/**
 * Iterate over object key pairs in an object.
 *
 * @function Highcharts.objectEach<T>
 *
 * @param {*} obj
 *        The object to iterate over.
 *
 * @param {Highcharts.ObjectEachCallbackFunction<T>} fn
 *        The iterator callback. It passes three arguments:
 *        * value - The property value.
 *        * key - The property key.
 *        * obj - The object that objectEach is being applied to.
 *
 * @param {T} [ctx]
 *        The context.
 */
function objectEach(obj, fn, ctx) {
    for (const key in obj) {
        if (Object.hasOwnProperty.call(obj, key)) {
            fn.call(ctx || obj[key], obj[key], key, obj);
        }
    }
}
/**
 * Get the element's offset position, corrected for `overflow: auto`.
 *
 * @function Highcharts.offset
 *
 * @param {global.Element} el
 *        The DOM element.
 *
 * @return {Highcharts.OffsetObject}
 *         An object containing `left` and `top` properties for the position in
 *         the page.
 */
function offset(el) {
    const docElem = doc.documentElement, box = (el.parentElement || el.parentNode) ?
        el.getBoundingClientRect() :
        { top: 0, left: 0, width: 0, height: 0 };
    return {
        top: box.top + (win.pageYOffset || docElem.scrollTop) -
            (docElem.clientTop || 0),
        left: box.left + (win.pageXOffset || docElem.scrollLeft) -
            (docElem.clientLeft || 0),
        width: box.width,
        height: box.height
    };
}
/**
 * Left-pad a string to a given length by adding a character repetitively.
 *
 * @function Highcharts.pad
 *
 * @param {number} number
 *        The input string or number.
 *
 * @param {number} [length]
 *        The desired string length.
 *
 * @param {string} [padder=0]
 *        The character to pad with.
 *
 * @return {string}
 *         The padded string.
 */
function pad(number, length, padder) {
    return new Array((length || 2) +
        1 -
        String(number)
            .replace('-', '')
            .length).join(padder || '0') + number;
}
/* eslint-disable jsdoc/check-param-names */
/**
 * Return the first value that is not null or undefined.
 *
 * @function Highcharts.pick<T>
 *
 * @param {...Array<T|null|undefined>} items
 *        Variable number of arguments to inspect.
 *
 * @return {T}
 *         The value of the first argument that is not null or undefined.
 */
function Utilities_pick() {
    const args = arguments;
    const length = args.length;
    for (let i = 0; i < length; i++) {
        const arg = args[i];
        if (typeof arg !== 'undefined' && arg !== null) {
            return arg;
        }
    }
}
/* eslint-enable jsdoc/check-param-names */
/**
 * Shortcut for parseInt
 *
 * @internal
 * @function Highcharts.pInt
 *
 * @param {*} s
 *        any
 *
 * @param {number} [mag]
 *        Magnitude
 *
 * @return {number}
 *         number
 */
function pInt(s, mag) {
    return parseInt(s, mag || 10);
}
/**
 * Adds an item to an array, if it is not present in the array.
 *
 * @internal
 *
 * @function Highcharts.pushUnique
 *
 * @param {Array<unknown>} array
 * The array to add the item to.
 *
 * @param {unknown} item
 * The item to add.
 *
 * @return {boolean}
 * Returns true, if the item was not present and has been added.
 */
function pushUnique(array, item) {
    return array.indexOf(item) < 0 && !!array.push(item);
}
/**
 * Return a length based on either the integer value, or a percentage of a base.
 *
 * @function Highcharts.relativeLength
 *
 * @param {Highcharts.RelativeSize} value
 *        A percentage string or a number.
 *
 * @param {number} base
 *        The full length that represents 100%.
 *
 * @param {number} [offset=0]
 *        A pixel offset to apply for percentage values. Used internally in
 *        axis positioning.
 *
 * @return {number}
 *         The computed length.
 */
function relativeLength(value, base, offset) {
    return (/%$/).test(value) ?
        (base * parseFloat(value) / 100) + (offset || 0) :
        parseFloat(value);
}
/**
 * Replaces text in a string with a given replacement in a loop to catch nested
 * matches after previous replacements.
 *
 * @internal
 *
 * @function Highcharts.replaceNested
 *
 * @param {string} text
 * Text to search and modify.
 *
 * @param {...Array<(RegExp|string)>} replacements
 * One or multiple tuples with search pattern (`[0]: (string|RegExp)`) and
 * replacement (`[1]: string`) for matching text.
 *
 * @return {string}
 * Text with replacements.
 */
function replaceNested(text, ...replacements) {
    let previous, replacement;
    do {
        previous = text;
        for (replacement of replacements) {
            text = text.replace(replacement[0], replacement[1]);
        }
    } while (text !== previous);
    return text;
}
/**
 * Remove an event that was added with {@link Highcharts#addEvent}.
 *
 * @function Highcharts.removeEvent<T>
 *
 * @param {Highcharts.Class<T>|T} el
 *        The element to remove events on.
 *
 * @param {string} [type]
 *        The type of events to remove. If undefined, all events are removed
 *        from the element.
 *
 * @param {Highcharts.EventCallbackFunction<T>} [fn]
 *        The specific callback to remove. If undefined, all events that match
 *        the element and optionally the type are removed.
 *
 * @return {void}
 */
function removeEvent(el, type, fn) {
    /** @internal */
    function removeOneEvent(type, fn) {
        const removeEventListener = el.removeEventListener;
        if (removeEventListener) {
            removeEventListener.call(el, type, fn, false);
        }
    }
    /** @internal */
    function removeAllEvents(eventCollection) {
        let types, len;
        if (!el.nodeName) {
            return; // Break on non-DOM events
        }
        if (type) {
            types = {};
            types[type] = true;
        }
        else {
            types = eventCollection;
        }
        objectEach(types, function (_val, n) {
            if (eventCollection[n]) {
                len = eventCollection[n].length;
                while (len--) {
                    removeOneEvent(n, eventCollection[n][len].fn);
                }
            }
        });
    }
    const owner = typeof el === 'function' && el.prototype || el;
    if (Object.hasOwnProperty.call(owner, 'hcEvents')) {
        const events = owner.hcEvents;
        if (type) {
            const typeEvents = (events[type] || []);
            if (fn) {
                events[type] = typeEvents.filter(function (obj) {
                    return fn !== obj.fn;
                });
                removeOneEvent(type, fn);
            }
            else {
                removeAllEvents(events);
                events[type] = [];
            }
        }
        else {
            removeAllEvents(events);
            delete owner.hcEvents;
        }
    }
}
/**
 * Check if an element is an array, and if not, make it into an array.
 *
 * @function Highcharts.splat
 *
 * @param {*} obj
 *        The object to splat.
 *
 * @return {Array}
 *         The produced or original array.
 */
function splat(obj) {
    return isArray(obj) ? obj : [obj];
}
/**
 * Sort an object array and keep the order of equal items. The ECMAScript
 * standard does not specify the behavior when items are equal.
 *
 * @function Highcharts.stableSort
 *
 * @param {Array<*>} arr
 *        The array to sort.
 *
 * @param {Function} sortFunction
 *        The function to sort it with, like with regular Array.prototype.sort.
 */
function stableSort(arr, sortFunction) {
    // @todo It seems like Chrome since v70 sorts in a stable way internally,
    // plus all other browsers do it, so over time we may be able to remove this
    // function
    const length = arr.length;
    let sortValue, i;
    // Add index to each item
    for (i = 0; i < length; i++) {
        arr[i].safeI = i; // Stable sort index
    }
    arr.sort(function (a, b) {
        sortValue = sortFunction(a, b);
        return sortValue === 0 ? a.safeI - b.safeI : sortValue;
    });
    // Remove index from items
    for (i = 0; i < length; i++) {
        delete arr[i].safeI; // Stable sort index
    }
}
/**
 * Set a timeout if the delay is given, otherwise perform the function
 * synchronously.
 *
 * @function Highcharts.syncTimeout
 *
 * @param {Function} fn
 *        The function callback.
 *
 * @param {number} delay
 *        Delay in milliseconds.
 *
 * @param {*} [context]
 *        An optional context to send to the function callback.
 *
 * @return {number}
 *         An identifier for the timeout that can later be cleared with
 *         Highcharts.clearTimeout. Returns -1 if there is no timeout.
 */
function syncTimeout(fn, delay, context) {
    if (delay > 0) {
        return setTimeout(fn, delay, context);
    }
    fn.call(0, context);
    return -1;
}
/**
 * @internal
 */
function ucfirst(s) {
    return ((isString(s) ?
        s.substring(0, 1).toUpperCase() + s.substring(1) :
        String(s)));
}
/**
 * Wrap a method with extended functionality, preserving the original function.
 *
 * @function Highcharts.wrap
 *
 * @param {*} obj
 *        The context object that the method belongs to. In real cases, this is
 *        often a prototype.
 *
 * @param {string} method
 *        The name of the method to extend.
 *
 * @param {Highcharts.WrapProceedFunction} func
 *        A wrapper function callback. This function is called with the same
 *        arguments as the original function, except that the original function
 *        is unshifted and passed as the first argument.
 */
function wrap(obj, method, func) {
    const proceed = obj[method];
    obj[method] = function () {
        const outerArgs = arguments, scope = this;
        return func.apply(this, [
            function () {
                return proceed.apply(scope, arguments.length ? arguments : outerArgs);
            }
        ].concat([].slice.call(arguments)));
    };
}

;// ./code/dashboards/es-modules/Data/Converters/DataConverterUtils.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Kamil Kubik
 *
 * */

/* *
 *
 *  Functions
 *
 * */
/**
 * Converts a value to a Date.
 *
 * @param {DataConverterType} value
 * Value to convert.
 *
 * @return {globalThis.Date}
 * Converted value as a Date.
 */
function asDate(value, converter) {
    let timestamp;
    if (typeof value === 'string') {
        timestamp = converter.parseDate(value);
    }
    else if (typeof value === 'number') {
        timestamp = value;
    }
    else if (value instanceof Date) {
        return value;
    }
    else {
        timestamp = converter.parseDate(asString(value));
    }
    return new Date(timestamp);
}
/**
 * Converts a value to a number.
 *
 * @param {DataConverterType} value
 * Value to convert.
 *
 * @return {number}
 * Converted value as a number.
 */
function asNumber(value, decimalRegExp) {
    if (typeof value === 'number') {
        return value;
    }
    if (typeof value === 'boolean') {
        return value ? 1 : 0;
    }
    if (typeof value === 'string') {
        const decimalRegex = decimalRegExp;
        if (value.indexOf(' ') > -1) {
            value = value.replace(/\s+/g, '');
        }
        if (decimalRegex) {
            if (!decimalRegex.test(value)) {
                return NaN;
            }
            value = value.replace(decimalRegex, '$1.$2');
        }
        return parseFloat(value);
    }
    if (value instanceof Date) {
        return value.getDate();
    }
    if (value) {
        return value.getRowCount();
    }
    return NaN;
}
/**
 * Converts a value to a string.
 *
 * @param {DataConverterType} value
 * Value to convert.
 *
 * @return {string}
 * Converted value as a string.
 */
function asString(value) {
    return '' + value;
}
/**
 * Converts a value to a boolean.
 *
 * @param {DataConverterType} value
 * Value to convert.
 *
 * @return {boolean}
 * Converted value as a boolean.
 */
function asBoolean(value) {
    if (typeof value === 'boolean') {
        return value;
    }
    if (typeof value === 'string') {
        return value !== '' && value !== '0' && value !== 'false';
    }
    return !!asNumber(value);
}
/**
 * Guesses the potential type of a string value for parsing CSV etc.
 *
 * @param {*} value
 * The value to examine.
 *
 * @return {'number' | 'string' | 'Date'}
 * Type string, either `string`, `Date`, or `number`.
 */
function guessType(value, converter) {
    let result = 'string';
    if (typeof value === 'string') {
        const trimedValue = trim(`${value}`), decimalRegExp = converter.decimalRegExp;
        let innerTrimedValue = trim(trimedValue, true);
        if (decimalRegExp) {
            innerTrimedValue = (decimalRegExp.test(innerTrimedValue) ?
                innerTrimedValue.replace(decimalRegExp, '$1.$2') :
                '');
        }
        const floatValue = parseFloat(innerTrimedValue);
        if (+innerTrimedValue === floatValue) {
            // String is numeric
            value = floatValue;
        }
        else {
            // Determine if a date string
            const dateValue = converter.parseDate(value);
            result = Utilities_isNumber(dateValue) ? 'Date' : 'string';
        }
    }
    if (typeof value === 'number') {
        // Greater than milliseconds in a year assumed timestamp
        result = value > 365 * 24 * 3600 * 1000 ? 'Date' : 'number';
    }
    return result;
}
/**
 * Trim a string from whitespaces.
 *
 * @param {string} str
 * String to trim.
 *
 * @param {boolean} [inside=false]
 * Remove all spaces between numbers.
 *
 * @return {string}
 * Trimmed string
 */
function trim(str, inside) {
    if (typeof str === 'string') {
        str = str.replace(/^\s+|\s+$/g, '');
        // Clear white space inside the string, like thousands separators
        if (inside && /^[\d\s]+$/.test(str)) {
            str = str.replace(/\s/g, '');
        }
    }
    return str;
}
/**
 * Parses an array of columns to a column collection. If more headers are
 * provided, the corresponding, empty columns are added.
 *
 * @param {DataTableColumn[]} [columnsArray]
 * Array of columns.
 *
 * @param {string[]} [headers]
 * Column ids to use.
 *
 * @return {DataTableColumnCollection}
 * Parsed columns.
 */
function getColumnsCollection(columnsArray = [], headers) {
    const columns = {};
    for (let i = 0, iEnd = Math.max(headers.length, columnsArray.length); i < iEnd; ++i) {
        const columnId = headers[i] || `${i}`;
        columns[columnId] = columnsArray[i] ? columnsArray[i].slice() : [];
    }
    return columns;
}
/* *
 *
 *  Default Export
 *
 * */
const DataConverterUtils = {
    asBoolean,
    asDate,
    asNumber,
    asString,
    getColumnsCollection,
    guessType,
    trim
};
/* harmony default export */ const Converters_DataConverterUtils = (DataConverterUtils);

;// ./code/dashboards/es-modules/Data/Converters/DataConverter.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *  - Sebastian Bochan
 *  - Gøran Slettemark
 *  - Torstein Hønsi
 *  - Wojciech Chmiel
 *  - Jomar Hønsi
 *  - Kamil Kubik
 *
 * */



/* *
 *
 *  Class
 *
 * */
/**
 * Base class providing an interface and basic methods for a DataConverter
 *
 * @private
 */
class DataConverter {
    /**
     * Adds a converter class to the registry.
     *
     * @private
     *
     * @param {string} key
     * Registry key of the converter class.
     *
     * @param {DataConverterTypes} DataConverterClass
     * Connector class (aka class constructor) to register.
     *
     * @return {boolean}
     * Returns true, if the registration was successful. False is returned, if
     * their is already a converter registered with this key.
     */
    static registerType(key, DataConverterClass) {
        return (!!key &&
            !DataConverter.types[key] &&
            !!(DataConverter.types[key] = DataConverterClass));
    }
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Constructs an instance of the DataConverter.
     *
     * @param {UserOptions} [options]
     * Options for the DataConverter.
     */
    constructor(options) {
        /* *
         *
         *  Properties
         *
         * */
        /**
         * A collection of available date formats.
         */
        this.dateFormats = {
            'YYYY/mm/dd': {
                regex: /^(\d{4})([\-\.\/])(\d{1,2})\2(\d{1,2})$/,
                parser: function (match) {
                    return (match ?
                        Date.UTC(+match[1], +match[3] - 1, +match[4]) : NaN);
                }
            },
            'dd/mm/YYYY': {
                regex: /^(\d{1,2})([\-\.\/])(\d{1,2})\2(\d{4})$/,
                parser: function (match) {
                    return (match ?
                        Date.UTC(+match[4], +match[3] - 1, +match[1]) : NaN);
                },
                alternative: 'mm/dd/YYYY' // Different format with the same regex
            },
            'mm/dd/YYYY': {
                regex: /^(\d{1,2})([\-\.\/])(\d{1,2})\2(\d{4})$/,
                parser: function (match) {
                    return (match ?
                        Date.UTC(+match[4], +match[1] - 1, +match[3]) : NaN);
                }
            },
            'dd/mm/YY': {
                regex: /^(\d{1,2})([\-\.\/])(\d{1,2})\2(\d{2})$/,
                parser: function (match) {
                    const d = new Date();
                    if (!match) {
                        return NaN;
                    }
                    let year = +match[4];
                    if (year > (d.getFullYear() - 2000)) {
                        year += 1900;
                    }
                    else {
                        year += 2000;
                    }
                    return Date.UTC(year, +match[3] - 1, +match[1]);
                },
                alternative: 'mm/dd/YY' // Different format with the same regex
            },
            'mm/dd/YY': {
                regex: /^(\d{1,2})([\-\.\/])(\d{1,2})\2(\d{2})$/,
                parser: function (match) {
                    return (match ?
                        Date.UTC(+match[4] + 2000, +match[1] - 1, +match[3]) :
                        NaN);
                }
            }
        };
        const mergedOptions = merge(DataConverter.defaultOptions, options);
        let regExpPoint = mergedOptions.decimalPoint;
        if (regExpPoint === '.' || regExpPoint === ',') {
            regExpPoint = regExpPoint === '.' ? '\\.' : ',';
            this.decimalRegExp =
                new RegExp('^(-?[0-9]+)' + regExpPoint + '([0-9]+)$');
        }
        this.options = mergedOptions;
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Converts a string value based on its guessed type.
     *
     * @param {*} value
     * The value to examine.
     *
     * @return {number | string | Date}
     * The converted value.
     */
    convertByType(value) {
        const converter = this, typeMap = {
            'number': (value) => Converters_DataConverterUtils.asNumber(value, converter.decimalRegExp),
            'Date': (value) => Converters_DataConverterUtils.asDate(value, converter),
            'string': Converters_DataConverterUtils.asString
        };
        return typeMap[Converters_DataConverterUtils.guessType(value, converter)]
            .call(converter, value);
    }
    /**
     * Tries to guess the date format
     *  - Check if either month candidate exceeds 12
     *  - Check if year is missing (use current year)
     *  - Check if a shortened year format is used (e.g. 1/1/99)
     *  - If no guess can be made, the user must be prompted
     * data is the data to deduce a format based on
     * @private
     *
     * @param {string[]} data
     * Data to check the format.
     *
     * @param {number} limit
     * Max data to check the format.
     *
     * @param {boolean} save
     * Whether to save the date format in the converter options.
     */
    deduceDateFormat(data, limit, save) {
        const parser = this, stable = [], max = [];
        let format = 'YYYY/mm/dd', thing, guessedFormat = [], i = 0, madeDeduction = false, elem, j;
        if (!limit || limit > data.length) {
            limit = data.length;
        }
        for (; i < limit; i++) {
            if (typeof data[i] !== 'undefined' &&
                data[i] && data[i].length) {
                thing = data[i]
                    .trim()
                    .replace(/[\-\.\/]/g, ' ')
                    .split(' ');
                guessedFormat = [
                    '',
                    '',
                    ''
                ];
                for (j = 0; j < thing.length; j++) {
                    if (j < guessedFormat.length) {
                        elem = parseInt(thing[j], 10);
                        if (elem) {
                            max[j] = (!max[j] || max[j] < elem) ? elem : max[j];
                            if (typeof stable[j] !== 'undefined') {
                                if (stable[j] !== elem) {
                                    stable[j] = false;
                                }
                            }
                            else {
                                stable[j] = elem;
                            }
                            if (elem > 31) {
                                if (elem < 100) {
                                    guessedFormat[j] = 'YY';
                                }
                                else {
                                    guessedFormat[j] = 'YYYY';
                                }
                            }
                            else if (elem > 12 &&
                                elem <= 31) {
                                guessedFormat[j] = 'dd';
                                madeDeduction = true;
                            }
                            else if (!guessedFormat[j].length) {
                                guessedFormat[j] = 'mm';
                            }
                        }
                    }
                }
            }
        }
        if (madeDeduction) {
            // This handles a few edge cases with hard to guess dates
            for (j = 0; j < stable.length; j++) {
                if (stable[j] !== false) {
                    if (max[j] > 12 &&
                        guessedFormat[j] !== 'YY' &&
                        guessedFormat[j] !== 'YYYY') {
                        guessedFormat[j] = 'YY';
                    }
                }
                else if (max[j] > 12 && guessedFormat[j] === 'mm') {
                    guessedFormat[j] = 'dd';
                }
            }
            // If the middle one is dd, and the last one is dd,
            // the last should likely be year.
            if (guessedFormat.length === 3 &&
                guessedFormat[1] === 'dd' &&
                guessedFormat[2] === 'dd') {
                guessedFormat[2] = 'YY';
            }
            format = guessedFormat.join('/');
            // If the calculated format is not valid, we need to present an
            // error.
        }
        // Save the deduced format in the converter options.
        if (save) {
            parser.options.dateFormat = format;
        }
        return format;
    }
    /**
     * Emits an event on the DataConverter instance.
     *
     * @param {Event} [e]
     * Event object containing additional event data
     */
    emit(e) {
        fireEvent(this, e.type, e);
    }
    /**
     * Registers a callback for a specific event.
     *
     * @param {string} type
     * Event type as a string.
     *
     * @param {DataEventCallback} callback
     * Function to register for an modifier callback.
     *
     * @return {Function}
     * Function to unregister callback from the modifier event.
     */
    on(type, callback) {
        return addEvent(this, type, callback);
    }
    /**
     * Parse a date and return it as a number.
     *
     * @param {string} value
     * Value to parse.
     *
     * @param {string} dateFormatProp
     * Which of the predefined date formats
     * to use to parse date values.
     */
    parseDate(value, dateFormatProp) {
        const converter = this, options = converter.options;
        let dateFormat = dateFormatProp || options.dateFormat, result = NaN, key, match = null;
        if (options.parseDate) {
            result = options.parseDate(value);
        }
        else {
            const dateFormats = converter.dateFormats;
            // Auto-detect the date format the first time
            if (!dateFormat) {
                for (key in dateFormats) { // eslint-disable-line guard-for-in
                    const format = dateFormats[key];
                    match = value.match(format.regex);
                    if (match) {
                        dateFormat = key;
                        result = format.parser(match);
                        break;
                    }
                }
                // Next time, use the one previously found
            }
            else {
                let format = dateFormats[dateFormat];
                if (!format) {
                    // The selected format is invalid
                    format = dateFormats['YYYY/mm/dd'];
                }
                match = value.match(format.regex);
                if (match) {
                    result = format.parser(match);
                }
            }
            // Fall back to Date.parse
            if (!match) {
                const parsed = Date.parse(value);
                if (!isNaN(parsed)) {
                    result =
                        parsed - new Date(parsed).getTimezoneOffset() * 60000;
                    // Reset dates without year in Chrome
                    if (!value.includes('2001') &&
                        new Date(result).getFullYear() === 2001) {
                        result = NaN;
                    }
                }
            }
        }
        return result;
    }
}
/* *
 *
 *  Static Properties
 *
 * */
/**
 * Default options
 */
DataConverter.defaultOptions = {
    dateFormat: '',
    firstRowAsNames: true
};
/**
 * Registry as a record object with converter names and their class.
 */
DataConverter.types = {};
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Converters_DataConverter = (DataConverter);

;// ./code/dashboards/es-modules/Data/Converters/CSVConverter.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Torstein Hønsi
 *  - Christer Vasseng
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *  - Kamil Kubik
 *
 * */




/* *
 *
 *  Class
 *
 * */
/**
 * Handles parsing and transforming CSV to a table.
 *
 * @private
 */
class CSVConverter extends Converters_DataConverter {
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Constructs an instance of the CSV parser.
     *
     * @param {Partial<CSVConverterOptions>} [options]
     * Options for the CSV parser.
     */
    constructor(options) {
        const mergedOptions = merge(CSVConverter.defaultOptions, options);
        super(mergedOptions);
        /* *
         *
         *  Properties
         *
         * */
        this.headers = [];
        this.dataTypes = [];
        this.options = mergedOptions;
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Parses the CSV string into a DataTable column collection.
     * Handles line and item delimiters, optional header row, and
     * applies pre-processing if a beforeParse callback is provided.
     *
     * @param {Partial<CSVConverterOptions>} [options]
     * Options for the parser.
     * @param {DataEventDetail} [eventDetail]
     * Custom information for pending events.
     * @return {DataTableColumnCollection}
     * The parsed column collection.
     *
     * @emits CSVDataParser#parse
     * @emits CSVDataParser#afterParse
     */
    parse(options, eventDetail) {
        const converter = this, dataTypes = converter.dataTypes, parserOptions = merge(this.options, options), { beforeParse, lineDelimiter, firstRowAsNames, itemDelimiter } = parserOptions;
        let lines, rowIt = 0, { csv, startRow, endRow } = parserOptions, column;
        const columnsArray = [];
        converter.emit({
            type: 'parse',
            columns: columnsArray,
            detail: eventDetail,
            headers: converter.headers
        });
        if (csv && beforeParse) {
            csv = beforeParse(csv);
        }
        if (csv) {
            lines = csv
                .replace(/\r\n|\r/g, '\n') // Windows | Mac
                .split(lineDelimiter || '\n');
            if (!startRow || startRow < 0) {
                startRow = 0;
            }
            if (!endRow || endRow >= lines.length) {
                endRow = lines.length - 1;
            }
            if (!itemDelimiter) {
                converter.guessedItemDelimiter =
                    converter.guessDelimiter(lines);
            }
            // If the first row contain names, add them to the
            // headers array and skip the row.
            if (firstRowAsNames) {
                const headers = lines[0].split(itemDelimiter || converter.guessedItemDelimiter || ',');
                // Remove ""s from the headers
                for (let i = 0; i < headers.length; i++) {
                    headers[i] = headers[i].trim().replace(/^["']|["']$/g, '');
                }
                converter.headers = headers;
                startRow++;
            }
            let offset = 0;
            for (rowIt = startRow; rowIt <= endRow; rowIt++) {
                if (lines[rowIt][0] === '#') {
                    offset++;
                }
                else {
                    converter.parseCSVRow(columnsArray, lines[rowIt], rowIt - startRow - offset);
                }
            }
            if (dataTypes.length &&
                dataTypes[0].length &&
                dataTypes[0][1] === 'date' && // Format is a string date
                !converter.options.dateFormat) {
                converter.deduceDateFormat(columnsArray[0], null, true);
            }
            // Guess types.
            for (let i = 0, iEnd = columnsArray.length; i < iEnd; ++i) {
                column = columnsArray[i];
                for (let j = 0, jEnd = column.length; j < jEnd; ++j) {
                    if (column[j] && typeof column[j] === 'string') {
                        let cellValue = converter.convertByType(column[j]);
                        if (cellValue instanceof Date) {
                            cellValue = cellValue.getTime();
                        }
                        columnsArray[i][j] = cellValue;
                    }
                }
            }
        }
        // Normalize columns to same length to avoid truncation.
        columnsArray.forEach((col) => {
            col.length = Math.max(...columnsArray.map((c) => c.length));
        });
        converter.emit({
            type: 'afterParse',
            columns: columnsArray,
            detail: eventDetail,
            headers: converter.headers
        });
        return Converters_DataConverterUtils.getColumnsCollection(columnsArray, converter.headers);
    }
    /**
     * Parses a single CSV row string into columns, handling delimiters,
     * quoted values, data type inference, and column range selection.
     */
    parseCSVRow(columns, columnStr, rowNumber) {
        const converter = this, dataTypes = converter.dataTypes, { startColumn, endColumn } = converter.options, itemDelimiter = (converter.options.itemDelimiter ||
            converter.guessedItemDelimiter);
        let { decimalPoint } = converter.options;
        if (!decimalPoint || decimalPoint === itemDelimiter) {
            decimalPoint = converter.guessedDecimalPoint || '.';
        }
        let i = 0, c = '', token = '', actualColumn = 0, column = 0;
        const read = (j) => {
            c = columnStr[j];
        };
        const pushType = (type) => {
            if (dataTypes.length < column + 1) {
                dataTypes.push([type]);
            }
            if (dataTypes[column][dataTypes[column].length - 1] !== type) {
                dataTypes[column].push(type);
            }
        };
        const push = () => {
            if (startColumn > actualColumn || actualColumn > endColumn) {
                // Skip this column, but increment the column count (#7272)
                ++actualColumn;
                token = '';
                return;
            }
            // Save the type of the token.
            if (typeof token === 'string') {
                const parsedNumber = parseFloat(token);
                if (!isNaN(parsedNumber) && isFinite(Number(token))) {
                    token = parsedNumber;
                    pushType('number');
                }
                else if (!isNaN(Date.parse(token))) {
                    token = token.replace(/\//g, '-');
                    pushType('date');
                }
                else {
                    pushType('string');
                }
            }
            else {
                pushType('number');
            }
            if (columns.length < column + 1) {
                columns.push([]);
            }
            // Try to apply the decimal point, and check if the token then is a
            // number. If not, reapply the initial value
            if (typeof token !== 'number' &&
                Converters_DataConverterUtils.guessType(token, converter) !== 'number' &&
                decimalPoint) {
                const initialValue = token;
                token = token.replace(decimalPoint, '.');
                if (Converters_DataConverterUtils.guessType(token, converter) !== 'number') {
                    token = initialValue;
                }
            }
            columns[column][rowNumber] = token;
            token = '';
            ++column;
            ++actualColumn;
        };
        if (!columnStr.trim().length) {
            return;
        }
        if (columnStr.trim()[0] === '#') {
            return;
        }
        for (; i < columnStr.length; i++) {
            read(i);
            if (c === '#') {
                // If there are hexvalues remaining (#13283)
                if (!/^#[A-F\d]{3,3}|[A-F\d]{6,6}/i.test(columnStr.substring(i))) {
                    // The rest of the row is a comment
                    push();
                    return;
                }
            }
            // Quoted string
            if (c === '"') {
                read(++i);
                while (i < columnStr.length) {
                    if (c === '"') {
                        break;
                    }
                    token += c;
                    read(++i);
                }
            }
            else if (c === itemDelimiter) {
                push();
                // Actual column data
            }
            else {
                token += c;
            }
        }
        push();
    }
    /**
     * Internal method that guesses the delimiter from the first
     * 13 lines of the CSV
     * @param {string[]} lines
     * The CSV, split into lines
     */
    guessDelimiter(lines) {
        let points = 0, commas = 0, guessed;
        const potDelimiters = {
            ',': 0,
            ';': 0,
            '\t': 0
        }, linesCount = lines.length;
        for (let i = 0; i < linesCount; i++) {
            let inStr = false, c, cn, cl, token = '';
            // We should be able to detect dateFormats within 13 rows
            if (i > 13) {
                break;
            }
            const columnStr = lines[i];
            for (let j = 0; j < columnStr.length; j++) {
                c = columnStr[j];
                cn = columnStr[j + 1];
                cl = columnStr[j - 1];
                if (c === '#') {
                    // Skip the rest of the line - it's a comment
                    break;
                }
                if (c === '"') {
                    if (inStr) {
                        if (cl !== '"' && cn !== '"') {
                            while (cn === ' ' && j < columnStr.length) {
                                cn = columnStr[++j];
                            }
                            // After parsing a string, the next non-blank
                            // should be a delimiter if the CSV is properly
                            // formed.
                            if (typeof potDelimiters[cn] !== 'undefined') {
                                potDelimiters[cn]++;
                            }
                            inStr = false;
                        }
                    }
                    else {
                        inStr = true;
                    }
                }
                else if (typeof potDelimiters[c] !== 'undefined') {
                    token = token.trim();
                    if (!isNaN(Date.parse(token))) {
                        potDelimiters[c]++;
                    }
                    else if (isNaN(Number(token)) ||
                        !isFinite(Number(token))) {
                        potDelimiters[c]++;
                    }
                    token = '';
                }
                else {
                    token += c;
                }
                if (c === ',') {
                    commas++;
                }
                if (c === '.') {
                    points++;
                }
            }
        }
        // Count the potential delimiters.
        // This could be improved by checking if the number of delimiters
        // equals the number of columns - 1
        if (potDelimiters[';'] > potDelimiters[',']) {
            guessed = ';';
        }
        else if (potDelimiters[','] > potDelimiters[';']) {
            guessed = ',';
        }
        else {
            // No good guess could be made..
            guessed = ',';
        }
        // Try to deduce the decimal point if it's not explicitly set.
        // If both commas or points is > 0 there is likely an issue
        if (points > commas) {
            this.guessedDecimalPoint = '.';
        }
        else {
            this.guessedDecimalPoint = ',';
        }
        return guessed;
    }
}
/* *
 *
 *  Static Properties
 *
 * */
/**
 * Default options
 */
CSVConverter.defaultOptions = {
    ...Converters_DataConverter.defaultOptions,
    lineDelimiter: '\n',
    startColumn: 0,
    endColumn: Number.MAX_VALUE,
    startRow: 0,
    endRow: Number.MAX_VALUE
};
Converters_DataConverter.registerType('CSV', CSVConverter);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Converters_CSVConverter = (CSVConverter);

;// ./code/dashboards/es-modules/Data/Modifiers/DataModifier.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *  - Gøran Slettemark
 *  - Dawid Draguła
 *
 * */


/* *
 *
 *  Class
 *
 * */
/**
 * Abstract class to provide an interface for modifying a table.
 */
class DataModifier {
    /**
     * Adds a modifier class to the registry. The modifier class has to provide
     * the `DataModifier.options` property and the `DataModifier.modifyTable`
     * method to modify the table.
     *
     * @private
     *
     * @param {string} key
     * Registry key of the modifier class.
     *
     * @param {DataModifierType} DataModifierClass
     * Modifier class (aka class constructor) to register.
     *
     * @return {boolean}
     * Returns true, if the registration was successful. False is returned, if
     * their is already a modifier registered with this key.
     */
    static registerType(key, DataModifierClass) {
        return (!!key &&
            !DataModifier.types[key] &&
            !!(DataModifier.types[key] = DataModifierClass));
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Runs a timed execution of the modifier on the given datatable.
     * Can be configured to run multiple times.
     *
     * @param {DataTable} dataTable
     * The datatable to execute
     *
     * @param {BenchmarkOptions} options
     * Options. Currently supports `iterations` for number of iterations.
     *
     * @return {Array<number>}
     * An array of times in milliseconds
     *
     */
    benchmark(dataTable, options) {
        const results = [];
        const modifier = this;
        const execute = () => {
            modifier.modifyTable(dataTable);
            modifier.emit({
                type: 'afterBenchmarkIteration'
            });
        };
        const defaultOptions = {
            iterations: 1
        };
        const { iterations } = merge(defaultOptions, options);
        modifier.on('afterBenchmarkIteration', () => {
            if (results.length === iterations) {
                modifier.emit({
                    type: 'afterBenchmark',
                    results
                });
                return;
            }
            // Run again
            execute();
        });
        const times = {
            startTime: 0,
            endTime: 0
        };
        // Add timers
        modifier.on('modify', () => {
            times.startTime = window.performance.now();
        });
        modifier.on('afterModify', () => {
            times.endTime = window.performance.now();
            results.push(times.endTime - times.startTime);
        });
        // Initial run
        execute();
        return results;
    }
    /**
     * Emits an event on the modifier to all registered callbacks of this event.
     *
     * @param {DataModifierEvent} [e]
     * Event object containing additional event information.
     */
    emit(e) {
        fireEvent(this, e.type, e);
    }
    /**
     * Modifies the given table and sets its `modified` property as a reference
     * to the modified table. If `modified` property does not exist on the
     * original table, it's always created.
     *
     * @param {Highcharts.DataTable} table
     * Table to modify.
     *
     * @param {DataEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @return {Promise<Highcharts.DataTable>}
     * Table with `modified` property as a reference.
     */
    modify(table, eventDetail) {
        const modifier = this;
        return new Promise((resolve, reject) => {
            if (!table.modified) {
                table.modified = table.clone(false, eventDetail);
            }
            try {
                resolve(modifier.modifyTable(table, eventDetail));
            }
            catch (e) {
                modifier.emit({
                    type: 'error',
                    detail: eventDetail,
                    table
                });
                reject(e instanceof Error ? e : new Error('' + e));
            }
        });
    }
    /**
     * Registers a callback for a specific modifier event.
     *
     * @param {string} type
     * Event type as a string.
     *
     * @param {DataEventCallback} callback
     * Function to register for an modifier callback.
     *
     * @return {Function}
     * Function to unregister callback from the modifier event.
     */
    on(type, callback) {
        return addEvent(this, type, callback);
    }
}
/* *
 *
 *  Static Properties
 *
 * */
/**
 * Registry as a record object with modifier names and their class
 * constructor.
 */
DataModifier.types = {};
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Modifiers_DataModifier = (DataModifier);

;// ./code/dashboards/es-modules/Data/ColumnUtils.js
/* *
 *
 *  (c) 2020-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Dawid Draguła
 *
 * */
/* *
*
* Functions
*
* */
/**
 * Sets the length of the column array.
 *
 * @param {DataTableColumn} column
 * Column to be modified.
 *
 * @param {number} length
 * New length of the column.
 *
 * @param {boolean} asSubarray
 * If column is a typed array, return a subarray instead of a new array. It
 * is faster `O(1)`, but the entire buffer will be kept in memory until all
 * views of it are destroyed. Default is `false`.
 *
 * @return {DataTableColumn}
 * Modified column.
 *
 * @private
 */
function setLength(column, length, asSubarray) {
    if (Array.isArray(column)) {
        column.length = length;
        return column;
    }
    return column[asSubarray ? 'subarray' : 'slice'](0, length);
}
/**
 * Splices a column array.
 *
 * @param {DataTableColumn} column
 * Column to be modified.
 *
 * @param {number} start
 * Index at which to start changing the array.
 *
 * @param {number} deleteCount
 * An integer indicating the number of old array elements to remove.
 *
 * @param {boolean} removedAsSubarray
 * If column is a typed array, return a subarray instead of a new array. It
 * is faster `O(1)`, but the entire buffer will be kept in memory until all
 * views to it are destroyed. Default is `true`.
 *
 * @param {Array<number>|TypedArray} items
 * The elements to add to the array, beginning at the start index. If you
 * don't specify any elements, `splice()` will only remove elements from the
 * array.
 *
 * @return {SpliceResult}
 * Object containing removed elements and the modified column.
 *
 * @private
 */
function splice(column, start, deleteCount, removedAsSubarray, items = []) {
    if (Array.isArray(column)) {
        if (!Array.isArray(items)) {
            items = Array.from(items);
        }
        return {
            removed: column.splice(start, deleteCount, ...items),
            array: column
        };
    }
    const Constructor = Object.getPrototypeOf(column)
        .constructor;
    const removed = column[removedAsSubarray ? 'subarray' : 'slice'](start, start + deleteCount);
    const newLength = column.length - deleteCount + items.length;
    const result = new Constructor(newLength);
    result.set(column.subarray(0, start), 0);
    result.set(items, start);
    result.set(column.subarray(start + deleteCount), start + items.length);
    return {
        removed: removed,
        array: result
    };
}
/**
 * Converts a cell value to a number.
 *
 * @param {DataTableCellType} value
 * Cell value to convert to a number.
 *
 * @param {boolean} useNaN
 * If `true`, returns `NaN` for non-numeric values; if `false`,
 * returns `null` instead.
 *
 * @return {number | null}
 * Number or `null` if the value is not a number.
 *
 * @private
 */
function convertToNumber(value, useNaN) {
    switch (typeof value) {
        case 'boolean':
            return (value ? 1 : 0);
        case 'number':
            return (isNaN(value) && !useNaN ? null : value);
        default:
            value = parseFloat(`${value ?? ''}`);
            return (isNaN(value) && !useNaN ? null : value);
    }
}
/* *
 *
 *  Default Export
 *
 * */
const ColumnUtils = {
    convertToNumber,
    setLength,
    splice
};
/* harmony default export */ const Data_ColumnUtils = (ColumnUtils);

;// ./code/dashboards/es-modules/Core/Utilities.js
/* *
 *
 *  (c) 2010-2026 Highsoft AS
 *  Author: Torstein Hønsi
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 * */



const { charts, win: Utilities_win } = Core_Globals;
/* *
 *
 *  Functions
 *
 * */
/**
 * Provide error messages for debugging, with links to online explanation. This
 * function can be overridden to provide custom error handling.
 *
 * @sample highcharts/chart/highcharts-error/
 *         Custom error handler
 *
 * @function Highcharts.error
 *
 * @param {number|string} code
 *        The error code. See
 *        [errors.xml](https://github.com/highcharts/highcharts/blob/master/errors/errors.xml)
 *        for available codes. If it is a string, the error message is printed
 *        directly in the console.
 *
 * @param {boolean} [stop=false]
 *        Whether to throw an error or just log a warning in the console.
 *
 * @param {Highcharts.Chart} [chart]
 *        Reference to the chart that causes the error. Used in 'debugger'
 *        module to display errors directly on the chart.
 *        Important note: This argument is undefined for errors that lack
 *        access to the Chart instance. In such case, the error will be
 *        displayed on the last created chart.
 *
 * @param {Highcharts.Dictionary<string>} [params]
 *        Additional parameters for the generated message.
 *
 * @return {void}
 */
function error(code, stop, chart, params) {
    const severity = stop ? 'Highcharts error' : 'Highcharts warning';
    if (code === 32) {
        code = `${severity}: Deprecated member`;
    }
    const isCode = Utilities_isNumber(code);
    let message = isCode ?
        `${severity} #${code}: www.highcharts.com/errors/${code}/` :
        code.toString();
    const defaultHandler = function () {
        if (stop) {
            throw new Error(message);
        }
        // Else ...
        if (Utilities_win.console &&
            error.messages.indexOf(message) === -1 // Prevent console floating
        ) {
            console.warn(message); // eslint-disable-line no-console
        }
    };
    if (typeof params !== 'undefined') {
        let additionalMessages = '';
        if (isCode) {
            message += '?';
        }
        objectEach(params, function (value, key) {
            additionalMessages += `\n - ${key}: ${value}`;
            if (isCode) {
                message += encodeURI(key) + '=' + encodeURI(value);
            }
        });
        message += additionalMessages;
    }
    fireEvent(Core_Globals, 'displayError', { chart, code, message, params }, defaultHandler);
    error.messages.push(message);
}
(function (error) {
    error.messages = [];
})(error || (error = {}));
/**
 * Insert a series or an axis in a collection with other items, either the
 * chart series or yAxis series or axis collections, in the correct order
 * according to the index option and whether it is internal. Used internally
 * when adding series and axes.
 *
 * @internal
 * @function Highcharts.Chart#insertItem
 * @param  {Highcharts.Series|Highcharts.Axis} item
 *         The item to insert
 * @param  {Array<Highcharts.Series>|Array<Highcharts.Axis>} collection
 *         A collection of items, like `chart.series` or `xAxis.series`.
 * @return {number} The index of the series in the collection.
 */
function insertItem(item, collection) {
    const indexOption = item.options.index, length = collection.length;
    let i;
    for (
    // Internal item (navigator) should always be pushed to the end
    i = item.options.isInternal ? length : 0; i < length + 1; i++) {
        if (
        // No index option, reached the end of the collection,
        // equivalent to pushing
        !collection[i] ||
            // Handle index option, the element to insert has lower index
            (isNumber(indexOption) &&
                indexOption < pick(collection[i].options.index, collection[i]._i)) ||
            // Insert the new item before other internal items
            // (navigator)
            collection[i].options.isInternal) {
            collection.splice(i, 0, item);
            break;
        }
    }
    return i;
}
/**
 * The time unit lookup
 *
 * @ignore
 */
const timeUnits = {
    millisecond: 1,
    second: 1000,
    minute: 60000,
    hour: 3600000,
    day: 24 * 3600000,
    week: 7 * 24 * 3600000,
    month: 28 * 24 * 3600000,
    year: 364 * 24 * 3600000
};
/**
 * Easing definition
 *
 * @internal
 * @function Math.easeInOutSine
 *
 * @param {number} pos
 * Current position, ranging from 0 to 1.
 *
 * @return {number}
 * Ease result
 */
Math.easeInOutSine = function (pos) {
    return -0.5 * (Math.cos(Math.PI * pos) - 1);
};
let serialMode;
/**
 * Get a unique key for using in internal element id's and pointers. The key is
 * composed of a random hash specific to this Highcharts instance, and a
 * counter.
 *
 * @example
 * let id = uniqueKey(); // => 'highcharts-x45f6hp-0'
 *
 * @function Highcharts.uniqueKey
 *
 * @return {string}
 * A unique key.
 */
const uniqueKey = (function () {
    const hash = Math.random().toString(36).substring(2, 9) + '-';
    let id = 0;
    return function () {
        return 'highcharts-' + (serialMode ? '' : hash) + id++;
    };
}());
/**
 * Activates a serial mode for element IDs provided by
 * {@link Highcharts.uniqueKey}. This mode can be used in automated tests, where
 * a simple comparison of two rendered SVG graphics is needed.
 *
 * **Note:** This is only for testing purposes and will break functionality in
 * webpages with multiple charts.
 *
 * @example
 * if (
 *   process &&
 *   process.env.NODE_ENV === 'development'
 * ) {
 *   Highcharts.useSerialIds(true);
 * }
 *
 * @function Highcharts.useSerialIds
 *
 * @param {boolean} [mode]
 * Changes the state of serial mode.
 *
 * @return {boolean|undefined}
 * State of the serial mode.
 */
function useSerialIds(mode) {
    return (serialMode = pick(mode, serialMode));
}
/* *
 *
 *  External
 *
 * */
// Register Highcharts as a plugin in jQuery
if (Utilities_win.jQuery) {
    /**
     * Highcharts-extended JQuery.
     *
     * @external JQuery
     */
    /* eslint-disable jsdoc/check-param-names */
    /**
     * Helper function to return the chart of the current JQuery selector
     * element.
     *
     * @function external:JQuery#highcharts
     *
     * @return {Highcharts.Chart}
     *         The chart that is linked to the JQuery selector element.
     */ /**
    * Factory function to create a chart in the current JQuery selector
    * element.
    *
    * @function external:JQuery#highcharts
    *
    * @param {'Chart'|'Map'|'StockChart'|string} [className]
    *        Name of the factory class in the Highcharts namespace.
    *
    * @param {Highcharts.Options} [options]
    *        The chart options structure.
    *
    * @param {Highcharts.ChartCallbackFunction} [callback]
    *        Function to run when the chart has loaded and all external
    *        images are loaded. Defining a
    *        [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
    *        handler is equivalent.
    *
    * @return {JQuery}
    *         The current JQuery selector.
    */
    Utilities_win.jQuery.fn.highcharts = function () {
        const args = [].slice.call(arguments);
        if (this[0]) { // `this[0]` is the renderTo div
            // Create the chart
            if (args[0]) {
                new Core_Globals[ // eslint-disable-line computed-property-spacing, no-new
                // Constructor defaults to Chart
                isString(args[0]) ? args.shift() : 'Chart'](this[0], args[0], args[1]);
                return this;
            }
            // When called without parameters or with the return argument,
            // return an existing chart
            return charts[attr(this[0], 'data-highcharts-chart')];
        }
    };
    /* eslint-enable jsdoc/check-param-names */
}
/* *
 *
 *  API Declarations
 *
 * */
/**
 * Creates a frame for the animated SVG element.
 *
 * @callback Highcharts.AnimationStepCallbackFunction
 *
 * @param {Highcharts.SVGElement} this
 *        The SVG element to animate.
 *
 * @return {void}
 */
/**
 * Interface description for a class.
 *
 * @interface Highcharts.Class<T>
 * @extends Function
 */ /**
* Class constructor.
* @function Highcharts.Class<T>#new
* @param {...Array<*>} args
*        Constructor arguments.
* @return {T}
*         Class instance.
*/
/**
 * A style object with camel case property names to define visual appearance of
 * a SVG element or HTML element. The properties can be whatever styles are
 * supported on the given SVG or HTML element.
 *
 * @example
 * {
 *    fontFamily: 'monospace',
 *    fontSize: '1.2em'
 * }
 *
 * @interface Highcharts.CSSObject
 */ /**
* @name Highcharts.CSSObject#[key:string]
* @type {boolean|number|string|undefined}
*/ /**
* Background style for the element.
* @name Highcharts.CSSObject#background
* @type {string|undefined}
*/ /**
* Background color of the element.
* @name Highcharts.CSSObject#backgroundColor
* @type {Highcharts.ColorString|undefined}
*/ /**
* Border style for the element.
* @name Highcharts.CSSObject#border
* @type {string|undefined}
*/ /**
* Radius of the element border.
* @name Highcharts.CSSObject#borderRadius
* @type {string|undefined}
*/ /**
* Color used in the element. The 'contrast' option is a Highcharts custom
* property that results in black or white, depending on the background of the
* element.
* @name Highcharts.CSSObject#color
* @type {'contrast'|Highcharts.ColorString|undefined}
*/ /**
* Style of the mouse cursor when resting over the element.
* @name Highcharts.CSSObject#cursor
* @type {Highcharts.CursorValue|undefined}
*/ /**
* Font family of the element text. Multiple values have to be in decreasing
* preference order and separated by comma.
* @name Highcharts.CSSObject#fontFamily
* @type {string|undefined}
*/ /**
* Font size of the element text.
* @name Highcharts.CSSObject#fontSize
* @type {number|string|undefined}
*/ /**
* Font weight of the element text.
* @name Highcharts.CSSObject#fontWeight
* @type {string|undefined}
*/ /**
* Height of the element.
* @name Highcharts.CSSObject#height
* @type {number|undefined}
*/ /**
* The maximum number of lines. If lines are cropped away, an ellipsis will be
* added.
* @name Highcharts.CSSObject#lineClamp
* @type {number|undefined}
*/ /**
* Width of the element border.
* @name Highcharts.CSSObject#lineWidth
* @type {number|undefined}
*/ /**
* Opacity of the element.
* @name Highcharts.CSSObject#opacity
* @type {number|undefined}
*/ /**
* Space around the element content.
* @name Highcharts.CSSObject#padding
* @type {string|undefined}
*/ /**
* Behavior of the element when the mouse cursor rests over it.
* @name Highcharts.CSSObject#pointerEvents
* @type {string|undefined}
*/ /**
* Positioning of the element.
* @name Highcharts.CSSObject#position
* @type {string|undefined}
*/ /**
* Alignment of the element text.
* @name Highcharts.CSSObject#textAlign
* @type {string|undefined}
*/ /**
* Additional decoration of the element text.
* @name Highcharts.CSSObject#textDecoration
* @type {string|undefined}
*/ /**
* Outline style of the element text.
* @name Highcharts.CSSObject#textOutline
* @type {string|undefined}
*/ /**
* Line break style of the element text. Highcharts SVG elements support
* `ellipsis` when a `width` is set.
* @name Highcharts.CSSObject#textOverflow
* @type {string|undefined}
*/ /**
* Top spacing of the element relative to the parent element.
* @name Highcharts.CSSObject#top
* @type {string|undefined}
*/ /**
* Animated transition of selected element properties.
* @name Highcharts.CSSObject#transition
* @type {string|undefined}
*/ /**
* Line break style of the element text.
* @name Highcharts.CSSObject#whiteSpace
* @type {string|undefined}
*/ /**
* Width of the element.
* @name Highcharts.CSSObject#width
* @type {number|undefined}
*/
/**
 * All possible cursor styles.
 *
 * @typedef {'alias'|'all-scroll'|'auto'|'cell'|'col-resize'|'context-menu'|'copy'|'crosshair'|'default'|'e-resize'|'ew-resize'|'grab'|'grabbing'|'help'|'move'|'n-resize'|'ne-resize'|'nesw-resize'|'no-drop'|'none'|'not-allowed'|'ns-resize'|'nw-resize'|'nwse-resize'|'pointer'|'progress'|'row-resize'|'s-resize'|'se-resize'|'sw-resize'|'text'|'vertical-text'|'w-resize'|'wait'|'zoom-in'|'zoom-out'} Highcharts.CursorValue
 */
/**
 * All possible dash styles.
 *
 * @typedef {'Dash'|'DashDot'|'Dot'|'LongDash'|'LongDashDot'|'LongDashDotDot'|'ShortDash'|'ShortDashDot'|'ShortDashDotDot'|'ShortDot'|'Solid'} Highcharts.DashStyleValue
 */
/**
 * Generic dictionary in TypeScript notation.
 * Use the native `AnyRecord` instead.
 *
 * @deprecated
 * @interface Highcharts.Dictionary<T>
 */ /**
* @name Highcharts.Dictionary<T>#[key:string]
* @type {T}
*/
/**
 * @typedef {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} Highcharts.DOMElementType
 */
/**
 * The function callback to execute when the event is fired. The `this` context
 * contains the instance, that fired the event.
 *
 * @callback Highcharts.EventCallbackFunction<T>
 *
 * @param {T} this
 *
 * @param {Highcharts.Dictionary<*>|Event} [eventArguments]
 *        Event arguments.
 *
 * @param {T} [ctx]
 *        Since v12.6.0, the callback context is passed as the last argument,
 *        so arrow functions can access the same context as regular functions
 *        using `this`.
 *
 * @return {boolean|void}
 */
/**
 * The event options for adding function callback.
 *
 * @interface Highcharts.EventOptionsObject
 */ /**
* The order the event handler should be called. This opens for having one
* handler be called before another, independent of in which order they were
* added.
* @name Highcharts.EventOptionsObject#order
* @type {number}
*/ /**
* Whether an event should be passive or not.
* When set to `true`, the function specified by listener will never call
* `preventDefault()`.
* @name Highcharts.EventOptionsObject#passive
* @type boolean
*/
/**
 * Formats data as a string. Usually the data is accessible through the `this`
 * keyword.
 *
 * @callback Highcharts.FormatterCallbackFunction<T>
 *
 * @param {T} this
 *        Context to format
 *
 * @param {T} [ctx]
 *        Since v12.6.0, the callback context is passed as an extra argument,
 *        so arrow functions can access the same context as regular functions
 *        using `this`.
 *
 * @return {string}
 *         Formatted text
 */
/**
 * An object of key-value pairs for HTML attributes.
 *
 * @typedef {Highcharts.Dictionary<boolean|number|string|Function>} Highcharts.HTMLAttributes
 */
/**
 * An HTML DOM element. The type is a reference to the regular HTMLElement in
 * the global scope.
 *
 * @typedef {global.HTMLElement} Highcharts.HTMLDOMElement
 *
 * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
 */
/**
 * The iterator callback.
 *
 * @callback Highcharts.ObjectEachCallbackFunction<T>
 *
 * @param {T} this
 *        The context.
 *
 * @param {*} value
 *        The property value.
 *
 * @param {string} key
 *        The property key.
 *
 * @param {*} obj
 *        The object that objectEach is being applied to.
 */
/**
 * An object containing `left` and `top` properties for the position in the
 * page.
 *
 * @interface Highcharts.OffsetObject
 */ /**
* Left distance to the page border.
* @name Highcharts.OffsetObject#left
* @type {number}
*/ /**
* Top distance to the page border.
* @name Highcharts.OffsetObject#top
* @type {number}
*/
/**
 * Describes a range.
 *
 * @interface Highcharts.RangeObject
 */ /**
* Maximum number of the range.
* @name Highcharts.RangeObject#max
* @type {number}
*/ /**
* Minimum number of the range.
* @name Highcharts.RangeObject#min
* @type {number}
*/
/**
 * If a number is given, it defines the pixel length. If a percentage string is
 * given, like for example `'50%'`, the setting defines a length relative to a
 * base size, for example the size of a container.
 *
 * @typedef {number|string} Highcharts.RelativeSize
 */
/**
 * Proceed function to call original (wrapped) function.
 *
 * @callback Highcharts.WrapProceedFunction
 *
 * @param {*} [arg1]
 *        Optional argument. Without any arguments defaults to first argument of
 *        the wrapping function.
 *
 * @param {*} [arg2]
 *        Optional argument. Without any arguments defaults to second argument
 *        of the wrapping function.
 *
 * @param {*} [arg3]
 *        Optional argument. Without any arguments defaults to third argument of
 *        the wrapping function.
 *
 * @return {*}
 *         Return value of the original function.
 */
/**
 * The Highcharts object is the placeholder for all other members, and various
 * utility functions. The most important member of the namespace would be the
 * chart constructor.
 *
 * @example
 * let chart = Highcharts.chart('container', { ... });
 *
 * @namespace Highcharts
 */
''; // Detach doclets above

;// ./code/dashboards/es-modules/Data/DataTableCore.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *  - Gøran Slettemark
 *  - Torstein Hønsi
 *
 * */


const { setLength: DataTableCore_setLength, splice: DataTableCore_splice } = Data_ColumnUtils;


/* *
 *
 *  Class
 *
 * */
/**
 * Class to manage columns and rows in a table structure. It provides methods
 * to add, remove, and manipulate columns and rows, as well as to retrieve data
 * from specific cells.
 *
 * @class
 * @name Highcharts.DataTable
 *
 * @param {Highcharts.DataTableOptions} [options]
 * Options to initialize the new DataTable instance.
 */
class DataTableCore {
    /**
     * Constructs an instance of the DataTable class.
     *
     * @example
     * const dataTable = new Highcharts.DataTableCore({
     *   columns: {
     *     year: [2020, 2021, 2022, 2023],
     *     cost: [11, 13, 12, 14],
     *     revenue: [12, 15, 14, 18]
     *   }
     * });

     *
     * @param {Highcharts.DataTableOptions} [options]
     * Options to initialize the new DataTable instance.
     */
    constructor(options = {}) {
        /**
         * Whether the ID was automatic generated or given in the constructor.
         *
         * @name Highcharts.DataTable#autoId
         * @type {boolean}
         */
        this.autoId = !options.id;
        this.columns = {};
        /**
         * ID of the table for identification purposes.
         *
         * @name Highcharts.DataTable#id
         * @type {string}
         */
        this.id = (options.id || uniqueKey());
        this.rowCount = 0;
        this.versionTag = uniqueKey();
        let rowCount = 0;
        objectEach(options.columns || {}, (column, columnId) => {
            this.columns[columnId] = column.slice();
            rowCount = Math.max(rowCount, column.length);
        });
        this.applyRowCount(rowCount);
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Applies a row count to the table by setting the `rowCount` property and
     * adjusting the length of all columns.
     *
     * @private
     * @param {number} rowCount The new row count.
     */
    applyRowCount(rowCount) {
        this.rowCount = rowCount;
        objectEach(this.columns, (column, columnId) => {
            if (column.length !== rowCount) {
                this.columns[columnId] = DataTableCore_setLength(column, rowCount);
            }
        });
    }
    /**
     * Delete rows. Simplified version of the full
     * `DataTable.deleteRows` method.
     *
     * @param {number} rowIndex
     * The start row index
     *
     * @param {number} [rowCount=1]
     * The number of rows to delete
     *
     * @return {void}
     *
     * @emits #afterDeleteRows
     */
    deleteRows(rowIndex, rowCount = 1) {
        if (rowCount > 0 && rowIndex < this.rowCount) {
            let length = 0;
            objectEach(this.columns, (column, columnId) => {
                this.columns[columnId] =
                    DataTableCore_splice(column, rowIndex, rowCount).array;
                length = column.length;
            });
            this.rowCount = length;
        }
        fireEvent(this, 'afterDeleteRows', { rowIndex, rowCount });
        this.versionTag = uniqueKey();
    }
    /**
     * Fetches the given column by the canonical column name. Simplified version
     * of the full `DataTable.getRow` method, always returning by reference.
     *
     * @param {string} columnId
     * Name of the column to get.
     *
     * @return {Highcharts.DataTableColumn|undefined}
     * A copy of the column, or `undefined` if not found.
     */
    getColumn(columnId, 
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    asReference) {
        return this.columns[columnId];
    }
    /**
     * Retrieves all or the given columns. Simplified version of the full
     * `DataTable.getColumns` method, always returning by reference.
     *
     * @param {Array<string>} [columnIds]
     * Column ids to retrieve.
     *
     * @return {Highcharts.DataTableColumnCollection}
     * Collection of columns. If a requested column was not found, it is
     * `undefined`.
     */
    getColumns(columnIds, 
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    asReference) {
        return (columnIds || Object.keys(this.columns)).reduce((columns, columnId) => {
            columns[columnId] = this.columns[columnId];
            return columns;
        }, {});
    }
    /**
     * Retrieves the row at a given index.
     *
     * @param {number} rowIndex
     * Row index to retrieve. First row has index 0.
     *
     * @param {Array<string>} [columnIds]
     * Column names to retrieve.
     *
     * @return {Record<string, number|string|undefined>|undefined}
     * Returns the row values, or `undefined` if not found.
     */
    getRow(rowIndex, columnIds) {
        return (columnIds || Object.keys(this.columns)).map((key) => this.columns[key]?.[rowIndex]);
    }
    /**
     * Sets cell values for a column. Will insert a new column, if not found.
     *
     * @param {string} columnId
     * Column name to set.
     *
     * @param {Highcharts.DataTableColumn} [column]
     * Values to set in the column.
     *
     * @param {number} [rowIndex]
     * Index of the first row to change. (Default: 0)
     *
     * @param {Record<string, (boolean|number|string|null|undefined)>} [eventDetail]
     * Custom information for pending events.
     *
     * @emits #setColumns
     * @emits #afterSetColumns
     */
    setColumn(columnId, column = [], rowIndex = 0, eventDetail) {
        this.setColumns({ [columnId]: column }, rowIndex, eventDetail);
    }
    /**
     * Sets cell values for multiple columns. Will insert new columns, if not
     * found. Simplified version of the full `DataTableCore.setColumns`, limited
     * to full replacement of the columns (undefined `rowIndex`).
     *
     * @param {Highcharts.DataTableColumnCollection} columns
     * Columns as a collection, where the keys are the column names.
     *
     * @param {number} [rowIndex]
     * Index of the first row to change. Ignored in the `DataTableCore`, as it
     * always replaces the full column.
     *
     * @param {Record<string, (boolean|number|string|null|undefined)>} [eventDetail]
     * Custom information for pending events.
     *
     * @emits #setColumns
     * @emits #afterSetColumns
     */
    setColumns(columns, rowIndex, eventDetail) {
        let rowCount = this.rowCount;
        objectEach(columns, (column, columnId) => {
            this.columns[columnId] = column.slice();
            rowCount = column.length;
        });
        this.applyRowCount(rowCount);
        if (!eventDetail?.silent) {
            fireEvent(this, 'afterSetColumns');
            this.versionTag = uniqueKey();
        }
    }
    /**
     * Sets cell values of a row. Will insert a new row if no index was
     * provided, or if the index is higher than the total number of table rows.
     * A simplified version of the full `DateTable.setRow`, limited to objects.
     *
     * @param {Record<string, number|string|undefined>} row
     * Cell values to set.
     *
     * @param {number} [rowIndex]
     * Index of the row to set. Leave `undefined` to add as a new row.
     *
     * @param {boolean} [insert]
     * Whether to insert the row at the given index, or to overwrite the row.
     *
     * @param {Record<string, (boolean|number|string|null|undefined)>} [eventDetail]
     * Custom information for pending events.
     *
     * @emits #afterSetRows
     */
    setRow(row, rowIndex = this.rowCount, insert, eventDetail) {
        const { columns } = this, indexRowCount = insert ? this.rowCount + 1 : rowIndex + 1, rowKeys = Object.keys(row);
        if (eventDetail?.addColumns !== false) {
            for (let i = 0, iEnd = rowKeys.length; i < iEnd; i++) {
                const key = rowKeys[i];
                if (!columns[key]) {
                    columns[key] = [];
                }
            }
        }
        objectEach(columns, (column, columnId) => {
            if (!column && eventDetail?.addColumns !== false) {
                column = new Array(indexRowCount);
            }
            if (column) {
                if (insert) {
                    column = DataTableCore_splice(column, rowIndex, 0, true, [row[columnId] ?? null]).array;
                }
                else {
                    column[rowIndex] = row[columnId] ?? null;
                }
                columns[columnId] = column;
            }
        });
        if (indexRowCount > this.rowCount) {
            this.applyRowCount(indexRowCount);
        }
        if (!eventDetail?.silent) {
            fireEvent(this, 'afterSetRows');
            this.versionTag = uniqueKey();
        }
    }
    /**
     * Returns the modified (clone) or the original data table if the modified
     * one does not exist.
     *
     * @return {Highcharts.DataTableCore}
     * The modified (clone) or the original data table.
     */
    getModified() {
        return this.modified || this;
    }
}
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Data_DataTableCore = (DataTableCore);
/* *
 *
 *  API Declarations
 *
 * */
/**
 * A typed array.
 * @typedef {Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} Highcharts.TypedArray
 * //**
 * A column of values in a data table.
 * @typedef {Array<boolean|null|number|string|undefined>|Highcharts.TypedArray} Highcharts.DataTableColumn
 */ /**
* A collection of data table columns defined by a object where the key is the
* column name and the value is an array of the column values.
* @typedef {Record<string, Highcharts.DataTableColumn>} Highcharts.DataTableColumnCollection
*/
/**
 * Options for the `DataTable` or `DataTableCore` classes.
 * @interface Highcharts.DataTableOptions
 */ /**
* The column options for the data table. The columns are defined by an object
* where the key is the column ID and the value is an array of the column
* values.
*
* @name Highcharts.DataTableOptions.columns
* @type {Highcharts.DataTableColumnCollection|undefined}
*/ /**
* Custom ID to identify the new DataTable instance.
*
* @name Highcharts.DataTableOptions.id
* @type {string|undefined}
*/
(''); // Keeps doclets above in JS file

;// ./code/dashboards/es-modules/Data/DataTable.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *  - Gøran Slettemark
 *  - Jomar Hønsi
 *  - Dawid Draguła
 *
 * */



const { splice: DataTable_splice, setLength: DataTable_setLength } = Data_ColumnUtils;


/* *
 *
 *  Class
 *
 * */
/**
 * Class to manage columns and rows in a table structure. It provides methods
 * to add, remove, and manipulate columns and rows, as well as to retrieve data
 * from specific cells.
 *
 * @class
 * @name Highcharts.DataTable
 *
 * @param {Highcharts.DataTableOptions} [options]
 * Options to initialize the new DataTable instance.
 */
class DataTable extends Data_DataTableCore {
    /* *
     *
     *  Constructor
     *
     * */
    constructor(options = {}) {
        super(options);
        this.metadata = options.metadata;
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Returns a clone of this table. The cloned table is completely independent
     * of the original, and any changes made to the clone will not affect
     * the original table.
     *
     * @function Highcharts.DataTable#clone
     *
     * @param {boolean} [skipColumns]
     * Whether to clone columns or not.
     *
     * @param {Highcharts.DataTableEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @return {Highcharts.DataTable}
     * Clone of this data table.
     *
     * @emits #cloneTable
     * @emits #afterCloneTable
     */
    clone(skipColumns, eventDetail) {
        const table = this, tableOptions = {};
        table.emit({ type: 'cloneTable', detail: eventDetail });
        if (!skipColumns) {
            tableOptions.columns = table.columns;
        }
        if (!table.autoId) {
            tableOptions.id = table.id;
        }
        const tableClone = new DataTable(tableOptions);
        if (!skipColumns) {
            tableClone.versionTag = table.versionTag;
            tableClone.originalRowIndexes = table.originalRowIndexes;
            tableClone.localRowIndexes = table.localRowIndexes;
        }
        tableClone.metadata = { ...table.metadata };
        table.emit({
            type: 'afterCloneTable',
            detail: eventDetail,
            tableClone
        });
        return tableClone;
    }
    /**
     * Deletes columns from the table.
     *
     * @function Highcharts.DataTable#deleteColumns
     *
     * @param {Array<string>} [columnIds]
     * Names of columns to delete. If no array is provided, all
     * columns will be deleted.
     *
     * @param {Highcharts.DataTableEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @return {Highcharts.DataTableColumnCollection|undefined}
     * Returns the deleted columns, if found.
     *
     * @emits #deleteColumns
     * @emits #afterDeleteColumns
     */
    deleteColumns(columnIds, eventDetail) {
        const table = this, columns = table.columns, deletedColumns = {}, modifiedColumns = {}, modifier = table.modifier, rowCount = table.rowCount;
        columnIds = (columnIds || Object.keys(columns));
        if (columnIds.length) {
            table.emit({
                type: 'deleteColumns',
                columnIds,
                detail: eventDetail
            });
            for (let i = 0, iEnd = columnIds.length, column, columnId; i < iEnd; ++i) {
                columnId = columnIds[i];
                column = columns[columnId];
                if (column) {
                    deletedColumns[columnId] = column;
                    modifiedColumns[columnId] = new Array(rowCount);
                }
                delete columns[columnId];
            }
            if (!Object.keys(columns).length) {
                table.rowCount = 0;
                this.deleteRowIndexReferences();
            }
            if (modifier) {
                modifier.modifyTable(table);
            }
            table.emit({
                type: 'afterDeleteColumns',
                columns: deletedColumns,
                columnIds,
                detail: eventDetail
            });
            return deletedColumns;
        }
    }
    /**
     * Deletes the row index references. This is useful when the original table
     * is deleted, and the references are no longer needed. This table is
     * then considered an original table or a table that has the same rows
     * order as the original table.
     */
    deleteRowIndexReferences() {
        delete this.originalRowIndexes;
        delete this.localRowIndexes;
    }
    /**
     * Deletes rows in this table.
     *
     * @function Highcharts.DataTable#deleteRows
     *
     * @param {number | number[]} [rowIndex]
     * Index of the row where deletion should start, or an array of indices for
     * deleting multiple rows. If not specified, all rows will be deleted.
     *
     * @param {number} [rowCount]
     * Number of rows to delete.
     *
     * @param {Highcharts.DataTableEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @return {Array<Highcharts.DataTableRow>}
     * Returns the deleted rows, if found.
     *
     * @emits #deleteRows
     * @emits #afterDeleteRows
     */
    deleteRows(rowIndex, rowCount = 1, eventDetail) {
        const { columns, modifier } = this;
        const deletedRows = [];
        let indices;
        let actualRowCount;
        if (!defined(rowIndex)) {
            // No index provided - delete all rows.
            indices = [0];
            actualRowCount = this.rowCount;
        }
        else if (Array.isArray(rowIndex)) {
            // Array of indices provided - delete the specified rows.
            indices = rowIndex
                // Remove negative indices, and indices beyond the row count,
                // and remove duplicates.
                .filter((index, i, arr) => (index >= 0 &&
                index < this.rowCount &&
                arr.indexOf(index) === i))
                // Sort indices in descending order.
                .sort((a, b) => b - a);
            actualRowCount = indices.length;
        }
        else {
            // Single index provided - delete the specified range of rows.
            indices = [rowIndex];
            actualRowCount = rowCount;
        }
        this.emit({
            type: 'deleteRows',
            detail: eventDetail,
            rowCount: actualRowCount,
            rowIndex: rowIndex ?? 0
        });
        if (actualRowCount > 0) {
            const columnIds = Object.keys(columns);
            for (let i = 0; i < columnIds.length; ++i) {
                const columnId = columnIds[i];
                const column = columns[columnId];
                let deletedCells;
                // Perform a range splice.
                if (indices.length === 1 && actualRowCount > 1) {
                    const result = DataTable_splice(column, indices[0], actualRowCount);
                    deletedCells = result.removed;
                    columns[columnId] = result.array;
                }
                else {
                    // Perform a index splice for each index in the array.
                    deletedCells = [];
                    for (const index of indices) {
                        deletedCells.push(column[index]);
                        DataTable_splice(column, index, 1);
                    }
                    // Reverse the deleted cells to maintain the correct order.
                    deletedCells.reverse();
                }
                if (!i) {
                    this.rowCount = column.length;
                }
                for (let j = 0, jEnd = deletedCells.length; j < jEnd; ++j) {
                    deletedRows[j] = deletedRows[j] || [];
                    deletedRows[j][i] = deletedCells[j];
                }
            }
        }
        if (modifier) {
            modifier.modifyTable(this);
        }
        this.emit({
            type: 'afterDeleteRows',
            detail: eventDetail,
            rowCount: actualRowCount,
            rowIndex: rowIndex ?? 0,
            rows: deletedRows
        });
        return deletedRows;
    }
    /**
     * Emits an event on this table to all registered callbacks of the given
     * event.
     * @private
     *
     * @param {Event} e
     * Event object with event information.
     */
    emit(e) {
        if ([
            'afterDeleteColumns',
            'afterDeleteRows',
            'afterSetCell',
            'afterSetColumns',
            'afterSetRows'
        ].includes(e.type)) {
            this.versionTag = uniqueKey();
        }
        fireEvent(this, e.type, e);
    }
    /**
     * Fetches a single cell value.
     *
     * @function Highcharts.DataTable#getCell
     *
     * @param {string} columnId
     * Column name of the cell to retrieve.
     *
     * @param {number} rowIndex
     * Row index of the cell to retrieve.
     *
     * @return {Highcharts.DataTableCellType|undefined}
     * Returns the cell value or `undefined`.
     */
    getCell(columnId, rowIndex) {
        const table = this;
        const column = table.columns[columnId];
        if (column) {
            return column[rowIndex];
        }
    }
    /**
     * Fetches the given column by the canonical column name.
     * This function is a simplified wrap of {@link getColumns}.
     *
     * @function Highcharts.DataTable#getColumn
     *
     * @param {string} columnId
     * Name of the column to get.
     *
     * @param {boolean} [asReference]
     * Whether to return the column as a readonly reference.
     *
     * @return {Highcharts.DataTableColumn|undefined}
     * A copy of the column, or `undefined` if not found.
     */
    getColumn(columnId, asReference) {
        return this.getColumns([columnId], asReference)[columnId];
    }
    /**
     * Fetches all column IDs.
     *
     * @function Highcharts.DataTable#getColumnIds
     *
     * @return {Array<string>}
     * Returns all column IDs.
     */
    getColumnIds() {
        return Object.keys(this.columns);
    }
    /**
     * Retrieves all or the given columns.
     *
     * @function Highcharts.DataTable#getColumns
     *
     * @param {Array<string>} [columnIds]
     * Column names to retrieve.
     *
     * @param {boolean} [asReference]
     * Whether to return columns as a readonly reference.
     *
     * @param {boolean} [asBasicColumns]
     * Whether to transform all typed array columns to normal arrays.
     *
     * @return {Highcharts.DataTableColumnCollection}
     * Collection of columns. If a requested column was not found, it is
     * `undefined`.
     */
    getColumns(columnIds, asReference, asBasicColumns) {
        const table = this, tableColumns = table.columns, columns = {};
        columnIds = (columnIds || Object.keys(tableColumns));
        for (let i = 0, iEnd = columnIds.length, column, columnId; i < iEnd; ++i) {
            columnId = columnIds[i];
            column = tableColumns[columnId];
            if (column) {
                if (asReference) {
                    columns[columnId] = column;
                }
                else if (asBasicColumns && !Array.isArray(column)) {
                    columns[columnId] = Array.from(column);
                }
                else {
                    columns[columnId] = column.slice();
                }
            }
        }
        return columns;
    }
    /**
     * Takes the original row index and returns the local row index in the
     * modified table for which this function is called.
     *
     * @param {number} originalRowIndex
     * Original row index to get the local row index for.
     *
     * @return {number|undefined}
     * Returns the local row index or `undefined` if not found.
     */
    getLocalRowIndex(originalRowIndex) {
        const { localRowIndexes } = this;
        if (localRowIndexes) {
            return localRowIndexes[originalRowIndex];
        }
        return originalRowIndex;
    }
    /**
     * Returns the modifier associated with this table, if any.
     *
     * @return {Highcharts.DataModifier|undefined}
     * Returns the modifier or `undefined`.
     *
     * @private
     */
    getModifier() {
        return this.modifier;
    }
    /**
     * Takes the local row index and returns the index of the corresponding row
     * in the original table.
     *
     * @param {number} rowIndex
     * Local row index to get the original row index for.
     *
     * @return {number|undefined}
     * Returns the original row index or `undefined` if not found.
     */
    getOriginalRowIndex(rowIndex) {
        const { originalRowIndexes } = this;
        if (originalRowIndexes) {
            return originalRowIndexes[rowIndex];
        }
        return rowIndex;
    }
    /**
     * Retrieves the row at a given index. This function is a simplified wrap of
     * {@link getRows}.
     *
     * @function Highcharts.DataTable#getRow
     *
     * @param {number} rowIndex
     * Row index to retrieve. First row has index 0.
     *
     * @param {Array<string>} [columnIds]
     * Column names in order to retrieve.
     *
     * @return {Highcharts.DataTableRow}
     * Returns the row values, or `undefined` if not found.
     */
    getRow(rowIndex, columnIds) {
        return this.getRows(rowIndex, 1, columnIds)[0];
    }
    /**
     * Returns the number of rows in this table.
     *
     * @function Highcharts.DataTable#getRowCount
     *
     * @return {number}
     * Number of rows in this table.
     */
    getRowCount() {
        // @todo Implement via property getter `.length` browsers supported
        return this.rowCount;
    }
    /**
     * Retrieves the index of the first row matching a specific cell value.
     *
     * @function Highcharts.DataTable#getRowIndexBy
     *
     * @param {string} columnId
     * Column to search in.
     *
     * @param {Highcharts.DataTableCellType} cellValue
     * Cell value to search for. `NaN` and `undefined` are not supported.
     *
     * @param {number} [rowIndexOffset]
     * Index offset to start searching.
     *
     * @return {number|undefined}
     * Index of the first row matching the cell value.
     */
    getRowIndexBy(columnId, cellValue, rowIndexOffset) {
        const table = this;
        const column = table.columns[columnId];
        if (column) {
            let rowIndex = -1;
            if (Array.isArray(column)) {
                // Normal array
                rowIndex = column.indexOf(cellValue, rowIndexOffset);
            }
            else if (Utilities_isNumber(cellValue)) {
                // Typed array
                rowIndex = column.indexOf(cellValue, rowIndexOffset);
            }
            if (rowIndex !== -1) {
                return rowIndex;
            }
        }
    }
    /**
     * Retrieves the row at a given index. This function is a simplified wrap of
     * {@link getRowObjects}.
     *
     * @function Highcharts.DataTable#getRowObject
     *
     * @param {number} rowIndex
     * Row index.
     *
     * @param {Array<string>} [columnIds]
     * Column names and their order to retrieve.
     *
     * @return {Highcharts.DataTableRowObject}
     * Returns the row values, or `undefined` if not found.
     */
    getRowObject(rowIndex, columnIds) {
        return this.getRowObjects(rowIndex, 1, columnIds)[0];
    }
    /**
     * Fetches all or a number of rows as an object.
     *
     * @function Highcharts.DataTable#getRowObjects
     *
     * @param {number} [rowIndex]
     * Index of the first row to fetch. Defaults to first row at index `0`.
     *
     * @param {number} [rowCount]
     * Number of rows to fetch. Defaults to maximal number of rows.
     *
     * @param {Array<string>} [columnIds]
     * Column names and their order to retrieve.
     *
     * @return {Highcharts.DataTableRowObject}
     * Returns retrieved rows.
     */
    getRowObjects(rowIndex = 0, rowCount = (this.rowCount - rowIndex), columnIds) {
        const table = this, columns = table.columns, rows = new Array(rowCount);
        columnIds = (columnIds || Object.keys(columns));
        for (let i = rowIndex, i2 = 0, iEnd = Math.min(table.rowCount, (rowIndex + rowCount)), column, row; i < iEnd; ++i, ++i2) {
            row = rows[i2] = {};
            for (const columnId of columnIds) {
                column = columns[columnId];
                row[columnId] = (column ? column[i] : void 0);
            }
        }
        return rows;
    }
    /**
     * Fetches all or a number of rows as an array.
     *
     * @function Highcharts.DataTable#getRows
     *
     * @param {number} [rowIndex]
     * Index of the first row to fetch. Defaults to first row at index `0`.
     *
     * @param {number} [rowCount]
     * Number of rows to fetch. Defaults to maximal number of rows.
     *
     * @param {Array<string>} [columnIds]
     * Column names and their order to retrieve.
     *
     * @return {Highcharts.DataTableRow}
     * Returns retrieved rows.
     */
    getRows(rowIndex = 0, rowCount = (this.rowCount - rowIndex), columnIds) {
        const table = this, columns = table.columns, rows = new Array(rowCount);
        columnIds = (columnIds || Object.keys(columns));
        for (let i = rowIndex, i2 = 0, iEnd = Math.min(table.rowCount, (rowIndex + rowCount)), column, row; i < iEnd; ++i, ++i2) {
            row = rows[i2] = [];
            for (const columnId of columnIds) {
                column = columns[columnId];
                row.push(column ? column[i] : void 0);
            }
        }
        return rows;
    }
    /**
     * Returns the unique version tag of the current state of the table.
     *
     * @function Highcharts.DataTable#getVersionTag
     *
     * @return {string}
     * Unique version tag.
     */
    getVersionTag() {
        return this.versionTag;
    }
    /**
     * Determines whether all specified column names exist in the table.
     *
     * @function Highcharts.DataTable#hasColumns
     *
     * @param {Array<string>} columnIds
     * Column names to check.
     *
     * @return {boolean}
     * Returns `true` if all columns have been found, otherwise `false`.
     */
    hasColumns(columnIds) {
        const table = this, columns = table.columns;
        for (let i = 0, iEnd = columnIds.length, columnId; i < iEnd; ++i) {
            columnId = columnIds[i];
            if (!columns[columnId]) {
                return false;
            }
        }
        return true;
    }
    /**
     * Checks if any row in the specified column contains the given cell value.
     *
     * @function Highcharts.DataTable#hasRowWith
     *
     * @param {string} columnId
     * Column to search in.
     *
     * @param {Highcharts.DataTableCellType} cellValue
     * Cell value to search for. `NaN` and `undefined` are not supported.
     *
     * @return {boolean}
     * True, if a row has been found, otherwise false.
     */
    hasRowWith(columnId, cellValue) {
        const table = this;
        const column = table.columns[columnId];
        // Normal array
        if (Array.isArray(column)) {
            return (column.indexOf(cellValue) !== -1);
        }
        // Typed array
        if (defined(cellValue) && Number.isFinite(cellValue)) {
            return (column.indexOf(+cellValue) !== -1);
        }
        return false;
    }
    /**
     * Registers a callback function to be executed when a specific event is
     * emitted. To stop listening to the event, call the function returned by
     * this method.
     *
     * @function Highcharts.DataTable#on
     *
     * @param {string} type
     * Event type as a string.
     *
     * @param {Highcharts.EventCallbackFunction<Highcharts.DataTable>} callback
     * Function to register for an event callback.
     *
     * @return {Function}
     * Function to unregister callback from the event.
     */
    on(type, callback) {
        return addEvent(this, type, callback);
    }
    /**
     * Changes the ID of an existing column to a new ID, effectively renaming
     * the column.
     *
     * @function Highcharts.DataTable#changeColumnId
     *
     * @param {string} columnId
     * Id of the column to be changed.
     *
     * @param {string} newColumnId
     * New id of the column.
     *
     * @return {boolean}
     * Returns `true` if successful, `false` if the column was not found.
     */
    changeColumnId(columnId, newColumnId) {
        const table = this, columns = table.columns;
        if (columns[columnId]) {
            if (columnId !== newColumnId) {
                columns[newColumnId] = columns[columnId];
                delete columns[columnId];
            }
            return true;
        }
        return false;
    }
    /**
     * Sets the value of a specific cell identified by column ID and row index.
     * If the column does not exist, it will be created. If the row index is
     * beyond the current row count, the table will be expanded to accommodate
     * the new cell.
     *
     * @function Highcharts.DataTable#setCell
     *
     * @param {string} columnId
     * Column name to set.
     *
     * @param {number|undefined} rowIndex
     * Row index to set.
     *
     * @param {Highcharts.DataTableCellType} cellValue
     * Cell value to set.
     *
     * @param {Highcharts.DataTableEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @emits #setCell
     * @emits #afterSetCell
     */
    setCell(columnId, rowIndex, cellValue, eventDetail) {
        const table = this, columns = table.columns, modifier = table.modifier;
        let column = columns[columnId];
        if (column && column[rowIndex] === cellValue) {
            return;
        }
        table.emit({
            type: 'setCell',
            cellValue,
            columnId: columnId,
            detail: eventDetail,
            rowIndex
        });
        if (!column) {
            column = columns[columnId] = new Array(table.rowCount);
        }
        if (rowIndex >= table.rowCount) {
            table.rowCount = (rowIndex + 1);
        }
        column[rowIndex] = cellValue;
        if (modifier) {
            modifier.modifyTable(table);
        }
        table.emit({
            type: 'afterSetCell',
            cellValue,
            columnId: columnId,
            detail: eventDetail,
            rowIndex
        });
    }
    /**
     * Replaces or updates multiple columns in the table with new data. If a
     * column does not exist, it will be created and added to the table.
     *
     * @function Highcharts.DataTable#setColumns
     *
     * @param {Highcharts.DataTableColumnCollection} columns
     * Columns as a collection, where the keys are the column names.
     *
     * @param {number} [rowIndex]
     * Index of the first row to change. Keep undefined to reset.
     *
     * @param {Highcharts.DataTableEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @param {boolean} [typeAsOriginal=false]
     * Determines whether the original column retains its type when data
     * replaced. If `true`, the original column keeps its type. If not
     * (default), the original column will adopt the type of the replacement
     * column.
     *
     * @emits #setColumns
     * @emits #afterSetColumns
     */
    setColumns(columns, rowIndex, eventDetail, typeAsOriginal) {
        const table = this, tableColumns = table.columns, tableModifier = table.modifier, columnIds = Object.keys(columns);
        let rowCount = table.rowCount;
        table.emit({
            type: 'setColumns',
            columns,
            columnIds,
            detail: eventDetail,
            rowIndex
        });
        if (!defined(rowIndex) && !typeAsOriginal) {
            super.setColumns(columns, rowIndex, extend(eventDetail, { silent: true }));
        }
        else {
            for (let i = 0, iEnd = columnIds.length, column, tableColumn, columnId, ArrayConstructor; i < iEnd; ++i) {
                columnId = columnIds[i];
                column = columns[columnId];
                tableColumn = tableColumns[columnId];
                ArrayConstructor = Object.getPrototypeOf((tableColumn && typeAsOriginal) ? tableColumn : column).constructor;
                if (!tableColumn) {
                    tableColumn = new ArrayConstructor(rowCount);
                }
                else if (ArrayConstructor === Array) {
                    if (!Array.isArray(tableColumn)) {
                        tableColumn = Array.from(tableColumn);
                    }
                }
                else if (tableColumn.length < rowCount) {
                    tableColumn =
                        new ArrayConstructor(rowCount);
                    tableColumn.set(tableColumns[columnId]);
                }
                tableColumns[columnId] = tableColumn;
                for (let i = (rowIndex || 0), iEnd = column.length; i < iEnd; ++i) {
                    tableColumn[i] = column[i];
                }
                rowCount = Math.max(rowCount, column.length);
            }
            this.applyRowCount(rowCount);
        }
        if (tableModifier) {
            tableModifier.modifyTable(table);
        }
        table.emit({
            type: 'afterSetColumns',
            columns,
            columnIds,
            detail: eventDetail,
            rowIndex
        });
    }
    /**
     * Assigns a new data modifier to the table.
     *
     * This method does not modify the table directly. Instead, it sets the
     * `.modified` property of the table with a modified copy of this table,
     * as produced by the modifier.
     *
     * @param {Highcharts.DataModifier} [modifier]
     * Modifier to set, or `undefined` to unset.
     *
     * @param {Highcharts.DataTableEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @return {Promise<Highcharts.DataTable>}
     * Resolves to this table if successful, or rejects on failure.
     *
     * @emits #setModifier
     * @emits #afterSetModifier
     */
    setModifier(modifier, eventDetail) {
        const table = this;
        let promise;
        table.emit({
            type: 'setModifier',
            detail: eventDetail,
            modifier,
            modified: table.getModified()
        });
        table.modifier = modifier;
        if (modifier) {
            promise = modifier.modify(table);
        }
        else {
            promise = Promise.resolve(table);
        }
        return promise
            .then((table) => {
            table.emit({
                type: 'afterSetModifier',
                detail: eventDetail,
                modifier,
                modified: table.getModified()
            });
            return table;
        })['catch']((error) => {
            table.emit({
                type: 'setModifierError',
                error,
                modifier,
                modified: table.getModified()
            });
            throw error;
        });
    }
    /**
     * Sets the original row indexes for the table. It is used to keep the
     * reference to the original rows when modifying the table.
     *
     * @param {Array<number|undefined>} originalRowIndexes
     * Original row indexes array.
     *
     * @param {boolean} omitLocalRowIndexes
     * Whether to omit the local row indexes calculation. Defaults to `false`.
     */
    setOriginalRowIndexes(originalRowIndexes, omitLocalRowIndexes = false) {
        this.originalRowIndexes = originalRowIndexes;
        if (omitLocalRowIndexes) {
            return;
        }
        const modifiedIndexes = this.localRowIndexes = [];
        for (let i = 0, iEnd = originalRowIndexes.length, originalIndex; i < iEnd; ++i) {
            originalIndex = originalRowIndexes[i];
            if (defined(originalIndex)) {
                modifiedIndexes[originalIndex] = i;
            }
        }
    }
    /**
     * Sets cell values of a row. Will insert a new row, if no index was
     * provided, or if the index is higher than the total number of table rows.
     *
     * Note: This function is just a simplified wrap of
     * {@link Highcharts.DataTable#setRows}.
     *
     * @function Highcharts.DataTable#setRow
     *
     * @param {Highcharts.DataTableRow|Highcharts.DataTableRowObject} row
     * Cell values to set.
     *
     * @param {number} [rowIndex]
     * Index of the row to set. Leave `undefined` to add as a new row.
     *
     * @param {boolean} [insert]
     * Whether to insert the row at the given index, or to overwrite the row.
     *
     * @param {Highcharts.DataTableEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @emits #setRows
     * @emits #afterSetRows
     */
    setRow(row, rowIndex, insert, eventDetail) {
        this.setRows([row], rowIndex, insert, eventDetail);
    }
    /**
     * Sets cell values for multiple rows. Will insert new rows, if no index was
     * was provided, or if the index is higher than the total number of table
     * rows.
     *
     * @function Highcharts.DataTable#setRows
     *
     * @param {Array<(Highcharts.DataTableRow|Highcharts.DataTableRowObject)>} rows
     * Row values to set.
     *
     * @param {number} [rowIndex]
     * Index of the first row to set. Leave `undefined` to add as new rows.
     *
     * @param {boolean} [insert]
     * Whether to insert the row at the given index, or to overwrite the row.
     *
     * @param {Highcharts.DataTableEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @emits #setRows
     * @emits #afterSetRows
     */
    setRows(rows, rowIndex = this.rowCount, insert, eventDetail) {
        const table = this, columns = table.columns, columnIds = Object.keys(columns), modifier = table.modifier, rowCount = rows.length;
        table.emit({
            type: 'setRows',
            detail: eventDetail,
            rowCount,
            rowIndex,
            rows
        });
        for (let i = 0, i2 = rowIndex, row; i < rowCount; ++i, ++i2) {
            row = rows[i];
            if (Object.keys(row).length === 0) { // Is empty Object
                for (let j = 0, jEnd = columnIds.length; j < jEnd; ++j) {
                    const column = columns[columnIds[j]];
                    if (insert) {
                        columns[columnIds[j]] = DataTable_splice(column, i2, 0, true, [null]).array;
                    }
                    else {
                        column[i2] = null;
                    }
                }
            }
            else if (Array.isArray(row)) {
                for (let j = 0, jEnd = columnIds.length; j < jEnd; ++j) {
                    columns[columnIds[j]][i2] = row[j];
                }
            }
            else {
                super.setRow(row, i2, insert, { silent: true });
            }
        }
        const indexRowCount = insert ?
            rowCount + rows.length :
            rowIndex + rowCount;
        if (indexRowCount > table.rowCount) {
            table.rowCount = indexRowCount;
            for (let i = 0, iEnd = columnIds.length; i < iEnd; ++i) {
                const columnId = columnIds[i];
                columns[columnId] = DataTable_setLength(columns[columnId], indexRowCount);
            }
        }
        if (modifier) {
            modifier.modifyTable(table);
        }
        table.emit({
            type: 'afterSetRows',
            detail: eventDetail,
            rowCount,
            rowIndex,
            rows
        });
    }
}
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Data_DataTable = (DataTable);

;// ./code/dashboards/es-modules/Data/Connectors/DataConnector.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Dawid Draguła
 *  - Kamil Kubik
 *
 * */




/* *
 *
 *  Class
 *
 * */
/**
 * Abstract class providing an interface for managing a DataConnector.
 */
class DataConnector {
    /**
     * Adds a connector class to the registry. The connector has to provide the
     * `DataConnector.options` property and the `DataConnector.load` method to
     * modify the table.
     *
     * @private
     *
     * @param {string} key
     * Registry key of the connector class.
     *
     * @param {DataConnectorType} DataConnectorClass
     * Connector class (aka class constructor) to register.
     *
     * @return {boolean}
     * Returns true, if the registration was successful. False is returned, if
     * their is already a connector registered with this key.
     */
    static registerType(key, DataConnectorClass) {
        return (!!key &&
            !DataConnector.types[key] &&
            !!(DataConnector.types[key] = DataConnectorClass));
    }
    /**
     * Whether the connector is currently polling for new data.
     */
    get polling() {
        return !!this._polling;
    }
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Constructor for the connector class.
     *
     * @param {DataConnectorOptions} [options]
     * Options to use in the connector.
     */
    constructor(options) {
        /**
         * Tables managed by this DataConnector instance.
         */
        this.dataTables = {};
        /**
         * Helper flag for detecting whether the data connector is loaded.
         * @internal
         */
        this.loaded = false;
        this.metadata = options.metadata || { columns: {} };
        this.options = options;
        // Create a data table for each defined in the dataTables user options.
        const dataTables = options?.dataTables;
        let dataTableIndex = 0;
        if (options.options) {
            // eslint-disable-next-line no-console
            console.error('The `DataConnectorOptions.options` property was removed in Dashboards v4.0.0. Check how to upgrade your connector to use the new options structure here: https://api.highcharts.com/dashboards/#interfaces/Data_DataTableOptions.DataTableOptions');
        }
        if (dataTables && dataTables?.length > 0) {
            for (let i = 0, iEnd = dataTables.length; i < iEnd; ++i) {
                const dataTable = dataTables[i];
                const key = dataTable?.key;
                this.dataTables[key ?? dataTableIndex] =
                    new Data_DataTable(dataTable);
                if (!key) {
                    dataTableIndex++;
                }
            }
        }
        else {
            // If user options dataTables is not defined, generate a default
            // table.
            this.dataTables[0] = new Data_DataTable({
                id: options.id // Required by DataTableCore
            });
        }
    }
    /* *
     *
     *  Methods
     *
     * */
    /**
     * Returns a single data table instance based on the provided key.
     * Otherwise, returns the first data table.
     *
     * @param {string} [key]
     * The data table key.
     *
     * @return {DataTable}
     * The data table instance.
     */
    getTable(key) {
        if (key) {
            return this.dataTables[key];
        }
        return Object.values(this.dataTables)[0];
    }
    /**
     * Method for adding metadata for a single column.
     *
     * @param {string} name
     * The name of the column to be described.
     *
     * @param {MetaColumn} columnMeta
     * The metadata to apply to the column.
     */
    describeColumn(name, columnMeta) {
        const connector = this;
        const columns = connector.metadata.columns;
        columns[name] = merge(columns[name] || {}, columnMeta);
    }
    /**
     * Method for applying columns meta information to the whole DataConnector.
     *
     * @param {Record<string, MetaColumn>} columns
     * Pairs of column names and MetaColumn objects.
     */
    describeColumns(columns) {
        const connector = this;
        const columnIds = Object.keys(columns);
        let columnId;
        while (typeof (columnId = columnIds.pop()) === 'string') {
            connector.describeColumn(columnId, columns[columnId]);
        }
    }
    /**
     * Returns the order of columns.
     *
     * @return {string[] | undefined}
     * Order of columns.
     */
    getColumnOrder() {
        const connector = this, columns = connector.metadata.columns, names = Object.keys(columns || {});
        if (names.length) {
            return names.sort((a, b) => (Utilities_pick(columns[a].index, 0) - Utilities_pick(columns[b].index, 0)));
        }
    }
    /**
     * Retrieves the columns of the dataTable,
     * applies column order from meta.
     *
     * @return {Highcharts.DataTableColumnCollection}
     * An object with the properties `columnIds` and `columnValues`
     */
    getSortedColumns() {
        return this.getTable().getColumns(this.getColumnOrder());
    }
    /**
     * Sets the index and order of columns.
     *
     * @param {Array<string>} columnIds
     * Order of columns.
     */
    setColumnOrder(columnIds) {
        const connector = this;
        for (let i = 0, iEnd = columnIds.length; i < iEnd; ++i) {
            connector.describeColumn(columnIds[i], { index: i });
        }
    }
    /**
     * Updates the connector with new options.
     *
     * @param {object} newOptions
     * The new options to be applied to the connector.
     *
     * @param {boolean} [reload=true]
     * Whether to reload the connector after applying the new options.
     */
    async update(newOptions, reload = true) {
        this.emit({ type: 'beforeUpdate' });
        merge(true, this.options, newOptions);
        const { options } = this;
        if ('enablePolling' in newOptions || 'dataRefreshRate' in newOptions) {
            if ('enablePolling' in options && options.enablePolling) {
                this.stopPolling();
                this.startPolling(('dataRefreshRate' in options &&
                    typeof options.dataRefreshRate === 'number') ? Math.max(options.dataRefreshRate, 1) * 1000 : 1000);
            }
            else {
                this.stopPolling();
            }
        }
        if (reload) {
            await this.load();
        }
        this.emit({ type: 'afterUpdate' });
    }
    /**
     * The default load method, which fires the `afterLoad` event
     *
     * @return {Promise<DataConnector>}
     * The loaded connector.
     *
     * @emits DataConnector#afterLoad
     */
    load() {
        this.emit({ type: 'afterLoad' });
        return Promise.resolve(this);
    }
    /**
     * Applies the data modifiers to the data tables according to the
     * connector data tables options.
     */
    async applyTableModifiers() {
        const tableOptionsArray = this.options?.dataTables;
        for (const [key, table] of Object.entries(this.dataTables)) {
            // Take data modifier options from the corresponding data table
            // options, otherwise take the data modifier options from the
            // connector options.
            const dataModifierOptions = tableOptionsArray?.find((dataTable) => dataTable.key === key)?.dataModifier ?? this.options?.dataModifier;
            if (!dataModifierOptions) {
                continue;
            }
            const ModifierClass = (dataModifierOptions &&
                Modifiers_DataModifier.types[dataModifierOptions.type]);
            await table.setModifier(ModifierClass ?
                new ModifierClass(dataModifierOptions) :
                void 0);
        }
        return this;
    }
    /**
     * Starts polling new data after the specific time span in milliseconds.
     *
     * @param {number} refreshTime
     * Refresh time in milliseconds between polls.
     */
    startPolling(refreshTime = 1000) {
        const connector = this;
        // Assign a new abort controller.
        this.pollingController = new AbortController();
        // Clear the polling timeout.
        window.clearTimeout(connector._polling);
        connector._polling = window.setTimeout(
        // eslint-disable-next-line @typescript-eslint/no-misused-promises
        () => connector
            .load()['catch']((error) => connector.emit({
            type: 'loadError',
            error
        }))
            .then(() => {
            if (connector._polling) {
                connector.startPolling(refreshTime);
            }
        }), refreshTime);
    }
    /**
     * Stops polling data. Shouldn't be performed if polling is already stopped.
     */
    stopPolling() {
        const connector = this;
        if (!connector.polling) {
            return;
        }
        // Abort the existing request.
        connector?.pollingController?.abort();
        // Clear the polling timeout.
        window.clearTimeout(connector._polling);
        delete connector._polling;
    }
    /**
     * Emits an event on the connector to all registered callbacks of this
     * event.
     *
     * @param {Event} e
     * Event object containing additional event information.
     */
    emit(e) {
        fireEvent(this, e.type, e);
    }
    /**
     * Registers a callback for a specific connector event.
     *
     * @param {string} type
     * Event type.
     *
     * @param {Function} callback
     * Function to register for the connector callback.
     *
     * @return {Function}
     * Function to unregister callback from the connector event.
     */
    on(type, callback) {
        return addEvent(this, type, callback);
    }
    /**
     * Iterates over the dataTables and initiates the corresponding converters.
     * Updates the dataTables and assigns the first converter.
     *
     * @param {T}[data]
     * Data specific to the corresponding converter.
     *
     * @param {CreateConverterFunction}[createConverter]
     * Creates a specific converter combining the dataTable options.
     *
     * @param {ParseDataFunction<T>}[parseData]
     * Runs the converter parse method with the specific data type.
     */
    initConverters(data, createConverter, parseData) {
        let index = 0;
        for (const [key, table] of Object.entries(this.dataTables)) {
            // Create a proper converter and parse its data.
            const converter = createConverter(key);
            const columns = parseData(converter, data);
            // Update the dataTable.
            table.deleteColumns();
            table.setColumns(columns);
            // Assign the first converter.
            if (index === 0) {
                this.converter = converter;
            }
            index++;
        }
    }
}
/* *
 *
 *  Static Properties
 *
 * */
/**
 * Registry as a record object with connector names and their class.
 */
DataConnector.types = {};
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Connectors_DataConnector = (DataConnector);

;// ./code/dashboards/es-modules/Data/Connectors/CSVConnector.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Torstein Hønsi
 *  - Christer Vasseng
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *  - Kamil Kubik
 *
 * */




/* *
 *
 *  Class
 *
 * */
/**
 * Class that handles creating a DataConnector from CSV
 *
 * @private
 */
class CSVConnector extends Connectors_DataConnector {
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Constructs an instance of CSVConnector.
     *
     * @param {Partial<CSVConnectorOptions>} [options]
     * Options for the connector and converter.
     */
    constructor(options) {
        const mergedOptions = merge(CSVConnector.defaultOptions, options);
        super(mergedOptions);
        this.options = mergedOptions;
        if (mergedOptions.enablePolling) {
            this.startPolling(Math.max(mergedOptions.dataRefreshRate || 0, 1) * 1000);
        }
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Overrides the DataConnector method. Emits an event on the connector to
     * all registered callbacks of this event.
     *
     * @param {Event} e
     * Event object containing additional event information.
     */
    emit(e) {
        fireEvent(this, e.type, e);
    }
    /**
     * Initiates the loading of the CSV source to the connector
     *
     * @param {DataEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @emits CSVConnector#load
     * @emits CSVConnector#afterLoad
     */
    load(eventDetail) {
        const connector = this;
        const options = connector.options;
        const { csv, csvURL, dataTables, decimalPoint } = options;
        connector.emit({
            type: 'load',
            csv
        });
        return Promise
            .resolve(csvURL ?
            fetch(csvURL, {
                signal: connector?.pollingController?.signal
            }).then((response) => response.text()) :
            csv || '')
            .then((csv) => {
            if (csv) {
                this.initConverters(csv, (key) => {
                    const tableOptions = dataTables?.find((dataTable) => dataTable.key === key);
                    // The data table options takes precedence over the
                    // connector options.
                    const { firstRowAsNames = options.firstRowAsNames, beforeParse = options.beforeParse } = tableOptions || {};
                    const converterOptions = {
                        decimalPoint,
                        firstRowAsNames,
                        beforeParse
                    };
                    return new Converters_CSVConverter(merge(options, converterOptions));
                }, (converter, data) => converter.parse({ csv: data }));
            }
            return connector.applyTableModifiers().then(() => csv);
        })
            .then((csv) => {
            connector.emit({
                type: 'afterLoad',
                detail: eventDetail,
                csv
            });
            return connector;
        })['catch']((error) => {
            connector.emit({
                type: 'loadError',
                detail: eventDetail,
                error
            });
            throw error;
        });
    }
}
/* *
 *
 *  Static Properties
 *
 * */
CSVConnector.defaultOptions = {
    id: 'csv-connector',
    type: 'CSV',
    csv: '',
    csvURL: '',
    enablePolling: false,
    dataRefreshRate: 1,
    firstRowAsNames: true
};
/* *
 *
 *  Registry
 *
 * */
Connectors_DataConnector.registerType('CSV', CSVConnector);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Connectors_CSVConnector = (CSVConnector);

;// ./code/dashboards/es-modules/Data/Converters/GoogleSheetsConverter.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Torstein Hønsi
 *  - Gøran Slettemark
 *  - Wojciech Chmiel
 *  - Sophie Bremer
 *  - Kamil Kubik
 *
 * */





/* *
 *
 *  Class
 *
 * */
/**
 * Handles parsing and transformation of an Google Sheets to a table.
 *
 * @private
 */
class GoogleSheetsConverter extends Converters_DataConverter {
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Constructs an instance of the GoogleSheetsConverter.
     *
     * @param {Partial<GoogleSheetsConverterOptions>} [options]
     * Options for the GoogleSheetsConverter.
     */
    constructor(options) {
        const mergedOptions = merge(GoogleSheetsConverter.defaultOptions, options);
        super(mergedOptions);
        this.header = [];
        this.options = mergedOptions;
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Initiates the parsing of the Google Sheet
     *
     * @param {Partial<GoogleSheetsConverterOptions>}[options]
     * Options for the parser
     *
     * @param {DataEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @emits GoogleSheetsParser#parse
     * @emits GoogleSheetsParser#afterParse
     */
    parse(options, eventDetail) {
        const converter = this, parseOptions = merge(converter.options, options);
        let columnsArray = ((parseOptions.json?.values) || []).map((column) => column.slice());
        if (columnsArray.length === 0) {
            return {};
        }
        converter.header = [];
        converter.emit({
            type: 'parse',
            columns: [],
            detail: eventDetail,
            headers: converter.header
        });
        // If beforeParse is defined, use it to modify the data
        const { beforeParse, json } = parseOptions;
        if (beforeParse && json) {
            columnsArray = beforeParse(json.values);
        }
        let column;
        for (let i = 0, iEnd = columnsArray.length; i < iEnd; i++) {
            column = columnsArray[i];
            converter.header[i] = (parseOptions.firstRowAsNames ?
                `${column.shift()}` :
                uniqueKey());
            for (let j = 0, jEnd = column.length; j < jEnd; ++j) {
                let cellValue = column[j];
                if (isDateObject(cellValue)) {
                    cellValue = cellValue.getTime();
                }
                columnsArray[i][j] = cellValue;
            }
        }
        converter.emit({
            type: 'afterParse',
            columns: columnsArray,
            detail: eventDetail,
            headers: converter.header
        });
        return Converters_DataConverterUtils.getColumnsCollection(columnsArray, converter.header);
    }
}
/* *
 *
 *  Static Properties
 *
 * */
/**
 * Default options
 */
GoogleSheetsConverter.defaultOptions = {
    ...Converters_DataConverter.defaultOptions
};
Converters_DataConverter.registerType('GoogleSheets', GoogleSheetsConverter);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Converters_GoogleSheetsConverter = (GoogleSheetsConverter);
/**
 * Check if a value is a Date object
 *
 * @param {unknown} value to verify
 * @return {boolean}
 * True if the value is a Date object, false otherwise.
 */
function isDateObject(value) {
    return Object.prototype.toString.call(value) === '[object Date]';
}

;// ./code/dashboards/es-modules/Data/Connectors/GoogleSheetsConnector.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Torstein Hønsi
 *  - Gøran Slettemark
 *  - Wojciech Chmiel
 *  - Sophie Bremer
 *  - Jomar Hønsi
 *  - Kamil Kubik
 *
 * */




/* *
 *
 *  Functions
 *
 * */
/**
 * Tests Google's response for error.
 * @private
 */
function isGoogleError(json) {
    return (typeof json === 'object' && json &&
        typeof json.error === 'object' && json.error &&
        typeof json.error.code === 'number' &&
        typeof json.error.message === 'string' &&
        typeof json.error.status === 'string');
}
/* *
 *
 *  Class
 *
 * */
/**
 * @private
 * @todo implement save, requires oauth2
 */
class GoogleSheetsConnector extends Connectors_DataConnector {
    /* *
 *
 *  Constructor
 *
 * */
    /**
 * Constructs an instance of GoogleSheetsConnector
 *
 * @param {Partial<GoogleSheetsConnectorOptions>} [options]
 * Options for the connector and converter.
 */
    constructor(options) {
        const mergedOptions = merge(GoogleSheetsConnector.defaultOptions, options);
        super(mergedOptions);
        this.options = mergedOptions;
    }
    /* *
 *
 *  Functions
 *
 * */
    /**
 * Overrides the DataConnector method. Emits an event on the connector to
 * all registered callbacks of this event.
 *
 * @param {Event} e
 * Event object containing additional event information.
 */
    emit(e) {
        fireEvent(this, e.type, e);
    }
    /**
 * Loads data from a Google Spreadsheet.
 *
 * @param {DataEventDetail} [eventDetail]
 * Custom information for pending events.
 *
 * @return {Promise<this>}
 * Same connector instance with modified table.
 */
    load(eventDetail) {
        const connector = this;
        const options = connector.options;
        const { dataRefreshRate, enablePolling, googleAPIKey, googleSpreadsheetKey, dataTables } = options;
        const url = buildFetchURL(googleAPIKey, googleSpreadsheetKey, options);
        connector.emit({
            type: 'load',
            detail: eventDetail,
            url
        });
        if (!URL.canParse(url)) {
            throw new Error('Invalid URL: ' + url);
        }
        return fetch(url, { signal: connector?.pollingController?.signal })
            .then((response) => (response.json()))
            .then((json) => {
            if (isGoogleError(json)) {
                throw new Error(json.error.message);
            }
            this.initConverters(json, (key) => {
                const tableOptions = dataTables?.find((dataTable) => dataTable.key === key);
                // The data table options takes precedence over the
                // connector options.
                const { firstRowAsNames = options.firstRowAsNames, beforeParse = options.beforeParse } = tableOptions || {};
                const converterOptions = {
                    firstRowAsNames,
                    beforeParse
                };
                return new Converters_GoogleSheetsConverter(converterOptions);
            }, (converter, data) => converter.parse({ json: data }));
            return connector.applyTableModifiers();
        })
            .then(() => {
            connector.emit({
                type: 'afterLoad',
                detail: eventDetail,
                url
            });
            // Polling
            if (enablePolling) {
                setTimeout(() => connector.load(), Math.max(dataRefreshRate || 0, 1) * 1000);
            }
            return connector;
        })['catch']((error) => {
            connector.emit({
                type: 'loadError',
                detail: eventDetail,
                error
            });
            throw error;
        });
    }
}
/* *
*
*  Static Properties
*
* */
GoogleSheetsConnector.defaultOptions = {
    id: 'google-sheets-connector',
    type: 'GoogleSheets',
    googleAPIKey: '',
    googleSpreadsheetKey: '',
    enablePolling: false,
    dataRefreshRate: 2,
    firstRowAsNames: true
};
/* *
 *
 *  Constants
 *
 * */
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
/* *
 *
 *  Functions
 *
 * */
/**
 * Creates GoogleSheets API v4 URL.
 * @private
 */
function buildFetchURL(apiKey, sheetKey, options = {}) {
    const url = new URL(`https://sheets.googleapis.com/v4/spreadsheets/${sheetKey}/values/`);
    const range = options.onlyColumnIds ?
        'A1:Z1' : buildQueryRange(options);
    url.pathname += range;
    const searchParams = url.searchParams;
    searchParams.set('alt', 'json');
    if (!options.onlyColumnIds) {
        searchParams.set('dateTimeRenderOption', 'FORMATTED_STRING');
        searchParams.set('majorDimension', 'COLUMNS');
        searchParams.set('valueRenderOption', 'UNFORMATTED_VALUE');
    }
    searchParams.set('prettyPrint', 'false');
    searchParams.set('key', apiKey);
    return url.href;
}
/**
 * Creates sheets range.
 * @private
 */
function buildQueryRange(options = {}) {
    const { endColumn, endRow, googleSpreadsheetRange, startColumn, startRow } = options;
    return googleSpreadsheetRange || ((alphabet[startColumn || 0] || 'A') +
        (Math.max((startRow || 0), 0) + 1) +
        ':' +
        (alphabet[Utilities_pick(endColumn, 25)] || 'Z') +
        (endRow ?
            Math.max(endRow, 0) :
            'Z'));
}
/* *
 *
 *  Registry
 *
 * */
Connectors_DataConnector.registerType('GoogleSheets', GoogleSheetsConnector);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Connectors_GoogleSheetsConnector = (GoogleSheetsConnector);

;// ./code/dashboards/es-modules/Data/Converters/HTMLTableConverter.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Torstein Hønsi
 *  - Gøran Slettemark
 *  - Wojciech Chmiel
 *  - Sophie Bremer
 *  - Kamil Kubik
 *
 * */




/* *
 *
 *  Functions
 *
 * */
/**
 * Row equal
 */
function isRowEqual(row1, row2) {
    let i = row1.length;
    if (row2.length === i) {
        while (--i) {
            if (row1[i] !== row2[i]) {
                return false;
            }
        }
    }
    else {
        return false;
    }
    return true;
}
/* *
 *
 *  Class
 *
 * */
/**
 * Handles parsing and transformation of an HTML table to a table.
 *
 * @private
 */
class HTMLTableConverter extends Converters_DataConverter {
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Constructs an instance of the HTMLTableConverter.
     *
     * @param {Partial<HTMLTableConverterOptions>} [options]
     * Options for the HTMLTableConverter.
     */
    constructor(options) {
        const mergedOptions = merge(HTMLTableConverter.defaultOptions, options);
        super(mergedOptions);
        this.headers = [];
        this.options = mergedOptions;
        if (mergedOptions.tableElement) {
            this.tableElement = mergedOptions.tableElement;
            this.tableElementID = mergedOptions.tableElement.id;
        }
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Exports the dataconnector as an HTML string, using the options
     * provided on import unless other options are provided.
     *
     * @param {DataConnector} connector
     * Connector instance to export from.
     *
     * @param {HTMLTableConnector.ExportOptions} [options]
     * Options that override default or existing export options.
     *
     * @return {string}
     * HTML from the current dataTable.
     */
    export(connector, options = this.options) {
        const exportNames = (options.firstRowAsNames !== false), useMultiLevelHeaders = options.useMultiLevelHeaders;
        const columns = connector.getSortedColumns(), columnIds = Object.keys(columns), htmlRows = [], columnsCount = columnIds.length;
        const rowArray = [];
        let tableHead = '';
        // Add the names as the first row if they should be exported
        if (exportNames) {
            const subcategories = [];
            // If using multilevel headers, the first value
            // of each column is a subcategory
            if (useMultiLevelHeaders) {
                for (const columnId of columnIds) {
                    let column = columns[columnId];
                    if (!Array.isArray(column)) {
                        // Convert to conventional array from typed array
                        // if needed
                        column = Array.from(column);
                    }
                    const subhead = (column.shift() || '').toString();
                    columns[columnId] = column;
                    subcategories.push(subhead);
                }
                tableHead = this.getTableHeaderHTML(columnIds, subcategories, options);
            }
            else {
                tableHead = this.getTableHeaderHTML(void 0, columnIds, options);
            }
        }
        for (let columnIndex = 0; columnIndex < columnsCount; columnIndex++) {
            const columnId = columnIds[columnIndex], column = columns[columnId], columnLength = column.length;
            for (let rowIndex = 0; rowIndex < columnLength; rowIndex++) {
                let cellValue = column[rowIndex];
                if (!rowArray[rowIndex]) {
                    rowArray[rowIndex] = [];
                }
                if (!(typeof cellValue === 'string' ||
                    typeof cellValue === 'number' ||
                    typeof cellValue === 'undefined')) {
                    cellValue = (cellValue || '').toString();
                }
                rowArray[rowIndex][columnIndex] = this.getCellHTMLFromValue(columnIndex ? 'td' : 'th', null, columnIndex ? '' : 'scope="row"', cellValue);
                // On the final column, push the row to the array
                if (columnIndex === columnsCount - 1) {
                    htmlRows.push('<tr>' +
                        rowArray[rowIndex].join('') +
                        '</tr>');
                }
            }
        }
        let caption = '';
        // Add table caption
        // Current exportdata falls back to chart title
        // but that should probably be handled elsewhere?
        if (options.tableCaption) {
            caption = '<caption class="highcharts-table-caption">' +
                options.tableCaption +
                '</caption>';
        }
        return ('<table>' +
            caption +
            tableHead +
            '<tbody>' +
            htmlRows.join('') +
            '</tbody>' +
            '</table>');
    }
    /**
     * Get table cell markup from row data.
     */
    getCellHTMLFromValue(tag, classes, attrs, value, decimalPoint) {
        let val = value, className = 'text' + (classes ? ' ' + classes : '');
        // Convert to string if number
        if (typeof val === 'number') {
            val = val.toString();
            if (decimalPoint === ',') {
                val = val.replace('.', decimalPoint);
            }
            className = 'number';
        }
        else if (!value) {
            val = '';
            className = 'empty';
        }
        return '<' + tag + (attrs ? ' ' + attrs : '') +
            ' class="' + className + '">' +
            val + '</' + tag + '>';
    }
    /**
     * Get table header markup from row data.
     */
    getTableHeaderHTML(topheaders = [], subheaders = [], options = this.options) {
        const { useMultiLevelHeaders, useRowspanHeaders } = options;
        let html = '<thead>', i = 0, len = subheaders && subheaders.length, next, cur, curColspan = 0, rowspan;
        // Clean up multiple table headers. Exporting.getDataRows() returns two
        // levels of headers when using multilevel, not merged. We need to
        // merge identical headers, remove redundant headers, and keep it
        // all marked up nicely.
        if (useMultiLevelHeaders &&
            topheaders &&
            subheaders &&
            !isRowEqual(topheaders, subheaders)) {
            html += '<tr>';
            for (; i < len; ++i) {
                cur = topheaders[i];
                next = topheaders[i + 1];
                if (cur === next) {
                    ++curColspan;
                }
                else if (curColspan) {
                    // Ended colspan
                    // Add cur to HTML with colspan.
                    html += this.getCellHTMLFromValue('th', 'highcharts-table-topheading', 'scope="col" ' +
                        'colspan="' + (curColspan + 1) + '"', cur);
                    curColspan = 0;
                }
                else {
                    // Cur is standalone. If it is same as sublevel,
                    // remove sublevel and add just toplevel.
                    if (cur === subheaders[i]) {
                        if (useRowspanHeaders) {
                            rowspan = 2;
                            subheaders.splice(i, 1);
                        }
                        else {
                            rowspan = 1;
                            subheaders[i] = '';
                        }
                    }
                    else {
                        rowspan = 1;
                    }
                    html += this.getCellHTMLFromValue('th', 'highcharts-table-topheading', 'scope="col"' +
                        (rowspan > 1 ?
                            ' valign="top" rowspan="' + rowspan + '"' :
                            ''), cur);
                }
            }
            html += '</tr>';
        }
        // Add the subheaders (the only headers if not using multilevels)
        if (subheaders) {
            html += '<tr>';
            for (i = 0, len = subheaders.length; i < len; ++i) {
                if (typeof subheaders[i] !== 'undefined') {
                    html += this.getCellHTMLFromValue('th', null, 'scope="col"', subheaders[i]);
                }
            }
            html += '</tr>';
        }
        html += '</thead>';
        return html;
    }
    /**
     * Initiates the parsing of the HTML table
     *
     * @param {Partial<HTMLTableConverterOptions>}[options]
     * Options for the parser
     *
     * @param {DataEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @emits CSVDataParser#parse
     * @emits CSVDataParser#afterParse
     * @emits HTMLTableParser#parseError
     */
    parse(options, eventDetail) {
        const converter = this, columnsArray = [], headers = [], parseOptions = merge(converter.options, options), { endRow, startColumn, endColumn, firstRowAsNames } = parseOptions, tableHTML = parseOptions.tableElement || this.tableElement;
        if (!(tableHTML instanceof HTMLElement)) {
            converter.emit({
                type: 'parseError',
                columns: columnsArray,
                detail: eventDetail,
                headers,
                error: 'Not a valid HTML Table'
            });
            return {};
        }
        converter.tableElement = tableHTML;
        converter.tableElementID = tableHTML.id;
        this.emit({
            type: 'parse',
            columns: columnsArray,
            detail: eventDetail,
            headers: converter.headers
        });
        const rows = tableHTML.getElementsByTagName('tr'), rowsCount = rows.length;
        let rowIndex = 0, item, { startRow } = parseOptions;
        // Insert headers from the first row
        if (firstRowAsNames && rowsCount) {
            const items = rows[0].children, itemsLength = items.length;
            for (let i = startColumn; i < itemsLength; i++) {
                if (i > endColumn) {
                    break;
                }
                item = items[i];
                if (item.tagName === 'TD' ||
                    item.tagName === 'TH') {
                    headers.push(item.innerHTML);
                }
            }
            startRow++;
        }
        while (rowIndex < rowsCount) {
            if (rowIndex >= startRow && rowIndex <= endRow) {
                const columnsInRow = rows[rowIndex].children, columnsInRowLength = columnsInRow.length;
                let columnIndex = 0;
                while (columnIndex < columnsInRowLength) {
                    const relativeColumnIndex = columnIndex - startColumn, row = columnsArray[relativeColumnIndex];
                    item = columnsInRow[columnIndex];
                    if ((item.tagName === 'TD' ||
                        item.tagName === 'TH') &&
                        (columnIndex >= startColumn &&
                            columnIndex <= endColumn)) {
                        if (!columnsArray[relativeColumnIndex]) {
                            columnsArray[relativeColumnIndex] = [];
                        }
                        let cellValue = converter.convertByType(item.innerHTML);
                        if (cellValue instanceof Date) {
                            cellValue = cellValue.getTime();
                        }
                        columnsArray[relativeColumnIndex][rowIndex - startRow] = cellValue;
                        // Loop over all previous indices and make sure
                        // they are nulls, not undefined.
                        let i = 1;
                        while (rowIndex - startRow >= i &&
                            row[rowIndex - startRow - i] === void 0) {
                            row[rowIndex - startRow - i] = null;
                            i++;
                        }
                    }
                    columnIndex++;
                }
            }
            rowIndex++;
        }
        this.headers = headers;
        this.emit({
            type: 'afterParse',
            columns: columnsArray,
            detail: eventDetail,
            headers
        });
        return Converters_DataConverterUtils.getColumnsCollection(columnsArray, converter.headers);
    }
}
/* *
 *
 *  Static Properties
 *
 * */
/**
 * Default options
 */
HTMLTableConverter.defaultOptions = {
    ...Converters_DataConverter.defaultOptions,
    useRowspanHeaders: true,
    useMultiLevelHeaders: true,
    startColumn: 0,
    endColumn: Number.MAX_VALUE,
    startRow: 0,
    endRow: Number.MAX_VALUE
};
Converters_DataConverter.registerType('HTMLTable', HTMLTableConverter);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Converters_HTMLTableConverter = (HTMLTableConverter);

;// ./code/dashboards/es-modules/Data/Connectors/HTMLTableConnector.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Torstein Hønsi
 *  - Gøran Slettemark
 *  - Wojciech Chmiel
 *  - Sophie Bremer
 *  - Kamil Kubik
 *
 * */





const { win: HTMLTableConnector_win } = Core_Globals;
/* *
 *
 *  Class
 *
 * */
/**
 * Class that handles creating a data connector from an HTML table.
 *
 * @private
 */
class HTMLTableConnector extends Connectors_DataConnector {
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Constructs an instance of HTMLTableConnector.
     *
     * @param {CombinedHTMLTableConnectorOptions} [options]
     * Options for the connector and converter.
     */
    constructor(options) {
        const mergedOptions = merge(HTMLTableConnector.defaultOptions, options);
        super(mergedOptions);
        this.options = mergedOptions;
        this.converter = new Converters_HTMLTableConverter(mergedOptions);
    }
    /**
     * Initiates creating the dataconnector from the HTML table
     *
     * @param {DataEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @emits HTMLTableConnector#load
     * @emits HTMLTableConnector#afterLoad
     * @emits HTMLTableConnector#loadError
     */
    async load(eventDetail) {
        const connector = this;
        const options = connector.options;
        const converter = connector.converter;
        const table = connector.getTable();
        const htmlTable = options.htmlTable;
        connector.emit({
            type: 'load',
            detail: eventDetail
        });
        let tableElement;
        if (typeof htmlTable === 'string') {
            connector.tableID = htmlTable;
            tableElement = HTMLTableConnector_win.document.getElementById(htmlTable);
        }
        else {
            tableElement = htmlTable;
            connector.tableID = tableElement.id;
        }
        connector.tableElement = tableElement || void 0;
        if (!connector.tableElement) {
            const error = 'HTML table not provided, or element with ID not found';
            connector.emit({
                type: 'loadError',
                detail: eventDetail,
                error
            });
            return Promise.reject(new Error(error));
        }
        const columns = converter.parse(merge({ tableElement: connector.tableElement }, options), eventDetail);
        // If already loaded, clear the current rows
        table.deleteColumns();
        table.setColumns(columns);
        await connector.applyTableModifiers();
        connector.emit({
            type: 'afterLoad',
            detail: eventDetail
        });
        return connector;
    }
}
/* *
 *
 *  Static Properties
 *
 * */
HTMLTableConnector.defaultOptions = {
    id: 'HTML-table-connector',
    type: 'HTMLTable',
    htmlTable: ''
};
Connectors_DataConnector.registerType('HTMLTable', HTMLTableConnector);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Connectors_HTMLTableConnector = (HTMLTableConnector);

;// ./code/dashboards/es-modules/Data/Converters/JSONConverter.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Paweł Lysy
 *  - Kamil Kubik
 *
 * */





/* *
 *
 *  Class
 *
 * */
/**
 * Handles parsing and transforming JSON to a table.
 *
 * @private
 */
class JSONConverter extends Converters_DataConverter {
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Constructs an instance of the JSON parser.
     *
     * @param {Partial<JSONConverterOptions>} [options]
     * Options for the JSON parser.
     */
    constructor(options) {
        const mergedOptions = merge(JSONConverter.defaultOptions, options);
        super(mergedOptions);
        /* *
         *
         *  Properties
         *
         * */
        this.headerColumnIds = [];
        this.headers = [];
        this.options = mergedOptions;
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Initiates parsing of JSON structure.
     *
     * @param {Partial<JSONConverterOptions>}[options]
     * Options for the parser
     *
     * @param {DataEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @emits JSONConverter#parse
     * @emits JSONConverter#afterParse
     */
    parse(options, eventDetail) {
        const converter = this;
        options = merge(converter.options, options);
        const { beforeParse, orientation, firstRowAsNames, columnIds } = options;
        let data = options.data;
        if (!data) {
            return {};
        }
        converter.headers = [];
        const columnsArray = [];
        converter.emit({
            type: 'parse',
            columns: columnsArray,
            detail: eventDetail,
            headers: converter.headers
        });
        if (beforeParse) {
            data = beforeParse(data);
        }
        data = data.slice();
        if (orientation === 'columns') {
            this.parseColumnsOrientation(columnsArray, data, firstRowAsNames, columnIds);
        }
        else if (orientation === 'rows') {
            this.parseRowsOrientation(columnsArray, data, firstRowAsNames, columnIds);
        }
        converter.emit({
            type: 'afterParse',
            columns: columnsArray,
            detail: eventDetail,
            headers: converter.headers
        });
        return Converters_DataConverterUtils.getColumnsCollection(columnsArray, converter.headers);
    }
    /**
     * Helper for parsing data in 'columns' orientation.
     *
     * @param {DataTableBasicColumn[]} [columnsArray]
     * Array of columns.
     *
     * @param {unknown[]} [data]
     * Array of data elements.
     *
     * @param {Boolean} [firstRowAsNames]
     * Defines row as names.
     *
     * @param {Array<string>} [columnIds]
     * Column ids to retrieve.
     *
     * @return {void}
     */
    parseColumnsOrientation(columnsArray, data, firstRowAsNames, columnIds) {
        const converter = this;
        for (let i = 0, iEnd = data.length; i < iEnd; i++) {
            const item = data[i];
            if (!(Array.isArray(item))) {
                return;
            }
            if (Array.isArray(converter.headers)) {
                if (firstRowAsNames) {
                    converter.headers.push(`${item.shift()}`);
                }
                else if (columnIds && Array.isArray(columnIds)) {
                    converter.headers.push(columnIds[i]);
                }
                columnsArray.push(item);
            }
            else {
                error('JSONConverter: Invalid `columnIds` option.', false);
            }
        }
    }
    /**
     * Helper for parsing data in 'rows' orientation.
     *
     * @param {DataTableBasicColumn[]} [columnsArray]
     * Array of columns.
     *
     * Helper for parsing data in 'rows' orientation.
     *
     * @param {unknown[]} [data]
     * Array of data elements.
     *
     * @param {Boolean} [firstRowAsNames]
     * Defines row as names.
     *
     * @param {Array<string>} [columnIds]
     * Column ids to retrieve.
     *
     * @return {DataTableBasicColumn[]}
     * Parsed columns.
     */
    parseRowsOrientation(columnsArray, data, firstRowAsNames, columnIds) {
        const converter = this;
        if (firstRowAsNames) {
            converter.headers = data.shift();
        }
        else if (columnIds) {
            converter.headerColumnIds = columnIds;
        }
        for (let rowIndex = 0, iEnd = data.length; rowIndex < iEnd; rowIndex++) {
            let row = data[rowIndex];
            if (!isArray(row)) {
                row = this.convertItemToRow(row, columnIds);
            }
            for (let columnIndex = 0, jEnd = row.length; columnIndex < jEnd; columnIndex++) {
                if (columnsArray.length < columnIndex + 1) {
                    columnsArray.push([]);
                }
                columnsArray[columnIndex].push(row[columnIndex]);
                // Create headers only once.
                if (!firstRowAsNames && rowIndex === 0) {
                    if (Array.isArray(converter.headerColumnIds)) {
                        converter.headers.push(converter.headerColumnIds[columnIndex] ||
                            columnIndex.toString());
                    }
                    else {
                        error('JSONConverter: Invalid `columnIds` option.', false);
                    }
                }
            }
        }
    }
    /**
     * Extracts a row from an object, using columnIds if provided.
     *
     * @param {Record<string, string|number>} [rowObj]
     * Set of props.
     *
     * @param {Array<string>} [columnIds]
     * Column ids to retrieve.
     *
     * @return {Array<string | number>}
     * Row converted to array.
     */
    convertItemToRow(rowObj, columnIds) {
        const converter = this;
        if (columnIds && !(Array.isArray(columnIds))) {
            const newRow = [];
            objectEach(columnIds, (arrayWithPath, name) => {
                newRow.push(arrayWithPath.reduce((acc, key) => acc[key], rowObj));
                if (converter.headers.indexOf(name) < 0) {
                    converter.headers.push(name);
                }
            });
            return newRow;
        }
        converter.headerColumnIds = Object.keys(rowObj);
        return Object.values(rowObj);
    }
}
/* *
 *
 *  Static Properties
 *
 * */
/**
 * Default options
 */
JSONConverter.defaultOptions = {
    ...Converters_DataConverter.defaultOptions,
    orientation: 'rows'
};
Converters_DataConverter.registerType('JSON', JSONConverter);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Converters_JSONConverter = (JSONConverter);

;// ./code/dashboards/es-modules/Data/Connectors/JSONConnector.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Paweł Lysy
 *  - Kamil Kubik
 *
 * */




/* *
 *
 *  Class
 *
 * */
/**
 * Class that handles creating a DataConnector from JSON structure
 *
 * @private
 */
class JSONConnector extends Connectors_DataConnector {
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Constructs an instance of JSONConnector.
     *
     * @param {Partial<JSONConnectorOptions>} [options]
     * Options for the connector and converter.
     */
    constructor(options) {
        const mergedOptions = merge(JSONConnector.defaultOptions, options);
        super(mergedOptions);
        this.options = mergedOptions;
        if (mergedOptions.enablePolling) {
            this.startPolling(Math.max(mergedOptions.dataRefreshRate || 0, 1) * 1000);
        }
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Overrides the DataConnector method. Emits an event on the connector to
     * all registered callbacks of this event.
     *
     * @param {Event} e
     * Event object containing additional event information.
     */
    emit(e) {
        fireEvent(this, e.type, e);
    }
    /**
     * Initiates the loading of the JSON source to the connector
     *
     * @param {DataEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @emits JSONConnector#load
     * @emits JSONConnector#afterLoad
     */
    load(eventDetail) {
        const connector = this;
        const options = connector.options;
        const { data, dataUrl, dataTables } = options;
        connector.emit({
            type: 'load',
            detail: eventDetail,
            data
        });
        return Promise
            .resolve(dataUrl ?
            fetch(dataUrl, {
                signal: connector?.pollingController?.signal
            }).then((response) => response.json())['catch']((error) => {
                connector.emit({
                    type: 'loadError',
                    detail: eventDetail,
                    error
                });
                console.warn(`Unable to fetch data from ${dataUrl}.`); // eslint-disable-line no-console
            }) :
            data || [])
            .then(async (data) => {
            if (data) {
                this.initConverters(data, (key) => {
                    const tableOptions = dataTables?.find((dataTable) => dataTable.key === key);
                    // The data table options takes precedence over the
                    // connector options.
                    const { columnIds = options.columnIds, firstRowAsNames = options.firstRowAsNames, orientation = options.orientation, beforeParse = options.beforeParse } = tableOptions || {};
                    const converterOptions = {
                        data,
                        columnIds,
                        firstRowAsNames,
                        orientation,
                        beforeParse
                    };
                    return new Converters_JSONConverter(converterOptions);
                }, (converter, data) => converter.parse({ data }));
            }
            return connector.applyTableModifiers().then(() => data ?? []);
        })
            .then((data) => {
            connector.emit({
                type: 'afterLoad',
                detail: eventDetail,
                data
            });
            return connector;
        })['catch']((error) => {
            connector.emit({
                type: 'loadError',
                detail: eventDetail,
                error
            });
            throw error;
        });
    }
}
/* *
 *
 *  Static Properties
 *
 * */
JSONConnector.defaultOptions = {
    type: 'JSON',
    id: 'json-connector',
    data: [],
    enablePolling: false,
    dataRefreshRate: 0,
    firstRowAsNames: true,
    orientation: 'rows'
};
/* *
 *
 *  Registry
 *
 * */
Connectors_DataConnector.registerType('JSON', JSONConnector);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Connectors_JSONConnector = (JSONConnector);

;// ./code/dashboards/es-modules/Data/Modifiers/ChainModifier.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *  - Dawid Draguła
 *
 * */



/* *
 *
 *  Class
 *
 * */
/**
 * Modifies a table with the help of modifiers in an ordered chain.
 *
 */
class ChainModifier extends Modifiers_DataModifier {
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Constructs an instance of the modifier chain.
     *
     * @param {Partial<ChainModifierOptions>} [options]
     * Options to configure the modifier chain.
     *
     * @param {...DataModifier} [chain]
     * Ordered chain of modifiers.
     */
    constructor(options, ...chain) {
        super();
        this.chain = chain;
        this.options = merge(ChainModifier.defaultOptions, options);
        const optionsChain = this.options.chain || [];
        for (let i = 0, iEnd = optionsChain.length, modifierOptions, ModifierClass; i < iEnd; ++i) {
            modifierOptions = optionsChain[i];
            if (!modifierOptions.type) {
                continue;
            }
            ModifierClass = Modifiers_DataModifier.types[modifierOptions.type];
            if (ModifierClass) {
                chain.push(new ModifierClass(modifierOptions));
            }
        }
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Adds a configured modifier to the end of the modifier chain. Please note,
     * that the modifier can be added multiple times.
     *
     * @param {DataModifier} modifier
     * Configured modifier to add.
     *
     * @param {DataEventDetail} [eventDetail]
     * Custom information for pending events.
     */
    add(modifier, eventDetail) {
        this.emit({
            type: 'addModifier',
            detail: eventDetail,
            modifier
        });
        this.chain.push(modifier);
        this.emit({
            type: 'addModifier',
            detail: eventDetail,
            modifier
        });
    }
    /**
     * Clears all modifiers from the chain.
     *
     * @param {DataEventDetail} [eventDetail]
     * Custom information for pending events.
     */
    clear(eventDetail) {
        this.emit({
            type: 'clearChain',
            detail: eventDetail
        });
        this.chain.length = 0;
        this.emit({
            type: 'afterClearChain',
            detail: eventDetail
        });
    }
    /**
     * Sequentially applies all modifiers in the chain to the given table,
     * updating its `modified` property with the final result.
     *
     * *Note:* The `modified` property reference of the table gets replaced.
     *
     * @param {Highcharts.DataTable} table
     * Table to modify.
     *
     * @param {DataEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @return {Promise<Highcharts.DataTable>}
     * Table with `modified` property as a reference.
     */
    async modify(table, eventDetail) {
        const modifiers = (this.options.reverse ?
            this.chain.slice().reverse() :
            this.chain.slice());
        if (!table.modified) {
            table.modified = table.clone(false, eventDetail);
        }
        let modified = table;
        for (let i = 0, iEnd = modifiers.length; i < iEnd; ++i) {
            try {
                await modifiers[i].modify(modified, eventDetail);
            }
            catch (error) {
                this.emit({
                    type: 'error',
                    detail: eventDetail,
                    table
                });
                throw error;
            }
            modified = modified.getModified();
        }
        table.modified = modified;
        return table;
    }
    /**
     * Applies several modifications to the table.
     *
     * *Note:* The `modified` property reference of the table gets replaced.
     *
     * @param {DataTable} table
     * Table to modify.
     *
     * @param {DataEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @return {DataTable}
     * Table as a reference.
     *
     * @emits ChainDataModifier#execute
     * @emits ChainDataModifier#afterExecute
     */
    modifyTable(table, eventDetail) {
        const chain = this;
        chain.emit({
            type: 'modify',
            detail: eventDetail,
            table
        });
        const modifiers = (chain.options.reverse ?
            chain.chain.reverse() :
            chain.chain.slice());
        let modified = table.getModified();
        for (let i = 0, iEnd = modifiers.length, modifier; i < iEnd; ++i) {
            modifier = modifiers[i];
            modified =
                modifier.modifyTable(modified, eventDetail).getModified();
        }
        table.modified = modified;
        chain.emit({
            type: 'afterModify',
            detail: eventDetail,
            table
        });
        return table;
    }
    /**
     * Removes a configured modifier from all positions in the modifier chain.
     *
     * @param {DataModifier} modifier
     * Configured modifier to remove.
     *
     * @param {DataEventDetail} [eventDetail]
     * Custom information for pending events.
     */
    remove(modifier, eventDetail) {
        const modifiers = this.chain;
        this.emit({
            type: 'removeModifier',
            detail: eventDetail,
            modifier
        });
        modifiers.splice(modifiers.indexOf(modifier), 1);
        this.emit({
            type: 'afterRemoveModifier',
            detail: eventDetail,
            modifier
        });
    }
    emit(e) {
        fireEvent(this, e.type, e);
    }
    on(type, callback) {
        return addEvent(this, type, callback);
    }
}
/* *
 *
 *  Static Properties
 *
 * */
/**
 * Default option for the ordered modifier chain.
 */
ChainModifier.defaultOptions = {
    type: 'Chain'
};
Modifiers_DataModifier.registerType('Chain', ChainModifier);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Modifiers_ChainModifier = ((/* unused pure expression or super */ null && (ChainModifier)));

;// ./code/dashboards/es-modules/Data/Modifiers/InvertModifier.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Wojciech Chmiel
 *  - Sophie Bremer
 *
 * */



/* *
 *
 *  Class
 *
 * */
/**
 * Inverts columns and rows in a table.
 *
 * @private
 */
class InvertModifier extends Modifiers_DataModifier {
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Constructs an instance of the invert modifier.
     *
     * @param {Partial<InvertModifierOptions>} [options]
     * Options to configure the invert modifier.
     */
    constructor(options) {
        super();
        this.options = merge(InvertModifier.defaultOptions, options);
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Inverts rows and columns in the table. If the given table does not have
     * defined a `modified` property, the filtering is applied in-place on the
     * original table rather than on a `modified` copy.
     *
     * @param {DataTable} table
     * Table to invert.
     *
     * @param {DataEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @return {DataTable}
     * Table with inverted `modified` property as a reference or modified table,
     * if `modified` property of the original table is undefined.
     */
    modifyTable(table, eventDetail) {
        const modifier = this;
        modifier.emit({ type: 'modify', detail: eventDetail, table });
        const modified = table.getModified();
        if (table.hasColumns(['columnIds'])) { // Inverted table
            const columnIdsColumn = ((table.deleteColumns(['columnIds']) || {})
                .columnIds || []), columns = {}, columnIds = [];
            for (let i = 0, iEnd = columnIdsColumn.length; i < iEnd; ++i) {
                columnIds.push('' + columnIdsColumn[i]);
            }
            for (let i = 0, iEnd = table.getRowCount(), row; i < iEnd; ++i) {
                row = table.getRow(i);
                if (row) {
                    columns[columnIds[i]] = row;
                }
            }
            modified.deleteColumns();
            modified.setColumns(columns);
        }
        else { // Regular table
            const columns = {};
            for (let i = 0, iEnd = table.getRowCount(), row; i < iEnd; ++i) {
                row = table.getRow(i);
                if (row) {
                    columns[`${i}`] = row;
                }
            }
            columns.columnIds = table.getColumnIds();
            modified.deleteColumns();
            modified.setColumns(columns);
        }
        modifier.emit({ type: 'afterModify', detail: eventDetail, table });
        return table;
    }
}
/* *
 *
 *  Static Properties
 *
 * */
/**
 * Default options for the invert modifier.
 */
InvertModifier.defaultOptions = {
    type: 'Invert'
};
Modifiers_DataModifier.registerType('Invert', InvertModifier);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Modifiers_InvertModifier = ((/* unused pure expression or super */ null && (InvertModifier)));

;// ./code/dashboards/es-modules/Data/Modifiers/RangeModifier.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *  - Dawid Draguła
 *
 * */



/* *
 *
 *  Class
 *
 * */
/**
 * Slices the table rows based on the specified range.
 */
class RangeModifier extends Modifiers_DataModifier {
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Constructs an instance of the range modifier.
     *
     * @param {Partial<RangeModifierOptions>} [options]
     * Options to configure the range modifier.
     */
    constructor(options) {
        super();
        this.options = merge(RangeModifier.defaultOptions, options);
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Replaces table rows with ranged rows. If the given table does not have
     * defined a `modified` property, the filtering is applied in-place on the
     * original table rather than on a `modified` copy.
     *
     * @param {DataTable} table
     * Table to modify.
     *
     * @param {DataEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @return {DataTable}
     * Table with `modified` property as a reference or modified table, if
     * `modified` property of the original table is undefined.
     */
    modifyTable(table, eventDetail) {
        const modifier = this;
        modifier.emit({ type: 'modify', detail: eventDetail, table });
        let { start, end } = modifier.options;
        start = Math.max(0, start || 0);
        end = Math.min(end || Infinity, table.getRowCount());
        const length = Math.max(end - start, 0);
        const modified = table.getModified();
        modified.deleteRows();
        modified.setRows(table.getRows(start, length));
        modified.setOriginalRowIndexes(Array.from({ length }, (_, i) => table.getOriginalRowIndex(start + i)));
        modifier.emit({ type: 'afterModify', detail: eventDetail, table });
        return table;
    }
}
/* *
 *
 *  Static Properties
 *
 * */
/**
 * Default options for the range modifier.
 */
RangeModifier.defaultOptions = {
    type: 'Range',
    start: 0,
    end: Infinity
};
Modifiers_DataModifier.registerType('Range', RangeModifier);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Modifiers_RangeModifier = ((/* unused pure expression or super */ null && (RangeModifier)));

;// ./code/dashboards/es-modules/Data/Modifiers/SortModifier.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *  - Dawid Draguła
 *
 * */



/* *
 *
 *  Class
 *
 * */
/**
 * Sort table rows according to values of a column.
 *
 */
class SortModifier extends Modifiers_DataModifier {
    /* *
     *
     *  Static Functions
     *
     * */
    static ascending(a, b) {
        return ((a || 0) < (b || 0) ? -1 :
            (a || 0) > (b || 0) ? 1 :
                0);
    }
    static descending(a, b) {
        return ((b || 0) < (a || 0) ? -1 :
            (b || 0) > (a || 0) ? 1 :
                0);
    }
    static compareFactory(direction, customCompare) {
        if (customCompare) {
            if (direction === 'desc') {
                return (a, b) => -customCompare(a, b);
            }
            return customCompare;
        }
        return (direction === 'asc' ?
            SortModifier.ascending :
            SortModifier.descending);
    }
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Constructs an instance of the sort modifier.
     *
     * @param {Partial<SortModifierOptions>} [options]
     * Options to configure the sort modifier.
     */
    constructor(options) {
        super();
        this.options = merge(SortModifier.defaultOptions, options);
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Returns index and row for sort reference.
     *
     * @private
     *
     * @param {Highcharts.DataTable} table
     * Table with rows to reference.
     *
     * @return {Array<SortRowReference>}
     * Array of row references.
     */
    getRowReferences(table) {
        const rows = table.getRows(), rowReferences = [];
        for (let i = 0, iEnd = rows.length; i < iEnd; ++i) {
            rowReferences.push({
                index: i,
                row: rows[i]
            });
        }
        return rowReferences;
    }
    modifyTable(table, eventDetail) {
        const modifier = this;
        modifier.emit({ type: 'modify', detail: eventDetail, table });
        const columnIds = table.getColumnIds(), rowCount = table.getRowCount(), rowReferences = this.getRowReferences(table), { direction, orderInColumn, compare: customCompare } = modifier.options, modified = table.getModified();
        const orderBy = ('columns' in modifier.options ?
            modifier.options.columns :
            [modifier.options.orderByColumn]);
        const orderByIndexes = [];
        for (let i = 0, iEnd = orderBy.length; i < iEnd; ++i) {
            const sort = orderBy[i];
            const isString = typeof sort === 'string';
            const column = isString ? sort : sort.column;
            const columnIndex = columnIds.indexOf(column);
            if (columnIndex === -1) {
                continue;
            }
            orderByIndexes.push({
                columnIndex,
                compare: SortModifier.compareFactory(isString ? direction : (sort.direction || direction), isString ? customCompare : (sort.compare || customCompare))
            });
        }
        if (orderByIndexes.length) {
            rowReferences.sort((a, b) => {
                for (let i = 0, iEnd = orderByIndexes.length; i < iEnd; ++i) {
                    const { columnIndex, compare } = orderByIndexes[i];
                    const result = compare(a.row[columnIndex], b.row[columnIndex]);
                    if (result) {
                        return result;
                    }
                }
                return a.index - b.index;
            });
        }
        if (orderInColumn) {
            const column = [];
            for (let i = 0; i < rowCount; ++i) {
                column[rowReferences[i].index] = i;
            }
            modified.setColumns({ [orderInColumn]: column });
        }
        else {
            const originalIndexes = [];
            const rows = [];
            let rowReference;
            for (let i = 0; i < rowCount; ++i) {
                rowReference = rowReferences[i];
                originalIndexes.push(table.getOriginalRowIndex(rowReference.index));
                rows.push(rowReference.row);
            }
            modified.setRows(rows, 0);
            modified.setOriginalRowIndexes(originalIndexes);
        }
        modifier.emit({ type: 'afterModify', detail: eventDetail, table });
        return table;
    }
}
/* *
 *
 *  Static Properties
 *
 * */
/**
 * Default options to group table rows.
 */
SortModifier.defaultOptions = {
    type: 'Sort',
    direction: 'desc',
    orderByColumn: 'y'
};
Modifiers_DataModifier.registerType('Sort', SortModifier);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Modifiers_SortModifier = ((/* unused pure expression or super */ null && (SortModifier)));

;// ./code/dashboards/es-modules/Data/Modifiers/FilterModifier.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Dawid Draguła
 *
 * */



/* *
 *
 *  Class
 *
 * */
/**
 * Filters out table rows matching a given condition.
 */
class FilterModifier extends Modifiers_DataModifier {
    /* *
     *
     *  Static Functions
     *
     * */
    /**
     * Compiles a filter condition into a callback function.
     *
     * @param {FilterCondition} condition
     * Condition to compile.
     */
    static compile(condition) {
        if (isFunction(condition)) {
            return condition;
        }
        const op = condition.operator;
        switch (op) {
            case 'and': {
                const subs = condition.conditions.map((c) => this.compile(c));
                return (row, table, i) => subs.every((cond) => cond(row, table, i));
            }
            case 'or': {
                const subs = condition.conditions.map((c) => this.compile(c));
                return (row, table, i) => subs.some((cond) => cond(row, table, i));
            }
            case 'not': {
                const sub = this.compile(condition.condition);
                return (row, table, i) => !sub(row, table, i);
            }
        }
        const { columnId: col, value } = condition;
        switch (op) {
            case '==':
                // eslint-disable-next-line eqeqeq
                return (row) => row[col] == value;
            case '===':
                return (row) => row[col] === value;
            case '!=':
                // eslint-disable-next-line eqeqeq
                return (row) => row[col] != value;
            case '!==':
                return (row) => row[col] !== value;
            case '>':
                return (row) => (row[col] || 0) > (value || 0);
            case '>=':
                return (row) => (row[col] || 0) >= (value || 0);
            case '<':
                return (row) => (row[col] || 0) < (value || 0);
            case '<=':
                return (row) => (row[col] || 0) <= (value || 0);
            case 'empty':
                return (row) => row[col] === null || row[col] === '';
        }
        const { ignoreCase } = condition;
        const str = (val) => {
            const s = '' + val;
            return (ignoreCase ?? true) ? s.toLowerCase() : s;
        };
        switch (op) {
            case 'contains':
                return (row) => str(row[col]).includes(str(value));
            default:
                return (row) => str(row[col])[op](str(value));
        }
    }
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Constructs an instance of the filter modifier.
     *
     * @param {Partial<FilterModifierOptions>} [options]
     * Options to configure the filter modifier.
     */
    constructor(options) {
        super();
        this.options = merge(FilterModifier.defaultOptions, options);
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Filters out table rows matching a given condition. If the given table
     * does not have defined a `modified` property, the filtering is applied
     * in-place on the original table rather than on a `modified` copy.
     *
     * @param {DataTable} table
     * Table to modify.
     *
     * @param {DataEventDetail} [eventDetail]
     * Custom information for pending events.
     *
     * @return {DataTable}
     * Table with `modified` property as a reference or modified table, if
     * `modified` property of the original table is undefined.
     */
    modifyTable(table, eventDetail) {
        const modifier = this;
        modifier.emit({ type: 'modify', detail: eventDetail, table });
        const { condition } = modifier.options;
        if (!condition) {
            // If no condition is set, return the unmodified table.
            return table;
        }
        const matchRow = FilterModifier.compile(condition);
        const modified = table.getModified();
        const rows = [];
        const indexes = [];
        for (let i = 0, iEnd = table.getRowCount(); i < iEnd; ++i) {
            const row = table.getRowObject(i);
            if (!row) {
                continue;
            }
            if (matchRow(row, table, i)) {
                rows.push(row);
                indexes.push(table.getOriginalRowIndex(i));
            }
        }
        modified.deleteRows();
        modified.setRows(rows);
        modified.setOriginalRowIndexes(indexes);
        modifier.emit({ type: 'afterModify', detail: eventDetail, table });
        return table;
    }
}
/* *
 *
 *  Static Properties
 *
 * */
/**
 * Default options for the filter modifier.
 */
FilterModifier.defaultOptions = {
    type: 'Filter'
};
Modifiers_DataModifier.registerType('Filter', FilterModifier);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Modifiers_FilterModifier = ((/* unused pure expression or super */ null && (FilterModifier)));

;// ./code/dashboards/es-modules/Dashboards/Serializable.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */

/* *
 *
 *  Constants
 *
 * */
/**
 * Registry of serializable classes.
 */
const classRegistry = {};
/**
 * Registry of function sets.
 */
const helperRegistry = {};
/* *
 *
 *  Functions
 *
 * */
/**
 * Creates a class instance from the given JSON, if a suitable serializer
 * has been found.
 *
 * @function Serializable.fromJSON
 *
 * @param {JSON} json
 * JSON to create a class instance or object from.
 *
 * @return {AnyRecord}
 * Returns the class instance or object, or throws an exception.
 */
function fromJSON(json) {
    const $class = json.$class;
    if (typeof $class !== 'string') {
        throw new Error('JSON has no $class property.');
    }
    const classs = classRegistry[$class];
    if (classs) {
        return classs.fromJSON(json);
    }
    const helper = helperRegistry[$class];
    if (helper) {
        return helper.fromJSON(json);
    }
    throw new Error(`'${$class}' unknown.`);
}
/**
 * Registers a class prototype for the given JSON $class.
 *
 * @function Serializable.registerClassPrototype
 *
 * @param {string} $class
 * JSON $class to register for.
 *
 * @param {Serializable} classPrototype
 * Class to register.
 */
function registerClassPrototype($class, classPrototype) {
    if (classRegistry[$class]) {
        throw new Error('A serializer for \'' + $class + '\' is already registered.');
    }
    classRegistry[$class] = classPrototype;
}
/**
 * Registers helper functions for the given JSON $class.
 *
 * @function Serializable.registerHelper
 *
 * @param {Helper} helperFunctions
 * Helper functions to register.
 */
function registerHelper(helperFunctions) {
    if (helperRegistry[helperFunctions.$class]) {
        throw new Error('A serializer for \'' + helperFunctions.$class +
            '\' is already registered.');
    }
    helperRegistry[helperFunctions.$class] = helperFunctions;
}
/**
 * Creates JSON from a class instance.
 *
 * @function Serializable.toJSON
 *
 * @param {AnyRecord} obj
 * Class instance or object to serialize as JSON.
 *
 * @return {JSON}
 * JSON of the class instance.
 */
function toJSON(obj) {
    if (typeof obj.fromJSON === 'function' &&
        typeof obj.toJSON === 'function') {
        return obj.toJSON();
    }
    const classes = Object.keys(helperRegistry), numberOfHelpers = classes.length;
    let $class, serializer;
    for (let i = 0; i < numberOfHelpers; ++i) {
        $class = classes[i];
        serializer = helperRegistry[$class];
        if (serializer.jsonSupportFor(obj)) {
            return serializer.toJSON(obj);
        }
    }
    throw new Error('Object is not supported.');
}
/* *
 *
 *  Default Export
 *
 * */
const Serializable = {
    fromJSON,
    registerClassPrototype,
    registerHelper,
    toJSON
};
/* harmony default export */ const Dashboards_Serializable = (Serializable);

;// ./code/dashboards/es-modules/Dashboards/SerializeHelper/DataTableHelper.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */



/* *
 *
 *  Functions
 *
 * */
/**
 * Converts the given JSON to a class instance.
 *
 * @param {JSON} json
 * JSON to deserialize as a class instance or object.
 *
 * @return {DataTable}
 * Returns the class instance or object, or throws an exception.
 */
function DataTableHelper_fromJSON(json) {
    return new Data_DataTable({ columns: json.columns, id: json.id });
}
/**
 * Validates the given class instance for JSON support.
 *
 * @param {AnyRecord} obj
 * Class instance or object to validate.
 *
 * @return {boolean}
 * Returns true, if the function set can convert the given object, otherwise
 * false.
 */
function jsonSupportFor(obj) {
    return obj instanceof Data_DataTable;
}
/**
 * Converts the given class instance to JSON.
 *
 * @param {DataTable} obj
 * Class instance or object to serialize as JSON.
 *
 * @return {JSON}
 * Returns the JSON of the class instance or object.
 */
function DataTableHelper_toJSON(obj) {
    const json = {
        $class: 'Data.DataTable',
        columns: obj.getColumns(void 0, false, true)
    };
    // Custom ID
    if (!obj.autoId) {
        json.id = obj.id;
    }
    // Done
    return json;
}
/* *
 *
 *  Registry
 *
 * */
const DataTableHelper = {
    $class: 'Data.DataTable',
    fromJSON: DataTableHelper_fromJSON,
    jsonSupportFor,
    toJSON: DataTableHelper_toJSON
};
Dashboards_Serializable.registerHelper(DataTableHelper);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const SerializeHelper_DataTableHelper = (DataTableHelper);

;// ./code/dashboards/es-modules/Dashboards/SerializeHelper/CSVConnectorHelper.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */





/* *
 *
 *  Functions
 *
 * */
/**
 * Converts the given JSON to a class instance.
 *
 * @param {JSON} json
 * JSON to deserialize as a class instance or object.
 *
 * @return {CSVConnector}
 * Returns the class instance or object, or throws an exception.
 */
function CSVConnectorHelper_fromJSON(json) {
    return new Connectors_CSVConnector(json.options);
}
/**
 * Validates the given class instance for JSON support.
 *
 * @param {AnyRecord} obj
 * Class instance or object to validate.
 *
 * @return {boolean}
 * Returns true, if the function set can convert the given object, otherwise
 * false.
 */
function CSVConnectorHelper_jsonSupportFor(obj) {
    return obj instanceof Connectors_CSVConnector;
}
/**
 * Converts the given class instance to JSON.
 *
 * @param {CSVConnector} obj
 * Class instance or object to serialize as JSON.
 *
 * @return {JSON}
 * Returns the JSON of the class instance or object.
 */
function CSVConnectorHelper_toJSON(obj) {
    const options = merge(obj.options);
    options.dataTable = SerializeHelper_DataTableHelper.toJSON(obj.getTable());
    return {
        $class: 'Data.CSVConnector',
        options
    };
}
/* *
 *
 *  Registry
 *
 * */
const CSVConnectorHelper = {
    $class: 'Data.CSVConnector',
    fromJSON: CSVConnectorHelper_fromJSON,
    jsonSupportFor: CSVConnectorHelper_jsonSupportFor,
    toJSON: CSVConnectorHelper_toJSON
};
Dashboards_Serializable.registerHelper(CSVConnectorHelper);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const SerializeHelper_CSVConnectorHelper = ((/* unused pure expression or super */ null && (CSVConnectorHelper)));

;// ./code/dashboards/es-modules/Dashboards/SerializeHelper/DataConverterHelper.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Paweł Lysy
 *  - Sophie Bremer
 *
 * */




/* *
 *
 *  Functions
 *
 * */
/**
 * JSON object as a base.
 * @param json Serialized object
 * @return {DataConverter} New Data Converter object created from serialized object
 */
function DataConverterHelper_fromJSON(json) {
    return new Converters_DataConverter(json.options);
}
/**
 * Check if the Object on the input is the correct Object to be serialized
 * @param obj Obj to check
 * @return {obj is DataConverter} If object is DataConverter
 */
function DataConverterHelper_jsonSupportFor(obj) {
    return obj instanceof Converters_DataConverter;
}
/**
 * Change the obj of DataConverter to its Serialized form.
 * @param obj Object to serialize
 * @return Serialized object
 */
function DataConverterHelper_toJSON(obj) {
    return {
        $class: 'Data.DataConverter',
        options: merge(obj.options)
    };
}
/* *
 *
 *  Registry
 *
 * */
const DataConverterHelper = {
    $class: 'Data.DataConverter',
    fromJSON: DataConverterHelper_fromJSON,
    jsonSupportFor: DataConverterHelper_jsonSupportFor,
    toJSON: DataConverterHelper_toJSON
};
Dashboards_Serializable.registerHelper(DataConverterHelper);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const SerializeHelper_DataConverterHelper = ((/* unused pure expression or super */ null && (DataConverterHelper)));

;// ./code/dashboards/es-modules/Data/DataCursor.js
/* *
 *
 *  (c) 2020-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */

/* *
 *
 *  Class
 *
 * */
/**
 * This class manages state cursors pointing on {@link Data.DataTable}. It
 * creates a relation between states of the user interface and the table cells,
 * columns, or rows.
 *
 * @class
 * @name Data.DataCursor
 */
class DataCursor {
    /* *
     *
     *  Static Properties
     *
     * */
    /* *
     *
     *  Constructor
     *
     * */
    constructor(stateMap = {}) {
        this.emittingRegister = [];
        this.listenerMap = {};
        this.stateMap = stateMap;
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * This function registers a listener for a specific state and table.
     *
     * @example
     * ```TypeScript
     * dataCursor.addListener(myTable.id, 'hover', (e: DataCursorEvent) => {
     *     if (e.cursor.type === 'position') {
     *         console.log(`Hover over row #${e.cursor.row}.`);
     *     }
     * });
     * ```
     *
     * @function #addListener
     *
     * @param {Data.DataCursorTableId} tableId
     * The ID of the table to listen to.
     *
     * @param {Data.DataCursorState} state
     * The state on the table to listen to.
     *
     * @param {Data.DataCursorListener} listener
     * The listener to register.
     *
     * @return {Data.DataCursor}
     * Returns the DataCursor instance for a call chain.
     */
    addListener(tableId, state, listener) {
        const listenerMap = this.listenerMap[tableId] = (this.listenerMap[tableId] ||
            {});
        const listeners = listenerMap[state] = (listenerMap[state] ||
            []);
        listeners.push(listener);
        return this;
    }
    /**
     * @private
     */
    buildEmittingTag(e) {
        return (e.cursor.type === 'position' ?
            [
                e.table.id,
                e.cursor.column,
                e.cursor.row,
                e.cursor.state,
                e.cursor.type
            ] :
            [
                e.table.id,
                e.cursor.columns,
                e.cursor.firstRow,
                e.cursor.lastRow,
                e.cursor.state,
                e.cursor.type
            ]).join('\0');
    }
    /**
     * This function emits a state cursor related to a table. It will provide
     * lasting state cursors of the table to listeners.
     *
     * @example
     * ```ts
     * dataCursor.emit(myTable, {
     *     type: 'position',
     *     column: 'city',
     *     row: 4,
     *     state: 'hover',
     * });
     * ```
     *
     * @param {Data.DataTable} table
     * The related table of the cursor.
     *
     * @param {Data.DataCursorType} cursor
     * The state cursor to emit.
     *
     * @param {Event} [event]
     * Optional event information from a related source.
     *
     * @param {boolean} [lasting]
     * Whether this state cursor should be kept until it is cleared with
     * {@link DataCursor#remitCursor}.
     *
     * @return {Data.DataCursor}
     * Returns the DataCursor instance for a call chain.
     */
    emitCursor(table, cursor, event, lasting) {
        const tableId = table.id, state = cursor.state, listeners = (this.listenerMap[tableId] &&
            this.listenerMap[tableId][state]);
        if (listeners) {
            const stateMap = this.stateMap[tableId] = (this.stateMap[tableId] ?? {});
            const cursors = stateMap[cursor.state] || [];
            if (lasting) {
                if (!cursors.length) {
                    stateMap[cursor.state] = cursors;
                }
                if (getIndex(cursor, cursors) === -1) {
                    cursors.push(cursor);
                }
            }
            const e = {
                cursor,
                cursors,
                table
            };
            if (event) {
                e.event = event;
            }
            const emittingRegister = this.emittingRegister, emittingTag = this.buildEmittingTag(e);
            if (emittingRegister.indexOf(emittingTag) >= 0) {
                // Break call stack loops
                return this;
            }
            try {
                this.emittingRegister.push(emittingTag);
                for (let i = 0, iEnd = listeners.length; i < iEnd; ++i) {
                    listeners[i].call(this, e);
                }
            }
            finally {
                const index = this.emittingRegister.indexOf(emittingTag);
                if (index >= 0) {
                    this.emittingRegister.splice(index, 1);
                }
            }
        }
        return this;
    }
    /**
     * Removes a lasting state cursor.
     *
     * @function #remitCursor
     *
     * @param {string} tableId
     * ID of the related cursor table.
     *
     * @param {Data.DataCursorType} cursor
     * Copy or reference of the cursor.
     *
     * @return {Data.DataCursor}
     * Returns the DataCursor instance for a call chain.
     */
    remitCursor(tableId, cursor) {
        const cursors = (this.stateMap[tableId] &&
            this.stateMap[tableId][cursor.state]);
        if (cursors) {
            const index = getIndex(cursor, cursors);
            if (index >= 0) {
                cursors.splice(index, 1);
            }
        }
        return this;
    }
    /**
     * This function removes a listener.
     *
     * @function #addListener
     *
     * @param {Data.DataCursorTableId} tableId
     * The ID of the table the listener is connected to.
     *
     * @param {Data.DataCursorState} state
     * The state on the table the listener is listening to.
     *
     * @param {Data.DataCursorListener} listener
     * The listener to deregister.
     *
     * @return {Data.DataCursor}
     * Returns the DataCursor instance for a call chain.
     */
    removeListener(tableId, state, listener) {
        const listeners = (this.listenerMap[tableId] &&
            this.listenerMap[tableId][state]);
        if (listeners) {
            const index = listeners.indexOf(listener);
            if (index >= 0) {
                listeners.splice(index, 1);
            }
        }
        return this;
    }
}
/* *
 *
 *  Functions
 *
 * */
/**
 * Finds the index of an cursor in an array.
 * @private
 */
function getIndex(needle, cursors) {
    if (needle.type === 'position') {
        for (let cursor, i = 0, iEnd = cursors.length; i < iEnd; ++i) {
            cursor = cursors[i];
            if (cursor.type === 'position' &&
                cursor.state === needle.state &&
                cursor.column === needle.column &&
                cursor.row === needle.row) {
                return i;
            }
        }
    }
    else {
        const columnNeedle = JSON.stringify(needle.columns);
        for (let cursor, i = 0, iEnd = cursors.length; i < iEnd; ++i) {
            cursor = cursors[i];
            if (cursor.type === 'range' &&
                cursor.state === needle.state &&
                cursor.firstRow === needle.firstRow &&
                cursor.lastRow === needle.lastRow &&
                JSON.stringify(cursor.columns) === columnNeedle) {
                return i;
            }
        }
    }
    return -1;
}
/**
 * Checks whether two cursor share the same properties.
 * @private
 */
function isEqual(cursorA, cursorB) {
    if (cursorA.type === 'position' && cursorB.type === 'position') {
        return (cursorA.column === cursorB.column &&
            cursorA.row === cursorB.row &&
            cursorA.state === cursorB.state);
    }
    if (cursorA.type === 'range' && cursorB.type === 'range') {
        return (cursorA.firstRow === cursorB.firstRow &&
            cursorA.lastRow === cursorB.lastRow &&
            (JSON.stringify(cursorA.columns) ===
                JSON.stringify(cursorB.columns)));
    }
    return false;
}
/**
 * Checks whether a cursor is in a range.
 * @private
 */
function isInRange(needle, range) {
    if (range.type === 'position') {
        range = toRange(range);
    }
    if (needle.type === 'position') {
        needle = toRange(needle, range);
    }
    const needleColumns = needle.columns;
    const rangeColumns = range.columns;
    return (needle.firstRow >= range.firstRow &&
        needle.lastRow <= range.lastRow &&
        (!needleColumns ||
            !rangeColumns ||
            needleColumns.every((column) => rangeColumns.indexOf(column) >= 0)));
}
/**
 * @private
 */
function toPositions(cursor) {
    if (cursor.type === 'position') {
        return [cursor];
    }
    const columns = (cursor.columns || []);
    const positions = [];
    const state = cursor.state;
    for (let row = cursor.firstRow, rowEnd = cursor.lastRow; row < rowEnd; ++row) {
        if (!columns.length) {
            positions.push({
                type: 'position',
                row,
                state
            });
            continue;
        }
        for (let column = 0, columnEnd = columns.length; column < columnEnd; ++column) {
            positions.push({
                type: 'position',
                column: columns[column],
                row,
                state
            });
        }
    }
    return positions;
}
/**
 * @private
 */
function toRange(cursor, defaultRange) {
    if (cursor.type === 'range') {
        return cursor;
    }
    const range = {
        type: 'range',
        firstRow: (cursor.row ??
            (defaultRange && defaultRange.firstRow) ??
            0),
        lastRow: (cursor.row ??
            (defaultRange && defaultRange.lastRow) ??
            Number.MAX_VALUE),
        state: cursor.state
    };
    if (typeof cursor.column !== 'undefined') {
        range.columns = [cursor.column];
    }
    return range;
}
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Data_DataCursor = (DataCursor);

;// ./code/dashboards/es-modules/Dashboards/SerializeHelper/DataCursorHelper.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */



/* *
 *
 *  Functions
 *
 * */
/**
 * Converts the given JSON to a class instance.
 *
 * @param {JSON} json
 * JSON to deserialize as a class instance or object.
 *
 * @return {DataCursor}
 * Returns the class instance or object, or throws an exception.
 */
function DataCursorHelper_fromJSON(json) {
    return new Data_DataCursor(json.stateMap);
}
/**
 * Validates the given class instance for JSON support.
 *
 * @param {AnyRecord} obj
 * Class instance or object to validate.
 *
 * @return {boolean}
 * Returns true, if the function set can convert the given object, otherwise
 * false.
 */
function DataCursorHelper_jsonSupportFor(obj) {
    return obj instanceof Data_DataCursor;
}
/**
 * Converts the given class instance to JSON.
 *
 * @param {DataTable} obj
 * Class instance or object to serialize as JSON.
 *
 * @return {DataTableHelper.JSON}
 * Returns the JSON of the class instance or object.
 */
function DataCursorHelper_toJSON(obj) {
    const stateMap = obj.stateMap, stateMapJSON = {}, tableIds = Object.keys(obj.stateMap);
    let cursors, cursorsJSON, tableId, state, states;
    for (let i = 0, iEnd = tableIds.length; i < iEnd; ++i) {
        tableId = tableIds[i];
        states = Object.keys(stateMap[tableId]);
        stateMapJSON[tableId] = {};
        for (let j = 0, jEnd = states.length; j < jEnd; ++j) {
            state = states[j];
            cursors = stateMap[tableId][state];
            stateMapJSON[tableId][state] = cursorsJSON = [];
            for (let k = 0, kEnd = cursors.length; k < kEnd; ++k) {
                cursorsJSON.push({ ...cursors[k] });
            }
        }
    }
    return {
        $class: 'Data.DataCursor',
        stateMap: stateMapJSON
    };
}
/* *
 *
 *  Registry
 *
 * */
const DataCursorHelper = {
    $class: 'Data.DataCursor',
    fromJSON: DataCursorHelper_fromJSON,
    jsonSupportFor: DataCursorHelper_jsonSupportFor,
    toJSON: DataCursorHelper_toJSON
};
Dashboards_Serializable.registerHelper(DataCursorHelper);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const SerializeHelper_DataCursorHelper = ((/* unused pure expression or super */ null && (DataCursorHelper)));

;// ./code/dashboards/es-modules/Dashboards/SerializeHelper/GoogleSheetsConnectorHelper.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */





/* *
 *
 *  Functions
 *
 * */
/**
 * Converts the given JSON to a class instance.
 *
 * @param {JSON} json
 * JSON to deserialize as a class instance or object.
 *
 * @return {GoogleSheetsConnector}
 * Returns the class instance or object, or throws an exception.
 */
function GoogleSheetsConnectorHelper_fromJSON(json) {
    return new Connectors_GoogleSheetsConnector(json.options || { googleAPIKey: '', googleSpreadsheetKey: '' });
}
/**
 * Validates the given class instance for JSON support.
 *
 * @param {AnyRecord} obj
 * Class instance or object to validate.
 *
 * @return {boolean}
 * Returns true, if the function set can convert the given object, otherwise
 * false.
 */
function GoogleSheetsConnectorHelper_jsonSupportFor(obj) {
    return obj instanceof Connectors_GoogleSheetsConnector;
}
/**
 * Converts the given class instance to JSON.
 *
 * @param {GoogleSheetsConnector} obj
 * Class instance or object to serialize as JSON.
 *
 * @return {JSON}
 * Returns the JSON of the class instance or object.
 */
function GoogleSheetsConnectorHelper_toJSON(obj) {
    const options = merge(obj.options);
    options.dataTable = SerializeHelper_DataTableHelper.toJSON(obj.getTable());
    return {
        $class: 'Data.GoogleSheetsConnector',
        options
    };
}
/* *
 *
 *  Registry
 *
 * */
const GoogleSheetsConnectorHelper = {
    $class: 'Data.GoogleSheetsConnector',
    fromJSON: GoogleSheetsConnectorHelper_fromJSON,
    jsonSupportFor: GoogleSheetsConnectorHelper_jsonSupportFor,
    toJSON: GoogleSheetsConnectorHelper_toJSON
};
Dashboards_Serializable.registerHelper(GoogleSheetsConnectorHelper);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const SerializeHelper_GoogleSheetsConnectorHelper = ((/* unused pure expression or super */ null && (GoogleSheetsConnectorHelper)));

;// ./code/dashboards/es-modules/Dashboards/SerializeHelper/HTMLTableConnectorHelper.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */





/* *
 *
 *  Functions
 *
 * */
/**
 * Converts the given JSON to a class instance.
 *
 * @param {JSON} json
 * JSON to deserialize as a class instance or object.
 *
 * @return {HTMLTableConnector}
 * Returns the class instance or object, or throws an exception.
 */
function HTMLTableConnectorHelper_fromJSON(json) {
    return new Connectors_HTMLTableConnector(json.options);
}
/**
 * Validates the given class instance for JSON support.
 *
 * @param {AnyRecord} obj
 * Class instance or object to validate.
 *
 * @return {boolean}
 * Returns true, if the function set can convert the given object, otherwise
 * false.
 */
function HTMLTableConnectorHelper_jsonSupportFor(obj) {
    return obj instanceof Connectors_HTMLTableConnector;
}
/**
 * Converts the given class instance to JSON.
 *
 * @param {HTMLTableConnector} obj
 * Class instance or object to serialize as JSON.
 *
 * @return {JSON}
 * Returns the JSON of the class instance or object.
 */
function HTMLTableConnectorHelper_toJSON(obj) {
    const options = merge(obj.options);
    options.dataTable = SerializeHelper_DataTableHelper.toJSON(obj.getTable());
    return {
        $class: 'Data.HTMLTableConnector',
        options
    };
}
/* *
 *
 *  Default Export
 *
 * */
const HTMLTableConnectorHelper = {
    $class: 'Data.HTMLTableConnector',
    fromJSON: HTMLTableConnectorHelper_fromJSON,
    jsonSupportFor: HTMLTableConnectorHelper_jsonSupportFor,
    toJSON: HTMLTableConnectorHelper_toJSON
};
Dashboards_Serializable.registerHelper(HTMLTableConnectorHelper);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const SerializeHelper_HTMLTableConnectorHelper = ((/* unused pure expression or super */ null && (HTMLTableConnectorHelper)));

;// ./code/dashboards/es-modules/Dashboards/SerializeHelper/JSONConnectorHelper.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Paweł Lysy
 *
 * */




/* *
 *
 *  Functions
 *
 * */
/**
 * Converts the given JSON to a class instance.
 *
 * @param {JSON} json
 * JSON to deserialize as a class instance or object.
 *
 * @return {JSONConnector}
 * Returns the class instance or object, or throws an exception.
 */
function JSONConnectorHelper_fromJSON(json) {
    return new Connectors_JSONConnector(json.options);
}
/**
 * Validates the given class instance for JSON support.
 *
 * @param {AnyRecord} obj
 * Class instance or object to validate.
 *
 * @return {boolean}
 * Returns true, if the function set can convert the given object, otherwise
 * false.
 */
function JSONConnectorHelper_jsonSupportFor(obj) {
    return obj instanceof Connectors_JSONConnector;
}
/**
 * Converts the given class instance to JSON.
 *
 * @param {JSONConnector} obj
 * Class instance or object to serialize as JSON.
 *
 * @return {JSON}
 * Returns the JSON of the class instance or object.
 */
function JSONConnectorHelper_toJSON(obj) {
    const options = merge(obj.options);
    options.dataTable = SerializeHelper_DataTableHelper.toJSON(obj.getTable());
    return {
        $class: 'Data.JSONConnector',
        options
    };
}
/* *
 *
 *  Registry
 *
 * */
const JSONConnectorHelper = {
    $class: 'Data.JSONConnector',
    fromJSON: JSONConnectorHelper_fromJSON,
    jsonSupportFor: JSONConnectorHelper_jsonSupportFor,
    toJSON: JSONConnectorHelper_toJSON
};
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const SerializeHelper_JSONConnectorHelper = ((/* unused pure expression or super */ null && (JSONConnectorHelper)));

;// ./code/dashboards/es-modules/Core/Renderer/HTML/AST.js
/* *
 *
 *  (c) 2010-2026 Highsoft AS
 *  Author: Torstein Hønsi
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 * */


const { SVG_NS, win: AST_win } = Core_Globals;


const { trustedTypes } = AST_win;
/* *
 *
 *  Constants
 *
 * */
// Create the trusted type policy. This should not be exposed.
const trustedTypesPolicy = (trustedTypes &&
    isFunction(trustedTypes.createPolicy) &&
    trustedTypes.createPolicy('highcharts', {
        createHTML: (s) => s
    }));
const emptyHTML = trustedTypesPolicy ?
    trustedTypesPolicy.createHTML('') :
    '';
/* *
 *
 *  Class
 *
 * */
/**
 * The AST class represents an abstract syntax tree of HTML or SVG content. It
 * can take HTML as an argument, parse it, optionally transform it to SVG, then
 * perform sanitation before inserting it into the DOM.
 *
 * @class
 * @name Highcharts.AST
 *
 * @param {string|Array<Highcharts.ASTNode>} source
 * Either an HTML string or an ASTNode list to populate the tree.
 */
class AST {
    /* *
     *
     *  Static Functions
     *
     * */
    /**
     * Filter an object of SVG or HTML attributes against the allow list.
     *
     * @static
     *
     * @function Highcharts.AST#filterUserAttributes
     *
     * @param {Highcharts.SVGAttributes} attributes The attributes to filter
     *
     * @return {Highcharts.SVGAttributes}
     * The filtered attributes
     */
    static filterUserAttributes(attributes) {
        objectEach(attributes, (val, key) => {
            let valid = true;
            if (AST.allowedAttributes.indexOf(key) === -1) {
                valid = false;
            }
            if (['background', 'dynsrc', 'href', 'lowsrc', 'src']
                .indexOf(key) !== -1) {
                valid = isString(val) && AST.allowedReferences.some((ref) => val.indexOf(ref) === 0);
            }
            if (!valid) {
                error(33, false, void 0, {
                    'Invalid attribute in config': `${key}`
                });
                delete attributes[key];
            }
            // #17753, < is not allowed in SVG attributes
            if (isString(val) && attributes[key]) {
                attributes[key] = val.replace(/</g, '&lt;');
            }
        });
        return attributes;
    }
    /**
     * Utility function to parse a style string to a CSSObject.
     *
     * @internal
     * @param {string} style
     * The style string to parse.
     * @return {Highcharts.CSSObject}
     * The parsed CSSObject.
     */
    static parseStyle(style) {
        return style
            .split(';')
            .reduce((styles, line) => {
            const pair = line.split(':').map((s) => s.trim()), key = pair.shift();
            if (key && pair.length) {
                styles[key.replace(/-([a-z])/g, (g) => g[1].toUpperCase())] = pair.join(':'); // #17146
            }
            return styles;
        }, {});
    }
    /**
     * Utility function to set html content for an element by passing in a
     * markup string. The markup is safely parsed by the AST class to avoid
     * XSS vulnerabilities. This function should be used instead of setting
     * `innerHTML` in all cases where the content is not fully trusted.
     *
     * @static
     * @function Highcharts.AST#setElementHTML
     *
     * @param {SVGDOMElement|HTMLDOMElement} el
     * Node to set content of.
     *
     * @param {string} html
     * Markup string
     */
    static setElementHTML(el, html) {
        el.innerHTML = AST.emptyHTML; // Clear previous
        if (html) {
            const ast = new AST(html);
            ast.addToDOM(el);
        }
    }
    /* *
     *
     *  Constructor
     *
     * */
    // Construct an AST from HTML markup, or wrap an array of existing AST nodes
    constructor(source) {
        this.nodes = typeof source === 'string' ?
            this.parseMarkup(source) : source;
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Add the tree defined as a hierarchical JS structure to the DOM
     *
     * @function Highcharts.AST#addToDOM
     *
     * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} parent
     * The node where it should be added
     *
     * @return {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement}
     * The inserted node.
     */
    addToDOM(parent) {
        /**
         * @internal
         * @param {Highcharts.ASTNode} subtree
         * HTML/SVG definition.
         * @param {Element} [subParent]
         * Parent node.
         * @return {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement}
         * The inserted node.
         */
        function recurse(subtree, subParent) {
            let ret;
            splat(subtree).forEach(function (item) {
                const tagName = item.tagName;
                const textNode = item.textContent ?
                    Core_Globals.doc.createTextNode(item.textContent) :
                    void 0;
                // Whether to ignore the AST filtering totally, #15345
                const bypassHTMLFiltering = AST.bypassHTMLFiltering;
                let node;
                if (tagName) {
                    if (tagName === '#text') {
                        node = textNode;
                    }
                    else if (AST.allowedTags.indexOf(tagName) !== -1 ||
                        bypassHTMLFiltering) {
                        const NS = tagName === 'svg' ?
                            SVG_NS :
                            (subParent.namespaceURI || SVG_NS);
                        const element = Core_Globals.doc.createElementNS(NS, tagName);
                        const attributes = item.attributes || {};
                        // Apply attributes from root of AST node, legacy from
                        // from before TextBuilder
                        objectEach(item, function (val, key) {
                            if (key !== 'tagName' &&
                                key !== 'attributes' &&
                                key !== 'children' &&
                                key !== 'style' &&
                                key !== 'textContent') {
                                attributes[key] = val;
                            }
                        });
                        attr(element, bypassHTMLFiltering ?
                            attributes :
                            AST.filterUserAttributes(attributes));
                        if (item.style) {
                            css(element, item.style);
                        }
                        // Add text content
                        if (textNode) {
                            element.appendChild(textNode);
                        }
                        // Recurse
                        recurse(item.children || [], element);
                        node = element;
                    }
                    else {
                        error(33, false, void 0, {
                            'Invalid tagName in config': tagName
                        });
                    }
                }
                // Add to the tree
                if (node) {
                    subParent.appendChild(node);
                }
                ret = node;
            });
            // Return last node added (on top level it's the only one)
            return ret;
        }
        return recurse(this.nodes, parent);
    }
    /**
     * Parse HTML/SVG markup into AST Node objects. Used internally from the
     * constructor.
     *
     * @internal
     * @param {string} markup
     * The markup string.
     * @return {Array<Highcharts.ASTNode>}
     * The parsed nodes.
     */
    parseMarkup(markup) {
        const nodes = [];
        markup = markup
            .trim()
            // The style attribute throws a warning when parsing when CSP is
            // enabled (#6884), so use an alias and pick it up below
            // Make all quotation marks parse correctly to DOM (#17627)
            .replace(/ style=(["'])/g, ' data-style=$1');
        let doc;
        try {
            doc = new DOMParser().parseFromString(trustedTypesPolicy ?
                trustedTypesPolicy.createHTML(markup) :
                markup, 'text/html');
        }
        catch {
            // There are two cases where this fails:
            // 1. IE9 and PhantomJS, where the DOMParser only supports parsing
            //    XML
            // 2. Due to a Chromium issue where chart redraws are triggered by
            //    a `beforeprint` event (#16931),
            //    https://issues.chromium.org/issues/40222135
        }
        if (!doc) {
            const body = createElement('div');
            body.innerHTML = markup;
            doc = { body };
        }
        const appendChildNodes = (node, addTo) => {
            const tagName = node.nodeName.toLowerCase();
            // Add allowed tags
            const astNode = {
                tagName
            };
            if (tagName === '#text') {
                astNode.textContent = node.textContent || '';
            }
            const parsedAttributes = node.attributes;
            // Add attributes
            if (parsedAttributes) {
                const attributes = {};
                [].forEach.call(parsedAttributes, (attrib) => {
                    if (attrib.name === 'data-style') {
                        astNode.style = AST.parseStyle(attrib.value);
                    }
                    else {
                        attributes[attrib.name] = attrib.value;
                    }
                });
                astNode.attributes = attributes;
            }
            // Handle children
            if (node.childNodes.length) {
                const children = [];
                [].forEach.call(node.childNodes, (childNode) => {
                    appendChildNodes(childNode, children);
                });
                if (children.length) {
                    astNode.children = children;
                }
            }
            addTo.push(astNode);
        };
        [].forEach.call(doc.body.childNodes, (childNode) => appendChildNodes(childNode, nodes));
        return nodes;
    }
}
/* *
 *
 *  Static Properties
 *
 * */
/**
 * The list of allowed SVG or HTML attributes, used for sanitizing
 * potentially harmful content from the chart configuration before adding to
 * the DOM.
 *
 * @see [Source code with default values](
 * https://github.com/highcharts/highcharts/blob/master/ts/Core/Renderer/HTML/AST.ts#:~:text=public%20static%20allowedAttributes)
 *
 * @example
 * // Allow a custom, trusted attribute
 * Highcharts.AST.allowedAttributes.push('data-value');
 *
 * @name Highcharts.AST.allowedAttributes
 * @type {Array<string>}
 */
AST.allowedAttributes = [
    'alt',
    'aria-controls',
    'aria-describedby',
    'aria-expanded',
    'aria-haspopup',
    'aria-hidden',
    'aria-label',
    'aria-labelledby',
    'aria-live',
    'aria-pressed',
    'aria-readonly',
    'aria-roledescription',
    'aria-selected',
    'aria-sort',
    'class',
    'clip-path',
    'color',
    'colspan',
    'cx',
    'cy',
    'd',
    'disabled',
    'dx',
    'dy',
    'fill',
    'filterUnits',
    'flood-color',
    'flood-opacity',
    'height',
    'href',
    'id',
    'in',
    'in2',
    'markerHeight',
    'markerWidth',
    'offset',
    'opacity',
    'operator',
    'orient',
    'padding',
    'paddingLeft',
    'paddingRight',
    'patternUnits',
    'r',
    'radius',
    'refX',
    'refY',
    'result',
    'role',
    'rowspan',
    'scope',
    'slope',
    'src',
    'startOffset',
    'stdDeviation',
    'stroke-linecap',
    'stroke-width',
    'stroke',
    'style',
    'summary',
    'tabindex',
    'tableValues',
    'target',
    'text-align',
    'text-anchor',
    'textAnchor',
    'textLength',
    'title',
    'type',
    'valign',
    'width',
    'x',
    'x1',
    'x2',
    'xlink:href',
    'y',
    'y1',
    'y2',
    'zIndex'
];
/**
 * The list of allowed references for referring attributes like `href` and
 * `src`. Attribute values will only be allowed if they start with one of
 * these strings.
 *
 * @see [Source code with default values](
 * https://github.com/highcharts/highcharts/blob/master/ts/Core/Renderer/HTML/AST.ts#:~:text=public%20static%20allowedReferences)
 *
 * @example
 * // Allow tel:
 * Highcharts.AST.allowedReferences.push('tel:');
 *
 * @name    Highcharts.AST.allowedReferences
 * @type    {Array<string>}
 */
AST.allowedReferences = [
    'https://',
    'http://',
    'mailto:',
    '/',
    '../',
    './',
    '#'
];
/**
 * The list of allowed SVG or HTML tags, used for sanitizing potentially
 * harmful content from the chart configuration before adding to the DOM.
 *
 * @see [Source code with default values](
 * https://github.com/highcharts/highcharts/blob/master/ts/Core/Renderer/HTML/AST.ts#:~:text=public%20static%20allowedTags)
 *
 * @example
 * // Allow a custom, trusted tag
 * Highcharts.AST.allowedTags.push('blink'); // ;)
 *
 * @name    Highcharts.AST.allowedTags
 * @type    {Array<string>}
 */
AST.allowedTags = [
    '#text',
    'a',
    'abbr',
    'b',
    'br',
    'button',
    'caption',
    'circle',
    'clipPath',
    'code',
    'dd',
    'defs',
    'div',
    'dl',
    'dt',
    'em',
    'feComponentTransfer',
    'feComposite',
    'feDropShadow',
    'feFlood',
    'feFuncA',
    'feFuncB',
    'feFuncG',
    'feFuncR',
    'feGaussianBlur',
    'feMerge',
    'feMergeNode',
    'feMorphology',
    'feOffset',
    'filter',
    'h1',
    'h2',
    'h3',
    'h4',
    'h5',
    'h6',
    'hr',
    'i',
    'img',
    'li',
    'linearGradient',
    'marker',
    'ol',
    'p',
    'path',
    'pattern',
    'pre',
    'rect',
    'small',
    'span',
    'stop',
    'strong',
    'style',
    'sub',
    'sup',
    'svg',
    'table',
    'tbody',
    'td',
    'text',
    'textPath',
    'th',
    'thead',
    'title',
    'tr',
    'tspan',
    'u',
    'ul'
];
/** @internal */
AST.emptyHTML = emptyHTML;
/**
 * Allow all custom SVG and HTML attributes, references and tags (together
 * with potentially harmful ones) to be added to the DOM from the chart
 * configuration. In other words, disable the allow-listing which is the
 * primary functionality of the AST.
 *
 * WARNING: Setting this property to `true` while allowing untrusted user
 * data in the chart configuration will expose your application to XSS
 * security risks!
 *
 * Note that in case you want to allow a known set of tags or attributes,
 * you should allow-list them instead of disabling the filtering totally.
 * See [allowedAttributes](Highcharts.AST#.allowedAttributes),
 * [allowedReferences](Highcharts.AST#.allowedReferences) and
 * [allowedTags](Highcharts.AST#.allowedTags). The `bypassHTMLFiltering`
 * setting is intended only for those cases where allow-listing is not
 * practical, and the chart configuration already comes from a secure
 * source.
 *
 * @example
 * // Allow all custom attributes, references and tags (disable DOM XSS
 * // filtering)
 * Highcharts.AST.bypassHTMLFiltering = true;
 *
 * @name Highcharts.AST.bypassHTMLFiltering
 * @static
 */
AST.bypassHTMLFiltering = false;
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const HTML_AST = (AST);
/* *
 *
 *  API Declarations
 *
 * */
/**
 * Serialized form of an SVG/HTML definition, including children.
 *
 * @interface Highcharts.ASTNode
 */ /**
* @name Highcharts.ASTNode#attributes
* @type {Highcharts.SVGAttributes|undefined}
*/ /**
* @name Highcharts.ASTNode#children
* @type {Array<Highcharts.ASTNode>|undefined}
*/ /**
* @name Highcharts.ASTNode#tagName
* @type {string|undefined}
*/ /**
* @name Highcharts.ASTNode#textContent
* @type {string|undefined}
*/
(''); // Keeps doclets above in file

;// ./code/dashboards/es-modules/Dashboards/Components/ComponentRegistry.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 * */

/* *
 *
 *  Constants
 *
 * */
/**
 *
 * Record of component classes
 * @todo
 *
 */
const types = {};
/* *
 *
 *  Functions
 *
 * */
/**
 * Method used to register new component classes.
 *
 * @param {string} key
 * Registry key of the component class.
 *
 * @param {ComponentType} ComponentClass
 * Component class (aka class constructor) to register.
 */
function registerComponent(key, ComponentClass) {
    return (!!key &&
        !types[key] &&
        !!(types[key] = ComponentClass));
}
/* *
 *
 *  Default Export
 *
 * */
const ComponentRegistry = {
    registerComponent,
    types
};
/* harmony default export */ const Components_ComponentRegistry = (ComponentRegistry);

;// ./code/dashboards/es-modules/Dashboards/Globals.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *  - Paweł Lysy
 *  - Karol Kołodziej
 *
 * */

/* *
 *
 *  Constants
 *
 * */
/**
 * Prefix of a GUIElement HTML class name.
 */
const classNamePrefix = 'highcharts-dashboards-';
const version = '4.2.0';
/** @internal */
const classNames = {
    layout: classNamePrefix + 'layout',
    cell: classNamePrefix + 'cell',
    cellHover: classNamePrefix + 'cell-state-hover',
    cellActive: classNamePrefix + 'cell-state-active',
    cellLoading: classNamePrefix + 'cell-state-loading',
    row: classNamePrefix + 'row',
    layoutsWrapper: classNamePrefix + 'layouts-wrapper',
    boardContainer: classNamePrefix + 'wrapper'
};
/** @internal */
const guiElementType = {
    row: 'row',
    cell: 'cell',
    layout: 'layout'
};
/**
 * Contains all Board instances of this window.
 */
const boards = [];
/**
 * Reference to the window used by Dashboards.
 */
const Globals_win = window;
const Globals_doc = document;
const noop = function () { };
const isMS = /(edge|msie|trident)/i
    .test((Globals_win.navigator && Globals_win.navigator.userAgent) || '') && !Globals_win.opera;
const supportsPassiveEvents = (function () {
    // Checks whether the browser supports passive events, (#11353).
    let supportsPassive = false;
    // Object.defineProperty doesn't work on IE as well as passive
    // events - instead of using polyfill, we can exclude IE totally.
    if (!isMS) {
        const opts = Object.defineProperty({}, 'passive', {
            get: function () {
                supportsPassive = true;
            }
        });
        if (Globals_win.addEventListener && Globals_win.removeEventListener) {
            Globals_win.addEventListener('testPassive', noop, opts);
            Globals_win.removeEventListener('testPassive', noop, opts);
        }
    }
    return supportsPassive;
}());
const Globals_Globals = {
    boards,
    classNamePrefix,
    classNames,
    doc: Globals_doc,
    guiElementType,
    isMS,
    noop,
    supportsPassiveEvents,
    version,
    win: Globals_win
};
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Dashboards_Globals = (Globals_Globals);

;// ./code/dashboards/es-modules/Dashboards/EditMode/EditGlobals.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *
 * */


const PREFIX = Dashboards_Globals.classNamePrefix + 'edit-';
const EditGlobals = {
    classNames: {
        resizeSnap: PREFIX + 'resize-snap',
        resizeSnapX: PREFIX + 'resize-snap-x',
        resizeSnapY: PREFIX + 'resize-snap-y',
        separator: PREFIX + 'separator',
        contextMenuBtn: PREFIX + 'context-menu-btn',
        contextMenuBtnText: PREFIX + 'context-menu-btn-text',
        contextMenu: PREFIX + 'context-menu',
        contextMenuItem: PREFIX + 'context-menu-item',
        editModeEnabled: PREFIX + 'enabled',
        editToolbar: PREFIX + 'toolbar',
        editToolbarCellOutline: PREFIX + 'toolbar-cell-outline',
        editToolbarRowOutline: PREFIX + 'toolbar-row-outline',
        editToolbarItem: PREFIX + 'toolbar-item',
        editToolbarRow: PREFIX + 'toolbar-row',
        editToolbarCell: PREFIX + 'toolbar-cell',
        editSidebar: PREFIX + 'sidebar',
        editSidebarShow: PREFIX + 'sidebar-show',
        editSidebarHide: PREFIX + 'sidebar-hide',
        editSidebarHeader: PREFIX + 'sidebar-header',
        editSidebarTitle: PREFIX + 'sidebar-title',
        editSidebarMenuItem: PREFIX + 'sidebar-item',
        rowContextHighlight: PREFIX + 'row-context-highlight',
        cellEditHighlight: PREFIX + 'cell-highlight',
        dashboardCellEditHighlightActive: PREFIX + 'cell-highlight-active',
        dragMock: PREFIX + 'drag-mock',
        dropPointer: PREFIX + 'drop-pointer',
        contextDetectionPointer: PREFIX + 'ctx-detection-pointer',
        resizePointer: PREFIX + 'resize-pointer',
        currentEditedElement: PREFIX + 'unmask',
        maskElement: PREFIX + 'mask',
        menuItem: PREFIX + 'menu-item',
        menu: PREFIX + 'menu',
        menuVerticalSeparator: PREFIX + 'menu-vertical-separator',
        menuHorizontalSeparator: PREFIX + 'menu-horizontal-separator',
        menuDestroy: PREFIX + 'menu-destroy',
        editSidebarWrapper: PREFIX + 'sidebar-wrapper',
        customSelect: PREFIX + 'custom-select',
        customSelectButton: PREFIX + 'custom-option-button',
        toggleContainer: PREFIX + 'toggle-container',
        toggleWrapper: PREFIX + 'toggle-wrapper',
        toggleSlider: PREFIX + 'toggle-slider',
        toggleWrapperColored: PREFIX + 'toggle-wrapper-colored',
        toggleLabels: PREFIX + 'toggle-labels',
        labeledToggleWrapper: PREFIX + 'labeled-toggle-wrapper',
        button: PREFIX + 'button',
        sidebarNavButton: PREFIX + 'sidebar-button-nav',
        labelText: PREFIX + 'label-text',
        editSidebarTabBtn: PREFIX + 'sidebar-tab-btn',
        editToolsBtn: PREFIX + 'tools-btn',
        editTools: PREFIX + 'tools',
        editGridItems: PREFIX + 'grid-items',
        editStandaloneToggle: PREFIX + 'standalone-toggle',
        // Confirmation popup
        confirmationPopup: PREFIX + 'confirmation-popup',
        popupButtonContainer: PREFIX + 'confirmation-popup-button-container',
        popupContentContainer: PREFIX + 'confirmation-popup-content',
        popupCancelBtn: PREFIX + 'confirmation-popup-cancel-btn',
        popupConfirmBtn: PREFIX + 'confirmation-popup-confirm-btn',
        popupCloseButton: PREFIX + 'popup-close',
        editOverlay: PREFIX + 'overlay',
        editOverlayActive: PREFIX + 'overlay-active',
        resizerMenuBtnActive: PREFIX + 'resizer-menu-btn-active',
        sidebarCloseButton: PREFIX + 'close-btn',
        editSidebarTabBtnWrapper: PREFIX + 'tabs-buttons-wrapper',
        editSidebarRight: PREFIX + 'sidebar-right',
        editSidebarRightShow: PREFIX + 'sidebar-right-show',
        viewFullscreen: PREFIX + 'view-fullscreen',
        // Accordion
        accordionMenu: PREFIX + 'accordion-menu',
        accordionContainer: PREFIX + 'accordion',
        accordionHeader: PREFIX + 'accordion-header',
        accordionHeaderBtn: PREFIX + 'accordion-header-btn',
        accordionHeaderWrapper: PREFIX + 'accordion-header-wrapper',
        accordionHeaderIcon: PREFIX + 'accordion-header-icon',
        accordionContent: PREFIX + 'accordion-content',
        accordionNestedWrapper: PREFIX + 'accordion-nested',
        accordionMenuButtonsContainer: PREFIX + 'accordion-menu-buttons-container',
        accordionMenuButton: PREFIX + 'accordion-menu-button',
        accordionStandaloneWrapper: PREFIX + 'accordion-standalone-wrapper',
        hiddenElement: PREFIX + 'hidden-element',
        collapsableContentHeader: PREFIX + 'collapsable-content-header',
        standaloneElement: PREFIX + 'standalone-element',
        // Custom dropdown with icons
        collapsedElement: PREFIX + 'collapsed-element',
        dropdown: PREFIX + 'dropdown',
        dropdownContent: PREFIX + 'dropdown-content',
        dropdownButton: PREFIX + 'dropdown-button',
        dropdownButtonContent: PREFIX + 'dropdown-button-content',
        dropdownIcon: PREFIX + 'pointer',
        icon: PREFIX + 'icon'
    },
    lang: {
        accessibility: {
            contextMenu: {
                button: 'Context menu'
            },
            editMode: {
                editMode: 'Edit mode toggle button'
            }
        },
        addComponent: 'Add component',
        cancelButton: 'Cancel',
        caption: 'Caption',
        chartClassName: 'Chart class name',
        chartConfig: 'Chart configuration',
        chartID: 'Chart ID',
        chartOptions: 'Chart options',
        chartType: 'Chart type',
        connectorName: 'Connector name',
        confirmButton: 'Confirm',
        confirmDestroyCell: 'Do you really want to destroy the cell?',
        confirmDestroyRow: 'Do you really want to destroy the row?',
        confirmDiscardChanges: 'Do you really want to discard the changes?',
        dataLabels: 'Data labels',
        editMode: 'Edit mode',
        errorMessage: 'Something went wrong',
        exitFullscreen: 'Exit full screen',
        htmlInput: 'HTML',
        id: 'Id',
        off: 'off',
        on: 'on',
        pointFormat: 'Point format',
        settings: 'Settings',
        style: 'Styles',
        title: 'Title',
        viewFullscreen: 'View in full screen',
        sidebar: {
            HTML: 'HTML',
            row: 'Row',
            Highcharts: 'Highcharts',
            Grid: 'Grid',
            KPI: 'KPI'
        }
    }
};
/* harmony default export */ const EditMode_EditGlobals = (EditGlobals);

;// ./code/dashboards/es-modules/Dashboards/Layout/GUIElement.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *
 * */



class GUIElement {
    /* *
    *
    *  Static Properties
    *
    * */
    /**
     * Get offsets of the guiElement relative to the referenceElement or the
     * Viewport.
     *
     * @param guiElement
     * The element to get the offsets from.
     *
     * @param referenceElement
     * The element to get the offsets relative to.
     *
     * @returns
     * The offsets of the guiElement.
     */
    static getOffsets(guiElement, referenceElement) {
        const offset = { left: 0, top: 0, right: 0, bottom: 0 };
        if (!guiElement.container) {
            return offset;
        }
        const guiElementClientRect = guiElement.container.getBoundingClientRect();
        const referenceClientRect = referenceElement ?
            referenceElement.getBoundingClientRect() : { left: 0, top: 0 };
        offset.left = guiElementClientRect.left - referenceClientRect.left;
        offset.top = guiElementClientRect.top - referenceClientRect.top;
        offset.right =
            guiElementClientRect.right - referenceClientRect.left;
        offset.bottom =
            guiElementClientRect.bottom - referenceClientRect.top;
        return offset;
    }
    /**
     * Get dimensions of the guiElement container from offsets.
     *
     * @param offsets
     * The offsets of the guiElement container.
     *
     * @returns
     * The dimensions of the guiElement container.
     */
    static getDimFromOffsets(offsets) {
        return {
            width: offsets.right - offsets.left,
            height: offsets.bottom - offsets.top
        };
    }
    /**
     * Based on the element provided, generate an unique id.
     *
     * @param elementType
     * Type of the element.
     *
     * @returns
     * The unique id.
     */
    static getElementId(elementType) {
        return (Dashboards_Globals.classNamePrefix + elementType + '-' +
            uniqueKey().slice(11));
    }
    /**
     * Get width in percentages (0% - 100%).
     *
     * @param width
     * The width of the element. Supported formats '50%' or '1/2'.
     *
     * @returns
     * The width in percentages.
     */
    static getPercentageWidth(width) {
        const fractionRegEx = /^(\d{1})[\-\/\.](\d{1,2})$/;
        let result;
        if (fractionRegEx.test(width)) {
            const match = width.match(fractionRegEx) || [], multiplier = +match[1], divider = +match[2];
            result = 100 * multiplier / divider;
            result = (result <= 100 ? result : 100) + '%';
        }
        else if (width.indexOf('%') !== -1) {
            const value = parseFloat(width);
            result = (value <= 100 ?
                (value >= 0 ? value : 0) : 100) + '%';
        }
        return result;
    }
    /* *
    *
    *  Functions
    *
    * */
    /**
     * Create or get existing HTML element as a GUIElement container.
     *
     * @param {GetElementContainerOptions} options
     * Options.
     *
     * @returns
     * The HTML element for the element container.
     */
    getElementContainer(options) {
        const guiElement = this;
        let elem;
        if (options.render) {
            if (options.attribs && !options.attribs.id) {
                delete options.attribs.id;
            }
        }
        else if (typeof options.elementId === 'string') {
            const div = document.getElementById(options.elementId);
            if (div) {
                guiElement.container = div;
            }
            else {
                error('Element ' + options.elementId + ' does not exist');
            }
        }
        if (options.element instanceof HTMLElement) {
            elem = options.element;
        }
        else {
            elem = createElement('div', options.attribs || {}, options.style || {}, options.parentContainer);
        }
        // Set bindedGUIElement event on GUIElement container.
        guiElement.removeBindedEventFn = addEvent(elem, 'bindedGUIElement', function (e) {
            e.guiElement = guiElement;
            e.stopImmediatePropagation();
        });
        return elem;
    }
    /**
     * Destroy the element, its container, event hooks and all properties.
     */
    destroy() {
        const guiElement = this;
        // Remove bindedGUIElement event.
        if (guiElement.removeBindedEventFn) {
            guiElement.removeBindedEventFn();
        }
        // Remove HTML container.
        if (guiElement.container && guiElement.container.parentNode) {
            guiElement.container.parentNode.removeChild(guiElement.container);
        }
        // Delete all properties.
        objectEach(guiElement, function (val, key) {
            delete guiElement[key];
        });
    }
    /**
     * Return the GUIElement instance type.
     *
     * @returns
     * The GUIElement instance type
     */
    getType() {
        return this.type;
    }
    changeVisibility(setVisible = true, displayStyle) {
        const visibilityChanged = (this.isVisible && !setVisible ||
            !this.isVisible && setVisible);
        if (this.container && visibilityChanged) {
            this.container.style.display = (setVisible ?
                (displayStyle || 'block') :
                'none');
            this.isVisible = setVisible;
        }
    }
    hide() {
        this.changeVisibility(false);
    }
    show() {
        this.changeVisibility();
    }
}
/* harmony default export */ const Layout_GUIElement = (GUIElement);

;// ./code/dashboards/es-modules/Dashboards/Layout/CellHTML.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *
 * */




/* *
 *
 *  Class
 *
 * */
/**
 * @internal
 **/
class CellHTML extends Layout_GUIElement {
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Constructs an instance of the CellHTML class.
     *
     * @param {Options} options
     * Options for the cell.
     */
    constructor(options) {
        super();
        /**
         * The type of a GUIElement instance.
         */
        this.type = 'cell-html';
        this.options = options;
        this.id = options.id;
        this.container = options.container;
        this.mountedComponent = options.mountedComponent;
    }
    /**
     * Destroy the element, its container, event hooks
     * and mounted component.
     */
    destroy() {
        const cell = this;
        // Destroy mounted component.
        cell.mountedComponent?.destroy();
        super.destroy();
    }
    /**
     * Highlight the cell.
     */
    setHighlight() {
        const cell = this;
        cell.container.classList.toggle(EditMode_EditGlobals.classNames.cellEditHighlight);
        cell.mountedComponent?.board.container.classList.toggle(EditMode_EditGlobals.classNames.dashboardCellEditHighlightActive);
    }
    setActiveState() {
        const cell = this;
        // Apply class
        if (cell.container) {
            cell.container.classList.add(Dashboards_Globals.classNames.cellActive);
        }
    }
}
/* *
 *
 *  Type Declarations
 *
 * */
/**
 * Checks if a valid cell HTML instance.
 */
function isCellHTML(cellHTML) {
    return cellHTML instanceof CellHTML;
}
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Layout_CellHTML = (CellHTML);

;// ./code/dashboards/es-modules/Dashboards/Actions/Bindings.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *
 * */





/* *
 *
 *  Functions
 *
 * */
function getGUIElement(idOrElement, parentElement) {
    let guiElement;
    if (typeof idOrElement === 'string' &&
        document.querySelectorAll('#' + idOrElement).length > 1) {
        // eslint-disable-next-line no-console
        console.warn(`Multiple cells have identical ID %c${idOrElement}%c, potentially leading to unexpected behavior. \nEnsure that each cell has a unique ID on the page.`, 'font-weight: bold', '');
    }
    const container = parentElement ?
        parentElement.querySelector('#' + idOrElement) :
        document.getElementById(idOrElement);
    if (container !== null) {
        fireEvent(container, 'bindedGUIElement', {}, function (e) {
            guiElement = e.guiElement;
        });
    }
    return guiElement;
}
async function addComponent(options, board, cell) {
    const optionsStates = options.states;
    const optionsEvents = options.events;
    const renderTo = options.renderTo;
    if (!renderTo) {
        // eslint-disable-next-line no-console
        console.error('The%c renderTo%c option is required to render the component.', 'font-weight: bold', '');
        return;
    }
    if (board.mountedComponents.filter((el) => el.options.renderTo === renderTo).length > 0) {
        // eslint-disable-next-line no-console
        console.error(`A component has already been declared in the cell %c${renderTo}%c use a different cell.`, 'font-weight: bold', '');
        return;
    }
    cell = cell || Bindings.getCell(renderTo, board.container);
    const componentContainer = cell?.container || document.querySelector('#' + renderTo);
    if (!componentContainer || !options.type) {
        // eslint-disable-next-line no-console
        console.error(`The component is unable to find the HTML cell element %c${renderTo}%c to render the content.`, 'font-weight: bold', '');
        return;
    }
    let ComponentClass = Components_ComponentRegistry.types[options.type];
    if (!ComponentClass) {
        // eslint-disable-next-line no-console
        console.error(`The component's type %c${options.type}%c does not exist.`, 'font-weight: bold', '');
        if (cell) {
            ComponentClass =
                Components_ComponentRegistry.types['HTML'];
            options.title = {
                text: board.editMode?.lang.errorMessage ||
                    'Something went wrong',
                className: Dashboards_Globals.classNamePrefix + 'component-title-error ' +
                    Dashboards_Globals.classNamePrefix + 'component-title'
            };
        }
    }
    const component = new ComponentClass(cell, options, board);
    const promise = component.load()['catch']((e) => {
        // eslint-disable-next-line no-console
        console.error(e);
        return component.update({
            connector: {
                id: ''
            },
            title: {
                text: board.editMode?.lang.errorMessage ||
                    'Something went wrong',
                className: Dashboards_Globals.classNamePrefix + 'component-title-error ' +
                    Dashboards_Globals.classNamePrefix + 'component-title'
            }
        });
    });
    if (cell) {
        component.setCell(cell);
        cell.mountedComponent = component;
    }
    board.mountedComponents.push({
        options: options,
        component: component,
        cell: cell || new Layout_CellHTML({
            id: renderTo,
            container: componentContainer,
            mountedComponent: component
        })
    });
    if (cell &&
        optionsStates?.active?.enabled &&
        optionsStates?.active?.isActive) {
        cell.setActiveState();
        component.isActive = true;
    }
    fireEvent(component, 'mount');
    // Events
    addEvent(componentContainer, 'click', () => {
        // Call the component's click callback
        if (optionsEvents && optionsEvents.click) {
            optionsEvents.click.call(component);
        }
        // Default behavior
        if (cell &&
            component &&
            componentContainer &&
            optionsStates?.active?.enabled) {
            cell.setActiveState();
            component.isActive = true;
        }
    });
    // States
    if (optionsStates?.hover?.enabled) {
        componentContainer.classList.add(Dashboards_Globals.classNames.cellHover);
    }
    fireEvent(component, 'afterLoad');
    return promise;
}
function getCell(idOrElement, parentElement) {
    const cell = getGUIElement(idOrElement, parentElement);
    if (!(cell && cell.getType() === 'cell')) {
        return;
    }
    return cell;
}
function getRow(idOrElement, parentElement) {
    const row = getGUIElement(idOrElement, parentElement);
    if (!(row && row.getType() === 'row')) {
        return;
    }
    return row;
}
function getLayout(idOrElement, parentElement) {
    const layout = getGUIElement(idOrElement, parentElement);
    if (!(layout && layout.getType() === 'layout')) {
        return;
    }
    return layout;
}
/* *
 *
 *  Default Export
 *
 * */
const Bindings = {
    addComponent,
    getCell,
    getLayout,
    getRow
};
/* harmony default export */ const Actions_Bindings = (Bindings);

;// ./code/dashboards/es-modules/Dashboards/Accessibility/DashboardsAccessibility.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *
 * */

/* *
 *
 *  Functions
 *
 * */
/* *
 *
 *  Class
 *
 * */
class DashboardsAccessibility {
    /* *
    *
    *  Constructor
    *
    * */
    constructor(board) {
        this.board = board;
        this.addTabIndexToCells();
    }
    /* *
    *
    *  Functions
    *
    * */
    addTabIndexToCells() {
        const components = this.board.mountedComponents;
        let cell;
        for (let i = 0, iEnd = components.length; i < iEnd; ++i) {
            cell = components[i].cell;
            if (cell && cell.container) {
                cell.container.setAttribute('tabindex', -1);
            }
        }
    }
}
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Accessibility_DashboardsAccessibility = (DashboardsAccessibility);

;// ./code/dashboards/es-modules/Data/DataPool.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */



/* *
 *
 *  Class
 *
 * */
/**
 * Data pool to load connectors on-demand.
 *
 * @class
 * @name Data.DataPool
 *
 * @param {DataPoolOptions} options
 * Pool options with all connectors.
 */
class DataPool {
    /* *
     *
     *  Constructor
     *
     * */
    constructor(options) {
        this.options = merge(DataPool.defaultOptions, options);
        this.connectors = {};
        this.waiting = {};
    }
    /* *
     *
     *  Methods
     *
     * */
    /**
     * Emits an event on this data pool to all registered callbacks of the given
     * event.
     *
     * @param {DataTableEvent} e
     * Event object with event information.
     */
    emit(e) {
        fireEvent(this, e.type, e);
    }
    /**
     * Loads the connector.
     *
     * @function Data.DataPool#getConnector
     *
     * @param {string} connectorId
     * ID of the connector.
     *
     * @return {Promise<Data.DataConnectorType>}
     * Returns the connector.
     */
    getConnector(connectorId) {
        const connector = this.connectors[connectorId];
        // Already loaded
        if (connector?.loaded) {
            return Promise.resolve(connector);
        }
        let waitingList = this.waiting[connectorId];
        // Start loading
        if (!waitingList) {
            waitingList = this.waiting[connectorId] = [];
            const connectorOptions = this.getConnectorOptions(connectorId);
            if (!connectorOptions) {
                throw new Error(`Connector '${connectorId}' not found.`);
            }
            // eslint-disable-next-line @typescript-eslint/no-floating-promises
            this
                .loadConnector(connectorOptions)
                .then((connector) => {
                delete this.waiting[connectorId];
                for (let i = 0, iEnd = waitingList.length; i < iEnd; ++i) {
                    waitingList[i][0](connector);
                }
            })['catch']((error) => {
                delete this.waiting[connectorId];
                for (let i = 0, iEnd = waitingList.length; i < iEnd; ++i) {
                    waitingList[i][1](error);
                }
            });
        }
        // Add request to waiting list
        return new Promise((resolve, reject) => {
            waitingList.push([resolve, reject]);
        });
    }
    /**
     * Returns the IDs of all connectors.
     *
     * @private
     *
     * @return {Array<string>}
     * Names of all connectors.
     */
    getConnectorIds() {
        const connectors = this.options.connectors, connectorIds = [];
        for (let i = 0, iEnd = connectors.length; i < iEnd; ++i) {
            connectorIds.push(connectors[i].id);
        }
        return connectorIds;
    }
    /**
     * Loads the options of the connector.
     *
     * @private
     *
     * @param {string} connectorId
     * ID of the connector.
     *
     * @return {DataConnectorTypeOptions | undefined}
     * Returns the options of the connector, or `undefined` if not found.
     */
    getConnectorOptions(connectorId) {
        const connectors = this.options.connectors;
        for (let i = 0, iEnd = connectors.length; i < iEnd; ++i) {
            if (connectors[i].id === connectorId) {
                return connectors[i];
            }
        }
    }
    /**
     * Tests whether the connector has never been requested.
     *
     * @param {string} connectorId
     * Name of the connector.
     *
     * @return {boolean}
     * Returns `true`, if the connector has never been requested, otherwise
     * `false`.
     */
    isNewConnector(connectorId) {
        return !this.connectors[connectorId];
    }
    /**
     * Instantiates the connector class for the given options and loads its
     * data.
     *
     * @private
     *
     * @param {Data.DataPoolConnectorOptions} options
     * Options of connector.
     *
     * @return {Promise<Data.DataConnectorType>}
     * Returns the connector.
     */
    loadConnector(options) {
        return new Promise((resolve, reject) => {
            this.emit({
                type: 'load',
                options
            });
            const ConnectorClass = Connectors_DataConnector.types[options.type];
            if (!ConnectorClass) {
                throw new Error(`Connector type not found. (${options.type})`);
            }
            const connector = this.connectors[options.id] =
                new ConnectorClass(options);
            // eslint-disable-next-line @typescript-eslint/no-floating-promises
            connector
                .load()
                .then(({ converter }) => {
                connector.converter = converter;
                connector.loaded = true;
                this.emit({
                    type: 'afterLoad',
                    options
                });
                resolve(connector);
            })['catch'](reject);
        });
    }
    /**
     * Cancels all data connectors pending requests.
     */
    cancelPendingRequests() {
        const { connectors } = this;
        for (const connectorKey of Object.keys(connectors)) {
            connectors[connectorKey].stopPolling();
        }
    }
    /**
     * Registers a callback for a specific event.
     *
     * @function Highcharts.DataPool#on
     *
     * @param {string} type
     * Event type as a string.
     *
     * @param {Highcharts.EventCallbackFunction<Highcharts.DataPool>} callback
     * Function to register for an event callback.
     *
     * @return {Function}
     * Function to unregister callback from the event.
     */
    on(type, callback) {
        return addEvent(this, type, callback);
    }
    /**
     * Sets connector options under the specified `options.id`.
     *
     * @param {object} options
     * Connector options to set.
     *
     * @param {boolean} [update]
     * Whether to update the existing connector with the new options and reload
     * it (`true`) or replace it with a new connector instance (`false`).
     */
    async setConnectorOptions(options, update) {
        const connectorsOptions = this.options.connectors;
        const connectorsInstances = this.connectors;
        this.emit({
            type: 'setConnectorOptions',
            options
        });
        for (let i = 0, iEnd = connectorsOptions.length; i < iEnd; ++i) {
            if (connectorsOptions[i].id === options.id) {
                connectorsOptions.splice(i, 1);
                break;
            }
        }
        let existingConnector = connectorsInstances[options.id];
        if (existingConnector) {
            if (update) {
                await existingConnector.update(options, true);
            }
            else {
                existingConnector.stopPolling();
                existingConnector = void 0;
                delete connectorsInstances[options.id];
            }
        }
        if (!existingConnector) {
            connectorsOptions.push(options);
        }
        this.emit({
            type: 'afterSetConnectorOptions',
            options
        });
    }
}
/* *
 *
 *  Static Properties
 *
 * */
DataPool.defaultOptions = {
    connectors: []
};
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Data_DataPool = (DataPool);

;// ./code/dashboards/es-modules/Dashboards/Defaults.js
/* *
 *
 *  Dashboards default options
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 * */


/**
 * Default options for the Board.
 */
const defaultOptions = {
    gui: {
        enabled: true,
        layoutOptions: {
            rowClassName: void 0,
            cellClassName: void 0
        },
        layouts: []
    },
    components: []
};
/**
 * Merge the default options with custom options. Commonly used for defining
 * reusable templates.
 *
 * @param options
 * The new custom board options.
 */
function setOptions(options) {
    merge(true, defaultOptions, options);
}
/* *
 *
 *  Default Export
 *
 * */
const Defaults = {
    defaultOptions,
    setOptions
};
/* harmony default export */ const Dashboards_Defaults = (Defaults);

;// ./code/dashboards/es-modules/Dashboards/Layout/Cell.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *
 * */





/* *
 *
 *  Class
 *
 * */
/**
 * @internal
 **/
class Cell extends Layout_GUIElement {
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Constructs an instance of the Cell class.
     *
     * @param {Row} row
     * Reference to the row instance.
     *
     * @param {Options} options
     * Options for the cell.
     *
     * @param {HTMLElement} cellElement
     * The container of the cell HTML element.
     */
    constructor(row, options, cellElement) {
        super();
        /**
         * The type of GUI element.
         */
        this.type = Dashboards_Globals.guiElementType.cell;
        this.id = options.id;
        this.options = options;
        this.row = row;
        this.isVisible = true;
        // Get parent container
        const parentContainer = document.getElementById(options.parentContainerId || '') ||
            row.container;
        const layoutOptions = row.layout.options || {}, rowOptions = row.options || {}, cellClassName = layoutOptions.cellClassName || '';
        const cellStyle = options.style || {};
        const elementStyle = merge(layoutOptions.style, rowOptions.style, cellStyle);
        this.applySizeOptions(options, cellStyle, elementStyle);
        this.container = this.getElementContainer({
            render: row.layout.board.guiEnabled,
            parentContainer: parentContainer,
            attribs: {
                id: options.id,
                className: Dashboards_Globals.classNames.cell + ' ' +
                    cellClassName
            },
            element: cellElement,
            elementId: options.id,
            style: elementStyle
        });
        // Nested layout
        if (this.options.layout) {
            this.setNestedLayout();
        }
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Create a nested layout in the cell and assign it to the nestedCell
     * property.
     * @internal
     */
    setNestedLayout() {
        const board = this.row.layout.board, Layout = this.row.layout.constructor;
        const optionsGui = board.options.gui;
        this.nestedLayout = new Layout(board, merge({}, optionsGui && optionsGui.layoutOptions, this.options.layout, {
            parentContainerId: this.options.id
        }), this);
    }
    /**
     * Destroy the element, its container, event hooks
     * and mounted component.
     */
    destroy() {
        fireEvent(this, 'outdate');
        const cell = this;
        const { row } = cell;
        // Destroy mounted component.
        cell.mountedComponent?.destroy();
        // If layout exists in the cell - destroy it
        cell.nestedLayout?.destroy();
        row.unmountCell(cell);
        const destroyRow = row.cells?.length === 0;
        super.destroy();
        if (destroyRow) {
            row.destroy();
        }
    }
    /**
     * Get the cell's options.
     * @returns
     * The JSON of cell's options.
     *
     * @internal
     *
     */
    getOptions() {
        const cell = this;
        if (cell.options.layout && cell.nestedLayout) {
            return {
                ...cell.options,
                layout: cell.nestedLayout.getOptions()
            };
        }
        return cell.options;
    }
    changeVisibility(setVisible = true) {
        super.changeVisibility(setVisible);
        const cell = this, row = cell.row;
        // Change row visibility if needed.
        if (!cell.row.getVisibleCells().length) {
            cell.row.hide();
        }
        else if (cell.isVisible && !row.isVisible) {
            cell.row.show();
        }
        setTimeout(() => {
            fireEvent(row, 'cellChange', { row, cell });
        }, 0);
    }
    getParentCell(level) {
        const cell = this;
        let parentCell;
        if (level <= cell.row.layout.level) {
            if (cell.row.layout.level === level) {
                return cell;
            }
            if (cell.row.layout.level - 1 >= 0) {
                parentCell = cell.row.layout.parentCell;
                if (parentCell) {
                    return parentCell.getParentCell(level);
                }
            }
        }
    }
    // Method to get array of overlapping levels.
    getOverlappingLevels(align, levelMaxGap, // Max distance between levels
    offset // Analyzed cell offset
    ) {
        const cell = this, parentCell = cell.row.layout.parentCell;
        let levels = [cell.row.layout.level];
        if (parentCell) {
            const cellOffset = offset || Layout_GUIElement.getOffsets(cell)[align];
            const parentCellOffset = Layout_GUIElement.getOffsets(parentCell)[align];
            if (Math.abs(cellOffset - parentCellOffset) < levelMaxGap) {
                levels = [
                    ...levels,
                    ...parentCell.getOverlappingLevels(align, levelMaxGap, parentCellOffset)
                ];
            }
        }
        return levels;
    }
    /**
     * Set cell size.
     *
     * @param width
     * % value or 'auto' or px
     *
     * @param height
     * value in px
     */
    setSize(width, height) {
        const cell = this, editMode = cell.row.layout.board.editMode;
        if (cell.container) {
            if (defined(width)) {
                if (width === 'auto' &&
                    cell.container.style.flex !== '1 1 0%') {
                    cell.container.style.flex = '1 1 0%';
                    cell.options.width = cell.container.style.flex;
                }
                else {
                    const cellWidth = cell.convertWidthToValue(width);
                    if (cellWidth) {
                        cell.container.style.flex = '0 0 ' + cellWidth;
                        cell.options.width = cell.container.style.flex;
                    }
                }
            }
            if (defined(height)) {
                const heightValue = (typeof height === 'number' ?
                    height + 'px' :
                    height);
                cell.height = cell.container.style.height = heightValue;
                cell.options.height = heightValue;
            }
            if (editMode) {
                editMode.hideContextPointer();
                if (editMode.cellToolbar &&
                    editMode.cellToolbar.isVisible) {
                    if (editMode.cellToolbar.cell === cell) {
                        editMode.cellToolbar.showToolbar(cell);
                    }
                    else {
                        editMode.cellToolbar.hide();
                    }
                }
            }
            // Call cellResize board event.
            fireEvent(cell.row.layout.board, 'cellResize', { cell: cell });
            fireEvent(cell.row, 'cellChange', { cell: cell, row: cell.row });
        }
    }
    setHighlight(remove) {
        const cell = this, editMode = cell.row?.layout.board.editMode;
        if (cell.container && editMode) {
            const cnt = cell.container, isSet = cnt.classList.contains(EditMode_EditGlobals.classNames.cellEditHighlight);
            if (!remove && !isSet) {
                cnt.classList.add(EditMode_EditGlobals.classNames.cellEditHighlight);
                cell.row.layout.board.container.classList.add(EditMode_EditGlobals.classNames.dashboardCellEditHighlightActive);
                cell.isHighlighted = true;
            }
            else if (remove && isSet) {
                cnt.classList.remove(EditMode_EditGlobals.classNames.cellEditHighlight);
                cell.row.layout.board.container.classList.remove(EditMode_EditGlobals.classNames.dashboardCellEditHighlightActive);
                cell.isHighlighted = false;
            }
        }
    }
    /**
     * Sets the active state of the cell and resets the state of other cells.
     */
    setActiveState() {
        const cell = this;
        // Reset other boxes
        cell.row.layout.board.mountedComponents.forEach((mountedComponent) => {
            if (mountedComponent.cell.container) {
                mountedComponent.cell.container.classList.remove(Dashboards_Globals.classNames.cellActive);
            }
            mountedComponent.component.isActive = false;
        });
        // Apply class
        if (cell.container) {
            cell.container.classList.add(Dashboards_Globals.classNames.cellActive);
        }
    }
    /**
     * Enables or disables the loading indicator in the cell.
     *
     * @internal
     */
    setLoadingState(enabled = true) {
        this.container?.classList?.toggle(Dashboards_Globals.classNames.cellLoading, enabled);
    }
    convertWidthToValue(width) {
        if (typeof width === 'number') {
            return width + 'px';
        }
        if (/px/.test(width)) {
            return width;
        }
        return Layout_GUIElement.getPercentageWidth(width) || '';
    }
    applySizeOptions(options, cellStyle, elementStyle) {
        const heightValue = defined(options.height) ?
            options.height :
            cellStyle.height;
        if (defined(heightValue)) {
            this.height = typeof heightValue === 'number' ?
                heightValue + 'px' :
                String(heightValue);
            elementStyle.height = this.height;
            options.height = this.height;
        }
        else if (defined(elementStyle.height)) {
            delete elementStyle.height;
        }
        const widthSource = defined(options.width) ?
            options.width :
            cellStyle.flex;
        if (defined(widthSource) &&
            (typeof widthSource === 'string' ||
                typeof widthSource === 'number')) {
            let flexValue;
            if (typeof widthSource === 'string' &&
                widthSource.indexOf(' ') !== -1) {
                flexValue = widthSource;
            }
            else if (widthSource === 'auto') {
                flexValue = '1 1 0%';
            }
            else {
                const cellWidth = this.convertWidthToValue(widthSource);
                if (cellWidth) {
                    flexValue = '0 0 ' + cellWidth;
                }
            }
            if (flexValue) {
                elementStyle.flex = flexValue;
                options.width = flexValue;
            }
        }
    }
}
/**
 * Checks if a valid cell instance.
 */
function isCell(cell) {
    return (!!cell && 'row' in cell && cell.type === 'cell');
}
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Layout_Cell = (Cell);

;// ./code/dashboards/es-modules/Dashboards/Layout/Row.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *
 * */






/**
 * @internal
 **/
class Row extends Layout_GUIElement {
    /* *
    *
    *  Static Properties
    *
    * */
    static setContainerHeight(rowContainer, height) {
        if (height) {
            rowContainer.style.height = height + 'px';
        }
    }
    /* *
    *
    *  Constructor
    *
    * */
    /**
     * Constructs an instance of the Row class.
     *
     * @param {Layout} layout
     * Reference to the layout instance.
     *
     * @param {Options} options
     * Options for the row.
     *
     * @param {HTMLElement} rowElement
     * The container of the row HTML element.
     */
    constructor(layout, options, rowElement) {
        super();
        /**
         * The type of GUI element.
         */
        this.type = Dashboards_Globals.guiElementType.row;
        this.layout = layout;
        this.cells = [];
        this.options = options;
        this.isVisible = true;
        // Get parent container
        const parentContainer = document.getElementById(options.parentContainerId || '') ||
            layout.container;
        const layoutOptions = (layout.options || {}), rowClassName = layoutOptions.rowClassName || '';
        this.container = this.getElementContainer({
            render: layout.board.guiEnabled,
            parentContainer: parentContainer,
            attribs: {
                id: options.id,
                className: Dashboards_Globals.classNames.row + ' ' +
                    rowClassName
            },
            element: rowElement,
            elementId: options.id,
            style: merge(layoutOptions.style, options.style)
        });
        // Init rows from options.
        if (this.options.cells) {
            this.setCells();
        }
    }
    /* *
    *
    *  Functions
    *
    * */
    /**
     * Set the row cells using cell options or cellClassName.
     */
    setCells() {
        const row = this, cellClassName = (row.layout.options || {}).cellClassName || '', cellsElements = Utilities_pick(row.options.cells, row.container && row.container.getElementsByClassName(cellClassName)) || [];
        let cellElement, i, iEnd;
        for (i = 0, iEnd = cellsElements.length; i < iEnd; ++i) {
            cellElement = cellsElements[i];
            row.addCell(row.layout.board.guiEnabled ? cellElement : { id: '' }, cellElement instanceof HTMLElement ? cellElement : void 0);
        }
    }
    /**
     * Add a new Cell instance to the row cells array.
     *
     * @param {CellOptions} [options]
     * Options for the row cell.
     *
     * @param {HTMLElement} [cellElement]
     * The container for a new cell HTML element.
     *
     * @return {Cell}
     * Returns the Cell object.
     */
    addCell(options, cellElement, index) {
        const row = this, cell = new Layout_Cell(row, options, cellElement);
        if (!defined(index)) {
            row.cells.push(cell);
        }
        else {
            row.mountCell(cell, index);
        }
        // Set editMode events.
        if (row.layout.board.editMode) {
            row.layout.board.editMode.setCellEvents(cell);
        }
        return cell;
    }
    /**
     * Destroy the element, its container, event hooks
     * and inner cells.
     */
    destroy() {
        const row = this;
        const { layout } = row;
        const board = row.layout.board;
        const editMode = board.editMode;
        // Destroy cells.
        if (row.cells) {
            // Copy to avoid problem with index when shifting array of cells
            // during the destroy.
            const rowCells = [...row.cells];
            for (let i = 0, iEnd = rowCells.length; i < iEnd; ++i) {
                if (rowCells[i]) {
                    rowCells[i].destroy();
                }
            }
        }
        if (row.layout) {
            row.layout.unmountRow(row);
            super.destroy();
            if (layout.rows?.length === 0) {
                layout.destroy();
            }
        }
        fireEvent(editMode, 'rowDestroyed', {
            target: row,
            board: board
        });
    }
    /**
     * Get the row's options.
     * @returns
     * The JSON of row's options.
     *
     * @internal
     *
     */
    getOptions() {
        const row = this, cells = [];
        for (let i = 0, iEnd = row.cells.length; i < iEnd; ++i) {
            cells.push(row.cells[i].getOptions());
        }
        return {
            id: this.options.id,
            style: this.options.style,
            cells
        };
    }
    setSize(height) {
        Row.setContainerHeight(this.container, height);
    }
    // Get cell index from the row.cells array.
    getCellIndex(cell) {
        for (let i = 0, iEnd = this.cells?.length; i < iEnd; ++i) {
            if (this.cells[i].id === cell.id) {
                return i;
            }
        }
    }
    // Add cell to the row.cells array and move cell container.
    mountCell(cell, index = 0) {
        const row = this, nextCell = row.cells[index], prevCell = row.cells[index - 1];
        if (cell.container) {
            if (nextCell && nextCell.container) {
                nextCell.container.parentNode.insertBefore(cell.container, nextCell.container);
            }
            else if (prevCell && prevCell.container) {
                prevCell.container.parentNode.insertBefore(cell.container, prevCell.container.nextSibling);
            }
            else if (!prevCell && !nextCell && row.container) {
                row.container.appendChild(cell.container);
            }
            row.cells.splice(index, 0, cell);
            cell.row = row;
            setTimeout(() => {
                fireEvent(row, 'cellChange', { row, cell });
            }, 0);
        }
    }
    // Remove cell from the row.cells array.
    unmountCell(cell) {
        const cellIndex = this.getCellIndex(cell);
        if (defined(cellIndex)) {
            this.cells.splice(cellIndex, 1);
        }
        setTimeout(() => {
            fireEvent(this, 'cellChange', { row: this, cell });
        }, 0);
    }
    getVisibleCells() {
        const cells = [];
        for (let i = 0, iEnd = this.cells.length; i < iEnd; ++i) {
            if (this.cells[i].isVisible) {
                cells.push(this.cells[i]);
            }
        }
        return cells;
    }
    changeVisibility(setVisible = true, displayStyle) {
        const row = this;
        super.changeVisibility(setVisible, displayStyle);
        // Change layout visibility if needed.
        if (!row.layout.getVisibleRows().length) {
            row.layout.hide();
        }
        else if (row.isVisible && !row.layout.isVisible) {
            row.layout.show();
        }
    }
    show() {
        this.changeVisibility(true, 'flex');
    }
    setHighlight(remove) {
        const classList = this.container.classList;
        const highlightClass = EditMode_EditGlobals.classNames.rowContextHighlight;
        if (remove === true) {
            classList.remove(highlightClass);
        }
        else {
            classList.toggle(highlightClass, !remove);
        }
    }
    // Row can have cells below each others.
    // This method returns cells split into levels.
    getRowLevels() {
        const row = this, rowLevels = {}, rowLevelsArray = [];
        let cell, cellOffsets;
        for (let k = 0, kEnd = row.cells.length; k < kEnd; ++k) {
            cell = row.cells[k];
            if (cell.isVisible) {
                cellOffsets = Layout_GUIElement.getOffsets(cell);
                if (!rowLevels[cellOffsets.top]) {
                    rowLevels[cellOffsets.top] = {
                        top: cellOffsets.top,
                        bottom: cellOffsets.bottom,
                        cells: []
                    };
                }
                if (rowLevels[cellOffsets.top].bottom < cellOffsets.bottom) {
                    rowLevels[cellOffsets.top].bottom = cellOffsets.bottom;
                }
                rowLevels[cellOffsets.top].cells.push(cell);
            }
        }
        objectEach(rowLevels, (value) => {
            rowLevelsArray.push(value);
        });
        return rowLevelsArray;
    }
    // Get row level with additional info
    // on a specific Y position.
    getRowLevelInfo(posY) {
        const rowLevels = this.getRowLevels();
        let rowLevelInfo;
        for (let i = 0, iEnd = rowLevels.length; i < iEnd; ++i) {
            if (rowLevels[i].top <= posY && rowLevels[i].bottom > posY) {
                rowLevelInfo = {
                    index: i,
                    rowLevels: rowLevels,
                    rowLevel: rowLevels[i]
                };
            }
        }
        return rowLevelInfo;
    }
}
/* harmony default export */ const Layout_Row = (Row);

;// ./code/dashboards/es-modules/Dashboards/Layout/Layout.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *
 * */




/**
 * @internal
 **/
class Layout extends Layout_GUIElement {
    /* *
    *
    *  Constructor
    *
    * */
    /**
     * Constructs an instance of the Layout class.
     *
     * @param {Dashboard} board
     * Reference to the dashboard instance.
     *
     * @param {Options} options
     * Options for the layout.
     */
    constructor(board, options, parentCell) {
        super();
        /**
         * The type of GUI element.
         */
        this.type = Dashboards_Globals.guiElementType.layout;
        this.board = board;
        this.rows = [];
        this.options = options;
        this.isVisible = true;
        // Get parent container
        const parentContainer = parentCell ? parentCell.container :
            document.getElementById(options.parentContainerId || '') || board.layoutsWrapper;
        // Set layout level.
        if (parentCell) {
            this.parentCell = parentCell;
            this.level = parentCell.row.layout.level + 1;
        }
        else {
            this.level = 0;
        }
        // GUI structure
        if (options.copyId) {
            this.copyId = options.copyId;
        }
        const layoutOptions = (this.options || {}), layoutClassName = layoutOptions.rowClassName || '';
        this.container = this.getElementContainer({
            render: board.guiEnabled,
            parentContainer: parentContainer,
            attribs: {
                id: (options.id || '') + (this.copyId ? '_' + this.copyId : ''),
                className: Dashboards_Globals.classNames.layout + ' ' +
                    layoutClassName
            },
            elementId: options.id,
            style: this.options.style
        });
        // Init rows from options.
        if (this.options.rows) {
            this.setRows();
        }
    }
    /* *
    *
    *  Functions
    *
    * */
    /**
     * Set the layout rows using rows options or rowClassName.
     */
    setRows() {
        const layout = this, rowsElements = Utilities_pick(layout.options.rows, layout.container && layout.container.getElementsByClassName(layout.options.rowClassName || '')) || [];
        let rowElement, i, iEnd;
        for (i = 0, iEnd = rowsElements.length; i < iEnd; ++i) {
            rowElement = rowsElements[i];
            layout.addRow(layout.board.guiEnabled ? rowElement : {}, rowElement instanceof HTMLElement ? rowElement : void 0);
        }
    }
    /**
     * Add a new Row instance to the layout rows array.
     *
     * @param {RowOptions} options
     * Options of a row.
     *
     * @param {HTMLElement} rowElement
     * The container for a new row HTML element.
     *
     * @return {Row}
     * Returns the Row object.
     */
    addRow(options, rowElement, index) {
        const layout = this, row = new Layout_Row(layout, options, rowElement);
        if (!defined(index)) {
            layout.rows.push(row);
        }
        else {
            layout.mountRow(row, index);
        }
        // Set editMode events.
        if (layout.board.editMode) {
            layout.board.editMode.setRowEvents(row);
        }
        return row;
    }
    /**
     * Destroy the element, its container, event hooks
     * and inner rows.
     */
    destroy() {
        const layout = this;
        for (let i = layout.board.layouts.length - 1; i >= 0; i--) {
            if (layout.board.layouts[i] === layout) {
                layout.board.layouts.splice(i, 1);
            }
        }
        if (layout.parentCell) {
            delete layout.parentCell.nestedLayout;
        }
        // Destroy rows.
        for (let i = layout.rows.length - 1; i >= 0; i--) {
            layout.rows[i].destroy();
        }
        if (layout.parentCell) {
            layout.parentCell.destroy();
        }
        super.destroy();
    }
    // Get row index from the layout.rows array.
    getRowIndex(row) {
        for (let i = 0, iEnd = this.rows.length; i < iEnd; ++i) {
            if (this.rows[i] === row) {
                return i;
            }
        }
    }
    // Add cell to the layout.rows array and move row container.
    mountRow(row, index) {
        const nextRow = this.rows[index], prevRow = this.rows[index - 1];
        if (row.container) {
            if (nextRow && nextRow.container) {
                nextRow.container.parentNode.insertBefore(row.container, nextRow.container);
            }
            else if (prevRow && prevRow.container) {
                prevRow.container.parentNode.insertBefore(row.container, prevRow.container.nextSibling);
            }
            this.rows.splice(index, 0, row);
            row.layout = this;
        }
    }
    // Remove row from the layout.rows array.
    unmountRow(row) {
        const rowIndex = this.getRowIndex(row);
        if (defined(rowIndex)) {
            this.rows.splice(rowIndex, 1);
        }
    }
    getVisibleRows() {
        const rows = [];
        for (let i = 0, iEnd = this.rows.length; i < iEnd; ++i) {
            if (this.rows[i].isVisible) {
                rows.push(this.rows[i]);
            }
        }
        return rows;
    }
    changeVisibility(setVisible = true) {
        const layout = this;
        super.changeVisibility(setVisible);
        // Change parentCell visibility.
        if (layout.parentCell) {
            if (layout.isVisible && !layout.parentCell.isVisible) {
                layout.parentCell.show();
            }
            else if (!layout.isVisible && layout.parentCell.isVisible) {
                layout.parentCell.hide();
            }
        }
    }
    /**
     * Get the layout's options.
     * @returns
     * Layout's options.
     *
     * @internal
     *
     */
    getOptions() {
        const layout = this, rows = [];
        // Get rows JSON.
        for (let i = 0, iEnd = layout.rows.length; i < iEnd; ++i) {
            rows.push(layout.rows[i].getOptions());
        }
        return {
            id: this.options.id,
            layoutClassName: this.options.layoutClassName,
            rowClassName: this.options.rowClassName,
            cellClassName: this.options.cellClassName,
            style: this.options.style,
            rows
        };
    }
}
/* harmony default export */ const Layout_Layout = (Layout);

;// ./code/dashboards/es-modules/Dashboards/CallbackRegistry.js
class CallbackRegistry {
    constructor() {
        this.registry = {};
    }
    addCallback(id, callback) {
        this.registry[id] = callback;
    }
    getCallback(id) {
        return this.registry[id];
    }
    /** @internal */
    toJSON() {
        const json = {};
        Object.keys(this.registry).forEach((key) => {
            const entry = this.getCallback(key);
            const { func, type } = entry;
            json[key] = {
                func: func.toString(),
                type
            };
        });
        return json;
    }
}
/* harmony default export */ const Dashboards_CallbackRegistry = (CallbackRegistry);

;// ./code/dashboards/es-modules/Dashboards/Components/ConnectorHandler.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Dawid Draguła
 *
 * */


/* *
 *
 *  Class
 *
 * */
/**
 * A class that handles the connection between the component and the data
 * connector.
 */
class ConnectorHandler {
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Creates an object that manages the data layer for the component.
     *
     * @param component
     * The component that the connector is tied to.
     *
     * @param options
     * The options for the connector.
     *
     */
    constructor(component, options) {
        /**
         * Event listeners tied to the current DataTable. Used for rerendering the
         * component on data changes.
         * @internal
         */
        this.tableEvents = [];
        this.component = component;
        this.options = options;
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Inits connectors for the component and rerenders it.
     *
     * @returns
     * Promise resolving to the component.
     */
    async initConnector() {
        const component = this.component;
        const connectorId = this.options.id;
        const dataPool = this.component.board.dataPool;
        if (connectorId &&
            (this.connectorId !== connectorId ||
                dataPool.isNewConnector(connectorId))) {
            if (isCell(component.cell)) {
                component.cell.setLoadingState();
            }
            const connector = await dataPool.getConnector(connectorId);
            // The connector shouldn't be set if the handler was destroyed
            // during its creation.
            if (!this.destroyed) {
                this.setConnector(connector);
            }
        }
        return component;
    }
    /**
     * Sets the data table settings and events.
     *
     * @param table
     * The data table instance for settings and events.
     */
    setTable(table) {
        // Set up event listeners
        this.clearTableListeners(table);
        this.setupTableListeners(table);
        // Re-setup if modifier changes
        table.on('setModifier', () => this.clearTableListeners(table));
        table.on('afterSetModifier', (e) => {
            if (e.type === 'afterSetModifier' && e.modified) {
                this.setupTableListeners(e.modified);
                this.component.emit({
                    type: 'tableChanged',
                    connector: this.connector
                });
            }
        });
        this.dataTable = table;
    }
    /**
     * Sets the connector for the component connector handler.
     *
     * @param connector
     * The connector to set.
     */
    setConnector(connector) {
        // Clean up old event listeners
        while (this.tableEvents.length) {
            const eventCallback = this.tableEvents.pop();
            if (typeof eventCallback === 'function') {
                eventCallback();
            }
        }
        this.connector = connector;
        if (connector) {
            this.setTable(connector.getTable(this.options.dataTableKey));
        }
        this.addConnectorAssignment();
        return this.component;
    }
    /**
     * Adds event listeners to data table.
     * @param table
     * Data table that is source of data.
     * @internal
     */
    setupTableListeners(table) {
        const connector = this.connector;
        if (connector) {
            if (table) {
                [
                    'afterDeleteRows',
                    'afterSetCell',
                    'afterSetColumns',
                    'afterSetRows'
                ].forEach((event) => {
                    this.tableEvents.push(table.on(event, (e) => {
                        clearTimeout(this.tableEventTimeout);
                        this.tableEventTimeout = Dashboards_Globals.win.setTimeout(() => {
                            this.component.emit({
                                ...e,
                                type: 'tableChanged',
                                targetConnector: connector
                            });
                            this.tableEventTimeout = void 0;
                        });
                    }));
                });
            }
        }
    }
    /**
     * Remove event listeners in data table.
     *
     * @param table
     * The connector data table (data source).
     *
     * @internal
     */
    clearTableListeners(table) {
        const connector = this.connector;
        const tableEvents = this.tableEvents;
        this.removeTableEvents();
        if (connector) {
            tableEvents.push(table.on('afterSetModifier', (e) => {
                if (e.type === 'afterSetModifier') {
                    clearTimeout(this.tableEventTimeout);
                    this.tableEventTimeout = Dashboards_Globals.win.setTimeout(() => {
                        this.component.emit({
                            ...e,
                            type: 'tableChanged',
                            targetConnector: connector
                        });
                        this.tableEventTimeout = void 0;
                    });
                }
            }));
        }
    }
    /**
     * Adds the component to the provided connector.
     * Starts the connector polling if inactive and one component is provided.
     */
    addConnectorAssignment() {
        const { connector } = this;
        if (!connector) {
            return;
        }
        if (!connector.components) {
            connector.components = [];
        }
        if (!connector.components.includes(this.component)) {
            const options = connector.options;
            // Add the component assignment.
            connector.components.push(this.component);
            // Start the connector polling.
            if ('enablePolling' in options &&
                options.enablePolling &&
                !connector.polling &&
                connector.components.length === 1 &&
                'dataRefreshRate' in options) {
                connector.startPolling(Math.max(options.dataRefreshRate || 0, 1) * 1000);
            }
        }
    }
    /**
     * Removes the component instance from the provided connector.
     * Stops the connector polling if the last element is removed.
     */
    removeConnectorAssignment() {
        const { connector } = this;
        if (!connector?.components) {
            return;
        }
        const index = connector.components.indexOf(this.component);
        if (index > -1) {
            connector.components.splice(index, 1);
            if (!connector.components.length) {
                connector.stopPolling();
                delete connector.components;
            }
        }
    }
    /**
     * Clears all event listeners in the table.
     */
    removeTableEvents() {
        this.tableEvents.forEach((clearEvent) => clearEvent());
        this.tableEvents.length = 0;
    }
    /**
     * Updates the options for the connector handler.
     *
     * @param newOptions
     * The new options to update.
     */
    updateOptions(newOptions) {
        this.options = newOptions;
    }
    /**
     * Destroys the connector handler.
     * @internal
     */
    destroy() {
        this.destroyed = true;
        this.removeConnectorAssignment();
        this.removeTableEvents();
    }
}
/* harmony default export */ const Components_ConnectorHandler = (ConnectorHandler);

;// ./code/dashboards/es-modules/Dashboards/Components/EditableOptions.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *
 * */
class EditableOptions {
    constructor(component, bindings = EditableOptions.defaultBindings) {
        this.component = component;
        this.bindings = bindings;
    }
    getOptions() {
        const options = this.component.options.editableOptions;
        if (!options) {
            return [];
        }
        for (let i = 0, iEnd = options.length; i < iEnd; i++) {
            const option = options[i];
            if (option.propertyPath?.some((path) => path === 'connector')) {
                const board = this.component.board;
                const selectOptions = !board ?
                    [] :
                    board.dataPool
                        .getConnectorIds()
                        .map((name) => ({ name }));
                option.selectOptions = selectOptions;
            }
        }
        return options;
    }
}
EditableOptions.defaultBindings = {
    keyMap: {
        color: 'colorPicker',
        title: 'text',
        caption: 'text',
        style: 'textarea'
    },
    typeMap: {
        'string': 'text',
        'number': 'input',
        'boolean': 'toggle'
    },
    skipRedraw: []
};
// Bindings of basic types to "editor components"
EditableOptions.defaultTypeMap = {
    'string': 'text',
    'number': 'input',
    'boolean': 'toggle'
};
/* harmony default export */ const Components_EditableOptions = (EditableOptions);

;// ./code/dashboards/es-modules/Dashboards/Components/Sync/Emitter.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *
 * */
/**
 *  Class responsible for adding event listeners on a component
 *  @internal
 */
class SyncEmitter {
    /**
     * Adds an emitter to the emitter registry.
     *
     * @param emitter the emitter to add to the registry.
     */
    static register(emitter) {
        const { id } = emitter;
        this.registry[id] = emitter;
    }
    /**
     * Gets an emitter from emitter registry.
     *
     * @param emitterID The ID of the emitter to get.
     */
    static get(emitterID) {
        return this.registry[emitterID];
    }
    /**
     * Creates a new emitter instance.
     *
     * @param id An unique ID for the emitter.
     *
     * @param func
     * The function to be called when the emitter is activated.
     */
    constructor(id, func) {
        this.id = id;
        this.func = func;
        SyncEmitter.register(this);
    }
    /**
     * Attaches the emitter to a component.
     *
     * @param component The component to attach to.
     */
    create(component) {
        this.callback = this.func.call(component);
    }
    /**
     * To be used when removing the emitter from the component.
     * Calls the {@link callback} function.
     */
    remove() {
        if (this.callback) {
            this.callback();
        }
    }
}
/**
 * Registry for reusable emitter.
 * The emitter is stored by ID.
 */
SyncEmitter.registry = {};
/* harmony default export */ const Emitter = (SyncEmitter);

;// ./code/dashboards/es-modules/Dashboards/Components/Sync/Handler.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *
 * */

/* *
 *
 *  Class
 *
 * */
/**
 * Class responsible for storing handler callbacks used in component sync.
 * @internal
 */
class SyncHandler {
    /**
     * Adds a handler to the handler registry.
     *
     * @param handler
     * The handler to add to the registry.
     */
    static register(handler) {
        const { id } = handler;
        this.registry[id] = handler;
    }
    /**
     * Gets a handler from handler registry.
     *
     * @param handlerID
     * The ID of the handler to get.
     */
    static get(handlerID) {
        return this.registry[handlerID];
    }
    /**
     * Creates a new handler instance.
     *
     * @param id
     * An unique ID for the handler.
     *
     * @param func
     * The function to be called when the handler is activated.
     */
    constructor(id, func) {
        this.id = id;
        this.func = func;
        SyncHandler.register(this);
    }
    /**
     * Calls the activation function on the component and sets the callback to
     * the return function.
     *
     * @param component
     * The component to register on.
     */
    register(component) {
        const { func } = this;
        this.callback = func.call(component);
    }
    /**
     * To be used when removing the handler from the component.
     * Calls the {@link callback} function.
     */
    remove() {
        if (this.callback) {
            this.callback();
        }
    }
}
/**
 * Registry for reusable handlers.
 * The handler is stored by ID.
 */
SyncHandler.registry = {};
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Handler = (SyncHandler);

;// ./code/dashboards/es-modules/Dashboards/Components/Sync/Sync.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *
 * */




/* *
 *
 * Class
 *
 * */
/** @internal */
class Sync {
    /* *
     *
     * Constructor
     *
     * */
    /**
     * Creates an instance of the sync class.
     *
     * @param component
     * The component to which the emitters and handlers are attached.
     *
     * @param predefinedSyncConfig
     * The predefined sync configuration.
     */
    constructor(component, predefinedSyncConfig) {
        this.component = component;
        this.predefinedSyncConfig = predefinedSyncConfig;
        this.syncConfig = Sync.prepareSyncConfig(predefinedSyncConfig, component.options.sync);
        this.registeredSyncHandlers = {};
        this.registeredSyncEmitters = {};
        this.isSyncing = false;
        this.listeners = [];
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Method that prepares the sync configuration from the predefined config
     * and current component options.
     *
     * @param predefinedConfig The predefined sync configuration.
     * @param componentSyncOptions The current component sync options.
     * @returns The sync configuration.
     */
    static prepareSyncConfig(predefinedConfig, componentSyncOptions = {}) {
        const { defaultSyncPairs: defaultPairs, defaultSyncOptions: defaultOptionsList } = predefinedConfig;
        return Object.keys(componentSyncOptions).reduce((acc, syncName) => {
            if (syncName) {
                const defaultPair = defaultPairs[syncName];
                const defaultOptions = defaultOptionsList[syncName];
                const entry = componentSyncOptions[syncName];
                const preparedOptions = merge(defaultOptions || {}, { enabled: isObject(entry) ? entry.enabled : entry }, isObject(entry) ? entry : {});
                if (defaultPair && preparedOptions.enabled) {
                    const keys = [
                        'emitter',
                        'handler'
                    ];
                    for (const key of keys) {
                        if (preparedOptions[key] === true ||
                            preparedOptions[key] === void 0) {
                            preparedOptions[key] =
                                defaultPair[key];
                        }
                    }
                }
                acc[syncName] = preparedOptions;
            }
            return acc;
        }, {});
    }
    /**
     * Add new emitter to the registered emitters.
     *
     * @param emitter
     * The emitter to register.
     */
    registerSyncEmitter(emitter) {
        const { id } = emitter;
        this.registeredSyncEmitters[id] = emitter;
    }
    /**
     * Method that checks if the emitter is registered.
     *
     * @param id
     * The id of the emitter to check.
     *
     * @returns
     * Whether the emitter is registered.
     */
    isRegisteredEmitter(id) {
        return Boolean(this.registeredSyncEmitters[id]);
    }
    /**
     * Register new handler to the registered handlers.
     *
     * @param handler
     * The handler to register.
     */
    registerSyncHandler(handler) {
        const { id } = handler;
        this.registeredSyncHandlers[id] = handler;
    }
    /**
     * Method that checks if the handler is registered.
     *
     * @param handlerID
     * The id of the handler to check.
     *
     * @returns
     * Whether the handler is registered.
     */
    isRegisteredHandler(handlerID) {
        return Boolean(this.registeredSyncHandlers[handlerID]);
    }
    /**
     * Registers the handlers and emitters on the component
     */
    start() {
        const { component } = this;
        this.syncConfig = Sync.prepareSyncConfig(this.predefinedSyncConfig, component.options.sync);
        for (const id of Object.keys(this.syncConfig)) {
            const syncOptions = this.syncConfig[id];
            if (!syncOptions) {
                continue;
            }
            let { emitter: emitterConfig, handler: handlerConfig } = syncOptions;
            if (handlerConfig) {
                if (handlerConfig === true) {
                    handlerConfig =
                        Sync.defaultHandlers[id]
                            .handler;
                }
                const handler = new Handler(id, handlerConfig);
                if (!this.isRegisteredHandler(handler.id)) {
                    this.registerSyncHandler(handler);
                    handler.register(component);
                }
            }
            if (emitterConfig) {
                if (emitterConfig === true) {
                    emitterConfig =
                        Sync.defaultHandlers[id]
                            .emitter;
                }
                const emitter = new Emitter(id, emitterConfig);
                if (!this.isRegisteredEmitter(emitter.id)) {
                    this.registerSyncEmitter(emitter);
                    emitter.create(component);
                }
            }
        }
        this.isSyncing = true;
        this.listeners.push(component.on('update', () => this.stop()));
    }
    /**
     * Removes the handlers and emitters from the component.
     */
    stop() {
        const { component, listeners, registeredSyncHandlers, registeredSyncEmitters } = this;
        Object.keys(registeredSyncHandlers).forEach((id) => {
            registeredSyncHandlers[id].remove();
            delete registeredSyncHandlers[id];
        });
        Object.keys(registeredSyncEmitters).forEach((id) => {
            registeredSyncEmitters[id].remove();
            delete registeredSyncEmitters[id];
        });
        this.isSyncing = false;
        for (let i = 0, iEnd = listeners.length; i < iEnd; ++i) {
            listeners[i]();
        }
        this.listeners.length = 0;
        this.listeners.push(component.on('afterUpdate', () => {
            this.start();
        }));
    }
}
/**
 * Default handlers for the sync class. This property is extended by
 * different Components, where default syncs are added. Allows overwriting
 * the configuration before creating the dashboard.
 */
Sync.defaultHandlers = {};
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Sync_Sync = (Sync);

;// ./code/dashboards/es-modules/Dashboards/Components/ComponentUtilities.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *
 * */

/* *
 *
 *  Functions
 *
 * */
function getMargins(element, includeBorders = true) {
    const borders = {
        x: ['borderLeft', 'borderRight'],
        y: ['borderTop', 'borderBottom']
    };
    return {
        y: getStyles(element, [
            'marginTop',
            'marginBottom',
            ...(includeBorders ? borders.y : [])
        ]).reduce(sumPixels, 0),
        x: getStyles(element, [
            'marginLeft',
            'marginTop',
            ...(includeBorders ? borders.x : [])
        ]).reduce(sumPixels, 0)
    };
}
function getPaddings(element) {
    return {
        x: getStyles(element, ['paddingLeft', 'paddingRight']).reduce(sumPixels, 0),
        y: getStyles(element, ['paddingTop', 'paddingBottom']).reduce(sumPixels, 0)
    };
}
function getStyles(element, styles) {
    const elementStyles = window.getComputedStyle(element);
    return styles.map((style) => elementStyles[style]); // Cannot use getPropertyValue?
}
function sumPixels(accumulator, value) {
    if (value) {
        accumulator += (typeof value === 'number' ? value : parseFloat(value));
    }
    return accumulator;
}
/* *
 *
 *  Default Export
 *
 * */
const ComponentUtilities = {
    getMargins,
    getPaddings,
    getStyles,
    sumPixels
};
/* harmony default export */ const Components_ComponentUtilities = (ComponentUtilities);

;// ./code/dashboards/es-modules/Dashboards/Utilities.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *
 * */


/* *
 *
 *  Functions
 *
 * */
/**
 * Returns a deep copy of an argument. It differs from `merge` in that it copies
 * also arrays.
 *
 * @param value
 * The value to clone.
 *
 * @param excludedKeys
 * An array of keys to exclude from the clone.
 */
function deepClone(value, excludedKeys) {
    if (Array.isArray(value)) {
        return value.map((v) => deepClone(v, excludedKeys));
    }
    if (value && typeof value === 'object') {
        const clone = {};
        const keys = Object.keys(value);
        for (const key of keys) {
            if (excludedKeys && excludedKeys.includes(key)) {
                clone[key] = value[key];
            }
            else {
                clone[key] = deepClone(value[key], excludedKeys);
            }
        }
        return clone;
    }
    return value;
}
/**
 * Provide error messages for debugging, with links to online explanation. This
 * function can be overridden to provide custom error handling.
 *
 * @sample highcharts/chart/highcharts-error/
 *         Custom error handler
 *
 * @function Dashboards.error
 *
 * @param {number|string} code
 *        The error code. See
 *        [errors.xml](https://github.com/highcharts/highcharts/blob/master/errors/errors.xml)
 *        for available codes. If it is a string, the error message is printed
 *        directly in the console.
 *
 * @param {boolean} [stop=false]
 *        Whether to throw an error or just log a warning in the console.
 *
 * @return {void}
 */
function Utilities_error(code, stop) {
    // TODO- replace with proper error handling
    if (code === 16) {
        console.warn(// eslint-disable-line no-console
        'Dashboard error: Dashboards library loaded more than once.' +
            'This may cause undefined behavior.');
        return;
    }
    error(code, stop);
}
/* *
 *
 *  Default Export
 *
 * */
const Utilities = {
    deepClone,
    error: Utilities_error
};
/* harmony default export */ const Dashboards_Utilities = (Utilities);

;// ./code/dashboards/es-modules/Dashboards/Components/Component.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *  - Dawid Draguła
 *
 * */







const { classNamePrefix: Component_classNamePrefix } = Dashboards_Globals;

const { getMargins: Component_getMargins, getPaddings: Component_getPaddings } = Components_ComponentUtilities;



const { deepClone: Component_deepClone } = Dashboards_Utilities;
/* *
 *
 *  Class
 *
 * */
/**
 * Abstract class of component.
 */
class Component {
    /* *
     *
     *  Static Functions
     *
     * */
    /**
     * Creates HTML text element like header or title
     *
     * @param tagName
     * HTML tag name used as wrapper of text like `h2` or `p`.
     *
     * @param elementName
     * Name of element
     *
     * @param textOptions
     * The options for the component
     *
     * @returns
     * HTML object when title is created, otherwise undefined
     *
     * @internal
     */
    static createTextElement(tagName, elementName, textOptions) {
        if (typeof textOptions === 'object') {
            const { className, text, style } = textOptions;
            return createElement(tagName, {
                className: className || `${Component_classNamePrefix}component-${elementName}`,
                textContent: text
            }, style);
        }
        if (typeof textOptions === 'string') {
            return createElement(tagName, {
                className: `${Component_classNamePrefix}component-${elementName}`,
                textContent: textOptions
            }, {});
        }
    }
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Creates a component in the cell.
     *
     * @param cell
     * Instance of cell, where component is attached.
     *
     * @param options
     * The options for the component.
     */
    constructor(cell, options, board) {
        /**
         * The connector handlers for the component. They are used to handle the
         * connector options and data tables.
         */
        this.connectorHandlers = [];
        /**
         * Registry of callbacks registered on the component. Used in the Highcharts
         * component to keep track of chart events.
         * @internal
         */
        this.callbackRegistry = new Dashboards_CallbackRegistry();
        /**
         * Event listeners tied to the parent cell. Used for rendering/resizing the
         * component on interactions.
         * @internal
         */
        this.cellListeners = [];
        /**
         * Timeouts for calls to `Component.resizeTo()`.
         * @internal
         */
        this.resizeTimeouts = [];
        /**
         * Timeouts for resizing the content. I.e. `chart.setSize()`.
         * @internal
         */
        this.innerResizeTimeouts = [];
        const renderTo = options.renderTo;
        this.board = board || cell?.row?.layout?.board || {};
        this.parentElement =
            cell?.container || document.querySelector('#' + renderTo);
        this.cell = cell;
        this.options = merge(Component.defaultOptions, options);
        this.id = this.options.id && this.options.id.length ?
            this.options.id :
            uniqueKey();
        if (this.options.connector) {
            const connectorOptionsArray = isArray(this.options.connector) ?
                this.options.connector :
                [this.options.connector];
            for (const connectorOptions of connectorOptionsArray) {
                this.connectorHandlers.push(new Components_ConnectorHandler(this, connectorOptions));
            }
        }
        this.editableOptions =
            new Components_EditableOptions(this, options.editableOptionsBindings);
        this.dimensions = {
            width: null,
            height: null
        };
        this.element = createElement('div', {
            className: this.options.className
        }, {}, this.parentElement);
        if (!Number(getStyle(this.element, 'padding'))) {
            // Fix flex problem, because of wrong height in internal elements
            this.element.style.padding = '0.1px';
        }
        this.contentElement = createElement('div', {
            className: `${this.options.className}-content`
        }, {}, this.element, true);
        this.sync = new Sync_Sync(this, this.constructor.predefinedSyncConfig);
        this.setupEventListeners();
        if (cell) {
            this.attachCellListeners();
            this.on('update', () => {
                if (isCell(this.cell)) {
                    this.cell.setLoadingState();
                }
            });
            this.on('afterRender', () => {
                if (isCell(this.cell)) {
                    this.cell.setLoadingState(false);
                }
            });
        }
        this.on('tableChanged', () => {
            this.onTableChanged();
        });
    }
    /**
     * Returns the component's options when it is dropped from the sidebar.
     *
     * @param sidebar
     * The sidebar popup.
     */
    getOptionsOnDrop(
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    sidebar) {
        return {};
    }
    /**
     * Returns the first connector of the component if it exists.
     *
     * @internal
     * @deprecated
     */
    getFirstConnector() {
        return this.connectorHandlers[0]?.connector;
    }
    /**
     * Returns the data table connected to the component by the `connectorId`
     * and `dataTableKey`. If both args are undefined, the first data table is
     * returned.
     *
     * @param connectorId
     * The id of the connector.
     *
     * @param dataTableKey
     * The key of the data table within the connector.
     *
     * @returns
     * The data table, or undefined if no matching handler is found.
     */
    getDataTable(connectorId, dataTableKey) {
        for (const handler of this.connectorHandlers) {
            if ((!connectorId ||
                handler.options.id === connectorId) && (!dataTableKey ||
                handler.options.dataTableKey === dataTableKey)) {
                return handler.dataTable;
            }
        }
    }
    /**
     * Setup listeners on cell/other things up the chain
     * @internal
     */
    attachCellListeners() {
        // Remove old listeners
        while (this.cellListeners.length) {
            const destroy = this.cellListeners.pop();
            if (destroy) {
                destroy();
            }
        }
        if (this.cell &&
            isCell(this.cell) &&
            Object.keys(this.cell).length) {
            const board = this.cell.row.layout.board;
            this.cellListeners.push(
            // Listen for resize on dashboard
            addEvent(board, 'cellResize', () => {
                this.resizeTo(this.parentElement);
            }), 
            // Listen for changed parent
            addEvent(this.cell.row, 'cellChange', (e) => {
                const { row } = e;
                if (row && this.cell) {
                    const hasLeftTheRow = row.getCellIndex(this.cell) === void 0;
                    if (hasLeftTheRow) {
                        if (this.cell) {
                            this.setCell(this.cell);
                        }
                    }
                }
            }));
        }
    }
    /**
     * Set a parent cell.
     *
     * @param cell
     * Instance of a cell.
     *
     * @param resize
     * Flag that allow to resize the component.
     *
     * @internal
     */
    setCell(cell, resize = false) {
        this.cell = cell;
        if (cell.container) {
            this.parentElement = cell.container;
        }
        this.attachCellListeners();
        if (resize) {
            this.resizeTo(this.parentElement);
        }
    }
    /**
     * Initializes connector handlers for the component.
     */
    async initConnectors() {
        fireEvent(this, 'setConnectors', {
            connectorHandlers: this.connectorHandlers
        });
        for (const connectorHandler of this.connectorHandlers) {
            await connectorHandler.initConnector();
        }
        fireEvent(this, 'afterSetConnectors', {
            connectorHandlers: this.connectorHandlers
        });
        return this;
    }
    /**
     * Gets height of the component's content.
     *
     * @returns
     * Current height as number.
     *
     * @internal
     */
    getContentHeight() {
        const titleHeight = this.titleElement ?
            this.titleElement.clientHeight + Component_getMargins(this.titleElement).y :
            0;
        const captionHeight = this.captionElement ?
            this.captionElement.clientHeight +
                Component_getMargins(this.captionElement).y :
            0;
        return titleHeight + captionHeight;
    }
    /**
     * Resize the component
     *
     * @param width
     * The width to set the component to.
     * Can be pixels, a percentage string or null.
     * Null will unset the style
     *
     * @param height
     * The height to set the component to.
     * Can be pixels, a percentage string or null.
     * Null will unset the style.
     */
    resize(width, height) {
        if (height) {
            // Get offset for border, padding
            const pad = Component_getPaddings(this.element).y + Component_getMargins(this.element).y;
            this.element.style.height = 'calc(100% - ' + pad + 'px)';
            this.contentElement.style.height =
                'calc(100% - ' + this.getContentHeight() + 'px)';
        }
        else if (height === null) {
            this.dimensions.height = null;
            this.element.style.removeProperty('height');
        }
        fireEvent(this, 'resize', {
            width,
            height
        });
    }
    /**
     * It's a temporary alternative for the `resize` method. It sets the strict
     * pixel height for the component so that the content can be distributed in
     * the right way, without looping the resizers in the content and container.
     *
     * @param width
     * The width to set the component to.
     *
     * @param height
     * The height to set the component to.
     */
    resizeDynamicContent(width, height) {
        const { element } = this;
        if (height) {
            const margins = Component_getMargins(element).y;
            const paddings = Component_getPaddings(element).y;
            if (typeof height === 'string') {
                height = parseFloat(height);
            }
            height = Math.round(height);
            element.style.height = `${height - margins - paddings}px`;
            this.contentElement.style.height = `${element.clientHeight - this.getContentHeight() - paddings}px`;
        }
        else if (height === null) {
            this.dimensions.height = null;
            element.style.removeProperty('height');
        }
        fireEvent(this, 'resize', {
            width,
            height
        });
    }
    /**
     * Adjusts size of component to parent's cell size when animation is done.
     *
     * @param element
     * HTML element that is resized.
     */
    resizeTo(element) {
        while (this.resizeTimeouts.length) {
            const timeout = this.resizeTimeouts.pop();
            if (timeout) {
                cancelAnimationFrame(timeout);
            }
        }
        const timeoutID = requestAnimationFrame(() => {
            const { width, height } = element.getBoundingClientRect();
            const padding = Component_getPaddings(element);
            const margins = Component_getMargins(element);
            this.resize(width - padding.x - margins.x, height - padding.y - margins.y);
        });
        this.resizeTimeouts.push(timeoutID);
    }
    /**
     * Handles updating via options.
     *
     * @param newOptions
     * The options to apply.
     *
     * @param shouldRerender
     * Set to true if the update should rerender the component.
     */
    async update(newOptions, shouldRerender = true) {
        const eventObject = {
            options: newOptions,
            shouldForceRerender: false
        };
        // Update options
        fireEvent(this, 'update', eventObject);
        if (newOptions.connector && Array.isArray(this.options.connector)) {
            this.options.connector = void 0;
        }
        this.options = merge(this.options, newOptions);
        const connectorOptions = (this.options.connector ? (isArray(this.options.connector) ? this.options.connector :
            [this.options.connector]) : []);
        let connectorsHaveChanged = connectorOptions.length !== this.connectorHandlers.length;
        if (!connectorsHaveChanged) {
            for (let i = 0, iEnd = connectorOptions.length; i < iEnd; i++) {
                const oldOptions = this.connectorHandlers[i]?.options;
                const newOptions = connectorOptions[i];
                // Check if the connector id has changed.
                if (oldOptions.id !== newOptions.id) {
                    connectorsHaveChanged = true;
                    break;
                }
                // Check if the data table key has changed.
                if (oldOptions.dataTableKey !== newOptions.dataTableKey) {
                    connectorsHaveChanged = true;
                    break;
                }
                this.connectorHandlers[i].updateOptions(newOptions);
            }
        }
        if (connectorsHaveChanged) {
            for (const connectorHandler of this.connectorHandlers) {
                connectorHandler.destroy();
            }
            this.connectorHandlers.length = 0;
            for (const options of connectorOptions) {
                this.connectorHandlers.push(new Components_ConnectorHandler(this, options));
            }
            await this.initConnectors();
        }
        if (shouldRerender || eventObject.shouldForceRerender) {
            this.render();
        }
    }
    /**
     * Private method which sets up event listeners for the component.
     *
     * @internal
     */
    setupEventListeners() {
        const events = this.options.events;
        if (events) {
            Object.keys(events).forEach((key) => {
                const eventCallback = events[key];
                if (eventCallback) {
                    this.callbackRegistry.addCallback(key, {
                        type: 'component',
                        func: eventCallback
                    });
                }
            });
            objectEach(events, (eventCallback, eventType) => {
                if (isFunction(eventCallback)) {
                    this.on(eventType, eventCallback);
                }
            });
        }
        const resizeObserverCallback = () => {
            this.resizeTo(this.parentElement);
        };
        if (typeof ResizeObserver === 'function') {
            this.resizeObserver = new ResizeObserver(resizeObserverCallback);
            this.resizeObserver.observe(this.element);
        }
        else {
            const unbind = addEvent(window, 'resize', resizeObserverCallback);
            addEvent(this, 'destroy', unbind);
        }
    }
    /**
     * Adds title at the top of component's container.
     *
     * @param titleOptions
     * The options for the title.
     */
    setTitle(titleOptions) {
        const titleElement = this.titleElement, shouldExist = titleOptions &&
            (typeof titleOptions === 'string' || titleOptions.text);
        if (shouldExist) {
            const newTitle = Component.createTextElement('h2', 'title', titleOptions);
            if (newTitle) {
                if (!titleElement) {
                    this.element.insertBefore(newTitle, this.element.firstChild);
                }
                else {
                    titleElement.replaceWith(newTitle);
                }
                this.titleElement = newTitle;
            }
        }
        else {
            if (titleElement) {
                titleElement.remove();
                delete this.titleElement;
                return;
            }
        }
    }
    /**
     * Adds caption at the bottom of component's container.
     *
     * @param captionOptions
     * The options for the caption.
     */
    setCaption(captionOptions) {
        const captionElement = this.captionElement, shouldExist = captionOptions &&
            (typeof captionOptions === 'string' || captionOptions.text);
        if (shouldExist) {
            const newCaption = Component.createTextElement('div', 'caption', captionOptions);
            if (newCaption) {
                if (!captionElement) {
                    this.element.appendChild(newCaption);
                }
                else {
                    captionElement.replaceWith(newCaption);
                }
                this.captionElement = newCaption;
            }
        }
        else {
            if (captionElement) {
                captionElement.remove();
                delete this.captionElement;
                return;
            }
        }
    }
    /**
     * Handles setting things up on initial render.
     *
     * @returns
     * The component for chaining.
     *
     * @internal
     */
    async load() {
        await this.initConnectors();
        this.render();
        return this;
    }
    /**
     * Renders the component.
     *
     * @returns
     * The component for chaining.
     *
     * @internal
     */
    render() {
        this.emit({ type: 'render' });
        this.setTitle(this.options.title);
        this.setCaption(this.options.caption);
        this.resizeTo(this.parentElement);
        return this;
    }
    /**
     * Destroys the component.
     */
    destroy() {
        /**
         * TODO: Should perhaps set an `isActive` flag to false.
         */
        if (this.sync.isSyncing) {
            this.sync.stop();
        }
        while (this.element.firstChild) {
            this.element.firstChild.remove();
        }
        // Call unmount
        fireEvent(this, 'unmount');
        for (const connectorHandler of this.connectorHandlers) {
            connectorHandler.destroy();
        }
        // Used to removed the onTableChanged event.
        removeEvent(this);
        this.element.remove();
    }
    /**
     * Adds an event listener to the component.
     *
     * @param type
     * The type of event to listen for.
     *
     * @param callback
     * The callback to call when the event is triggered.
     *
     * @returns
     * The function to remove the event listener.
     *
     * @internal
     */
    on(type, callback) {
        return addEvent(this, type, callback);
    }
    /** @internal */
    emit(e) {
        if (!e.target) {
            e.target = this;
        }
        fireEvent(this, e.type, e);
    }
    /**
     * Get the component's options.
     * @returns
     * The JSON of component's options.
     *
     * @internal
     *
     */
    getOptions() {
        return diffObjects(this.options, Component.defaultOptions);
    }
    getEditableOptions() {
        const component = this;
        // When refactoring, limit the copied options to the ones that are
        // actually editable to avoid unnecessary memory usage.
        return Component_deepClone(component.options, [
            'dataTable', 'points', 'series', 'data', 'editableOptions'
        ]);
    }
    getEditableOptionValue(propertyPath) {
        const component = this;
        if (!propertyPath) {
            return;
        }
        let result = component.getEditableOptions();
        for (let i = 0, end = propertyPath.length; i < end; i++) {
            if (isArray(result) &&
                propertyPath[0] === 'connector' &&
                result.length > 1) {
                return 'multiple connectors';
            }
            if (!result) {
                return;
            }
            result = result[propertyPath[i]];
            if (result === false &&
                (propertyPath.indexOf('title') >= 0 ||
                    propertyPath.indexOf('subtitle') >= 0 ||
                    propertyPath.indexOf('caption') >= 0)) {
                result = '';
            }
        }
        return result;
    }
}
/* *
 *
 *  Static Properties
 *
 * */
/** @internal */
Component.Sync = Sync_Sync;
/**
 * Predefined sync config for component.
 */
Component.predefinedSyncConfig = {
    defaultSyncOptions: {},
    defaultSyncPairs: {}
};
/**
 * Default options of the component.
 */
Component.defaultOptions = {
    className: `${Component_classNamePrefix}component`,
    id: '',
    title: false,
    caption: false,
    sync: Sync_Sync.defaultHandlers,
    editableOptions: [{
            name: 'title',
            propertyPath: ['title'],
            type: 'input'
        }, {
            name: 'caption',
            propertyPath: ['caption'],
            type: 'input'
        }]
};
/* harmony default export */ const Components_Component = (Component);

;// ./code/dashboards/es-modules/Dashboards/Components/HTMLComponent/HTMLComponentDefaults.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Karol Kołodziej
 *
 * */


/* *
 *
 *  Constants
 *
 * */
const HTMLComponentDefaults = {
    type: 'HTML',
    className: [
        Components_Component.defaultOptions.className,
        `${Components_Component.defaultOptions.className}-html`
    ].join(' '),
    elements: [],
    editableOptions: [
        ...Components_Component.defaultOptions.editableOptions || [],
        {
            name: 'htmlInput',
            propertyPath: ['html'],
            type: 'textarea'
        }
    ]
};
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const HTMLComponent_HTMLComponentDefaults = (HTMLComponentDefaults);

;// ./code/dashboards/es-modules/Dashboards/Components/HTMLComponent/HTMLSyncs/HTMLSyncs.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Dawid Draguła
 *
 * */

/* *
*
*  Constants
*
* */
const predefinedSyncConfig = {
    defaultSyncPairs: {},
    defaultSyncOptions: {}
};
/* *
 *
 *  Default export
 *
 * */
/* harmony default export */ const HTMLSyncs = (predefinedSyncConfig);

;// ./code/dashboards/es-modules/Dashboards/Components/HTMLComponent/HTMLComponent.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *
 * */







const { deepClone: HTMLComponent_deepClone } = Dashboards_Utilities;
// TODO: This may affect the AST parsing in Highcharts
// should look into adding these as options if possible
// Needs to go in a composition in the Highcharts plugin
HTML_AST.allowedTags = [
    ...HTML_AST.allowedTags,
    'option',
    'select',
    'label',
    'input',
    'textarea'
];
HTML_AST.allowedAttributes = [
    ...HTML_AST.allowedAttributes,
    'for',
    'value',
    'checked',
    'src',
    'name',
    'selected'
];
HTML_AST.allowedReferences = [
    ...HTML_AST.allowedReferences,
    'data:image/'
];
/* *
 *
 *  Class
 *
 * */
/**
 *
 * Class that represents a HTML component.
 *
 */
class HTMLComponent extends Components_Component {
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Creates a HTML component in the cell.
     *
     * @param cell
     * Instance of cell, where component is attached.
     *
     * @param options
     * The options for the component.
     */
    constructor(cell, options) {
        if (options.className) {
            options.className = `${HTMLComponent.defaultOptions.className} ${options.className}`;
        }
        options = merge(HTMLComponent.defaultOptions, options);
        super(cell, options);
        this.options = options;
        this.type = 'HTML';
        this.elements = [];
    }
    /* *
     *
     *  Functions
     *
     * */
    /** @internal */
    async load() {
        this.emit({
            type: 'load'
        });
        await super.load();
        const options = this.options;
        let isError = false;
        if (options.elements?.length) {
            this.elements = options.elements.map(function (element) {
                if (typeof element === 'string') {
                    return new HTML_AST(element).nodes[0];
                }
                if (!element.textContent &&
                    !element.tagName &&
                    element.attributes) {
                    isError = true;
                }
                return element;
            });
        }
        else if (options.html) {
            this.elements = this.getElementsFromString(options.html);
            this.options.elements = this.elements;
        }
        this.constructTree();
        this.emit({ type: 'afterLoad' });
        if (isError) {
            throw new Error(`Missing tagName param in component: ${options.renderTo}`);
        }
        return this;
    }
    render() {
        super.render();
        this.constructTree();
        this.sync.start();
        this.emit({ type: 'afterRender' });
        return this;
    }
    resize(width, height) {
        super.resize(width, height);
        return this;
    }
    /**
     * Handles updating via options.
     *
     * @param options
     * The options to apply.
     */
    async update(options, shouldRerender = true) {
        if (options.html) {
            this.elements = this.getElementsFromString(options.html);
            this.options.elements = this.elements;
            this.constructTree();
        }
        else if (options.elements) {
            this.elements = options.elements;
        }
        await super.update(options, shouldRerender);
        this.emit({ type: 'afterUpdate' });
    }
    getOptionsOnDrop() {
        return {
            type: 'HTML',
            elements: [{
                    tagName: 'span',
                    textContent: '[Your custom HTML here- edit the component]'
                }]
        };
    }
    /**
     * Constructs the HTML tree.
     * @internal
     */
    constructTree() {
        // Remove old tree if rerendering.
        while (this.contentElement.firstChild) {
            this.contentElement.firstChild.remove();
        }
        const parser = new HTML_AST(this.options.elements || []);
        parser.addToDOM(this.contentElement);
    }
    /**
     * When HTML definition is a string, it needs to be parsed to AST.
     *
     * @internal
     */
    getElementsFromString(htmlString) {
        return new HTML_AST(htmlString).nodes;
    }
    /**
     * Get the HTML component's options.
     * @returns
     * HTML component's options.
     *
     * @internal
     *
     */
    getOptions() {
        return {
            ...diffObjects(this.options, HTMLComponent.defaultOptions),
            type: 'HTML'
        };
    }
    /**
     * Retrieves editable options for the HTML component.
     */
    getEditableOptions() {
        return HTMLComponent_deepClone(this.options, ['editableOptions']);
    }
    /**
     * Get the value of the editable option by property path. Parse the elements
     * if the HTML options is not set.
     *
     * @param propertyPath
     * The property path of the option.
     */
    getEditableOptionValue(propertyPath) {
        if (!propertyPath) {
            return;
        }
        if (propertyPath[0] === 'html') {
            const result = this.getEditableOptions();
            if (!result.html && result.elements) {
                return this.getStringFromElements(result.elements);
            }
            return result[propertyPath[0]];
        }
        return super.getEditableOptionValue(propertyPath);
    }
    /**
     * Returns the HTML string from the given elements.
     *
     * @param elements
     * The array of elements to serialize.
     */
    getStringFromElements(elements) {
        let html = '';
        for (const element of elements) {
            html += this.serializeNode(element);
        }
        return html;
    }
    /**
     * Serializes the HTML node to string.
     *
     * @param node
     * The HTML node to serialize.
     */
    serializeNode(node) {
        if (!node.tagName || node.tagName === '#text') {
            // Text node
            return node.textContent || '';
        }
        const attributes = node.attributes;
        let html = `<${node.tagName}`;
        if (attributes) {
            for (const key in attributes) {
                if (Object.prototype.hasOwnProperty.call(attributes, key)) {
                    const value = attributes[key];
                    if (value !== void 0) {
                        html += ` ${key}="${value}"`;
                    }
                }
            }
        }
        html += '>';
        html += node.textContent || '';
        (node.children || []).forEach((child) => {
            html += this.serializeNode(child);
        });
        html += `</${node.tagName}>`;
        return html;
    }
    /**
     * @internal
     */
    onTableChanged(e) {
        if (e.detail?.sender !== this.id) {
            this.render();
        }
    }
}
/* *
 *
 *  Static properties
 *
 * */
/**
 * Default options of the HTML component.
 */
HTMLComponent.defaultOptions = merge(Components_Component.defaultOptions, HTMLComponent_HTMLComponentDefaults);
/**
 * Predefined sync config for HTML component.
 */
HTMLComponent.predefinedSyncConfig = HTMLSyncs;
/* *
 *
 *  Default export
 *
 * */
/* harmony default export */ const HTMLComponent_HTMLComponent = (HTMLComponent);

;// ./code/dashboards/es-modules/Dashboards/Board.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *  - Paweł Lysy
 *  - Karol Kołodziej
 *
 * */












/* *
 *
 *  Class
 *
 * */
/**
 * Class that represents a dashboard.
 *
 * @example
 * const dashboard = Dashboards.board('container', {
 *      gui: {
 *          layouts: [{
 *              id: 'layout-1',
 *              rows: [{
 *                  cells: [{
 *                      id: 'dashboard-col-0'
 *                  }]
 *              }]
 *          }]
 *      },
 *      components: [{
 *          renderTo: 'dashboard-col-0',
 *          type: 'Highcharts',
 *          chartOptions: {
 *              series: [{
 *                  data: [1, 2, 3, 4]
 *              }]
 *          }
 *      }]
 * });
 */
class Board {
    // Implementation:
    static board(renderTo, options, async) {
        return new Board(renderTo, options).init(async);
    }
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Creates a dashboard with components like charts, tables, and HTML
     * elements.
     *
     * @internal
     * @param renderTo
     * The DOM element to render to, or its id.
     *
     * @param options
     * The options for the dashboard.
     */
    constructor(renderTo, options) {
        /**
         * All types of components available in the dashboard.
         * @internal
         */
        this.componentTypes = Components_ComponentRegistry.types;
        this.options = merge(Dashboards_Defaults.defaultOptions, options);
        this.dataPool = new Data_DataPool(options.dataPool);
        this.id = uniqueKey();
        this.guiEnabled = !options.gui ?
            false : this.options?.gui?.enabled;
        this.editModeEnabled = !options.editMode ?
            false : this.options?.editMode?.enabled;
        this.layouts = [];
        this.mountedComponents = [];
        this.initContainer(renderTo);
        this.initEditMode();
        // Add table cursors support.
        this.dataCursor = new Data_DataCursor();
        this.index = Dashboards_Globals.boards.length;
        Dashboards_Globals.boards.push(this);
        // A11y module
        this.a11y = new Accessibility_DashboardsAccessibility(this);
    }
    // Implementation:
    init(async) {
        const options = this.options;
        const componentPromises = (options.components) ?
            this.setComponents(options.components) : [];
        // Init events.
        this.initEvents();
        if (async) {
            return Promise.all(componentPromises).then(() => {
                options.events?.mounted?.call(this);
                return this;
            });
        }
        options.events?.mounted?.call(this);
        return this;
    }
    /**
     * Initializes the events.
     * @internal
     */
    initEvents() {
        const board = this, runReflow = () => {
            board.reflow();
        };
        if (typeof ResizeObserver === 'function') {
            this.resizeObserver = new ResizeObserver(runReflow);
            this.resizeObserver.observe(board.container);
        }
        else {
            const unbind = addEvent(window, 'resize', runReflow);
            addEvent(this, 'destroy', unbind);
        }
    }
    /**
     * Initialize the container for the dashboard.
     * @internal
     *
     * @param renderTo
     * The DOM element to render to, or its id.
     */
    initContainer(renderTo) {
        const board = this;
        if (typeof renderTo === 'string') {
            renderTo = window.document.getElementById(renderTo);
        }
        // Display an error if the renderTo doesn't exist.
        if (!renderTo) {
            error(13, true);
        }
        board.container = renderTo;
    }
    /**
     * Inits creating a layouts and setup the EditMode tools.
     * @internal
     */
    initEditMode() {
        const { EditMode } = Dashboards_Globals.win.Dashboards;
        if (EditMode) {
            this.editMode = new EditMode(this, this.options.editMode);
        }
        else if (this.editModeEnabled) {
            throw new Error('Missing layout.js module');
        }
    }
    /**
     * Set the components from options.
     * @internal
     *
     * @param components
     * An array of component options.
     *
     */
    setComponents(components) {
        const promises = [];
        const board = this;
        for (let i = 0, iEnd = components.length; i < iEnd; ++i) {
            promises.push(Actions_Bindings.addComponent(components[i], board));
        }
        return promises;
    }
    /**
     * Destroy the whole dashboard, its layouts and elements.
     */
    destroy() {
        const board = this;
        const index = this.index;
        // Cancel all data connectors pending requests.
        this.dataPool.cancelPendingRequests();
        // Destroy layouts.
        if (this.guiEnabled) {
            for (let i = 0, iEnd = board.layouts?.length; i < iEnd; ++i) {
                board.layouts[i].destroy();
            }
        }
        else {
            for (const mountedComponent of board.mountedComponents) {
                mountedComponent.component.destroy();
            }
        }
        // Remove resizeObserver from the board
        this.resizeObserver?.unobserve(board.container);
        // Destroy container.
        if (this.guiEnabled) {
            board.container?.remove();
        }
        // @ToDo Destroy bindings.
        // Delete all properties.
        objectEach(board, function (val, key) {
            delete board[key];
        });
        Dashboards_Globals.boards[index] = void 0;
        return;
    }
    /**
     * Reflow the dashboard. Hide the toolbars and context pointer. Reflow the
     * layouts and its cells.
     */
    reflow() {
        const board = this;
        if (board.editMode) {
            const editModeTools = board.editMode.tools;
            board.editMode.hideToolbars(['cell', 'row']);
            board.editMode.hideContextPointer();
            // Update expanded context menu container
            if (editModeTools.contextMenu) {
                editModeTools.contextMenu
                    .updatePosition(editModeTools.contextButtonElement);
            }
        }
    }
    /**
     * Update the dashboard with new options.
     *
     * @param newOptions
     * The new options to apply to the dashboard.
     */
    update(newOptions) {
        const board = this;
        // Merge new options with existing ones
        board.options = merge(board.options, newOptions);
        // Update dataPool if dataPool options changed
        if (newOptions.dataPool) {
            board.dataPool = new Data_DataPool(board.options.dataPool);
        }
        // Update guiEnabled and editModeEnabled flags if changed
        if (newOptions.gui !== void 0) {
            board.guiEnabled = !newOptions.gui ?
                false : board.options?.gui?.enabled;
        }
        if (newOptions.editMode !== void 0) {
            board.editModeEnabled = !newOptions.editMode ?
                false : board.options?.editMode?.enabled;
        }
        // Destroy existing components
        for (const mountedComponent of board.mountedComponents) {
            mountedComponent.component.destroy();
        }
        board.mountedComponents = [];
        // Destroy existing layouts if GUI is enabled
        if (board.guiEnabled && board.layouts) {
            for (let i = 0, iEnd = board.layouts.length; i < iEnd; ++i) {
                board.layouts[i].destroy();
            }
            board.layouts = [];
            // Ensure layoutsWrapper exists
            if (!board.layoutsWrapper && board.container) {
                board.layoutsWrapper = createElement('div', {
                    className: Dashboards_Globals.classNames.layoutsWrapper
                }, {}, board.container);
            }
            // Create new layouts if they are provided
            if (board.options.gui?.layouts) {
                const guiOptions = board.options.gui;
                for (let i = 0, iEnd = guiOptions.layouts.length; i < iEnd; ++i) {
                    board.layouts.push(new Layout_Layout(board, merge({}, guiOptions.layoutOptions, guiOptions.layouts[i])));
                }
                // Re-initialize editMode events if editMode exists
                if (board.editMode) {
                    // Re-initialize events for all layouts
                    let j = 0;
                    const jEnd = board.layouts.length;
                    for (j; j < jEnd; ++j) {
                        board.editMode.setLayoutEvents(board.layouts[j]);
                    }
                }
            }
        }
        // Add new components
        if (board.options.components) {
            void board.setComponents(board.options.components);
        }
    }
    /**
     * Convert the current state of board's options into JSON. The function does
     * not support converting functions or events into JSON object.
     *
     * @returns
     * Dashboards options.
     */
    getOptions() {
        const board = this, options = {
            ...this.options,
            components: []
        };
        for (let i = 0, iEnd = board.mountedComponents.length; i < iEnd; ++i) {
            if (board.mountedComponents[i].cell &&
                board.mountedComponents[i].cell.mountedComponent) {
                options.components?.push(board.mountedComponents[i].component.getOptions());
            }
        }
        if (this.guiEnabled) {
            options.gui = {
                layouts: []
            };
            for (let i = 0, iEnd = board.layouts.length; i < iEnd; ++i) {
                options.gui.layouts?.push(board.layouts[i].getOptions());
            }
        }
        else {
            delete options.gui;
        }
        return options;
    }
    /**
     * Get a Dashboards component by its identifier.
     *
     * @param id
     * The identifier of the requested component.
     *
     * @returns
     * The component with the given identifier.
     */
    getComponentById(id) {
        return this.mountedComponents.find((c) => c.component.id === id)?.component;
    }
    /**
     * Get a Dashboards component by its cell identifier.
     *
     * @param id
     * The identifier of the cell that contains the requested component.
     *
     * @returns
     * The component with the given cell identifier.
     */
    getComponentByCellId(id) {
        return this.mountedComponents.find((c) => c.cell.id === id)?.component;
    }
}
/* *
 *
 *  Registry
 *
 * */
Components_ComponentRegistry.registerComponent('HTML', HTMLComponent_HTMLComponent);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Dashboards_Board = (Board);

;// ./code/dashboards/es-modules/Dashboards/Components/GridComponent/GridDataProvider.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 * */

/* *
 *
 *  Functions
 *
 * */
/**
 * Returns whether the provider exposes `getDataTable`.
 *
 * @param provider
 * Data provider instance to test.
 *
 * @returns
 * `true` when provider exposes `getDataTable`.
 */
function hasDataTableProvider(provider) {
    return !!(provider &&
        typeof provider.getDataTable === 'function');
}

;// ./code/dashboards/es-modules/Dashboards/Components/GridComponent/GridSyncs/GridExtremesSync.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Dawid Draguła
 *
 * */


/* *
 *
 *  Constants
 *
 * */
const GridExtremesSync_defaultOptions = {};
const syncPair = {
    emitter: void 0,
    handler: function () {
        if (this.type !== 'Grid') {
            return;
        }
        const component = this;
        const syncOptions = this.sync.syncConfig.extremes;
        const groupKey = syncOptions.group ?
            ':' + syncOptions.group : '';
        const { board } = component;
        const handleChangeExtremes = (e) => {
            const cursor = e.cursor;
            if (cursor.type === 'position' &&
                component.grid &&
                typeof cursor?.row === 'number') {
                const { row } = cursor;
                const dataProvider = component.grid.dataProvider;
                const rowIndex = hasDataTableProvider(dataProvider) ?
                    dataProvider.getDataTable(true)?.getLocalRowIndex(row) :
                    void 0;
                if (rowIndex !== void 0) {
                    component.grid.viewport?.scrollToRow(rowIndex);
                }
            }
        };
        const registerCursorListeners = () => {
            const { dataCursor: cursor } = board;
            if (!cursor) {
                return;
            }
            const table = component.connectorHandlers?.[0]?.connector?.getTable();
            if (!table) {
                return;
            }
            cursor.addListener(table.id, 'xAxis.extremes.min' + groupKey, handleChangeExtremes);
        };
        const unregisterCursorListeners = () => {
            const table = component.connectorHandlers?.[0]?.connector?.getTable();
            const { dataCursor: cursor } = board;
            if (!table) {
                return;
            }
            cursor.removeListener(table.id, 'xAxis.extremes.min' + groupKey, handleChangeExtremes);
        };
        if (board) {
            registerCursorListeners();
            return unregisterCursorListeners;
        }
    }
};
/* *
*
*  Default export
*
* */
/* harmony default export */ const GridExtremesSync = ({ defaultOptions: GridExtremesSync_defaultOptions, syncPair });

;// ./code/dashboards/es-modules/Dashboards/Components/GridComponent/GridSyncs/GridHighlightSync.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Dawid Draguła
 *
 * */



/* *
 *
 *  Constants
 *
 * */
const GridHighlightSync_defaultOptions = {
    autoScroll: false
};
const GridHighlightSync_syncPair = {
    emitter: function () {
        if (this.type !== 'Grid') {
            return;
        }
        const component = this;
        const { grid, board } = component;
        const highlightOptions = this.sync.syncConfig.highlight;
        const groupKey = highlightOptions.group ?
            ':' + highlightOptions.group : '';
        if (!board || !grid || !highlightOptions?.enabled) {
            return;
        }
        const { dataCursor: cursor } = board;
        const table = this.getDataTable();
        const dataProvider = grid.dataProvider;
        const presentationTable = hasDataTableProvider(dataProvider) ?
            dataProvider.getDataTable(true) :
            void 0;
        const onCellHover = (e) => {
            if (table) {
                const cell = e.target;
                const localIndex = cell.row.index;
                const originalIndex = presentationTable?.getOriginalRowIndex(localIndex);
                if (typeof originalIndex !== 'number') {
                    return;
                }
                cursor.emitCursor(table, {
                    type: 'position',
                    row: originalIndex,
                    column: cell.column.id,
                    state: 'point.mouseOver' + groupKey,
                    sourceId: this.id
                });
            }
        };
        const onCellMouseOut = (e) => {
            if (table) {
                const cell = e.target;
                const localIndex = cell.row.index;
                const originalIndex = presentationTable?.getOriginalRowIndex(localIndex);
                if (typeof originalIndex !== 'number') {
                    return;
                }
                cursor.emitCursor(table, {
                    type: 'position',
                    row: originalIndex,
                    column: cell.column.id,
                    state: 'point.mouseOut' + groupKey,
                    sourceId: this.id
                });
            }
        };
        addEvent(grid, 'cellMouseOver', onCellHover);
        addEvent(grid, 'cellMouseOut', onCellMouseOut);
        // Return a function that calls the callbacks
        return function () {
            removeEvent(grid.container, 'cellMouseOver', onCellHover);
            removeEvent(grid.container, 'cellMouseOut', onCellMouseOut);
        };
    },
    handler: function () {
        if (this.type !== 'Grid') {
            return;
        }
        const component = this;
        const { board } = component;
        const highlightOptions = component.sync.syncConfig.highlight;
        const groupKey = highlightOptions.group ?
            ':' + highlightOptions.group : '';
        if (!highlightOptions?.enabled) {
            return;
        }
        const table = component.getDataTable();
        const handleCursor = (e) => {
            const cursor = e.cursor;
            if (cursor.sourceId === component.id ||
                cursor.type !== 'position') {
                return;
            }
            const { row, column } = cursor;
            const { grid } = component;
            const viewport = grid?.viewport;
            const dataProvider = grid?.dataProvider;
            const presentationTable = hasDataTableProvider(dataProvider) ?
                dataProvider.getDataTable(true) :
                void 0;
            if (row === void 0 || !viewport) {
                return;
            }
            const rowIndex = presentationTable?.getLocalRowIndex(row);
            if (rowIndex === void 0) {
                return;
            }
            if (highlightOptions.autoScroll) {
                viewport.scrollToRow(rowIndex);
            }
            grid.syncRow(rowIndex);
            grid.syncColumn(column);
        };
        const handleCursorOut = (e) => {
            const { grid } = component;
            if (grid && e.cursor.sourceId !== component.id) {
                grid.syncColumn();
                grid.syncRow();
            }
        };
        const registerCursorListeners = () => {
            const { dataCursor: cursor } = board;
            if (!cursor) {
                return;
            }
            if (!table) {
                return;
            }
            cursor.addListener(table.id, 'point.mouseOver' + groupKey, handleCursor);
            cursor.addListener(table.id, 'point.mouseOut' + groupKey, handleCursorOut);
        };
        const unregisterCursorListeners = () => {
            if (!table) {
                return;
            }
            const cursor = board.dataCursor;
            cursor.removeListener(table.id, 'point.mouseOver' + groupKey, handleCursor);
            cursor.removeListener(table.id, 'point.mouseOut' + groupKey, handleCursorOut);
        };
        if (board) {
            registerCursorListeners();
            return unregisterCursorListeners;
        }
    }
};
/* *
*
*  Default export
*
* */
/* harmony default export */ const GridHighlightSync = ({ defaultOptions: GridHighlightSync_defaultOptions, syncPair: GridHighlightSync_syncPair });

;// ./code/dashboards/es-modules/Dashboards/Components/GridComponent/GridSyncs/GridVisibilitySync.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Dawid Draguła
 *
 * */

/* *
 *
 *  Constants
 *
 * */
const GridVisibilitySync_defaultOptions = {};
const GridVisibilitySync_syncPair = {
    emitter: void 0,
    handler: function () {
        if (this.type !== 'Grid') {
            return;
        }
        const component = this;
        const syncOptions = this.sync.syncConfig.visibility;
        const groupKey = syncOptions.group ?
            ':' + syncOptions.group : '';
        const { board } = component;
        const handleVisibilityChange = (e) => {
            const cursor = e.cursor, grid = component.grid;
            if (!(grid && cursor.type === 'position' && cursor.column)) {
                return;
            }
            void grid.updateColumn(cursor.column, {
                enabled: cursor.state !== 'series.hide' + groupKey
            });
        };
        const registerCursorListeners = () => {
            const { dataCursor: cursor } = board;
            if (!cursor) {
                return;
            }
            const table = component.getDataTable();
            if (!table) {
                return;
            }
            cursor.addListener(table.id, 'series.show' + groupKey, handleVisibilityChange);
            cursor.addListener(table.id, 'series.hide' + groupKey, handleVisibilityChange);
        };
        const unregisterCursorListeners = () => {
            const table = component.getDataTable();
            const { dataCursor: cursor } = board;
            if (!table) {
                return;
            }
            cursor.removeListener(table.id, 'series.show' + groupKey, handleVisibilityChange);
            cursor.removeListener(table.id, 'series.hide' + groupKey, handleVisibilityChange);
        };
        if (board) {
            registerCursorListeners();
            return unregisterCursorListeners;
        }
    }
};
/* *
*
*  Default export
*
* */
/* harmony default export */ const GridVisibilitySync = ({ defaultOptions: GridVisibilitySync_defaultOptions, syncPair: GridVisibilitySync_syncPair });

;// ./code/dashboards/es-modules/Dashboards/Components/GridComponent/GridSyncs/GridSyncs.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Dawid Draguła
 *
 * */




/* *
*
*  Constants
*
* */
const GridSyncs_predefinedSyncConfig = {
    defaultSyncPairs: {
        extremes: GridExtremesSync.syncPair,
        highlight: GridHighlightSync.syncPair,
        visibility: GridVisibilitySync.syncPair
    },
    defaultSyncOptions: {
        extremes: GridExtremesSync.defaultOptions,
        highlight: GridHighlightSync.defaultOptions,
        visibility: GridVisibilitySync.defaultOptions
    }
};
/* *
 *
 *  Default export
 *
 * */
/* harmony default export */ const GridSyncs = (GridSyncs_predefinedSyncConfig);

;// ./code/dashboards/es-modules/Dashboards/Components/GridComponent/GridComponentDefaults.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Karol Kołodziej
 *
 * */



/* *
 *
 *  Constants
 *
 * */
const GridComponentDefaults = {
    gridClassName: 'highcharts-grid-container',
    gridID: 'grid-' + uniqueKey(),
    gridOptions: {},
    editableOptions: [
        {
            name: 'connectorName',
            propertyPath: ['connector', 'id'],
            type: 'select'
        }, {
            name: 'title',
            propertyPath: ['title'],
            type: 'input'
        }, {
            name: 'caption',
            propertyPath: ['caption'],
            type: 'input'
        }, {
            name: 'Grid options',
            type: 'nested',
            nestedOptions: [{
                    name: 'General',
                    options: [
                        {
                            name: 'Caption/title',
                            propertyPath: ['gridOptions', 'caption', 'text'],
                            type: 'input'
                        }, {
                            name: 'Columns resizing mode',
                            propertyPath: [
                                'gridOptions',
                                'rendering',
                                'columns',
                                'resizing',
                                'mode'
                            ],
                            type: 'select',
                            selectOptions: [{
                                    name: 'adjacent'
                                }, {
                                    name: 'distributed'
                                }, {
                                    name: 'independent'
                                }]
                        }, {
                            name: 'Editable Grid',
                            propertyPath: [
                                'gridOptions',
                                'columnDefaults',
                                'cells',
                                'editMode',
                                'enabled'
                            ],
                            type: 'toggle'
                        }, {
                            name: 'Resizable columns',
                            propertyPath: [
                                'gridOptions',
                                'rendering',
                                'columns',
                                'resizing',
                                'enabled'
                            ],
                            type: 'toggle'
                        }, {
                            name: 'Sortable columns',
                            propertyPath: [
                                'gridOptions',
                                'columnDefaults',
                                'sorting',
                                'enabled'
                            ],
                            type: 'toggle'
                        }, {
                            name: 'Cell text truncation',
                            propertyPath: [
                                'gridOptions',
                                'rendering',
                                'rows',
                                'strictHeights'
                            ],
                            type: 'toggle'
                        }
                    ]
                }]
        }, {
            name: 'Grid class name',
            propertyPath: ['gridClassName'],
            type: 'input'
        }, {
            name: 'Grid ID',
            propertyPath: ['gridID'],
            type: 'input'
        }
    ],
    onUpdate: (e, connector) => {
        const inputElement = e.target;
        if (inputElement) {
            const parentRow = inputElement
                .closest('.hcg-row');
            const cell = inputElement.closest('.hcg-cell');
            if (parentRow &&
                parentRow instanceof HTMLElement &&
                cell &&
                cell instanceof HTMLElement) {
                const dataTableRowIndex = parentRow.dataset.rowIndex;
                const { columnId } = cell.dataset;
                if (dataTableRowIndex !== void 0 &&
                    columnId !== void 0) {
                    const table = connector.getTable();
                    if (table) {
                        const converter = new Converters_DataConverter();
                        let valueToSet = converter
                            .convertByType(inputElement.value);
                        if (valueToSet instanceof Date) {
                            valueToSet = valueToSet.toString();
                        }
                        table.setCell(columnId, Number(dataTableRowIndex), valueToSet);
                    }
                }
            }
        }
    }
};
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const GridComponent_GridComponentDefaults = (GridComponentDefaults);

;// ./code/dashboards/es-modules/Dashboards/Components/GridComponent/GridComponent.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Karol Kołodziej
 *  - Dawid Draguła
 *
 * */







const { deepClone: GridComponent_deepClone } = Dashboards_Utilities;
/* *
 *
 *  Class
 *
 * */
/**
 * Grid Component for Highcharts Dashboards.
 * @private
 */
class GridComponent extends Components_Component {
    /* *
     *
     *  Constructor
     *
     * */
    constructor(cell, options, board) {
        options = merge(GridComponent.defaultOptions, options);
        super(cell, options, board);
        this.options = options;
        this.type = 'Grid';
        this.setOptions();
    }
    /* *
     *
     *  Functions
     *
     * */
    async update(options, shouldRerender = true) {
        const previousGridDataTableId = this.getGridDataTable(true)?.id;
        // Avoid triggering GridComponent.render() from Component.update().
        // That render starts a fire-and-forget renderViewport() which can
        // race with the awaited redraw() below when connector data changes.
        await super.update(options, false);
        this.setOptions();
        const grid = this.grid;
        const table = this.getDataTable();
        if (grid &&
            this.options.connector &&
            previousGridDataTableId !== table?.id) {
            this.recreateGrid(shouldRerender);
            this.emit({ type: 'afterUpdate' });
            return;
        }
        if (grid && shouldRerender) {
            super.render();
        }
        if (grid) {
            void grid.update(options.gridOptions, false);
            if (
            // #24067 - Update dataTable in options when changed.
            options.gridOptions?.dataTable &&
                this.options.gridOptions) {
                this.options.gridOptions.dataTable =
                    options.gridOptions.dataTable;
            }
            await grid.redraw();
            this.options.gridOptions = this.getGridOptionsSnapshot(grid);
            if (shouldRerender) {
                this.finalizeGridRender();
            }
        }
        else if (shouldRerender) {
            this.render();
        }
        this.emit({ type: 'afterUpdate' });
    }
    render() {
        super.render();
        if (!this.grid) {
            this.grid = this.constructGrid();
        }
        else {
            void this.grid.renderViewport();
        }
        this.finalizeGridRender();
        return this;
    }
    resize(width, height) {
        if (height) {
            this.contentElement.style.minHeight = '0';
        }
        else if (height === null) {
            this.contentElement.style.removeProperty('min-height');
        }
        this.resizeDynamicContent(width, height);
        this.grid?.viewport?.reflow();
    }
    onTableChanged() {
        const { grid } = this;
        if (!grid) {
            return;
        }
        // Check if the grid is of the legacy version (not using the data
        // provider).
        if (!('dataProvider' in grid)) {
            // eslint-disable-next-line no-console
            console.warn('GridComponent: Legacy Grid detected. Using legacy handler ' +
                'for table changes. Consider upgrading the Highcharts Grid ' +
                'Library to the latest version.');
            this.onTableChangedLegacy();
            return;
        }
        if (!grid?.dataProvider ||
            !hasDataTableProvider(grid.dataProvider) ||
            !this.connectorHandlers?.length) {
            return;
        }
        const dataTable = this.getDataTable()?.getModified();
        if (!dataTable) {
            this.recreateGrid(true);
            return;
        }
        if (!grid.options?.header) {
            // If the header is not defined, we need to check if the column
            // names have changed, so we can update the whole grid. If they
            // have not changed, we can just update the rows (more efficient).
            const newColumnIds = dataTable.getColumnIds();
            const { enabledColumns, columnPolicy } = grid;
            let index = 0;
            for (const newColumn of newColumnIds) {
                if (columnPolicy.getIndividualColumnOptions(newColumn)
                    ?.enabled === false) {
                    continue;
                }
                if (enabledColumns?.[index] !== newColumn) {
                    // If the visible columns have changed,
                    // update the whole grid.
                    this.recreateGrid(true);
                    return;
                }
                index++;
            }
        }
        if (this.getGridDataTable() !== dataTable) {
            this.recreateGrid(true);
            return;
        }
        // Data has changed and the whole grid is not re-rendered, so mark in
        // the querying that data table was modified.
        grid.querying.shouldBeUpdated = true;
        // If the column names have not changed, just update the rows.
        void grid.viewport?.updateRows();
    }
    /**
     * Legacy handler for table changes.
     */
    onTableChangedLegacy() {
        const { grid } = this;
        if (!grid) {
            return;
        }
        const dataTable = this.getDataTable()?.getModified();
        if (!dataTable) {
            void grid.update({ dataTable: void 0 });
            return;
        }
        if (!grid.options?.header) {
            // If the header is not defined, we need to check if the column
            // names have changed, so we can update the whole grid. If they
            // have not changed, we can just update the rows (more efficient).
            const newColumnIds = dataTable.getColumnIds();
            const { enabledColumns, columnPolicy } = grid;
            let index = 0;
            for (const newColumn of newColumnIds) {
                if (columnPolicy.getIndividualColumnOptions(newColumn)
                    ?.enabled === false) {
                    continue;
                }
                if (enabledColumns?.[index] !== newColumn) {
                    // If the visible columns have changed,
                    // update the whole grid.
                    void grid.update({ dataTable });
                    return;
                }
                index++;
            }
        }
        // Workaround for legacy Grid component.
        grid.dataTable =
            dataTable;
        // Data has changed and the whole grid is not re-rendered, so mark in
        // the querying that data table was modified.
        grid.querying.shouldBeUpdated = true;
        // If the column names have not changed, just update the rows.
        void grid.viewport?.updateRows();
    }
    getEditableOptions() {
        const componentOptions = this.options;
        const gridOptions = this.grid ?
            this.getGridOptionsSnapshot(this.grid) :
            void 0;
        return GridComponent_deepClone(merge({
            gridOptions: gridOptions
        }, componentOptions), ['editableOptions', 'dataTable']);
    }
    getOptionsOnDrop(sidebar) {
        const connectorsIds = sidebar.editMode.board.dataPool.getConnectorIds();
        let options = {
            type: 'Grid'
        };
        if (connectorsIds.length) {
            options = {
                ...options,
                connector: {
                    id: connectorsIds[0]
                }
            };
        }
        return options;
    }
    /**
     * Get the Grid Component's options.
     *
     * @returns
     * Grid Component's options.
     *
     * @internal
     */
    getOptions() {
        const optionsCopy = merge(this.options);
        optionsCopy.gridOptions = this.grid ?
            this.getGridOptionsSnapshot(this.grid) :
            void 0;
        // Remove the table from the options copy if the connector is set.
        if (optionsCopy.connector?.id) {
            delete optionsCopy.gridOptions?.dataTable;
        }
        else if (optionsCopy.gridOptions?.dataTable?.id) {
            optionsCopy.gridOptions.dataTable = {
                columns: optionsCopy.gridOptions.dataTable.columns
            };
        }
        return {
            ...diffObjects(optionsCopy, GridComponent.defaultOptions),
            type: 'Grid'
        };
    }
    /**
     * Destroys the data grid component.
     */
    destroy() {
        this.sync.stop();
        this.grid?.destroy();
        super.destroy();
    }
    /**
     * Sets the options for the data grid component content container.
     */
    setOptions() {
        const options = this.options, gridClassName = options.gridClassName, gridID = options.gridID;
        if (gridClassName) {
            this.contentElement.classList.value =
                GridComponent_GridComponentDefaults.gridClassName + ' ' + gridClassName;
        }
        if (gridID) {
            this.contentElement.id = gridID;
        }
    }
    finalizeGridRender() {
        const { grid } = this;
        if (!grid) {
            return;
        }
        grid.initialContainerHeight =
            getStyle(this.parentElement, 'height', true) || 0;
        this.sync.start();
        this.emit({ type: 'afterRender' });
    }
    getGridOptionsSnapshot(grid) {
        const gridOptions = merge(grid.getOptions());
        if (!this.options.connector) {
            return gridOptions;
        }
        delete gridOptions.dataTable;
        if (gridOptions.data?.providerType === 'local') {
            delete gridOptions.data.dataTable;
            delete gridOptions.data.columns;
            if (Object.keys(gridOptions.data).length === 1 &&
                gridOptions.data.providerType === 'local') {
                delete gridOptions.data;
            }
        }
        return gridOptions;
    }
    getGridOptionsWithConnectorData() {
        const gridOptions = merge(this.options.gridOptions) ?? {};
        if (!this.options.connector) {
            return gridOptions;
        }
        delete gridOptions.dataTable;
        if (gridOptions.data?.providerType === 'local') {
            delete gridOptions.data.dataTable;
            delete gridOptions.data.columns;
            if (Object.keys(gridOptions.data).length === 1 &&
                gridOptions.data.providerType === 'local') {
                delete gridOptions.data;
            }
        }
        const dataTable = this.getDataTable();
        if (dataTable) {
            gridOptions.data = merge(gridOptions.data?.providerType === 'local' ?
                gridOptions.data :
                {}, {
                providerType: 'local',
                dataTable: dataTable.getModified()
            });
        }
        return gridOptions;
    }
    recreateGrid(shouldRerender) {
        this.sync.stop();
        this.grid?.destroy();
        delete this.grid;
        if (shouldRerender) {
            this.render();
            return;
        }
        this.grid = this.constructGrid();
        this.finalizeGridRender();
    }
    getGridDataTable(presentation = false) {
        const dataProvider = this.grid?.dataProvider;
        return hasDataTableProvider(dataProvider) ?
            dataProvider.getDataTable(presentation) :
            void 0;
    }
    /**
     * Function to create the Grid.
     *
     * @returns The Grid.
     */
    constructGrid() {
        const DGN = GridComponent.GridNamespace;
        if (!DGN) {
            throw new Error('Grid not connected.');
        }
        const gridOptions = this.getGridOptionsWithConnectorData();
        const gridInstance = new DGN.Grid(this.contentElement, gridOptions ?? {});
        this.options.gridOptions = this.getGridOptionsSnapshot(gridInstance);
        return gridInstance;
    }
}
/* *
 *
 *  Static Properties
 *
 * */
/**
 * Predefined sync config for the Grid Component.
 */
GridComponent.predefinedSyncConfig = GridSyncs;
/**
 * The default options for the Grid Component.
 */
GridComponent.defaultOptions = merge(Components_Component.defaultOptions, GridComponent_GridComponentDefaults);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const GridComponent_GridComponent = (GridComponent);

;// ./code/dashboards/es-modules/Dashboards/Plugins/GridPlugin.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Karol Kołodziej
 *  - Dawid Draguła
 *
 * */


/* *
 *
 *  Functions
 *
 * */
/**
 * Connects Grid with the Dashboard plugin.
 *
 * @param GridNS
 * Grid core to connect.
 */
function connectGrid(GridNS) {
    GridComponent_GridComponent.GridNamespace = GridNS;
}
/**
 * Callback function of the Dashboard plugin.
 *
 * @param {Dashboards.PluginHandler.Event} e
 * Plugin context provided by the Dashboard.
 */
function onRegister(e) {
    const { ComponentRegistry } = e;
    ComponentRegistry.registerComponent('Grid', GridComponent_GridComponent);
}
/**
 * Callback function of the Dashboard plugin.
 *
 * @param {Dashboard.PluginHandler.Event} e Plugin context provided by the Dashboard.
 */
function onUnregister(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
e) { }
/* *
 *
 *  Default Export
 *
 * */
const GridCustom = {
    connectGrid
};
const GridPlugin = {
    custom: GridCustom,
    name: 'Grid.DashboardsPlugin',
    onRegister,
    onUnregister
};
/* harmony default export */ const Plugins_GridPlugin = (GridPlugin);

;// ./code/dashboards/es-modules/Dashboards/Components/HighchartsComponent/HighchartsSyncs/HighchartsExtremesSync.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Dawid Draguła
 *
 * */


/* *
 *
 *  Constants
 *
 * */
const HighchartsExtremesSync_defaultOptions = {};
const HighchartsExtremesSync_syncPair = {
    emitter: function () {
        if (this.type !== 'Highcharts') {
            return;
        }
        const component = this;
        const cleanupCallbacks = [];
        const { chart, board } = component;
        const connector = component.connectorHandlers?.[0]?.connector;
        const table = connector && connector.getTable();
        const syncOptions = this.sync.syncConfig.extremes;
        const groupKey = syncOptions.group ?
            ':' + syncOptions.group : '';
        const { dataCursor: cursor } = board;
        if (table && chart) {
            const extremesEventHandler = (e) => {
                const reset = !!e.resetSelection;
                if ((!e.trigger || (e.trigger && e.trigger !== 'dashboards-sync')) && !reset) {
                    // TODO: investigate this type?
                    const axis = e.target;
                    const seriesFromConnectorArray = Object.keys(component.seriesFromConnector);
                    // Prefer a series that's in a related table,
                    // but allow for other data
                    const series = seriesFromConnectorArray.length > 0 ?
                        chart.get(seriesFromConnectorArray[0]) :
                        axis.series[0];
                    if (series) {
                        // Get the indexes of the first and last drawn points
                        const visiblePoints = series.points.filter((point) => point.isInside || false);
                        const minCursorData = {
                            type: 'position',
                            state: `${axis.coll}.extremes.min${groupKey}`
                        };
                        const maxCursorData = {
                            type: 'position',
                            state: `${axis.coll}.extremes.max${groupKey}`
                        };
                        if (seriesFromConnectorArray.length > 0 &&
                            axis.coll === 'xAxis' &&
                            visiblePoints.length) {
                            let columnId;
                            const columnAssignment = (component.connectorHandlers[0]
                                ?.options).columnAssignment;
                            if (columnAssignment) {
                                const assignment = columnAssignment.find((assignment) => (assignment.seriesId ===
                                    series.options.id));
                                if (assignment) {
                                    const data = assignment.data;
                                    if (isString(data)) {
                                        columnId = data;
                                    }
                                    else if (Array.isArray(data)) {
                                        columnId = data[data.length - 1];
                                    }
                                    else {
                                        columnId = data.y;
                                    }
                                }
                            }
                            if (!columnId) {
                                columnId = axis.dateTime && (table.hasColumns(['x']) ? 'x' :
                                    series.options.id ?? series.name);
                            }
                            minCursorData.row = visiblePoints[0].index;
                            minCursorData.column = columnId;
                            maxCursorData.row =
                                visiblePoints[visiblePoints.length - 1].index;
                            maxCursorData.column = columnId;
                        }
                        // Emit as lasting cursors
                        cursor.emitCursor(table, minCursorData, e, true).emitCursor(table, maxCursorData, e, true);
                    }
                }
            };
            const addExtremesEvent = () => chart.axes.map((axis) => addEvent(axis, 'afterSetExtremes', extremesEventHandler));
            let addExtremesEventCallbacks = addExtremesEvent();
            const resetExtremesEvent = () => {
                addExtremesEventCallbacks.forEach((callback) => {
                    callback();
                });
                addExtremesEventCallbacks = [];
            };
            const handleChartResetSelection = (e) => {
                if (e.resetSelection) {
                    resetExtremesEvent();
                    cursor.emitCursor(table, {
                        type: 'position',
                        state: 'chart.zoomOut' + groupKey
                    }, e);
                    addExtremesEventCallbacks.push(...addExtremesEvent());
                }
            };
            cleanupCallbacks.push(addEvent(chart, 'selection', handleChartResetSelection));
            cleanupCallbacks.push(() => {
                cursor.remitCursor(table.id, {
                    type: 'position',
                    state: 'xAxis.extremes.min' + groupKey
                });
                cursor.remitCursor(table.id, {
                    type: 'position',
                    state: 'xAxis.extremes.max' + groupKey
                });
                resetExtremesEvent();
            });
        }
        // Return cleanup
        return function () {
            // Call back the cleanup callbacks
            cleanupCallbacks.forEach((callback) => {
                callback();
            });
        };
    },
    handler: function () {
        if (this.type !== 'Highcharts') {
            return;
        }
        const component = this;
        const syncOptions = this.sync.syncConfig.extremes;
        const groupKey = syncOptions.group ?
            ':' + syncOptions.group : '';
        const { chart, board } = component;
        if (chart && board && chart.zooming?.type) {
            const dimensions = chart.zooming.type.split('')
                .map((c) => c + 'Axis');
            const unregisterCallbacks = [];
            dimensions.forEach((dimension) => {
                const handleUpdateExtremes = (e) => {
                    const { cursor, event } = e;
                    if (cursor.type === 'position') {
                        const eventTarget = event?.target;
                        if (eventTarget && chart) {
                            const axes = chart[dimension];
                            let didZoom = false;
                            axes.forEach((axis) => {
                                if (eventTarget.coll === axis.coll &&
                                    eventTarget !== axis &&
                                    eventTarget.min !== null &&
                                    eventTarget.max !== null && (axis.max !== eventTarget.max ||
                                    axis.min !== eventTarget.min)) {
                                    axis.setExtremes(eventTarget.min, eventTarget.max, false, void 0, {
                                        trigger: 'dashboards-sync'
                                    });
                                    didZoom = true;
                                }
                            });
                            if (didZoom && !chart.resetZoomButton) {
                                chart.showResetZoom();
                            }
                            chart.redraw();
                        }
                    }
                };
                const addCursorListeners = () => {
                    const { dataCursor: cursor } = board;
                    const connector = component.connectorHandlers?.[0]?.connector;
                    if (connector) {
                        const table = connector.getTable();
                        cursor.addListener(table.id, `${dimension}.extremes.min${groupKey}`, handleUpdateExtremes);
                        cursor.addListener(table.id, `${dimension}.extremes.max${groupKey}`, handleUpdateExtremes);
                        const handleChartZoomOut = () => {
                            chart.zoomOut();
                            setTimeout(() => {
                                // Workaround for zoom button not being removed
                                const resetZoomButtons = component.element
                                    .querySelectorAll('.highcharts-reset-zoom');
                                resetZoomButtons.forEach((button) => {
                                    button.remove();
                                });
                            });
                        };
                        cursor.addListener(table.id, 'chart.zoomOut', handleChartZoomOut);
                        unregisterCallbacks.push(() => {
                            cursor.removeListener(table.id, `${dimension}.extremes.min${groupKey}`, handleUpdateExtremes);
                            cursor.removeListener(table.id, `${dimension}.extremes.max${groupKey}`, handleUpdateExtremes);
                            cursor.removeListener(table.id, 'chart.zoomOut' + groupKey, handleChartZoomOut);
                        });
                    }
                };
                if (board) {
                    addCursorListeners();
                }
            });
            return function () {
                unregisterCallbacks.forEach((callback) => {
                    callback();
                });
            };
        }
    }
};
/* *
*
*  Default export
*
* */
/* harmony default export */ const HighchartsExtremesSync = ({ defaultOptions: HighchartsExtremesSync_defaultOptions, syncPair: HighchartsExtremesSync_syncPair });

;// ./code/dashboards/es-modules/Dashboards/Components/HighchartsComponent/HighchartsSyncs/HighchartsHighlightSync.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Dawid Draguła
 *
 * */


const { error: HighchartsHighlightSync_error } = Dashboards_Utilities;
/* *
 *
 *  Constants
 *
 * */
const HighchartsHighlightSync_defaultOptions = {
    affectedSeriesId: null,
    highlightPoint: true,
    showTooltip: true,
    showCrosshair: true
};
const HighchartsHighlightSync_syncPair = {
    emitter: function () {
        if (this.type !== 'Highcharts') {
            return;
        }
        const component = this;
        const { chart, board } = component;
        const highlightOptions = this.sync.syncConfig.highlight;
        const groupKey = highlightOptions.group ?
            ':' + highlightOptions.group : '';
        if (!highlightOptions.enabled || !chart) {
            return;
        }
        const { dataCursor: cursor } = board;
        for (let i = 0, iEnd = chart.series?.length ?? 0; i < iEnd; ++i) {
            const series = chart.series[i];
            const seriesId = series.options.id ?? '';
            const connectorHandler = component.seriesFromConnector[seriesId];
            const connectorId = connectorHandler?.options.id;
            const table = this.getDataTable(connectorId);
            let columnId;
            if (!table) {
                continue;
            }
            const presTable = table?.getModified();
            const colAssignment = connectorHandler?.columnAssignment?.find((s) => s.seriesId === seriesId);
            // TODO: Better way to recognize the column name.
            if (colAssignment) {
                const { data } = colAssignment;
                if (typeof data === 'string') {
                    columnId = data;
                }
                else if (Array.isArray(data)) {
                    columnId = data[1];
                }
                else {
                    columnId = data.y ?? data.value;
                }
            }
            if (!columnId) {
                columnId = series.name;
            }
            series.update({
                point: {
                    events: {
                        // Emit table cursor
                        mouseOver: function () {
                            cursor.emitCursor(table, {
                                type: 'position',
                                row: presTable.getOriginalRowIndex(this.index),
                                column: columnId,
                                state: 'point.mouseOver' + groupKey,
                                sourceId: component.id
                            });
                        },
                        mouseOut: function () {
                            cursor.emitCursor(table, {
                                type: 'position',
                                row: presTable.getOriginalRowIndex(this.index),
                                column: columnId,
                                state: 'point.mouseOut' + groupKey,
                                sourceId: component.id
                            });
                        }
                    }
                }
            }, false);
        }
        chart.redraw();
        // Return function that handles cleanup
        return function () {
            if (chart && chart.series) {
                chart.series.forEach((series) => {
                    series.update({
                        point: {
                            events: {
                                mouseOver: void 0,
                                mouseOut: void 0
                            }
                        }
                    }, false);
                });
                chart.redraw();
            }
        };
    },
    handler: function () {
        if (this.type !== 'Highcharts') {
            return;
        }
        const component = this;
        const groupKey = this.sync.syncConfig.highlight.group ?
            ':' + this.sync.syncConfig.highlight.group : '';
        const { chart, board } = component;
        const getHoveredPoint = (e) => {
            const { table, cursor } = e;
            const highlightOptions = this.sync
                .syncConfig.highlight;
            if (chart && chart.series?.length && cursor.type === 'position') {
                let series;
                const seriesId = highlightOptions.affectedSeriesId;
                if (highlightOptions.affectedSeriesId) {
                    const foundSeries = chart.get(highlightOptions.affectedSeriesId);
                    if (foundSeries?.points) {
                        series = foundSeries;
                    }
                    else {
                        HighchartsHighlightSync_error('No series with ID \'' + seriesId + '\' found in ' +
                            'the chart. Affected series will be selected ' +
                            'according to the column assignment.');
                    }
                }
                if (!series) {
                    const seriesIds = Object.keys(component.seriesFromConnector);
                    for (let i = 0, iEnd = seriesIds.length; i < iEnd; ++i) {
                        const seriesId = seriesIds[i];
                        const connectorHandler = component.seriesFromConnector[seriesId];
                        const dataTableKey = connectorHandler?.options.dataTableKey;
                        const connectorTable = connectorHandler?.connector?.getTable(dataTableKey);
                        if (connectorTable !== table) {
                            continue;
                        }
                        const colAssignment = connectorHandler.columnAssignment;
                        series = chart.get(seriesId);
                        if (!colAssignment) {
                            break;
                        }
                        const { data } = colAssignment.find((s) => s.seriesId === seriesId) ?? {};
                        if (!data || !cursor.column) {
                            break;
                        }
                        if (typeof data === 'string') {
                            if (data === cursor.column) {
                                break;
                            }
                        }
                        else if (Array.isArray(data)) {
                            if (data.includes(cursor.column)) {
                                break;
                            }
                        }
                        else {
                            if (Object.keys(data)
                                .map((key) => data[key])
                                .includes(cursor.column)) {
                                break;
                            }
                        }
                    }
                }
                const row = cursor.row;
                if (series?.visible && row !== void 0) {
                    const rowIndex = table.getModified().getLocalRowIndex(row);
                    if (rowIndex === void 0) {
                        return;
                    }
                    const point = series.data[rowIndex];
                    if (point?.visible) {
                        return point;
                    }
                }
            }
        };
        const handleCursor = (e) => {
            const highlightOptions = this.sync
                .syncConfig.highlight;
            if (!highlightOptions.enabled ||
                e.cursor.sourceId === component.id) {
                return;
            }
            const point = getHoveredPoint(e);
            if (!point || !chart ||
                // Non-cartesian points do not use 'isInside'
                (!point.isInside && point.series.isCartesian) ||
                // Abort if the affected chart is the same as the one
                // that is currently affected manually.
                point === chart.hoverPoint) {
                return;
            }
            const tooltip = chart.tooltip;
            if (tooltip && highlightOptions.showTooltip) {
                const useSharedTooltip = tooltip.shared;
                const hoverPoint = chart.hoverPoint;
                const hoverSeries = hoverPoint?.series ||
                    chart.hoverSeries;
                const points = chart.pointer?.getHoverData(point, hoverSeries, chart.series, true, true);
                if (chart.tooltip && points?.hoverPoints.length) {
                    tooltip.refresh(useSharedTooltip ? points.hoverPoints : point);
                }
            }
            if (highlightOptions.highlightPoint && (
            // If the tooltip is shared, the hover state is
            // already set on the point.
            (!tooltip?.shared && highlightOptions.showTooltip) ||
                !highlightOptions.showTooltip)) {
                point.setState('hover');
            }
            if (highlightOptions.showCrosshair) {
                point.series.xAxis?.drawCrosshair(void 0, point);
                point.series.yAxis?.drawCrosshair(void 0, point);
            }
        };
        const handleCursorOut = (e) => {
            const highlightOptions = this.sync
                .syncConfig.highlight;
            if (!chart || !chart.series.length ||
                !highlightOptions.enabled ||
                e.cursor.sourceId === component.id) {
                return;
            }
            const point = getHoveredPoint(e);
            // Abort if the affected chart is the same as the one
            // that is currently affected manually.
            if (point && (!point.isInside && point.series.isCartesian ||
                point === chart.hoverPoint)) {
                return;
            }
            let unhovered = false;
            const unhoverAllPoints = () => {
                // If the 'row' parameter is missing in the event
                // object, the unhovered point cannot be identified.
                const series = chart.series;
                const seriesLength = series.length;
                for (let i = 0; i < seriesLength; i++) {
                    const points = chart.series[i].points;
                    const pointsLength = points.length;
                    for (let j = 0; j < pointsLength; j++) {
                        points[j].setState();
                    }
                }
            };
            const tooltip = chart.tooltip;
            if (tooltip && highlightOptions.showTooltip) {
                tooltip.hide();
                // Shared tooltip refresh always hovers points, so it's
                // important to unhover all points on cursor out.
                if (tooltip.shared) {
                    unhoverAllPoints();
                    unhovered = true;
                }
            }
            if (highlightOptions.highlightPoint && !unhovered) {
                if (point) {
                    point.setState();
                }
                else {
                    unhoverAllPoints();
                }
            }
            if (highlightOptions.showCrosshair) {
                if (point) {
                    point.series.xAxis?.drawCrosshair();
                    point.series.yAxis?.drawCrosshair();
                }
                else {
                    // If the 'row' parameter is missing in the event
                    // object, the unhovered point cannot be identified.
                    const xAxes = chart.xAxis;
                    const yAxes = chart.yAxis;
                    for (let i = 0, l = xAxes.length; i < l; i++) {
                        xAxes[i].drawCrosshair();
                    }
                    for (let i = 0, l = yAxes.length; i < l; i++) {
                        yAxes[i].drawCrosshair();
                    }
                }
            }
        };
        const registerCursorListeners = () => {
            const { dataCursor: cursor } = board;
            const { connectorHandlers } = this;
            if (!cursor) {
                return;
            }
            for (let i = 0, iEnd = connectorHandlers.length; i < iEnd; ++i) {
                const connectorId = connectorHandlers[i]?.options.id;
                const table = this.getDataTable(connectorId);
                if (!table) {
                    continue;
                }
                cursor.addListener(table.id, 'point.mouseOver' + groupKey, handleCursor);
                cursor.addListener(table.id, 'point.mouseOut' + groupKey, handleCursorOut);
            }
        };
        const unregisterCursorListeners = () => {
            const { dataCursor: cursor } = board;
            const { connectorHandlers } = this;
            if (!cursor) {
                return;
            }
            for (let i = 0, iEnd = connectorHandlers.length; i < iEnd; ++i) {
                const table = connectorHandlers[i]?.connector?.getTable();
                if (!table) {
                    continue;
                }
                cursor.removeListener(table.id, 'point.mouseOver' + groupKey, handleCursor);
                cursor.removeListener(table.id, 'point.mouseOut' + groupKey, handleCursorOut);
            }
        };
        if (board) {
            registerCursorListeners();
            return unregisterCursorListeners;
        }
    }
};
/* *
*
*  Default export
*
* */
/* harmony default export */ const HighchartsHighlightSync = ({ defaultOptions: HighchartsHighlightSync_defaultOptions, syncPair: HighchartsHighlightSync_syncPair });

;// ./code/dashboards/es-modules/Dashboards/Components/HighchartsComponent/HighchartsSyncs/HighchartsVisibilitySync.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Dawid Draguła
 *
 * */

/* *
 *
 *  Constants
 *
 * */
const HighchartsVisibilitySync_defaultOptions = {};
const HighchartsVisibilitySync_syncPair = {
    emitter: function () {
        if (this.type !== 'Highcharts') {
            return;
        }
        const component = this;
        const syncOptions = this.sync.syncConfig.visibility;
        const groupKey = syncOptions.group ? ':' + syncOptions.group : '';
        const { chart, board } = component;
        if (!board || !chart) {
            return;
        }
        const table = this.getDataTable();
        if (table) { // Has a connector
            const { dataCursor: cursor } = board;
            const { series } = chart;
            series.forEach((series) => {
                series.update({
                    events: {
                        show: function () {
                            cursor.emitCursor(table, {
                                type: 'position',
                                state: 'series.show' + groupKey,
                                column: this.name
                            });
                        },
                        hide: function () {
                            cursor.emitCursor(table, {
                                type: 'position',
                                state: 'series.hide' + groupKey,
                                column: this.name
                            });
                        }
                    }
                }, false);
            });
            chart.redraw();
        }
        return function () {
            if (!chart || !chart.series?.length) {
                return;
            }
            chart.series.forEach((series) => {
                series.update({
                    events: {
                        show: void 0,
                        hide: void 0
                    }
                }, false);
            });
            chart.redraw();
        };
    },
    handler: function () {
        if (this.type !== 'Highcharts') {
            return;
        }
        const component = this;
        const syncOptions = this.sync.syncConfig.visibility;
        const groupKey = syncOptions.group ? ':' + syncOptions.group : '';
        const { board } = component;
        const findSeries = (seriesArray, name) => {
            for (const series of seriesArray) {
                if (series.name === name) {
                    return series;
                }
            }
        };
        const handleShow = (e) => {
            const chart = component.chart;
            if (!chart || !chart.series?.length) {
                return;
            }
            if (e.cursor.type === 'position' && e.cursor.column !== void 0) {
                const series = findSeries(chart.series, e.cursor.column);
                if (series) {
                    series.setVisible(true, true);
                }
            }
        };
        const handleHide = (e) => {
            const chart = component.chart;
            if (!chart || !chart.series?.length) {
                return;
            }
            if (e.cursor.type === 'position' && e.cursor.column !== void 0) {
                const series = findSeries(chart.series, e.cursor.column);
                if (series) {
                    series.setVisible(false, true);
                }
            }
        };
        const registerCursorListeners = () => {
            const { dataCursor } = board;
            if (!dataCursor) {
                return;
            }
            const table = component.getDataTable();
            if (!table) {
                return;
            }
            dataCursor.addListener(table.id, 'series.show' + groupKey, handleShow);
            dataCursor.addListener(table.id, 'series.hide' + groupKey, handleHide);
        };
        const unregisterCursorListeners = () => {
            const table = component.getDataTable();
            if (table) {
                board.dataCursor.removeListener(table.id, 'series.show' + groupKey, handleShow);
                board.dataCursor.removeListener(table.id, 'series.hide' + groupKey, handleHide);
            }
        };
        if (board) {
            registerCursorListeners();
            return unregisterCursorListeners;
        }
    }
};
/* *
*
*  Default export
*
* */
/* harmony default export */ const HighchartsVisibilitySync = ({ defaultOptions: HighchartsVisibilitySync_defaultOptions, syncPair: HighchartsVisibilitySync_syncPair });

;// ./code/dashboards/es-modules/Dashboards/Components/HighchartsComponent/HighchartsSyncs/HighchartsSyncs.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Dawid Draguła
 *
 * */




/* *
*
*  Constants
*
* */
const HighchartsSyncs_predefinedSyncConfig = {
    defaultSyncPairs: {
        extremes: HighchartsExtremesSync.syncPair,
        highlight: HighchartsHighlightSync.syncPair,
        visibility: HighchartsVisibilitySync.syncPair
    },
    defaultSyncOptions: {
        extremes: HighchartsExtremesSync.defaultOptions,
        highlight: HighchartsHighlightSync.defaultOptions,
        visibility: HighchartsVisibilitySync.defaultOptions
    }
};
/* *
 *
 *  Default export
 *
 * */
/* harmony default export */ const HighchartsSyncs = (HighchartsSyncs_predefinedSyncConfig);

;// ./code/dashboards/es-modules/Dashboards/Components/HighchartsComponent/HighchartsComponentDefaults.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Karol Kołodziej
 *
 * */




/* *
 *
 *  Constants
 *
 * */
const HighchartsComponentDefaults = {
    allowConnectorUpdate: true,
    className: [
        Components_Component.defaultOptions.className,
        `${Components_Component.defaultOptions.className}-highcharts`
    ].join(' '),
    chartClassName: 'chart-container',
    chartID: 'chart-' + uniqueKey(),
    chartOptions: {
        series: []
    },
    chartConstructor: 'chart',
    editableOptions: [
        {
            name: 'connectorName',
            propertyPath: ['connector', 'id'],
            type: 'select'
        },
        ...Components_Component.defaultOptions.editableOptions || [],
        {
            name: 'chartOptions',
            type: 'nested',
            nestedOptions: [{
                    name: 'chart',
                    options: [{
                            name: 'title',
                            propertyPath: ['chartOptions', 'title', 'text'],
                            type: 'input'
                        }, {
                            name: 'subtitle',
                            propertyPath: ['chartOptions', 'subtitle', 'text'],
                            type: 'input'
                        }, {
                            name: 'type',
                            propertyPath: ['chartOptions', 'chart', 'type'],
                            type: 'select',
                            selectOptions: [{
                                    name: 'column',
                                    iconURL: 'series-types/icon-column.svg'
                                }, {
                                    name: 'line',
                                    iconURL: 'series-types/icon-line.svg'
                                }, {
                                    name: 'scatter',
                                    iconURL: 'series-types/icon-scatter.svg'
                                }, {
                                    name: 'pie',
                                    iconURL: 'series-types/icon-pie.svg'
                                }]
                        }]
                }, {
                    name: 'xAxis',
                    options: [{
                            name: 'title',
                            propertyPath: ['chartOptions', 'xAxis', 'title', 'text'],
                            type: 'input'
                        }, {
                            name: 'type',
                            propertyPath: ['chartOptions', 'xAxis', 'type'],
                            type: 'select',
                            selectOptions: [{
                                    name: 'linear'
                                }, {
                                    name: 'datetime'
                                }, {
                                    name: 'logarithmic'
                                }]
                        }]
                }, {
                    name: 'yAxis',
                    options: [{
                            name: 'title',
                            propertyPath: ['chartOptions', 'yAxis', 'title', 'text'],
                            type: 'input'
                        }, {
                            name: 'type',
                            propertyPath: ['chartOptions', 'yAxis', 'type'],
                            type: 'select',
                            selectOptions: [{
                                    name: 'linear'
                                }, {
                                    name: 'datetime'
                                }, {
                                    name: 'logarithmic'
                                }]
                        }]
                }, {
                    name: 'legend',
                    showToggle: true,
                    propertyPath: ['chartOptions', 'legend', 'enabled'],
                    options: [{
                            name: 'align',
                            propertyPath: ['chartOptions', 'legend', 'align'],
                            type: 'select',
                            selectOptions: [{
                                    name: 'left'
                                }, {
                                    name: 'center'
                                }, {
                                    name: 'right'
                                }]
                        }]
                }, {
                    name: 'tooltip',
                    showToggle: true,
                    propertyPath: ['chartOptions', 'tooltip', 'enabled'],
                    options: [{
                            name: 'split',
                            propertyPath: ['chartOptions', 'tooltip', 'split'],
                            type: 'toggle'
                        }]
                }, {
                    name: 'dataLabels',
                    propertyPath: [
                        'chartOptions',
                        'plotOptions',
                        'series',
                        'dataLabels',
                        'enabled'
                    ],
                    showToggle: true,
                    options: [{
                            name: 'align',
                            propertyPath: [
                                'chartOptions',
                                'plotOptions',
                                'series',
                                'dataLabels',
                                'align'
                            ],
                            type: 'select',
                            selectOptions: [{
                                    name: 'left'
                                }, {
                                    name: 'center'
                                }, {
                                    name: 'right'
                                }]
                        }]
                }, {
                    name: 'credits',
                    showToggle: true,
                    propertyPath: ['chartOptions', 'credits', 'enabled'],
                    options: [{
                            name: 'name',
                            propertyPath: [
                                'chartOptions',
                                'credits',
                                'text'
                            ],
                            type: 'input'
                        }, {
                            name: 'url',
                            propertyPath: [
                                'chartOptions',
                                'credits',
                                'href'
                            ],
                            type: 'input'
                        }]
                }]
        }, {
            name: 'chartConfig',
            propertyPath: ['chartOptions'],
            type: 'textarea'
        }, {
            name: 'chartClassName',
            propertyPath: ['chartClassName'],
            type: 'input'
        }, {
            name: 'chartID',
            propertyPath: ['chartID'],
            type: 'input'
        }
    ],
    editableOptionsBindings: merge(Components_Component.defaultOptions.editableOptionsBindings, {
        skipRedraw: [
            'chartOptions',
            'chartConfig'
        ]
    })
};
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const HighchartsComponent_HighchartsComponentDefaults = (HighchartsComponentDefaults);

;// ./code/dashboards/es-modules/Dashboards/Components/HighchartsComponent/HighchartsComponent.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Gøran Slettemark
 *  - Wojciech Chmiel
 *  - Sebastian Bochan
 *  - Sophie Bremer
 *  - Dawid Draguła
 *
 * */










const { deepClone: HighchartsComponent_deepClone } = Dashboards_Utilities;
/* *
 *
 *  Class
 *
 * */
/**
 *
 * Class that represents a Highcharts component.
 *
 */
class HighchartsComponent extends Components_Component {
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Creates a Highcharts component in the cell.
     *
     * @param options
     * The options for the component.
     */
    constructor(cell, options, board) {
        options = merge(HighchartsComponent.defaultOptions, options);
        super(cell, options, board);
        /**
         * An object of series IDs and their connector handlers.
         */
        this.seriesFromConnector = {};
        this.options = options;
        this.chartConstructor = this.options.chartConstructor || 'chart';
        this.type = 'Highcharts';
        this.chartContainer = createElement('figure', void 0, void 0, this.contentElement, true);
        this.setOptions();
        this.chartOptions = merge((this.options.chartOptions ||
            { chart: {} }), {
            tooltip: {} // Temporary fix for #18876
        });
        this.innerResizeTimeouts = [];
    }
    onTableChanged() {
        this.updateSeries();
    }
    /* *
     *
     *  Functions
     *
     * */
    async load() {
        this.emit({ type: 'load' });
        await super.load();
        this.emit({ type: 'afterLoad' });
        return this;
    }
    render() {
        const hcComponent = this;
        super.render();
        hcComponent.chart = hcComponent.getChart();
        hcComponent.updateSeries();
        hcComponent.emit({ type: 'afterRender' });
        hcComponent.setupConnectorUpdate();
        this.sync.start();
        return this;
    }
    resize(width, height) {
        this.resizeDynamicContent(width, height);
        while (this.innerResizeTimeouts.length) {
            const timeoutID = this.innerResizeTimeouts.pop();
            if (timeoutID) {
                clearTimeout(timeoutID);
            }
        }
        this.innerResizeTimeouts.push(setTimeout(() => {
            if (this.chart && this.chart.container) {
                const heightOffset = this.contentElement.offsetHeight -
                    this.chart?.container.offsetHeight;
                this.chart.setSize(null, (Math.abs(heightOffset) > 1) ?
                    this.contentElement.offsetHeight : null, false);
            }
        }, 33));
        return this;
    }
    /**
     * Adds call update value in store, when chart's point is updated.
     * @private
     */
    setupConnectorUpdate() {
        const { connectorHandlers, chart } = this;
        if (!chart || !this.options.allowConnectorUpdate) {
            return;
        }
        const seriesLength = chart.series.length;
        for (let i = 0, iEnd = connectorHandlers.length; i < iEnd; i++) {
            const connectorHandler = connectorHandlers[i];
            for (let j = 0; j < seriesLength; j++) {
                const series = chart.series[j];
                series.update({
                    point: {
                        events: {
                            update: (e) => {
                                this.onChartUpdate(e.target, connectorHandler);
                            }
                        }
                    }
                }, false);
            }
        }
    }
    /**
     * Update the store, when the point is being dragged.
     * @param point Dragged point.
     * @param connectorHandler Connector handler with data to update.
     */
    onChartUpdate(point, connectorHandler) {
        const table = connectorHandler.dataTable;
        const columnAssignment = connectorHandler.columnAssignment;
        const seriesId = point.series.options.id;
        const converter = new Converters_DataConverter();
        const valueToSet = Converters_DataConverterUtils.asNumber(point.y, converter.decimalRegExp);
        if (!table) {
            return;
        }
        let columnId;
        if (columnAssignment && seriesId) {
            const data = columnAssignment.find((s) => s.seriesId === seriesId)?.data;
            if (isString(data)) {
                columnId = data;
            }
            else if (Array.isArray(data)) {
                columnId = data[1];
            }
            else if (data) {
                columnId = data.y ?? data.value;
            }
        }
        if (!columnId) {
            columnId = seriesId ?? point.series.name;
        }
        table.setCell(columnId, point.index, valueToSet);
    }
    /**
     * Internal method for handling option updates.
     * @internal
     */
    setOptions() {
        if (this.options.chartClassName) {
            this.chartContainer.classList.value =
                HighchartsComponent_HighchartsComponentDefaults.className + ' ' +
                    this.options.chartClassName;
        }
        if (this.options.chartID) {
            this.chartContainer.id = this.options.chartID;
        }
    }
    /**
     * Handles updating via options.
     *
     * @param options
     * The options to apply.
     */
    async update(options, shouldRerender = true) {
        await super.update(options, false);
        this.setOptions();
        if (this.options.chartConstructor !== this.chartConstructor) {
            this.chartConstructor = this.options.chartConstructor || 'chart';
            this.chartOptions = this.options.chartOptions || {};
            this.chart?.destroy();
            delete this.chart;
        }
        else {
            this.chart?.update(merge(this.options.chartOptions) || {});
        }
        this.emit({ type: 'afterUpdate' });
        shouldRerender && this.render();
    }
    /**
     * Updates chart's series when the data table is changed.
     * @private
     */
    updateSeries() {
        const { chart } = this;
        const connectorHandlers = this.connectorHandlers;
        if (!chart) {
            return;
        }
        const newSeriesIds = [];
        for (const connectorHandler of connectorHandlers) {
            const options = connectorHandler.options;
            let columnAssignment = options.columnAssignment;
            if (!columnAssignment && connectorHandler.dataTable) {
                columnAssignment = this.getDefaultColumnAssignment(connectorHandler.dataTable.getColumnIds(), connectorHandler.dataTable);
            }
            if (columnAssignment) {
                connectorHandler.columnAssignment = columnAssignment;
                for (const { seriesId } of columnAssignment) {
                    if (seriesId) {
                        newSeriesIds.push(seriesId);
                    }
                }
            }
        }
        const seriesArray = Object.keys(this.seriesFromConnector);
        // Remove series that were added in the previous update and are not
        // present in the new columnAssignment.
        for (let i = 0, iEnd = seriesArray.length; i < iEnd; ++i) {
            const oldSeriesId = seriesArray[i];
            if (newSeriesIds.some((newSeriesId) => newSeriesId === oldSeriesId)) {
                continue;
            }
            const series = chart.get(oldSeriesId);
            if (series) {
                series.destroy();
            }
        }
        this.seriesFromConnector = {};
        for (const connectorHandler of connectorHandlers) {
            this.updateSeriesFromConnector(connectorHandler);
        }
        chart.redraw();
    }
    /**
     * Updates the series based on the connector from each connector handler.
     * @param connectorHandler The connector handler.
     * @private
     */
    updateSeriesFromConnector(connectorHandler) {
        const chart = this.chart;
        if (!connectorHandler.connector ||
            !chart ||
            !connectorHandler.dataTable) {
            return;
        }
        const table = connectorHandler.dataTable.getModified();
        const modifierOptions = connectorHandler.dataTable.getModifier()?.options;
        const columnAssignment = connectorHandler.columnAssignment ?? [];
        // Create the series or update the existing ones.
        for (let i = 0, iEnd = columnAssignment.length; i < iEnd; ++i) {
            const assignment = columnAssignment[i];
            const dataStructure = assignment.data;
            const series = chart.get(assignment.seriesId);
            const seriesOptions = {};
            // Prevent dragging on series, which were created out of a
            // columns which are created by MathModifier.
            const adjustDraggableOptions = (compare) => {
                if (modifierOptions?.type === 'Math' &&
                    modifierOptions
                        .columnFormulas?.some((formula) => compare(formula.column))) {
                    seriesOptions.dragDrop = {
                        draggableY: false
                    };
                }
            };
            // Set the series data based on the column assignment data structure
            // type.
            if (isString(dataStructure)) {
                const column = table.getColumn(dataStructure);
                if (column) {
                    seriesOptions.data = column.slice();
                }
                adjustDraggableOptions((columnId) => (columnId === dataStructure));
            }
            else if (Array.isArray(dataStructure)) {
                const seriesTable = new Data_DataTable({
                    columns: table.getColumns(dataStructure)
                });
                seriesOptions.data = seriesTable.getRows();
                adjustDraggableOptions((columnId) => (dataStructure.some((name) => name === columnId)));
            }
            else {
                const keys = Object.keys(dataStructure);
                const columnIds = [];
                for (let j = 0, jEnd = keys.length; j < jEnd; ++j) {
                    columnIds.push(dataStructure[keys[j]]);
                }
                const seriesTable = new Data_DataTable({
                    columns: table.getColumns(columnIds)
                });
                seriesOptions.keys = keys;
                seriesOptions.data = seriesTable.getRows();
                adjustDraggableOptions((columnId) => (columnIds.some((name) => name === columnId)));
            }
            if (!series) {
                chart.addSeries({
                    name: assignment.seriesId,
                    id: assignment.seriesId,
                    ...seriesOptions
                }, false);
            }
            else {
                series.update(seriesOptions, false);
            }
            this.seriesFromConnector[assignment.seriesId] = connectorHandler;
        }
    }
    /**
     * Destroy chart and create a new one.
     *
     * @returns
     * The chart.
     *
     * @private
     *
     */
    getChart() {
        return this.chart || this.createChart();
    }
    /**
     * Destroys the highcharts component.
     */
    destroy() {
        // Cleanup references in the global Highcharts scope
        // Destroy chart before destroying the component element
        // to ensure chart has access to its renderTo element
        if (this.chart && this.chart.renderTo && this.chart.renderer) {
            try {
                this.chart.destroy();
            }
            catch (e) {
                // Chart may already be destroyed or renderTo/renderer
                // eslint-disable-next-line no-console
                console.warn('Error destroying chart:', e);
            }
            this.chart = void 0;
        }
        super.destroy();
    }
    /**
     * Creates default mapping when columnAssignment is not declared.
     * @param  { Array<string>} columnIds all columns returned from dataTable.
     *
     * @returns
     * The record of mapping
     *
     * @private
     *
     */
    getDefaultColumnAssignment(columnIds = [], presentationTable) {
        const result = [];
        const firstColumn = presentationTable.getColumn(columnIds[0]);
        if (firstColumn && isString(firstColumn[0])) {
            for (let i = 1, iEnd = columnIds.length; i < iEnd; ++i) {
                result.push({
                    seriesId: columnIds[i],
                    data: [columnIds[0], columnIds[i]]
                });
            }
            return result;
        }
        for (let i = 0, iEnd = columnIds.length; i < iEnd; ++i) {
            result.push({
                seriesId: columnIds[i],
                data: columnIds[i]
            });
        }
        return result;
    }
    /**
     * Creates chart.
     *
     * @returns
     * The chart.
     *
     * @private
     *
     */
    createChart() {
        const charter = HighchartsComponent.charter || Dashboards_Globals.win.Highcharts;
        if (!this.chartConstructor) {
            this.chartConstructor = 'chart';
        }
        const Factory = charter[this.chartConstructor];
        if (Factory) {
            try {
                if (this.chartConstructor === 'chart') {
                    return charter.Chart.chart(this.chartContainer, this.chartOptions);
                }
                return new Factory(this.chartContainer, this.chartOptions);
            }
            catch (e) {
                throw new Error(`The Highcharts component in cell '${this.cell.id}' ` +
                    'is misconfigured. \n____________\n' +
                    String(e));
            }
        }
        if (typeof charter.chart !== 'function') {
            throw new Error('Chart constructor not found');
        }
        return this.chart;
    }
    getOptionsOnDrop(sidebar) {
        const connectorsIds = sidebar.editMode.board.dataPool.getConnectorIds();
        let options = {
            type: 'Highcharts',
            chartOptions: {
                chart: {
                    animation: false,
                    type: 'column',
                    zooming: {}
                }
            }
        };
        if (connectorsIds.length) {
            options = {
                ...options,
                connector: {
                    id: connectorsIds[0]
                }
            };
        }
        return options;
    }
    /**
     * Get the HighchartsComponent component's options.
     * @returns
     * HighchartsComponent component's options.
     *
     * @internal
     *
     */
    getOptions() {
        return {
            ...diffObjects(this.options, HighchartsComponent.defaultOptions),
            type: 'Highcharts'
        };
    }
    /**
     * Retrieves editable options for the chart.
     *
     * @returns
     * The editable options for the chart and its values.
     */
    getEditableOptions() {
        const component = this;
        const componentOptions = component.options;
        const chart = component.chart;
        const chartOptions = chart && chart.options;
        const chartType = chartOptions?.chart?.type || 'line';
        return HighchartsComponent_deepClone(merge({
            chartOptions
        }, {
            chartOptions: {
                yAxis: splat(chart && chart.yAxis[0].options),
                xAxis: splat(chart && chart.xAxis[0].options),
                plotOptions: {
                    series: ((chartOptions && chartOptions.plotOptions) ||
                        {})[chartType]
                }
            }
        }, componentOptions), ['dataTable', 'points', 'series', 'data', 'editableOptions']);
    }
    getEditableOptionValue(propertyPath) {
        const component = this;
        if (!propertyPath) {
            return;
        }
        if (propertyPath.length === 1 && propertyPath[0] === 'chartOptions') {
            return JSON.stringify(component.options.chartOptions, null, 2);
        }
        return super.getEditableOptionValue.call(this, propertyPath);
    }
}
/**
 * Predefined sync config for Highcharts component.
 */
HighchartsComponent.predefinedSyncConfig = HighchartsSyncs;
/**
 * Default options of the Highcharts component.
 */
HighchartsComponent.defaultOptions = merge(Components_Component.defaultOptions, HighchartsComponent_HighchartsComponentDefaults);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const HighchartsComponent_HighchartsComponent = (HighchartsComponent);

;// ./code/dashboards/es-modules/Dashboards/Components/KPIComponent/KPISyncs/KPIExtremesSync.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Dawid Draguła
 *
 * */


/* *
 *
 *  Constants
 *
 * */
const KPIExtremesSync_defaultOptions = {};
const KPIExtremesSync_syncPair = {
    emitter: void 0,
    handler: function () {
        if (this.type !== 'KPI') {
            return;
        }
        const component = this;
        const syncOptions = this.sync.syncConfig.extremes;
        const groupKey = syncOptions.group ? ':' + syncOptions.group : '';
        const { board } = this;
        const handleChangeExtremes = (e) => {
            const cursor = e.cursor;
            if (cursor.type === 'position' &&
                typeof cursor?.row === 'number' &&
                defined(cursor.column) &&
                component.connectorHandlers?.[0]?.connector &&
                !defined(component.options.value)) {
                const value = String(component.connectorHandlers[0].connector
                    .getTable()
                    .getModified()
                    .getCell(cursor.column, cursor.row));
                component.setValue(value);
            }
        };
        const registerCursorListeners = () => {
            const { dataCursor: cursor } = board;
            if (!cursor) {
                return;
            }
            const table = this.getDataTable();
            if (!table) {
                return;
            }
            cursor.addListener(table.id, 'xAxis.extremes.max' + groupKey, handleChangeExtremes);
        };
        const unregisterCursorListeners = () => {
            const table = this.getDataTable();
            const { dataCursor: cursor } = board;
            if (!table) {
                return;
            }
            cursor.removeListener(table.id, 'xAxis.extremes.max' + groupKey, handleChangeExtremes);
        };
        if (board) {
            registerCursorListeners();
            return unregisterCursorListeners;
        }
    }
};
/* *
*
*  Default export
*
* */
/* harmony default export */ const KPIExtremesSync = ({ defaultOptions: KPIExtremesSync_defaultOptions, syncPair: KPIExtremesSync_syncPair });

;// ./code/dashboards/es-modules/Dashboards/Components/KPIComponent/KPISyncs/KPISyncs.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Dawid Draguła
 *
 * */


/* *
*
*  Constants
*
* */
const KPISyncs_predefinedSyncConfig = {
    defaultSyncPairs: {
        extremes: KPIExtremesSync.syncPair
    },
    defaultSyncOptions: {
        extremes: KPIExtremesSync.defaultOptions
    }
};
/* *
 *
 *  Default export
 *
 * */
/* harmony default export */ const KPISyncs = (KPISyncs_predefinedSyncConfig);

;// ./code/dashboards/es-modules/Dashboards/Components/KPIComponent/KPIComponentDefaults.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */


/* *
 *
 *  Constants
 *
 * */
const KPIComponentDefaults = {
    type: 'KPI',
    className: [
        Components_Component.defaultOptions.className,
        `${Components_Component.defaultOptions.className}-kpi`
    ].join(' '),
    minFontSize: 20,
    thresholdColors: ['#f45b5b', '#90ed7d'],
    editableOptions: [
        {
            name: 'connectorName',
            propertyPath: ['connector', 'id'],
            type: 'select'
        },
        ...Components_Component.defaultOptions.editableOptions || [],
        {
            name: 'Value',
            type: 'input',
            propertyPath: ['value']
        }, {
            name: 'Column name',
            type: 'input',
            propertyPath: ['columnId']
        }, {
            name: 'Value format',
            type: 'input',
            propertyPath: ['valueFormat']
        }
    ],
    linkedValueTo: {
        enabled: true,
        seriesIndex: 0,
        pointIndex: 0
    }
};
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const KPIComponent_KPIComponentDefaults = (KPIComponentDefaults);

;// ./code/dashboards/es-modules/Data/Formula/FormulaTypes.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */

/* *
 *
 *  Constants
 *
 * */
/**
 * Array of all possible operators.
 * @private
 */
const operators = ['+', '-', '*', '/', '^', '=', '<', '<=', '>', '>='];
/* *
 *
 *  Functions
 *
 * */
/**
 * Tests an item for a Formula array.
 *
 * @private
 *
 * @param {Highcharts.FormulaItem} item
 * Item to test.
 *
 * @return {boolean}
 * `true`, if the item is a formula (or argument) array.
 */
function isFormula(item) {
    return Array.isArray(item);
}
/**
 * Tests an item for a Function structure.
 *
 * @private
 *
 * @param {Highcharts.FormulaItem} item
 * Item to test.
 *
 * @return {boolean}
 * `true`, if the item is a formula function.
 */
function FormulaTypes_isFunction(item) {
    return (typeof item === 'object' &&
        !(Array.isArray(item)) &&
        item.type === 'function');
}
/**
 * Tests an item for an Operator string.
 *
 * @private
 *
 * @param {Highcharts.FormulaItem} item
 * Item to test.
 *
 * @return {boolean}
 * `true`, if the item is an operator string.
 */
function isOperator(item) {
    return (typeof item === 'string' &&
        operators.indexOf(item) >= 0);
}
/**
 * Tests an item for a Range structure.
 *
 * @private
 *
 * @param {Highcharts.FormulaItem} item
 * Item to test.
 *
 * @return {boolean}
 * `true`, if the item is a range.
 */
function isRange(item) {
    return (typeof item === 'object' &&
        !(Array.isArray(item)) &&
        item.type === 'range');
}
/**
 * Tests an item for a Reference structure.
 *
 * @private
 *
 * @param {Highcharts.FormulaItem} item
 * Item to test.
 *
 * @return {boolean}
 * `true`, if the item is a reference.
 */
function isReference(item) {
    return (typeof item === 'object' &&
        !(Array.isArray(item)) &&
        item.type === 'reference');
}
/**
 * Tests an item for a Value structure.
 *
 * @private
 *
 * @param {Highcharts.FormulaItem|null|undefined} item
 * Item to test.
 *
 * @return {boolean}
 * `true`, if the item is a value.
 */
function isValue(item) {
    return (typeof item === 'boolean' ||
        typeof item === 'number' ||
        typeof item === 'string');
}
/* *
 *
 *  Default Export
 *
 * */
const MathFormula = {
    isFormula,
    isFunction: FormulaTypes_isFunction,
    isOperator,
    isRange,
    isReference,
    isValue
};
/* harmony default export */ const FormulaTypes = (MathFormula);

;// ./code/dashboards/es-modules/Data/Formula/FormulaProcessor.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */



const { isFormula: FormulaProcessor_isFormula, isFunction: FormulaProcessor_isFunction, isOperator: FormulaProcessor_isOperator, isRange: FormulaProcessor_isRange, isReference: FormulaProcessor_isReference, isValue: FormulaProcessor_isValue } = FormulaTypes;
/* *
 *
 *  Constants
 *
 * */
const asLogicalStringRegExp = / */;
const MAX_FALSE = Number.MAX_VALUE / 1.000000000001;
const MAX_STRING = Number.MAX_VALUE / 1.000000000002;
const MAX_TRUE = Number.MAX_VALUE;
const operatorPriority = {
    '^': 3,
    '*': 2,
    '/': 2,
    '+': 1,
    '-': 1,
    '=': 0,
    '<': 0,
    '<=': 0,
    '>': 0,
    '>=': 0
};
const processorFunctions = {};
const processorFunctionNameRegExp = /^[A-Z][A-Z\.]*$/;
/* *
 *
 *  Functions
 *
 * */
/**
 * Converts non-number types to logical numbers.
 *
 * @param {Highcharts.FormulaValue} value
 * Value to convert.
 *
 * @return {number}
 * Logical number value. `NaN` if not convertable.
 */
function asLogicalNumber(value) {
    switch (typeof value) {
        case 'boolean':
            return value ? MAX_TRUE : MAX_FALSE;
        case 'string':
            return MAX_STRING;
        case 'number':
            return value;
        default:
            return NaN;
    }
}
/**
 * Converts strings to logical strings, while other types get passed through. In
 * logical strings the space character is the lowest value and letters are case
 * insensitive.
 *
 * @param {Highcharts.FormulaValue} value
 * Value to convert.
 *
 * @return {Highcharts.FormulaValue}
 * Logical string value or passed through value.
 */
function asLogicalString(value) {
    if (typeof value === 'string') {
        return value.toLowerCase().replace(asLogicalStringRegExp, '\0');
    }
    return value;
}
/**
 * Converts non-number types to a logic number.
 *
 * @param {Highcharts.FormulaValue} value
 * Value to convert.
 *
 * @return {number}
 * Number value. `NaN` if not convertable.
 */
function FormulaProcessor_asNumber(value) {
    switch (typeof value) {
        case 'boolean':
            return value ? 1 : 0;
        case 'string':
            return parseFloat(value.replace(',', '.'));
        case 'number':
            return value;
        default:
            return NaN;
    }
}
/**
 * Process a basic operation of two given values.
 *
 * @private
 *
 * @param {Highcharts.FormulaOperator} operator
 * Operator between values.
 *
 * @param {Highcharts.FormulaValue} x
 * First value for operation.
 *
 * @param {Highcharts.FormulaValue} y
 * Second value for operation.
 *
 * @return {Highcharts.FormulaValue}
 * Operation result. `NaN` if operation is not support.
 */
function basicOperation(operator, x, y) {
    switch (operator) {
        case '=':
            return asLogicalString(x) === asLogicalString(y);
        case '<':
            if (typeof x === typeof y) {
                return asLogicalString(x) < asLogicalString(y);
            }
            return asLogicalNumber(x) < asLogicalNumber(y);
        case '<=':
            if (typeof x === typeof y) {
                return asLogicalString(x) <= asLogicalString(y);
            }
            return asLogicalNumber(x) <= asLogicalNumber(y);
        case '>':
            if (typeof x === typeof y) {
                return asLogicalString(x) > asLogicalString(y);
            }
            return asLogicalNumber(x) > asLogicalNumber(y);
        case '>=':
            if (typeof x === typeof y) {
                return asLogicalString(x) >= asLogicalString(y);
            }
            return asLogicalNumber(x) >= asLogicalNumber(y);
    }
    x = FormulaProcessor_asNumber(x);
    y = FormulaProcessor_asNumber(y);
    let result;
    switch (operator) {
        case '+':
            result = x + y;
            break;
        case '-':
            result = x - y;
            break;
        case '*':
            result = x * y;
            break;
        case '/':
            result = x / y;
            break;
        case '^':
            result = Math.pow(x, y);
            break;
        default:
            return NaN;
    }
    // Limit decimal to 9 digits
    return (result % 1 ?
        Math.round(result * 1000000000) / 1000000000 :
        result);
}
/**
 * Converts an argument to Value and in case of a range to an array of Values.
 *
 * @function Highcharts.Formula.getArgumentValue
 *
 * @param {Highcharts.FormulaRange|Highcharts.FormulaTerm} arg
 * Formula range or term to convert.
 *
 * @param {Highcharts.DataTable} [table]
 * Table to use for references and ranges.
 *
 * @return {Highcharts.FormulaValue|Array<Highcharts.FormulaValue>}
 * Converted value.
 */
function getArgumentValue(arg, table) {
    // Add value
    if (FormulaProcessor_isValue(arg)) {
        return arg;
    }
    // Add values of a range
    if (FormulaProcessor_isRange(arg)) {
        return (table && getRangeValues(arg, table) || []);
    }
    // Add values of a function
    if (FormulaProcessor_isFunction(arg)) {
        return processFunction(arg, table);
    }
    // Process functions, operations, references with formula processor
    return processFormula((FormulaProcessor_isFormula(arg) ? arg : [arg]), table);
}
/**
 * Converts all arguments to Values and in case of ranges to arrays of Values.
 *
 * @function Highcharts.Formula.getArgumentsValues
 *
 * @param {Highcharts.FormulaArguments} args
 * Formula arguments to convert.
 *
 * @param {Highcharts.DataTable} [table]
 * Table to use for references and ranges.
 *
 * @return {Array<(Highcharts.FormulaValue|Array<Highcharts.FormulaValue>)>}
 * Converted values.
 */
function getArgumentsValues(args, table) {
    const values = [];
    for (let i = 0, iEnd = args.length; i < iEnd; ++i) {
        values.push(getArgumentValue(args[i], table));
    }
    return values;
}
/**
 * Extracts cell values from a table for a given range.
 *
 * @function Highcharts.Formula.getRangeValues
 *
 * @param {Highcharts.FormulaRange} range
 * Formula range to use.
 *
 * @param {Highcharts.DataTable} table
 * Table to extract from.
 *
 * @return {Array<Highcharts.FormulaValue>}
 * Extracted values.
 */
function getRangeValues(range, table) {
    const columnIds = table
        .getColumnIds()
        .slice(range.beginColumn, range.endColumn + 1), values = [];
    for (let i = 0, iEnd = columnIds.length, cell; i < iEnd; ++i) {
        const cells = table.getColumn(columnIds[i], true) || [];
        for (let j = range.beginRow, jEnd = range.endRow + 1; j < jEnd; ++j) {
            cell = cells[j];
            if (typeof cell === 'string' &&
                cell[0] === '=' &&
                table !== table.getModified()) {
                // Look in the modified table for formula result
                cell = table.getModified().getCell(columnIds[i], j);
            }
            values.push(FormulaProcessor_isValue(cell) ? cell : NaN);
        }
    }
    return values;
}
/**
 * Extracts the cell value from a table for a given reference.
 *
 * @private
 *
 * @param {Highcharts.FormulaReference} reference
 * Formula reference to use.
 *
 * @param {Highcharts.DataTable} table
 * Table to extract from.
 *
 * @return {Highcharts.FormulaValue}
 * Extracted value. 'undefined' might also indicate that the cell was not found.
 */
function getReferenceValue(reference, table) {
    const columnId = table.getColumnIds()[reference.column];
    if (columnId) {
        const cell = table.getCell(columnId, reference.row);
        if (typeof cell === 'string' &&
            cell[0] === '=' &&
            table !== table.getModified()) {
            // Look in the modified table for formula result
            const result = table.getModified().getCell(columnId, reference.row);
            return FormulaProcessor_isValue(result) ? result : NaN;
        }
        if (FormulaProcessor_isValue(cell)) {
            return reference.isNegative ? -cell : cell;
        }
        return NaN;
    }
    return NaN;
}
/**
 * Calculates a value based on the two top values and the related operator.
 *
 * Used to properly process the formula's values based on its operators.
 *
 * @private
 * @function Highcharts.applyOperator
 *
 * @param {Array<Highcharts.Value>} values
 * Processed formula values.
 *
 * @param {Array<Highcharts.Operator>} operators
 * Processed formula operators.
 */
function applyOperator(values, operators) {
    if (values.length < 2 || operators.length < 1) {
        values.push(NaN);
    }
    const secondValue = values.pop();
    const firstValue = values.pop();
    const operator = operators.pop();
    if (!defined(secondValue) || !defined(firstValue) || !defined(operator)) {
        values.push(NaN);
    }
    else {
        values.push(basicOperation(operator, firstValue, secondValue));
    }
}
/**
 * Processes a formula array on the given table. If the formula does not contain
 * references or ranges, then no table has to be provided.
 *
 * Performs formulas considering the operators precedence.
 *
 * // Example of the `2 * 3 + 4` formula:
 * 2 -> values: [2], operators: []
 * * -> values: [2], operators: [*]
 * 3 -> values: [2, 3], operators: [*]
 * // Since the higher precedence operator exists (* > +), perform it first.
 * + -> values: [6], operators: [+]
 * 4 -> values: [6, 4], operators: [+]
 * // When non-higher precedence operators remain, perform rest calculations.
 * -> values: [10], operators: []
 *
 * @private
 * @function Highcharts.processFormula
 *
 * @param {Highcharts.Formula} formula
 * Formula array to process.
 *
 * @param {Highcharts.DataTable} [table]
 * Table to use for references and ranges.
 *
 * @return {Highcharts.FormulaValue}
 * Result value of the process. `NaN` indicates an error.
 */
function processFormula(formula, table) {
    // Keeps all the values to calculate them in a proper priority, based on the
    // given operators.
    const values = [];
    // Keeps all the operators to calculate the values above, following the
    // proper priority.
    const operators = [];
    // Indicates if the next item is a value (not an operator).
    let expectingValue = true;
    for (let i = 0, iEnd = formula.length; i < iEnd; ++i) {
        const item = formula[i];
        if (FormulaProcessor_isOperator(item)) {
            if (expectingValue && item === '-') {
                // Split the negative values to be handled as a binary
                // operation if the next item is a value.
                values.push(0);
                operators.push('-');
                expectingValue = true;
            }
            else {
                // Perform if the higher precedence operator exist.
                while (operators.length &&
                    operatorPriority[operators[operators.length - 1]] >=
                        operatorPriority[item]) {
                    applyOperator(values, operators);
                }
                operators.push(item);
                expectingValue = true;
            }
            continue;
        }
        let value;
        // Assign the proper value, starting from the most common types.
        if (FormulaProcessor_isValue(item)) {
            value = item;
        }
        else if (FormulaProcessor_isReference(item)) {
            value = table ? getReferenceValue(item, table) : NaN;
        }
        else if (FormulaProcessor_isFunction(item)) {
            const result = processFunction(item, table);
            value = FormulaProcessor_isValue(result) ? result : NaN;
        }
        else if (FormulaProcessor_isFormula(item)) {
            value = processFormula(item, table);
        }
        if (typeof value !== 'undefined') {
            values.push(value);
            expectingValue = false;
        }
        else {
            return NaN;
        }
    }
    // Handle the remaining operators that weren't taken into consideration, due
    // to non-higher precedence.
    while (operators.length) {
        applyOperator(values, operators);
    }
    if (values.length !== 1) {
        return NaN;
    }
    return values[0];
}
/**
 * Process a function on the given table. If the arguments do not contain
 * references or ranges, then no table has to be provided.
 *
 * @private
 *
 * @param {Highcharts.FormulaFunction} formulaFunction
 * Formula function to process.
 *
 * @param {Highcharts.DataTable} [table]
 * Table to use for references and ranges.
 *
 * @param {Highcharts.FormulaReference} [reference]
 * Table cell reference to use for relative references and ranges.
 *
 * @return {Highcharts.FormulaValue|Array<Highcharts.FormulaValue>}
 * Result value (or values) of the process. `NaN` indicates an error.
 */
function processFunction(formulaFunction, table, 
// eslint-disable-next-line @typescript-eslint/no-unused-vars
reference // @todo
) {
    const processor = processorFunctions[formulaFunction.name];
    if (processor) {
        try {
            return processor(formulaFunction.args, table);
        }
        catch {
            return NaN;
        }
    }
    const error = new Error(`Function "${formulaFunction.name}" not found.`);
    error.name = 'FormulaProcessError';
    throw error;
}
/**
 * Registers a function for the FormulaProcessor.
 *
 * @param {string} name
 * Name of the function in spreadsheets notation with upper case.
 *
 * @param {Highcharts.FormulaFunction} processorFunction
 * ProcessorFunction for the FormulaProcessor. This is an object so that it
 * can take additional parameter for future validation routines.
 *
 * @return {boolean}
 * Return true, if the ProcessorFunction has been registered.
 */
function registerProcessorFunction(name, processorFunction) {
    return (processorFunctionNameRegExp.test(name) &&
        !processorFunctions[name] &&
        !!(processorFunctions[name] = processorFunction));
}
/**
 * Translates relative references and ranges in-place.
 *
 * @param {Highcharts.Formula} formula
 * Formula to translate references and ranges in.
 *
 * @param {number} [columnDelta=0]
 * Column delta to translate to. Negative translate back.
 *
 * @param {number} [rowDelta=0]
 * Row delta to translate to. Negative numbers translate back.
 *
 * @return {Highcharts.Formula}
 * Formula with translated reference and ranges. This formula is equal to the
 * first argument.
 */
function translateReferences(formula, columnDelta = 0, rowDelta = 0) {
    for (let i = 0, iEnd = formula.length, item; i < iEnd; ++i) {
        item = formula[i];
        if (Array.isArray(item)) {
            translateReferences(item, columnDelta, rowDelta);
        }
        else if (FormulaProcessor_isFunction(item)) {
            translateReferences(item.args, columnDelta, rowDelta);
        }
        else if (FormulaProcessor_isRange(item)) {
            if (item.beginColumnRelative) {
                item.beginColumn += columnDelta;
            }
            if (item.beginRowRelative) {
                item.beginRow += rowDelta;
            }
            if (item.endColumnRelative) {
                item.endColumn += columnDelta;
            }
            if (item.endRowRelative) {
                item.endRow += rowDelta;
            }
        }
        else if (FormulaProcessor_isReference(item)) {
            if (item.columnRelative) {
                item.column += columnDelta;
            }
            if (item.rowRelative) {
                item.row += rowDelta;
            }
        }
    }
    return formula;
}
/* *
 *
 *  Default Export
 *
 * */
const FormulaProcessor = {
    asNumber: FormulaProcessor_asNumber,
    getArgumentValue,
    getArgumentsValues,
    getRangeValues,
    getReferenceValue,
    processFormula,
    processorFunctions,
    registerProcessorFunction,
    translateReferences
};
/* harmony default export */ const Formula_FormulaProcessor = (FormulaProcessor);

;// ./code/dashboards/es-modules/Data/Formula/Functions/SUM.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */


/* *
 *
 *  Functions
 *
 * */
/**
 * Processor for the `SUM(...values)` implementation. Calculates the sum of the
 * given values.
 *
 * @private
 * @function Formula.processorFunctions.SUM
 *
 * @param {Highcharts.FormulaArguments} args
 * Arguments to process.
 *
 * @param {Highcharts.DataTable} [table]
 * Table to process.
 *
 * @return {number}
 * Result value of the process.
 */
function SUM(args, table) {
    const values = Formula_FormulaProcessor.getArgumentsValues(args, table);
    let result = 0;
    for (let i = 0, iEnd = values.length, value; i < iEnd; ++i) {
        value = values[i];
        switch (typeof value) {
            case 'number':
                if (!isNaN(value)) {
                    result += value;
                }
                break;
            case 'object':
                result += SUM(value, table);
                break;
        }
    }
    return result;
}
/* *
 *
 *  Registry
 *
 * */
Formula_FormulaProcessor.registerProcessorFunction('SUM', SUM); // 🐝
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Functions_SUM = (SUM);

;// ./code/dashboards/es-modules/Data/Formula/Functions/AVERAGE.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */


const { getArgumentsValues: AVERAGE_getArgumentsValues } = Formula_FormulaProcessor;
/* *
 *
 *  Functions
 *
 * */
/**
 * Processor for the `AVERAGE(...values)` implementation. Calculates the average
 * of the given values that are numbers.
 *
 * @private
 * @function Formula.processorFunctions.AVERAGE
 *
 * @param {Highcharts.FormulaArguments} args
 * Arguments to process.
 *
 * @param {Highcharts.DataTable} [table]
 * Table to use for references and ranges.
 *
 * @return {number}
 * Result value of the process.
 */
function AVERAGE(args, table) {
    const values = AVERAGE_getArgumentsValues(args, table);
    let count = 0, result = 0;
    for (let i = 0, iEnd = values.length, value; i < iEnd; ++i) {
        value = values[i];
        switch (typeof value) {
            case 'number':
                if (!isNaN(value)) {
                    ++count;
                    result += value;
                }
                break;
            case 'object':
                for (let j = 0, jEnd = value.length, value2; j < jEnd; ++j) {
                    value2 = value[j];
                    if (typeof value2 === 'number' &&
                        !isNaN(value2)) {
                        ++count;
                        result += value2;
                    }
                }
                break;
        }
    }
    return (count ? (result / count) : 0);
}
/* *
 *
 *  Registry
 *
 * */
Formula_FormulaProcessor.registerProcessorFunction('AVERAGE', AVERAGE);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Functions_AVERAGE = (AVERAGE);

;// ./code/dashboards/es-modules/Data/Formula/Functions/MEDIAN.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */


/* *
 *
 *  Functions
 *
 * */
/**
 * Processor for the `MEDIAN(...values)` implementation. Calculates the median
 * average of the given values.
 *
 * @private
 * @function Formula.processorFunctions.MEDIAN
 *
 * @param {Highcharts.FormulaArguments} args
 * Arguments to process.
 *
 * @param {Highcharts.DataTable} [table]
 * Table to process.
 *
 * @return {number}
 * Result value of the process.
 */
function MEDIAN(args, table) {
    const median = [], values = Formula_FormulaProcessor.getArgumentsValues(args, table);
    for (let i = 0, iEnd = values.length, value; i < iEnd; ++i) {
        value = values[i];
        switch (typeof value) {
            case 'number':
                if (!isNaN(value)) {
                    median.push(value);
                }
                break;
            case 'object':
                for (let j = 0, jEnd = value.length, value2; j < jEnd; ++j) {
                    value2 = value[j];
                    if (typeof value2 === 'number' &&
                        !isNaN(value2)) {
                        median.push(value2);
                    }
                }
                break;
        }
    }
    const count = median.length;
    if (!count) {
        return NaN;
    }
    const half = Math.floor(count / 2); // Floor because index starts at 0
    return (count % 2 ?
        median[half] : // Odd
        (median[half - 1] + median[half]) / 2 // Even
    );
}
/* *
 *
 *  Registry
 *
 * */
Formula_FormulaProcessor.registerProcessorFunction('MEDIAN', MEDIAN);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Functions_MEDIAN = (MEDIAN);

;// ./code/dashboards/es-modules/Data/Formula/Functions/MAX.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */


const { getArgumentsValues: MAX_getArgumentsValues } = Formula_FormulaProcessor;
/* *
 *
 *  Functions
 *
 * */
/**
 * Processor for the `MAX(...values)` implementation. Calculates the largest
 * of the given values that are numbers.
 *
 * @private
 * @function Formula.processorFunctions.MAX
 *
 * @param {Highcharts.FormulaArguments} args
 * Arguments to process.
 *
 * @param {Highcharts.DataTable} [table]
 * Table to use for references and ranges.
 *
 * @return {number}
 * Result value of the process.
 */
function MAX(args, table) {
    const values = MAX_getArgumentsValues(args, table);
    let result = Number.NEGATIVE_INFINITY;
    for (let i = 0, iEnd = values.length, value; i < iEnd; ++i) {
        value = values[i];
        switch (typeof value) {
            case 'number':
                if (value > result) {
                    result = value;
                }
                break;
            case 'object':
                value = MAX(value);
                if (value > result) {
                    result = value;
                }
                break;
        }
    }
    return isFinite(result) ? result : 0;
}
/* *
 *
 *  Registry
 *
 * */
Formula_FormulaProcessor.registerProcessorFunction('MAX', MAX);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Functions_MAX = (MAX);

;// ./code/dashboards/es-modules/Data/Formula/Functions/MIN.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */


const { getArgumentsValues: MIN_getArgumentsValues } = Formula_FormulaProcessor;
/* *
 *
 *  Functions
 *
 * */
/**
 * Processor for the `MIN(...values)` implementation. Calculates the lowest
 * of the given values that are numbers.
 *
 * @private
 * @function Formula.processorFunctions.MIN
 *
 * @param {Highcharts.FormulaArguments} args
 * Arguments to process.
 *
 * @param {Highcharts.DataTable} [table]
 * Table to use for references and ranges.
 *
 * @return {number}
 * Result value of the process.
 */
function MIN(args, table) {
    const values = MIN_getArgumentsValues(args, table);
    let result = Number.POSITIVE_INFINITY;
    for (let i = 0, iEnd = values.length, value; i < iEnd; ++i) {
        value = values[i];
        switch (typeof value) {
            case 'number':
                if (value < result) {
                    result = value;
                }
                break;
            case 'object':
                value = MIN(value);
                if (value < result) {
                    result = value;
                }
                break;
        }
    }
    return isFinite(result) ? result : 0;
}
/* *
 *
 *  Registry
 *
 * */
Formula_FormulaProcessor.registerProcessorFunction('MIN', MIN);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Functions_MIN = (MIN);

;// ./code/dashboards/es-modules/Data/Formula/Functions/COUNT.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */


/* *
 *
 *  Functions
 *
 * */
/**
 * Processor for the `COUNT(...values)` implementation. Returns the count of
 * given values that are numbers.
 *
 * @private
 * @function Formula.processorFunctions.COUNT
 *
 * @param {Highcharts.FormulaArguments} args
 * Arguments to process.
 *
 * @param {Highcharts.DataTable} [table]
 * Table to use for references and ranges.
 *
 * @return {number}
 * Result value of the process.
 */
function COUNT(args, table) {
    const values = Formula_FormulaProcessor.getArgumentsValues(args, table);
    let count = 0;
    for (let i = 0, iEnd = values.length, value; i < iEnd; ++i) {
        value = values[i];
        switch (typeof value) {
            case 'number':
                if (!isNaN(value)) {
                    ++count;
                }
                break;
            case 'object':
                count += COUNT(value, table);
                break;
        }
    }
    return count;
}
/* *
 *
 *  Registry
 *
 * */
Formula_FormulaProcessor.registerProcessorFunction('COUNT', COUNT);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Functions_COUNT = (COUNT);

;// ./code/dashboards/es-modules/Data/Formula/Functions/PRODUCT.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */


const { getArgumentsValues: PRODUCT_getArgumentsValues } = Formula_FormulaProcessor;
/* *
 *
 *  Functions
 *
 * */
/**
 * Processor for the `PRODUCT(...values)` implementation. Calculates the product
 * of the given values.
 *
 * @private
 * @function Formula.processorFunctions.PRODUCT
 *
 * @param {Highcharts.FormulaArguments} args
 * Arguments to process.
 *
 * @param {Highcharts.DataTable} [table]
 * Table to use for references and ranges.
 *
 * @return {number}
 * Result value of the process.
 */
function PRODUCT(args, table) {
    const values = PRODUCT_getArgumentsValues(args, table);
    let result = 1, calculated = false;
    for (let i = 0, iEnd = values.length, value; i < iEnd; ++i) {
        value = values[i];
        switch (typeof value) {
            case 'number':
                if (!isNaN(value)) {
                    calculated = true;
                    result *= value;
                }
                break;
            case 'object':
                calculated = true;
                result *= PRODUCT(value, table);
                break;
        }
    }
    return (calculated ? result : 0);
}
/* *
 *
 *  Registry
 *
 * */
Formula_FormulaProcessor.registerProcessorFunction('PRODUCT', PRODUCT);
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Functions_PRODUCT = (PRODUCT);

;// ./code/dashboards/es-modules/Core/Chart/ChartDefaults.js
/* *
 *
 *  (c) 2010-2026 Highsoft AS
 *  Author: Torstein Hønsi
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 * */

/* *
 *
 *  API Options
 *
 * */
/**
 * General options for the chart.
 *
 * @optionparent chart
 */
const ChartDefaults = {
    /**
     * Default `mapData` for all series, in terms of a GeoJSON or TopoJSON
     * object. If set to a string, it functions as an index into the
     * `Highcharts.maps` array.
     *
     * For picking out individual shapes and geometries to use for each series
     * of the map, see [series.mapData](#series.map.mapData).
     *
     * @sample    maps/demo/geojson
     *            Loading GeoJSON data
     * @sample    maps/chart/topojson
     *            Loading TopoJSON data
     *
     * @type      {string|Array<*>|Highcharts.GeoJSON|Highcharts.TopoJSON}
     * @since     5.0.0
     * @product   highmaps
     * @apioption chart.map
     */
    /**
     * Set lat/lon transformation definitions for the chart. If not defined,
     * these are extracted from the map data.
     *
     * @type      {*}
     * @since     5.0.0
     * @product   highmaps
     * @apioption chart.mapTransforms
     */
    /**
     * When using multiple axes, the ticks of two or more opposite axes
     * will automatically be aligned by adding ticks to the axis or axes
     * with the least ticks, as if `tickAmount` were specified.
     *
     * This can be prevented by setting `alignTicks` to false. If the grid
     * lines look messy, it's a good idea to hide them for the secondary
     * axis by setting `gridLineWidth` to 0.
     *
     * If `startOnTick` or `endOnTick` in the axis options are set to false,
     * then the `alignTicks ` will be disabled for the axis.
     *
     * Disabled for logarithmic axes.
     *
     * @sample {highcharts} highcharts/chart/alignticks-true/
     *         True by default
     * @sample {highcharts} highcharts/chart/alignticks-false/
     *         False
     * @sample {highstock} stock/chart/alignticks-true/
     *         True by default
     * @sample {highstock} stock/chart/alignticks-false/
     *         False
     *
     * @type      {boolean}
     * @default   true
     * @product   highcharts highstock gantt
     * @apioption chart.alignTicks
     */
    /**
     * When using multiple axes, align the thresholds. When this is true, other
     * ticks will also be aligned.
     *
     * Note that for line series and some other series types, the `threshold`
     * option is set to `null` by default. This will in turn cause their y-axis
     * to not have a threshold. In order to avoid that, set the series
     * `threshold` to 0 or another number.
     *
     * If `startOnTick` or `endOnTick` in the axis options are set to false, or
     * if the axis is logarithmic, the threshold will not be aligned.
     *
     * @sample {highcharts} highcharts/chart/alignthresholds/ Set to true
     *
     * @since 10.0.0
     * @product   highcharts highstock gantt
     * @apioption chart.alignThresholds
     */
    alignThresholds: false,
    /**
     * Set the overall animation for all chart updating. Animation can be
     * disabled throughout the chart by setting it to false here. It can
     * be overridden for each individual API method as a function parameter.
     * The only animation not affected by this option is the initial series
     * animation, see [plotOptions.series.animation](
     * #plotOptions.series.animation).
     *
     * The animation can either be set as a boolean or a configuration
     * object. If `true`, it will use the 'swing' jQuery easing and a
     * duration of 500 ms. If used as a configuration object, the following
     * properties are supported:
     *
     * - `defer`: The animation delay time in milliseconds.
     *
     * - `duration`: The duration of the animation in milliseconds.
     *
     * - `easing`: A string reference to an easing function set on the
     *   `Math` object. See
     *   [the easing demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-animation-easing/).
     *
     * When zooming on a series with less than 100 points, the chart redraw
     * will be done with animation, but in case of more data points, it is
     * necessary to set this option to ensure animation on zoom.
     *
     * @sample {highcharts} highcharts/chart/animation-none/
     *         Updating with no animation
     * @sample {highcharts} highcharts/chart/animation-duration/
     *         With a longer duration
     * @sample {highcharts} highcharts/chart/animation-easing/
     *         With a jQuery UI easing
     * @sample {highmaps} maps/chart/animation-none/
     *         Updating with no animation
     * @sample {highmaps} maps/chart/animation-duration/
     *         With a longer duration
     *
     * @type      {boolean|Partial<Highcharts.AnimationOptionsObject>}
     * @default   true
     * @apioption chart.animation
     */
    /**
     * A CSS class name to apply to the charts container `div`, allowing
     * unique CSS styling for each chart.
     *
     * @type      {string}
     * @apioption chart.className
     */
    /**
     * Event listeners for the chart.
     *
     * @apioption chart.events
     */
    /**
     * Fires when a series is added to the chart after load time, using the
     * `addSeries` method. One parameter, `event`, is passed to the
     * function, containing common event information. Through
     * `event.options` you can access the series options that were passed to
     * the `addSeries` method. Returning false prevents the series from
     * being added.
     *
     * @sample {highcharts} highcharts/chart/events-addseries/
     *         Alert on add series
     * @sample {highstock} stock/chart/events-addseries/
     *         Alert on add series
     *
     * @type      {Highcharts.ChartAddSeriesCallbackFunction}
     * @since     1.2.0
     * @context   Highcharts.Chart
     * @apioption chart.events.addSeries
     */
    /**
     * Fires when clicking on the plot background. One parameter, `event`,
     * is passed to the function, containing common event information.
     *
     * Information on the clicked spot can be found through `event.xAxis`
     * and `event.yAxis`, which are arrays containing the axes of each
     * dimension and each axis' value at the clicked spot. The primary axes
     * are `event.xAxis[0]` and `event.yAxis[0]`. Remember the unit of a
     * datetime axis is milliseconds since 1970-01-01 00:00:00.
     *
     * ```js
     * click: function(e) {
     *     console.log(
     *         Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', e.xAxis[0].value),
     *         e.yAxis[0].value
     *     )
     * }
     * ```
     *
     * @sample {highcharts} highcharts/chart/events-click/
     *         Alert coordinates on click
     * @sample {highcharts} highcharts/chart/events-container/
     *         Alternatively, attach event to container
     * @sample {highstock} stock/chart/events-click/
     *         Alert coordinates on click
     * @sample {highstock} highcharts/chart/events-container/
     *         Alternatively, attach event to container
     * @sample {highmaps} maps/chart/events-click/
     *         Record coordinates on click
     * @sample {highmaps} highcharts/chart/events-container/
     *         Alternatively, attach event to container
     *
     * @type      {Highcharts.ChartClickCallbackFunction}
     * @since     1.2.0
     * @context   Highcharts.Chart
     * @apioption chart.events.click
     */
    /**
     * Fires when the chart is finished loading. Since v4.2.2, it also waits
     * for images to be loaded, for example from point markers. One
     * parameter, `event`, is passed to the function, containing common
     * event information.
     *
     * There is also a second parameter to the chart constructor where a
     * callback function can be passed to be executed on chart.load.
     *
     * @sample {highcharts} highcharts/chart/events-load/
     *         Alert on chart load
     * @sample {highcharts} highcharts/chart/events-render/
     *         Load vs Redraw vs Render
     * @sample {highstock} stock/chart/events-load/
     *         Alert on chart load
     * @sample {highmaps} maps/chart/events-load/
     *         Add series on chart load
     *
     * @type      {Highcharts.ChartLoadCallbackFunction}
     * @context   Highcharts.Chart
     * @apioption chart.events.load
     */
    /**
     * Fires when the chart is redrawn, either after a call to
     * `chart.redraw()` or after an axis, series or point is modified with
     * the `redraw` option set to `true`. One parameter, `event`, is passed
     * to the function, containing common event information.
     *
     * @sample {highcharts} highcharts/chart/events-redraw/
     *         Alert on chart redraw
     * @sample {highcharts} highcharts/chart/events-render/
     *         Load vs Redraw vs Render
     * @sample {highstock} stock/chart/events-redraw/
     *         Alert on chart redraw when adding a series or moving the
     *         zoomed range
     * @sample {highmaps} maps/chart/events-redraw/
     *         Set subtitle on chart redraw
     *
     * @type      {Highcharts.ChartRedrawCallbackFunction}
     * @since     1.2.0
     * @context   Highcharts.Chart
     * @apioption chart.events.redraw
     */
    /**
     * Fires after initial load of the chart (directly after the `load`
     * event), and after each redraw (directly after the `redraw` event).
     *
     * @sample {highcharts} highcharts/chart/events-render/
     *         Load vs Redraw vs Render
     *
     * @type      {Highcharts.ChartRenderCallbackFunction}
     * @since     5.0.7
     * @context   Highcharts.Chart
     * @apioption chart.events.render
     */
    /**
     * Fires when an area of the chart has been selected. Selection is
     * enabled by setting the chart's zoomType. One parameter, `event`, is
     * passed to the function, containing common event information. The
     * default action for the selection event is to zoom the chart to the
     * selected area. It can be prevented by calling
     * `event.preventDefault()` or return false.
     *
     * Information on the selected area can be found through `event.xAxis`
     * and `event.yAxis`, which are arrays containing the axes of each
     * dimension and each axis' min and max values. The primary axes are
     * `event.xAxis[0]` and `event.yAxis[0]`. Remember the unit of a
     * datetime axis is milliseconds since 1970-01-01 00:00:00.
     *
     * ```js
     * selection: function(event) {
     *     // log the min and max of the primary, datetime x-axis
     *     console.log(
     *         Highcharts.dateFormat(
     *             '%Y-%m-%d %H:%M:%S',
     *             event.xAxis[0].min
     *         ),
     *         Highcharts.dateFormat(
     *             '%Y-%m-%d %H:%M:%S',
     *             event.xAxis[0].max
     *         )
     *     );
     *     // log the min and max of the y axis
     *     console.log(event.yAxis[0].min, event.yAxis[0].max);
     * }
     * ```
     *
     * @sample {highcharts} highcharts/chart/events-selection/
     *         Report on selection and reset
     * @sample {highcharts} highcharts/chart/events-selection-points/
     *         Select a range of points through a drag selection
     * @sample {highstock} stock/chart/events-selection/
     *         Report on selection and reset
     * @sample {highstock} highcharts/chart/events-selection-points/
     *         Select a range of points through a drag selection
     *         (Highcharts)
     *
     * @type      {Highcharts.ChartSelectionCallbackFunction}
     * @apioption chart.events.selection
     */
    /**
     * The margin between the outer edge of the chart and the plot area.
     * The numbers in the array designate top, right, bottom and left
     * respectively. Use the options `marginTop`, `marginRight`,
     * `marginBottom` and `marginLeft` for shorthand setting of one option.
     *
     * By default there is no margin. The actual space is dynamically
     * calculated from the offset of axis labels, axis title, title,
     * subtitle and legend in addition to the `spacingTop`, `spacingRight`,
     * `spacingBottom` and `spacingLeft` options.
     *
     * @sample {highcharts} highcharts/chart/margins-zero/
     *         Zero margins
     * @sample {highstock} stock/chart/margin-zero/
     *         Zero margins
     *
     * @type      {number|Array<number>}
     * @apioption chart.margin
     */
    /**
     * The margin between the bottom outer edge of the chart and the plot
     * area. Use this to set a fixed pixel value for the margin as opposed
     * to the default dynamic margin. See also `spacingBottom`.
     *
     * @sample {highcharts} highcharts/chart/marginbottom/
     *         100px bottom margin
     * @sample {highstock} stock/chart/marginbottom/
     *         100px bottom margin
     * @sample {highmaps} maps/chart/margin/
     *         100px margins
     *
     * @type      {number}
     * @since     2.0
     * @apioption chart.marginBottom
     */
    /**
     * The margin between the left outer edge of the chart and the plot
     * area. Use this to set a fixed pixel value for the margin as opposed
     * to the default dynamic margin. See also `spacingLeft`.
     *
     * @sample {highcharts} highcharts/chart/marginleft/
     *         150px left margin
     * @sample {highstock} stock/chart/marginleft/
     *         150px left margin
     * @sample {highmaps} maps/chart/margin/
     *         100px margins
     *
     * @type      {number}
     * @since     2.0
     * @apioption chart.marginLeft
     */
    /**
     * The margin between the right outer edge of the chart and the plot
     * area. Use this to set a fixed pixel value for the margin as opposed
     * to the default dynamic margin. See also `spacingRight`.
     *
     * @sample {highcharts} highcharts/chart/marginright/
     *         100px right margin
     * @sample {highstock} stock/chart/marginright/
     *         100px right margin
     * @sample {highmaps} maps/chart/margin/
     *         100px margins
     *
     * @type      {number}
     * @since     2.0
     * @apioption chart.marginRight
     */
    /**
     * The margin between the top outer edge of the chart and the plot area.
     * Use this to set a fixed pixel value for the margin as opposed to
     * the default dynamic margin. See also `spacingTop`.
     *
     * @sample {highcharts} highcharts/chart/margintop/ 100px top margin
     * @sample {highstock} stock/chart/margintop/
     *         100px top margin
     * @sample {highmaps} maps/chart/margin/
     *         100px margins
     *
     * @type      {number}
     * @since     2.0
     * @apioption chart.marginTop
     */
    /**
     * Callback function to override the default function that formats all
     * the numbers in the chart. Returns a string with the formatted number.
     * Since v12.6.0, the callback also receives `ctx` as the last argument,
     * so that arrow functions can access the same context as regular
     * functions using `this`.
     *
     * @sample highcharts/members/highcharts-numberformat
     *      Arabic digits in Highcharts
     * @type {Highcharts.NumberFormatterCallbackFunction}
     * @since 8.0.0
     * @apioption chart.numberFormatter
     */
    /**
     * When a chart with an x and a y-axis is rendered, we first pre-render the
     * labels of both in order to measure them. Then, if either of the axis
     * labels take up so much space that it significantly affects the length of
     * the other axis, we repeat the process.
     *
     * By default we stop at two axis layout runs, but it may be that the second
     * run also alter the space required by either axis, for example if it
     * causes the labels to rotate. In this situation, a subsequent redraw of
     * the chart may cause the tick and label placement to change for apparently
     * no reason.
     *
     * Use the `axisLayoutRuns` option to set the maximum allowed number of
     * repetitions. But keep in mind that the default value of 2 is set because
     * every run costs performance time.
     *
     * **Note:** Changing that option to higher than the default might decrease
     * performance significantly, especially with bigger sets of data.
     *
     * @type      {number}
     * @default   2
     * @since     11.3.0
     * @apioption chart.axisLayoutRuns
     */
    /**
     * Allows setting a key to switch between zooming and panning. Can be
     * one of `alt`, `ctrl`, `meta` (the command key on Mac and Windows
     * key on Windows) or `shift`. The keys are mapped directly to the key
     * properties of the click event argument (`event.altKey`,
     * `event.ctrlKey`, `event.metaKey` and `event.shiftKey`).
     *
     * @type       {string}
     * @since      4.0.3
     * @product    highcharts gantt
     * @validvalue ["alt", "ctrl", "meta", "shift"]
     * @apioption  chart.panKey
     */
    /**
     * Allow panning in a chart. Best used with [panKey](#chart.panKey)
     * to combine zooming and panning.
     *
     * On touch devices, when the [tooltip.followTouchMove](
     * #tooltip.followTouchMove) option is `true` (default), panning
     * requires two fingers. To allow panning with one finger, set
     * `followTouchMove` to `false`.
     *
     * **Note:** If both zooming and panning are enabled without keys, zooming
     * will take precedence by default. To prioritize panning, either set
     * [chart.zooming.key](#chart.zooming.key) or panKey.
     *
     * @sample  {highcharts} highcharts/chart/pankey/ Zooming and panning
     * @sample  {highstock} stock/chart/panning/ Zooming and xy panning
     */
    panning: {
        /**
         * Enable or disable chart panning.
         *
         * @type      {boolean}
         * @default   {highcharts} false
         * @default   {highstock|highmaps} true
         */
        enabled: false,
        /**
         * Decides in what dimensions the user can pan the chart. Can be
         * one of `x`, `y`, or `xy`.
         *
         * During panning, all axes will behave as if
         * [`startOnTick`](#yAxis.startOnTick) and
         * [`endOnTick`](#yAxis.endOnTick) were set to `false`. After the
         * panning action is finished, the axes will adjust to their actual
         * settings.
         *
         * **Note:** For non-cartesian series, the only supported panning type
         * is `xy`, as zooming in a single direction is not applicable due to
         * the radial nature of the coordinate system.
         *
         * @sample {highcharts} highcharts/chart/panning-type
         *         Zooming and xy panning
         *
         * @declare    Highcharts.OptionsChartPanningTypeValue
         * @type       {string}
         * @validvalue ["x", "y", "xy"]
         * @product    highcharts highstock gantt
         */
        type: 'x'
    },
    /**
     * Equivalent to [zoomType](#chart.zoomType), but for multitouch
     * gestures only. By default, the `pinchType` is the same as the
     * `zoomType` setting. However, pinching can be enabled separately in
     * some cases, for example in stock charts where a mouse drag pans the
     * chart, while pinching is enabled. When [tooltip.followTouchMove](
     * #tooltip.followTouchMove) is true, pinchType only applies to
     * two-finger touches.
     *
     * @type       {string}
     * @default    {highcharts} undefined
     * @default    {highstock} undefined
     * @since      3.0
     * @product    highcharts highstock gantt
     * @deprecated
     * @validvalue ["x", "y", "xy"]
     * @apioption  chart.pinchType
     */
    /**
     * Whether to apply styled mode. When in styled mode, no presentational
     * attributes or CSS are applied to the chart SVG. Instead, CSS rules
     * are required to style the chart. The default style sheet is
     * available from `https://code.highcharts.com/css/highcharts.css`.
     *
     * [Read more in the docs](https://www.highcharts.com/docs/chart-design-and-style/style-by-css)
     * on what classes and variables are available.
     *
     * @sample highcharts/css/colors
     *         Color theming with CSS
     * @sample highcharts/css/prefers-color-scheme
     *         Dynamic theme based on system settings
     * @type       {boolean}
     * @default    false
     * @since      7.0
     * @apioption  chart.styledMode
     */
    styledMode: false,
    /**
     * The corner radius of the outer chart border.
     *
     * @sample {highcharts} highcharts/chart/borderradius/
     *         20px radius
     * @sample {highstock} stock/chart/border/
     *         10px radius
     * @sample {highmaps} maps/chart/border/
     *         Border options
     *
     */
    borderRadius: 0,
    /**
     * In styled mode, this sets how many colors the class names
     * should rotate between. With ten colors, series (or points) are
     * given class names like `highcharts-color-0`, `highcharts-color-1`
     * [...] `highcharts-color-9`. The equivalent in non-styled mode
     * is to set colors using the [colors](#colors) setting.
     *
     * @since      5.0.0
     */
    colorCount: 10,
    /**
     * By default, (because of memory and performance reasons) the chart does
     * not copy the data but keeps it as a reference. In some cases, this might
     * result in mutating the original data source. In order to prevent that,
     * set that property to false. Please note that changing that might decrease
     * performance, especially with bigger sets of data.
     *
     * @type       {boolean}
     * @since 10.1.0
     */
    allowMutatingData: true,
    /**
     * If true, the axes will scale to the remaining visible series once
     * one series is hidden. If false, hiding and showing a series will
     * not affect the axes or the other series. For stacks, once one series
     * within the stack is hidden, the rest of the stack will close in
     * around it even if the axis is not affected.
     *
     * @sample {highcharts} highcharts/chart/ignorehiddenseries-true/
     *         True by default
     * @sample {highcharts} highcharts/chart/ignorehiddenseries-false/
     *         False
     * @sample {highcharts} highcharts/chart/ignorehiddenseries-true-stacked/
     *         True with stack
     * @sample {highstock} stock/chart/ignorehiddenseries-true/
     *         True by default
     * @sample {highstock} stock/chart/ignorehiddenseries-false/
     *         False
     *
     * @since   1.2.0
     * @product highcharts highstock gantt
     */
    ignoreHiddenSeries: true,
    /**
     * Whether to invert the axes so that the x axis is vertical and y axis
     * is horizontal. When `true`, the x axis is [reversed](#xAxis.reversed)
     * by default.
     *
     * @productdesc {highcharts}
     * If a bar series is present in the chart, it will be inverted
     * automatically. Inverting the chart doesn't have an effect if there
     * are no cartesian series in the chart.
     *
     * @sample {highcharts} highcharts/chart/inverted/
     *         Inverted line
     * @sample {highstock} stock/navigator/inverted/
     *         Inverted stock chart
     *
     * @type      {boolean}
     * @default   false
     * @product   highcharts highstock gantt
     * @apioption chart.inverted
     */
    /**
     * The distance between the outer edge of the chart and the content,
     * like title or legend, or axis title and labels if present. The
     * numbers in the array designate top, right, bottom and left
     * respectively. Use the options spacingTop, spacingRight, spacingBottom
     * and spacingLeft options for shorthand setting of one option.
     *
     * @type    {Array<number>}
     * @see     [chart.margin](#chart.margin)
     * @default [10, 10, 15, 10]
     * @since   3.0.6
     */
    spacing: [10, 10, 15, 10],
    /**
     * The button that appears after a selection zoom, allowing the user
     * to reset zoom. This option is deprecated in favor of
     * [zooming](#chart.zooming).
     *
     * @since      2.2
     * @deprecated 10.2.1
     */
    resetZoomButton: {
        /**
         * What frame the button placement should be related to. Can be
         * either `plotBox` or `spacingBox`.
         *
         * @sample {highcharts} highcharts/chart/resetzoombutton-relativeto/
         *         Relative to the chart
         * @sample {highstock} highcharts/chart/resetzoombutton-relativeto/
         *         Relative to the chart
         *
         * @type      {Highcharts.ButtonRelativeToValue}
         * @apioption chart.resetZoomButton.relativeTo
         */
        /**
         * A collection of attributes for the button. The object takes SVG
         * attributes like `fill`, `stroke`, `stroke-width` or `r`, the
         * border radius. The theme also supports `style`, a collection of
         * CSS properties for the text. Equivalent attributes for the hover
         * state are given in `theme.states.hover`.
         *
         * @sample {highcharts} highcharts/chart/resetzoombutton-theme/
         *         Theming the button
         * @sample {highstock} highcharts/chart/resetzoombutton-theme/
         *         Theming the button
         *
         * @type {Highcharts.SVGAttributes}
         */
        theme: {
        /**
         * The z-index of the button.
         *
         * @type {number}
         * @apioption chart.resetZoomButton.theme.zIndex
         */
        },
        /**
         * The position of the button.
         *
         * @sample {highcharts} highcharts/chart/resetzoombutton-position/
         *         Above the plot area
         * @sample {highstock} highcharts/chart/resetzoombutton-position/
         *         Above the plot area
         * @sample {highmaps} highcharts/chart/resetzoombutton-position/
         *         Above the plot area
         *
         * @type {Highcharts.AlignObject}
         */
        position: {
        /**
         * The horizontal alignment of the button.
         *
         * @type {number}
         * @apioption chart.resetZoomButton.position.align
         */
        /**
         * The horizontal offset of the button.
         *
         * @type {number}
         * @apioption chart.resetZoomButton.position.x
         */
        /**
         * The vertical alignment of the button.
         *
         * @type      {Highcharts.VerticalAlignValue}
         * @apioption chart.resetZoomButton.position.verticalAlign
         */
        /**
         * The vertical offset of the button.
         *
         * @type {number}
         * @apioption chart.resetZoomButton.position.y
         */
        }
    },
    /**
     * The pixel width of the plot area border.
     *
     * @sample {highcharts} highcharts/chart/plotborderwidth/
     *         1px border
     * @sample {highstock} stock/chart/plotborder/
     *         2px border
     * @sample {highmaps} maps/chart/plotborder/
     *         Plot border options
     *
     * @type      {number}
     * @default   0
     * @apioption chart.plotBorderWidth
     */
    /**
     * Whether to apply a drop shadow to the plot area. Requires that
     * plotBackgroundColor be set. The shadow can be an object configuration
     * containing `color`, `offsetX`, `offsetY`, `opacity` and `width`.
     *
     * @sample {highcharts} highcharts/chart/plotshadow/
     *         Plot shadow
     * @sample {highstock} stock/chart/plotshadow/
     *         Plot shadow
     * @sample {highmaps} maps/chart/plotborder/
     *         Plot border options
     *
     * @type      {boolean|Highcharts.ShadowOptionsObject}
     * @default   false
     * @apioption chart.plotShadow
     */
    /**
     * When true, cartesian charts like line, spline, area and column are
     * transformed into the polar coordinate system. This produces _polar
     * charts_, also known as _radar charts_.
     *
     * @sample {highcharts} highcharts/demo/polar/
     *         Polar chart
     * @sample {highcharts} highcharts/demo/polar-wind-rose/
     *         Wind rose, stacked polar column chart
     * @sample {highcharts} highcharts/demo/polar-spider/
     *         Spider web chart
     * @sample {highcharts} highcharts/parallel-coordinates/polar/
     *         Star plot, multivariate data in a polar chart
     *
     * @type      {boolean}
     * @default   false
     * @since     2.3.0
     * @product   highcharts
     * @requires  highcharts-more
     * @apioption chart.polar
     */
    /**
     * Whether to reflow the chart to fit the width of the container div
     * on resizing the window.
     *
     * @sample {highcharts} highcharts/chart/reflow-true/
     *         True by default
     * @sample {highcharts} highcharts/chart/reflow-false/
     *         False
     * @sample {highstock} stock/chart/reflow-true/
     *         True by default
     * @sample {highstock} stock/chart/reflow-false/
     *         False
     * @sample {highmaps} maps/chart/reflow-true/
     *         True by default
     * @sample {highmaps} maps/chart/reflow-false/
     *         False
     *
     * @since     2.1
     */
    reflow: true,
    /**
     * The HTML element where the chart will be rendered. If it is a string,
     * the element by that id is used. The HTML element can also be passed
     * by direct reference, or as the first argument of the chart
     * constructor, in which case the option is not needed.
     *
     * @sample {highcharts} highcharts/chart/reflow-true/
     *         String
     * @sample {highcharts} highcharts/chart/renderto-object/
     *         Object reference
     * @sample {highstock} stock/chart/renderto-string/
     *         String
     * @sample {highstock} stock/chart/renderto-object/
     *         Object reference
     *
     * @type      {string|Highcharts.HTMLDOMElement}
     * @apioption chart.renderTo
     */
    /**
     * The background color of the marker square when selecting (zooming
     * in on) an area of the chart.
     *
     * @see In styled mode, the selection marker fill is set with the
     *      `.highcharts-selection-marker` class.
     *
     * @type      {Highcharts.ColorType}
     * @default   rgba(51,92,173,0.25)
     * @since     2.1.7
     * @apioption chart.selectionMarkerFill
     */
    /**
     * Whether to apply a drop shadow to the global series group. This causes
     * all the series to have the same shadow. Contrary to the `series.shadow`
     * option, this prevents items from casting shadows on each other, like for
     * others series in a stack. The shadow can be an object configuration
     * containing `color`, `offsetX`, `offsetY`, `opacity` and `width`.
     *
     * @sample highcharts/chart/seriesgroupshadow/
     *         Shadow
     *
     * @type      {boolean|Highcharts.ShadowOptionsObject}
     * @default   false
     * @apioption chart.seriesGroupShadow
     */
    /**
     * Whether to apply a drop shadow to the outer chart area. Requires
     * that backgroundColor be set. The shadow can be an object
     * configuration containing `color`, `offsetX`, `offsetY`, `opacity` and
     * `width`.
     *
     * @sample {highcharts} highcharts/chart/shadow/
     *         Shadow
     * @sample {highstock} stock/chart/shadow/
     *         Shadow
     * @sample {highmaps} maps/chart/border/
     *         Chart border and shadow
     *
     * @type      {boolean|Highcharts.ShadowOptionsObject}
     * @default   false
     * @apioption chart.shadow
     */
    /**
     * Whether to show the axes initially. This only applies to empty charts
     * where series are added dynamically, as axes are automatically added
     * to cartesian series.
     *
     * @sample {highcharts} highcharts/chart/showaxes-false/
     *         False by default
     * @sample {highcharts} highcharts/chart/showaxes-true/
     *         True
     *
     * @type      {boolean}
     * @since     1.2.5
     * @product   highcharts gantt
     * @apioption chart.showAxes
     */
    /**
     * The space between the bottom edge of the chart and the content (plot
     * area, axis title and labels, title, subtitle or legend in top
     * position).
     *
     * @sample {highcharts} highcharts/chart/spacingbottom/
     *         Spacing bottom set to 100
     * @sample {highstock} stock/chart/spacingbottom/
     *         Spacing bottom set to 100
     * @sample {highmaps} maps/chart/spacing/
     *         Spacing 100 all around
     *
     * @type      {number}
     * @default   15
     * @since     2.1
     * @apioption chart.spacingBottom
     */
    /**
     * The space between the left edge of the chart and the content (plot
     * area, axis title and labels, title, subtitle or legend in top
     * position).
     *
     * @sample {highcharts} highcharts/chart/spacingleft/
     *         Spacing left set to 100
     * @sample {highstock} stock/chart/spacingleft/
     *         Spacing left set to 100
     * @sample {highmaps} maps/chart/spacing/
     *         Spacing 100 all around
     *
     * @type      {number}
     * @default   10
     * @since     2.1
     * @apioption chart.spacingLeft
     */
    /**
     * The space between the right edge of the chart and the content (plot
     * area, axis title and labels, title, subtitle or legend in top
     * position).
     *
     * @sample {highcharts} highcharts/chart/spacingright-100/
     *         Spacing set to 100
     * @sample {highcharts} highcharts/chart/spacingright-legend/
     *         Legend in right position with default spacing
     * @sample {highstock} stock/chart/spacingright/
     *         Spacing set to 100
     * @sample {highmaps} maps/chart/spacing/
     *         Spacing 100 all around
     *
     * @type      {number}
     * @default   10
     * @since     2.1
     * @apioption chart.spacingRight
     */
    /**
     * The space between the top edge of the chart and the content (plot
     * area, axis title and labels, title, subtitle or legend in top
     * position).
     *
     * @sample {highcharts} highcharts/chart/spacingtop-100/
     *         A top spacing of 100
     * @sample {highcharts} highcharts/chart/spacingtop-10/
     *         Floating chart title makes the plot area align to the default
     *         spacingTop of 10.
     * @sample {highstock} stock/chart/spacingtop/
     *         A top spacing of 100
     * @sample {highmaps} maps/chart/spacing/
     *         Spacing 100 all around
     *
     * @type      {number}
     * @default   10
     * @since     2.1
     * @apioption chart.spacingTop
     */
    /**
     * Additional CSS styles to apply inline to the container `div` and the root
     * SVG.
     *
     * According to the CSS syntax documentation, it is recommended to quote
     * font family names that contain white space, digits, or punctuation
     * characters other than hyphens. In such cases, wrap the fontFamily
     * name as follows: `fontFamily: '"Font name"'`.
     *
     * Since v11, the root font size is 1rem by default, and all child element
     * are given a relative `em` font size by default. This allows implementers
     * to control all the chart's font sizes by only setting the root level.
     *
     * @see    In styled mode, general chart styles can be set with the
     *         `.highcharts-root` class.
     * @sample {highcharts} highcharts/chart/style-serif-font/
     *         Using a serif type font
     * @sample {highcharts} highcharts/chart/style-special-font/
     *         Using a font with special character in name
     * @sample {highcharts} highcharts/members/relative-font-size/
     *         Relative font sizes
     * @sample {highcharts} highcharts/css/em/
     *         Styled mode with relative font sizes
     * @sample {highstock} stock/chart/style/
     *         Using a serif type font
     * @sample {highmaps} maps/chart/style-serif-font/
     *         Using a serif type font
     *
     * @type      {Highcharts.CSSObject}
     * @default   {"fontFamily": "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', sans-serif", "fontSize":"1rem"}
     * @apioption chart.style
     */
    /**
     * The default series type for the chart. Can be any of the chart types
     * listed under [plotOptions](#plotOptions) and [series](#series) or can
     * be a series provided by an additional module.
     *
     * In TypeScript this option has no effect in sense of typing and
     * instead the `type` option must always be set in the series.
     *
     * @sample {highcharts} highcharts/chart/type-bar/
     *         Bar
     * @sample {highstock} stock/chart/type/
     *         Areaspline
     * @sample {highmaps} maps/chart/type-mapline/
     *         Mapline
     *
     * @type       {string}
     * @default    {highcharts} line
     * @default    {highstock} line
     * @default    {highmaps} map
     * @since      2.1.0
     * @apioption  chart.type
     */
    type: 'line',
    /**
     * Decides in what dimensions the user can zoom by dragging the mouse.
     * Can be one of `x`, `y` or `xy`.
     *
     * @see [panKey](#chart.panKey)
     *
     * @sample {highcharts} highcharts/chart/zoomtype-none/
     *         None by default
     * @sample {highcharts} highcharts/chart/zoomtype-x/
     *         X
     * @sample {highcharts} highcharts/chart/zoomtype-y/
     *         Y
     * @sample {highcharts} highcharts/chart/zoomtype-xy/
     *         Xy
     * @sample {highcharts} highcharts/chart/zoomtype-polar/
     *         Zoom on polar chart
     * @sample {highstock} stock/demo/basic-line/
     *         None by default
     * @sample {highstock} stock/chart/zoomtype-x/
     *         X
     * @sample {highstock} stock/chart/zoomtype-y/
     *         Y
     * @sample {highstock} stock/chart/zoomtype-xy/
     *         Xy
     * @sample {highmaps} maps/chart/zoomtype-xy/
     *         Map with selection zoom
     *
     * @type       {string}
     * @validvalue ["x", "y", "xy"]
     * @deprecated
     * @apioption  chart.zoomType
     */
    /**
     * Enables zooming by a single touch, in combination with
     * [chart.zoomType](#chart.zoomType). When enabled, two-finger pinch
     * will still work as set up by [chart.pinchType](#chart.pinchType).
     * However, `zoomBySingleTouch` will interfere with touch-dragging the
     * chart to read the tooltip. And especially when vertical zooming is
     * enabled, it will make it hard to scroll vertically on the page.
     * @since      9.0.0
     * @sample     highcharts/chart/zoombysingletouch
     *             Zoom by single touch enabled, with buttons to toggle
     * @product    highcharts highstock gantt
     * @deprecated
     */
    /**
     * Chart zooming options.
     * @since 10.2.1
     *
     * @sample     highcharts/plotoptions/sankey-node-color
     *             Zooming in sankey series
     * @sample     highcharts/series-treegraph/link-types
     *             Zooming in treegraph series
     */
    zooming: {
        /**
         * Equivalent to [type](#chart.zooming.type), but for multitouch
         * gestures only. By default, the `pinchType` is the same as the
         * `type` setting. However, pinching can be enabled separately in
         * some cases, for example in stock charts where a mouse drag pans the
         * chart, while pinching is enabled. When [tooltip.followTouchMove](
         * #tooltip.followTouchMove) is true, pinchType only applies to
         * two-finger touches.
         *
         * @type       {string}
         * @default    {highcharts} undefined
         * @default    {highstock} x
         * @product    highcharts highstock gantt
         * @validvalue ["x", "y", "xy"]
         * @apioption  chart.zooming.pinchType
         */
        /**
         * Decides in what dimensions the user can zoom by dragging the mouse.
         * Can be one of `x`, `y` or `xy`.
         *
         * **Note:** For non-cartesian series, the only supported zooming type
         * is `xy`, as zooming in a single direction is not applicable due to
         * the radial nature of the coordinate system.
         *
         * @declare    Highcharts.OptionsChartZoomingTypeValue
         * @type       {string}
         * @default    {highcharts} undefined
         * @product    highcharts highstock gantt
         * @validvalue ["x", "y", "xy"]
         * @apioption  chart.zooming.type
         */
        /**
         * Set a key to hold when dragging to zoom the chart. This is useful to
         * avoid zooming while moving points. Should be set different than
         * [chart.panKey](#chart.panKey).
         *
         * **Note:** If both zooming and panning are enabled without keys,
         * zooming will take precedence by default. To prioritize panning,
         * either set zooming key or [chart.panKey](#chart.panKey).
         *
         * @type       {string}
         * @default    {highcharts} undefined
         * @validvalue ["alt", "ctrl", "meta", "shift"]
         * @requires   modules/draggable-points
         * @apioption  chart.zooming.key
         */
        /**
         * Enables zooming by a single touch, in combination with
         * [chart.zooming.type](#chart.zooming.type). When enabled, two-finger
         * pinch will still work as set up by [chart.zooming.pinchType]
         * (#chart.zooming.pinchType). However, `singleTouch` will interfere
         * with touch-dragging the chart to read the tooltip. And especially
         * when vertical zooming is enabled, it will make it hard to scroll
         * vertically on the page.
         *
         * @sample  highcharts/chart/zoombysingletouch
         *          Zoom by single touch enabled, with buttons to toggle
         *
         * @product highcharts highstock gantt
         */
        singleTouch: false,
        /**
         * The button that appears after a selection zoom, allowing the user
         * to reset zoom.
         */
        resetButton: {
            /**
             * What frame the button placement should be related to. Can be
             * either `plotBox` or `spacingBox`.
             *
             * @sample {highcharts} highcharts/chart/resetzoombutton-relativeto/
             *         Relative to the chart
             * @sample {highstock} highcharts/chart/resetzoombutton-relativeto/
             *         Relative to the chart
             *
             * @type      {Highcharts.ButtonRelativeToValue}
             * @default   plot
             * @apioption chart.zooming.resetButton.relativeTo
             */
            /**
             * A collection of attributes for the button. The object takes SVG
             * attributes like `fill`, `stroke`, `stroke-width` or `r`, the
             * border radius. The theme also supports `style`, a collection of
             * CSS properties for the text. Equivalent attributes for the hover
             * state are given in `theme.states.hover`.
             *
             * @sample {highcharts} highcharts/chart/resetzoombutton-theme/
             *         Theming the button
             * @sample {highstock} highcharts/chart/resetzoombutton-theme/
             *         Theming the button
             *
             * @default { zIndex: 6 }
             * @type  {Highcharts.SVGAttributes}
             * @since 10.2.1
             */
            theme: {
                /** @internal */
                zIndex: 6
            },
            /**
             * The position of the button.
             *
             * Note: Adjusting position values might cause overlap with chart
             * elements. Ensure coordinates do not obstruct other components or
             * data visibility.
             *
             * @sample {highcharts} highcharts/chart/resetzoombutton-position/
             *         Above the plot area
             * @sample {highstock} highcharts/chart/resetzoombutton-position/
             *         Above the plot area
             * @sample {highmaps} highcharts/chart/resetzoombutton-position/
             *         Above the plot area
             *
             * @type  {Highcharts.AlignObject}
             * @since 10.2.1
             */
            position: {
                align: 'right',
                /**
                 * The vertical alignment of the button.
                 *
                 * @type      {Highcharts.VerticalAlignValue}
                 * @default   top
                 * @apioption chart.zooming.resetButton.position.verticalAlign
                 */
                x: -10,
                y: 10
            }
        }
    },
    /**
     * An explicit width for the chart. By default (when `null`) the width
     * is calculated from the offset width of the containing element.
     *
     * @sample {highcharts} highcharts/chart/width/
     *         800px wide
     * @sample {highstock} stock/chart/width/
     *         800px wide
     * @sample {highmaps} maps/chart/size/
     *         Chart with explicit size
     *
     * @type {null|number|string}
     */
    width: null,
    /**
     * An explicit height for the chart. If a _number_, the height is
     * given in pixels. If given a _percentage string_ (for example
     * `'56%'`), the height is given as the percentage of the actual chart
     * width. This allows for preserving the aspect ratio across responsive
     * sizes.
     *
     * By default (when `null`) the height is calculated from the offset
     * height of the containing element, or 400 pixels if the containing
     * element's height is 0.
     *
     * @sample {highcharts} highcharts/chart/height/
     *         Forced 200px height
     * @sample {highstock} stock/chart/height/
     *         300px height
     * @sample {highmaps} maps/chart/size/
     *         Chart with explicit size
     * @sample highcharts/chart/height-percent/
     *         Highcharts with percentage height
     * @sample highcharts/chart/height-inherited/
     *         Chart with inherited height
     *
     * @type {null|number|string}
     */
    height: null,
    /**
     * The color of the outer chart border.
     *
     * @see In styled mode, the stroke is set with the
     *      `.highcharts-background` class.
     *
     * @sample {highcharts} highcharts/chart/bordercolor/
     *         Brown border
     * @sample {highstock} stock/chart/border/
     *         Brown border
     * @sample {highmaps} maps/chart/border/
     *         Border options
     *
     * @type {Highcharts.ColorType}
     */
    borderColor: "#334eff" /* Palette.highlightColor80 */,
    /**
     * The pixel width of the outer chart border.
     *
     * @see In styled mode, the stroke is set with the
     *      `.highcharts-background` class.
     *
     * @sample {highcharts} highcharts/chart/borderwidth/
     *         5px border
     * @sample {highstock} stock/chart/border/
     *         2px border
     * @sample {highmaps} maps/chart/border/
     *         Border options
     *
     * @type      {number}
     * @default   0
     * @apioption chart.borderWidth
     */
    /**
     * The background color or gradient for the outer chart area.
     *
     * @see In styled mode, the background is set with the
     *      `.highcharts-background` class.
     *
     * @sample {highcharts} highcharts/chart/backgroundcolor-color/
     *         Color
     * @sample {highcharts} highcharts/chart/backgroundcolor-gradient/
     *         Gradient
     * @sample {highstock} stock/chart/backgroundcolor-color/
     *         Color
     * @sample {highstock} stock/chart/backgroundcolor-gradient/
     *         Gradient
     * @sample {highmaps} maps/chart/backgroundcolor-color/
     *         Color
     * @sample {highmaps} maps/chart/backgroundcolor-gradient/
     *         Gradient
     *
     * @type {Highcharts.ColorType}
     */
    backgroundColor: "#ffffff" /* Palette.backgroundColor */,
    /**
     * The background color or gradient for the plot area.
     *
     * @see In styled mode, the plot background is set with the
     *      `.highcharts-plot-background` class.
     *
     * @sample {highcharts} highcharts/chart/plotbackgroundcolor-color/
     *         Color
     * @sample {highcharts} highcharts/chart/plotbackgroundcolor-gradient/
     *         Gradient
     * @sample {highstock} stock/chart/plotbackgroundcolor-color/
     *         Color
     * @sample {highstock} stock/chart/plotbackgroundcolor-gradient/
     *         Gradient
     * @sample {highmaps} maps/chart/plotbackgroundcolor-color/
     *         Color
     * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
     *         Gradient
     *
     * @type      {Highcharts.ColorType}
     * @apioption chart.plotBackgroundColor
     */
    /**
     * The URL for an image to use as the plot background. To set an image
     * as the background for the entire chart, set a CSS background image
     * to the container element. Note that for the image to be applied to
     * exported charts, its URL needs to be accessible by the export server.
     *
     * @see In styled mode, a plot background image can be set with the
     *      `.highcharts-plot-background` class and a [custom pattern](
     *      https://www.highcharts.com/docs/chart-design-and-style/gradients-shadows-and-patterns).
     *
     * @sample {highcharts} highcharts/chart/plotbackgroundimage/
     *         Skies
     * @sample {highstock} stock/chart/plotbackgroundimage/
     *         Skies
     *
     * @type      {string}
     * @apioption chart.plotBackgroundImage
     */
    /**
     * The color of the inner chart or plot area border.
     *
     * @see In styled mode, a plot border stroke can be set with the
     *      `.highcharts-plot-border` class.
     *
     * @sample {highcharts} highcharts/chart/plotbordercolor/
     *         Blue border
     * @sample {highstock} stock/chart/plotborder/
     *         Blue border
     * @sample {highmaps} maps/chart/plotborder/
     *         Plot border options
     *
     * @type {Highcharts.ColorType}
     */
    plotBorderColor: "#cccccc" /* Palette.neutralColor20 */
};
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const Chart_ChartDefaults = (ChartDefaults);

;// ./code/dashboards/es-modules/Core/Color/Palettes.js
/**
 * Series palettes for Highcharts. Series colors are defined in highcharts.css.
 * **Do not edit this file!** This file is generated using the 'gulp palette' task.
 * @internal
 */
const SeriesPalettes = {
    /**
     * Colors for data series and points
     */
    colors: [
        '#2caffe',
        '#544fc5',
        '#00e272',
        '#fe6a35',
        '#6b8abc',
        '#d568fb',
        '#2ee0ca',
        '#fa4b42',
        '#feb56a',
        '#91e8e1'
    ],
};
/** @internal */
/* harmony default export */ const Palettes = (SeriesPalettes);

;// ./code/dashboards/es-modules/Shared/TimeBase.js
/* *
 *
 *  (c) 2010-2026 Highsoft AS
 *  Author: Torstein Hønsi
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 * */


const { pageLang, win: TimeBase_win } = Core_Globals;


/* *
 *
 *  Constants
 *
 * */
// To do: Remove this when we no longer need support for Safari < v14.1
const hasOldSafariBug = Core_Globals.isSafari &&
    TimeBase_win.Intl &&
    !TimeBase_win.Intl.DateTimeFormat.prototype.formatRange;
const isDateTimeFormatOptions = (obj) => obj.main === void 0;
/* *
 *
 *  Class
 *
 * */
/* eslint-disable no-invalid-this, valid-jsdoc */
/**
 * The Time class. Time settings are applied in general for each page using
 * `Highcharts.setOptions`, or individually for each Chart item through the
 * [time](https://api.highcharts.com/highcharts/time) options set.
 *
 * The Time object is available from {@link Highcharts.Chart#time}, which refers
 * to  `Highcharts.time` unless individual time settings are applied for each
 * chart.
 *
 * When configuring time settings for individual chart instances, be aware that
 * using `Highcharts.dateFormat` or `Highcharts.time.dateFormat` within
 * formatter callbacks relies on the global time object, which applies the
 * global language and time zone settings. To ensure charts with local time
 * settings function correctly, use `chart.time.dateFormat? instead. However,
 * the recommended best practice is to use `setOptions` to define global time
 * settings unless specific configurations are needed for each chart.
 *
 * @example
 * // Apply time settings globally
 * Highcharts.setOptions({
 *     time: {
 *         timezone: 'Europe/London'
 *     }
 * });
 *
 * // Apply time settings by instance
 * const chart = Highcharts.chart('container', {
 *     time: {
 *         timezone: 'America/New_York'
 *     },
 *     series: [{
 *         data: [1, 4, 3, 5]
 *     }]
 * });
 *
 * // Use the Time object of a chart instance
 * console.log(
 *        'Current time in New York',
 *        chart.time.dateFormat('%Y-%m-%d %H:%M:%S', Date.now())
 * );
 *
 * // Standalone use
 * const time = new Highcharts.Time({
 *    timezone: 'America/New_York'
 * });
 * const s = time.dateFormat('%Y-%m-%d %H:%M:%S', Date.UTC(2020, 0, 1));
 * console.log(s); // => 2019-12-31 19:00:00
 *
 * @since 6.0.5
 *
 * @class
 * @name Highcharts.Time
 *
 * @param {Highcharts.TimeOptions} [options] Time options as defined in
 * [chart.options.time](/highcharts/time).
 */
class TimeBase {
    /* *
     *
     *  Constructors
     *
     * */
    constructor(options, lang) {
        /* *
         *
         *  Properties
         *
         * */
        this.options = {
            timezone: 'UTC'
        };
        this.variableTimezone = false;
        this.Date = TimeBase_win.Date;
        this.update(options);
        this.lang = lang;
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Update the Time object with current options. It is called internally on
     * initializing Highcharts, after running `Highcharts.setOptions` and on
     * `Chart.update`.
     *
     * @internal
     * @function Highcharts.Time#update
     *
     * @param {Highcharts.TimeOptions} [options]
     *
     */
    update(options = {}) {
        this.dTLCache = {};
        this.options = options = merge(true, this.options, options);
        const { timezoneOffset, useUTC, locale } = options;
        // Allow using a different Date class
        this.Date = options.Date || TimeBase_win.Date || Date;
        // Assign the time zone. Handle the legacy, deprecated `useUTC` option.
        let timezone = options.timezone;
        if (defined(useUTC)) {
            timezone = useUTC ? 'UTC' : void 0;
        }
        // The Etc/GMT time zones do not support offsets with half-hour
        // resolutions
        if (timezoneOffset && timezoneOffset % 60 === 0) {
            timezone = 'Etc/GMT' + ((timezoneOffset > 0 ? '+' : '')) + timezoneOffset / 60;
        }
        /*
         * The time object has options allowing for variable time zones, meaning
         * the axis ticks or series data needs to consider this.
         */
        this.variableTimezone = timezone !== 'UTC' &&
            timezone?.indexOf('Etc/GMT') !== 0;
        this.timezone = timezone;
        // Update locale.
        if (this.lang && locale) {
            this.lang.locale = locale;
        }
        // Assign default time formats from locale strings
        ['months', 'shortMonths', 'weekdays', 'shortWeekdays'].forEach((name) => {
            const isMonth = /months/i.test(name), isShort = /short/.test(name), options = {
                timeZone: 'UTC'
            };
            options[isMonth ? 'month' : 'weekday'] = isShort ? 'short' : 'long';
            this[name] = (isMonth ?
                [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] :
                [3, 4, 5, 6, 7, 8, 9]).map((position) => this.dateFormat(options, (isMonth ? 31 : 1) * 24 * 36e5 * position));
        });
    }
    /**
     * Get a date in terms of numbers (year, month, day etc) for further
     * processing. Takes the current `timezone` setting into account. Inverse of
     * `makeTime` and the native `Date` constructor and `Date.UTC`.
     *
     * The date is returned in array format with the following indices:
     *
     * 0: year,
     * 1: month (zero based),
     * 2: day,
     * 3: hours,
     * 4: minutes,
     * 5: seconds,
     * 6: milliseconds,
     * 7: weekday (Sunday as 0)
     *
     * @function Highcharts.Time#toParts
     *
     * @param {number|Date} [timestamp]
     *                 The timestamp in milliseconds since January 1st 1970.
     *                 A Date object is also accepted.
     *
     * @return {Array<number>} The date parts in array format.
     */
    toParts(timestamp) {
        const [weekday, dayOfMonth, month, year, hours, minutes, seconds] = this.dateTimeFormat({
            weekday: 'narrow',
            day: 'numeric',
            month: 'numeric',
            year: 'numeric',
            hour: 'numeric',
            minute: 'numeric',
            second: 'numeric'
        }, timestamp, 'es')
            // The ', ' splitter is for all modern browsers:
            //      L, 6/3/2023, 14:30:00
            // The ' ' splitter is for legacy Safari with no comma between date
            // and time (#22445):
            //      L, 6/3/2023 14:30:00
            .split(/(?:, | |\/|:)/g);
        return [
            year,
            +month - 1,
            dayOfMonth,
            hours,
            minutes,
            seconds,
            // Milliseconds
            Math.floor(Number(timestamp) || 0) % 1000,
            // Spanish weekday index
            'DLMXJVS'.indexOf(weekday)
        ].map(Number);
    }
    /**
     * Shorthand to get a cached `Intl.DateTimeFormat` instance.
     */
    dateTimeFormat(options, timestamp, locale = this.options.locale || pageLang) {
        const cacheKey = JSON.stringify(options) + locale;
        if (isString(options)) {
            options = this.str2dtf(options);
        }
        let dTL = this.dTLCache[cacheKey];
        if (!dTL) {
            options.timeZone ?? (options.timeZone = this.timezone);
            try {
                dTL = new Intl.DateTimeFormat(locale, options);
            }
            catch (e) {
                if (/Invalid time zone/i.test(e.message)) {
                    error(34);
                    options.timeZone = 'UTC';
                    dTL = new Intl.DateTimeFormat(locale, options);
                }
                else {
                    error(e.message, false);
                }
            }
        }
        this.dTLCache[cacheKey] = dTL;
        return dTL?.format(timestamp) || '';
    }
    /**
     * Take a locale-aware string format and return a full DateTimeFormat in
     * object form.
     */
    str2dtf(s, dtf = {}) {
        const mapping = {
            L: { fractionalSecondDigits: 3 },
            S: { second: '2-digit' },
            M: { minute: 'numeric' },
            H: { hour: '2-digit' },
            k: { hour: 'numeric' },
            E: { weekday: 'narrow' },
            a: { weekday: 'short' },
            A: { weekday: 'long' },
            d: { day: '2-digit' },
            e: { day: 'numeric' },
            b: { month: 'short' },
            B: { month: 'long' },
            m: { month: '2-digit' },
            o: { month: 'numeric' },
            y: { year: '2-digit' },
            Y: { year: 'numeric' }
        };
        Object.keys(mapping).forEach((key) => {
            if (s.indexOf(key) !== -1) {
                extend(dtf, mapping[key]);
            }
        });
        return dtf;
    }
    /**
     * Make a time and returns milliseconds. Similar to `Date.UTC`, but takes
     * the current `timezone` setting into account.
     *
     * @function Highcharts.Time#makeTime
     *
     * @param {number} year
     *        The year
     *
     * @param {number} month
     *        The month. Zero-based, so January is 0.
     *
     * @param {number} [date=1]
     *        The day of the month
     *
     * @param {number} [hours=0]
     *        The hour of the day, 0-23.
     *
     * @param {number} [minutes=0]
     *        The minutes
     *
     * @param {number} [seconds=0]
     *        The seconds
     *
     * @return {number}
     *         The time in milliseconds since January 1st 1970.
     */
    makeTime(year, month, date = 1, hours = 0, minutes, seconds, milliseconds) {
        // eslint-disable-next-line new-cap
        let d = this.Date.UTC(year, month, date, hours, minutes || 0, seconds || 0, milliseconds || 0);
        if (this.timezone !== 'UTC') {
            const offset = this.getTimezoneOffset(d);
            d += offset;
            // Adjustments close to DST transitions
            if (
            // Optimize for speed by limiting the number of calls to
            // `getTimezoneOffset`. According to
            // https://en.wikipedia.org/wiki/Daylight_saving_time_by_country,
            // DST change may only occur in these months.
            [2, 3, 8, 9, 10, 11].indexOf(month) !== -1 &&
                // DST transitions occur only in the night-time
                (hours < 5 || hours > 20)) {
                const newOffset = this.getTimezoneOffset(d);
                if (offset !== newOffset) {
                    d += newOffset - offset;
                    // A special case for transitioning from summer time to winter
                    // time. When the clock is set back, the same time is repeated
                    // twice, i.e. 02:30 am is repeated since the clock is set back
                    // from 3 am to 2 am. We need to make the same time as local
                    // Date does.
                }
                else if (offset - 36e5 === this.getTimezoneOffset(d - 36e5) &&
                    !hasOldSafariBug) {
                    d -= 36e5;
                }
            }
        }
        return d;
    }
    /**
     * Parse a datetime string. Unless the string contains time zone
     * information, apply the current `timezone` from options. If the argument
     * is a number, return it.
     *
     * @function Highcharts.Time#parse
     * @param    {string|number|undefined} s The datetime string to parse
     * @return   {number|undefined}          Parsed JavaScript timestamp
     */
    parse(s) {
        if (!isString(s)) {
            return s ?? void 0;
        }
        s = s
            // Firefox fails on YYYY/MM/DD
            .replace(/\//g, '-')
            // Replace some non-standard notations
            .replace(/(GMT|UTC)/, '');
        // Extend shorthand hour timezone offset like +02
        // .replace(/([+-][0-9]{2})$/, '$1:00');
        // Check if the string has time zone information
        const hasTimezone = s.indexOf('Z') > -1 ||
            /([+-][0-9]{2}):?[0-9]{2}$/.test(s), 
        // YYYY-MM-DD and YYYY-MM are always UTC
        isYYYYMMDD = /^[0-9]{4}-[0-9]{2}(-[0-9]{2}|)$/.test(s);
        if (!hasTimezone && !isYYYYMMDD) {
            s += 'Z';
        }
        const ts = Date.parse(s);
        if (Utilities_isNumber(ts)) {
            // Unless the string contains time zone information, convert from
            // the local time result of `Date.parse` via UTC into the current
            // timezone of the time object.
            return ts + ((!hasTimezone || isYYYYMMDD) ?
                this.getTimezoneOffset(ts) :
                0);
        }
    }
    /**
     * Get the time zone offset based on the current timezone information as
     * set in the global options.
     *
     * @function Highcharts.Time#getTimezoneOffset
     *
     * @param {number} timestamp
     *        The JavaScript timestamp to inspect.
     *
     * @return {number}
     *         The timezone offset in minutes compared to UTC.
     */
    getTimezoneOffset(timestamp) {
        if (this.timezone !== 'UTC') {
            // eslint-disable-next-line @typescript-eslint/no-unused-vars
            const [date, gmt, hours, colon, minutes = 0] = this.dateTimeFormat({ timeZoneName: 'shortOffset' }, timestamp, 'en')
                .split(/(GMT|:)/)
                .map(Number), offset = -(hours + minutes / 60) * 60 * 60000;
            // Possible future NaNs stop here
            if (Utilities_isNumber(offset)) {
                return offset;
            }
        }
        return 0;
    }
    /**
     * Formats a JavaScript date timestamp (milliseconds since January 1 1970)
     * into a human readable date string.
     *
     * The `format` parameter accepts two types of values:
     * - An object containing settings that are passed directly on to
     *   [Intl.DateTimeFormat.prototype.format](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/format).
     * - A format string containing either individual or locale-aware format
     *   keys. **Individual keys**, for example `%Y-%m-%d`, are listed below.
     *   **Locale-aware keys** are grouped by square brackets, for example
     *   `%[Ymd]`. The order of keys within the square bracket doesn't affect
     *   the output, which is determined by the locale. See example below.
     *   Internally, the locale-aware format keys are just a shorthand for the
     *   full object formats, but are particularly practical in
     *   [templating](https://www.highcharts.com/docs/chart-concepts/templating)
     *   where full object definitions are not an option.
     *
     * The available string format keys are listed below. Additional formats can
     * be given in the {@link Highcharts.dateFormats} hook.
     *
     * Supported format keys:
     * | Key  | Description                     | Notes on locale-aware format |
     * -------|----------------------------------------------|-------|
     * | `%A` | Long weekday, like 'Monday'                  |       |
     * | `%a` | Short weekday, like 'Mon'                    |       |
     * | `%E` | Narrow weekday, single character             |       |
     * | `%d` | Two digit day of the month, 01 to 31         |       |
     * | `%e` | Day of the month, 1 through 31               |       |
     * | `%w` | Day of the week, 0 through 6                 | N/A   |
     * | `%v` | The prefix "week from", read from `lang.weekFrom` | N/A |
     * | `%b` | Short month, like 'Jan'                      |       |
     * | `%B` | Long month, like 'January'                   |       |
     * | `%m` | Two digit month number, 01 through 12        |       |
     * | `%o` | Month number, 1 through 12                   |       |
     * | `%y` | Two digits year, like 24 for 2024            |       |
     * | `%Y` | Four digits year, like 2024                  |       |
     * | `%H` | Two digits hours in 24h format, 00 through 23 | Depending on the locale, 12h format may be inserted. |
     * | `%k` | Hours in 24h format, 0 through 23            | Depending on the locale, 12h format may be inserted. |
     * | `%I` | Two digits hours in 12h format, 00 through 11 | N/A. The locale determines the hour format. |
     * | `%l` | Hours in 12h format, 1 through 12            | N/A. The locale determines the hour format. |
     * | `%M` | Two digits minutes, 00 through 59            |       |
     * | `%p` | Upper case AM or PM                          | N/A. The locale determines whether to add AM and PM. |
     * | `%P` | Lower case AM or PM                          | N/A. The locale determines whether to add AM and PM. |
     * | `%S` | Two digits seconds, 00 through 59            |       |
     * | `%L` | Milliseconds (naming from Ruby)              |       |
     *
     * @example
     * // Object format, US English
     * const time1 = new Highcharts.Time({ locale: 'en-US' });
     * console.log(
     *     time1.dateFormat({
     *         day: 'numeric',
     *         month: 'short',
     *         year: 'numeric',
     *         hour: 'numeric',
     *         minute: 'numeric'
     *     }, Date.UTC(2024, 11, 31))
     * ); // => Dec 31, 2024, 12:00 AM
     *
     * // Object format, British English
     * const time2 = new Highcharts.Time({ locale: 'en-GB' });
     * console.log(
     *     time2.dateFormat({
     *         day: 'numeric',
     *         month: 'short',
     *         year: 'numeric',
     *         hour: 'numeric',
     *         minute: 'numeric'
     *     }, Date.UTC(2024, 11, 31))
     * ); // => 31 Dec 2024, 00:00
     *
     * // Individual key string replacement
     * const time3 = new Highcharts.Time();
     * console.log(
     *     time3.dateFormat('%Y-%m-%d %H:%M:%S', Date.UTC(2024, 11, 31))
     * ); // => 2024-12-31 00:00:00
     *
     * // Locale-aware keys, US English
     * const time4 = new Highcharts.Time({ locale: 'en-US' });
     * console.log(
     *     time4.dateFormat('%[YebHM]', Date.UTC(2024, 11, 31))
     * ); // => Dec 31, 2024, 12:00 AM
     *
     * // Locale-aware keys, British English
     * const time5 = new Highcharts.Time({ locale: 'en-GB' });
     * console.log(
     *     time5.dateFormat('%[YebHM]', Date.UTC(2024, 11, 31))
     * ); // => 31 Dec 2024, 00:00
     *
     * // Mixed locale-aware and individual keys
     * console.log(
     *     time5.dateFormat('%[Yeb], %H:%M', Date.UTC(2024, 11, 31))
     * ); // => 31 Dec 2024, 00:00
     *
     * @function Highcharts.Time#dateFormat
     *
     * @param {string|Highcharts.DateTimeFormatOptions} format
     *        The desired string format where various time representations are
     *        prefixed with %, or an object representing the locale-aware format
     *        options.
     *
     * @param {number} [timestamp]
     *        The JavaScript timestamp.
     *
     * @param {boolean} [upperCaseFirst=false]
     *        Upper case first letter in the return.
     *
     * @return {string}
     *         The formatted date.
     */
    dateFormat(format, timestamp, upperCaseFirst) {
        const lang = this.lang;
        if (!defined(timestamp) || isNaN(timestamp)) {
            return lang?.invalidDate || '';
        }
        format = format ?? '%Y-%m-%d %H:%M:%S';
        // First, identify and replace locale-aware formats like %[Ymd]
        if (isString(format)) {
            const localeAwareRegex = /%\[([a-zA-Z]+)\]/g;
            let match;
            while ((match = localeAwareRegex.exec(format))) {
                format = format.replace(match[0], this.dateTimeFormat(match[1], timestamp, lang?.locale));
            }
        }
        // Then, replace static formats like %Y, %m, %d etc.
        if (isString(format) && format.indexOf('%') !== -1) {
            const time = this, [fullYear, month, dayOfMonth, hours, minutes, seconds, milliseconds, weekday] = this.toParts(timestamp), langWeekdays = lang?.weekdays || this.weekdays, shortWeekdays = lang?.shortWeekdays || this.shortWeekdays, months = lang?.months || this.months, shortMonths = lang?.shortMonths || this.shortMonths, 
            // List all format keys. Custom formats can be added from the
            // outside.
            replacements = extend({
                // Day
                // Short weekday, like 'Mon'
                a: shortWeekdays ?
                    shortWeekdays[weekday] :
                    langWeekdays[weekday].substr(0, 3),
                // Long weekday, like 'Monday'
                A: langWeekdays[weekday],
                // Two digit day of the month, 01 to 31
                d: pad(dayOfMonth),
                // Day of the month, 1 through 31
                e: pad(dayOfMonth, 2, ' '),
                // Day of the week, 0 through 6
                w: weekday,
                // Week (none implemented)
                // 'W': weekNumber(),
                v: lang?.weekFrom ?? '',
                // Month
                // Short month, like 'Jan'
                b: shortMonths[month],
                // Long month, like 'January'
                B: months[month],
                // Two digit month number, 01 through 12
                m: pad(month + 1),
                // Month number, 1 through 12 (#8150)
                o: month + 1,
                // Year
                // Two digits year, like 09 for 2009
                y: fullYear.toString().substr(2, 2),
                // Four digits year, like 2009
                Y: fullYear,
                // Time
                // Two digits hours in 24h format, 00 through 23
                H: pad(hours),
                // Hours in 24h format, 0 through 23
                k: hours,
                // Two digits hours in 12h format, 00 through 11
                I: pad((hours % 12) || 12),
                // Hours in 12h format, 1 through 12
                l: (hours % 12) || 12,
                // Two digits minutes, 00 through 59
                M: pad(minutes),
                // Upper case AM or PM
                p: hours < 12 ? 'AM' : 'PM',
                // Lower case AM or PM
                P: hours < 12 ? 'am' : 'pm',
                // Two digits seconds, 00 through 59
                S: pad(seconds),
                // Milliseconds (naming from Ruby)
                L: pad(milliseconds, 3)
            }, Core_Globals.dateFormats);
            // Do the replaces
            objectEach(replacements, function (val, key) {
                if (isString(format)) {
                    // Regex would do it in one line, but this is faster
                    while (format.indexOf('%' + key) !== -1) {
                        format = format.replace('%' + key, typeof val === 'function' ?
                            val.call(time, timestamp, time) :
                            val);
                    }
                }
            });
        }
        else if (isObject(format)) {
            const tzHours = (this.getTimezoneOffset(timestamp) || 0) /
                (60000 * 60), timeZone = this.timezone || ('Etc/GMT' + (tzHours >= 0 ? '+' : '') + tzHours), { prefix = '', suffix = '' } = format;
            format = prefix + this.dateTimeFormat(extend({ timeZone }, format), timestamp) + suffix;
        }
        // Optionally sentence-case the string and return
        return upperCaseFirst ? ucfirst(format) : format;
    }
    /**
     * Resolve legacy formats of dateTimeLabelFormats (strings and arrays) into
     * an object.
     * @internal
     * @param {string|Array<T>|Highcharts.Dictionary<T>} f
     * General format description
     * @return {Highcharts.Dictionary<T>}
     * The object definition
     */
    resolveDTLFormat(f) {
        if (!isObject(f, true)) { // Check for string or array
            f = splat(f);
            return {
                main: f[0],
                from: f[1],
                to: f[2]
            };
        }
        // Type-check DateTimeFormatOptions against DateTimeLabelFormatObject
        if (isObject(f, true) && isDateTimeFormatOptions(f)) {
            return { main: f };
        }
        return f;
    }
    /**
     * Get the optimal date format for a point, based on a range.
     *
     * @internal
     * @function Highcharts.Time#getDateFormat
     *
     * @param {number} range
     *        The time range
     *
     * @param {number} timestamp
     *        The timestamp of the date
     *
     * @param {number} startOfWeek
     *        An integer representing the first day of the week, where 0 is
     *        Sunday.
     *
     * @param {Highcharts.Dictionary<string>} dateTimeLabelFormats
     *        A map of time units to formats.
     *
     * @return {string}
     *         The optimal date format for a point.
     */
    getDateFormat(range, timestamp, startOfWeek, dateTimeLabelFormats) {
        const dateStr = this.dateFormat('%m-%d %H:%M:%S.%L', timestamp), blank = '01-01 00:00:00.000', strpos = {
            millisecond: 15,
            second: 12,
            minute: 9,
            hour: 6,
            day: 3
        };
        let n = 'millisecond', 
        // For sub-millisecond data, #4223
        lastN = n;
        for (n in timeUnits) { // eslint-disable-line guard-for-in
            // If the range is exactly one week and we're looking at a
            // Sunday/Monday, go for the week format
            if (range &&
                range === timeUnits.week &&
                +this.dateFormat('%w', timestamp) === startOfWeek &&
                dateStr.substr(6) === blank.substr(6)) {
                n = 'week';
                break;
            }
            // The first format that is too great for the range
            if (range && timeUnits[n] > range) {
                n = lastN;
                break;
            }
            // If the point is placed every day at 23:59, we need to show
            // the minutes as well. #2637.
            if (strpos[n] &&
                dateStr.substr(strpos[n]) !== blank.substr(strpos[n])) {
                break;
            }
            // Weeks are outside the hierarchy, only apply them on
            // Mondays/Sundays like in the first condition
            if (n !== 'week') {
                lastN = n;
            }
        }
        return this.resolveDTLFormat(dateTimeLabelFormats[n]).main;
    }
}
/* *
 *
 * Default export
 *
 * */
/* harmony default export */ const Shared_TimeBase = (TimeBase);
/* *
 *
 * API Declarations
 *
 * */
/**
 * Normalized interval.
 *
 * @interface Highcharts.TimeNormalizedObject
 */ /**
* The count.
*
* @name Highcharts.TimeNormalizedObject#count
* @type {number|undefined}
*/ /**
* The interval in axis values (ms).
*
* @name Highcharts.TimeNormalizedObject#unitRange
* @type {number}
*/
/**
 * Function of an additional date format specifier.
 *
 * @callback Highcharts.TimeFormatCallbackFunction
 *
 * @param {number} timestamp
 *        The time to format.
 *
 * @return {string}
 *         The formatted portion of the date.
 */
/**
 * Time ticks.
 *
 * @interface Highcharts.AxisTickPositionsArray
 * @extends global.Array<number>
 */ /**
* @name Highcharts.AxisTickPositionsArray#info
* @type {Highcharts.TimeTicksInfoObject|undefined}
*/
/**
 * A callback to return the time zone offset for a given datetime. It
 * takes the timestamp in terms of milliseconds since January 1 1970,
 * and returns the timezone offset in minutes. This provides a hook
 * for drawing time based charts in specific time zones using their
 * local DST crossover dates, with the help of external libraries.
 *
 * @callback Highcharts.TimezoneOffsetCallbackFunction
 *
 * @param {number} timestamp
 * Timestamp in terms of milliseconds since January 1 1970.
 *
 * @return {number}
 * Timezone offset in minutes.
 */
/**
 * Options for formatting dates and times using the [Intl.DateTimeFormat](
 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat
 * ) API, and extended with some custom options for Highcharts.
 *
 * @interface Highcharts.DateTimeFormatOptions
 */ /**
* The locale matching algorithm to use.
*
* @name Highcharts.DateTimeFormatOptions#localeMatcher
* @type {string|undefined}
*/ /**
* The time zone to use. The default is the browser's default time zone.
*
* @name Highcharts.DateTimeFormatOptions#timeZone
* @type {string|undefined}
*/ /**
* Whether to use 12-hour time (as opposed to 24-hour time).
*
* @name Highcharts.DateTimeFormatOptions#hour12
* @type {'auto'|'always'|'never'|undefined}
*/ /**
* The format matching algorithm to use.
*
* @name Highcharts.DateTimeFormatOptions#formatMatcher
* @type {string|undefined}
*/ /**
* The representation of the weekday.
*
* @name Highcharts.DateTimeFormatOptions#weekday
* @type {'narrow'|'short'|'long'|undefined}
*/ /**
* The representation of the era.
*
* @name Highcharts.DateTimeFormatOptions#era
* @type {'narrow'|'short'|'long'|undefined}
*/ /**
* The representation of the year.
*
* @name Highcharts.DateTimeFormatOptions#year
* @type {'numeric'|'2-digit'|undefined}
*/ /**
* The representation of the month.
* "narrow", "short", "long".
*
* @name Highcharts.DateTimeFormatOptions#month
* @type {'numeric'|'2-digit'|'narrow'|'short'|'long'|undefined}
*/ /**
* The representation of the day.
*
* @name Highcharts.DateTimeFormatOptions#day
* @type {'numeric'|'2-digit'|undefined}
*/ /**
* The representation of the hour.
*
* @name Highcharts.DateTimeFormatOptions#hour
* @type {'numeric'|'2-digit'|undefined}
*/ /**
* The representation of the minute.
*
* @name Highcharts.DateTimeFormatOptions#minute
* @type {'numeric'|'2-digit'|undefined}
*/ /**
* The representation of the second.
*
* @name Highcharts.DateTimeFormatOptions#second
* @type {'numeric'|'2-digit'|undefined}
*/ /**
* The number of fractional digits to use. 3 means milliseconds.
*
* @name Highcharts.DateTimeFormatOptions#fractionalSecondDigits
* @type {1|2|3|undefined}
*/ /**
* The representation of the time zone name.
*
* @name Highcharts.DateTimeFormatOptions#timeZoneName
* @type {'short'|'long'|undefined}
*/ /**
* A prefix for the time string. Custom Highcharts option.
*
* @name Highcharts.DateTimeFormatOptions#prefix
* @type {'string'|undefined}
*/ /**
* A suffix for the time string. Custom Highcharts option.
*
* @name Highcharts.DateTimeFormatOptions#suffix
* @type {'string'|undefined}
*/
''; // Keeps doclets above in JS file

;// ./code/dashboards/es-modules/Core/Time.js
/* *
 *
 *  (c) 2010-2026 Highsoft AS
 *  Author: Torstein Hønsi
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 * */




/* *
 *
 *  Constants
 *
 * */
class Time extends Shared_TimeBase {
    /**
     * Return an array with time positions distributed on round time values
     * right and right after min and max. Used in datetime axes as well as for
     * grouping data on a datetime axis.
     *
     * @function Highcharts.Time#getTimeTicks
     *
     * @param {Highcharts.TimeNormalizedObject} normalizedInterval
     *        The interval in axis values (ms) and the count
     *
     * @param {number} [min]
     *        The minimum in axis values
     *
     * @param {number} [max]
     *        The maximum in axis values
     *
     * @param {number} [startOfWeek=1]
     *
     * @return {Highcharts.AxisTickPositionsArray}
     * Time positions
     */
    getTimeTicks(normalizedInterval, min, max, startOfWeek) {
        const time = this, tickPositions = [], higherRanks = {}, { count = 1, unitRange } = normalizedInterval;
        let [year, month, dayOfMonth, hours, minutes, seconds] = time.toParts(min), milliseconds = (min || 0) % 1000, variableDayLength;
        startOfWeek ?? (startOfWeek = 1);
        if (defined(min)) { // #1300
            milliseconds = unitRange >= timeUnits.second ?
                0 : // #3935
                count * Math.floor(milliseconds / count);
            if (unitRange >= timeUnits.second) { // Second
                seconds = unitRange >= timeUnits.minute ?
                    0 : // #3935
                    count * Math.floor(seconds / count);
            }
            if (unitRange >= timeUnits.minute) { // Minute
                minutes = unitRange >= timeUnits.hour ?
                    0 :
                    count * Math.floor(minutes / count);
            }
            if (unitRange >= timeUnits.hour) { // Hour
                hours = unitRange >= timeUnits.day ?
                    0 :
                    count * Math.floor(hours / count);
            }
            if (unitRange >= timeUnits.day) { // Day
                dayOfMonth = unitRange >= timeUnits.month ?
                    1 :
                    Math.max(1, count * Math.floor(dayOfMonth / count));
            }
            if (unitRange >= timeUnits.month) { // Month
                month = unitRange >= timeUnits.year ? 0 :
                    count * Math.floor(month / count);
            }
            if (unitRange >= timeUnits.year) { // Year
                year -= year % count;
            }
            // Week is a special case that runs outside the hierarchy
            if (unitRange === timeUnits.week) {
                if (count) {
                    min = time.makeTime(year, month, dayOfMonth, hours, minutes, seconds, milliseconds);
                }
                // Get start of current week, independent of count
                const weekday = this.dateTimeFormat({
                    timeZone: this.timezone,
                    weekday: 'narrow'
                }, min, 'es'), 
                // Spanish weekday index
                weekdayNo = 'DLMXJVS'.indexOf(weekday);
                dayOfMonth += -weekdayNo + startOfWeek +
                    // We don't want to skip days that are before
                    // startOfWeek (#7051)
                    (weekdayNo < startOfWeek ? -7 : 0);
            }
            min = time.makeTime(year, month, dayOfMonth, hours, minutes, seconds, milliseconds);
            // Handle local timezone offset
            if (time.variableTimezone && defined(max)) {
                // Detect whether we need to take the DST crossover into
                // consideration. If we're crossing over DST, the day length may
                // be 23h or 25h and we need to compute the exact clock time for
                // each tick instead of just adding hours. This comes at a cost,
                // so first we find out if it is needed (#4951).
                variableDayLength = (
                // Long range, assume we're crossing over.
                max - min > 4 * timeUnits.month ||
                    // Short range, check if min and max are in different time
                    // zones.
                    time.getTimezoneOffset(min) !==
                        time.getTimezoneOffset(max));
            }
            // Iterate and add tick positions at appropriate values
            let t = min, i = 1;
            while (t < max) {
                tickPositions.push(t);
                // Increase the years
                if (unitRange === timeUnits.year) {
                    t = time.makeTime(year + i * count, 0);
                    // Increase the months
                }
                else if (unitRange === timeUnits.month) {
                    t = time.makeTime(year, month + i * count);
                    // If we're using local time, the interval is not fixed as it
                    // jumps one hour at the DST crossover
                }
                else if (variableDayLength && (unitRange === timeUnits.day ||
                    unitRange === timeUnits.week)) {
                    t = time.makeTime(year, month, dayOfMonth +
                        i * count * (unitRange === timeUnits.day ? 1 : 7));
                }
                else if (variableDayLength &&
                    unitRange === timeUnits.hour &&
                    count > 1) {
                    // Make sure higher ranks are preserved across DST (#6797,
                    // #7621)
                    t = time.makeTime(year, month, dayOfMonth, hours + i * count);
                    // Else, the interval is fixed and we use simple addition
                }
                else {
                    t += unitRange * count;
                }
                i++;
            }
            // Push the last time
            tickPositions.push(t);
            // Handle higher ranks. Mark new days if the time is on midnight
            // (#950, #1649, #1760, #3349). Use a reasonable dropout threshold
            // to prevent looping over dense data grouping (#6156).
            if (unitRange <= timeUnits.hour && tickPositions.length < 10000) {
                tickPositions.forEach((t) => {
                    if (
                    // Speed optimization, no need to run dateFormat unless
                    // we're on a full or half hour
                    t % 1800000 === 0 &&
                        // Check for local or global midnight
                        time.dateFormat('%H%M%S%L', t) === '000000000') {
                        higherRanks[t] = 'day';
                    }
                });
            }
        }
        // Record information on the chosen unit - for dynamic label formatter
        tickPositions.info = extend(normalizedInterval, {
            higherRanks,
            totalRange: unitRange * count
        });
        return tickPositions;
    }
}
/* *
 *
 * Default export
 *
 * */
/* harmony default export */ const Core_Time = (Time);

;// ./code/dashboards/es-modules/Core/Defaults.js
/* *
 *
 *  (c) 2010-2026 Highsoft AS
 *  Author: Torstein Hønsi
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 * */



const { isTouchDevice } = Core_Globals;



/* *
 *
 *  API Options
 *
 * */
/**
 * Global default settings.
 *
 * @name Highcharts.defaultOptions
 * @type {Highcharts.Options}
 */ /**
* @optionparent
* @internal
*/
const Defaults_defaultOptions = {
    /**
     * An array containing the default colors for the chart's series. When
     * all colors are used, new colors are pulled from the start again.
     *
     * Default colors can also be set on a series or series.type basis,
     * see [column.colors](#plotOptions.column.colors),
     * [pie.colors](#plotOptions.pie.colors).
     *
     * In styled mode, the colors option doesn't exist. Instead, colors
     * are defined in CSS and applied either through series or point class
     * names, or through the [chart.colorCount](#chart.colorCount) option.
     *
     * @sample {highcharts} highcharts/chart/colors/
     *         Assign a global color theme
     * @sample highcharts/members/theme-v10/
     *         Latest release styled like version 10
     *
     * @type    {Array<Highcharts.ColorType>}
     * @default [
     *     "#2caffe",
     *     "#544fc5",
     *     "#00e272",
     *     "#fe6a35",
     *     "#6b8abc",
     *     "#d568fb",
     *     "#2ee0ca",
     *     "#fa4b42",
     *     "#feb56a",
     *     "#91e8e1"
     * ]
     */
    colors: Palettes.colors,
    /**
     * Styled mode only. Configuration object for adding SVG definitions for
     * reusable elements. See [gradients, shadows and
     * patterns](https://www.highcharts.com/docs/chart-design-and-style/gradients-shadows-and-patterns)
     * for more information and code examples.
     *
     * @type      {*}
     * @since     5.0.0
     * @apioption defs
     */
    /**
     * @ignore-option
     */
    symbols: ['circle', 'diamond', 'square', 'triangle', 'triangle-down'],
    /**
     * An object containing language-related strings and settings. A typical
     * setup uses `Highcharts.setOptions` to make the options apply to all
     * charts in the same page.
     *
     * Some language options, like `months` and `weekdays`, are only used
     * with non-locale-aware date formats.
     *
     * ```js
     * Highcharts.setOptions({
     *     lang: {
     *         locale: 'fr'
     *     }
     * });
     * ```
     *
     * @optionparent lang
     */
    lang: {
        weekFrom: 'week from',
        /**
         * The default chart title.
         *
         * @since 12.2.0
         */
        chartTitle: 'Chart title',
        /**
         * The browser locale to use for date and number formatting. The actual
         * locale used for each chart is determined in three steps:
         * 1. If this `lang.locale` option is specified, it is used.
         * 2. Else, look for the closest ancestor HTML element with a `lang`
         *    attribute, typically the `<html>` element.
         * 3. If no 'lang' attribute is found, use the default browser locale.
         *
         * Use `en-GB`, British English, for approximate consistency with
         * Highcharts v < 12.
         *
         * @sample highcharts/lang/locale/
         *         Set the locale using the `lang.locale` option
         * @sample highcharts/lang/locale-attribute/
         *         Pick up the locale from the HTML `lang` attribute
         * @sample highcharts/members/highcharts-numberformat
         *         Arabic locale with digits and dates         *
         *
         * @since 12.0.0
         * @type {string|Array<string>}
         */
        locale: void 0,
        /**
         * The loading text that appears when the chart is set into the loading
         * state following a call to `chart.showLoading`.
         */
        loading: 'Loading...',
        /**
         * An array containing the months names. Corresponds to the `%B` format
         * in `Highcharts.dateFormat()`. Defaults to 'undefined',
         * meaning the default month names are used according to the
         * `lang.locale` or browser settings.
         *
         * @type    {Array<string>}
         */
        months: void 0,
        /**
         * [Format string](https://www.highcharts.com/docs/chart-concepts/templating) for the default series name.
         *
         * @since 12.2.0
         */
        seriesName: 'Series {add index 1}',
        /**
         * An array containing the months names in abbreviated form. Corresponds
         * to the `%b` format in `Highcharts.dateFormat()`. Defaults to
         * 'undefined', meaning the default short month names are used according
         * to the `lang.locale` or browser settings.
         *
         * @type    {Array<string>}
         */
        shortMonths: void 0,
        /**
         * An array containing the weekday names. Corresponds
         * to the `%A` format in `Highcharts.dateFormat()`. Defaults to
         * 'undefined', meaning the default weekday names are used according to
         * the `lang.locale` or browser settings.
         *
         * @type    {Array<string>}
         */
        weekdays: void 0,
        /**
         * Short week days, starting Sunday. Corresponds to the `%a` format in
         * `Highcharts.dateFormat()`. Defaults to 'undefined', meaning
         * the default short weekday names are used according to the
         * `lang.locale` or browser settings.
         *
         * @sample highcharts/lang/shortweekdays/
         *         Finnish two-letter abbreviations
         *
         * @type      {Array<string>}
         * @since     4.2.4
         * @apioption lang.shortWeekdays
         */
        /**
         * What to show in a date field for invalid dates. Defaults to an empty
         * string.
         *
         * @type      {string}
         * @since     4.1.8
         * @product   highcharts highstock
         * @apioption lang.invalidDate
         */
        /**
         * The title appearing on hovering the zoom in button. The text itself
         * defaults to "+" and can be changed in the button options.
         *
         * @type      {string}
         * @default   Zoom in
         * @product   highmaps
         * @apioption lang.zoomIn
         */
        /**
         * The title appearing on hovering the zoom out button. The text itself
         * defaults to "-" and can be changed in the button options.
         *
         * @type      {string}
         * @default   Zoom out
         * @product   highmaps
         * @apioption lang.zoomOut
         */
        /**
         * The default decimal point used in the `Highcharts.numberFormat`
         * method unless otherwise specified in the function arguments. Defaults
         * to the locale decimal point as determined by `lang.locale`.
         *
         * @type      {string}
         * @default   undefined
         * @since     1.2.2
         * @apioption lang.decimalPoint
         */
        /**
         * [Metric prefixes](https://en.wikipedia.org/wiki/Metric_prefix) used
         * to shorten high numbers in axis labels. Replacing any of the
         * positions with `null` causes the full number to be written. Setting
         * `numericSymbols` to `undefined` disables shortening altogether.
         *
         * @sample {highcharts} highcharts/lang/numericsymbols/
         *         Replacing the symbols with text
         * @sample {highstock} highcharts/lang/numericsymbols/
         *         Replacing the symbols with text
         *
         * @type    {Array<string>}
         * @default ["k", "M", "G", "T", "P", "E"]
         * @since   2.3.0
         */
        numericSymbols: ['k', 'M', 'G', 'T', 'P', 'E'],
        /**
         * The default name for a pie slice (point).
         * @since 12.2.0
         */
        pieSliceName: 'Slice',
        /**
         * The magnitude of [numericSymbols](#lang.numericSymbol) replacements.
         * Use 10000 for Japanese, Korean and various Chinese locales, which
         * use symbols for 10^4, 10^8 and 10^12.
         *
         * @sample highcharts/lang/numericsymbolmagnitude/
         *         10000 magnitude for Japanese
         *
         * @type      {number}
         * @default   1000
         * @since     5.0.3
         * @apioption lang.numericSymbolMagnitude
         */
        /**
         * The default thousands separator used in the `Highcharts.numberFormat`
         * method unless otherwise specified in the function arguments. Defaults
         * to the locale thousands separator as determined by `lang.locale`.
         *
         * @type      {string}
         * @default   undefined
         * @since     1.2.2
         * @apioption lang.thousandsSep
         */
        /**
         * The text for the label appearing when a chart is zoomed.
         *
         * @since 1.2.4
         */
        resetZoom: 'Reset zoom',
        /**
         * The default title of the Y axis
         *
         * @since 12.2.0
         */
        yAxisTitle: 'Values',
        /**
         * The tooltip title for the label appearing when a chart is zoomed.
         *
         * @since 1.2.4
         */
        resetZoomTitle: 'Reset zoom level 1:1'
    },
    /**
     * Global options that don't apply to each chart. These options must be set
     * using the `Highcharts.setOptions` method.
     *
     * ```js
     * Highcharts.setOptions({
     *     global: {
     *         buttonTheme: {
     *             fill: '#d0d0d0'
     *         }
     *     }
     * });
     * ```
     */
    global: {
        /**
         * General theme for buttons. This applies to the zoom button, exporting
         * context menu, map navigation, range selector buttons and custom
         * buttons generated using the `SVGRenderer.button` function. However,
         * each of these may be overridden with more specific options.
         *
         * @sample highcharts/global/buttontheme
         *         General button theme
         * @since 11.4.2
         */
        buttonTheme: {
            /**
             * The fill color for buttons
             */
            fill: "#f7f7f7" /* Palette.neutralColor3 */,
            /**
             * The padding of buttons
             */
            padding: 8,
            /**
             * The border radius for buttons
             */
            r: 2,
            /**
             * The stroke color for buttons
             */
            stroke: "#cccccc" /* Palette.neutralColor20 */,
            /**
             * The stroke width for buttons
             */
            'stroke-width': 1,
            /**
             * CSS styling for the buttons' text
             */
            style: {
                /**
                 * @type {Highcharts.ColorType}
                 */
                color: "#333333" /* Palette.neutralColor80 */,
                cursor: 'pointer',
                fontSize: '0.8em',
                fontWeight: 'normal'
            },
            /**
             * State overrides for the buttons
             */
            states: {
                /**
                 * Hover state overrides for the buttons are applied in addition
                 * to the normal state options
                 */
                hover: {
                    fill: "#e6e6e6" /* Palette.neutralColor10 */
                },
                /**
                 * Select state overrides for the buttons are applied in
                 * addition to the normal state options
                 */
                select: {
                    /**
                     * @type {Highcharts.ColorType}
                     */
                    fill: "#e6e9ff" /* Palette.highlightColor10 */,
                    style: {
                        /**
                         * @type {Highcharts.ColorType}
                         */
                        color: "#000000" /* Palette.neutralColor100 */,
                        fontWeight: 'bold'
                    }
                },
                /**
                 * Disabled state overrides for the buttons are applied in
                 * addition to the normal state options
                 */
                disabled: {
                    /**
                     * Disabled state CSS style overrides for the buttons' text
                     */
                    style: {
                        color: "#cccccc" /* Palette.neutralColor20 */
                    }
                }
            }
        }
    },
    /**
     * Time options that can apply globally or to individual charts. These
     * settings affect how `datetime` axes are laid out, how tooltips are
     * formatted, how series
     * [pointIntervalUnit](#plotOptions.series.pointIntervalUnit) works and how
     * the Highcharts Stock range selector handles time.
     *
     * The common use case is that all charts in the same Highcharts object
     * share the same time settings, in which case the global settings are set
     * using `setOptions`.
     *
     * ```js
     * // Apply time settings globally
     * Highcharts.setOptions({
     *     time: {
     *         timezone: 'Europe/London'
     *     }
     * });
     * // Apply time settings by instance
     * const chart = Highcharts.chart('container', {
     *     time: {
     *         timezone: 'America/New_York'
     *     },
     *     series: [{
     *         data: [1, 4, 3, 5]
     *     }]
     * });
     *
     * // Use the Time object
     * console.log(
     *        'Current time in New York',
     *        chart.time.dateFormat('%Y-%m-%d %H:%M:%S', Date.now())
     * );
     * ```
     *
     * Since v6.0.5, the time options were moved from the `global` object to the
     * `time` object, and time options can be set on each individual chart.
     *
     * @sample {highcharts|highstock}
     *         highcharts/time/timezone/
     *         Set the timezone globally
     * @sample {highcharts}
     *         highcharts/time/individual/
     *         Set the timezone per chart instance
     * @sample {highstock}
     *         stock/time/individual/
     *         Set the timezone per chart instance
     *
     * @since     6.0.5
     * @optionparent time
     */
    time: {
        /**
         * A custom `Date` class for advanced date handling. For example,
         * [JDate](https://github.com/tahajahangir/jdate) can be hooked in to
         * handle Jalali dates.
         *
         * @type      {*}
         * @since     4.0.4
         * @product   highcharts highstock gantt
         */
        Date: void 0,
        /**
         * A named time zone. Supported time zone names rely on the browser
         * implementations, as described in the [mdn
         * docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#timezone).
         * If the given time zone is not recognized by the browser, Highcharts
         * provides a warning and falls back to returning a 0 offset,
         * corresponding to the UTC time zone.
         *
         * The time zone affects axis scaling, tickmark placement and
         * time display in `Highcharts.dateFormat`.
         *
         * Setting `timezone` to `undefined` falls back to the default browser
         * timezone setting.
         *
         * Until v11.2.0, this option depended on moment.js.
         *
         * @sample {highcharts|highstock} highcharts/time/timezone/ Europe/Oslo
         *
         * @type      {string}
         * @since     5.0.7
         * @product   highcharts highstock gantt
         */
        timezone: 'UTC',
        /**
         * The timezone offset in minutes. Positive values are west, negative
         * values are east of UTC, as in the ECMAScript
         * [getTimezoneOffset](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset)
         * method. Use this to display UTC based data in a predefined time zone.
         *
         * This option is deprecated as of v11.4.1 and will be removed in a
         * future release. Use the [time.timezone](#time.timezone) option
         * instead.
         *
         * @see [time.getTimezoneOffset](#time.getTimezoneOffset)
         *
         * @sample {highcharts|highstock} highcharts/time/timezoneoffset/
         *         Timezone offset
         *
         * @since     3.0.8
         * @deprecated 11.4.2
         * @product   highcharts highstock gantt
         */
        timezoneOffset: 0,
        /**
         * Whether to use UTC time for axis scaling, tickmark placement and
         * time display in `Highcharts.dateFormat`. Advantages of using UTC
         * is that the time displays equally regardless of the user agent's
         * time zone settings. Local time can be used when the data is loaded
         * in real time or when correct Daylight Saving Time transitions are
         * required.
         *
         * Setting `useUTC` to true is equivalent to setting `time.timezone` to
         * `"UTC"`. Setting `useUTC` to false is equivalent to setting
         * `time.timezone` to `undefined`.
         *
         * @see [timezone](#time.timezone)
         *
         * @sample {highcharts} highcharts/time/useutc-true/
         *         True by default
         * @sample {highcharts} highcharts/time/useutc-false/
         *         False
         *
         * @deprecated
         */
        useUTC: void 0
    },
    chart: Chart_ChartDefaults,
    /**
     * The chart's main title.
     *
     * @sample {highmaps} maps/title/title/
     *         Title options demonstrated
     * @sample {highcharts} highcharts/title/align-auto/
     *         Default title alignment
     */
    title: {
        /**
         * When the title is floating, the plot area will not move to make space
         * for it.
         *
         * @sample {highcharts} highcharts/chart/zoomtype-none/
         *         False by default
         * @sample {highcharts} highcharts/title/floating/
         *         True - title on top of the plot area
         * @sample {highstock} stock/chart/title-floating/
         *         True - title on top of the plot area
         *
         * @type      {boolean}
         * @default   false
         * @since     2.1
         * @apioption title.floating
         */
        /**
         * Whether to
         * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
         * to render the text.
         *
         * @type      {boolean}
         * @default   false
         * @apioption title.useHTML
         */
        /**
         * The vertical alignment of the title. Can be one of `"top"`,
         * `"middle"` and `"bottom"`. When a value is given, the title behaves
         * as if [floating](#title.floating) were `true`.
         *
         * @sample {highcharts} highcharts/title/verticalalign/
         *         Chart title in bottom right corner
         * @sample {highstock} stock/chart/title-verticalalign/
         *         Chart title in bottom right corner
         *
         * @type      {Highcharts.VerticalAlignValue}
         * @since     2.1
         * @apioption title.verticalAlign
         */
        /**
         * The x position of the title relative to the alignment within
         * `chart.spacingLeft` and `chart.spacingRight`.
         *
         * @sample {highcharts} highcharts/title/align/
         *         Aligned to the plot area (x = 70px = margin left - spacing
         *         left)
         * @sample {highstock} stock/chart/title-align/
         *         Aligned to the plot area (x = 50px = margin left - spacing
         *         left)
         *
         * @type      {number}
         * @default   0
         * @since     2.0
         * @apioption title.x
         */
        /**
         * The y position of the title relative to the alignment within
         * [chart.spacingTop](#chart.spacingTop) and [chart.spacingBottom](
         * #chart.spacingBottom). By default it depends on the font size.
         *
         * @sample {highcharts} highcharts/title/y/
         *         Title inside the plot area
         * @sample {highstock} stock/chart/title-verticalalign/
         *         Chart title in bottom right corner
         *
         * @type      {number}
         * @since     2.0
         * @apioption title.y
         */
        /**
         * CSS styles for the title. Use this for font styling, but use `align`,
         * `x` and `y` for text alignment.
         *
         * Note that the default [title.minScale](#title.minScale) option also
         * affects the rendered font size. In order to keep the font size fixed
         * regardless of title length, set `minScale` to 1.
         *
         * In styled mode, the title style is given in the `.highcharts-title`
         * class.
         *
         * @sample {highcharts} highcharts/title/style/
         *         Custom color and weight
         * @sample {highstock} stock/chart/title-style/
         *         Custom color and weight
         * @sample highcharts/css/titles/
         *         Styled mode
         *
         * @type      {Highcharts.CSSObject}
         * @default   {highcharts|highmaps} { "color": "#333333", "fontSize": "18px" }
         * @default   {highstock} { "color": "#333333", "fontSize": "16px" }
         */
        style: {
            /**
             * @type {Highcharts.ColorType}
             */
            color: "#333333" /* Palette.neutralColor80 */,
            fontWeight: 'bold'
        },
        /**
         * The title of the chart. To disable the title, set the `text` to
         * `undefined`.
         *
         * @sample {highcharts} highcharts/title/text/
         *         Custom title
         * @sample {highstock} stock/chart/title-text/
         *         Custom title
         *
         * @default {highcharts|highmaps} Chart title
         * @default {highstock} undefined
         */
        text: 'Chart title',
        /**
         * The horizontal alignment of the title. Can be one of "left", "center"
         * and "right".
         *
         * Since v12 it defaults to `undefined`, meaning the alignment is
         * computed for best fit. If the text fits in one line, it aligned to
         * the center, but if it is wrapped into multiple lines, it is aligned
         * to the left.
         *
         * @sample {highcharts} highcharts/title/align-auto/
         *         Default alignment, dynamic
         * @sample {highcharts} highcharts/title/align/
         *         Aligned to the plot area (x = 70px = margin left - spacing
         *         left)
         * @sample {highstock} stock/chart/title-align/
         *         Aligned to the plot area (x = 50px = margin left - spacing
         *         left)
         *
         * @type      {Highcharts.AlignValue}
         * @default   undefined
         * @since     2.0
         * @apioption title.align
         */
        /**
         * The margin between the title and the plot area, or if a subtitle
         * is present, the margin between the subtitle and the plot area.
         *
         * @sample {highcharts} highcharts/title/margin-50/
         *         A chart title margin of 50
         * @sample {highcharts} highcharts/title/margin-subtitle/
         *         The same margin applied with a subtitle
         * @sample {highstock} stock/chart/title-margin/
         *         A chart title margin of 50
         *
         * @since 2.1
         */
        margin: 15,
        /**
         * When the title is too wide to fit in the chart, the default behavior
         * is to scale it down to fit, or apply word wrap if it is scaled down
         * to `minScale` and still doesn't fit.
         *
         * The default value reflects the scale, when using default font sizes,
         * when the title font size matches that of the subtitle. The title
         * still stands out as it is bold by default.
         *
         * Set `minScale` to 1 to avoid downscaling.
         *
         * @sample {highcharts} highcharts/title/align-auto/
         *         Downscaling demonstrated
         *
         * @since 12.0.0
         */
        minScale: 0.67
    },
    /**
     * The chart's subtitle. This can be used both to display a subtitle below
     * the main title, and to display random text anywhere in the chart. The
     * subtitle can be updated after chart initialization through the
     * `Chart.setTitle` method.
     *
     * @sample {highcharts} highcharts/title/align-auto/
     *         Default title alignment
     * @sample {highmaps} maps/title/subtitle/
     *         Subtitle options demonstrated
     */
    subtitle: {
        /**
         * The horizontal alignment of the subtitle. Can be one of "left",
         * "center" and "right". Since v12, it defaults to `undefined`, meaning
         * the actual alignment is inherited from the alignment of the main
         * title.
         *
         * @sample {highcharts} highcharts/title/align-auto/
         *         Default title and subtitle alignment, dynamic
         * @sample {highcharts} highcharts/subtitle/align/
         *         Footnote at right of plot area
         * @sample {highstock} stock/chart/subtitle-footnote
         *         Footnote at bottom right of plot area
         *
         * @type  {Highcharts.AlignValue}
         * @default undefined
         * @since 2.0
         * @apioption subtitle.align
         */
        /**
         * When the subtitle is floating, the plot area will not move to make
         * space for it.
         *
         * @sample {highcharts} highcharts/subtitle/floating/
         *         Floating title and subtitle
         * @sample {highstock} stock/chart/subtitle-footnote
         *         Footnote floating at bottom right of plot area
         *
         * @type      {boolean}
         * @default   false
         * @since     2.1
         * @apioption subtitle.floating
         */
        /**
         * Whether to
         * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
         * to render the text.
         *
         * @type      {boolean}
         * @default   false
         * @apioption subtitle.useHTML
         */
        /**
         * The vertical alignment of the title. Can be one of `"top"`,
         * `"middle"` and `"bottom"`. When middle, the subtitle behaves as
         * floating.
         *
         * @sample {highcharts} highcharts/subtitle/verticalalign/
         *         Footnote at the bottom right of plot area
         * @sample {highstock} stock/chart/subtitle-footnote
         *         Footnote at the bottom right of plot area
         *
         * @type      {Highcharts.VerticalAlignValue}
         * @since     2.1
         * @apioption subtitle.verticalAlign
         */
        /**
         * The x position of the subtitle relative to the alignment within
         * `chart.spacingLeft` and `chart.spacingRight`.
         *
         * @sample {highcharts} highcharts/subtitle/align/
         *         Footnote at right of plot area
         * @sample {highstock} stock/chart/subtitle-footnote
         *         Footnote at the bottom right of plot area
         *
         * @type      {number}
         * @default   0
         * @since     2.0
         * @apioption subtitle.x
         */
        /**
         * The y position of the subtitle relative to the alignment within
         * `chart.spacingTop` and `chart.spacingBottom`. By default the subtitle
         * is laid out below the title unless the title is floating.
         *
         * @sample {highcharts} highcharts/subtitle/verticalalign/
         *         Footnote at the bottom right of plot area
         * @sample {highstock} stock/chart/subtitle-footnote
         *         Footnote at the bottom right of plot area
         *
         * @type      {number}
         * @since     2.0
         * @apioption subtitle.y
         */
        /**
         * CSS styles for the title.
         *
         * In styled mode, the subtitle style is given in the
         * `.highcharts-subtitle` class.
         *
         * @sample {highcharts} highcharts/subtitle/style/
         *         Custom color and weight
         * @sample {highcharts} highcharts/css/titles/
         *         Styled mode
         * @sample {highstock} stock/chart/subtitle-style
         *         Custom color and weight
         * @sample {highstock} highcharts/css/titles/
         *         Styled mode
         * @sample {highmaps} highcharts/css/titles/
         *         Styled mode
         *
         * @type      {Highcharts.CSSObject}
         * @default   {"color": "#666666"}
         */
        style: {
            /**
             * @type {Highcharts.ColorType}
             */
            color: "#666666" /* Palette.neutralColor60 */,
            /**
             * @type {number|string}
             */
            fontSize: '0.8em'
        },
        /**
         * The subtitle of the chart.
         *
         * @sample {highcharts|highstock} highcharts/subtitle/text/
         *         Custom subtitle
         * @sample {highcharts|highstock} highcharts/subtitle/text-formatted/
         *         Formatted and linked text.
         */
        text: ''
    },
    /**
     * The chart's caption, which will render below the chart and will be part
     * of exported charts. The caption can be updated after chart initialization
     * through the `Chart.update` or `Chart.caption.update` methods.
     *
     * @sample highcharts/caption/text/
     *         A chart with a caption
     * @since  7.2.0
     */
    caption: {
        /**
         * When the caption is floating, the plot area will not move to make
         * space for it.
         *
         * @type      {boolean}
         * @default   false
         * @apioption caption.floating
         */
        /**
         * The margin between the caption and the plot area.
         */
        margin: 15,
        /**
         * Whether to
         * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
         * to render the text.
         *
         * @type      {boolean}
         * @default   false
         * @apioption caption.useHTML
         */
        /**
         * The x position of the caption relative to the alignment within
         * `chart.spacingLeft` and `chart.spacingRight`.
         *
         * @type      {number}
         * @default   0
         * @apioption caption.x
         */
        /**
         * The y position of the caption relative to the alignment within
         * `chart.spacingTop` and `chart.spacingBottom`.
         *
         * @type      {number}
         * @apioption caption.y
         */
        /**
         * CSS styles for the caption.
         *
         * In styled mode, the caption style is given in the
         * `.highcharts-caption` class.
         *
         * @sample {highcharts} highcharts/css/titles/
         *         Styled mode
         *
         * @type      {Highcharts.CSSObject}
         * @default   {"color": "#666666"}
         */
        style: {
            /**
             * @type {Highcharts.ColorType}
             */
            color: "#666666" /* Palette.neutralColor60 */,
            /**
             * @type {number|string}
             */
            fontSize: '0.8em'
        },
        /**
         * The caption text of the chart.
         *
         * @sample {highcharts} highcharts/caption/text/
         *         Custom caption
         */
        text: '',
        /**
         * The horizontal alignment of the caption. Can be one of "left",
         *  "center" and "right".
         *
         * @type  {Highcharts.AlignValue}
         */
        align: 'left',
        /**
         * The vertical alignment of the caption. Can be one of `"top"`,
         * `"middle"` and `"bottom"`. When middle, the caption behaves as
         * floating.
         *
         * @type      {Highcharts.VerticalAlignValue}
         */
        verticalAlign: 'bottom'
    },
    /**
     * The plotOptions is a wrapper object for config objects for each series
     * type. The config objects for each series can also be overridden for
     * each series item as given in the series array.
     *
     * Configuration options for the series are given in three levels. Options
     * for all series in a chart are given in the [plotOptions.series](
     * #plotOptions.series) object. Then options for all series of a specific
     * type are given in the plotOptions of that type, for example
     * `plotOptions.line`. Next, options for one single series are given in
     * [the series array](#series).
     */
    plotOptions: {},
    /**
     * The legend is a box containing a symbol and name for each series
     * item or point item in the chart. Each series (or points in case
     * of pie charts) is represented by a symbol and its name in the legend.
     *
     * It is possible to override the symbol creator function and create
     * [custom legend symbols](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/studies/legend-custom-symbol/).
     *
     * @productdesc {highmaps}
     * A Highmaps legend by default contains one legend item per series, but if
     * a `colorAxis` is defined, the axis will be displayed in the legend.
     * Either as a gradient, or as multiple legend items for `dataClasses`.
     */
    legend: {
        /**
         * The background color of the legend.
         *
         * @see In styled mode, the legend background fill can be applied with
         *      the `.highcharts-legend-box` class.
         *
         * @sample {highcharts} highcharts/legend/backgroundcolor/
         *         Yellowish background
         * @sample {highstock} stock/legend/align/
         *         Various legend options
         * @sample {highmaps} maps/legend/border-background/
         *         Border and background options
         *
         * @type      {Highcharts.ColorType}
         * @apioption legend.backgroundColor
         */
        /**
         * The width of the drawn border around the legend.
         *
         * @see In styled mode, the legend border stroke width can be applied
         *      with the `.highcharts-legend-box` class.
         *
         * @sample {highcharts} highcharts/legend/borderwidth/
         *         2px border width
         * @sample {highstock} stock/legend/align/
         *         Various legend options
         * @sample {highmaps} maps/legend/border-background/
         *         Border and background options
         *
         * @type      {number}
         * @default   0
         * @apioption legend.borderWidth
         */
        /**
         * Enable or disable the legend. There is also a series-specific option,
         * [showInLegend](#plotOptions.series.showInLegend), that can hide the
         * series from the legend. In some series types this is `false` by
         * default, so it must set to `true` in order to show the legend for the
         * series.
         *
         * @sample {highcharts} highcharts/legend/enabled-false/ Legend disabled
         * @sample {highstock} stock/legend/align/ Various legend options
         * @sample {highmaps} maps/legend/enabled-false/ Legend disabled
         *
         * @default {highstock} false
         * @default {highmaps} true
         * @default {gantt} false
         */
        enabled: true,
        /**
         * The horizontal alignment of the legend box within the chart area.
         * Valid values are `left`, `center` and `right`.
         *
         * In the case that the legend is aligned in a corner position, the
         * `layout` option will determine whether to place it above/below
         * or on the side of the plot area.
         *
         * @sample {highcharts} highcharts/legend/align/
         *         Legend at the right of the chart
         * @sample {highstock} stock/legend/align/
         *         Various legend options
         * @sample {highmaps} maps/legend/alignment/
         *         Legend alignment
         *
         * @type  {Highcharts.AlignValue}
         * @since 2.0
         */
        align: 'center',
        /**
         * If the [layout](legend.layout) is `horizontal` and the legend items
         * span over two lines or more, whether to align the items into vertical
         * columns. Setting this to `false` makes room for more items, but will
         * look more messy.
         *
         * @sample highcharts/legend/aligncolumns
         *         Align columns
         *
         * @since 6.1.0
         */
        alignColumns: true,
        /**
         * A CSS class name to apply to the legend group.
         */
        className: 'highcharts-no-tooltip',
        /**
         * General event handlers for the legend. These event hooks can
         * also be attached to the legend at run time using the
         * `Highcharts.addEvent` function.
         *
         * @declare Highcharts.LegendEventsOptionsObject
         */
        events: {
        /**
         * Fires when the legend item belonging to the series is clicked.
         * One parameter, `event`, is passed to the function. The default
         * action is to toggle the visibility of the series, point or data
         * class. This can be prevented by returning `false` or calling
         * `event.preventDefault()`.
         *
         * @sample {highcharts} highcharts/legend/itemclick/
         *         Confirm hiding and showing
         * @sample {highcharts} highcharts/legend/pie-legend-itemclick/
         *         Confirm toggle visibility of pie slices
         *
         * @type      {Highcharts.LegendItemClickCallbackFunction}
         * @context   Highcharts.Legend
         * @apioption legend.events.itemClick
         */
        },
        /**
         * When the legend is floating, the plot area ignores it and is allowed
         * to be placed below it.
         *
         * @sample {highcharts} highcharts/legend/floating-false/
         *         False by default
         * @sample {highcharts} highcharts/legend/floating-true/
         *         True
         * @sample {highmaps} maps/legend/alignment/
         *         Floating legend
         *
         * @type      {boolean}
         * @default   false
         * @since     2.1
         * @apioption legend.floating
         */
        /**
         * The layout of the legend items. Can be one of `horizontal` or
         * `vertical` or `proximate`. When `proximate`, the legend items will be
         * placed as close as possible to the graphs they're representing,
         * except in inverted charts or when the legend position doesn't allow
         * it.
         *
         * @sample {highcharts} highcharts/legend/layout-horizontal/
         *         Horizontal by default
         * @sample {highcharts} highcharts/legend/layout-vertical/
         *         Vertical
         * @sample highcharts/legend/layout-proximate
         *         Labels proximate to the data
         * @sample {highstock} stock/legend/layout-horizontal/
         *         Horizontal by default
         * @sample {highmaps} maps/legend/padding-itemmargin/
         *         Vertical with data classes
         * @sample {highmaps} maps/legend/layout-vertical/
         *         Vertical with color axis gradient
         *
         * @validvalue ["horizontal", "vertical", "proximate"]
         */
        layout: 'horizontal',
        /**
         * In a legend with horizontal layout, the itemDistance defines the
         * pixel distance between each item.
         *
         * @sample {highcharts} highcharts/legend/itemwidth-default/
         *         40px item distance
         * @sample {highstock} highcharts/legend/itemwidth-default/
         *         40px item distance
         *
         * @type      {number}
         * @default   {highcharts} 20
         * @default   {highstock} 20
         * @default   {highmaps} 8
         * @since     3.0.3
         * @apioption legend.itemDistance
         */
        /**
         * The pixel bottom margin for each legend item.
         *
         * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
         *         Padding and item margins demonstrated
         * @sample {highmaps} maps/legend/padding-itemmargin/
         *         Padding and item margins demonstrated
         *
         * @since     2.2.0
         */
        itemMarginBottom: 2,
        /**
         * The pixel top margin for each legend item.
         *
         * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
         *         Padding and item margins demonstrated
         * @sample {highmaps} maps/legend/padding-itemmargin/
         *         Padding and item margins demonstrated
         *
         * @since     2.2.0
         */
        itemMarginTop: 2,
        /**
         * The width for each legend item. By default the items are laid out
         * successively. In a [horizontal layout](legend.layout), if the items
         * are laid out across two rows or more, they will be vertically aligned
         * depending on the [legend.alignColumns](legend.alignColumns) option.
         *
         * @sample {highcharts} highcharts/legend/itemwidth-default/
         *         Undefined by default
         * @sample {highcharts} highcharts/legend/itemwidth-80/
         *         80 for aligned legend items
         *
         * @type      {number}
         * @since     2.0
         * @apioption legend.itemWidth
         */
        /**
         * A [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
         * for each legend label. Available variables relates to properties on
         * the series, or the point in case of pies.
         *
         * @sample {highcharts} highcharts/legend/labelformat/
         *         Add text
         *
         * @type      {string}
         * @default   {name}
         * @since     1.3
         * @apioption legend.labelFormat
         */
        /**
         * Callback function to format each of the series' labels. The `this`
         * keyword refers to the series object, or the point object in case of
         * pie charts. By default the series or point name is printed. Since
         * v12.5.0, the callback also receives `ctx` as the first argument, so
         * that arrow functions can access the same context as regular
         * functions using `this`.
         *
         * @productdesc {highmaps}
         * In Highmaps the context can also be a data class in case of a
         * `colorAxis`.
         *
         * @sample {highcharts} highcharts/legend/labelformatter/
         *         Add text
         * @sample {highmaps} maps/legend/labelformatter/
         *         Data classes with label formatter
         *
         * @type {Highcharts.FormatterCallbackFunction<Highcharts.Point|Highcharts.Series>}
         */
        labelFormatter: function () {
            // eslint-enable valid-jsdoc
            return this.name;
        },
        /**
         * Line height for the legend items. Deprecated as of 2.1\. Instead,
         * the line height for each item can be set using
         * `itemStyle.lineHeight`, and the padding between items using
         * `itemMarginTop` and `itemMarginBottom`.
         *
         * @sample {highcharts} highcharts/legend/lineheight/
         *         Setting padding
         *
         * @deprecated
         *
         * @type      {number}
         * @default   16
         * @since     2.0
         * @product   highcharts gantt
         * @apioption legend.lineHeight
         */
        /**
         * If the plot area sized is calculated automatically and the legend is
         * not floating, the legend margin is the space between the legend and
         * the axis labels or plot area.
         *
         * @sample {highcharts} highcharts/legend/margin-default/
         *         12 pixels by default
         * @sample {highcharts} highcharts/legend/margin-30/
         *         30 pixels
         *
         * @type      {number}
         * @default   12
         * @since     2.1
         * @apioption legend.margin
         */
        /**
         * Maximum width for the legend. Can be a percentage of the chart width,
         * or an integer representing how many pixels wide the legend can be.
         *
         * @sample {highcharts} highcharts/legend/maxwidth/
         *         Max width set to 7%
         *
         * @type      {number|string}
         * @apioption legend.maxWidth
         */
        /**
         * Maximum pixel height for the legend. When the maximum height is
         * extended, navigation will show.
         *
         * @type      {number}
         * @since     2.3.0
         * @apioption legend.maxHeight
         */
        /**
         * The color of the drawn border around the legend.
         *
         * @see In styled mode, the legend border stroke can be applied with the
         *      `.highcharts-legend-box` class.
         *
         * @sample {highcharts} highcharts/legend/bordercolor/
         *         Brown border
         * @sample {highstock} stock/legend/align/
         *         Various legend options
         * @sample {highmaps} maps/legend/border-background/
         *         Border and background options
         *
         * @type {Highcharts.ColorType}
         */
        borderColor: "#999999" /* Palette.neutralColor40 */,
        /**
         * The border corner radius of the legend.
         *
         * @sample {highcharts} highcharts/legend/borderradius-default/
         *         Square by default
         * @sample {highcharts} highcharts/legend/borderradius-round/
         *         5px rounded
         * @sample {highmaps} maps/legend/border-background/
         *         Border and background options
         */
        borderRadius: 0,
        /**
         * Options for the paging or navigation appearing when the legend is
         * overflown. Navigation works well on screen, but not in static
         * exported images. One way of working around that is to
         * [increase the chart height in
         * export](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/legend/navigation-enabled-false/).
         *
         * @sample highcharts/legend/scrollable-vertical/
         *         Legend with vertical scrollable extension
         * @sample highcharts/legend/scrollable-horizontal/
         *         Legend with horizontal scrollable extension
         *
         */
        navigation: {
            /**
             * How to animate the pages when navigating up or down. A value of
             * `true` applies the default navigation given in the
             * `chart.animation` option. Additional options can be given as an
             * object containing values for easing and duration.
             *
             * @sample {highcharts} highcharts/legend/navigation/
             *         Legend page navigation demonstrated
             * @sample {highstock} highcharts/legend/navigation/
             *         Legend page navigation demonstrated
             *
             * @type      {boolean|Partial<Highcharts.AnimationOptionsObject>}
             * @default   true
             * @since     2.2.4
             * @apioption legend.navigation.animation
             */
            /**
             * The pixel size of the up and down arrows in the legend paging
             * navigation.
             *
             * @sample {highcharts} highcharts/legend/navigation/
             *         Legend page navigation demonstrated
             * @sample {highstock} highcharts/legend/navigation/
             *         Legend page navigation demonstrated
             *
             * @type      {number}
             * @default   12
             * @since     2.2.4
             * @apioption legend.navigation.arrowSize
             */
            /**
             * Whether to enable the legend navigation. In most cases, disabling
             * the navigation results in an unwanted overflow.
             *
             * See also the
             * [adapt chart to legend](https://github.com/highcharts/adapt-chart-to-legend)
             * plugin for a solution to extend the chart height to make room for
             * the legend, optionally in exported charts only.
             *
             * @type      {boolean}
             * @default   true
             * @since     4.2.4
             * @apioption legend.navigation.enabled
             */
            /**
             * Text styles for the legend page navigation.
             *
             * @see In styled mode, the navigation items are styled with the
             *      `.highcharts-legend-navigation` class.
             *
             * @sample {highcharts} highcharts/legend/navigation/
             *         Legend page navigation demonstrated
             * @sample {highstock} highcharts/legend/navigation/
             *         Legend page navigation demonstrated
             *
             * @type      {Highcharts.CSSObject}
             * @since     2.2.4
             * @apioption legend.navigation.style
             */
            style: {
                /**
                 * @type {number|string}
                 */
                fontSize: '0.8em'
            },
            /**
             * The color for the active up or down arrow in the legend page
             * navigation.
             *
             * @see In styled mode, the active arrow be styled with the
             *      `.highcharts-legend-nav-active` class.
             *
             * @sample  {highcharts} highcharts/legend/navigation/
             *          Legend page navigation demonstrated
             * @sample  {highstock} highcharts/legend/navigation/
             *          Legend page navigation demonstrated
             *
             * @type  {Highcharts.ColorType}
             * @since 2.2.4
             */
            activeColor: "#0022ff" /* Palette.highlightColor100 */,
            /**
             * The color of the inactive up or down arrow in the legend page
             * navigation. .
             *
             * @see In styled mode, the inactive arrow be styled with the
             *      `.highcharts-legend-nav-inactive` class.
             *
             * @sample {highcharts} highcharts/legend/navigation/
             *         Legend page navigation demonstrated
             * @sample {highstock} highcharts/legend/navigation/
             *         Legend page navigation demonstrated
             *
             * @type  {Highcharts.ColorType}
             * @since 2.2.4
             */
            inactiveColor: "#cccccc" /* Palette.neutralColor20 */
        },
        /**
         * The inner padding of the legend box.
         *
         * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
         *         Padding and item margins demonstrated
         * @sample {highmaps} maps/legend/padding-itemmargin/
         *         Padding and item margins demonstrated
         *
         * @type      {number}
         * @default   8
         * @since     2.2.0
         * @apioption legend.padding
         */
        /**
         * Whether to reverse the order of the legend items compared to the
         * order of the series or points as defined in the configuration object.
         *
         * @see [yAxis.reversedStacks](#yAxis.reversedStacks),
         *      [series.legendIndex](#series.legendIndex)
         *
         * @sample {highcharts} highcharts/legend/reversed/
         *         Stacked bar with reversed legend
         *
         * @type      {boolean}
         * @default   false
         * @since     1.2.5
         * @apioption legend.reversed
         */
        /**
         * Whether to show the symbol on the right side of the text rather than
         * the left side. This is common in Arabic and Hebrew.
         *
         * @sample {highcharts} highcharts/legend/rtl/
         *         Symbol to the right
         *
         * @type      {boolean}
         * @default   false
         * @since     2.2
         * @apioption legend.rtl
         */
        /**
         * CSS styles for the legend area. In the 1.x versions the position
         * of the legend area was determined by CSS. In 2.x, the position is
         * determined by properties like `align`, `verticalAlign`, `x` and `y`,
         * but the styles are still parsed for backwards compatibility.
         *
         * @deprecated
         *
         * @type      {Highcharts.CSSObject}
         * @product   highcharts highstock
         * @apioption legend.style
         */
        /**
         * CSS styles for each legend item. Only a subset of CSS is supported,
         * notably those options related to text. The default `textOverflow`
         * property makes long texts truncate. Set it to `undefined` to wrap
         * text instead. A `width` property can be added to control the text
         * width.
         *
         * @see In styled mode, the legend items can be styled with the
         *      `.highcharts-legend-item` class.
         *
         * @sample {highcharts} highcharts/legend/itemstyle/
         *         Bold black text
         * @sample {highmaps} maps/legend/itemstyle/
         *         Item text styles
         *
         * @type    {Highcharts.CSSObject}
         * @default {"color": "#333333", "cursor": "pointer", "fontSize": "0.8em", "fontWeight": "bold", "textOverflow": "ellipsis"}
         */
        itemStyle: {
            /**
             * @ignore
             */
            color: "#333333" /* Palette.neutralColor80 */,
            /**
             * @ignore
             */
            cursor: 'pointer',
            /**
             * @ignore
             */
            fontSize: '0.8em',
            /**
             * @ignore
             */
            textDecoration: 'none',
            /**
             * @ignore
             */
            textOverflow: 'ellipsis'
        },
        /**
         * CSS styles for each legend item in hover mode. Only a subset of
         * CSS is supported, notably those options related to text. Properties
         * are inherited from `style` unless overridden here.
         *
         * @see In styled mode, the hovered legend items can be styled with
         *      the `.highcharts-legend-item:hover` pseudo-class.
         *
         * @sample {highcharts} highcharts/legend/itemhoverstyle/
         *         Red on hover
         * @sample {highmaps} maps/legend/itemstyle/
         *         Item text styles
         *
         * @type    {Highcharts.CSSObject}
         * @default {"color": "#000000"}
         */
        itemHoverStyle: {
            /**
             * @ignore
             */
            color: "#000000" /* Palette.neutralColor100 */
        },
        /**
         * CSS styles for each legend item when the corresponding series or
         * point is hidden. Only a subset of CSS is supported, notably those
         * options related to text. Properties are inherited from `style`
         * unless overridden here.
         *
         * @see In styled mode, the hidden legend items can be styled with
         *      the `.highcharts-legend-item-hidden` class.
         *
         * @sample {highcharts} highcharts/legend/itemhiddenstyle/
         *         Darker gray color
         *
         * @type    {Highcharts.CSSObject}
         * @default {"color": "#cccccc"}
         */
        itemHiddenStyle: {
            /**
             * @ignore
             */
            color: "#666666" /* Palette.neutralColor60 */,
            /**
             * @ignore
             */
            textDecoration: 'line-through'
        },
        /**
         * Whether to apply a drop shadow to the legend. A `backgroundColor`
         * also needs to be applied for this to take effect. The shadow can be
         * an object configuration containing `color`, `offsetX`, `offsetY`,
         * `opacity` and `width`.
         *
         * @sample {highcharts} highcharts/legend/shadow/
         *         White background and drop shadow
         * @sample {highstock} stock/legend/align/
         *         Various legend options
         * @sample {highmaps} maps/legend/border-background/
         *         Border and background options
         *
         * @type {boolean|Highcharts.CSSObject}
         */
        shadow: false,
        /**
         * Default styling for the checkbox next to a legend item when
         * `showCheckbox` is true.
         *
         * @type {Highcharts.CSSObject}
         * @default {"width": "13px", "height": "13px", "position":"absolute"}
         */
        itemCheckboxStyle: {
            /**
             * @ignore
             */
            position: 'absolute',
            /**
             * @ignore
             */
            width: '13px', // For IE precision
            /**
             * @ignore
             */
            height: '13px'
        },
        /// itemWidth: undefined,
        /**
         * When this is true, the legend symbol width will be the same as
         * the symbol height, which in turn defaults to the font size of the
         * legend items.
         *
         * @since 5.0.0
         */
        squareSymbol: true,
        /**
         * The pixel height of the symbol for series types that use a rectangle
         * in the legend. Defaults to the font size of legend items.
         *
         * Note: This option is a default source of color axis height, if the
         * [colorAxis.height](https://api.highcharts.com/highcharts/colorAxis.height)
         * option is not set.
         *
         * @productdesc {highmaps}
         * In Highmaps, when the symbol is the gradient of a vertical color
         * axis, the height defaults to 200.
         *
         * @sample {highmaps} maps/legend/layout-vertical-sized/
         *         Sized vertical gradient
         * @sample {highmaps} maps/legend/padding-itemmargin/
         *         No distance between data classes
         *
         * @type      {number}
         * @since     3.0.8
         * @apioption legend.symbolHeight
         */
        /**
         * The border radius of the symbol for series types that use a rectangle
         * in the legend. Defaults to half the `symbolHeight`, effectively
         * creating a circle.
         *
         * For color axis scales, it defaults to 3.
         *
         * @sample {highcharts} highcharts/legend/symbolradius/
         *         Round symbols
         * @sample {highstock} highcharts/legend/symbolradius/
         *         Round symbols
         * @sample {highmaps} highcharts/legend/symbolradius/
         *         Round symbols
         *
         * @type      {number}
         * @since     3.0.8
         * @apioption legend.symbolRadius
         */
        /**
         * The pixel width of the legend item symbol. When the `squareSymbol`
         * option is set, this defaults to the `symbolHeight`, otherwise 16.
         *
         * Note: This option is a default source of color axis width, if the
         * [colorAxis.width](https://api.highcharts.com/highcharts/colorAxis.width)
         * option is not set.
         *
         * @productdesc {highmaps}
         * In Highmaps, when the symbol is the gradient of a horizontal color
         * axis, the width defaults to 200.
         *
         * @sample {highcharts} highcharts/legend/symbolwidth/
         *         Greater symbol width and padding
         * @sample {highmaps} maps/legend/padding-itemmargin/
         *         Padding and item margins demonstrated
         * @sample {highmaps} maps/legend/layout-vertical-sized/
         *         Sized vertical gradient
         *
         * @type      {number}
         * @apioption legend.symbolWidth
         */
        /**
         * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
         * to render the legend item texts.
         *
         * Prior to 4.1.7, when using HTML, [legend.navigation](
         * #legend.navigation) was disabled.
         *
         * @sample highcharts/legend/scrollable-vertical/
         *         Legend with vertical scrollable extension
         * @sample highcharts/legend/scrollable-horizontal/
         *         Legend with horizontal scrollable extension
         *
         * @type      {boolean}
         * @default   false
         * @apioption legend.useHTML
         */
        /**
         * For a color axis with data classes, how many decimals to render in
         * the legend. The default preserves the decimals of the range numbers.
         *
         * @type      {number}
         * @default   -1
         * @product   highcharts highmaps
         * @apioption legend.valueDecimals
         */
        /**
         * For a color axis with data classes, a suffix for the range numbers in
         * the legend.
         *
         * @type      {string}
         * @default   ''
         * @product   highcharts highmaps
         * @apioption legend.valueSuffix
         */
        /**
         * The width of the legend box. If a number is set, it translates to
         * pixels. Since v7.0.2 it allows setting a percent string of the full
         * chart width, for example `40%`.
         *
         * Defaults to the full chart width for legends below or above the
         * chart, half the chart width for legends to the left and right.
         *
         * @sample {highcharts} highcharts/legend/width/
         *         Aligned to the plot area
         * @sample {highcharts} highcharts/legend/width-percent/
         *         A percent of the chart width
         *
         * @type      {number|string}
         * @since     2.0
         * @apioption legend.width
         */
        /**
         * The pixel padding between the legend item symbol and the legend
         * item text.
         *
         * @sample {highcharts} highcharts/legend/symbolpadding/
         *         Greater symbol width and padding
         */
        symbolPadding: 5,
        /**
         * The vertical alignment of the legend box. Can be one of `top`,
         * `middle` or `bottom`. Vertical position can be further determined
         * by the `y` option.
         *
         * In the case that the legend is aligned in a corner position, the
         * `layout` option will determine whether to place it above/below
         * or on the side of the plot area.
         *
         * When the [layout](#legend.layout) option is `proximate`, the
         * `verticalAlign` option doesn't apply.
         *
         * @sample {highcharts} highcharts/legend/verticalalign/
         *         Legend 100px from the top of the chart
         * @sample {highstock} stock/legend/align/
         *         Various legend options
         * @sample {highmaps} maps/legend/alignment/
         *         Legend alignment
         *
         * @type  {Highcharts.VerticalAlignValue}
         * @since 2.0
         */
        verticalAlign: 'bottom',
        // Width: undefined,
        /**
         * The x offset of the legend relative to its horizontal alignment
         * `align` within chart.spacingLeft and chart.spacingRight. Negative
         * x moves it to the left, positive x moves it to the right.
         *
         * @sample {highcharts} highcharts/legend/width/
         *         Aligned to the plot area
         *
         * @since 2.0
         */
        x: 0,
        /**
         * The vertical offset of the legend relative to it's vertical alignment
         * `verticalAlign` within chart.spacingTop and chart.spacingBottom.
         *  Negative y moves it up, positive y moves it down.
         *
         * @sample {highcharts} highcharts/legend/verticalalign/
         *         Legend 100px from the top of the chart
         * @sample {highstock} stock/legend/align/
         *         Various legend options
         * @sample {highmaps} maps/legend/alignment/
         *         Legend alignment
         *
         * @since 2.0
         */
        y: 0,
        /**
         * A title to be added on top of the legend.
         *
         * @sample {highcharts} highcharts/legend/title/
         *         Legend title
         * @sample {highmaps} maps/legend/alignment/
         *         Legend with title
         *
         * @since 3.0
         */
        title: {
            /**
             * A text or HTML string for the title.
             *
             * @type      {string}
             * @since     3.0
             * @apioption legend.title.text
             */
            /**
             * Generic CSS styles for the legend title.
             *
             * @see In styled mode, the legend title is styled with the
             *      `.highcharts-legend-title` class.
             *
             * @type    {Highcharts.CSSObject}
             * @default {"fontSize": "0.8em", "fontWeight": "bold"}
             * @since   3.0
             */
            style: {
                /**
                 * @ignore
                 */
                color: "#333333" /* Palette.neutralColor80 */,
                /**
                 * @ignore
                 */
                fontSize: '0.8em',
                /**
                 * @ignore
                 */
                fontWeight: 'bold'
            }
        }
    },
    /**
     * The loading options control the appearance of the loading screen
     * that covers the plot area on chart operations. This screen only
     * appears after an explicit call to `chart.showLoading()`. It is a
     * utility for developers to communicate to the end user that something
     * is going on, for example while retrieving new data via an XHR connection.
     * The "Loading..." text itself is not part of this configuration
     * object, but part of the `lang` object.
     */
    loading: {
        /**
         * The duration in milliseconds of the fade out effect.
         *
         * @sample highcharts/loading/hideduration/
         *         Fade in and out over a second
         *
         * @type      {number}
         * @default   100
         * @since     1.2.0
         * @apioption loading.hideDuration
         */
        /**
         * The duration in milliseconds of the fade in effect.
         *
         * @sample highcharts/loading/hideduration/
         *         Fade in and out over a second
         *
         * @type      {number}
         * @default   100
         * @since     1.2.0
         * @apioption loading.showDuration
         */
        /**
         * CSS styles for the loading label `span`.
         *
         * @see In styled mode, the loading label is styled with the
         *      `.highcharts-loading-inner` class.
         *
         * @sample {highcharts|highmaps} highcharts/loading/labelstyle/
         *         Vertically centered
         * @sample {highstock} stock/loading/general/
         *         Label styles
         *
         * @type    {Highcharts.CSSObject}
         * @default {"fontWeight": "bold", "position": "relative", "top": "45%"}
         * @since   1.2.0
         */
        labelStyle: {
            /**
             * @ignore
             */
            fontWeight: 'bold',
            /**
             * @ignore
             */
            position: 'relative',
            /**
             * @ignore
             */
            top: '45%'
        },
        /**
         * CSS styles for the loading screen that covers the plot area.
         *
         * In styled mode, the loading label is styled with the
         * `.highcharts-loading` class.
         *
         * @sample  {highcharts|highmaps} highcharts/loading/style/
         *          Gray plot area, white text
         * @sample  {highstock} stock/loading/general/
         *          Gray plot area, white text
         *
         * @type    {Highcharts.CSSObject}
         * @default {"position": "absolute", "backgroundColor": "#ffffff", "opacity": 0.5, "textAlign": "center"}
         * @since   1.2.0
         */
        style: {
            /**
             * @ignore
             */
            position: 'absolute',
            /**
             * @ignore
             */
            backgroundColor: "#ffffff" /* Palette.backgroundColor */,
            /**
             * @ignore
             */
            opacity: 0.5,
            /**
             * @ignore
             */
            textAlign: 'center'
        }
    },
    /**
     * Options for the tooltip that appears when the user hovers over a
     * series or point.
     *
     * @declare Highcharts.TooltipOptions
     */
    tooltip: {
        /**
         * The color of the tooltip border. When `undefined`, the border takes
         * the color of the corresponding series or point.
         *
         * Note that the [borderWidth](#tooltip.borderWidth) is usually 0 by
         * default, so the border color may not be visible until a border width
         * is set.
         *
         * @sample {highcharts} highcharts/tooltip/bordercolor-default/ Follow
         *         series by default
         * @sample {highcharts} highcharts/tooltip/bordercolor-black/ Black
         *         border
         * @sample {highstock} stock/tooltip/general/ Styled tooltip
         * @sample {highmaps} maps/tooltip/background-border/ Background and
         *         border demo
         *
         * @type {Highcharts.ColorType}
         * @apioption tooltip.borderColor
         */
        /**
         * A CSS class name to apply to the tooltip, allowing unique CSS
         * styling for each chart.
         *
         * **Note:** The class is applied to the SVG element of the tooltip
         * (the tooltip label group), not to a container div. This allows you
         * to style the tooltip using CSS applicable to SVG elements.
         *
         * When [tooltip.outside](#tooltip.outside) is `true`, a separate
         * container div with class `highcharts-tooltip-container` is created
         * as the parent to the SVG tooltip element, but the `className` option
         * is still applied to the SVG element itself, not to the container.
         *
         * @type      {string}
         * @apioption tooltip.className
         */
        /**
         * Since 4.1, the crosshair definitions are moved to the Axis object
         * in order for a better separation from the tooltip. See
         * [xAxis.crosshair](#xAxis.crosshair).
         *
         * @sample {highcharts} highcharts/tooltip/crosshairs-x/
         *         Enable a crosshair for the x value
         *
         * @deprecated
         *
         * @type      {*}
         * @default   true
         * @apioption tooltip.crosshairs
         */
        /**
         * Distance from point to tooltip in pixels.
         *
         * @type      {number}
         * @default   16
         * @apioption tooltip.distance
         */
        /**
         * Whether the tooltip should be fixed to one position in the chart, or
         * located next to the point or mouse. When the tooltip is fixed, the
         * position can be further specified with the
         * [tooltip.position](#tooltip.position) options set.
         *
         * @sample    highcharts/tooltip/fixed/
         *            Fixed tooltip and position options
         * @sample    {highstock} stock/tooltip/fixed/
         *            Stock chart with fixed tooltip
         * @sample    {highmaps} maps/tooltip/fixed/
         *            Map with fixed tooltip
         *
         * @type      {boolean}
         * @default   false
         * @since 12.2.0
         * @apioption tooltip.fixed
         */
        /**
         * Whether the tooltip should follow the mouse as it moves across
         * columns, pie slices and other point types with an extent.
         * By default it behaves this way for pie, polygon, map, sankey
         * and wordcloud series by override in the `plotOptions`
         * for those series types.
         *
         * Does not apply if [split](#tooltip.split) is `true`.
         *
         * For touch moves to behave the same way, [followTouchMove](
         * #tooltip.followTouchMove) must be `true` also.
         *
         * @sample highcharts/tooltip/followpointer/
         *         Tooltip follow pointer comparison
         *
         * @type      {boolean}
         * @default   {highcharts} false
         * @default   {highstock} false
         * @default   {highmaps} true
         * @since     3.0
         * @apioption tooltip.followPointer
         */
        /**
         * Whether the tooltip should update as the finger moves on a touch
         * device. If this is `true` and [chart.panning](#chart.panning) is
         * set,`followTouchMove` will take over one-finger touches, so the user
         * needs to use two fingers for zooming and panning.
         *
         * Note the difference to [followPointer](#tooltip.followPointer) that
         * only defines the _position_ of the tooltip. If `followPointer` is
         * false in for example a column series, the tooltip will show above or
         * below the column, but as `followTouchMove` is true, the tooltip will
         * jump from column to column as the user swipes across the plot area.
         *
         * @type      {boolean}
         * @default   {highcharts} true
         * @default   {highstock} true
         * @default   {highmaps} false
         * @since     3.0.1
         * @apioption tooltip.followTouchMove
         */
        /**
         * A [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
         * for the whole shared tooltip. When format strings are a requirement,
         * it is usually more convenient to use `headerFormat`, `pointFormat`
         * and `footerFormat`, but the `format` option allows combining them
         * into one setting.
         *
         * The context of the format string is the same as that of the
         * `tooltip.formatter` callback.
         *
         * @sample {highcharts} highcharts/tooltip/format-shared/
         *         Format for shared tooltip
         *
         * @type      {string}
         * @default   undefined
         * @since     11.1.0
         * @apioption tooltip.format
         */
        /**
         * Callback function to format the text of the tooltip from scratch. In
         * case of single or [shared](#tooltip.shared) tooltips, a string should
         * be returned. In case of [split](#tooltip.split) tooltips, it should
         * return an array where the first item is the header, and subsequent
         * items are mapped to the points. Return `false` to disable tooltip for
         * a specific point on series.
         *
         * A subset of HTML is supported. Unless `useHTML` is true, the HTML of
         * the tooltip is parsed and converted to SVG, therefore this isn't a
         * complete HTML renderer. The following HTML tags are supported: `b`,
         * `br`, `em`, `i`, `span`, `strong`. Spans can be styled with a `style`
         * attribute, but only text-related CSS, that is shared with SVG, is
         * handled.
         *
         * The context of the formatter (since v12) is the
         * [Point](https://api.highcharts.com/class-reference/Highcharts.Point)
         * instance. If the tooltip is shared or split, an array `this.points`
         * contains all points of the hovered x-value.
         *
         * Common properties from the Point to use in the formatter include:
         *
         * - **Point.percentage**:
         *   Stacked series and pies only. The point's percentage of the total.
         *
         * - **Point.points**:
         *   In a shared or split tooltip, this is an array containing all the
         *   hovered points.
         *
         * - **this.series**:
         *   The series object. The series name is available through
         *   `this.series.name`.
         *
         * - **this.total**:
         *   The total value at this point's x value in a stacked series, or the
         *   sum of all slices in a pie series.
         *
         * - **this.x**:
         *   The x value.
         *
         * - **this.y**:
         *   The y value.
         *
         * @sample {highcharts} highcharts/tooltip/formatter-simple/
         *         Simple string formatting
         * @sample {highcharts} highcharts/tooltip/formatter-shared/
         *         Formatting with shared tooltip
         * @sample {highcharts|highstock} highcharts/tooltip/formatter-split/
         *         Formatting with split tooltip
         * @sample highcharts/tooltip/formatter-conditional-default/
         *         Extending default formatter
         * @sample {highstock} stock/tooltip/formatter/
         *         Formatting with shared tooltip
         * @sample {highmaps} maps/tooltip/formatter/
         *         String formatting
         *
         * Since v12.6.0, the callback also receives `ctx` as the second
         * argument, so that arrow functions can access the same context as
         * regular functions using `this`.
         *
         * @type      {Highcharts.TooltipFormatterCallbackFunction}
         * @apioption tooltip.formatter
         */
        /**
         * Callback function to format the text of the tooltip for
         * visible null points.
         * Works analogously to [formatter](#tooltip.formatter), including the
         * `ctx` callback argument added in v12.5.0.
         *
         * @sample highcharts/plotoptions/series-nullformat
         *         Format data label and tooltip for null point.
         *
         * @type      {Highcharts.TooltipFormatterCallbackFunction}
         * @apioption tooltip.nullFormatter
         */
        /**
         * Whether to allow the tooltip to render outside the chart's SVG
         * element box. By default (`false`), the tooltip is rendered within the
         * chart's SVG element, which results in the tooltip being aligned
         * inside the chart area. For small charts, this may result in clipping
         * or overlapping. When `true`, a separate SVG element is created and
         * overlaid on the page, allowing the tooltip to be aligned inside the
         * page itself. Beware that with this option active, CSS classes on the
         * chart's target container, with classnames matching the pattern
         * 'highcharts-*', will be set on the tooltip as well. This is done to
         * support theming for tooltips with this option.
         *
         * Defaults to `true` if `chart.scrollablePlotArea` is activated,
         * otherwise `false`.
         *
         * @sample highcharts/tooltip/outside
         *         Small charts with tooltips outside
         *
         * @type      {boolean|undefined}
         * @default   undefined
         * @since     6.1.1
         * @apioption tooltip.outside
         */
        /**
         * A callback function for formatting the HTML output for a single point
         * in the tooltip. Like the `pointFormat` string, but with more
         * flexibility. Since v12.6.0, the callback also receives `ctx` as the
         * first argument, so that arrow functions can access the same context
         * as regular functions using `this`.
         *
         * @type      {Highcharts.FormatterCallbackFunction<Highcharts.Point>}
         * @since     4.1.0
         * @context   Highcharts.Point
         * @apioption tooltip.pointFormatter
         */
        /**
         * A callback function to place the tooltip in a custom position. The
         * callback receives four parameters: `labelWidth`, `labelHeight`,
         * `point`, and `ctx`, where point contains values for `plotX` and
         * `plotY` telling where the reference point is in the plot area, and
         * `ctx` is the tooltip context (so that arrow-functions can access the
         * same context as a normal function using `this`). Add
         * `chart.plotLeft` and `chart.plotTop` to get the full coordinates.
         * Since v12.6.0, the callback receives `ctx`.
         *
         * To find the actual hovered `Point` instance, use
         * `this.chart.hoverPoint`. For shared or split tooltips, all the hover
         * points are available in `this.chart.hoverPoints`.
         *
         * Since v7, when [tooltip.split](#tooltip.split) option is enabled,
         * positioner is called for each of the boxes separately, including
         * xAxis header. xAxis header is not a point, instead `point` argument
         * contains info: `{ plotX: Number, plotY: Number, isHeader: Boolean }`
         *
         * Since v12.2, the [tooltip.fixed](#tooltip.fixed) option combined with
         * [tooltip.position](#tooltip.position) covers most of the use cases
         * for custom tooltip positioning.
         *
         * The return should be an object containing x and y values, for example
         * `{ x: 100, y: 100 }`.
         *
         * @sample {highcharts} highcharts/tooltip/positioner/
         *         A fixed tooltip position
         * @sample {highstock} stock/tooltip/positioner/
         *         A fixed tooltip position on top of the chart
         * @sample {highmaps} maps/tooltip/positioner/
         *         A fixed tooltip position
         * @sample {highstock} stock/tooltip/split-positioner/
         *         Split tooltip with fixed positions
         * @sample {highstock} stock/tooltip/positioner-scrollable-plotarea/
         *         Scrollable plot area combined with tooltip positioner
         *
         * @see [position](#tooltip.position)
         *
         * @type      {Highcharts.TooltipPositionerCallbackFunction}
         * @since     2.2.4
         * @apioption tooltip.positioner
         */
        /**
         * Shows tooltip for all points with the same X value. Splits the
         * tooltip into one label per series, with the header close to the axis.
         * This is recommended over [shared](#tooltip.shared)
         * tooltips for charts with multiple line series, generally making them
         * easier to read. This option takes precedence over `tooltip.shared`.
         *
         * Not supported for [polar](#chart.polar) and [inverted](#chart.inverted) charts.
         *
         * @productdesc {highstock} In Highcharts Stock, tooltips are split
         * by default since v6.0.0. Stock charts typically contain
         * multi-dimension points and multiple panes, making split tooltips
         * the preferred layout over
         * the previous `shared` tooltip.
         *
         * @sample highcharts/tooltip/split/
         *         Split tooltip
         * @sample {highcharts|highstock} highcharts/tooltip/formatter-split/
         *         Split tooltip and custom formatter callback
         *
         * @type      {boolean}
         * @default   {highcharts} false
         * @default   {highstock} true
         * @since     5.0.0
         * @product   highcharts highstock
         * @apioption tooltip.split
         */
        /**
         * How many decimals to show in each series' y value. This is
         * overridable in each series' tooltip options object. The default is to
         * preserve all decimals.
         *
         * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
         *         Set decimals, prefix and suffix for the value
         * @sample {highmaps} maps/tooltip/valuedecimals/
         *         Set decimals, prefix and suffix for the value
         *
         * @type      {number|undefined}
         * @since     2.2
         * @apioption tooltip.valueDecimals
         */
        /**
         * A string to prepend to each series' y value. Overridable in each
         * series' tooltip options object.
         *
         * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
         *         Set decimals, prefix and suffix for the value
         * @sample {highmaps} maps/tooltip/valuedecimals/
         *         Set decimals, prefix and suffix for the value
         *
         * @type      {string}
         * @since     2.2
         * @apioption tooltip.valuePrefix
         */
        /**
         * A string to append to each series' y value. Overridable in each
         * series' tooltip options object.
         *
         * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
         *         Set decimals, prefix and suffix for the value
         * @sample {highmaps} maps/tooltip/valuedecimals/
         *         Set decimals, prefix and suffix for the value
         *
         * @type      {string}
         * @since     2.2
         * @apioption tooltip.valueSuffix
         */
        /**
         * The format for the date in the tooltip header if the X axis is a
         * datetime axis. The default is a best guess based on the smallest
         * distance between points in the chart.
         *
         * @sample {highcharts} highcharts/tooltip/xdateformat/
         *         A different format
         *
         * @type      {string|Highcharts.DateTimeFormatOptions}
         * @product   highcharts highstock gantt
         * @apioption tooltip.xDateFormat
         */
        /**
         * How many decimals to show for the `point.change`
         * or the `point.cumulativeSum` value when the `series.compare`
         * or the `series.cumulative` option is set.
         * This is overridable in each series' tooltip options object.
         *
         * @type      {number}
         * @default   2
         * @since     1.0.1
         * @product   highstock
         * @apioption tooltip.changeDecimals
         */
        /**
         * Enable or disable the tooltip.
         *
         * @sample {highcharts} highcharts/tooltip/enabled/
         *         Disabled
         * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
         *         Disable tooltip and show values on chart instead
         */
        enabled: true,
        /**
         * Enable or disable animation of the tooltip.
         *
         * @type       {boolean|Partial<Highcharts.AnimationOptionsObject>}
         * @since      2.3.0
         */
        animation: {
            duration: 300,
            // EaseOutCirc
            easing: (x) => Math.sqrt(1 - Math.pow(x - 1, 2))
        },
        /**
         * The radius of the rounded border corners.
         *
         * @sample {highcharts} highcharts/tooltip/bordercolor-default/
         *         Default border radius
         * @sample {highcharts} highcharts/tooltip/borderradius-0/
         *         Square borders
         * @sample {highmaps} maps/tooltip/background-border/
         *         Background and border demo
         */
        borderRadius: 3,
        /**
         * For series on datetime axes, the date format in the tooltip's
         * header will by default be guessed based on the closest data points.
         * This member gives the default string representations used for
         * each unit. For an overview of the string or object configuration, see
         * [dateFormat](/class-reference/Highcharts.Time#dateFormat).
         *
         * @see [xAxis.dateTimeLabelFormats](#xAxis.dateTimeLabelFormats)
         *
         * @type    {Highcharts.Dictionary<string|Highcharts.DateTimeFormatOptions>}
         * @product highcharts highstock gantt
         */
        dateTimeLabelFormats: {
            millisecond: '%[AebHMSL]',
            second: '%[AebHMS]',
            minute: '%[AebHM]',
            hour: '%[AebHM]',
            day: '%[AebY]',
            week: '%v %[AebY]',
            month: '%[BY]',
            year: '%Y'
        },
        /**
         * A string to append to the tooltip format.
         *
         * @sample {highcharts} highcharts/tooltip/footerformat/
         *         A table for value alignment
         * @sample {highmaps} maps/tooltip/format/
         *         Format demo
         *
         * @since 2.2
         */
        footerFormat: '',
        /**
         * The name of a symbol to use for the border around the tooltip
         * header. Applies only when [tooltip.split](#tooltip.split) is
         * enabled.
         *
         * Custom callbacks for symbol path generation can also be added to
         * `Highcharts.SVGRenderer.prototype.symbols` the same way as for
         * [series.marker.symbol](plotOptions.line.marker.symbol).
         *
         * @see [tooltip.shape](#tooltip.shape)
         *
         * @sample {highstock} stock/tooltip/split-positioner/
         *         Different shapes for header and split boxes
         *
         * @type       {Highcharts.TooltipShapeValue}
         * @validvalue ["callout", "rect"]
         * @since      7.0
         */
        headerShape: 'callout',
        /**
         * The number of milliseconds to wait until the tooltip is hidden when
         * mouse out from a point or chart.
         *
         * @since 3.0
         */
        hideDelay: 500,
        /**
         * The number of milliseconds to wait until the tooltip is shown when
         * mouse over a point. Works on initial hover.
         *
         * @sample {highcharts|highstock} highcharts/tooltip/showdelay/
         *         Show tooltip after 2 seconds
         *
         * @since 12.6.0
         */
        showDelay: 0,
        /**
         * Padding inside the tooltip, in pixels.
         *
         * @since 5.0.0
         */
        padding: 8,
        /**
         * Positioning options for fixed tooltip, taking effect only when
         * [tooltip.fixed](#tooltip.fixed) is `true`.
         *
         * @sample {highcharts} highcharts/tooltip/fixed/
         *         Fixed tooltip and position options
         * @sample {highstock} stock/tooltip/fixed/
         *         Stock chart with fixed tooltip
         * @sample {highmaps} maps/tooltip/fixed/
         *         Map with fixed tooltip
         *
         * @since 12.2.0
         */
        position: {
            /**
             * The horizontal alignment of the fixed tooltip.
             *
             * @sample highcharts/tooltip/fixed/
             *         Fixed tooltip
             * @sample {highstock} stock/tooltip/fixed/
             *         Stock chart with fixed tooltip
             *
             * @type {Highcharts.AlignValue}
             * @default left
             * @apioption tooltip.position.align
             */
            /**
             * The vertical alignment of the fixed tooltip.
             *
             * @sample highcharts/tooltip/fixed/
             *         Fixed tooltip
             * @sample {highstock} stock/tooltip/fixed/
             *         Stock chart with fixed tooltip
             *
             * @type {Highcharts.VerticalAlignValue}
             * @default top
             * @apioption tooltip.position.verticalAlign
             */
            /**
             * What the fixed tooltip alignment should be relative to.
             *
             * The default, `pane`, means that it is aligned within the plot
             * area for that given series. If the tooltip is split (as default
             * in Stock charts), each partial tooltip is aligned within the
             * series' pane.
             *
             * @sample highcharts/tooltip/fixed/
             *         Fixed tooltip
             * @sample {highstock} stock/tooltip/fixed/
             *         Stock chart with fixed tooltip
             *
             * @type {string}
             * @default pane
             * @validvalue ["pane", "chart", "plotBox", "spacingBox"]
             * @apioption tooltip.position.relativeTo
             */
            /**
             * X pixel offset from the given position. Can be used to shy away
             * from axis lines, grid lines etc to avoid the tooltip overlapping
             * other elements.
             *
             * @sample highcharts/tooltip/fixed/
             *         Fixed tooltip
             * @sample {highstock} stock/tooltip/fixed/
             *         Stock chart with fixed tooltip
             */
            x: 0,
            /**
             * Y pixel offset from the given position. Can be used to shy away
             * from axis lines, grid lines etc to avoid the tooltip overlapping
             * other elements.
             *
             * @sample highcharts/tooltip/fixed/
             *         Fixed tooltip
             * @sample {highstock} stock/tooltip/fixed/
             *         Stock chart with fixed tooltip
             */
            y: 3
        },
        /**
         * The name of a symbol to use for the border around the tooltip. Can
         * be one of: `"callout"`, `"circle"` or `"rect"`. When
         * [tooltip.split](#tooltip.split)
         * option is enabled, shape is applied to all boxes except header, which
         * is controlled by
         * [tooltip.headerShape](#tooltip.headerShape).
         *
         * Custom callbacks for symbol path generation can also be added to
         * `Highcharts.SVGRenderer.prototype.symbols` the same way as for
         * [series.marker.symbol](plotOptions.line.marker.symbol).
         *
         * Defaults to `callout` for floating tooltip, `rect` for
         * [fixed](#tooltip.fixed) tooltip.
         *
         * @type  {Highcharts.TooltipShapeValue}
         * @since 4.0
         * @default undefined
         * @apioption tooltip.shape
         */
        /**
         * Shows information in the tooltip for all points with the same X
         * value. When the tooltip is shared, the entire plot area will capture
         * mouse movement or touch events. Tooltip texts for series types with
         * ordered data (not pie, scatter, flags etc) will be shown in a single
         * bubble. This is recommended for single series charts and for
         * tablet/mobile optimized charts.
         *
         * See also [tooltip.split](#tooltip.split), that is better suited for
         * charts with many series, especially line-type series. The
         * `tooltip.split` option takes precedence over `tooltip.shared`.
         *
         * @sample {highcharts} highcharts/tooltip/shared-false/
         *         False by default
         * @sample {highcharts} highcharts/tooltip/shared-true/
         *         True
         * @sample {highcharts} highcharts/tooltip/shared-x-crosshair/
         *         True with x axis crosshair
         * @sample {highcharts} highcharts/tooltip/shared-true-mixed-types/
         *         True with mixed series types
         *
         * @since   2.1
         * @product highcharts highstock
         */
        shared: false,
        /**
         * Proximity snap for graphs or single points. It defaults to 10 for
         * mouse-powered devices and 25 for touch devices.
         *
         * Note that in most cases the whole plot area captures the mouse
         * movement, and in these cases `tooltip.snap` doesn't make sense. This
         * applies when [stickyTracking](#plotOptions.series.stickyTracking)
         * is `true` (default) and when the tooltip is [shared](#tooltip.shared)
         * or [split](#tooltip.split).
         *
         * @sample {highcharts} highcharts/tooltip/bordercolor-default/
         *         10 px by default
         * @sample {highcharts} highcharts/tooltip/snap-50/
         *         50 px on graph
         *
         * @type    {number}
         * @default 10/25
         * @since   1.2.0
         * @product highcharts highstock
         */
        snap: isTouchDevice ? 25 : 10,
        /**
         * The HTML of the tooltip header line. The context is the
         * [Point class](https://api.highcharts.com/class-reference/Highcharts.Point).
         * Variables are enclosed in curly brackets. Examples of common
         * variables to include are `x`, `y`, `series.name` and `series.color`
         * and other properties on the same form. The `point.key` variable
         * contains the category name, x value or datetime string depending on
         * the type of axis. For datetime axes, the `point.key` date format can
         * be set using `tooltip.xDateFormat`.
         * In v12+, locale-aware date names follow the browser's casing and can
         * be lower-case, so use the `ucfirst` helper (for example
         * `{ucfirst point.key}`) if you want a capitalized header.
         *
         * @sample {highcharts} highcharts/tooltip/footerformat/
         *         An HTML table in the tooltip
         * @sample {highstock} highcharts/tooltip/footerformat/
         *         An HTML table in the tooltip
         * @sample {highmaps} maps/tooltip/format/
         *         Format demo
         *
         * @type      {string}
         * @apioption tooltip.headerFormat
         */
        headerFormat: '<span style="font-size: 0.8em">{ucfirst point.key}</span><br/>',
        /**
         * The HTML of the null point's line in the tooltip. Works analogously
         * to [pointFormat](#tooltip.pointFormat).
         *
         * @sample {highcharts} highcharts/series/null-interaction
         *         Line chart with null interaction
         * @sample {highcharts} highcharts/plotoptions/series-nullformat
         *         Heatmap with null interaction
         *
         * @type      {string}
         * @apioption tooltip.nullFormat
         */
        /**
         * The HTML of the point's line in the tooltip. The context is the
         * [Point class](https://api.highcharts.com/class-reference/Highcharts.Point).
         * Variables are enclosed in curly brackets. Examples of common
         * variables to include are `x`, `y`, `series.name` and `series.color`
         * and other properties on the same form. Furthermore, `y` can be
         * extended by the `tooltip.valuePrefix` and `tooltip.valueSuffix`
         * variables. This can also be overridden for each series, which makes
         * it a good hook for displaying units.
         *
         * In styled mode, the dot is colored by a class name rather than the
         * point color.
         *
         * @sample {highcharts} highcharts/tooltip/pointformat/
         *         A different point format with value suffix
         * @sample {highcharts|highstock} highcharts/tooltip/pointformat-extra-information/
         *         Show extra information about points in the tooltip
         * @sample {highmaps} maps/tooltip/format/
         *         Format demo
         *
         * @type       {string}
         * @since      2.2
         * @apioption  tooltip.pointFormat
         */
        pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.y}</b><br/>',
        /**
         * The background color or gradient for the tooltip.
         *
         * In styled mode, the stroke width is set in the
         * `.highcharts-tooltip-box` class.
         *
         * @sample {highcharts} highcharts/tooltip/backgroundcolor-solid/
         *         Yellowish background
         * @sample {highcharts} highcharts/tooltip/backgroundcolor-gradient/
         *         Gradient
         * @sample {highcharts} highcharts/css/tooltip-border-background/
         *         Tooltip in styled mode
         * @sample {highstock} stock/tooltip/general/
         *         Custom tooltip
         * @sample {highstock} highcharts/css/tooltip-border-background/
         *         Tooltip in styled mode
         * @sample {highmaps} maps/tooltip/background-border/
         *         Background and border demo
         * @sample {highmaps} highcharts/css/tooltip-border-background/
         *         Tooltip in styled mode
         *
         * @type {Highcharts.ColorType}
         */
        backgroundColor: "#ffffff" /* Palette.backgroundColor */,
        /**
         * The pixel width of the tooltip border. Defaults to 0 for single
         * tooltips and fixed tooltips, otherwise 1 for split tooltips.
         *
         * In styled mode, the stroke width is set in the
         * `.highcharts-tooltip-box` class.
         *
         * @sample {highcharts} highcharts/tooltip/bordercolor-default/
         *         2 pixels
         * @sample {highcharts} highcharts/tooltip/borderwidth/
         *         No border (shadow only)
         * @sample {highcharts} highcharts/css/tooltip-border-background/
         *         Tooltip in styled mode
         * @sample {highstock} stock/tooltip/general/
         *         Custom tooltip
         * @sample {highstock} highcharts/css/tooltip-border-background/
         *         Tooltip in styled mode
         * @sample {highmaps} maps/tooltip/background-border/
         *         Background and border demo
         * @sample {highmaps} highcharts/css/tooltip-border-background/
         *         Tooltip in styled mode
         *
         * @type {number}
         */
        borderWidth: void 0,
        /**
         * Whether to apply a drop shadow to the tooltip. Defaults to true,
         * unless the tooltip is [fixed](#tooltip.fixed).
         *
         * @sample {highcharts} highcharts/tooltip/bordercolor-default/
         *         True by default
         * @sample {highcharts} highcharts/tooltip/shadow/
         *         False
         * @sample {highmaps} maps/tooltip/positioner/
         *         Fixed tooltip position, border and shadow disabled
         *
         * @type {boolean|Highcharts.ShadowOptionsObject}
         * @default undefined
         * @apioption tooltip.shadow
         */
        /**
         * Prevents the tooltip from switching or closing, when touched or
         * pointed.
         *
         * @sample highcharts/tooltip/stickoncontact/
         *         Tooltip sticks on pointer contact
         * @sample highcharts/tooltip/stickoncontact-anchor-link/
         *         Tooltip with clickable links
         *
         * @type      {boolean}
         * @since     8.0.1
         */
        stickOnContact: false,
        /**
         * CSS styles for the tooltip. The tooltip can also be styled through
         * the CSS class `.highcharts-tooltip`.
         *
         * Note that the default `pointerEvents` style makes the tooltip ignore
         * mouse events, so in order to use clickable tooltips, this value must
         * be set to `auto`.
         *
         * @sample {highcharts} highcharts/tooltip/style/
         *         Greater padding, bold text
         *
         * @type {Highcharts.CSSObject}
         */
        style: {
            /** @type {Highcharts.ColorType} */
            color: "#333333" /* Palette.neutralColor80 */,
            cursor: 'default',
            /** @type {number|string} */
            fontSize: '0.8em'
        },
        /**
         * Use HTML to render the contents of the tooltip instead of SVG. Using
         * HTML allows advanced formatting like tables and images in the
         * tooltip. It is also recommended for rtl languages as it works around
         * rtl bugs in early Firefox.
         *
         * @sample {highcharts|highstock} highcharts/tooltip/footerformat/
         *         A table for value alignment
         * @sample {highcharts|highstock} highcharts/tooltip/fullhtml/
         *         Full HTML tooltip
         * @sample {highmaps} maps/tooltip/usehtml/
         *         Pure HTML tooltip
         *
         * @since     2.2
         */
        useHTML: false
    },
    /**
     * Highcharts by default puts a credits label in the lower right corner
     * of the chart. This can be changed using these options.
     */
    credits: {
        /**
         * Credits for map source to be concatenated with conventional credit
         * text. By default this is a format string that collects copyright
         * information from the map if available.
         *
         * @see [mapTextFull](#credits.mapTextFull)
         * @see [text](#credits.text)
         *
         * @type      {string}
         * @default   \u00a9 <a href="{geojson.copyrightUrl}">{geojson.copyrightShort}</a>
         * @since     4.2.2
         * @product   highmaps
         * @apioption credits.mapText
         */
        /**
         * Detailed credits for map source to be displayed on hover of credits
         * text. By default this is a format string that collects copyright
         * information from the map if available.
         *
         * @see [mapText](#credits.mapText)
         * @see [text](#credits.text)
         *
         * @type      {string}
         * @default   {geojson.copyright}
         * @since     4.2.2
         * @product   highmaps
         * @apioption credits.mapTextFull
         */
        /**
         * Events for the credits label.
         *
         * @type     {object}
         * @since    12.6.0
         * @apioption credits.events
         */
        /**
         * Callback function to handle click events on the credits label.
         * The callback can call `event.preventDefault()` to prevent the
         * default navigation behavior. Alternatively, you can add a general
         * event handler using `Highcharts.addEvent(Chart, 'creditsClick',
         * callback)` instead of providing it in the options tree.
         *
         * @sample {highcharts} highcharts/credits/events-click/
         *         Custom click handler
         *
         * @param {Event} event
         *        The click event object.
         *
         * @type  {Function}
         * @since 12.6.0
         * @apioption credits.events.click
         */
        /**
         * Whether to show the credits text.
         *
         * @sample {highcharts} highcharts/credits/enabled-false/
         *         Credits disabled
         * @sample {highstock} stock/credits/enabled/
         *         Credits disabled
         * @sample {highmaps} maps/credits/enabled-false/
         *         Credits disabled
         */
        enabled: true,
        /**
         * The URL for the credits label.
         *
         * @sample {highcharts} highcharts/credits/href/
         *         Custom URL and text
         * @sample {highmaps} maps/credits/customized/
         *         Custom URL and text
         */
        href: 'https://www.highcharts.com?credits',
        /**
         * Position configuration for the credits label.
         *
         * @sample {highcharts} highcharts/credits/position-left/
         *         Left aligned
         * @sample {highmaps} maps/credits/customized/
         *         Left aligned
         *
         * @type  {Highcharts.AlignObject}
         * @since 2.1
         */
        position: {
            align: 'right',
            verticalAlign: 'bottom',
            x: -10,
            y: -5
        },
        /**
         * CSS styles for the credits label.
         *
         * @see In styled mode, credits styles can be set with the
         *      `.highcharts-credits` class.
         *
         * @type {Highcharts.CSSObject}
         */
        style: {
            cursor: 'pointer',
            /**
             * @type {Highcharts.ColorType}
             */
            color: "#999999" /* Palette.neutralColor40 */,
            /**
             * @type {number|string}
             */
            fontSize: '0.6em'
        },
        /**
         * The text for the credits label.
         *
         * @productdesc {highmaps}
         * If a map is loaded as GeoJSON, the text defaults to
         * `Highcharts @ {map-credits}`. Otherwise, it defaults to
         * `Highcharts.com`.
         *
         * @sample {highcharts} highcharts/credits/href/
         *         Custom URL and text
         * @sample {highmaps} maps/credits/customized/
         *         Custom URL and text
         */
        text: 'Highcharts.com'
    }
};
const defaultTime = new Core_Time(Defaults_defaultOptions.time, Defaults_defaultOptions.lang);
/**
 * Get the updated default options. Until 3.0.7, merely exposing defaultOptions
 * for outside modules wasn't enough because the setOptions method created a new
 * object.
 *
 * @function Highcharts.getOptions
 *
 * @return {Highcharts.Options}
 * Default options.
 */
function getOptions() {
    return Defaults_defaultOptions;
}
/**
 * Merge the default options with custom options and return the new options
 * structure. Commonly used for defining reusable templates.
 *
 * @sample highcharts/members/setoptions Applying a global theme
 *
 * @function Highcharts.setOptions
 *
 * @param {Highcharts.Options} options
 * The new custom chart options.
 *
 * @return {Highcharts.Options}
 * Updated options.
 */
function Defaults_setOptions(options) {
    fireEvent(Core_Globals, 'setOptions', { options });
    // Copy in the default options
    merge(true, Defaults_defaultOptions, options);
    // Update the time object
    if (options.time) {
        defaultTime.update(Defaults_defaultOptions.time);
    }
    if (options.lang && 'locale' in options.lang) {
        defaultTime.update({
            locale: options.lang.locale
        });
    }
    if (options.lang?.chartTitle) {
        Defaults_defaultOptions.title = {
            ...Defaults_defaultOptions.title,
            text: options.lang.chartTitle
        };
    }
    return Defaults_defaultOptions;
}
/* *
 *
 *  Default Export
 *
 * */
const DefaultOptions = {
    defaultOptions: Defaults_defaultOptions,
    defaultTime,
    getOptions,
    setOptions: Defaults_setOptions
};
/* harmony default export */ const Core_Defaults = (DefaultOptions);
/* *
 *
 *  API Declarations
 *
 * */
/**
 * @typedef {"plotBox"|"spacingBox"} Highcharts.ButtonRelativeToValue
 */
/**
 * Gets fired when a series is added to the chart after load time, using the
 * `addSeries` method. Returning `false` prevents the series from being added.
 *
 * @callback Highcharts.ChartAddSeriesCallbackFunction
 *
 * @param {Highcharts.Chart} this
 *        The chart on which the event occurred.
 *
 * @param {Highcharts.ChartAddSeriesEventObject} event
 *        The event that occurred.
 */
/**
 * Contains common event information. Through the `options` property you can
 * access the series options that were passed to the `addSeries` method.
 *
 * @interface Highcharts.ChartAddSeriesEventObject
 */ /**
* The series options that were passed to the `addSeries` method.
* @name Highcharts.ChartAddSeriesEventObject#options
* @type {Highcharts.SeriesOptionsType}
*/ /**
* Prevents the default behavior of the event.
* @name Highcharts.ChartAddSeriesEventObject#preventDefault
* @type {Function}
*/ /**
* The event target.
* @name Highcharts.ChartAddSeriesEventObject#target
* @type {Highcharts.Chart}
*/ /**
* The event type.
* @name Highcharts.ChartAddSeriesEventObject#type
* @type {"addSeries"}
*/
/**
 * Gets fired when clicking on the plot background.
 *
 * @callback Highcharts.ChartClickCallbackFunction
 *
 * @param {Highcharts.Chart} this
 *        The chart on which the event occurred.
 *
 * @param {Highcharts.PointerEventObject} event
 *        The event that occurred.
 */
/**
 * Contains an axes of the clicked spot.
 *
 * @interface Highcharts.ChartClickEventAxisObject
 */ /**
* Axis at the clicked spot.
* @name Highcharts.ChartClickEventAxisObject#axis
* @type {Highcharts.Axis}
*/ /**
* Axis value at the clicked spot.
* @name Highcharts.ChartClickEventAxisObject#value
* @type {number}
*/
/**
 * Contains information about the clicked spot on the chart. Remember the unit
 * of a datetime axis is milliseconds since 1970-01-01 00:00:00.
 *
 * @interface Highcharts.ChartClickEventObject
 * @extends Highcharts.PointerEventObject
 */ /**
* Information about the x-axis on the clicked spot.
* @name Highcharts.ChartClickEventObject#xAxis
* @type {Array<Highcharts.ChartClickEventAxisObject>}
*/ /**
* Information about the y-axis on the clicked spot.
* @name Highcharts.ChartClickEventObject#yAxis
* @type {Array<Highcharts.ChartClickEventAxisObject>}
*/ /**
* Information about the z-axis on the clicked spot.
* @name Highcharts.ChartClickEventObject#zAxis
* @type {Array<Highcharts.ChartClickEventAxisObject>|undefined}
*/
/**
 * Gets fired when the chart is finished loading.
 *
 * @callback Highcharts.ChartLoadCallbackFunction
 *
 * @param {Highcharts.Chart} this
 *        The chart on which the event occurred.
 *
 * @param {global.Event} event
 *        The event that occurred.
 */
/**
 * Fires when the chart is redrawn, either after a call to `chart.redraw()` or
 * after an axis, series or point is modified with the `redraw` option set to
 * `true`.
 *
 * @callback Highcharts.ChartRedrawCallbackFunction
 *
 * @param {Highcharts.Chart} this
 *        The chart on which the event occurred.
 *
 * @param {global.Event} event
 *        The event that occurred.
 */
/**
 * Gets fired after initial load of the chart (directly after the `load` event),
 * and after each redraw (directly after the `redraw` event).
 *
 * @callback Highcharts.ChartRenderCallbackFunction
 *
 * @param {Highcharts.Chart} this
 *        The chart on which the event occurred.
 *
 * @param {global.Event} event
 *        The event that occurred.
 */
/**
 * Gets fired when an area of the chart has been selected. The default action
 * for the selection event is to zoom the chart to the selected area. It can be
 * prevented by calling `event.preventDefault()` or return false.
 *
 * @callback Highcharts.ChartSelectionCallbackFunction
 *
 * @param {Highcharts.Chart} this
 *        The chart on which the event occurred.
 *
 * @param {Highcharts.SelectEventObject} event
 *        Event information
 *
 * @return {boolean|undefined}
 *         Return false to prevent the default action, usually zoom.
 */
(''); // Detach doclets above

;// ./code/dashboards/es-modules/Core/Templating.js
/* *
 *
 *  (c) 2010-2026 Highsoft AS
 *  Author: Torstein Hønsi
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 * */


const { defaultOptions: Templating_defaultOptions, defaultTime: Templating_defaultTime } = Core_Defaults;

const { pageLang: Templating_pageLang } = Core_Globals;

/** @internal */
const helpers = {
    // Built-in helpers
    add: (a, b) => a + b,
    divide: (a, b) => (b !== 0 ? a / b : ''),
    // eslint-disable-next-line eqeqeq
    eq: (a, b) => a == b,
    each: function (arr) {
        const match = arguments[arguments.length - 1];
        return isArray(arr) ?
            arr.map((item, i) => format(match.body, extend(isObject(item) ? item : { '@this': item }, {
                '@index': i,
                '@first': i === 0,
                '@last': i === arr.length - 1
            }))).join('') :
            false;
    },
    ge: (a, b) => a >= b,
    gt: (a, b) => a > b,
    'if': (condition) => !!condition,
    le: (a, b) => a <= b,
    lt: (a, b) => a < b,
    multiply: (a, b) => a * b,
    // eslint-disable-next-line eqeqeq
    ne: (a, b) => a != b,
    subtract: (a, b) => a - b,
    ucfirst: ucfirst,
    unless: (condition) => !condition
};
const numberFormatCache = {};
/* *
 *
 *  Functions
 *
 * */
/**
 * Internal convenience function.
 * @internal
 */
const isQuotedString = (str) => /^["'].+["']$/.test(str);
/**
 * Formats a JavaScript date timestamp (milliseconds since Jan 1st 1970) into a
 * human readable date string. The format is a subset of the formats for PHP's
 * [strftime](https://www.php.net/manual/en/function.strftime.php) function.
 * Additional formats can be given in the {@link Highcharts.dateFormats} hook.
 *
 * Since v6.0.5, all internal dates are formatted through the
 * {@link Highcharts.Chart#time} instance to respect chart-level time settings.
 * The `Highcharts.dateFormat` function only reflects global time settings set
 * with `setOptions`.
 *
 * Supported format keys:
 * - `%a`: Short weekday, like 'Mon'
 * - `%A`: Long weekday, like 'Monday'
 * - `%d`: Two digit day of the month, 01 to 31
 * - `%e`: Day of the month, 1 through 31
 * - `%w`: Day of the week, 0 through 6
 * - `%b`: Short month, like 'Jan'
 * - `%B`: Long month, like 'January'
 * - `%m`: Two digit month number, 01 through 12
 * - `%y`: Two digits year, like 09 for 2009
 * - `%Y`: Four digits year, like 2009
 * - `%H`: Two digits hours in 24h format, 00 through 23
 * - `%k`: Hours in 24h format, 0 through 23
 * - `%I`: Two digits hours in 12h format, 00 through 11
 * - `%l`: Hours in 12h format, 1 through 12
 * - `%M`: Two digits minutes, 00 through 59
 * - `%p`: Upper case AM or PM
 * - `%P`: Lower case AM or PM
 * - `%S`: Two digits seconds, 00 through 59
 * - `%L`: Milliseconds (naming from Ruby)
 *
 * @function Highcharts.dateFormat
 *
 * @param {string} format
 *        The desired format where various time representations are prefixed
 *        with `%`.
 *
 * @param {number} timestamp
 *        The JavaScript timestamp.
 *
 * @param {boolean} [upperCaseFirst=false]
 *        Upper case first letter in the return.
 *
 * @return {string}
 *         The formatted date.
 */
function dateFormat(format, timestamp, upperCaseFirst) {
    return Templating_defaultTime.dateFormat(format, timestamp, upperCaseFirst);
}
/**
 * Format a string according to a subset of the rules of Python's String.format
 * method.
 *
 * @example
 * let s = Highcharts.format(
 *     'The {color} fox was {len:.2f} feet long',
 *     { color: 'red', len: Math.PI }
 * );
 * // => The red fox was 3.14 feet long
 *
 * @function Highcharts.format
 *
 * @param {string} str
 *        The string to format.
 *
 * @param {Record<string, *>} ctx
 *        The context, a collection of key-value pairs where each key is
 *        replaced by its value.
 *
 * @param {Highcharts.Chart} [owner]
 *        A `Chart` or `Grid` instance used to get numberFormatter and time.
 *
 * @return {string}
 *         The formatted string.
 */
function format(str = '', ctx, owner) {
    // eslint-disable-next-line prefer-regex-literals
    const regex = new RegExp('\\{([\\p{L}\\p{M}\\d:\\.,;\\-\\/<>\\[\\]%_@+"\'’= #\\(\\)]+)\\}', 'gu'), 
    // The sub expression regex is the same as the top expression regex,
    // but except parens and block helpers (#), and surrounded by parens
    // instead of curly brackets.
    // eslint-disable-next-line prefer-regex-literals
    subRegex = new RegExp('\\(([\\p{L}\\p{M}\\d:\\.,;\\-\\/<>\\[\\]%_@+"\'= ]+)\\)', 'gu'), matches = [], floatRegex = /f$/, decRegex = /\.(\d)/, lang = owner?.options?.lang || Templating_defaultOptions.lang, time = owner?.time || Templating_defaultTime, numberFormatter = owner?.numberFormatter || numberFormat.bind(owner);
    /*
     * Get a literal or variable value inside a template expression. May be
     * extended with other types like string or null if needed, but keep it
     * small for now.
     */
    const resolveProperty = (key = '') => {
        let n;
        // Literals
        if (key === 'true') {
            return true;
        }
        if (key === 'false') {
            return false;
        }
        if ((n = Number(key)).toString() === key) {
            return n;
        }
        if (isQuotedString(key)) {
            return key.slice(1, -1);
        }
        // Variables and constants
        return getNestedProperty(key, ctx);
    };
    let match, currentMatch, depth = 0, hasSub;
    // Parse and create tree
    while ((match = regex.exec(str)) !== null) {
        // When a sub expression is found, it is evaluated first, and the
        // results recursively evaluated until no subexpression exists.
        const mainMatch = match, subMatch = subRegex.exec(match[1]);
        if (subMatch) {
            match = subMatch;
            hasSub = true;
        }
        if (!currentMatch?.isBlock) {
            currentMatch = {
                ctx,
                expression: match[1],
                find: match[0],
                isBlock: match[1].charAt(0) === '#',
                start: match.index,
                startInner: match.index + match[0].length,
                length: match[0].length
            };
        }
        // Identify helpers
        const fn = (currentMatch.isBlock ? mainMatch : match)[1].split(' ')[0].replace('#', '');
        if (helpers[fn]) {
            // Block helper, only 0 level is handled
            if (currentMatch.isBlock && fn === currentMatch.fn) {
                depth++;
            }
            if (!currentMatch.fn) {
                currentMatch.fn = fn;
            }
        }
        // Closing a block helper
        const startingElseSection = match[1] === 'else';
        if (currentMatch.isBlock &&
            currentMatch.fn && (match[1] === `/${currentMatch.fn}` ||
            startingElseSection)) {
            if (!depth) { // === 0
                const start = currentMatch.startInner, body = str.substr(start, match.index - start);
                // Either closing without an else section, or when encountering
                // an else section
                if (currentMatch.body === void 0) {
                    currentMatch.body = body;
                    currentMatch.startInner = match.index + match[0].length;
                    // The body exists already, so this is the else section
                }
                else {
                    currentMatch.elseBody = body;
                }
                currentMatch.find += body + match[0];
                if (!startingElseSection) {
                    matches.push(currentMatch);
                    currentMatch = void 0;
                }
            }
            else if (!startingElseSection) {
                depth--;
            }
            // Common expression
        }
        else if (!currentMatch.isBlock) {
            matches.push(currentMatch);
        }
        // Evaluate sub-matches one by one to prevent orphaned block closers
        if (subMatch && !currentMatch?.isBlock) {
            break;
        }
    }
    // Execute
    matches.forEach((match) => {
        const { body, elseBody, expression, fn } = match;
        let replacement, i;
        // Helper function
        if (fn) {
            // Pass the helpers the amount of arguments defined by the function,
            // then the match as the last argument.
            const args = [match], parts = [], len = expression.length;
            let start = 0, startChar;
            for (i = 0; i <= len; i++) {
                const char = expression.charAt(i);
                // Start of string
                if (!startChar && (char === '"' || char === '\'')) {
                    startChar = char;
                    // End of string
                }
                else if (startChar === char) {
                    startChar = '';
                }
                if (!startChar &&
                    (char === ' ' || i === len)) {
                    parts.push(expression.substr(start, i - start));
                    start = i + 1;
                }
            }
            i = helpers[fn].length;
            while (i--) {
                args.unshift(resolveProperty(parts[i + 1]));
            }
            replacement = helpers[fn].apply(ctx, args);
            // Block helpers may return true or false. They may also return a
            // string, like the `each` helper.
            if (match.isBlock && typeof replacement === 'boolean') {
                replacement = format(replacement ? body : elseBody, ctx, owner);
            }
            // Simple variable replacement
        }
        else {
            const valueAndFormat = isQuotedString(expression) ?
                [expression] : expression.split(':');
            replacement = resolveProperty(valueAndFormat.shift() || '');
            // Format the replacement
            if (valueAndFormat.length && typeof replacement === 'number') {
                const segment = valueAndFormat.join(':');
                if (floatRegex.test(segment)) { // Float
                    const decimals = parseInt((segment.match(decRegex) || ['', '-1'])[1], 10);
                    if (replacement !== null) {
                        replacement = numberFormatter(replacement, decimals, lang.decimalPoint, segment.indexOf(',') > -1 ? lang.thousandsSep : '');
                    }
                }
                else {
                    replacement = time.dateFormat(segment, replacement);
                }
            }
            // Use string literal in order to be preserved in the outer
            // expression
            subRegex.lastIndex = 0;
            if (subRegex.test(match.find) && isString(replacement)) {
                replacement = `"${replacement}"`;
            }
        }
        str = str.replace(match.find, Utilities_pick(replacement, ''));
    });
    return hasSub ? format(str, ctx, owner) : str;
}
/**
 * Format a number and return a string based on input settings.
 *
 * @sample highcharts/members/highcharts-numberformat/
 *         Custom number format
 *
 * @function Highcharts.numberFormat
 *
 * @param {number} number
 *        The input number to format.
 *
 * @param {number} decimals
 *        The amount of decimals. A value of -1 preserves the amount in the
 *        input number.
 *
 * @param {string} [decimalPoint]
 *        The decimal point, defaults to the one given in the lang options, or
 *        a dot.
 *
 * @param {string} [thousandsSep]
 *        The thousands separator, defaults to the one given in the lang
 *        options, or a space character.
 *
 * @return {string}
 *         The formatted number.
 */
function numberFormat(number, decimals, decimalPoint, thousandsSep) {
    number = +number || 0;
    decimals = +decimals;
    let ret, fractionDigits, [mantissa, exp] = number.toString().split('e').map(Number);
    const lang = this?.options?.lang || Templating_defaultOptions.lang, origDec = (number.toString().split('.')[1] || '').split('e')[0].length, firstDecimals = decimals, options = {};
    decimalPoint ?? (decimalPoint = lang.decimalPoint);
    thousandsSep ?? (thousandsSep = lang.thousandsSep);
    if (decimals === -1) {
        // Preserve decimals. Not huge numbers (#3793).
        decimals = Math.min(origDec, 20);
    }
    else if (!Utilities_isNumber(decimals)) {
        decimals = 2;
    }
    else if (decimals && exp < 0) {
        // Expose decimals from exponential notation (#7042)
        fractionDigits = decimals + exp;
        if (fractionDigits >= 0) {
            // Remove too small part of the number while keeping the notation
            mantissa = +mantissa.toExponential(fractionDigits).split('e')[0];
            decimals = fractionDigits;
        }
        else {
            // `fractionDigits < 0`
            mantissa = Math.floor(mantissa);
            if (decimals < 20) {
                // Use number instead of exponential notation (#7405)
                number = +(mantissa * Math.pow(10, exp)).toFixed(decimals);
            }
            else {
                // Or zero
                number = 0;
            }
            exp = 0;
        }
    }
    if (exp) {
        decimals ?? (decimals = 2);
        number = mantissa;
    }
    if (Utilities_isNumber(decimals) && decimals >= 0) {
        options.minimumFractionDigits = decimals;
        options.maximumFractionDigits = decimals;
    }
    if (thousandsSep === '') {
        options.useGrouping = false;
    }
    const hasSeparators = thousandsSep || decimalPoint, locale = hasSeparators ?
        'en' : (this?.locale || lang.locale || Templating_pageLang), cacheKey = JSON.stringify(options) + locale, nf = numberFormatCache[cacheKey] ?? (numberFormatCache[cacheKey] = new Intl.NumberFormat(locale, options));
    ret = nf.format(number);
    // If thousandsSep or decimalPoint are set, fall back to using English
    // format with string replacement for the separators.
    if (hasSeparators) {
        ret = ret
            // Preliminary step to avoid re-swapping (#22402)
            .replace(/([,\.])/g, '_$1')
            .replace(/_\,/g, thousandsSep ?? ',')
            .replace('_.', decimalPoint ?? '.');
    }
    if (
    // Remove signed zero (#20564)
    (!decimals && +ret === 0) ||
        // Small numbers, no decimals (#14023)
        (exp < 0 && !firstDecimals)) {
        ret = '0';
    }
    if (exp && +ret !== 0) {
        ret += 'e' + (exp < 0 ? '' : '+') + exp;
    }
    return ret;
}
/* *
 *
 *  Default Export
 *
 * */
const Templating = {
    dateFormat,
    format,
    helpers,
    numberFormat
};
/* harmony default export */ const Core_Templating = (Templating);
/* *
 * API Declarations
 * */
/**
 * @interface Highcharts.Templating
 *
 * The Highcharts.Templating interface provides a structure for defining
 * helpers. Helpers can be used as conditional blocks or functions within
 * expressions. Highcharts includes several built-in helpers and supports
 * the addition of custom helpers.
 *
 * @see [More information](
 * https://www.highcharts.com/docs/chart-concepts/templating#helpers)
 *
 * @example
 * // Define a custom helper to return the absolute value of a number
 * Highcharts.Templating.helpers.abs = value => Math.abs(value);
 *
 * // Usage in a format string
 * format: 'Absolute value: {abs point.y}'
 *
 * @name Highcharts.Templating#helpers
 * @type {Record<string, Function>}
 */
(''); // Keeps doclets above in file

;// ./code/dashboards/es-modules/Dashboards/Components/KPIComponent/KPIComponent.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *
 * */














const { format: KPIComponent_format } = Core_Templating;
/* *
 *
 *  Class
 *
 * */
/**
 *
 * Class that represents a KPI component.
 *
 */
class KPIComponent extends Components_Component {
    /* *
     *
     *  Constructor
     *
     * */
    /**
     * Creates a KPI component in the cell.
     *
     * @param cell
     * Instance of cell, where component is attached.
     *
     * @param options
     * The options for the component.
     */
    constructor(cell, options, board) {
        options = merge(KPIComponent.defaultOptions, options);
        super(cell, options, board);
        this.options = options;
        this.type = 'KPI';
        this.value = createElement('span', {
            className: `${options.className}-value`
        }, {}, this.contentElement);
        this.subtitle = createElement('span', {
            className: this.getSubtitleClassName()
        }, {}, this.contentElement);
    }
    /* *
     *
     *  Functions
     *
     * */
    /** @internal */
    async load() {
        await super.load();
        this.linkValueToChart();
        return this;
    }
    resize(width, height) {
        super.resize(width, height);
        // Animate
        if (this.chart && this.chart.container) {
            this.chart.reflow();
        }
        return this;
    }
    render() {
        super.render();
        this.updateElements();
        const charter = KPIComponent.charter?.Chart;
        if (charter &&
            this.options.chartOptions &&
            !this.chart) {
            if (!this.chartContainer) {
                this.chartContainer = createElement('div', {
                    className: `${this.options.className}-chart-container`
                }, {
                    // Fix inner height, when using flex box
                    padding: '0.1px'
                }, this.contentElement);
            }
            this.chart = charter.chart(this.chartContainer, merge(KPIComponent.defaultChartOptions, this.options.chartOptions));
        }
        else if (this.chart &&
            !this.options.chartOptions &&
            'chartOptions' in this.options) {
            this.chart.destroy();
            this.chart = void 0;
        }
        this.sync.start();
        this.emit({ type: 'afterRender' });
        return this;
    }
    /**
     * Handles updating via options.
     *
     * @param options
     * The options to apply.
     */
    async update(options, shouldRerender = true) {
        await super.update(options);
        if (options.chartOptions && this.chart) {
            this.chart.update(options.chartOptions);
        }
        shouldRerender && this.render();
    }
    /**
     * @internal
     */
    onTableChanged() {
        this.setValue();
    }
    /**
     * Destroys the highcharts component.
     */
    destroy() {
        // Cleanup references in the global Highcharts scope
        this.chart?.destroy();
        super.destroy();
    }
    /**
     * Gets a proper value, according to the provided formula option.
     *
     * @returns
     * The formula value. Can be a number internally, or a string from the
     * callback function.
     *
     * @internal
     */
    getFormulaValue() {
        const formula = this.options.formula;
        const table = this.getDataTable();
        const column = table?.getColumn(this.options.columnId);
        if (!column || !formula) {
            return;
        }
        if (isFunction(formula)) {
            return formula.call(this, column);
        }
        let filteredColumn = Array.isArray(column) ?
            column.slice().filter(defined) : Array.from(column);
        // Filter NaN values and empty strings since the formula functions don't
        // handle it internally.
        if (formula === 'MIN' || formula === 'MAX' || formula === 'MEDIAN') {
            filteredColumn = filteredColumn.filter((val) => val !== '' && !isNaN(Number(val)));
        }
        // Sort values since the formula function don't handle it internally.
        if (formula === 'MEDIAN') {
            filteredColumn.sort((a, b) => Number(a) - Number(b));
        }
        try {
            return KPIComponent.formulaFunctions[formula](filteredColumn);
        }
        catch {
            console.warn('Invalid formula option provided.'); // eslint-disable-line no-console
        }
    }
    /**
     * Gets the default value that should be displayed in the KPI.
     *
     * @returns
     * The value that should be displayed in the KPI.
     */
    getValue() {
        if (defined(this.options.value)) {
            return this.options.value;
        }
        const dataTable = this.getDataTable()?.getModified();
        if (dataTable && this.options.columnId) {
            if (defined(this.options.formula)) {
                return this.getFormulaValue();
            }
            const column = dataTable.getColumn(this.options.columnId), length = column?.length || 0;
            return String(dataTable.getCell(this.options.columnId, length - 1));
        }
    }
    /**
     * Sets the value that should be displayed in the KPI.
     *
     * @param value
     * The value to display in the KPI.
     */
    setValue(value = this.getValue()) {
        const { valueFormat, valueFormatter } = this.options;
        if (defined(value)) {
            let prevValue;
            if (Utilities_isNumber(+value)) {
                prevValue = +value;
            }
            if (valueFormatter) {
                value = valueFormatter.call(this, value);
            }
            else if (valueFormat) {
                value = KPIComponent_format(valueFormat, { value });
            }
            else if (Utilities_isNumber(value)) {
                value = value.toLocaleString();
            }
            HTML_AST.setElementHTML(this.value, '' + value);
            this.linkValueToChart(prevValue);
            this.prevValue = prevValue;
        }
    }
    /**
     * Handles updating chart point value.
     *
     * @internal
     */
    linkValueToChart(value = this.getValue()) {
        const chart = this.chart;
        const linkedValueTo = this.options.linkedValueTo;
        if (!chart || !linkedValueTo.enabled ||
            !defined(value) || !Utilities_isNumber(+value)) {
            return;
        }
        value = +value;
        const targetSeries = chart.series[linkedValueTo.seriesIndex ?? 0], targetPoint = targetSeries?.points[linkedValueTo.pointIndex ?? 0];
        if (targetSeries) {
            if (targetPoint) {
                targetPoint.update({
                    y: value
                });
                return;
            }
            targetSeries.addPoint({
                y: value
            });
            return;
        }
        chart.addSeries({
            data: [{
                    y: value
                }]
        });
    }
    /**
     * Handles updating elements via options
     *
     * @internal
     */
    updateElements() {
        const { style, subtitle } = this.options;
        this.setValue();
        HTML_AST.setElementHTML(this.subtitle, this.getSubtitle());
        if (style) {
            css(this.element, style);
        }
        if (typeof subtitle === 'object') {
            if (subtitle.style) {
                css(this.subtitle, subtitle.style);
            }
            this.subtitle.className = this.getSubtitleClassName();
        }
        if (this.chartContainer) {
            this.chartContainer.style.flex =
                this.options.chartOptions ? '1' : '0';
        }
        if (this.chart) {
            this.chart.reflow();
        }
        this.value.style.color = this.getValueColor();
    }
    /**
     * Gets KPI subtitle text.
     *
     * @returns
     * The subtitle's text.
     *
     * @internal
     */
    getSubtitle() {
        const { subtitle, value } = this.options;
        if (typeof subtitle === 'string') {
            return subtitle;
        }
        if (subtitle) {
            if (Utilities_isNumber(this.prevValue) && Utilities_isNumber(value)) {
                const diff = value - this.prevValue;
                let prefix = '';
                if (diff > 0) {
                    prefix = '<span style="color:green">&#9650;</span> +';
                }
                else if (diff < 0) {
                    prefix = '<span style="color:red">&#9660;</span> ';
                }
                else {
                    return this.subtitle.innerHTML;
                }
                if (subtitle.type === 'diff') {
                    return prefix + diff.toLocaleString();
                }
                if (subtitle.type === 'diffpercent') {
                    return prefix + KPIComponent_format('{v:,.2f}%', {
                        v: diff / this.prevValue * 100
                    });
                }
            }
            return subtitle.text || '';
        }
        return '';
    }
    /**
     * Gets CSS class name of the KPI subtitle.
     *
     * @returns
     * The name of class.
     *
     * @internal
     */
    getSubtitleClassName() {
        const { subtitle } = this.options;
        return `${Components_Component.defaultOptions.className}-subtitle` +
            ((typeof subtitle === 'object' && subtitle.className) || '');
    }
    /**
     * Applies title's color according to the threshold.
     *
     * @returns
     * Hex of color.
     *
     * @internal
     */
    getValueColor() {
        const { threshold, thresholdColors, value } = this.options;
        if (thresholdColors && threshold && Utilities_isNumber(value)) {
            if (isArray(threshold)) {
                for (let i = threshold.length - 1; i >= 0; i--) {
                    if (value >= threshold[i]) {
                        if (i + 1 < thresholdColors.length) {
                            return thresholdColors[i + 1];
                        }
                        return thresholdColors[thresholdColors.length - 1];
                    }
                }
            }
            else if (value >= threshold) {
                return thresholdColors[1];
            }
            return thresholdColors[0];
        }
        return '';
    }
    getOptionsOnDrop(sidebar) {
        const connectorsIds = sidebar.editMode.board.dataPool.getConnectorIds();
        let options = {
            type: 'KPI'
        };
        if (connectorsIds.length) {
            options = {
                ...options,
                connector: {
                    id: connectorsIds[0]
                }
            };
        }
        return options;
    }
    /**
     * Get the KPI component's options.
     * @returns
     * KPI component's options.
     *
     * @internal
     *
     */
    getOptions() {
        return {
            ...diffObjects(this.options, KPIComponent.defaultOptions),
            type: 'KPI'
        };
    }
}
/**
 * Default options of the KPI component.
 */
KPIComponent.defaultOptions = merge(Components_Component.defaultOptions, KPIComponent_KPIComponentDefaults);
/**
 * Predefined sync config for the KPI component.
 */
KPIComponent.predefinedSyncConfig = KPISyncs;
/**
 * Default options of the KPI component.
 *
 * @default {
    chart: {
        type: 'spline',
        zooming: {
            mouseWheel: {
                enabled: false
            }
        }
    },
    title: {
        text: void 0
    },
    xAxis: {
        visible: false
    },
    yAxis: {
        visible: false,
        title: {
            text: null
        }
    },
    legend: {
        enabled: false
    },
    credits: {
        enabled: false
    },
    tooltip: {
        outside: true
    },
    plotOptions: {
        series: {
            marker: {
                enabled: false
            }
        }
    }
}
 */
KPIComponent.defaultChartOptions = {
    chart: {
        type: 'spline',
        zooming: {
            mouseWheel: {
                enabled: false
            }
        }
    },
    title: {
        text: void 0
    },
    xAxis: {
        visible: false
    },
    yAxis: {
        visible: false,
        title: {
            text: null
        }
    },
    legend: {
        enabled: false
    },
    credits: {
        enabled: false
    },
    tooltip: {
        outside: true
    },
    plotOptions: {
        series: {
            marker: {
                enabled: false
            }
        }
    }
};
/**
 * The formula option's default formula functions map.
 */
KPIComponent.formulaFunctions = {
    SUM: Functions_SUM,
    AVERAGE: Functions_AVERAGE,
    MEDIAN: Functions_MEDIAN,
    MAX: Functions_MAX,
    MIN: Functions_MIN,
    COUNT: Functions_COUNT,
    PRODUCT: Functions_PRODUCT
};
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const KPIComponent_KPIComponent = (KPIComponent);

;// ./code/dashboards/es-modules/Dashboards/Components/NavigatorComponent/NavigatorComponentDefaults.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */


/* *
 *
 *  Constants
 *
 * */
const NavigatorComponentDefaults = {
    type: 'Navigator',
    className: [
        Components_Component.defaultOptions.className,
        `${Components_Component.defaultOptions.className}-navigator`
    ].join(' '),
    chartOptions: {
        chart: {
            animation: false,
            height: 200,
            type: 'column',
            zooming: {
                mouseWheel: {
                    enabled: false
                }
            }
        },
        credits: {
            enabled: false
        },
        legend: {
            enabled: false
        },
        navigator: {
            enabled: true,
            outlineWidth: 0,
            series: {
                animation: false,
                lineWidth: 0,
                colorIndex: 0
            },
            xAxis: {
                endOnTick: true,
                gridZIndex: 4,
                labels: {
                    x: 1,
                    y: 22
                },
                opposite: true,
                showFirstLabel: true,
                showLastLabel: true,
                startOnTick: true,
                tickPosition: 'inside'
            },
            yAxis: {
                maxPadding: 0.5
            }
        },
        plotOptions: {
            series: {
                borderRadius: 0,
                marker: {
                    enabled: false
                },
                states: {
                    hover: {
                        enabled: false
                    }
                }
            }
        },
        scrollbar: {
            enabled: true
        },
        title: {
            text: ''
        },
        tooltip: {
            enabled: false
        },
        xAxis: {
            visible: false,
            minRange: Number.MIN_VALUE
        },
        yAxis: {
            visible: false
        }
    },
    editableOptions: (Components_Component.defaultOptions.editableOptions || []).concat()
};
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const NavigatorComponent_NavigatorComponentDefaults = (NavigatorComponentDefaults);

;// ./code/dashboards/es-modules/Dashboards/Components/NavigatorComponent/NavigatorSyncs/NavigatorSyncUtils.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Dawid Draguła
 *
 * */


/* *
*
*  Utility Functions
*
* */
/**
 * Adds or updates range options for a specific column.
 * @param filterOptions Filter modifier options object reference.
 * @param column Column name.
 * @param minValue Minimum value.
 * @param maxValue Maximum value.
 * @internal
 */
function setRangeOptions(filterOptions, column, minValue, maxValue) {
    let changedMin = false;
    let changedMax = false;
    if (typeof filterOptions.condition !== 'object' ||
        filterOptions.condition.operator !== 'and') {
        filterOptions.condition = {
            operator: 'and',
            conditions: []
        };
    }
    const { conditions } = filterOptions.condition;
    for (let i = 0, iEnd = conditions.length; i < iEnd; ++i) {
        const condition = conditions[i];
        if (!condition ||
            typeof condition !== 'object' ||
            !(condition.operator === '<=' || condition.operator === '>=') ||
            condition.columnId !== column) {
            continue;
        }
        if (condition.operator === '<=') {
            condition.value = maxValue;
            changedMax = true;
        }
        else {
            condition.value = minValue;
            changedMin = true;
        }
        if (changedMin && changedMax) {
            return;
        }
    }
    if (!changedMax) {
        conditions.push({
            operator: '<=',
            columnId: column,
            value: maxValue
        });
    }
    if (!changedMin) {
        conditions.push({
            operator: '>=',
            columnId: column,
            value: minValue
        });
    }
}
/**
 * Removes range options for a specific column.
 * @param filterOptions Filter modifier options object reference.
 * @param column Column name.
 * @internal
 */
function unsetRangeOptions(filterOptions, column) {
    if (typeof filterOptions.condition !== 'object' ||
        filterOptions.condition.operator !== 'and') {
        return;
    }
    const { conditions } = filterOptions.condition;
    for (let i = 0, iEnd = conditions.length; i < iEnd; ++i) {
        const condition = conditions[i];
        if (!condition ||
            typeof condition !== 'object' ||
            !(condition.operator === '<=' || condition.operator === '>=') ||
            condition.columnId !== column) {
            continue;
        }
        conditions.splice(i, 1)[0];
    }
}
/**
 * Converts filter options to ranges array.
 *
 * @param filterOptions
 * Filter modifier options object reference.
 */
function NavigatorSyncUtils_toRange(filterOptions) {
    const rangesMap = {};
    if (typeof filterOptions.condition !== 'object' ||
        filterOptions.condition.operator !== 'and') {
        return [];
    }
    const { conditions } = filterOptions.condition;
    for (let i = 0, iEnd = conditions.length; i < iEnd; ++i) {
        const condition = conditions[i];
        if (!condition ||
            typeof condition !== 'object' ||
            !(condition.operator === '<=' || condition.operator === '>=') ||
            typeof condition.columnId !== 'string' ||
            !defined(condition.value)) {
            continue;
        }
        const colName = condition.columnId;
        if (!rangesMap[colName]) {
            rangesMap[colName] = {
                maxValue: Infinity,
                minValue: -Infinity,
                columnId: colName
            };
        }
        if (condition.operator === '<=') {
            rangesMap[colName].maxValue = condition.value;
        }
        else {
            rangesMap[colName].minValue = condition.value;
        }
    }
    return Object.values(rangesMap);
}
const NavigatorSyncUtils = {
    setRangeOptions,
    unsetRangeOptions,
    toRange: NavigatorSyncUtils_toRange
};
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const NavigatorSyncs_NavigatorSyncUtils = (NavigatorSyncUtils);

;// ./code/dashboards/es-modules/Dashboards/Components/NavigatorComponent/NavigatorSyncs/NavigatorCrossfilterSync.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Dawid Draguła
 *
 * */




const { Filter: NavigatorCrossfilterSync_FilterModifier } = Modifiers_DataModifier.types;
/* *
 *
 *  Constants
 *
 * */
const NavigatorCrossfilterSync_defaultOptions = {
    affectNavigator: false
};
const NavigatorCrossfilterSync_syncPair = {
    emitter: function () {
        if (this.type !== 'Navigator') {
            return;
        }
        const component = this;
        const syncOptions = this.sync.syncConfig.crossfilter;
        const groupKey = syncOptions.group ? ':' + syncOptions.group : '';
        const afterSetExtremes = async (extremes) => {
            if (component.connectorHandlers?.[0]?.connector) {
                const table = component.connectorHandlers[0].connector.getTable(), dataCursor = component.board.dataCursor, filterColumn = component.getColumnAssignment()[0], [min, max] = component.getAxisExtremes();
                let modifier = table.getModifier();
                if (modifier instanceof NavigatorCrossfilterSync_FilterModifier) {
                    NavigatorSyncs_NavigatorSyncUtils.setRangeOptions(modifier.options, filterColumn, min, max);
                }
                else {
                    modifier = new NavigatorCrossfilterSync_FilterModifier({
                        condition: {
                            operator: 'and',
                            conditions: [{
                                    columnId: filterColumn,
                                    operator: '>=',
                                    value: min
                                }, {
                                    columnId: filterColumn,
                                    operator: '<=',
                                    value: max
                                }]
                        }
                    });
                }
                await table.setModifier(modifier);
                dataCursor.emitCursor(table, {
                    type: 'position',
                    column: filterColumn,
                    row: table.getRowIndexBy(filterColumn, min),
                    state: 'crossfilter' + groupKey
                }, extremes);
                dataCursor.emitCursor(table, {
                    type: 'position',
                    column: filterColumn,
                    row: table.getRowIndexBy(filterColumn, max),
                    state: 'crossfilter' + groupKey
                }, extremes);
            }
        };
        let delay;
        return addEvent(component.chart.xAxis[0], 'afterSetExtremes', function (extremes) {
            clearTimeout(delay);
            delay = setTimeout(afterSetExtremes, 50, this, extremes);
        });
    },
    handler: void 0
};
/* *
*
*  Default export
*
* */
/* harmony default export */ const NavigatorCrossfilterSync = ({ defaultOptions: NavigatorCrossfilterSync_defaultOptions, syncPair: NavigatorCrossfilterSync_syncPair });

;// ./code/dashboards/es-modules/Dashboards/Components/NavigatorComponent/NavigatorSyncs/NavigatorExtremesSync.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Dawid Draguła
 *
 * */




const { Filter: NavigatorExtremesSync_FilterModifier } = Modifiers_DataModifier.types;
/* *
 *
 *  Constants
 *
 * */
const NavigatorExtremesSync_defaultOptions = {};
const NavigatorExtremesSync_syncPair = {
    emitter: function () {
        if (this.type !== 'Navigator') {
            return;
        }
        const component = this;
        const syncOptions = this.sync.syncConfig.extremes;
        const groupKey = syncOptions.group ? ':' + syncOptions.group : '';
        const afterSetExtremes = (extremes) => {
            if (component.connectorHandlers?.[0]?.connector) {
                const table = component.connectorHandlers[0].connector.getTable(), dataCursor = component.board.dataCursor, filterColumn = component.getColumnAssignment()[0], [min, max] = component.getAxisExtremes();
                dataCursor.emitCursor(table, {
                    type: 'position',
                    column: filterColumn,
                    row: table.getRowIndexBy(filterColumn, min),
                    state: 'xAxis.extremes.min' + groupKey
                }, extremes);
                dataCursor.emitCursor(table, {
                    type: 'position',
                    column: filterColumn,
                    row: table.getRowIndexBy(filterColumn, max),
                    state: 'xAxis.extremes.max' + groupKey
                }, extremes);
            }
        };
        let delay;
        return addEvent(component.chart.xAxis[0], 'afterSetExtremes', function (extremes) {
            clearTimeout(delay);
            delay = setTimeout(afterSetExtremes, 50, this, extremes);
        });
    },
    handler: function () {
        if (this.type !== 'Navigator') {
            return;
        }
        const component = this;
        const syncOptions = this.sync.syncConfig.extremes;
        const groupKey = syncOptions.group ? ':' + syncOptions.group : '';
        const dataCursor = component.board.dataCursor;
        const extremesListener = (e) => {
            const cursor = e.cursor;
            if (!component.connectorHandlers?.[0]?.connector) {
                return;
            }
            const table = component.connectorHandlers[0].connector.getTable();
            // Assume first column with unique keys as fallback
            let extremesColumn = table.getColumnIds()[0], maxIndex = table.getRowCount(), minIndex = 0;
            if (cursor.type === 'range') {
                maxIndex = cursor.lastRow;
                minIndex = cursor.firstRow;
                if (cursor.columns) {
                    extremesColumn = Utilities_pick(cursor.columns[0], extremesColumn);
                }
            }
            else if (cursor.state === 'xAxis.extremes.max' + groupKey) {
                extremesColumn = Utilities_pick(cursor.column, extremesColumn);
                maxIndex = Utilities_pick(cursor.row, maxIndex);
            }
            else {
                extremesColumn = Utilities_pick(cursor.column, extremesColumn);
                minIndex = Utilities_pick(cursor.row, minIndex);
            }
            const modifier = table.getModifier();
            if (typeof extremesColumn === 'string' &&
                modifier instanceof NavigatorExtremesSync_FilterModifier) {
                const min = table.getCell(extremesColumn, minIndex);
                const max = table.getCell(extremesColumn, maxIndex);
                if (defined(max) && defined(min)) {
                    NavigatorSyncs_NavigatorSyncUtils.setRangeOptions(modifier.options, extremesColumn, min, max);
                    void table.setModifier(modifier);
                }
            }
        };
        const registerCursorListeners = () => {
            const table = component.connectorHandlers?.[0]?.connector?.getTable();
            if (table) {
                dataCursor.addListener(table.id, 'xAxis.extremes' + groupKey, extremesListener);
                dataCursor.addListener(table.id, 'xAxis.extremes.max' + groupKey, extremesListener);
                dataCursor.addListener(table.id, 'xAxis.extremes.min' + groupKey, extremesListener);
            }
        };
        const unregisterCursorListeners = () => {
            const table = component.connectorHandlers?.[0]?.connector?.getTable();
            if (table) {
                dataCursor.removeListener(table.id, 'xAxis.extremes' + groupKey, extremesListener);
                dataCursor.removeListener(table.id, 'xAxis.extremes.max' + groupKey, extremesListener);
                dataCursor.removeListener(table.id, 'xAxis.extremes.min' + groupKey, extremesListener);
            }
        };
        registerCursorListeners();
        return unregisterCursorListeners;
    }
};
/* *
*
*  Default export
*
* */
/* harmony default export */ const NavigatorExtremesSync = ({ defaultOptions: NavigatorExtremesSync_defaultOptions, syncPair: NavigatorExtremesSync_syncPair });

;// ./code/dashboards/es-modules/Dashboards/Components/NavigatorComponent/NavigatorSyncs/NavigatorSyncs.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Dawid Draguła
 *
 * */



/* *
*
*  Constants
*
* */
const NavigatorSyncs_predefinedSyncConfig = {
    defaultSyncPairs: {
        crossfilter: NavigatorCrossfilterSync.syncPair,
        extremes: NavigatorExtremesSync.syncPair
    },
    defaultSyncOptions: {
        crossfilter: NavigatorCrossfilterSync.defaultOptions,
        extremes: NavigatorExtremesSync.defaultOptions
    }
};
/* *
 *
 *  Default export
 *
 * */
/* harmony default export */ const NavigatorSyncs = (NavigatorSyncs_predefinedSyncConfig);

;// ./code/dashboards/es-modules/Dashboards/Components/NavigatorComponent/NavigatorComponent.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */







/* *
 *
 *  Class
 *
 * */
/**
 * Setup a component with data navigation.
 */
class NavigatorComponent extends Components_Component {
    /* *
     *
     *  Constructor
     *
     * */
    constructor(cell, options) {
        super(cell, options);
        this.type = 'Navigator';
        this.options = merge(NavigatorComponent.defaultOptions, options);
        const charter = (NavigatorComponent.charter.Chart ||
            Dashboards_Globals.win.Highcharts);
        this.chartContainer = Dashboards_Globals.win.document.createElement('div');
        this.chart = charter
            .chart(this.chartContainer, (this.options.chartOptions || {}));
        this.chartContainer.classList
            .add(Dashboards_Globals.classNamePrefix + 'navigator');
        if (this.sync.syncConfig.crossfilter?.enabled) {
            this.chart.update(merge({ navigator: { xAxis: { labels: { format: '{value}' } } } }, this.options.chartOptions || {}), false);
        }
    }
    /* *
     *
     *  Functions
     *
     * */
    /** @private */
    adjustNavigator() {
        const chart = this.chart, height = Utilities_pick(chart.chartHeight, this.contentElement.clientHeight), width = this.contentElement.clientWidth, chartUpdates = {};
        if (chart.chartHeight !== height ||
            chart.chartWidth !== width) {
            chartUpdates.chart = {
                height,
                width
            };
        }
        if (chart.navigator) {
            const navigator = chart.navigator, navigatorHeight = (navigator.top - chart.plotTop + navigator.height);
            if (navigator.height !== navigatorHeight) {
                chartUpdates.navigator = {
                    handles: {
                        height: Math.round(height / 4)
                    },
                    height: navigatorHeight
                };
            }
            if (Object.keys(chartUpdates).length) {
                chart.update(chartUpdates, false);
            }
            if (navigator.series && navigator.series[0]) {
                navigator.series[0].update({
                    type: chart.series[0].type
                }, false);
            }
        }
        else if (Object.keys(chartUpdates).length) {
            chart.update(chartUpdates, false);
        }
    }
    /**
     * Returns the first column of columnAssignment to use for navigator data.
     * @private
     *
     * @return
     * Navigator column assignment.
     */
    getColumnAssignment() {
        const columnAssignment = this.options.columnAssignment ?? {};
        let columnsAssignment;
        for (const column of Object.keys(columnAssignment)) {
            columnsAssignment = columnAssignment[column];
            if (columnsAssignment !== null) {
                return [column, columnsAssignment];
            }
        }
        const table = this.getDataTable();
        if (table) {
            const columns = table.getColumnIds();
            if (columns.length) {
                return [columns[0], 'y'];
            }
        }
        return ['', 'y'];
    }
    /**
     * Gets the component's options.
     * @internal
     */
    getOptions() {
        return {
            ...diffObjects(this.options, NavigatorComponent_NavigatorComponentDefaults),
            type: 'Navigator'
        };
    }
    /**
     * Gets the extremes of the navigator's x-axis.
     */
    getAxisExtremes() {
        const axis = this.chart.xAxis[0], extremes = axis.getExtremes(), min = Utilities_isNumber(extremes.min) ? extremes.min : extremes.dataMin, max = Utilities_isNumber(extremes.max) ? extremes.max : extremes.dataMax;
        if (this.categories) {
            return [
                this.categories[Math.max(0, Math.ceil(min))],
                this.categories[Math.min(this.categories.length - 1, Math.floor(max))]
            ];
        }
        if (axis.hasNames) {
            return [
                axis.names[Math.ceil(min)],
                axis.names[Math.floor(max)]
            ];
        }
        return [min, max];
    }
    /** @private */
    async load() {
        await super.load();
        this.contentElement.appendChild(this.chartContainer);
        this.parentElement.appendChild(this.element);
        this.adjustNavigator();
        this.emit({ type: 'afterLoad' });
        return this;
    }
    onTableChanged() {
        this.renderNavigator();
    }
    /** @private */
    redrawNavigator() {
        const timeouts = this.resizeTimeouts;
        for (let i = 0, iEnd = timeouts.length; i < iEnd; ++i) {
            clearTimeout(timeouts[i]);
        }
        timeouts.length = 0;
        timeouts.push(setTimeout(() => {
            this.adjustNavigator();
            this.chart.redraw();
        }, 33));
    }
    /** @private */
    render() {
        const component = this;
        super.render();
        component.renderNavigator();
        component.sync.start();
        component.emit({ type: 'afterRender' });
        return component;
    }
    /** @private */
    renderNavigator() {
        const chart = this.chart;
        const table = this.getDataTable();
        if (table) {
            const column = this.getColumnAssignment(), columnValues = table.getColumn(column[0], true) || [];
            let data;
            if (this.sync.syncConfig.crossfilter?.enabled) {
                data = this.generateCrossfilterData();
            }
            else {
                data = columnValues.slice();
            }
            if (!chart.series[0]) {
                chart.addSeries({ id: table.id, data }, false);
            }
            else {
                chart.series[0].setData(data, false);
            }
        }
        this.redrawNavigator();
    }
    /**
     * Generates the data for the crossfilter navigator.
     */
    generateCrossfilterData() {
        const crossfilterOptions = this.sync.syncConfig.crossfilter;
        const table = this.getDataTable();
        const columnValues = table?.getColumn(this.getColumnAssignment()[0], true) || [];
        if (!table || columnValues.length < 1 || !crossfilterOptions) {
            return [];
        }
        const values = [];
        const uniqueXValues = [];
        for (let i = 0, iEnd = columnValues.length; i < iEnd; i++) {
            let value = columnValues[i];
            if (value === null) {
                continue;
            }
            else if (!Utilities_isNumber(value)) {
                value = `${value}`;
            }
            // Check if the x-axis data is not of mixed type.
            if (this.stringData === void 0) {
                this.stringData = isString(value);
            }
            else if (this.stringData !== isString(value)) {
                throw new Error('Mixed data types in crossfilter navigator are ' +
                    'not supported.');
            }
            values.push(value);
            if (uniqueXValues.indexOf(value) === -1) {
                uniqueXValues.push(value);
            }
        }
        uniqueXValues.sort((a, b) => (Utilities_pick(a, NaN) < Utilities_pick(b, NaN) ? -1 : a === b ? 0 : 1));
        let filteredValues;
        const modifierOptions = table.getModifier()?.options;
        if (crossfilterOptions.affectNavigator &&
            modifierOptions?.type === 'Filter') {
            const appliedRanges = [];
            const rangedColumns = [];
            const ranges = NavigatorSyncs_NavigatorSyncUtils.toRange(modifierOptions);
            for (let i = 0, iEnd = ranges.length; i < iEnd; i++) {
                if (ranges[i].columnId !== this.getColumnAssignment()[0]) {
                    appliedRanges.push(ranges[i]);
                    rangedColumns.push(table.getColumn(ranges[i].columnId, true) || []);
                }
            }
            filteredValues = [];
            const appliedRagesLength = appliedRanges.length;
            for (let i = 0, iEnd = values.length; i < iEnd; i++) {
                const value = values[i];
                let allConditionsMet = true;
                for (let j = 0; j < appliedRagesLength; j++) {
                    const range = appliedRanges[j];
                    if (!(rangedColumns[j][i] >=
                        (range.minValue ?? -Infinity) &&
                        rangedColumns[j][i] <=
                            (range.maxValue ?? Infinity))) {
                        allConditionsMet = false;
                        break;
                    }
                }
                if (allConditionsMet) {
                    filteredValues.push(value);
                }
            }
        }
        else {
            filteredValues = values;
        }
        const seriesData = [];
        if (this.stringData) {
            this.categories = uniqueXValues;
            for (let i = 0, iEnd = uniqueXValues.length; i < iEnd; i++) {
                seriesData.push([i, null]);
            }
        }
        else {
            for (let i = 0, iEnd = uniqueXValues.length; i < iEnd; i++) {
                seriesData.push([uniqueXValues[i], null]);
            }
        }
        for (let i = 0, iEnd = filteredValues.length; i < iEnd; i++) {
            const index = uniqueXValues.indexOf(filteredValues[i]);
            seriesData[index][1] = (seriesData[index][1] || 0) + 1;
        }
        return seriesData;
    }
    /** @private */
    resize(width, height) {
        super.resize(width, height);
        this.redrawNavigator();
        return this;
    }
    /**
     * Handles updating via options.
     *
     * @param options
     * The options to apply.
     */
    async update(options, shouldRerender = true) {
        const chart = this.chart;
        await super.update(options, false);
        if (options.chartOptions) {
            chart.update(merge(this.sync.syncConfig.crossfilter?.enabled ? ({ navigator: { xAxis: { labels: { format: '{value}' } } } }) : {}, options.chartOptions), false);
        }
        this.emit({ type: 'afterUpdate' });
        if (shouldRerender) {
            this.render();
        }
    }
    getOptionsOnDrop() {
        return {};
    }
}
/**
 * Default options of the Navigator component.
 */
NavigatorComponent.defaultOptions = merge(Components_Component.defaultOptions, NavigatorComponent_NavigatorComponentDefaults);
/**
 * Predefined sync configuration for the Navigator component.
 */
NavigatorComponent.predefinedSyncConfig = NavigatorSyncs;
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const NavigatorComponent_NavigatorComponent = (NavigatorComponent);

;// ./code/dashboards/es-modules/Dashboards/Plugins/HighchartsPlugin.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */




/* *
 *
 *  Functions
 *
 * */
/**
 * Connects Highcharts core with the Dashboard plugin.
 *
 * @param {Highcharts} highcharts
 * Highcharts core to connect.
 */
function connectHighcharts(highcharts) {
    HighchartsComponent_HighchartsComponent.charter = highcharts;
    KPIComponent_KPIComponent.charter = highcharts;
    NavigatorComponent_NavigatorComponent.charter = highcharts;
}
/**
 * Callback function of the Dashboard plugin.
 *
 * @param {Dashboards.PluginHandler.Event} e
 * Plugin context provided by the Dashboard.
 */
function HighchartsPlugin_onRegister(e) {
    const { ComponentRegistry } = e;
    ComponentRegistry.registerComponent('Highcharts', HighchartsComponent_HighchartsComponent);
    ComponentRegistry.registerComponent('KPI', KPIComponent_KPIComponent);
    ComponentRegistry.registerComponent('Navigator', NavigatorComponent_NavigatorComponent);
}
/**
 * Callback function of the Dashboard plugin.
 *
 * @param {Dashboard.PluginHandler.Event} e
 * Plugin context provided by the Dashboard.
 */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function HighchartsPlugin_onUnregister(e) {
}
/* *
 *
 *  Default Export
 *
 * */
const HighchartsCustom = {
    connectHighcharts
};
const HighchartsPlugin = {
    custom: HighchartsCustom,
    name: 'Highcharts.DashboardsPlugin',
    onRegister: HighchartsPlugin_onRegister,
    onUnregister: HighchartsPlugin_onUnregister
};
/* harmony default export */ const Plugins_HighchartsPlugin = (HighchartsPlugin);

;// ./code/dashboards/es-modules/Dashboards/PluginHandler.js
/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */




/* *
 *
 *  Constants
 *
 * */
/** @internal */
const registry = {};
/**
 * Revision of the Dashboard plugin API.
 *
 * @internal
 */
const revision = 0;
/* *
 *
 *  Functions
 *
 * */
/**
 * Adds a dashboard plugin.
 *
 * @param {Dashboards.Plugin} plugin
 * Dashboard plugin to register.
 *
 * @param {string} [key]
 * Plugin key for the registry. (Default: `plugin.name`)
 */
function addPlugin(plugin, key = plugin.name) {
    const { maxRevision, minRevision, onRegister } = plugin;
    if (registry[key]) {
        // Only throw error with custom key
        if (key !== plugin.name) {
            throw new Error(`Plugin '${key}' already registered.`);
        }
        return;
    }
    if ((typeof minRevision === 'number' && minRevision > revision) ||
        (typeof maxRevision === 'number' && maxRevision < revision)) {
        throw new Error(`Plugin '${key}' does not support revision ${revision}.`);
    }
    onRegister({
        Board: Dashboards_Board,
        ComponentRegistry: Components_ComponentRegistry,
        Sync: Sync_Sync,
        revision
    });
    registry[key] = plugin;
}
/**
 * Removes a dashboard plugin.
 *
 * @param {string} key
 * Plugin key in the registry.
 */
function removePlugin(key) {
    if (registry[key]) {
        registry[key].onUnregister({
            ComponentRegistry: Components_ComponentRegistry,
            Board: Dashboards_Board,
            Sync: Sync_Sync,
            revision
        });
        delete registry[key];
    }
}
/* *
 *
 *  Default Export
 *
 * */
const PluginHandler = {
    addPlugin,
    removePlugin,
    registry,
    revision
};
/* harmony default export */ const Dashboards_PluginHandler = (PluginHandler);

;// ./code/dashboards/es-modules/masters/dashboards.src.js


// Fill registries









// Import SerializeHelper modules to register them


























// Import components





/* *
 *
 *  Namespace
 *
 * */
const G = Dashboards_Globals;
G.board = Dashboards_Board.board;
G.addEvent = addEvent;
G.error = Dashboards_Utilities.error;
G.merge = merge;
G.removeEvent = removeEvent;
G.setOptions = Dashboards_Defaults.setOptions;
G.uniqueKey = uniqueKey;
G.AST = HTML_AST;
G.Board = Dashboards_Board;
G.Component = Components_Component;
G.ComponentRegistry = Components_ComponentRegistry;
G.GridComponent = GridComponent_GridComponent;
G.HighchartsComponent = HighchartsComponent_HighchartsComponent;
G.HTMLComponent = HTMLComponent_HTMLComponent;
G.KPIComponent = KPIComponent_KPIComponent;
G.NavigatorComponent = NavigatorComponent_NavigatorComponent;
G.DataConnector = Connectors_DataConnector;
G.DataConverter = Converters_DataConverter;
G.DataCursor = Data_DataCursor;
G.DataModifier = Modifiers_DataModifier;
G.DataPool = Data_DataPool;
G.DataTable = Data_DataTable;
G.defaultOptions = Dashboards_Defaults.defaultOptions;
G.GridPlugin = Plugins_GridPlugin;
G.HighchartsPlugin = Plugins_HighchartsPlugin;
G.PluginHandler = Dashboards_PluginHandler;
G.Sync = Sync_Sync;
/* *
 *
 *  Classic Export
 *
 * */
if (!G.win.Dashboards) {
    G.win.Dashboards = G;
}
if (G.win.Grid) {
    Plugins_GridPlugin.custom.connectGrid(G.win.Grid);
    G.PluginHandler.addPlugin(Plugins_GridPlugin);
}
if (G.win.Highcharts) {
    Plugins_HighchartsPlugin.custom.connectHighcharts(G.win.Highcharts);
    G.PluginHandler.addPlugin(Plugins_HighchartsPlugin);
}
/* *
 *
 *  Default Export
 *
 * */
/* harmony default export */ const dashboards_src = (G);

__webpack_exports__ = __webpack_exports__["default"];
/******/ 	return __webpack_exports__;
/******/ })()
;
});