Hacked By AnonymousFox
/**
* @license Highcharts JS v12.1.2 (2025-01-09)
* @module highcharts/highcharts
*
* (c) 2009-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*/
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define("highcharts/highcharts", [], factory);
else if(typeof exports === 'object')
exports["highcharts"] = factory();
else
((root["Highcharts"] && root["Highcharts"].error(16, true)), root["Highcharts"] = factory());
})(this, function() {
return /******/ (function() { // webpackBootstrap
/******/ "use strict";
/******/ // The require scope
/******/ var __webpack_require__ = {};
/******/
/************************************************************************/
/******/ /* webpack/runtime/define property getters */
/******/ !function() {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = function(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 */
/******/ !function() {
/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
/******/ }();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// EXPORTS
__webpack_require__.d(__webpack_exports__, {
"default": function() { return /* binding */ highstock_src; }
});
;// ./code/es5/es-modules/Core/Globals.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* Namespace
*
* */
/**
* Shared Highcharts properties.
* @private
*/
var Globals;
(function (Globals) {
/* *
*
* Constants
*
* */
var _a,
_b;
Globals.SVG_NS = 'http://www.w3.org/2000/svg', Globals.product = 'Highcharts', Globals.version = '12.1.2', Globals.win = (typeof window !== 'undefined' ?
window :
{}), // eslint-disable-line node/no-unsupported-features/es-builtins
Globals.doc = Globals.win.document, Globals.svg = (Globals.doc &&
Globals.doc.createElementNS &&
!!Globals.doc.createElementNS(Globals.SVG_NS, 'svg').createSVGRect), Globals.pageLang = (_b = (_a = Globals.doc === null || Globals.doc === void 0 ? void 0 : Globals.doc.documentElement) === null || _a === void 0 ? void 0 : _a.closest('[lang]')) === null || _b === void 0 ? void 0 : _b.lang, Globals.userAgent = (Globals.win.navigator && 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).
var 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) {
var 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.
* @private
*/
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 = {};
/**
* @private
* @deprecated
* @todo Use only `Core/Series/SeriesRegistry.seriesTypes`
*/
Globals.seriesTypes = {};
/**
* @private
*/
Globals.symbolSizes = {};
/* *
*
* Properties
*
* */
// eslint-disable-next-line prefer-const
Globals.chartCount = 0;
})(Globals || (Globals = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var 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/es5/es-modules/Core/Utilities.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var __spreadArray = (undefined && undefined.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var charts = Core_Globals.charts, doc = Core_Globals.doc, win = Core_Globals.win;
/* *
*
* 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) {
var severity = stop ? 'Highcharts error' : 'Highcharts warning';
if (code === 32) {
code = "" + severity + ": Deprecated member";
}
var isCode = isNumber(code);
var message = isCode ?
"" + severity + " #" + code + ": www.highcharts.com/errors/" + code + "/" :
code.toString();
var defaultHandler = function () {
if (stop) {
throw new Error(message);
}
// Else ...
if (win.console &&
error.messages.indexOf(message) === -1 // Prevent console flooting
) {
console.warn(message); // eslint-disable-line no-console
}
};
if (typeof params !== 'undefined') {
var additionalMessages_1 = '';
if (isCode) {
message += '?';
}
objectEach(params, function (value, key) {
additionalMessages_1 += "\n - ".concat(key, ": ").concat(value);
if (isCode) {
message += encodeURI(key) + '=' + encodeURI(value);
}
});
message += additionalMessages_1;
}
fireEvent(Core_Globals, 'displayError', { chart: chart, code: code, message: message, params: params }, defaultHandler);
error.messages.push(message);
}
(function (error) {
error.messages = [];
})(error || (error = {}));
/**
* 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) {
var sources = [];
for (var _a = 1; _a < arguments.length; _a++) {
sources[_a - 1] = arguments[_a];
}
var i,
args = __spreadArray([extendOrSource],
sources,
true),
ret = {};
var 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
var len = args.length;
for (i = 0; i < len; i++) {
ret = doCopy(ret, args[i]);
}
return ret;
}
/**
* Constrain a value to within a lower and upper threshold.
*
* @private
* @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;
}
/**
* Utility for crisping a line position to the nearest full pixel depening on
* the line width
* @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, inverted) {
if (lineWidth === void 0) { lineWidth = 0; }
var mod = lineWidth % 2 / 2,
inverter = inverted ? -1 : 1;
return (Math.round(value * inverter - mod) + mod) * inverter;
}
// 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.
* @private
*/
function diffObjects(newer, older, keepOlder, collectionsWithUpdate) {
var 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) {
var 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 (var 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;
}
/**
* Shortcut for parseInt
*
* @private
* @function Highcharts.pInt
*
* @param {*} s
* any
*
* @param {number} [mag]
* Magnitude
*
* @return {number}
* number
*/
function pInt(s, mag) {
return parseInt(s, mag || 10);
}
/**
* 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) {
var str = Object.prototype.toString.call(obj);
return str === '[object Array]' || str === '[object Array Iterator]';
}
/**
* 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 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) {
var c = obj && obj.constructor;
return !!(isObject(obj, true) &&
!isDOMElement(obj) &&
(c && 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 isNumber(n) {
return typeof n === 'number' && !isNaN(n) && n < Infinity && n > -Infinity;
}
/**
* Remove the last occurence 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) {
var i = arr.length;
while (i--) {
if (arr[i] === item) {
arr.splice(i, 1);
break;
}
}
}
/**
* 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.
*
* @private
* @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) {
var indexOption = item.options.index,
length = collection.length;
var 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;
}
/**
* Adds an item to an array, if it is not present in the array.
*
* @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);
}
/**
* 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;
}
/**
* 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) {
var isGetter = isString(keyOrAttribs) && !defined(value);
var ret;
var attrSingle = function (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;
}
/**
* 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];
}
/**
* 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 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).
*
* @function Highcharts.clearTimeout
*
* @param {number|undefined} id
* Id of a timeout.
*/
function internalClearTimeout(id) {
if (defined(id)) {
clearTimeout(id);
}
}
/* eslint-disable valid-jsdoc */
/**
* 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) {
/* eslint-enable valid-jsdoc */
var n;
if (!a) {
a = {};
}
for (n in b) { // eslint-disable-line guard-for-in
a[n] = b[n];
}
return a;
}
/* eslint-disable valid-jsdoc */
/**
* 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 pick() {
var args = arguments;
var length = args.length;
for (var i = 0; i < length; i++) {
var arg = args[i];
if (typeof arg !== 'undefined' && arg !== null) {
return arg;
}
}
}
/**
* 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);
}
/**
* 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) {
var 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;
}
// 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) {
var obj = (function () { });
obj.prototype = new parent(); // eslint-disable-line new-cap
extend(obj.prototype, members);
return obj;
}
/**
* 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;
}
/**
* 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.
*
* @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) {
var replacements = [];
for (var _a = 1; _a < arguments.length; _a++) {
replacements[_a - 1] = arguments[_a];
}
var previous,
replacement;
do {
previous = text;
for (var _b = 0, replacements_1 = replacements; _b < replacements_1.length; _b++) {
replacement = replacements_1[_b];
text = text.replace(replacement[0], replacement[1]);
}
} while (text !== previous);
return text;
}
/**
* 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) {
var proceed = obj[method];
obj[method] = function () {
var outerArgs = arguments,
scope = this;
return func.apply(this, [
function () {
return proceed.apply(scope, arguments.length ? arguments : outerArgs);
}
].concat([].slice.call(arguments)));
};
}
/**
* 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));
}
/**
* 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) {
var i,
retInterval = interval;
// Round to a tenfold of 1, 2, 2.5 or 5
magnitude = pick(magnitude, getMagnitude(interval));
var 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;
}
/**
* Sort an object array and keep the order of equal items. The ECMAScript
* standard does not specify the behaviour 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
var length = arr.length;
var 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
}
}
/**
* 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) {
var 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) {
var i = data.length,
max = data[0];
while (i--) {
if (data[i] > max) {
max = data[i];
}
}
return max;
}
/**
* 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 === null || val === void 0 ? void 0 : val.destroy)) {
// Invoke the destroy
val.destroy();
}
// Delete the property from the object
if ((val === null || val === void 0 ? void 0 : 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) {
if (element && element.parentElement) {
element.parentElement.removeChild(element);
}
}
/**
* 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));
}
/**
* The time unit lookup
*
* @ignore
*/
var 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
*
* @private
* @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);
};
/**
* Convenience function to get the align factor, used several places for
* computing positions
* @private
*/
var getAlignFactor = function (align) {
if (align === void 0) { align = ''; }
return ({
center: 0.5,
right: 1,
middle: 0.5,
bottom: 1
}[align] || 0);
};
/**
* Find the closest distance between two values of a two-dimensional array
* @private
* @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) {
var allowNegative = !onError;
var closest,
loopLength,
distance,
i;
arrays.forEach(function (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 === null || onError === void 0 ? void 0 : onError();
// Only one call
onError = void 0;
}
else if (distance && (typeof closest === 'undefined' || distance < closest)) {
closest = distance;
}
}
}
});
return closest;
}
/**
* Returns the value of a property path on a given object.
*
* @private
* @function getNestedProperty
*
* @param {string} path
* Path to the property, for example `custom.myValue`.
*
* @param {unknown} obj
* Instance containing the property on the specific path.
*
* @return {unknown}
* The unknown property value.
*/
function getNestedProperty(path, parent) {
var pathElements = path.split('.');
while (pathElements.length && defined(parent)) {
var pathElement = pathElements.shift();
// Filter on the key
if (typeof pathElement === 'undefined' ||
pathElement === '__proto__') {
return; // Undefined
}
if (pathElement === 'this') {
var thisProp = void 0;
if (isObject(parent)) {
thisProp = parent['@this'];
}
return thisProp !== null && thisProp !== void 0 ? thisProp : parent;
}
var 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) {
var style;
// For width and height, return the actual inner pixel size (#4913)
if (prop === 'width') {
var 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) ...
var boundingClientRectWidth = el.getBoundingClientRect &&
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
var css = win.getComputedStyle(el,
void 0); // eslint-disable-line no-undefined
if (css) {
style = css.getPropertyValue(prop);
if (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.
*/
var find = Array.prototype.find ?
function (arr, callback) {
return arr.find(callback);
} :
// Legacy implementation. PhantomJS, IE <= 11 etc. #7223.
function (arr, callback) {
var i;
var length = arr.length;
for (i = 0; i < length; i++) {
if (callback(arr[i], i)) { // eslint-disable-line node/callback-return
return arr[i];
}
}
};
/**
* 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) {
var 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
};
}
/* eslint-disable valid-jsdoc */
/**
* 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) {
/* eslint-enable valid-jsdoc */
for (var key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
fn.call(ctx || obj[key], obj[key], key, obj);
}
}
}
/* eslint-disable valid-jsdoc */
/**
* 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) {
/* eslint-enable valid-jsdoc */
if (options === void 0) { 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).
var owner = typeof el === 'function' && el.prototype || el;
if (!Object.hasOwnProperty.call(owner, 'hcEvents')) {
owner.hcEvents = {};
}
var 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).
var 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] = [];
}
var eventObject = {
fn: fn,
order: typeof options.order === 'number' ? options.order : Infinity
};
events[type].push(eventObject);
// Order the calls
events[type].sort(function (a, b) { return a.order - b.order; });
// Return a function that can be called to remove this event.
return function () {
removeEvent(el, type, fn);
};
}
/* eslint-disable valid-jsdoc */
/**
* 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) {
/* eslint-enable valid-jsdoc */
/**
* @private
*/
function removeOneEvent(type, fn) {
var removeEventListener = el.removeEventListener;
if (removeEventListener) {
removeEventListener.call(el, type, fn, false);
}
}
/**
* @private
*/
function removeAllEvents(eventCollection) {
var 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);
}
}
});
}
var owner = typeof el === 'function' && el.prototype || el;
if (Object.hasOwnProperty.call(owner, 'hcEvents')) {
var events = owner.hcEvents;
if (type) {
var 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;
}
}
}
/* eslint-disable valid-jsdoc */
/**
* 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) {
/* eslint-enable valid-jsdoc */
eventArguments = eventArguments || {};
if (doc.createEvent &&
(el.dispatchEvent ||
(el.fireEvent &&
// Enable firing events on Highcharts instance.
el !== Core_Globals))) {
var 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
});
}
var events = [];
var object = el;
var 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(function (a, b) { return a.order - b.order; });
}
// Call the collected event handlers
events.forEach(function (obj) {
// If the event handler returns false, prevent the default handler
// from executing
if (obj.fn.call(el, eventArguments) === false) {
eventArguments.preventDefault();
}
});
}
// Run the default if not prevented
if (defaultFunction && !eventArguments.defaultPrevented) {
defaultFunction.call(el, eventArguments);
}
}
var 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.
*/
var uniqueKey = (function () {
var hash = Math.random().toString(36).substring(2, 9) + '-';
var 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));
}
function isFunction(obj) {
return typeof obj === 'function';
}
function ucfirst(s) {
return ((isString(s) ?
s.substring(0, 1).toUpperCase() + s.substring(1) :
String(s)));
}
/* *
*
* External
*
* */
// Register Highcharts as a plugin in jQuery
if (win.jQuery) {
/**
* Highcharts-extended JQuery.
*
* @external JQuery
*/
/**
* 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.
*/
win.jQuery.fn.highcharts = function () {
var 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')];
}
};
}
/* *
*
* Default Export
*
* */
// TODO use named exports when supported.
var Utilities = {
addEvent: addEvent,
arrayMax: arrayMax,
arrayMin: arrayMin,
attr: attr,
clamp: clamp,
clearTimeout: internalClearTimeout,
correctFloat: correctFloat,
createElement: createElement,
crisp: crisp,
css: css,
defined: defined,
destroyObjectProperties: destroyObjectProperties,
diffObjects: diffObjects,
discardElement: discardElement,
erase: erase,
error: error,
extend: extend,
extendClass: extendClass,
find: find,
fireEvent: fireEvent,
getAlignFactor: getAlignFactor,
getClosestDistance: getClosestDistance,
getMagnitude: getMagnitude,
getNestedProperty: getNestedProperty,
getStyle: getStyle,
insertItem: insertItem,
isArray: isArray,
isClass: isClass,
isDOMElement: isDOMElement,
isFunction: isFunction,
isNumber: isNumber,
isObject: isObject,
isString: isString,
merge: merge,
normalizeTickInterval: normalizeTickInterval,
objectEach: objectEach,
offset: offset,
pad: pad,
pick: pick,
pInt: pInt,
pushUnique: pushUnique,
relativeLength: relativeLength,
removeEvent: removeEvent,
replaceNested: replaceNested,
splat: splat,
stableSort: stableSort,
syncTimeout: syncTimeout,
timeUnits: timeUnits,
ucfirst: ucfirst,
uniqueKey: uniqueKey,
useSerialIds: useSerialIds,
wrap: wrap
};
/* harmony default export */ var Core_Utilities = (Utilities);
/* *
*
* API Declarations
*
* */
/**
* An animation configuration. Animation configurations can also be defined as
* booleans, where `false` turns off animation and `true` defaults to a duration
* of 500ms and defer of 0ms.
*
* @interface Highcharts.AnimationOptionsObject
*/ /**
* A callback function to execute when the animation finishes.
* @name Highcharts.AnimationOptionsObject#complete
* @type {Function|undefined}
*/ /**
* The animation defer in milliseconds.
* @name Highcharts.AnimationOptionsObject#defer
* @type {number|undefined}
*/ /**
* The animation duration in milliseconds.
* @name Highcharts.AnimationOptionsObject#duration
* @type {number|undefined}
*/ /**
* The name of an easing function as defined on the `Math` object.
* @name Highcharts.AnimationOptionsObject#easing
* @type {string|Function|undefined}
*/ /**
* A callback function to execute on each step of each attribute or CSS property
* that's being animated. The first argument contains information about the
* animation and progress.
* @name Highcharts.AnimationOptionsObject#step
* @type {Function|undefined}
*/
/**
* 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 {number|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 {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}
*/ /**
* Behaviour 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}
*/
/**
* 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.
*
* @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
*
* @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/es5/es-modules/Core/Chart/ChartDefaults.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* API Options
*
* */
/**
* General options for the chart.
*
* @optionparent chart
*/
var 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.
*
* @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`.
*
* @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.
*
* @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.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @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.shadow
*/
/**
* 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
*/
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`.
*
* @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).
*
* @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
*
* @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: {
/**
* The horizontal alignment of the button.
*/
align: 'right',
/**
* The horizontal offset of the button.
*/
x: -10,
/**
* The vertical alignment of the button.
*
* @type {Highcharts.VerticalAlignValue}
* @default top
* @apioption chart.zooming.resetButton.position.verticalAlign
*/
/**
* The vertical offset of the button.
*/
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.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
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.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
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.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @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.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
plotBorderColor: "#cccccc" /* Palette.neutralColor20 */
};
/* *
*
* Default Export
*
* */
/* harmony default export */ var Chart_ChartDefaults = (ChartDefaults);
;// ./code/es5/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.
*/
var SeriesPalettes = {
/**
* Colors for data series and points
*/
colors: [
'#2caffe',
'#544fc5',
'#00e272',
'#fe6a35',
'#6b8abc',
'#d568fb',
'#2ee0ca',
'#fa4b42',
'#feb56a',
'#91e8e1'
],
};
/* harmony default export */ var Palettes = (SeriesPalettes);
;// ./code/es5/es-modules/Core/Time.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var pageLang = Core_Globals.pageLang, Time_win = Core_Globals.win;
var Time_defined = Core_Utilities.defined, Time_error = Core_Utilities.error, Time_extend = Core_Utilities.extend, Time_isNumber = Core_Utilities.isNumber, Time_isObject = Core_Utilities.isObject, Time_isString = Core_Utilities.isString, Time_merge = Core_Utilities.merge, Time_objectEach = Core_Utilities.objectEach, Time_pad = Core_Utilities.pad, Time_splat = Core_Utilities.splat, Time_timeUnits = Core_Utilities.timeUnits, Time_ucfirst = Core_Utilities.ucfirst;
/* *
*
* Constants
*
* */
// To do: Remove this when we no longer need support for Safari < v14.1
var hasOldSafariBug = Core_Globals.isSafari &&
Time_win.Intl &&
!Time_win.Intl.DateTimeFormat.prototype.formatRange;
var isDateTimeFormatOptions = function (obj) {
return obj.main === void 0;
};
// We use the Spanish locale for internal weekday handling because it uses
// unique letters for narrow weekdays
var spanishWeekdayIndex = function (weekday) {
return ['D', 'L', 'M', 'X', 'J', 'V', 'S'].indexOf(weekday);
};
/* *
*
* 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).
*/
var Time = /** @class */ (function () {
/* *
*
* Constructors
*
* */
function Time(options) {
/* *
*
* Properties
*
* */
this.options = {
timezone: 'UTC'
};
this.variableTimezone = false;
this.Date = Time_win.Date;
this.update(options);
}
/* *
*
* Functions
*
* */
/**
* Update the Time object with current options. It is called internally on
* initializing Highcharts, after running `Highcharts.setOptions` and on
* `Chart.update`.
*
* @private
* @function Highcharts.Time#update
*
* @param {Highcharts.TimeOptions} [options]
*
*/
Time.prototype.update = function (options) {
var _this = this;
if (options === void 0) { options = {}; }
this.dTLCache = {};
this.options = options = Time_merge(true, this.options, options);
var timezoneOffset = options.timezoneOffset,
useUTC = options.useUTC;
// Allow using a different Date class
this.Date = options.Date || Time_win.Date || Date;
// Assign the time zone. Handle the legacy, deprecated `useUTC` option.
var timezone = options.timezone;
if (Time_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 === null || timezone === void 0 ? void 0 : timezone.indexOf('Etc/GMT')) !== 0;
this.timezone = timezone;
// Assign default time formats from locale strings
['months', 'shortMonths', 'weekdays', 'shortWeekdays'].forEach(function (name) {
var 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(function (position) { return _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.
*/
Time.prototype.toParts = function (timestamp) {
var _a = this.dateTimeFormat({
weekday: 'narrow',
day: 'numeric',
month: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
}, timestamp, 'es')
.split(/(?:, |\/|:)/g), weekday = _a[0], dayOfMonth = _a[1], month = _a[2], year = _a[3], hours = _a[4], minutes = _a[5], seconds = _a[6];
return [
year,
+month - 1,
dayOfMonth,
hours,
minutes,
seconds,
// Milliseconds
Math.floor(Number(timestamp) || 0) % 1000,
// Weekday index
spanishWeekdayIndex(weekday)
].map(Number);
};
/**
* Shorthand to get a cached `Intl.DateTimeFormat` instance.
*/
Time.prototype.dateTimeFormat = function (options, timestamp, locale) {
var _a;
if (locale === void 0) { locale = this.options.locale || pageLang; }
var cacheKey = JSON.stringify(options) + locale;
if (Time_isString(options)) {
options = this.str2dtf(options);
}
var dTL = this.dTLCache[cacheKey];
if (!dTL) {
(_a = options.timeZone) !== null && _a !== void 0 ? _a : (options.timeZone = this.timezone);
try {
dTL = new Intl.DateTimeFormat(locale, options);
}
catch (e) {
if (/Invalid time zone/i.test(e.message)) {
Time_error(34);
options.timeZone = 'UTC';
dTL = new Intl.DateTimeFormat(locale, options);
}
else {
Time_error(e.message, false);
}
}
}
this.dTLCache[cacheKey] = dTL;
return (dTL === null || dTL === void 0 ? void 0 : dTL.format(timestamp)) || '';
};
/**
* Take a locale-aware string format and return a full DateTimeFormat in
* object form.
*/
Time.prototype.str2dtf = function (s, dtf) {
if (dtf === void 0) { dtf = {}; }
var 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(function (key) {
if (s.indexOf(key) !== -1) {
Time_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.
*/
Time.prototype.makeTime = function (year, month, date, hours, minutes, seconds, milliseconds) {
if (date === void 0) { date = 1; }
if (hours === void 0) { hours = 0; }
// eslint-disable-next-line new-cap
var d = this.Date.UTC(year,
month,
date,
hours,
minutes || 0,
seconds || 0,
milliseconds || 0);
if (this.timezone !== 'UTC') {
var 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)) {
var 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
*/
Time.prototype.parse = function (s) {
if (!Time_isString(s)) {
return s !== null && s !== void 0 ? 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
var hasTimezone = s.indexOf('Z') > -1 ||
/([+-][0-9]{2}):?[0-9]{2}$/.test(s), isYYYYMMDD = /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(s);
if (!hasTimezone && !isYYYYMMDD) {
s += 'Z';
}
var ts = Date.parse(s);
if (Time_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.
*/
Time.prototype.getTimezoneOffset = function (timestamp) {
if (this.timezone !== 'UTC') {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
var _a = this.dateTimeFormat({ timeZoneName: 'shortOffset' }, timestamp, 'en')
.split(/(GMT|:)/)
.map(Number), date = _a[0], gmt = _a[1], hours = _a[2], colon = _a[3], _b = _a[4], minutes = _b === void 0 ? 0 : _b, offset = -(hours + minutes / 60) * 60 * 60000;
// Possible future NaNs stop here
if (Time_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 |
* | `%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 instered. |
* | `%k` | Hours in 24h format, 0 through 23 | Depending on the locale, 12h format may be instered. |
* | `%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.
*/
Time.prototype.dateFormat = function (format, timestamp, upperCaseFirst) {
var _a;
var lang = (_a = Core_Globals.defaultOptions) === null || _a === void 0 ? void 0 : _a.lang;
if (!Time_defined(timestamp) || isNaN(timestamp)) {
return (lang === null || lang === void 0 ? void 0 : lang.invalidDate) || '';
}
format = format !== null && format !== void 0 ? format : '%Y-%m-%d %H:%M:%S';
// First, identify and replace locale-aware formats like %[Ymd]
if (Time_isString(format)) {
var localeAwareRegex = /%\[([a-zA-Z]+)\]/g;
var match = void 0;
while ((match = localeAwareRegex.exec(format))) {
format = format.replace(match[0], this.dateTimeFormat(match[1], timestamp));
}
}
// Then, replace static formats like %Y, %m, %d etc.
if (Time_isString(format) && format.indexOf('%') !== -1) {
var time_1 = this,
_b = this.toParts(timestamp),
fullYear = _b[0],
month = _b[1],
dayOfMonth = _b[2],
hours = _b[3],
minutes = _b[4],
seconds = _b[5],
milliseconds = _b[6],
weekday = _b[7],
langWeekdays = (lang === null || lang === void 0 ? void 0 : lang.weekdays) || this.weekdays,
shortWeekdays = (lang === null || lang === void 0 ? void 0 : lang.shortWeekdays) || this.shortWeekdays,
months = (lang === null || lang === void 0 ? void 0 : lang.months) || this.months,
shortMonths = (lang === null || lang === void 0 ? void 0 : lang.shortMonths) || this.shortMonths,
// List all format keys. Custom formats can be added from the
// outside.
replacements = Time_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: Time_pad(dayOfMonth),
// Day of the month, 1 through 31
e: Time_pad(dayOfMonth, 2, ' '),
// Day of the week, 0 through 6
w: weekday,
// Week (none implemented)
// 'W': weekNumber(),
// Month
// Short month, like 'Jan'
b: shortMonths[month],
// Long month, like 'January'
B: months[month],
// Two digit month number, 01 through 12
m: Time_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: Time_pad(hours),
// Hours in 24h format, 0 through 23
k: hours,
// Two digits hours in 12h format, 00 through 11
I: Time_pad((hours % 12) || 12),
// Hours in 12h format, 1 through 12
l: (hours % 12) || 12,
// Two digits minutes, 00 through 59
M: Time_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: Time_pad(seconds),
// Milliseconds (naming from Ruby)
L: Time_pad(milliseconds, 3)
},
Core_Globals.dateFormats);
// Do the replaces
Time_objectEach(replacements, function (val, key) {
if (Time_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_1, timestamp) :
val);
}
}
});
}
else if (Time_isObject(format)) {
var tzHours = (this.getTimezoneOffset(timestamp) || 0) /
(60000 * 60), timeZone = this.timezone || ('Etc/GMT' + (tzHours >= 0 ? '+' : '') + tzHours), _c = format.prefix, prefix = _c === void 0 ? '' : _c, _d = format.suffix, suffix = _d === void 0 ? '' : _d;
format = prefix + this.dateTimeFormat(Time_extend({ timeZone: timeZone }, format), timestamp) + suffix;
}
// Optionally sentence-case the string and return
return upperCaseFirst ? Time_ucfirst(format) : format;
};
/**
* Resolve legacy formats of dateTimeLabelFormats (strings and arrays) into
* an object.
* @private
* @param {string|Array<T>|Highcharts.Dictionary<T>} f
* General format description
* @return {Highcharts.Dictionary<T>}
* The object definition
*/
Time.prototype.resolveDTLFormat = function (f) {
if (!Time_isObject(f, true)) { // Check for string or array
f = Time_splat(f);
return {
main: f[0],
from: f[1],
to: f[2]
};
}
// Type-check DateTimeFormatOptions against DateTimeLabelFormatObject
if (Time_isObject(f, true) && isDateTimeFormatOptions(f)) {
return { main: f };
}
return f;
};
/**
* 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
*/
Time.prototype.getTimeTicks = function (normalizedInterval, min, max, startOfWeek) {
var time = this,
tickPositions = [],
higherRanks = {},
_a = normalizedInterval.count,
count = _a === void 0 ? 1 : _a,
unitRange = normalizedInterval.unitRange;
var _b = time.toParts(min),
year = _b[0],
month = _b[1],
dayOfMonth = _b[2],
hours = _b[3],
minutes = _b[4],
seconds = _b[5],
milliseconds = (min || 0) % 1000,
variableDayLength;
startOfWeek !== null && startOfWeek !== void 0 ? startOfWeek : (startOfWeek = 1);
if (Time_defined(min)) { // #1300
milliseconds = unitRange >= Time_timeUnits.second ?
0 : // #3935
count * Math.floor(milliseconds / count);
if (unitRange >= Time_timeUnits.second) { // Second
seconds = unitRange >= Time_timeUnits.minute ?
0 : // #3935
count * Math.floor(seconds / count);
}
if (unitRange >= Time_timeUnits.minute) { // Minute
minutes = unitRange >= Time_timeUnits.hour ?
0 :
count * Math.floor(minutes / count);
}
if (unitRange >= Time_timeUnits.hour) { // Hour
hours = unitRange >= Time_timeUnits.day ?
0 :
count * Math.floor(hours / count);
}
if (unitRange >= Time_timeUnits.day) { // Day
dayOfMonth = unitRange >= Time_timeUnits.month ?
1 :
Math.max(1, count * Math.floor(dayOfMonth / count));
}
if (unitRange >= Time_timeUnits.month) { // Month
month = unitRange >= Time_timeUnits.year ? 0 :
count * Math.floor(month / count);
}
if (unitRange >= Time_timeUnits.year) { // Year
year -= year % count;
}
// Week is a special case that runs outside the hierarchy
if (unitRange === Time_timeUnits.week) {
if (count) {
min = time.makeTime(year, month, dayOfMonth, hours, minutes, seconds, milliseconds);
}
// Get start of current week, independent of count
var weekday = this.dateTimeFormat({
timeZone: this.timezone,
weekday: 'narrow'
},
min, 'es'),
weekdayNo = spanishWeekdayIndex(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 && Time_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 * Time_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
var t = min,
i = 1;
while (t < max) {
tickPositions.push(t);
// Increase the years
if (unitRange === Time_timeUnits.year) {
t = time.makeTime(year + i * count, 0);
// Increase the months
}
else if (unitRange === Time_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 === Time_timeUnits.day ||
unitRange === Time_timeUnits.week)) {
t = time.makeTime(year, month, dayOfMonth +
i * count * (unitRange === Time_timeUnits.day ? 1 : 7));
}
else if (variableDayLength &&
unitRange === Time_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 <= Time_timeUnits.hour && tickPositions.length < 10000) {
tickPositions.forEach(function (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 = Time_extend(normalizedInterval, {
higherRanks: higherRanks,
totalRange: unitRange * count
});
return tickPositions;
};
/**
* Get the optimal date format for a point, based on a range.
*
* @private
* @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.
*/
Time.prototype.getDateFormat = function (range, timestamp, startOfWeek, dateTimeLabelFormats) {
var 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
};
var n = 'millisecond',
// For sub-millisecond data, #4223
lastN = n;
for (n in Time_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 === Time_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 (Time_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;
};
return Time;
}());
/* *
*
* Default export
*
* */
/* harmony default export */ var Core_Time = (Time);
/* *
*
* 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 {number|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/es5/es-modules/Core/Defaults.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var isTouchDevice = Core_Globals.isTouchDevice;
var Defaults_fireEvent = Core_Utilities.fireEvent, Defaults_merge = Core_Utilities.merge;
/* *
*
* API Options
*
* */
/**
* Global default settings.
*
* @name Highcharts.defaultOptions
* @type {Highcharts.Options}
*/ /**
* @optionparent
* @private
*/
var 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.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject)>}
* @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.
*
* ```js
* Highcharts.setOptions({
* lang: {
* locale: 'fr'
* }
* });
* ```
*/
lang: {
/**
* 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` setting.
*
* @type {Array<string>}
*/
months: void 0,
/**
* 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` setting.
*
* @type {Array<string>}
*/
shortMonths: void 0,
/**
* An array containing the weekday names. Defaults to 'undefined',
* meaning the default weekday names are used according to the
* `lang.locale` setting.
*
* @type {Array<string>}
*/
weekdays: void 0,
/**
* Short week days, starting Sunday. Defaults to 'undefined', meaning
* the default short weekday names are used according to the
* `lang.locale` setting.
*
* @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 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 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, like
* the `lang` 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: {
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: {
fill: "#e6e9ff" /* Palette.highlightColor10 */,
style: {
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 [time.timezone](#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: {
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: {
/**
* 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
*/
/**
* 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"}
* @apioption subtitle.style
*/
/**
* 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: {
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 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
*/
},
/**
* 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: {
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.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @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.
*
* @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
*
* @private
*/
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/layout-horizontal/
* 50px item distance
* @sample {highstock} highcharts/legend/layout-horizontal/
* 50px 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.
*
* @type {string}
* @default {name}
* @since 1.3
* @apioption legend.labelFormat
*/
/* eslint-disable valid-jsdoc */
/**
* 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.
*
* @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<Point|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 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.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
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.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @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.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @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
*/
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.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @apioption tooltip.borderColor
*/
/**
* A CSS class name to apply to the tooltip's container div,
* allowing unique CSS styling for each chart.
*
* @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 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
*
* @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).
*
* @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.
*
* @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 three parameters: `labelWidth`, `labelHeight` and
* `point`, where point contains values for `plotX` and `plotY` telling
* where the reference point is in the plot area. Add `chart.plotLeft`
* and `chart.plotTop` to get the full coordinates.
*
* 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 }`
*
* 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
*
* @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
*/
/**
* Prevents the tooltip from switching or closing, when touched or
* pointed.
*
* @sample highcharts/tooltip/stickoncontact/
* Tooltip sticks on pointer contact
*
* @type {boolean}
* @since 8.0.1
* @apioption tooltip.stickOnContact
*/
/**
* 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
*
* @type {boolean}
* @default false
* @since 2.2
* @apioption tooltip.useHTML
*/
/**
* 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: function (x) { return 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: {
/** @internal */
millisecond: '%[AebHMSL]',
/** @internal */
second: '%[AebHMS]',
/** @internal */
minute: '%[AebHM]',
/** @internal */
hour: '%[AebHM]',
/** @internal */
day: '%[AebY]',
/** @internal */
week: 'Week from %[AebY]',
/** @internal */
month: '%[BY]',
/** @internal */
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,
/**
* Padding inside the tooltip, in pixels.
*
* @since 5.0.0
*/
padding: 8,
/**
* 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).
*
* @type {Highcharts.TooltipShapeValue}
* @since 4.0
*/
shape: 'callout',
/**
* 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`.
*
* @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/plotoptions/series-nullformat
* Format data label and tooltip for null point.
*
* @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.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
backgroundColor: "#ffffff" /* Palette.backgroundColor */,
/**
* The pixel width of the tooltip border. Defaults to 0 for single
* tooltips and 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.
*
* @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}
*/
shadow: true,
/**
* Prevents the tooltip from switching or closing when touched or
* pointed.
*
* @sample highcharts/tooltip/stickoncontact/
* Tooltip sticks on pointer contact
*
* @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: {
/** @internal */
color: "#333333" /* Palette.neutralColor80 */,
/** @internal */
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
},
/**
* Highchart 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
*/
/**
* 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 {highcharts} highcharts/credits/position-left/
* Left aligned
* @sample {highmaps} maps/credits/customized/
* Left aligned
* @sample {highmaps} maps/credits/customized/
* Left aligned
*
* @type {Highcharts.AlignObject}
* @since 2.1
*/
position: {
/** @internal */
align: 'right',
/** @internal */
x: -10,
/** @internal */
verticalAlign: 'bottom',
/** @internal */
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: {
/** @internal */
cursor: 'pointer',
/** @internal */
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'
}
};
var defaultTime = new Core_Time(defaultOptions.time);
/**
* 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 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 setOptions(options) {
Defaults_fireEvent(Core_Globals, 'setOptions', { options: options });
// Copy in the default options
Defaults_merge(true, defaultOptions, options);
// Update the time object
if (options.time) {
defaultTime.update(defaultOptions.time);
}
if (options.lang && 'locale' in options.lang) {
defaultTime.update({
locale: options.lang.locale
});
}
return defaultOptions;
}
/* *
*
* Default Export
*
* */
var DefaultOptions = {
defaultOptions: defaultOptions,
defaultTime: defaultTime,
getOptions: getOptions,
setOptions: setOptions
};
/* harmony default export */ var 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 behaviour 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 informations
*
* @return {boolean|undefined}
* Return false to prevent the default action, usually zoom.
*/
(''); // Detach doclets above
;// ./code/es5/es-modules/Core/Color/Color.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var Color_isNumber = Core_Utilities.isNumber, Color_merge = Core_Utilities.merge, Color_pInt = Core_Utilities.pInt, Color_defined = Core_Utilities.defined;
/* *
*
* Class
*
* */
/* eslint-disable valid-jsdoc */
/**
* Handle color operations. Some object methods are chainable.
*
* @class
* @name Highcharts.Color
*
* @param {Highcharts.ColorType} input
* The input color in either rgba or hex format
*/
var Color = /** @class */ (function () {
/* *
*
* Constructor
*
* */
function Color(input) {
this.rgba = [NaN, NaN, NaN, NaN];
this.input = input;
var GlobalColor = Core_Globals.Color;
// Backwards compatibility, allow class overwrite
if (GlobalColor && GlobalColor !== Color) {
return new GlobalColor(input);
}
var result,
rgba,
i,
parser;
// Gradients
if (typeof input === 'object' &&
typeof input.stops !== 'undefined') {
this.stops = input.stops.map(function (stop) { return new Color(stop[1]); });
// Solid colors
}
else if (typeof input === 'string') {
this.input = input = (Color.names[input.toLowerCase()] || input);
i = Color.parsers.length;
while (i-- && !rgba) {
parser = Color.parsers[i];
result = parser.regex.exec(input);
if (result) {
rgba = parser.parse(result);
}
}
}
if (rgba) {
this.rgba = rgba;
}
}
/* *
*
* Static Functions
*
* */
/**
* Creates a color instance out of a color string or object.
*
* @function Highcharts.Color.parse
*
* @param {Highcharts.ColorType} [input]
* The input color in either rgba or hex format.
*
* @return {Highcharts.Color}
* Color instance.
*/
Color.parse = function (input) {
return input ? new Color(input) : Color.None;
};
/* *
*
* Functions
*
* */
/**
* Return the color or gradient stops in the specified format
*
* @function Highcharts.Color#get
*
* @param {string} [format]
* Possible values are 'a', 'rgb', 'rgba' (default).
*
* @return {Highcharts.ColorType}
* This color as a string or gradient stops.
*/
Color.prototype.get = function (format) {
var input = this.input,
rgba = this.rgba;
if (typeof input === 'object' &&
typeof this.stops !== 'undefined') {
var ret_1 = Color_merge(input);
ret_1.stops = [].slice.call(ret_1.stops);
this.stops.forEach(function (stop, i) {
ret_1.stops[i] = [
ret_1.stops[i][0],
stop.get(format)
];
});
return ret_1;
}
// It's NaN if gradient colors on a column chart
if (rgba && Color_isNumber(rgba[0])) {
if (format === 'rgb' || (!format && rgba[3] === 1)) {
return 'rgb(' + rgba[0] + ',' + rgba[1] + ',' + rgba[2] + ')';
}
if (format === 'a') {
return "".concat(rgba[3]);
}
return 'rgba(' + rgba.join(',') + ')';
}
return input;
};
/**
* Brighten the color instance.
*
* @function Highcharts.Color#brighten
*
* @param {number} alpha
* The alpha value.
*
* @return {Highcharts.Color}
* This color with modifications.
*/
Color.prototype.brighten = function (alpha) {
var rgba = this.rgba;
if (this.stops) {
this.stops.forEach(function (stop) {
stop.brighten(alpha);
});
}
else if (Color_isNumber(alpha) && alpha !== 0) {
for (var i = 0; i < 3; i++) {
rgba[i] += Color_pInt(alpha * 255);
if (rgba[i] < 0) {
rgba[i] = 0;
}
if (rgba[i] > 255) {
rgba[i] = 255;
}
}
}
return this;
};
/**
* Set the color's opacity to a given alpha value.
*
* @function Highcharts.Color#setOpacity
*
* @param {number} alpha
* Opacity between 0 and 1.
*
* @return {Highcharts.Color}
* Color with modifications.
*/
Color.prototype.setOpacity = function (alpha) {
this.rgba[3] = alpha;
return this;
};
/**
* Return an intermediate color between two colors.
*
* @function Highcharts.Color#tweenTo
*
* @param {Highcharts.Color} to
* The color object to tween to.
*
* @param {number} pos
* The intermediate position, where 0 is the from color (current color
* item), and 1 is the `to` color.
*
* @return {Highcharts.ColorType}
* The intermediate color in rgba notation, or unsupported type.
*/
Color.prototype.tweenTo = function (to, pos) {
var fromRgba = this.rgba,
toRgba = to.rgba;
// Unsupported color, return to-color (#3920, #7034)
if (!Color_isNumber(fromRgba[0]) || !Color_isNumber(toRgba[0])) {
return to.input || 'none';
}
// Check for has alpha, because rgba colors perform worse due to
// lack of support in WebKit.
var hasAlpha = (toRgba[3] !== 1 || fromRgba[3] !== 1),
channel = function (to,
i) {
return to + (fromRgba[i] - to) * (1 - pos);
}, rgba = toRgba.slice(0, 3).map(channel).map(Math.round);
if (hasAlpha) {
rgba.push(channel(toRgba[3], 3));
}
return (hasAlpha ? 'rgba(' : 'rgb(') + rgba.join(',') + ')';
};
/* *
*
* Static Properties
*
* */
/**
* Collection of named colors. Can be extended from the outside by adding
* colors to Highcharts.Color.names.
* @private
*/
Color.names = {
white: '#ffffff',
black: '#000000'
};
/**
* Collection of parsers. This can be extended from the outside by pushing
* parsers to `Color.parsers`.
* @private
*/
Color.parsers = [{
// RGBA color
// eslint-disable-next-line max-len
regex: /rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d?(?:\.\d+)?)\s*\)/,
parse: function (result) {
return [
Color_pInt(result[1]),
Color_pInt(result[2]),
Color_pInt(result[3]),
parseFloat(result[4], 10)
];
}
}, {
// RGB color
regex: /rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/,
parse: function (result) {
return [Color_pInt(result[1]), Color_pInt(result[2]), Color_pInt(result[3]), 1];
}
}, {
// RGBA 3 & 4 digit hex color, e.g. #F0F, #F0FA
regex: /^#([a-f0-9])([a-f0-9])([a-f0-9])([a-f0-9])?$/i,
parse: function (result) {
// #abcd => #aabbccdd, hence result + result.
return [
(Color_pInt(result[1] + result[1], 16)),
(Color_pInt(result[2] + result[2], 16)),
(Color_pInt(result[3] + result[3], 16)),
!Color_defined(result[4]) ?
1 :
(Color_pInt(result[4] + result[4], 16) / 255)
];
}
}, {
// RGBA 6 & 8 digit hex color, e.g. #FFCC00, #FFCC00FF
regex: /^#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})?$/i,
parse: function (result) {
return [
Color_pInt(result[1], 16),
Color_pInt(result[2], 16),
Color_pInt(result[3], 16),
!Color_defined(result[4]) ?
1 :
(Color_pInt(result[4], 16) / 255)
];
}
}];
// Must be last static member for init cycle
Color.None = new Color('');
return Color;
}());
/* *
*
* Default Export
*
* */
/* harmony default export */ var Color_Color = (Color);
/* *
*
* API Declarations
*
* */
/**
* A valid color to be parsed and handled by Highcharts. Highcharts internally
* supports hex colors like `#ffffff`, rgb colors like `rgb(255,255,255)` and
* rgba colors like `rgba(255,255,255,1)`. Other colors may be supported by the
* browsers and displayed correctly, but Highcharts is not able to process them
* and apply concepts like opacity and brightening.
*
* @typedef {string} Highcharts.ColorString
*/
/**
* A valid color type than can be parsed and handled by Highcharts. It can be a
* color string, a gradient object, or a pattern object.
*
* @typedef {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject} Highcharts.ColorType
*/
/**
* Gradient options instead of a solid color.
*
* @example
* // Linear gradient used as a color option
* color: {
* linearGradient: { x1: 0, x2: 0, y1: 0, y2: 1 },
* stops: [
* [0, '#003399'], // start
* [0.5, '#ffffff'], // middle
* [1, '#3366AA'] // end
* ]
* }
*
* @interface Highcharts.GradientColorObject
*/ /**
* Holds an object that defines the start position and the end position relative
* to the shape.
* @name Highcharts.GradientColorObject#linearGradient
* @type {Highcharts.LinearGradientColorObject|undefined}
*/ /**
* Holds an object that defines the center position and the radius.
* @name Highcharts.GradientColorObject#radialGradient
* @type {Highcharts.RadialGradientColorObject|undefined}
*/ /**
* The first item in each tuple is the position in the gradient, where 0 is the
* start of the gradient and 1 is the end of the gradient. Multiple stops can be
* applied. The second item is the color for each stop. This color can also be
* given in the rgba format.
* @name Highcharts.GradientColorObject#stops
* @type {Array<Highcharts.GradientColorStopObject>}
*/
/**
* Color stop tuple.
*
* @see Highcharts.GradientColorObject
*
* @interface Highcharts.GradientColorStopObject
*/ /**
* @name Highcharts.GradientColorStopObject#0
* @type {number}
*/ /**
* @name Highcharts.GradientColorStopObject#1
* @type {Highcharts.ColorString}
*/ /**
* @name Highcharts.GradientColorStopObject#color
* @type {Highcharts.Color|undefined}
*/
/**
* Defines the start position and the end position for a gradient relative
* to the shape. Start position (x1, y1) and end position (x2, y2) are relative
* to the shape, where 0 means top/left and 1 is bottom/right.
*
* @interface Highcharts.LinearGradientColorObject
*/ /**
* Start horizontal position of the gradient. Float ranges 0-1.
* @name Highcharts.LinearGradientColorObject#x1
* @type {number}
*/ /**
* End horizontal position of the gradient. Float ranges 0-1.
* @name Highcharts.LinearGradientColorObject#x2
* @type {number}
*/ /**
* Start vertical position of the gradient. Float ranges 0-1.
* @name Highcharts.LinearGradientColorObject#y1
* @type {number}
*/ /**
* End vertical position of the gradient. Float ranges 0-1.
* @name Highcharts.LinearGradientColorObject#y2
* @type {number}
*/
/**
* Defines the center position and the radius for a gradient.
*
* @interface Highcharts.RadialGradientColorObject
*/ /**
* Center horizontal position relative to the shape. Float ranges 0-1.
* @name Highcharts.RadialGradientColorObject#cx
* @type {number}
*/ /**
* Center vertical position relative to the shape. Float ranges 0-1.
* @name Highcharts.RadialGradientColorObject#cy
* @type {number}
*/ /**
* Radius relative to the shape. Float ranges 0-1.
* @name Highcharts.RadialGradientColorObject#r
* @type {number}
*/
/**
* Creates a color instance out of a color string.
*
* @function Highcharts.color
*
* @param {Highcharts.ColorType} input
* The input color in either rgba or hex format
*
* @return {Highcharts.Color}
* Color instance
*/
(''); // Detach doclets above
;// ./code/es5/es-modules/Core/Animation/Fx.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var color = Color_Color.parse;
var Fx_win = Core_Globals.win;
var Fx_isNumber = Core_Utilities.isNumber, Fx_objectEach = Core_Utilities.objectEach;
/* eslint-disable no-invalid-this, valid-jsdoc */
/* *
*
* Class
*
* */
/**
* An animator object used internally. One instance applies to one property
* (attribute or style prop) on one element. Animation is always initiated
* through {@link SVGElement#animate}.
*
* @example
* let rect = renderer.rect(0, 0, 10, 10).add();
* rect.animate({ width: 100 });
*
* @private
* @class
* @name Highcharts.Fx
*
* @param {Highcharts.HTMLDOMElement|Highcharts.SVGElement} elem
* The element to animate.
*
* @param {Partial<Highcharts.AnimationOptionsObject>} options
* Animation options.
*
* @param {string} prop
* The single attribute or CSS property to animate.
*/
var Fx = /** @class */ (function () {
/* *
*
* Constructors
*
* */
function Fx(elem, options, prop) {
this.pos = NaN;
this.options = options;
this.elem = elem;
this.prop = prop;
}
/* *
*
* Functions
*
* */
/**
* Set the current step of a path definition on SVGElement.
*
* @function Highcharts.Fx#dSetter
*
*/
Fx.prototype.dSetter = function () {
var paths = this.paths,
start = paths && paths[0],
end = paths && paths[1],
now = this.now || 0;
var path = [];
// Land on the final path without adjustment points appended in the ends
if (now === 1 || !start || !end) {
path = this.toD || [];
}
else if (start.length === end.length && now < 1) {
for (var i = 0; i < end.length; i++) {
// Tween between the start segment and the end segment. Start
// with a copy of the end segment and tween the appropriate
// numerics
var startSeg = start[i];
var endSeg = end[i];
var tweenSeg = [];
for (var j = 0; j < endSeg.length; j++) {
var startItem = startSeg[j];
var endItem = endSeg[j];
// Tween numbers
if (Fx_isNumber(startItem) &&
Fx_isNumber(endItem) &&
// Arc boolean flags
!(endSeg[0] === 'A' && (j === 4 || j === 5))) {
tweenSeg[j] = startItem + now * (endItem - startItem);
// Strings, take directly from the end segment
}
else {
tweenSeg[j] = endItem;
}
}
path.push(tweenSeg);
}
// If animation is finished or length not matching, land on right value
}
else {
path = end;
}
this.elem.attr('d', path, void 0, true);
};
/**
* Update the element with the current animation step.
*
* @function Highcharts.Fx#update
*
*/
Fx.prototype.update = function () {
var elem = this.elem,
prop = this.prop, // If destroyed, it is null
now = this.now,
step = this.options.step;
// Animation setter defined from outside
if (this[prop + 'Setter']) {
this[prop + 'Setter']();
// Other animations on SVGElement
}
else if (elem.attr) {
if (elem.element) {
elem.attr(prop, now, null, true);
}
// HTML styles, raw HTML content like container size
}
else {
elem.style[prop] = now + this.unit;
}
if (step) {
step.call(elem, now, this);
}
};
/**
* Run an animation.
*
* @function Highcharts.Fx#run
*
* @param {number} from
* The current value, value to start from.
*
* @param {number} to
* The end value, value to land on.
*
* @param {string} unit
* The property unit, for example `px`.
*
*/
Fx.prototype.run = function (from, to, unit) {
var self = this,
options = self.options,
timer = function (gotoEnd) {
return timer.stopped ? false : self.step(gotoEnd);
}, requestAnimationFrame = Fx_win.requestAnimationFrame ||
function (step) {
setTimeout(step, 13);
}, step = function () {
for (var i = 0; i < Fx.timers.length; i++) {
if (!Fx.timers[i]()) {
Fx.timers.splice(i--, 1);
}
}
if (Fx.timers.length) {
requestAnimationFrame(step);
}
};
if (from === to && !this.elem['forceAnimate:' + this.prop]) {
delete options.curAnim[this.prop];
if (options.complete &&
Object.keys(options.curAnim).length === 0) {
options.complete.call(this.elem);
}
}
else { // #7166
this.startTime = +new Date();
this.start = from;
this.end = to;
this.unit = unit;
this.now = this.start;
this.pos = 0;
timer.elem = this.elem;
timer.prop = this.prop;
if (timer() && Fx.timers.push(timer) === 1) {
requestAnimationFrame(step);
}
}
};
/**
* Run a single step in the animation.
*
* @function Highcharts.Fx#step
*
* @param {boolean} [gotoEnd]
* Whether to go to the endpoint of the animation after abort.
*
* @return {boolean}
* Returns `true` if animation continues.
*/
Fx.prototype.step = function (gotoEnd) {
var t = +new Date(),
options = this.options,
elem = this.elem,
complete = options.complete,
duration = options.duration,
curAnim = options.curAnim;
var ret,
done;
if (!!elem.attr && !elem.element) { // #2616, element is destroyed
ret = false;
}
else if (gotoEnd || t >= duration + this.startTime) {
this.now = this.end;
this.pos = 1;
this.update();
curAnim[this.prop] = true;
done = true;
Fx_objectEach(curAnim, function (val) {
if (val !== true) {
done = false;
}
});
if (done && complete) {
complete.call(elem);
}
ret = false;
}
else {
this.pos = options.easing((t - this.startTime) / duration);
this.now = this.start + ((this.end -
this.start) * this.pos);
this.update();
ret = true;
}
return ret;
};
/**
* Prepare start and end values so that the path can be animated one to one.
*
* @function Highcharts.Fx#initPath
*
* @param {Highcharts.SVGElement} elem
* The SVGElement item.
*
* @param {Highcharts.SVGPathArray|undefined} fromD
* Starting path definition.
*
* @param {Highcharts.SVGPathArray} toD
* Ending path definition.
*
* @return {Array<Highcharts.SVGPathArray,Highcharts.SVGPathArray>}
* An array containing start and end paths in array form so that
* they can be animated in parallel.
*/
Fx.prototype.initPath = function (elem, fromD, toD) {
var startX = elem.startX,
endX = elem.endX,
end = toD.slice(), // Copy
isArea = elem.isArea,
positionFactor = isArea ? 2 : 1,
disableAnimation = fromD &&
toD.length > fromD.length &&
toD.hasStackedCliffs; // #16925
var shift,
fullLength,
i,
reverse,
start = fromD && fromD.slice(); // Copy
if (!start || disableAnimation) {
return [end,
end];
}
/**
* If shifting points, prepend a dummy point to the end path.
* @private
*/
function prepend(arr, other) {
while (arr.length < fullLength) {
// Move to, line to or curve to?
var moveSegment = arr[0],
otherSegment = other[fullLength - arr.length];
if (otherSegment && moveSegment[0] === 'M') {
if (otherSegment[0] === 'C') {
arr[0] = [
'C',
moveSegment[1],
moveSegment[2],
moveSegment[1],
moveSegment[2],
moveSegment[1],
moveSegment[2]
];
}
else {
arr[0] = ['L', moveSegment[1], moveSegment[2]];
}
}
// Prepend a copy of the first point
arr.unshift(moveSegment);
// For areas, the bottom path goes back again to the left, so we
// need to append a copy of the last point.
if (isArea) {
var z = arr.pop();
arr.push(arr[arr.length - 1], z); // Append point and the Z
}
}
}
/**
* Copy and append last point until the length matches the end length.
* @private
*/
function append(arr) {
while (arr.length < fullLength) {
// Pull out the slice that is going to be appended or inserted.
// In a line graph, the positionFactor is 1, and the last point
// is sliced out. In an area graph, the positionFactor is 2,
// causing the middle two points to be sliced out, since an area
// path starts at left, follows the upper path then turns and
// follows the bottom back.
var segmentToAdd = arr[Math.floor(arr.length / positionFactor) - 1].slice();
// Disable the first control point of curve segments
if (segmentToAdd[0] === 'C') {
segmentToAdd[1] = segmentToAdd[5];
segmentToAdd[2] = segmentToAdd[6];
}
if (!isArea) {
arr.push(segmentToAdd);
}
else {
var lowerSegmentToAdd = arr[Math.floor(arr.length / positionFactor)].slice();
arr.splice(arr.length / 2, 0, segmentToAdd, lowerSegmentToAdd);
}
}
}
// For sideways animation, find out how much we need to shift to get the
// start path Xs to match the end path Xs.
if (startX && endX && endX.length) {
for (i = 0; i < startX.length; i++) {
// Moving left, new points coming in on right
if (startX[i] === endX[0]) {
shift = i;
break;
// Moving right
}
else if (startX[0] ===
endX[endX.length - startX.length + i]) {
shift = i;
reverse = true;
break;
// Fixed from the right side, "scaling" left
}
else if (startX[startX.length - 1] ===
endX[endX.length - startX.length + i]) {
shift = startX.length - i;
break;
}
}
if (typeof shift === 'undefined') {
start = [];
}
}
if (start.length && Fx_isNumber(shift)) {
// The common target length for the start and end array, where both
// arrays are padded in opposite ends
fullLength = end.length + shift * positionFactor;
if (!reverse) {
prepend(end, start);
append(start);
}
else {
prepend(start, end);
append(end);
}
}
return [start, end];
};
/**
* Handle animation of the color attributes directly.
*
* @function Highcharts.Fx#fillSetter
*
*/
Fx.prototype.fillSetter = function () {
Fx.prototype.strokeSetter.apply(this, arguments);
};
/**
* Handle animation of the color attributes directly.
*
* @function Highcharts.Fx#strokeSetter
*
*/
Fx.prototype.strokeSetter = function () {
this.elem.attr(this.prop, color(this.start).tweenTo(color(this.end), this.pos), void 0, true);
};
/* *
*
* Static Properties
*
* */
Fx.timers = [];
return Fx;
}());
/* *
*
* Default Export
*
* */
/* harmony default export */ var Animation_Fx = (Fx);
;// ./code/es5/es-modules/Core/Animation/AnimationUtilities.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var AnimationUtilities_defined = Core_Utilities.defined, AnimationUtilities_getStyle = Core_Utilities.getStyle, AnimationUtilities_isArray = Core_Utilities.isArray, AnimationUtilities_isNumber = Core_Utilities.isNumber, AnimationUtilities_isObject = Core_Utilities.isObject, AnimationUtilities_merge = Core_Utilities.merge, AnimationUtilities_objectEach = Core_Utilities.objectEach, AnimationUtilities_pick = Core_Utilities.pick;
/* *
*
* Functions
*
* */
/**
* Set the global animation to either a given value, or fall back to the given
* chart's animation option.
*
* @function Highcharts.setAnimation
*
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>|undefined} animation
* The animation object.
*
* @param {Highcharts.Chart} chart
* The chart instance.
*
* @todo
* This function always relates to a chart, and sets a property on the renderer,
* so it should be moved to the SVGRenderer.
*/
function setAnimation(animation, chart) {
chart.renderer.globalAnimation = AnimationUtilities_pick(animation, chart.options.chart.animation, true);
}
/**
* Get the animation in object form, where a disabled animation is always
* returned as `{ duration: 0 }`.
*
* @function Highcharts.animObject
*
* @param {boolean|Highcharts.AnimationOptionsObject} [animation=0]
* An animation setting. Can be an object with duration, complete and
* easing properties, or a boolean to enable or disable.
*
* @return {Highcharts.AnimationOptionsObject}
* An object with at least a duration property.
*/
function animObject(animation) {
return AnimationUtilities_isObject(animation) ?
AnimationUtilities_merge({ duration: 500, defer: 0 }, animation) :
{ duration: animation ? 500 : 0, defer: 0 };
}
/**
* Get the defer as a number value from series animation options.
*
* @function Highcharts.getDeferredAnimation
*
* @param {Highcharts.Chart} chart
* The chart instance.
*
* @param {boolean|Highcharts.AnimationOptionsObject} animation
* An animation setting. Can be an object with duration, complete and
* easing properties, or a boolean to enable or disable.
*
* @param {Highcharts.Series} [series]
* Series to defer animation.
*
* @return {number}
* The numeric value.
*/
function getDeferredAnimation(chart, animation, series) {
var labelAnimation = animObject(animation),
s = series ? [series] : chart.series;
var defer = 0,
duration = 0;
s.forEach(function (series) {
var seriesAnim = animObject(series.options.animation);
defer = AnimationUtilities_isObject(animation) && AnimationUtilities_defined(animation.defer) ?
labelAnimation.defer :
Math.max(defer, seriesAnim.duration + seriesAnim.defer);
duration = Math.min(labelAnimation.duration, seriesAnim.duration);
});
// Disable defer for exporting
if (chart.renderer.forExport) {
defer = 0;
}
var anim = {
defer: Math.max(0,
defer - duration),
duration: Math.min(defer,
duration)
};
return anim;
}
/**
* The global animate method, which uses Fx to create individual animators.
*
* @function Highcharts.animate
*
* @param {Highcharts.HTMLDOMElement|Highcharts.SVGElement} el
* The element to animate.
*
* @param {Highcharts.CSSObject|Highcharts.SVGAttributes} params
* An object containing key-value pairs of the properties to animate.
* Supports numeric as pixel-based CSS properties for HTML objects and
* attributes for SVGElements.
*
* @param {Partial<Highcharts.AnimationOptionsObject>} [opt]
* Animation options.
*
* @return {void}
*/
function animate(el, params, opt) {
var start,
unit = '',
end,
fx,
args;
if (!AnimationUtilities_isObject(opt)) { // Number or undefined/null
args = arguments;
opt = {
duration: args[2],
easing: args[3],
complete: args[4]
};
}
if (!AnimationUtilities_isNumber(opt.duration)) {
opt.duration = 400;
}
opt.easing = typeof opt.easing === 'function' ?
opt.easing :
(Math[opt.easing] || Math.easeInOutSine);
opt.curAnim = AnimationUtilities_merge(params);
AnimationUtilities_objectEach(params, function (val, prop) {
// Stop current running animation of this property
stop(el, prop);
fx = new Animation_Fx(el, opt, prop);
end = void 0;
if (prop === 'd' && AnimationUtilities_isArray(params.d)) {
fx.paths = fx.initPath(el, el.pathArray, params.d);
fx.toD = params.d;
start = 0;
end = 1;
}
else if (el.attr) {
start = el.attr(prop);
}
else {
start = parseFloat(AnimationUtilities_getStyle(el, prop)) || 0;
if (prop !== 'opacity') {
unit = 'px';
}
}
if (!end) {
end = val;
}
if (typeof end === 'string' && end.match('px')) {
end = end.replace(/px/g, ''); // #4351
}
fx.run(start, end, unit);
});
}
/**
* Stop running animation.
*
* @function Highcharts.stop
*
* @param {Highcharts.SVGElement} el
* The SVGElement to stop animation on.
*
* @param {string} [prop]
* The property to stop animating. If given, the stop method will stop a
* single property from animating, while others continue.
*
* @return {void}
*
* @todo
* A possible extension to this would be to stop a single property, when
* we want to continue animating others. Then assign the prop to the timer
* in the Fx.run method, and check for the prop here. This would be an
* improvement in all cases where we stop the animation from .attr. Instead of
* stopping everything, we can just stop the actual attributes we're setting.
*/
function stop(el, prop) {
var i = Animation_Fx.timers.length;
// Remove timers related to this element (#4519)
while (i--) {
if (Animation_Fx.timers[i].elem === el && (!prop || prop === Animation_Fx.timers[i].prop)) {
Animation_Fx.timers[i].stopped = true; // #4667
}
}
}
var animationExports = {
animate: animate,
animObject: animObject,
getDeferredAnimation: getDeferredAnimation,
setAnimation: setAnimation,
stop: stop
};
/* *
*
* Default Export
*
* */
/* harmony default export */ var AnimationUtilities = (animationExports);
;// ./code/es5/es-modules/Core/Renderer/HTML/AST.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var SVG_NS = Core_Globals.SVG_NS, AST_win = Core_Globals.win;
var AST_attr = Core_Utilities.attr, AST_createElement = Core_Utilities.createElement, AST_css = Core_Utilities.css, AST_error = Core_Utilities.error, AST_isFunction = Core_Utilities.isFunction, AST_isString = Core_Utilities.isString, AST_objectEach = Core_Utilities.objectEach, AST_splat = Core_Utilities.splat;
var trustedTypes = AST_win.trustedTypes;
/* *
*
* Constants
*
* */
// Create the trusted type policy. This should not be exposed.
var trustedTypesPolicy = (trustedTypes &&
AST_isFunction(trustedTypes.createPolicy) &&
trustedTypes.createPolicy('highcharts', {
createHTML: function (s) { return s; }
}));
var 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.
*/
var AST = /** @class */ (function () {
/* *
*
* Constructor
*
* */
// Construct an AST from HTML markup, or wrap an array of existing AST nodes
function AST(source) {
this.nodes = typeof source === 'string' ?
this.parseMarkup(source) : source;
}
/* *
*
* 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
*/
AST.filterUserAttributes = function (attributes) {
AST_objectEach(attributes, function (val, key) {
var valid = true;
if (AST.allowedAttributes.indexOf(key) === -1) {
valid = false;
}
if (['background', 'dynsrc', 'href', 'lowsrc', 'src']
.indexOf(key) !== -1) {
valid = AST_isString(val) && AST.allowedReferences.some(function (ref) { return val.indexOf(ref) === 0; });
}
if (!valid) {
AST_error(33, false, void 0, {
'Invalid attribute in config': "".concat(key)
});
delete attributes[key];
}
// #17753, < is not allowed in SVG attributes
if (AST_isString(val) && attributes[key]) {
attributes[key] = val.replace(/</g, '<');
}
});
return attributes;
};
AST.parseStyle = function (style) {
return style
.split(';')
.reduce(function (styles, line) {
var pair = line.split(':').map(function (s) { return s.trim(); }),
key = pair.shift();
if (key && pair.length) {
styles[key.replace(/-([a-z])/g, function (g) { return 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
*/
AST.setElementHTML = function (el, html) {
el.innerHTML = AST.emptyHTML; // Clear previous
if (html) {
var ast = new AST(html);
ast.addToDOM(el);
}
};
/* *
*
* 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.
*/
AST.prototype.addToDOM = function (parent) {
/**
* @private
* @param {Highcharts.ASTNode} subtree
* HTML/SVG definition
* @param {Element} [subParent]
* parent node
* @return {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement}
* The inserted node.
*/
function recurse(subtree, subParent) {
var ret;
AST_splat(subtree).forEach(function (item) {
var tagName = item.tagName;
var textNode = item.textContent ?
Core_Globals.doc.createTextNode(item.textContent) :
void 0;
// Whether to ignore the AST filtering totally, #15345
var bypassHTMLFiltering = AST.bypassHTMLFiltering;
var node;
if (tagName) {
if (tagName === '#text') {
node = textNode;
}
else if (AST.allowedTags.indexOf(tagName) !== -1 ||
bypassHTMLFiltering) {
var NS = tagName === 'svg' ?
SVG_NS :
(subParent.namespaceURI || SVG_NS);
var element = Core_Globals.doc.createElementNS(NS,
tagName);
var attributes_1 = item.attributes || {};
// Apply attributes from root of AST node, legacy from
// from before TextBuilder
AST_objectEach(item, function (val, key) {
if (key !== 'tagName' &&
key !== 'attributes' &&
key !== 'children' &&
key !== 'style' &&
key !== 'textContent') {
attributes_1[key] = val;
}
});
AST_attr(element, bypassHTMLFiltering ?
attributes_1 :
AST.filterUserAttributes(attributes_1));
if (item.style) {
AST_css(element, item.style);
}
// Add text content
if (textNode) {
element.appendChild(textNode);
}
// Recurse
recurse(item.children || [], element);
node = element;
}
else {
AST_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.
*
* @private
*
* @function Highcharts.AST#getNodesFromMarkup
*
* @param {string} markup The markup string.
*
* @return {Array<Highcharts.ASTNode>} The parsed nodes.
*/
AST.prototype.parseMarkup = function (markup) {
var 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');
var doc;
try {
doc = new DOMParser().parseFromString(trustedTypesPolicy ?
trustedTypesPolicy.createHTML(markup) :
markup, 'text/html');
}
catch (e) {
// 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) {
var body = AST_createElement('div');
body.innerHTML = markup;
doc = { body: body };
}
var appendChildNodes = function (node,
addTo) {
var tagName = node.nodeName.toLowerCase();
// Add allowed tags
var astNode = {
tagName: tagName
};
if (tagName === '#text') {
astNode.textContent = node.textContent || '';
}
var parsedAttributes = node.attributes;
// Add attributes
if (parsedAttributes) {
var attributes_2 = {};
[].forEach.call(parsedAttributes, function (attrib) {
if (attrib.name === 'data-style') {
astNode.style = AST.parseStyle(attrib.value);
}
else {
attributes_2[attrib.name] = attrib.value;
}
});
astNode.attributes = attributes_2;
}
// Handle children
if (node.childNodes.length) {
var children_1 = [];
[].forEach.call(node.childNodes, function (childNode) {
appendChildNodes(childNode, children_1);
});
if (children_1.length) {
astNode.children = children_1;
}
}
addTo.push(astNode);
};
[].forEach.call(doc.body.childNodes, function (childNode) { return 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',
'class',
'clip-path',
'color',
'colspan',
'cx',
'cy',
'd',
'dx',
'dy',
'disabled',
'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',
'role',
'scope',
'slope',
'src',
'startOffset',
'stdDeviation',
'stroke',
'stroke-linecap',
'stroke-width',
'style',
'tableValues',
'result',
'rowspan',
'summary',
'target',
'tabindex',
'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 = [
'a',
'abbr',
'b',
'br',
'button',
'caption',
'circle',
'clipPath',
'code',
'dd',
'defs',
'div',
'dl',
'dt',
'em',
'feComponentTransfer',
'feComposite',
'feDropShadow',
'feFlood',
'feFuncA',
'feFuncB',
'feFuncG',
'feFuncR',
'feGaussianBlur',
'feMorphology',
'feOffset',
'feMerge',
'feMergeNode',
'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',
'text',
'textPath',
'thead',
'title',
'tbody',
'tspan',
'td',
'th',
'tr',
'u',
'ul',
'#text'
];
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;
return AST;
}());
/* *
*
* Default Export
*
* */
/* harmony default export */ var 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/es5/es-modules/Core/Templating.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var Templating_defaultOptions = Defaults.defaultOptions, Templating_defaultTime = Defaults.defaultTime;
var Templating_pageLang = Core_Globals.pageLang;
var Templating_extend = Core_Utilities.extend, Templating_getNestedProperty = Core_Utilities.getNestedProperty, Templating_isArray = Core_Utilities.isArray, Templating_isNumber = Core_Utilities.isNumber, Templating_isObject = Core_Utilities.isObject, Templating_isString = Core_Utilities.isString, Templating_pick = Core_Utilities.pick, Templating_ucfirst = Core_Utilities.ucfirst;
var helpers = {
// Built-in helpers
add: function (a, b) { return a + b; },
divide: function (a, b) { return (b !== 0 ? a / b : ''); },
// eslint-disable-next-line eqeqeq
eq: function (a, b) { return a == b; },
each: function (arr) {
var match = arguments[arguments.length - 1];
return Templating_isArray(arr) ?
arr.map(function (item, i) { return format(match.body, Templating_extend(Templating_isObject(item) ? item : { '@this': item }, {
'@index': i,
'@first': i === 0,
'@last': i === arr.length - 1
})); }).join('') :
false;
},
ge: function (a, b) { return a >= b; },
gt: function (a, b) { return a > b; },
'if': function (condition) { return !!condition; },
le: function (a, b) { return a <= b; },
lt: function (a, b) { return a < b; },
multiply: function (a, b) { return a * b; },
// eslint-disable-next-line eqeqeq
ne: function (a, b) { return a != b; },
subtract: function (a, b) { return a - b; },
ucfirst: Templating_ucfirst,
unless: function (condition) { return !condition; }
};
var numberFormatCache = {};
/* *
*
* Functions
*
* */
// Internal convenience function
var isQuotedString = function (str) { return /^["'].+["']$/.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} [chart]
* A `Chart` instance used to get numberFormatter and time.
*
* @return {string}
* The formatted string.
*/
function format(str, ctx, chart) {
if (str === void 0) { str = ''; }
var regex = /\{([\p{L}\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.
subRegex = /\(([\p{L}\d:\.,;\-\/<>\[\]%_@+"'= ]+)\)/gu, matches = [], floatRegex = /f$/, decRegex = /\.(\d)/, lang = (chart === null || chart === void 0 ? void 0 : chart.options.lang) || Templating_defaultOptions.lang, time = chart && chart.time || Templating_defaultTime, numberFormatter = chart && chart.numberFormatter || numberFormat;
/*
* 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.
*/
var resolveProperty = function (key) {
if (key === void 0) { key = ''; }
var 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 Templating_getNestedProperty(key, ctx);
};
var 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.
var mainMatch = match,
subMatch = subRegex.exec(match[1]);
if (subMatch) {
match = subMatch;
hasSub = true;
}
if (!currentMatch || !currentMatch.isBlock) {
currentMatch = {
ctx: 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
var 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
var startingElseSection = match[1] === 'else';
if (currentMatch.isBlock &&
currentMatch.fn && (match[1] === "/".concat(currentMatch.fn) ||
startingElseSection)) {
if (!depth) { // === 0
var 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 === null || currentMatch === void 0 ? void 0 : currentMatch.isBlock)) {
break;
}
}
// Execute
matches.forEach(function (match) {
var body = match.body,
elseBody = match.elseBody,
expression = match.expression,
fn = match.fn;
var replacement,
i;
// Helper function
if (fn) {
// Pass the helpers the amount of arguments defined by the function,
// then the match as the last argument.
var args = [match],
parts = [],
len = expression.length;
var start = 0,
startChar = void 0;
for (i = 0; i <= len; i++) {
var 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, chart);
}
// Simple variable replacement
}
else {
var valueAndFormat = isQuotedString(expression) ?
[expression] : expression.split(':');
replacement = resolveProperty(valueAndFormat.shift() || '');
// Format the replacement
if (valueAndFormat.length && typeof replacement === 'number') {
var segment = valueAndFormat.join(':');
if (floatRegex.test(segment)) { // Float
var 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) && Templating_isString(replacement)) {
replacement = "\"".concat(replacement, "\"");
}
}
str = str.replace(match.find, Templating_pick(replacement, ''));
});
return hasSub ? format(str, ctx, chart) : 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) {
var _a,
_b;
number = +number || 0;
decimals = +decimals;
var ret,
fractionDigits,
_c = number.toString().split('e').map(Number),
mantissa = _c[0],
exp = _c[1];
var lang = ((_a = this === null || this === void 0 ? void 0 : this.options) === null || _a === void 0 ? void 0 : _a.lang) || Templating_defaultOptions.lang,
origDec = (number.toString().split('.')[1] || '').split('e')[0].length,
firstDecimals = decimals,
options = {};
decimalPoint !== null && decimalPoint !== void 0 ? decimalPoint : (decimalPoint = lang.decimalPoint);
thousandsSep !== null && thousandsSep !== void 0 ? thousandsSep : (thousandsSep = lang.thousandsSep);
if (decimals === -1) {
// Preserve decimals. Not huge numbers (#3793).
decimals = Math.min(origDec, 20);
}
else if (!Templating_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 !== null && decimals !== void 0 ? decimals : (decimals = 2);
number = mantissa;
}
if (Templating_isNumber(decimals) && decimals >= 0) {
options.minimumFractionDigits = decimals;
options.maximumFractionDigits = decimals;
}
if (thousandsSep === '') {
options.useGrouping = false;
}
var hasSeparators = thousandsSep || decimalPoint,
locale = hasSeparators ?
'en' :
((this === null || this === void 0 ? void 0 : this.locale) || lang.locale || Templating_pageLang),
cacheKey = JSON.stringify(options) + locale,
nf = (_b = numberFormatCache[cacheKey]) !== null && _b !== void 0 ? _b : (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 !== null && thousandsSep !== void 0 ? thousandsSep : ',')
.replace('_.', decimalPoint !== null && decimalPoint !== void 0 ? 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
*
* */
var Templating = {
dateFormat: dateFormat,
format: format,
helpers: helpers,
numberFormat: numberFormat
};
/* harmony default export */ var Core_Templating = (Templating);
;// ./code/es5/es-modules/Core/Renderer/RendererRegistry.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* Namespace
*
* */
var RendererRegistry;
(function (RendererRegistry) {
/* *
*
* Constants
*
* */
RendererRegistry.rendererTypes = {};
/* *
*
* Variables
*
* */
var defaultRenderer;
/* *
*
* Functions
*
* */
/**
* Gets a registered renderer class. If no renderer type is provided or the
* requested renderer was not founded, the default renderer is returned.
*
* @param {string} [rendererType]
* Renderer type or the default renderer.
*
* @return {Highcharts.Class<Highcharts.SVGRenderer>}
* Returns the requested renderer class or the default renderer class.
*/
function getRendererType(rendererType) {
if (rendererType === void 0) { rendererType = defaultRenderer; }
return (RendererRegistry.rendererTypes[rendererType] || RendererRegistry.rendererTypes[defaultRenderer]);
}
RendererRegistry.getRendererType = getRendererType;
/**
* Register a renderer class.
*
* @param {string} rendererType
* Renderer type to register.
*
* @param {Highcharts.Class<Highcharts.SVGRenderer>} rendererClass
* Returns the requested renderer class or the default renderer class.
*
* @param {boolean} setAsDefault
* Sets the renderer class as the default renderer.
*/
function registerRendererType(rendererType, rendererClass, setAsDefault) {
RendererRegistry.rendererTypes[rendererType] = rendererClass;
if (!defaultRenderer || setAsDefault) {
defaultRenderer = rendererType;
Core_Globals.Renderer = rendererClass; // Compatibility
}
}
RendererRegistry.registerRendererType = registerRendererType;
})(RendererRegistry || (RendererRegistry = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Renderer_RendererRegistry = (RendererRegistry);
;// ./code/es5/es-modules/Core/Renderer/RendererUtilities.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* Imports
*
* */
var RendererUtilities_clamp = Core_Utilities.clamp, RendererUtilities_pick = Core_Utilities.pick, RendererUtilities_pushUnique = Core_Utilities.pushUnique, RendererUtilities_stableSort = Core_Utilities.stableSort;
/* *
*
* Namespace
*
* */
var RendererUtilities;
(function (RendererUtilities) {
/* *
*
* Declarations
*
* */
/* *
*
* Functions
*
* */
/* eslint-disable valid-jsdoc */
/**
* General distribution algorithm for distributing labels of differing size
* along a confined length in two dimensions. The algorithm takes an array
* of objects containing a size, a target and a rank. It will place the
* labels as close as possible to their targets, skipping the lowest ranked
* labels if necessary.
* @private
*/
function distribute(boxes, len, maxDistance) {
// Original array will be altered with added .pos
var origBoxes = boxes,
reducedLen = origBoxes.reducedLen || len,
sortByRank = function (a,
b) {
return (b.rank || 0) - (a.rank || 0);
}, sortByTarget = function (a, b) {
return a.target - b.target;
}, restBoxes = [], // The outranked overshoot
boxesLength = boxes.length, forDeletion = [], push = restBoxes.push;
var i,
cursor,
step,
overlapping = true,
box,
target,
total = 0,
equalRank;
// If the total size exceeds the len, remove those boxes with the lowest
// rank
i = boxesLength;
while (i--) {
total += boxes[i].size;
}
// Sort by rank, then slice away overshoot
if (total > reducedLen) {
RendererUtilities_stableSort(boxes, sortByRank);
equalRank = boxes[0].rank === boxes[boxes.length - 1].rank;
step = equalRank ? boxesLength / 2 : -1;
cursor = equalRank ? step : boxesLength - 1;
// When the boxes have equal rank (pie data labels, flags - #10073),
// decimate the boxes by starting in the middle and gradually remove
// more items inside the array. When they are sorted by rank, just
// remove the ones with the lowest rank from the end.
while (step && total > reducedLen) {
i = Math.floor(cursor);
box = boxes[i];
if (RendererUtilities_pushUnique(forDeletion, i)) {
total -= box.size;
}
cursor += step;
// Start over the decimation with smaller steps
if (equalRank && cursor >= boxes.length) {
step /= 2;
cursor = step;
}
}
// Clean out the boxes marked for deletion
forDeletion
.sort(function (a, b) { return b - a; })
.forEach(function (i) {
return push.apply(restBoxes, boxes.splice(i, 1));
});
}
// Order by target
RendererUtilities_stableSort(boxes, sortByTarget);
// So far we have been mutating the original array. Now
// create a copy with target arrays
boxes = boxes.map(function (box) { return ({
size: box.size,
targets: [box.target],
align: RendererUtilities_pick(box.align, 0.5)
}); });
while (overlapping) {
// Initial positions: target centered in box
i = boxes.length;
while (i--) {
box = boxes[i];
// Composite box, average of targets
target = (Math.min.apply(0, box.targets) +
Math.max.apply(0, box.targets)) / 2;
box.pos = RendererUtilities_clamp(target - box.size * box.align, 0, len - box.size);
}
// Detect overlap and join boxes
i = boxes.length;
overlapping = false;
while (i--) {
// Overlap
if (i > 0 &&
boxes[i - 1].pos + boxes[i - 1].size >
boxes[i].pos) {
// Add this size to the previous box
boxes[i - 1].size += boxes[i].size;
boxes[i - 1].targets = boxes[i - 1]
.targets
.concat(boxes[i].targets);
boxes[i - 1].align = 0.5;
// Overlapping right, push left
if (boxes[i - 1].pos + boxes[i - 1].size > len) {
boxes[i - 1].pos = len - boxes[i - 1].size;
}
boxes.splice(i, 1); // Remove this item
overlapping = true;
}
}
}
// Add the rest (hidden boxes)
push.apply(origBoxes, restBoxes);
// Now the composite boxes are placed, we need to put the original boxes
// within them
i = 0;
boxes.some(function (box) {
var posInCompositeBox = 0;
// Exceeded maxDistance => abort
return (box.targets || []).some(function () {
origBoxes[i].pos = box.pos + posInCompositeBox;
// If the distance between the position and the target exceeds
// maxDistance, abort the loop and decrease the length in
// increments of 10% to recursively reduce the number of
// visible boxes by rank. Once all boxes are within the
// maxDistance, we're good.
if (typeof maxDistance !== 'undefined' &&
Math.abs(origBoxes[i].pos - origBoxes[i].target) > maxDistance) {
// Reset the positions that are already set
origBoxes
.slice(0, i + 1)
.forEach(function (box) { return delete box.pos; });
// Try with a smaller length
origBoxes.reducedLen =
(origBoxes.reducedLen || len) - (len * 0.1);
// Recurse
if (origBoxes.reducedLen > len * 0.1) {
distribute(origBoxes, len, maxDistance);
}
// Exceeded maxDistance => abort
return true;
}
posInCompositeBox += origBoxes[i].size;
i++;
return false;
});
});
// Add the rest (hidden) boxes and sort by target
RendererUtilities_stableSort(origBoxes, sortByTarget);
return origBoxes;
}
RendererUtilities.distribute = distribute;
})(RendererUtilities || (RendererUtilities = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Renderer_RendererUtilities = (RendererUtilities);
;// ./code/es5/es-modules/Core/Renderer/SVG/SVGElement.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var SVGElement_animate = AnimationUtilities.animate, SVGElement_animObject = AnimationUtilities.animObject, SVGElement_stop = AnimationUtilities.stop;
var deg2rad = Core_Globals.deg2rad, SVGElement_doc = Core_Globals.doc, svg = Core_Globals.svg, SVGElement_SVG_NS = Core_Globals.SVG_NS, SVGElement_win = Core_Globals.win, isFirefox = Core_Globals.isFirefox;
var SVGElement_addEvent = Core_Utilities.addEvent, SVGElement_attr = Core_Utilities.attr, SVGElement_createElement = Core_Utilities.createElement, SVGElement_crisp = Core_Utilities.crisp, SVGElement_css = Core_Utilities.css, SVGElement_defined = Core_Utilities.defined, SVGElement_erase = Core_Utilities.erase, SVGElement_extend = Core_Utilities.extend, SVGElement_fireEvent = Core_Utilities.fireEvent, SVGElement_getAlignFactor = Core_Utilities.getAlignFactor, SVGElement_isArray = Core_Utilities.isArray, SVGElement_isFunction = Core_Utilities.isFunction, SVGElement_isNumber = Core_Utilities.isNumber, SVGElement_isObject = Core_Utilities.isObject, SVGElement_isString = Core_Utilities.isString, SVGElement_merge = Core_Utilities.merge, SVGElement_objectEach = Core_Utilities.objectEach, SVGElement_pick = Core_Utilities.pick, SVGElement_pInt = Core_Utilities.pInt, SVGElement_pushUnique = Core_Utilities.pushUnique, SVGElement_replaceNested = Core_Utilities.replaceNested, SVGElement_syncTimeout = Core_Utilities.syncTimeout, SVGElement_uniqueKey = Core_Utilities.uniqueKey;
/* *
*
* Class
*
* */
/* eslint-disable no-invalid-this, valid-jsdoc */
/**
* The SVGElement prototype is a JavaScript wrapper for SVG elements used in the
* rendering layer of Highcharts. Combined with the
* {@link Highcharts.SVGRenderer}
* object, these prototypes allow freeform annotation in the charts or even in
* HTML pages without instanciating a chart. The SVGElement can also wrap HTML
* labels, when `text` or `label` elements are created with the `useHTML`
* parameter.
*
* The SVGElement instances are created through factory functions on the
* {@link Highcharts.SVGRenderer}
* object, like
* {@link Highcharts.SVGRenderer#rect|rect},
* {@link Highcharts.SVGRenderer#path|path},
* {@link Highcharts.SVGRenderer#text|text},
* {@link Highcharts.SVGRenderer#label|label},
* {@link Highcharts.SVGRenderer#g|g}
* and more.
*
* See [How to use the SVG Renderer](
* https://www.highcharts.com/docs/advanced-chart-features/renderer) for a
* comprehensive tutorial on how to draw SVG elements on a chart.
*
* @class
* @name Highcharts.SVGElement
*/
var SVGElement = /** @class */ (function () {
/**
* Initialize the SVG element. This function only exists to make the
* initialization process overridable. It should not be called directly.
*
* @function Highcharts.SVGElement#init
*
* @param {Highcharts.SVGRenderer} renderer
* The SVGRenderer instance to initialize to.
*
* @param {string} nodeName
* The SVG node name.
*/
function SVGElement(renderer, nodeName) {
this.onEvents = {};
this.opacity = 1; // Default base for animation
this.SVG_NS = SVGElement_SVG_NS;
/**
* The primary DOM node. Each `SVGElement` instance wraps a main DOM
* node, but may also represent more nodes.
*
* @name Highcharts.SVGElement#element
* @type {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement}
*/
this.element = nodeName === 'span' || nodeName === 'body' ?
SVGElement_createElement(nodeName) :
SVGElement_doc.createElementNS(this.SVG_NS, nodeName);
/**
* The renderer that the SVGElement belongs to.
*
* @name Highcharts.SVGElement#renderer
* @type {Highcharts.SVGRenderer}
*/
this.renderer = renderer;
this.styles = {};
SVGElement_fireEvent(this, 'afterInit');
}
// @todo public zIndex?: number;
/* *
*
* Functions
*
* */
/**
* Get the current value of an attribute or pseudo attribute,
* used mainly for animation. Called internally from
* the {@link Highcharts.SVGRenderer#attr} function.
*
* @private
* @function Highcharts.SVGElement#_defaultGetter
*
* @param {string} key
* Property key.
*
* @return {number|string}
* Property value.
*/
SVGElement.prototype._defaultGetter = function (key) {
var ret = SVGElement_pick(this[key + 'Value'], // Align getter
this[key],
this.element ? this.element.getAttribute(key) : null, 0);
if (/^-?[\d\.]+$/.test(ret)) { // Is numerical
ret = parseFloat(ret);
}
return ret;
};
/**
* @private
* @function Highcharts.SVGElement#_defaultSetter
*
* @param {string} value
*
* @param {string} key
*
* @param {Highcharts.SVGDOMElement} element
*
*/
SVGElement.prototype._defaultSetter = function (value, key, element) {
element.setAttribute(key, value);
};
/**
* Add the element to the DOM. All elements must be added this way.
*
* @sample highcharts/members/renderer-g
* Elements added to a group
*
* @function Highcharts.SVGElement#add
*
* @param {Highcharts.SVGElement} [parent]
* The parent item to add it to. If undefined, the element is added
* to the {@link Highcharts.SVGRenderer.box}.
*
* @return {Highcharts.SVGElement}
* Returns the SVGElement for chaining.
*/
SVGElement.prototype.add = function (parent) {
var renderer = this.renderer,
element = this.element;
var inserted;
if (parent) {
this.parentGroup = parent;
}
// Build formatted text
if (typeof this.textStr !== 'undefined' &&
this.element.nodeName === 'text' // Not for SVGLabel instances
) {
renderer.buildText(this);
}
// Mark as added
this.added = true;
// If we're adding to renderer root, or other elements in the group
// have a z index, we need to handle it
if (!parent || parent.handleZ || this.zIndex) {
inserted = this.zIndexSetter();
}
// If zIndex is not handled, append at the end
if (!inserted) {
(parent ?
parent.element :
renderer.box).appendChild(element);
}
// Fire an event for internal hooks
if (this.onAdd) {
this.onAdd();
}
return this;
};
/**
* Add a class name to an element.
*
* @function Highcharts.SVGElement#addClass
*
* @param {string} className
* The new class name to add.
*
* @param {boolean} [replace=false]
* When true, the existing class name(s) will be overwritten with the new
* one. When false, the new one is added.
*
* @return {Highcharts.SVGElement}
* Return the SVG element for chainability.
*/
SVGElement.prototype.addClass = function (className, replace) {
var currentClassName = replace ? '' : (this.attr('class') || '');
// Trim the string and remove duplicates
className = (className || '')
.split(/ /g)
.reduce(function (newClassName, name) {
if (currentClassName.indexOf(name) === -1) {
newClassName.push(name);
}
return newClassName;
}, (currentClassName ?
[currentClassName] :
[]))
.join(' ');
if (className !== currentClassName) {
this.attr('class', className);
}
return this;
};
/**
* This method is executed in the end of `attr()`, after setting all
* attributes in the hash. In can be used to efficiently consolidate
* multiple attributes in one SVG property -- e.g., translate, rotate and
* scale are merged in one "transform" attribute in the SVG node.
*
* @private
* @function Highcharts.SVGElement#afterSetters
*/
SVGElement.prototype.afterSetters = function () {
// Update transform. Do this outside the loop to prevent redundant
// updating for batch setting of attributes.
if (this.doTransform) {
this.updateTransform();
this.doTransform = false;
}
};
/**
* Align the element relative to the chart or another box.
*
* @function Highcharts.SVGElement#align
*
* @param {Highcharts.AlignObject} [alignOptions]
* The alignment options. The function can be called without this
* parameter in order to re-align an element after the box has been
* updated.
*
* @param {boolean} [alignByTranslate]
* Align element by translation.
*
* @param {string|Highcharts.BBoxObject} [alignTo]
* The box to align to, needs a width and height. When the box is a
* string, it refers to an object in the Renderer. For example, when
* box is `spacingBox`, it refers to `Renderer.spacingBox` which
* holds `width`, `height`, `x` and `y` properties.
*
* @param {boolean} [redraw]
* Decide if SVGElement should be redrawn with new alignment or
* just change its attributes.
*
* @return {Highcharts.SVGElement} Returns the SVGElement for chaining.
*/
SVGElement.prototype.align = function (alignOptions, alignByTranslate, alignTo, redraw) {
if (redraw === void 0) { redraw = true; }
var renderer = this.renderer,
alignedObjects = renderer.alignedObjects,
initialAlignment = Boolean(alignOptions);
// First call on instanciate
if (alignOptions) {
this.alignOptions = alignOptions;
this.alignByTranslate = alignByTranslate;
this.alignTo = alignTo;
// When called on resize, no arguments are supplied
}
else {
alignOptions = this.alignOptions || {};
alignByTranslate = this.alignByTranslate;
alignTo = this.alignTo;
}
var alignToKey = !alignTo || SVGElement_isString(alignTo) ?
alignTo || 'renderer' :
void 0;
// When aligned to a key, automatically re-align on redraws
if (alignToKey) {
// Prevent duplicates, like legendGroup after resize
if (initialAlignment) {
SVGElement_pushUnique(alignedObjects, this);
}
alignTo = void 0; // Do not use the box
}
var alignToBox = SVGElement_pick(alignTo,
renderer[alignToKey],
renderer),
// Default: left align
x = (alignToBox.x || 0) + (alignOptions.x || 0) +
((alignToBox.width || 0) - (alignOptions.width || 0)) *
SVGElement_getAlignFactor(alignOptions.align),
// Default: top align
y = (alignToBox.y || 0) + (alignOptions.y || 0) +
((alignToBox.height || 0) - (alignOptions.height || 0)) *
SVGElement_getAlignFactor(alignOptions.verticalAlign),
attribs = {
'text-align': alignOptions === null || alignOptions === void 0 ? void 0 : alignOptions.align
};
attribs[alignByTranslate ? 'translateX' : 'x'] = Math.round(x);
attribs[alignByTranslate ? 'translateY' : 'y'] = Math.round(y);
// Animate only if already placed
if (redraw) {
this[this.placed ? 'animate' : 'attr'](attribs);
this.placed = true;
}
this.alignAttr = attribs;
return this;
};
/**
* @private
* @function Highcharts.SVGElement#alignSetter
* @param {"left"|"center"|"right"} value
*/
SVGElement.prototype.alignSetter = function (value) {
var convert = {
left: 'start',
center: 'middle',
right: 'end'
};
if (convert[value]) {
this.alignValue = value;
this.element.setAttribute('text-anchor', convert[value]);
}
};
/**
* Animate to given attributes or CSS properties.
*
* @sample highcharts/members/element-on/
* Setting some attributes by animation
*
* @function Highcharts.SVGElement#animate
*
* @param {Highcharts.SVGAttributes} params
* SVG attributes or CSS to animate.
*
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [options]
* Animation options.
*
* @param {Function} [complete]
* Function to perform at the end of animation.
*
* @return {Highcharts.SVGElement}
* Returns the SVGElement for chaining.
*/
SVGElement.prototype.animate = function (params, options, complete) {
var _this = this;
var animOptions = SVGElement_animObject(SVGElement_pick(options,
this.renderer.globalAnimation,
true)),
deferTime = animOptions.defer;
// When the page is hidden save resources in the background by not
// running animation at all (#9749).
if (SVGElement_doc.hidden) {
animOptions.duration = 0;
}
if (animOptions.duration !== 0) {
// Allows using a callback with the global animation without
// overwriting it
if (complete) {
animOptions.complete = complete;
}
// If defer option is defined delay the animation #12901
SVGElement_syncTimeout(function () {
if (_this.element) {
SVGElement_animate(_this, params, animOptions);
}
}, deferTime);
}
else {
this.attr(params, void 0, complete || animOptions.complete);
// Call the end step synchronously
SVGElement_objectEach(params, function (val, prop) {
if (animOptions.step) {
animOptions.step.call(this, val, { prop: prop, pos: 1, elem: this });
}
}, this);
}
return this;
};
/**
* Apply a text outline through a custom CSS property, by copying the text
* element and apply stroke to the copy. Used internally. Contrast checks at
* [example](https://jsfiddle.net/highcharts/43soe9m1/2/).
*
* @example
* // Specific color
* text.css({
* textOutline: '1px black'
* });
* // Automatic contrast
* text.css({
* color: '#000000', // black text
* textOutline: '1px contrast' // => white outline
* });
*
* @private
* @function Highcharts.SVGElement#applyTextOutline
*
* @param {string} textOutline
* A custom CSS `text-outline` setting, defined by `width color`.
*/
SVGElement.prototype.applyTextOutline = function (textOutline) {
var elem = this.element,
hasContrast = textOutline.indexOf('contrast') !== -1,
styles = {};
// When the text shadow is set to contrast, use dark stroke for light
// text and vice versa.
if (hasContrast) {
styles.textOutline = textOutline = textOutline.replace(/contrast/g, this.renderer.getContrast(elem.style.fill));
}
// Extract the stroke width and color
var parts = textOutline.split(' ');
var color = parts[parts.length - 1];
var strokeWidth = parts[0];
if (strokeWidth && strokeWidth !== 'none' && Core_Globals.svg) {
this.fakeTS = true; // Fake text shadow
// Since the stroke is applied on center of the actual outline, we
// need to double it to get the correct stroke-width outside the
// glyphs.
strokeWidth = strokeWidth.replace(/(^[\d\.]+)(.*?)$/g, function (match, digit, unit) {
return (2 * Number(digit)) + unit;
});
// Remove shadows from previous runs.
this.removeTextOutline();
var outline_1 = SVGElement_doc.createElementNS(SVGElement_SVG_NS, 'tspan');
SVGElement_attr(outline_1, {
'class': 'highcharts-text-outline',
fill: color,
stroke: color,
'stroke-width': strokeWidth,
'stroke-linejoin': 'round'
});
// For each of the tspans and text nodes, create a copy in the
// outline.
var parentElem = elem.querySelector('textPath') || elem;
[].forEach.call(parentElem.childNodes, function (childNode) {
var clone = childNode.cloneNode(true);
if (clone.removeAttribute) {
['fill', 'stroke', 'stroke-width', 'stroke'].forEach(function (prop) { return clone
.removeAttribute(prop); });
}
outline_1.appendChild(clone);
});
// Collect the sum of dy from all children, included nested ones
var totalHeight_1 = 0;
[].forEach.call(parentElem.querySelectorAll('text tspan'), function (element) {
totalHeight_1 += Number(element.getAttribute('dy'));
});
// Insert an absolutely positioned break before the original text
// to keep it in place
var br = SVGElement_doc.createElementNS(SVGElement_SVG_NS, 'tspan');
br.textContent = '\u200B';
// Reset the position for the following text
SVGElement_attr(br, {
x: Number(elem.getAttribute('x')),
dy: -totalHeight_1
});
// Insert the outline
outline_1.appendChild(br);
parentElem.insertBefore(outline_1, parentElem.firstChild);
}
};
/**
* @function Highcharts.SVGElement#attr
* @param {string} key
* @return {number|string}
*/ /**
* Apply native and custom attributes to the SVG elements.
*
* In order to set the rotation center for rotation, set x and y to 0 and
* use `translateX` and `translateY` attributes to position the element
* instead.
*
* Attributes frequently used in Highcharts are `fill`, `stroke`,
* `stroke-width`.
*
* @sample highcharts/members/renderer-rect/
* Setting some attributes
*
* @example
* // Set multiple attributes
* element.attr({
* stroke: 'red',
* fill: 'blue',
* x: 10,
* y: 10
* });
*
* // Set a single attribute
* element.attr('stroke', 'red');
*
* // Get an attribute
* element.attr('stroke'); // => 'red'
*
* @function Highcharts.SVGElement#attr
*
* @param {string|Highcharts.SVGAttributes} [hash]
* The native and custom SVG attributes.
*
* @param {number|string|Highcharts.SVGPathArray} [val]
* If the type of the first argument is `string`, the second can be a
* value, which will serve as a single attribute setter. If the first
* argument is a string and the second is undefined, the function
* serves as a getter and the current value of the property is
* returned.
*
* @param {Function} [complete]
* A callback function to execute after setting the attributes. This
* makes the function compliant and interchangeable with the
* {@link SVGElement#animate} function.
*
* @param {boolean} [continueAnimation=true]
* Used internally when `.attr` is called as part of an animation
* step. Otherwise, calling `.attr` for an attribute will stop
* animation for that attribute.
*
* @return {Highcharts.SVGElement}
* If used as a setter, it returns the current
* {@link Highcharts.SVGElement} so the calls can be chained. If
* used as a getter, the current value of the attribute is returned.
*/
SVGElement.prototype.attr = function (hash, val, complete, continueAnimation) {
var element = this.element,
symbolCustomAttribs = SVGElement.symbolCustomAttribs;
var key,
hasSetSymbolSize,
ret = this,
skipAttr,
setter;
// Single key-value pair
if (typeof hash === 'string' && typeof val !== 'undefined') {
key = hash;
hash = {};
hash[key] = val;
}
// Used as a getter: first argument is a string, second is undefined
if (typeof hash === 'string') {
ret = (this[hash + 'Getter'] ||
this._defaultGetter).call(this, hash, element);
// Setter
}
else {
SVGElement_objectEach(hash, function eachAttribute(val, key) {
skipAttr = false;
// Unless .attr is from the animator update, stop current
// running animation of this property
if (!continueAnimation) {
SVGElement_stop(this, key);
}
// Special handling of symbol attributes
if (this.symbolName &&
symbolCustomAttribs.indexOf(key) !== -1) {
if (!hasSetSymbolSize) {
this.symbolAttr(hash);
hasSetSymbolSize = true;
}
skipAttr = true;
}
if (this.rotation && (key === 'x' || key === 'y')) {
this.doTransform = true;
}
if (!skipAttr) {
setter = (this[key + 'Setter'] ||
this._defaultSetter);
setter.call(this, val, key, element);
}
}, this);
this.afterSetters();
}
// In accordance with animate, run a complete callback
if (complete) {
complete.call(this);
}
return ret;
};
/**
* Apply a clipping shape to this element.
*
* @function Highcharts.SVGElement#clip
*
* @param {SVGElement} [clipElem]
* The clipping shape. If skipped, the current clip is removed.
*
* @return {Highcharts.SVGElement}
* Returns the SVG element to allow chaining.
*/
SVGElement.prototype.clip = function (clipElem) {
if (clipElem && !clipElem.clipPath) {
// Add a hyphen at the end to avoid confusion in testing indexes
// -1 and -10, -11 etc (#6550)
var id = SVGElement_uniqueKey() + '-', clipPath = this.renderer.createElement('clipPath')
.attr({ id: id })
.add(this.renderer.defs);
SVGElement_extend(clipElem, { clipPath: clipPath, id: id, count: 0 });
clipElem.add(clipPath);
}
return this.attr('clip-path', clipElem ?
"url(".concat(this.renderer.url, "#").concat(clipElem.id, ")") :
'none');
};
/**
* Calculate the coordinates needed for drawing a rectangle crisply and
* return the calculated attributes.
*
* @function Highcharts.SVGElement#crisp
*
* @param {Highcharts.RectangleObject} rect
* Rectangle to crisp.
*
* @param {number} [strokeWidth]
* The stroke width to consider when computing crisp positioning. It can
* also be set directly on the rect parameter.
*
* @return {Highcharts.RectangleObject}
* The modified rectangle arguments.
*/
SVGElement.prototype.crisp = function (rect, strokeWidth) {
// Math.round because strokeWidth can sometimes have roundoff errors
strokeWidth = Math.round(strokeWidth || rect.strokeWidth || 0);
var x1 = rect.x || this.x || 0,
y1 = rect.y || this.y || 0,
x2 = (rect.width || this.width || 0) + x1,
y2 = (rect.height || this.height || 0) + y1,
// Find all the rounded coordinates for corners
x = SVGElement_crisp(x1,
strokeWidth),
y = SVGElement_crisp(y1,
strokeWidth),
x2Crisp = SVGElement_crisp(x2,
strokeWidth),
y2Crisp = SVGElement_crisp(y2,
strokeWidth);
SVGElement_extend(rect, {
x: x,
y: y,
width: x2Crisp - x,
height: y2Crisp - y
});
if (SVGElement_defined(rect.strokeWidth)) {
rect.strokeWidth = strokeWidth;
}
return rect;
};
/**
* Build and apply an SVG gradient out of a common JavaScript configuration
* object. This function is called from the attribute setters. An event
* hook is added for supporting other complex color types.
*
* @private
* @function Highcharts.SVGElement#complexColor
*
* @param {Highcharts.GradientColorObject|Highcharts.PatternObject} colorOptions
* The gradient or pattern options structure.
*
* @param {string} prop
* The property to apply, can either be `fill` or `stroke`.
*
* @param {Highcharts.SVGDOMElement} elem
* SVG element to apply the gradient on.
*/
SVGElement.prototype.complexColor = function (colorOptions, prop, elem) {
var renderer = this.renderer;
var colorObject,
gradName,
gradAttr,
radAttr,
gradients,
stops,
stopColor,
stopOpacity,
radialReference,
id,
key = [],
value;
SVGElement_fireEvent(this.renderer, 'complexColor', {
args: arguments
}, function () {
// Apply linear or radial gradients
if (colorOptions.radialGradient) {
gradName = 'radialGradient';
}
else if (colorOptions.linearGradient) {
gradName = 'linearGradient';
}
if (gradName) {
gradAttr = colorOptions[gradName];
gradients = renderer.gradients;
stops = colorOptions.stops;
radialReference = elem.radialReference;
// Keep < 2.2 compatibility
if (SVGElement_isArray(gradAttr)) {
colorOptions[gradName] = gradAttr = {
x1: gradAttr[0],
y1: gradAttr[1],
x2: gradAttr[2],
y2: gradAttr[3],
gradientUnits: 'userSpaceOnUse'
};
}
// Correct the radial gradient for the radial reference system
if (gradName === 'radialGradient' &&
radialReference &&
!SVGElement_defined(gradAttr.gradientUnits)) {
// Save the radial attributes for updating
radAttr = gradAttr;
gradAttr = SVGElement_merge(gradAttr, renderer.getRadialAttr(radialReference, radAttr), { gradientUnits: 'userSpaceOnUse' });
}
// Build the unique key to detect whether we need to create a
// new element (#1282)
SVGElement_objectEach(gradAttr, function (value, n) {
if (n !== 'id') {
key.push(n, value);
}
});
SVGElement_objectEach(stops, function (val) {
key.push(val);
});
key = key.join(',');
// Check if a gradient object with the same config object is
// created within this renderer
if (gradients[key]) {
id = gradients[key].attr('id');
}
else {
// Set the id and create the element
gradAttr.id = id = SVGElement_uniqueKey();
var gradientObject_1 = gradients[key] =
renderer.createElement(gradName)
.attr(gradAttr)
.add(renderer.defs);
gradientObject_1.radAttr = radAttr;
// The gradient needs to keep a list of stops to be able to
// destroy them
gradientObject_1.stops = [];
stops.forEach(function (stop) {
if (stop[1].indexOf('rgba') === 0) {
colorObject = Color_Color.parse(stop[1]);
stopColor = colorObject.get('rgb');
stopOpacity = colorObject.get('a');
}
else {
stopColor = stop[1];
stopOpacity = 1;
}
var stopObject = renderer.createElement('stop').attr({
offset: stop[0],
'stop-color': stopColor,
'stop-opacity': stopOpacity
}).add(gradientObject_1);
// Add the stop element to the gradient
gradientObject_1.stops.push(stopObject);
});
}
// Set the reference to the gradient object
value = 'url(' + renderer.url + '#' + id + ')';
elem.setAttribute(prop, value);
elem.gradient = key;
// Allow the color to be concatenated into tooltips formatters
// etc. (#2995)
colorOptions.toString = function () {
return value;
};
}
});
};
/**
* Set styles for the element. In addition to CSS styles supported by
* native SVG and HTML elements, there are also some custom made for
* Highcharts, like `width`, `ellipsis` and `textOverflow` for SVG text
* elements.
*
* @sample highcharts/members/renderer-text-on-chart/
* Styled text
*
* @function Highcharts.SVGElement#css
*
* @param {Highcharts.CSSObject} styles
* The new CSS styles.
*
* @return {Highcharts.SVGElement}
* Return the SVG element for chaining.
*/
SVGElement.prototype.css = function (styles) {
var oldStyles = this.styles,
newStyles = {},
elem = this.element;
var textWidth,
hasNew = !oldStyles;
// Filter out existing styles to increase performance (#2640)
if (oldStyles) {
SVGElement_objectEach(styles, function (value, n) {
if (oldStyles && oldStyles[n] !== value) {
newStyles[n] = value;
hasNew = true;
}
});
}
if (hasNew) {
// Merge the new styles with the old ones
if (oldStyles) {
styles = SVGElement_extend(oldStyles, newStyles);
}
// Get the text width from style
// Previously set, unset it (#8234)
if (styles.width === null || styles.width === 'auto') {
delete this.textWidth;
// Apply new
}
else if (elem.nodeName.toLowerCase() === 'text' &&
styles.width) {
textWidth = this.textWidth = SVGElement_pInt(styles.width);
}
// Store object
SVGElement_extend(this.styles, styles);
if (textWidth && (!svg && this.renderer.forExport)) {
delete styles.width;
}
var fontSize = isFirefox && styles.fontSize || null;
// Necessary in firefox to be able to set font-size, #22124
if (fontSize && (SVGElement_isNumber(fontSize) ||
/^\d+$/.test(fontSize))) {
styles.fontSize += 'px';
}
var stylesToApply_1 = SVGElement_merge(styles);
if (elem.namespaceURI === this.SVG_NS) {
// These CSS properties are interpreted internally by the SVG
// renderer, but are not supported by SVG and should not be
// added to the DOM. In styled mode, no CSS should find its way
// to the DOM whatsoever (#6173, #6474).
['textOutline', 'textOverflow', 'whiteSpace', 'width'].forEach(function (key) { return (stylesToApply_1 &&
delete stylesToApply_1[key]); });
// SVG requires fill for text
if (stylesToApply_1.color) {
stylesToApply_1.fill = stylesToApply_1.color;
}
}
SVGElement_css(elem, stylesToApply_1);
}
if (this.added) {
// Rebuild text after added. Cache mechanisms in the buildText will
// prevent building if there are no significant changes.
if (this.element.nodeName === 'text') {
this.renderer.buildText(this);
}
// Apply text outline after added
if (styles.textOutline) {
this.applyTextOutline(styles.textOutline);
}
}
return this;
};
/**
* @private
* @function Highcharts.SVGElement#dashstyleSetter
* @param {string} value
*/
SVGElement.prototype.dashstyleSetter = function (value) {
var i,
strokeWidth = this['stroke-width'];
// If "inherit", like maps in IE, assume 1 (#4981). With HC5 and the new
// strokeWidth function, we should be able to use that instead.
if (strokeWidth === 'inherit') {
strokeWidth = 1;
}
value = value && value.toLowerCase();
if (value) {
var v = value
.replace('shortdashdotdot', '3,1,1,1,1,1,')
.replace('shortdashdot', '3,1,1,1')
.replace('shortdot', '1,1,')
.replace('shortdash', '3,1,')
.replace('longdash', '8,3,')
.replace(/dot/g, '1,3,')
.replace('dash', '4,3,')
.replace(/,$/, '')
.split(','); // Ending comma
i = v.length;
while (i--) {
v[i] = '' + (SVGElement_pInt(v[i]) * SVGElement_pick(strokeWidth, NaN));
}
value = v.join(',').replace(/NaN/g, 'none'); // #3226
this.element.setAttribute('stroke-dasharray', value);
}
};
/**
* Destroy the element and element wrapper and clear up the DOM and event
* hooks.
*
* @function Highcharts.SVGElement#destroy
*/
SVGElement.prototype.destroy = function () {
var _a;
var wrapper = this,
element = wrapper.element || {},
renderer = wrapper.renderer,
ownerSVGElement = element.ownerSVGElement;
var parentToClean = (element.nodeName === 'SPAN' &&
wrapper.parentGroup ||
void 0),
grandParent,
i;
// Remove events
element.onclick = element.onmouseout = element.onmouseover =
element.onmousemove = element.point = null;
SVGElement_stop(wrapper); // Stop running animations
if (wrapper.clipPath && ownerSVGElement) {
var clipPath_1 = wrapper.clipPath;
// Look for existing references to this clipPath and remove them
// before destroying the element (#6196).
// The upper case version is for Edge
[].forEach.call(ownerSVGElement.querySelectorAll('[clip-path],[CLIP-PATH]'), function (el) {
if (el.getAttribute('clip-path').indexOf(clipPath_1.element.id) > -1) {
el.removeAttribute('clip-path');
}
});
wrapper.clipPath = clipPath_1.destroy();
}
wrapper.connector = (_a = wrapper.connector) === null || _a === void 0 ? void 0 : _a.destroy();
// Destroy stops in case this is a gradient object @todo old code?
if (wrapper.stops) {
for (i = 0; i < wrapper.stops.length; i++) {
wrapper.stops[i].destroy();
}
wrapper.stops.length = 0;
wrapper.stops = void 0;
}
// Remove element
wrapper.safeRemoveChild(element);
// In case of useHTML, clean up empty containers emulating SVG groups
// (#1960, #2393, #2697).
while (parentToClean &&
parentToClean.div &&
parentToClean.div.childNodes.length === 0) {
grandParent = parentToClean.parentGroup;
wrapper.safeRemoveChild(parentToClean.div);
delete parentToClean.div;
parentToClean = grandParent;
}
// Remove from alignObjects
if (wrapper.alignOptions) {
SVGElement_erase(renderer.alignedObjects, wrapper);
}
SVGElement_objectEach(wrapper, function (val, key) {
// Destroy child elements of a group
if (wrapper[key] &&
wrapper[key].parentGroup === wrapper &&
wrapper[key].destroy) {
wrapper[key].destroy();
}
// Delete all properties
delete wrapper[key];
});
return;
};
/**
* @private
* @function Highcharts.SVGElement#dSettter
* @param {number|string|Highcharts.SVGPathArray} value
* @param {string} key
* @param {Highcharts.SVGDOMElement} element
*/
SVGElement.prototype.dSetter = function (value, key, element) {
if (SVGElement_isArray(value)) {
// Backwards compatibility, convert one-dimensional array into an
// array of segments
if (typeof value[0] === 'string') {
value = this.renderer.pathToSegments(value);
}
this.pathArray = value;
value = value.reduce(function (acc, seg, i) {
if (!seg || !seg.join) {
return (seg || '').toString();
}
return (i ? acc + ' ' : '') + seg.join(' ');
}, '');
}
if (/(NaN| {2}|^$)/.test(value)) {
value = 'M 0 0';
}
// Check for cache before resetting. Resetting causes disturbance in the
// DOM, causing flickering in some cases in Edge/IE (#6747). Also
// possible performance gain.
if (this[key] !== value) {
element.setAttribute(key, value);
this[key] = value;
}
};
/**
* @private
* @function Highcharts.SVGElement#fillSetter
* @param {Highcharts.ColorType} value
* @param {string} key
* @param {Highcharts.SVGDOMElement} element
*/
SVGElement.prototype.fillSetter = function (value, key, element) {
if (typeof value === 'string') {
element.setAttribute(key, value);
}
else if (value) {
this.complexColor(value, key, element);
}
};
/**
* @private
* @function Highcharts.SVGElement#hrefSetter
* @param {Highcharts.ColorType} value
* @param {string} key
* @param {Highcharts.SVGDOMElement} element
*/
SVGElement.prototype.hrefSetter = function (value, key, element) {
// Namespace is needed for offline export, #19106
element.setAttributeNS('http://www.w3.org/1999/xlink', key, value);
};
/**
* Get the bounding box (width, height, x and y) for the element. Generally
* used to get rendered text size. Since this is called a lot in charts,
* the results are cached based on text properties, in order to save DOM
* traffic. The returned bounding box includes the rotation, so for example
* a single text line of rotation 90 will report a greater height, and a
* width corresponding to the line-height.
*
* @sample highcharts/members/renderer-on-chart/
* Draw a rectangle based on a text's bounding box
*
* @function Highcharts.SVGElement#getBBox
*
* @param {boolean} [reload]
* Skip the cache and get the updated DOM bounding box.
*
* @param {number} [rot]
* Override the element's rotation. This is internally used on axis
* labels with a value of 0 to find out what the bounding box would
* be have been if it were not rotated.
*
* @return {Highcharts.BBoxObject}
* The bounding box with `x`, `y`, `width` and `height` properties.
*/
SVGElement.prototype.getBBox = function (reload, rot) {
var wrapper = this,
alignValue = wrapper.alignValue,
element = wrapper.element,
renderer = wrapper.renderer,
styles = wrapper.styles,
textStr = wrapper.textStr,
cache = renderer.cache,
cacheKeys = renderer.cacheKeys,
isSVG = element.namespaceURI === wrapper.SVG_NS,
rotation = SVGElement_pick(rot,
wrapper.rotation, 0),
fontSize = renderer.styledMode ? (element &&
SVGElement.prototype.getStyle.call(element, 'font-size')) : (styles.fontSize);
var bBox,
height,
toggleTextShadowShim,
cacheKey;
// Avoid undefined and null (#7316)
if (SVGElement_defined(textStr)) {
cacheKey = textStr.toString();
// Since numbers are monospaced, and numerical labels appear a lot
// in a chart, we assume that a label of n characters has the same
// bounding box as others of the same length. Unless there is inner
// HTML in the label. In that case, leave the numbers as is (#5899).
if (cacheKey.indexOf('<') === -1) {
cacheKey = cacheKey.replace(/\d/g, '0');
}
// Properties that affect bounding box
cacheKey += [
'',
renderer.rootFontSize,
fontSize,
rotation,
wrapper.textWidth, // #7874, also useHTML
alignValue,
styles.lineClamp,
styles.textOverflow, // #5968
styles.fontWeight // #12163
].join(',');
}
if (cacheKey && !reload) {
bBox = cache[cacheKey];
}
// No cache found
if (!bBox || bBox.polygon) {
// SVG elements
if (isSVG || renderer.forExport) {
try { // Fails in Firefox if the container has display: none.
// When the text shadow shim is used, we need to hide the
// fake shadows to get the correct bounding box (#3872)
toggleTextShadowShim = this.fakeTS && function (display) {
var outline = element.querySelector('.highcharts-text-outline');
if (outline) {
SVGElement_css(outline, { display: display });
}
};
// Workaround for #3842, Firefox reporting wrong bounding
// box for shadows
if (SVGElement_isFunction(toggleTextShadowShim)) {
toggleTextShadowShim('none');
}
bBox = element.getBBox ?
// SVG: use extend because IE9 is not allowed to change
// width and height in case of rotation (below)
SVGElement_extend({}, element.getBBox()) : {
// HTML elements with `exporting.allowHTML` and
// legacy IE in export mode
width: element.offsetWidth,
height: element.offsetHeight,
x: 0,
y: 0
};
// #3842
if (SVGElement_isFunction(toggleTextShadowShim)) {
toggleTextShadowShim('');
}
}
catch (e) {
'';
}
// If the bBox is not set, the try-catch block above failed. The
// other condition is for Opera that returns a width of
// -Infinity on hidden elements.
if (!bBox || bBox.width < 0) {
bBox = { x: 0, y: 0, width: 0, height: 0 };
}
// Use HTML within SVG
}
else {
bBox = wrapper.htmlGetBBox();
}
// True SVG elements as well as HTML elements in modern browsers
// using the .useHTML option need to compensated for rotation
height = bBox.height;
// Workaround for wrong bounding box in IE, Edge and Chrome on
// Windows. With Highcharts' default font, IE and Edge report
// a box height of 16.899 and Chrome rounds it to 17. If this
// stands uncorrected, it results in more padding added below
// the text than above when adding a label border or background.
// Also vertical positioning is affected.
// https://jsfiddle.net/highcharts/em37nvuj/
// (#1101, #1505, #1669, #2568, #6213).
if (isSVG) {
bBox.height = height = ({
'11px,17': 14,
'13px,20': 16
}["" + (fontSize || '') + ",".concat(Math.round(height))] ||
height);
}
// Adjust for rotated text
if (rotation) {
bBox = this.getRotatedBox(bBox, rotation);
}
// Create a reference to catch changes to bBox
var e = { bBox: bBox };
SVGElement_fireEvent(this, 'afterGetBBox', e);
// Pick up any changes after the fired event
bBox = e.bBox;
}
// Cache it. When loading a chart in a hidden iframe in Firefox and
// IE/Edge, the bounding box height is 0, so don't cache it (#5620).
if (cacheKey && (textStr === '' || bBox.height > 0)) {
// Rotate (#4681)
while (cacheKeys.length > 250) {
delete cache[cacheKeys.shift()];
}
if (!cache[cacheKey]) {
cacheKeys.push(cacheKey);
}
cache[cacheKey] = bBox;
}
return bBox;
};
/**
* Get the rotated box.
* @private
*/
SVGElement.prototype.getRotatedBox = function (box, rotation) {
var boxX = box.x,
boxY = box.y,
width = box.width,
height = box.height,
_a = this,
alignValue = _a.alignValue,
translateY = _a.translateY,
_b = _a.rotationOriginX,
rotationOriginX = _b === void 0 ? 0 : _b,
_c = _a.rotationOriginY,
rotationOriginY = _c === void 0 ? 0 : _c,
alignFactor = SVGElement_getAlignFactor(alignValue),
baseline = Number(this.element.getAttribute('y') || 0) -
(translateY ? 0 : boxY),
rad = rotation * deg2rad,
rad90 = (rotation - 90) * deg2rad,
cosRad = Math.cos(rad),
sinRad = Math.sin(rad),
wCosRad = width * cosRad,
wSinRad = width * sinRad,
cosRad90 = Math.cos(rad90),
sinRad90 = Math.sin(rad90),
_d = [
rotationOriginX,
rotationOriginY
].map(function (rotOrigin) { return [
rotOrigin - (rotOrigin * cosRad),
rotOrigin * sinRad
]; }),
_e = _d[0],
xOriginCosRad = _e[0],
xOriginSinRad = _e[1],
_f = _d[1],
yOriginCosRad = _f[0],
yOriginSinRad = _f[1],
// Find the starting point on the left side baseline of
// the text
pX = ((boxX + alignFactor * (width - wCosRad)) +
xOriginCosRad + yOriginSinRad),
pY = ((boxY + baseline - alignFactor * wSinRad) -
xOriginSinRad + yOriginCosRad),
// Find all corners
aX = pX + baseline * cosRad90,
bX = aX + wCosRad,
cX = bX - height * cosRad90,
dX = cX - wCosRad,
aY = pY + baseline * sinRad90,
bY = aY + wSinRad,
cY = bY - height * sinRad90,
dY = cY - wSinRad;
// Deduct the bounding box from the corners
var x = Math.min(aX,
bX,
cX,
dX),
y = Math.min(aY,
bY,
cY,
dY),
boxWidth = Math.max(aX,
bX,
cX,
dX) - x,
boxHeight = Math.max(aY,
bY,
cY,
dY) - y;
/* Uncomment to debug boxes
this.renderer.path([
['M', aX, aY],
['L', bX, bY],
['L', cX, cY],
['L', dX, dY],
['Z']
])
.attr({
stroke: 'red',
'stroke-width': 1
})
.add();
// */
return {
x: x,
y: y,
width: boxWidth,
height: boxHeight,
polygon: [
[aX, aY],
[bX, bY],
[cX, cY],
[dX, dY]
]
};
};
/**
* Get the computed style. Only in styled mode.
*
* @example
* chart.series[0].points[0].graphic.getStyle('stroke-width'); // => '1px'
*
* @function Highcharts.SVGElement#getStyle
*
* @param {string} prop
* The property name to check for.
*
* @return {string}
* The current computed value.
*/
SVGElement.prototype.getStyle = function (prop) {
return SVGElement_win
.getComputedStyle(this.element || this, '')
.getPropertyValue(prop);
};
/**
* Check if an element has the given class name.
*
* @function Highcharts.SVGElement#hasClass
*
* @param {string} className
* The class name to check for.
*
* @return {boolean}
* Whether the class name is found.
*/
SVGElement.prototype.hasClass = function (className) {
return ('' + this.attr('class'))
.split(' ')
.indexOf(className) !== -1;
};
/**
* Hide the element, similar to setting the `visibility` attribute to
* `hidden`.
*
* @function Highcharts.SVGElement#hide
*
* @return {Highcharts.SVGElement}
* Returns the SVGElement for chaining.
*/
SVGElement.prototype.hide = function () {
return this.attr({ visibility: 'hidden' });
};
/**
* @private
*/
SVGElement.prototype.htmlGetBBox = function () {
return { height: 0, width: 0, x: 0, y: 0 };
};
/**
* Add an event listener. This is a simple setter that replaces the
* previous event of the same type added by this function, as opposed to
* the {@link Highcharts#addEvent} function.
*
* @sample highcharts/members/element-on/
* A clickable rectangle
*
* @function Highcharts.SVGElement#on
*
* @param {string} eventType
* The event type.
*
* @param {Function} handler
* The handler callback.
*
* @return {Highcharts.SVGElement}
* The SVGElement for chaining.
*/
SVGElement.prototype.on = function (eventType, handler) {
var onEvents = this.onEvents;
if (onEvents[eventType]) {
// Unbind existing event
onEvents[eventType]();
}
onEvents[eventType] = SVGElement_addEvent(this.element, eventType, handler);
return this;
};
/**
* @private
* @function Highcharts.SVGElement#opacitySetter
* @param {string} value
* @param {string} key
* @param {Highcharts.SVGDOMElement} element
*/
SVGElement.prototype.opacitySetter = function (value, key, element) {
// Round off to avoid float errors, like tests where opacity lands on
// 9.86957e-06 instead of 0
var opacity = Number(Number(value).toFixed(3));
this.opacity = opacity;
element.setAttribute(key, opacity);
};
/**
* Re-align an aligned text or label after setting the text.
*
* @private
* @function Highcharts.SVGElement#reAlign
*
*/
SVGElement.prototype.reAlign = function () {
var _a;
if (((_a = this.alignOptions) === null || _a === void 0 ? void 0 : _a.width) && this.alignOptions.align !== 'left') {
this.alignOptions.width = this.getBBox().width;
this.placed = false; // Block animation
this.align();
}
};
/**
* Remove a class name from the element.
*
* @function Highcharts.SVGElement#removeClass
*
* @param {string|RegExp} className
* The class name to remove.
*
* @return {Highcharts.SVGElement} Returns the SVG element for chainability.
*/
SVGElement.prototype.removeClass = function (className) {
return this.attr('class', ('' + this.attr('class'))
.replace(SVGElement_isString(className) ?
new RegExp("(^| )".concat(className, "( |$)")) : // #12064, #13590
className, ' ')
.replace(/ +/g, ' ')
.trim());
};
/**
*
* @private
*/
SVGElement.prototype.removeTextOutline = function () {
var outline = this.element
.querySelector('tspan.highcharts-text-outline');
if (outline) {
this.safeRemoveChild(outline);
}
};
/**
* Removes an element from the DOM.
*
* @private
* @function Highcharts.SVGElement#safeRemoveChild
*
* @param {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement} element
* The DOM node to remove.
*/
SVGElement.prototype.safeRemoveChild = function (element) {
var parentNode = element.parentNode;
if (parentNode) {
parentNode.removeChild(element);
}
};
/**
* Set the coordinates needed to draw a consistent radial gradient across
* a shape regardless of positioning inside the chart. Used on pie slices
* to make all the slices have the same radial reference point.
*
* @function Highcharts.SVGElement#setRadialReference
*
* @param {Array<number>} coordinates
* The center reference. The format is `[centerX, centerY, diameter]` in
* pixels.
*
* @return {Highcharts.SVGElement}
* Returns the SVGElement for chaining.
*/
SVGElement.prototype.setRadialReference = function (coordinates) {
var existingGradient = (this.element.gradient &&
this.renderer.gradients[this.element.gradient]);
this.element.radialReference = coordinates;
// On redrawing objects with an existing gradient, the gradient needs
// to be repositioned (#3801)
if (existingGradient && existingGradient.radAttr) {
existingGradient.animate(this.renderer.getRadialAttr(coordinates, existingGradient.radAttr));
}
return this;
};
/**
* Add a shadow to the element. In styled mode, this method is not used,
* instead use `defs` and filters.
*
* @example
* renderer.rect(10, 100, 100, 100)
* .attr({ fill: 'red' })
* .shadow(true);
*
* @function Highcharts.SVGElement#shadow
*
* @param {boolean|Highcharts.ShadowOptionsObject} [shadowOptions] The
* shadow options. If `true`, the default options are applied. If
* `false`, the current shadow will be removed.
*
* @return {Highcharts.SVGElement} Returns the SVGElement for chaining.
*/
SVGElement.prototype.shadow = function (shadowOptions) {
var _a;
var renderer = this.renderer,
options = SVGElement_merge(((_a = this.parentGroup) === null || _a === void 0 ? void 0 : _a.rotation) === 90 ? {
offsetX: -1,
offsetY: -1
} : {},
SVGElement_isObject(shadowOptions) ? shadowOptions : {}),
id = renderer.shadowDefinition(options);
return this.attr({
filter: shadowOptions ?
"url(".concat(renderer.url, "#").concat(id, ")") :
'none'
});
};
/**
* Show the element after it has been hidden.
*
* @function Highcharts.SVGElement#show
*
* @param {boolean} [inherit=true]
* Set the visibility attribute to `inherit` rather than `visible`.
* The difference is that an element with `visibility="visible"`
* will be visible even if the parent is hidden.
*
* @return {Highcharts.SVGElement}
* Returns the SVGElement for chaining.
*/
SVGElement.prototype.show = function (inherit) {
if (inherit === void 0) { inherit = true; }
return this.attr({ visibility: inherit ? 'inherit' : 'visible' });
};
/**
* Set the stroke-width and record it on the SVGElement
*
* @private
* @function Highcharts.SVGElement#strokeSetter
* @param {number|string|ColorType} value
* @param {string} key
* @param {Highcharts.SVGDOMElement} element
*/
SVGElement.prototype['stroke-widthSetter'] = function (value, key, element) {
// Record it for quick access in getter
this[key] = value;
element.setAttribute(key, value);
};
/**
* Get the computed stroke width in pixel values. This is used extensively
* when drawing shapes to ensure the shapes are rendered crisp and
* positioned correctly relative to each other. Using
* `shape-rendering: crispEdges` leaves us less control over positioning,
* for example when we want to stack columns next to each other, or position
* things pixel-perfectly within the plot box.
*
* The common pattern when placing a shape is:
* - Create the SVGElement and add it to the DOM. In styled mode, it will
* now receive a stroke width from the style sheet. In classic mode we
* will add the `stroke-width` attribute.
* - Read the computed `elem.strokeWidth()`.
* - Place it based on the stroke width.
*
* @function Highcharts.SVGElement#strokeWidth
*
* @return {number}
* The stroke width in pixels. Even if the given stroke width (in CSS or by
* attributes) is based on `em` or other units, the pixel size is returned.
*/
SVGElement.prototype.strokeWidth = function () {
// In non-styled mode, read the stroke width as set by .attr
if (!this.renderer.styledMode) {
return this['stroke-width'] || 0;
}
// In styled mode, read computed stroke width
var val = this.getStyle('stroke-width');
var ret = 0,
tempElement;
// Read pixel values directly
if (/px$/.test(val)) {
ret = SVGElement_pInt(val);
// Other values like em, pt etc need to be measured
}
else if (val !== '') {
tempElement = SVGElement_doc.createElementNS(SVGElement_SVG_NS, 'rect');
SVGElement_attr(tempElement, {
width: val,
'stroke-width': 0
});
this.element.parentNode.appendChild(tempElement);
ret = tempElement.getBBox().width;
tempElement.parentNode.removeChild(tempElement);
}
return ret;
};
/**
* If one of the symbol size affecting parameters are changed,
* check all the others only once for each call to an element's
* .attr() method
*
* @private
* @function Highcharts.SVGElement#symbolAttr
*
* @param {Highcharts.SVGAttributes} hash
* The attributes to set.
*/
SVGElement.prototype.symbolAttr = function (hash) {
var wrapper = this;
SVGElement.symbolCustomAttribs.forEach(function (key) {
wrapper[key] = SVGElement_pick(hash[key], wrapper[key]);
});
wrapper.attr({
d: wrapper.renderer.symbols[wrapper.symbolName](wrapper.x, wrapper.y, wrapper.width, wrapper.height, wrapper)
});
};
/**
* @private
* @function Highcharts.SVGElement#textSetter
* @param {string} value
*/
SVGElement.prototype.textSetter = function (value) {
if (value !== this.textStr) {
// Delete size caches when the text changes
// delete this.bBox; // old code in series-label
delete this.textPxLength;
this.textStr = value;
if (this.added) {
this.renderer.buildText(this);
}
this.reAlign();
}
};
/**
* @private
* @function Highcharts.SVGElement#titleSetter
* @param {string} value
*/
SVGElement.prototype.titleSetter = function (value) {
var el = this.element;
var titleNode = el.getElementsByTagName('title')[0] ||
SVGElement_doc.createElementNS(this.SVG_NS, 'title');
// Move to first child
if (el.insertBefore) {
el.insertBefore(titleNode, el.firstChild);
}
else {
el.appendChild(titleNode);
}
// Replace text content and escape markup
titleNode.textContent = SVGElement_replaceNested(// Scan #[73]
SVGElement_pick(value, ''), // #3276, #3895
[/<[^>]*>/g, '']).replace(/</g, '<').replace(/>/g, '>');
};
/**
* Bring the element to the front. Alternatively, a new zIndex can be set.
*
* @sample highcharts/members/element-tofront/
* Click an element to bring it to front
*
* @function Highcharts.SVGElement#toFront
*
* @return {Highcharts.SVGElement}
* Returns the SVGElement for chaining.
*/
SVGElement.prototype.toFront = function () {
var element = this.element;
element.parentNode.appendChild(element);
return this;
};
/**
* Move an object and its children by x and y values.
*
* @function Highcharts.SVGElement#translate
*
* @param {number} x
* The x value.
*
* @param {number} y
* The y value.
*
* @return {Highcharts.SVGElement}
* Translated element.
*/
SVGElement.prototype.translate = function (x, y) {
return this.attr({
translateX: x,
translateY: y
});
};
/**
* Update the transform attribute based on internal properties. Deals with
* the custom `translateX`, `translateY`, `rotation`, `scaleX` and `scaleY`
* attributes and updates the SVG `transform` attribute.
*
* @private
* @function Highcharts.SVGElement#updateTransform
*/
SVGElement.prototype.updateTransform = function (attrib) {
var _a;
if (attrib === void 0) { attrib = 'transform'; }
var _b = this,
element = _b.element,
matrix = _b.matrix,
_c = _b.rotation,
rotation = _c === void 0 ? 0 : _c,
rotationOriginX = _b.rotationOriginX,
rotationOriginY = _b.rotationOriginY,
scaleX = _b.scaleX,
scaleY = _b.scaleY,
_d = _b.translateX,
translateX = _d === void 0 ? 0 : _d,
_e = _b.translateY,
translateY = _e === void 0 ? 0 : _e;
// Apply translate. Nearly all transformed elements have translation,
// so instead of checking for translate = 0, do it always (#1767,
// #1846).
var transform = ['translate(' + translateX + ',' + translateY + ')'];
// Apply matrix
if (SVGElement_defined(matrix)) {
transform.push('matrix(' + matrix.join(',') + ')');
}
// Apply rotation
if (rotation) {
transform.push('rotate(' + rotation + ' ' +
SVGElement_pick(rotationOriginX, element.getAttribute('x'), 0) +
' ' +
SVGElement_pick(rotationOriginY, element.getAttribute('y') || 0) + ')');
// HTML labels rotation (#20685)
if (((_a = this.text) === null || _a === void 0 ? void 0 : _a.element.tagName) === 'SPAN') {
this.text.attr({
rotation: rotation,
rotationOriginX: (rotationOriginX || 0) - this.padding,
rotationOriginY: (rotationOriginY || 0) - this.padding
});
}
}
// Apply scale
if (SVGElement_defined(scaleX) || SVGElement_defined(scaleY)) {
transform.push('scale(' + SVGElement_pick(scaleX, 1) + ' ' + SVGElement_pick(scaleY, 1) + ')');
}
if (transform.length && !(this.text || this).textPath) {
element.setAttribute(attrib, transform.join(' '));
}
};
/**
* @private
* @function Highcharts.SVGElement#visibilitySetter
*
* @param {string} value
*
* @param {string} key
*
* @param {Highcharts.SVGDOMElement} element
*
*/
SVGElement.prototype.visibilitySetter = function (value, key, element) {
// IE9-11 doesn't handle visibility:inherit well, so we remove the
// attribute instead (#2881, #3909)
if (value === 'inherit') {
element.removeAttribute(key);
}
else if (this[key] !== value) { // #6747
element.setAttribute(key, value);
}
this[key] = value;
};
/**
* @private
* @function Highcharts.SVGElement#xGetter
*/
SVGElement.prototype.xGetter = function (key) {
if (this.element.nodeName === 'circle') {
if (key === 'x') {
key = 'cx';
}
else if (key === 'y') {
key = 'cy';
}
}
return this._defaultGetter(key);
};
/**
* @private
* @function Highcharts.SVGElement#zIndexSetter
*/
SVGElement.prototype.zIndexSetter = function (value, key) {
var renderer = this.renderer,
parentGroup = this.parentGroup,
parentWrapper = parentGroup || renderer,
parentNode = parentWrapper.element || renderer.box,
element = this.element,
svgParent = parentNode === renderer.box;
var childNodes,
otherElement,
otherZIndex,
inserted = false,
undefinedOtherZIndex,
run = this.added,
i;
if (SVGElement_defined(value)) {
// So we can read it for other elements in the group
element.setAttribute('data-z-index', value);
value = +value;
if (this[key] === value) {
// Only update when needed (#3865)
run = false;
}
}
else if (SVGElement_defined(this[key])) {
element.removeAttribute('data-z-index');
}
this[key] = value;
// Insert according to this and other elements' zIndex. Before .add() is
// called, nothing is done. Then on add, or by later calls to
// zIndexSetter, the node is placed on the right place in the DOM.
if (run) {
value = this.zIndex;
if (value && parentGroup) {
parentGroup.handleZ = true;
}
childNodes = parentNode.childNodes;
for (i = childNodes.length - 1; i >= 0 && !inserted; i--) {
otherElement = childNodes[i];
otherZIndex = otherElement.getAttribute('data-z-index');
undefinedOtherZIndex = !SVGElement_defined(otherZIndex);
if (otherElement !== element) {
if (
// Negative zIndex versus no zIndex:
// On all levels except the highest. If the parent is
// <svg>, then we don't want to put items before <desc>
// or <defs>
value < 0 &&
undefinedOtherZIndex &&
!svgParent &&
!i) {
parentNode.insertBefore(element, childNodes[i]);
inserted = true;
}
else if (
// Insert after the first element with a lower zIndex
SVGElement_pInt(otherZIndex) <= value ||
// If negative zIndex, add this before first undefined
// zIndex element
(undefinedOtherZIndex &&
(!SVGElement_defined(value) || value >= 0))) {
parentNode.insertBefore(element, childNodes[i + 1]);
inserted = true;
}
}
}
if (!inserted) {
parentNode.insertBefore(element, childNodes[svgParent ? 3 : 0]);
inserted = true;
}
}
return inserted;
};
/* *
*
* Properties
*
* */
// Custom attributes used for symbols, these should be filtered out when
// setting SVGElement attributes (#9375).
SVGElement.symbolCustomAttribs = [
'anchorX',
'anchorY',
'clockwise',
'end',
'height',
'innerR',
'r',
'start',
'width',
'x',
'y'
];
return SVGElement;
}());
// Some shared setters and getters
SVGElement.prototype.strokeSetter = SVGElement.prototype.fillSetter;
SVGElement.prototype.yGetter = SVGElement.prototype.xGetter;
SVGElement.prototype.matrixSetter =
SVGElement.prototype.rotationOriginXSetter =
SVGElement.prototype.rotationOriginYSetter =
SVGElement.prototype.rotationSetter =
SVGElement.prototype.scaleXSetter =
SVGElement.prototype.scaleYSetter =
SVGElement.prototype.translateXSetter =
SVGElement.prototype.translateYSetter =
SVGElement.prototype.verticalAlignSetter = function (value, key) {
this[key] = value;
this.doTransform = true;
};
/* *
*
* Default Export
*
* */
/* harmony default export */ var SVG_SVGElement = (SVGElement);
/* *
*
* API Declarations
*
* */
/**
* Reference to the global SVGElement class as a workaround for a name conflict
* in the Highcharts namespace.
*
* @global
* @typedef {global.SVGElement} GlobalSVGElement
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
*/
/**
* The horizontal alignment of an element.
*
* @typedef {"center"|"left"|"right"} Highcharts.AlignValue
*/
/**
* Options to align the element relative to the chart or another box.
*
* @interface Highcharts.AlignObject
*/ /**
* Horizontal alignment. Can be one of `left`, `center` and `right`.
*
* @name Highcharts.AlignObject#align
* @type {Highcharts.AlignValue|undefined}
*
* @default left
*/ /**
* Vertical alignment. Can be one of `top`, `middle` and `bottom`.
*
* @name Highcharts.AlignObject#verticalAlign
* @type {Highcharts.VerticalAlignValue|undefined}
*
* @default top
*/ /**
* Horizontal pixel offset from alignment.
*
* @name Highcharts.AlignObject#x
* @type {number|undefined}
*
* @default 0
*/ /**
* Vertical pixel offset from alignment.
*
* @name Highcharts.AlignObject#y
* @type {number|undefined}
*
* @default 0
*/ /**
* Use the `transform` attribute with translateX and translateY custom
* attributes to align this elements rather than `x` and `y` attributes.
*
* @name Highcharts.AlignObject#alignByTranslate
* @type {boolean|undefined}
*
* @default false
*/
/**
* Bounding box of an element.
*
* @interface Highcharts.BBoxObject
* @extends Highcharts.PositionObject
*/ /**
* Height of the bounding box.
*
* @name Highcharts.BBoxObject#height
* @type {number}
*/ /**
* Width of the bounding box.
*
* @name Highcharts.BBoxObject#width
* @type {number}
*/ /**
* Horizontal position of the bounding box.
*
* @name Highcharts.BBoxObject#x
* @type {number}
*/ /**
* Vertical position of the bounding box.
*
* @name Highcharts.BBoxObject#y
* @type {number}
*/
/**
* An object of key-value pairs for SVG attributes. Attributes in Highcharts
* elements for the most parts correspond to SVG, but some are specific to
* Highcharts, like `zIndex`, `rotation`, `rotationOriginX`,
* `rotationOriginY`, `translateX`, `translateY`, `scaleX` and `scaleY`. SVG
* attributes containing a hyphen are _not_ camel-cased, they should be
* quoted to preserve the hyphen.
*
* @example
* {
* 'stroke': '#ff0000', // basic
* 'stroke-width': 2, // hyphenated
* 'rotation': 45 // custom
* 'd': ['M', 10, 10, 'L', 30, 30, 'z'] // path definition, note format
* }
*
* @interface Highcharts.SVGAttributes
*/ /**
* @name Highcharts.SVGAttributes#[key:string]
* @type {*}
*/ /**
* @name Highcharts.SVGAttributes#d
* @type {string|Highcharts.SVGPathArray|undefined}
*/ /**
* @name Highcharts.SVGAttributes#dx
* @type {number|undefined}
*/ /**
* @name Highcharts.SVGAttributes#dy
* @type {number|undefined}
*/ /**
* @name Highcharts.SVGAttributes#fill
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
*/ /**
* @name Highcharts.SVGAttributes#inverted
* @type {boolean|undefined}
*/ /**
* @name Highcharts.SVGAttributes#matrix
* @type {Array<number>|undefined}
*/ /**
* @name Highcharts.SVGAttributes#rotation
* @type {number|undefined}
*/ /**
* @name Highcharts.SVGAttributes#rotationOriginX
* @type {number|undefined}
*/ /**
* @name Highcharts.SVGAttributes#rotationOriginY
* @type {number|undefined}
*/ /**
* @name Highcharts.SVGAttributes#scaleX
* @type {number|undefined}
*/ /**
* @name Highcharts.SVGAttributes#scaleY
* @type {number|undefined}
*/ /**
* @name Highcharts.SVGAttributes#stroke
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
*/ /**
* @name Highcharts.SVGAttributes#style
* @type {string|Highcharts.CSSObject|undefined}
*/ /**
* @name Highcharts.SVGAttributes#translateX
* @type {number|undefined}
*/ /**
* @name Highcharts.SVGAttributes#translateY
* @type {number|undefined}
*/ /**
* @name Highcharts.SVGAttributes#zIndex
* @type {number|undefined}
*/
/**
* An SVG DOM element. The type is a reference to the regular SVGElement in the
* global scope.
*
* @typedef {globals.GlobalSVGElement} Highcharts.SVGDOMElement
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
*/
/**
* The vertical alignment of an element.
*
* @typedef {"bottom"|"middle"|"top"} Highcharts.VerticalAlignValue
*/
''; // Keeps doclets above in JS file
;// ./code/es5/es-modules/Core/Renderer/SVG/SVGLabel.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var __extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var SVGLabel_defined = Core_Utilities.defined, SVGLabel_extend = Core_Utilities.extend, SVGLabel_getAlignFactor = Core_Utilities.getAlignFactor, SVGLabel_isNumber = Core_Utilities.isNumber, SVGLabel_merge = Core_Utilities.merge, SVGLabel_pick = Core_Utilities.pick, SVGLabel_removeEvent = Core_Utilities.removeEvent;
/* *
*
* Class
*
* */
/**
* SVG label to render text.
* @private
* @class
* @name Highcharts.SVGLabel
* @augments Highcharts.SVGElement
*/
var SVGLabel = /** @class */ (function (_super) {
__extends(SVGLabel, _super);
/* *
*
* Constructor
*
* */
function SVGLabel(renderer, str, x, y, shape, anchorX, anchorY, useHTML, baseline, className) {
var _this = _super.call(this,
renderer, 'g') || this;
_this.paddingLeftSetter = _this.paddingSetter;
_this.paddingRightSetter = _this.paddingSetter;
_this.doUpdate = false;
_this.textStr = str;
_this.x = x;
_this.y = y;
_this.anchorX = anchorX;
_this.anchorY = anchorY;
_this.baseline = baseline;
_this.className = className;
_this.addClass(className === 'button' ?
'highcharts-no-tooltip' :
'highcharts-label');
if (className) {
_this.addClass('highcharts-' + className);
}
// Create the text element. An undefined text content prevents redundant
// box calculation (#16121)
_this.text = renderer.text(void 0, 0, 0, useHTML).attr({ zIndex: 1 });
// Validate the shape argument
var hasBGImage;
if (typeof shape === 'string') {
hasBGImage = /^url\((.*?)\)$/.test(shape);
if (hasBGImage || _this.renderer.symbols[shape]) {
_this.symbolKey = shape;
}
}
_this.bBox = SVGLabel.emptyBBox;
_this.padding = 3;
_this.baselineOffset = 0;
_this.needsBox = renderer.styledMode || hasBGImage;
_this.deferredAttr = {};
_this.alignFactor = 0;
return _this;
}
/* *
*
* Functions
*
* */
SVGLabel.prototype.alignSetter = function (value) {
var alignFactor = SVGLabel_getAlignFactor(value);
this.textAlign = value;
if (alignFactor !== this.alignFactor) {
this.alignFactor = alignFactor;
// Bounding box exists, means we're dynamically changing
if (this.bBox && SVGLabel_isNumber(this.xSetting)) {
this.attr({ x: this.xSetting }); // #5134
}
}
};
SVGLabel.prototype.anchorXSetter = function (value, key) {
this.anchorX = value;
this.boxAttr(key, Math.round(value) - this.getCrispAdjust() - this.xSetting);
};
SVGLabel.prototype.anchorYSetter = function (value, key) {
this.anchorY = value;
this.boxAttr(key, value - this.ySetting);
};
/*
* Set a box attribute, or defer it if the box is not yet created
*/
SVGLabel.prototype.boxAttr = function (key, value) {
if (this.box) {
this.box.attr(key, value);
}
else {
this.deferredAttr[key] = value;
}
};
/*
* Pick up some properties and apply them to the text instead of the
* wrapper.
*/
SVGLabel.prototype.css = function (styles) {
if (styles) {
var textStyles_1 = {};
// Create a copy to avoid altering the original object
// (#537)
styles = SVGLabel_merge(styles);
SVGLabel.textProps.forEach(function (prop) {
if (typeof styles[prop] !== 'undefined') {
textStyles_1[prop] = styles[prop];
delete styles[prop];
}
});
this.text.css(textStyles_1);
// Update existing text, box (#9400, #12163, #18212)
if ('fontSize' in textStyles_1 || 'fontWeight' in textStyles_1) {
this.updateTextPadding();
}
else if ('width' in textStyles_1 || 'textOverflow' in textStyles_1) {
this.updateBoxSize();
}
}
return SVG_SVGElement.prototype.css.call(this, styles);
};
/*
* Destroy and release memory.
*/
SVGLabel.prototype.destroy = function () {
// Added by button implementation
SVGLabel_removeEvent(this.element, 'mouseenter');
SVGLabel_removeEvent(this.element, 'mouseleave');
if (this.text) {
this.text.destroy();
}
if (this.box) {
this.box = this.box.destroy();
}
// Call base implementation to destroy the rest
SVG_SVGElement.prototype.destroy.call(this);
return void 0;
};
SVGLabel.prototype.fillSetter = function (value, key) {
if (value) {
this.needsBox = true;
}
// For animation getter (#6776)
this.fill = value;
this.boxAttr(key, value);
};
/*
* Return the bounding box of the box, not the group.
*/
SVGLabel.prototype.getBBox = function (reload, rot) {
// If we have a text string and the DOM bBox was 0, it typically means
// that the label was first rendered hidden, so we need to update the
// bBox (#15246)
if (this.textStr && this.bBox.width === 0 && this.bBox.height === 0) {
this.updateBoxSize();
}
var _a = this,
padding = _a.padding,
_b = _a.height,
height = _b === void 0 ? 0 : _b,
_c = _a.translateX,
translateX = _c === void 0 ? 0 : _c,
_d = _a.translateY,
translateY = _d === void 0 ? 0 : _d,
_e = _a.width,
width = _e === void 0 ? 0 : _e,
paddingLeft = SVGLabel_pick(this.paddingLeft,
padding),
rotation = rot !== null && rot !== void 0 ? rot : (this.rotation || 0);
var bBox = {
width: width,
height: height,
x: translateX + this.bBox.x - paddingLeft,
y: translateY + this.bBox.y - padding + this.baselineOffset
};
if (rotation) {
bBox = this.getRotatedBox(bBox, rotation);
}
return bBox;
};
SVGLabel.prototype.getCrispAdjust = function () {
return (this.renderer.styledMode && this.box ?
this.box.strokeWidth() :
(this['stroke-width'] ?
parseInt(this['stroke-width'], 10) :
0)) % 2 / 2;
};
SVGLabel.prototype.heightSetter = function (value) {
this.heightSetting = value;
this.doUpdate = true;
};
/**
* This method is executed in the end of `attr()`, after setting all
* attributes in the hash. In can be used to efficiently consolidate
* multiple attributes in one SVG property -- e.g., translate, rotate and
* scale are merged in one "transform" attribute in the SVG node.
* Also updating height or width should trigger update of the box size.
*
* @private
* @function Highcharts.SVGLabel#afterSetters
*/
SVGLabel.prototype.afterSetters = function () {
_super.prototype.afterSetters.call(this);
if (this.doUpdate) {
this.updateBoxSize();
this.doUpdate = false;
}
};
/*
* After the text element is added, get the desired size of the border
* box and add it before the text in the DOM.
*/
SVGLabel.prototype.onAdd = function () {
this.text.add(this);
this.attr({
// Alignment is available now (#3295, 0 not rendered if given
// as a value)
text: SVGLabel_pick(this.textStr, ''),
x: this.x || 0,
y: this.y || 0
});
if (this.box && SVGLabel_defined(this.anchorX)) {
this.attr({
anchorX: this.anchorX,
anchorY: this.anchorY
});
}
};
SVGLabel.prototype.paddingSetter = function (value, key) {
if (!SVGLabel_isNumber(value)) {
this[key] = void 0;
}
else if (value !== this[key]) {
this[key] = value;
this.updateTextPadding();
}
};
SVGLabel.prototype.rSetter = function (value, key) {
this.boxAttr(key, value);
};
SVGLabel.prototype.strokeSetter = function (value, key) {
// For animation getter (#6776)
this.stroke = value;
this.boxAttr(key, value);
};
SVGLabel.prototype['stroke-widthSetter'] = function (value, key) {
if (value) {
this.needsBox = true;
}
this['stroke-width'] = value;
this.boxAttr(key, value);
};
SVGLabel.prototype['text-alignSetter'] = function (value) {
// The text-align variety is for the pre-animation getter. The code
// should be unified to either textAlign or text-align.
this.textAlign = this['text-align'] = value;
this.updateTextPadding();
};
SVGLabel.prototype.textSetter = function (text) {
if (typeof text !== 'undefined') {
// Must use .attr to ensure transforms are done (#10009)
this.text.attr({ text: text });
}
this.updateTextPadding();
this.reAlign();
};
/*
* This function runs after the label is added to the DOM (when the bounding
* box is available), and after the text of the label is updated to detect
* the new bounding box and reflect it in the border box.
*/
SVGLabel.prototype.updateBoxSize = function () {
var text = this.text,
attribs = {},
padding = this.padding,
// #12165 error when width is null (auto)
// #12163 when fontweight: bold, recalculate bBox without cache
// #3295 && 3514 box failure when string equals 0
bBox = this.bBox = (((!SVGLabel_isNumber(this.widthSetting) ||
!SVGLabel_isNumber(this.heightSetting) ||
this.textAlign) && SVGLabel_defined(text.textStr)) ?
text.getBBox(void 0, 0) :
SVGLabel.emptyBBox);
var crispAdjust;
this.width = this.getPaddedWidth();
this.height = (this.heightSetting || bBox.height || 0) + 2 * padding;
var metrics = this.renderer.fontMetrics(text);
// Update the label-scoped y offset. Math.min because of inline
// style (#9400)
this.baselineOffset = padding + Math.min(
// When applicable, use the font size of the first line (#15707)
(this.text.firstLineMetrics || metrics).b,
// When the height is 0, there is no bBox, so go with the font
// metrics. Highmaps CSS demos.
bBox.height || Infinity);
// #15491: Vertical centering
if (this.heightSetting) {
this.baselineOffset += (this.heightSetting - metrics.h) / 2;
}
if (this.needsBox && !text.textPath) {
// Create the border box if it is not already present
if (!this.box) {
// Symbol definition exists (#5324)
var box = this.box = this.symbolKey ?
this.renderer.symbol(this.symbolKey) :
this.renderer.rect();
box.addClass(// Don't use label className for buttons
(this.className === 'button' ?
'' : 'highcharts-label-box') +
(this.className ?
' highcharts-' + this.className + '-box' : ''));
box.add(this);
}
crispAdjust = this.getCrispAdjust();
attribs.x = crispAdjust;
attribs.y = ((this.baseline ? -this.baselineOffset : 0) + crispAdjust);
// Apply the box attributes
attribs.width = Math.round(this.width);
attribs.height = Math.round(this.height);
this.box.attr(SVGLabel_extend(attribs, this.deferredAttr));
this.deferredAttr = {};
}
};
/*
* This function runs after setting text or padding, but only if padding
* is changed.
*/
SVGLabel.prototype.updateTextPadding = function () {
var _a,
_b;
var text = this.text,
textAlign = text.styles.textAlign || this.textAlign;
if (!text.textPath) {
this.updateBoxSize();
// Determine y based on the baseline
var textY = this.baseline ? 0 : this.baselineOffset,
textX = ((_a = this.paddingLeft) !== null && _a !== void 0 ? _a : this.padding) +
// Compensate for alignment
SVGLabel_getAlignFactor(textAlign) * ((_b = this.widthSetting) !== null && _b !== void 0 ? _b : this.bBox.width);
// Update if anything changed
if (textX !== text.x || textY !== text.y) {
text.attr({
align: textAlign,
x: textX
});
if (typeof textY !== 'undefined') {
text.attr('y', textY);
}
}
// Record current values
text.x = textX;
text.y = textY;
}
};
SVGLabel.prototype.widthSetter = function (value) {
// `width:auto` => null
this.widthSetting = SVGLabel_isNumber(value) ? value : void 0;
this.doUpdate = true;
};
SVGLabel.prototype.getPaddedWidth = function () {
var padding = this.padding;
var paddingLeft = SVGLabel_pick(this.paddingLeft,
padding);
var paddingRight = SVGLabel_pick(this.paddingRight,
padding);
return ((this.widthSetting || this.bBox.width || 0) +
paddingLeft +
paddingRight);
};
SVGLabel.prototype.xSetter = function (value) {
this.x = value; // For animation getter
if (this.alignFactor) {
value -= this.alignFactor * this.getPaddedWidth();
// Force animation even when setting to the same value (#7898)
this['forceAnimate:x'] = true;
}
this.xSetting = Math.round(value);
this.attr('translateX', this.xSetting);
};
SVGLabel.prototype.ySetter = function (value) {
this.ySetting = this.y = Math.round(value);
this.attr('translateY', this.ySetting);
};
/* *
*
* Static Properties
*
* */
SVGLabel.emptyBBox = {
width: 0,
height: 0,
x: 0,
y: 0
};
/**
* For labels, these CSS properties are applied to the `text` node directly.
*
* @private
* @name Highcharts.SVGLabel#textProps
* @type {Array<string>}
*/
SVGLabel.textProps = [
'color', 'direction', 'fontFamily', 'fontSize', 'fontStyle',
'fontWeight', 'lineClamp', 'lineHeight', 'textAlign', 'textDecoration',
'textOutline', 'textOverflow', 'whiteSpace', 'width'
];
return SVGLabel;
}(SVG_SVGElement));
/* *
*
* Default Export
*
* */
/* harmony default export */ var SVG_SVGLabel = (SVGLabel);
;// ./code/es5/es-modules/Core/Renderer/SVG/Symbols.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var Symbols_defined = Core_Utilities.defined, Symbols_isNumber = Core_Utilities.isNumber, Symbols_pick = Core_Utilities.pick;
/* *
*
* Functions
*
* */
/* eslint-disable require-jsdoc, valid-jsdoc */
/**
*
*/
function arc(cx, cy, w, h, options) {
var arc = [];
if (options) {
var start = options.start || 0,
rx = Symbols_pick(options.r,
w),
ry = Symbols_pick(options.r,
h || w),
// Subtract a small number to prevent cos and sin of start and end
// from becoming equal on 360 arcs (#1561). The size of the circle
// affects the constant, therefore the division by `rx`. If the
// proximity is too small, the arc disappears. If it is too great, a
// gap appears. This can be seen in the animation of the official
// bubble demo (#20586).
proximity = 0.0002 / (options.borderRadius ? 1 : Math.max(rx, 1)),
fullCircle = (Math.abs((options.end || 0) - start - 2 * Math.PI) <
proximity),
end = (options.end || 0) - (fullCircle ? proximity : 0),
innerRadius = options.innerR,
open_1 = Symbols_pick(options.open,
fullCircle),
cosStart = Math.cos(start),
sinStart = Math.sin(start),
cosEnd = Math.cos(end),
sinEnd = Math.sin(end),
// Proximity takes care of rounding errors around PI (#6971)
longArc = Symbols_pick(options.longArc,
end - start - Math.PI < proximity ? 0 : 1);
var arcSegment = [
'A', // ArcTo
rx, // X radius
ry, // Y radius
0, // Slanting
longArc, // Long or short arc
Symbols_pick(options.clockwise, 1), // Clockwise
cx + rx * cosEnd,
cy + ry * sinEnd
];
arcSegment.params = { start: start, end: end, cx: cx, cy: cy }; // Memo for border radius
arc.push([
'M',
cx + rx * cosStart,
cy + ry * sinStart
], arcSegment);
if (Symbols_defined(innerRadius)) {
arcSegment = [
'A', // ArcTo
innerRadius, // X radius
innerRadius, // Y radius
0, // Slanting
longArc, // Long or short arc
// Clockwise - opposite to the outer arc clockwise
Symbols_defined(options.clockwise) ? 1 - options.clockwise : 0,
cx + innerRadius * cosStart,
cy + innerRadius * sinStart
];
// Memo for border radius
arcSegment.params = {
start: end,
end: start,
cx: cx,
cy: cy
};
arc.push(open_1 ?
[
'M',
cx + innerRadius * cosEnd,
cy + innerRadius * sinEnd
] : [
'L',
cx + innerRadius * cosEnd,
cy + innerRadius * sinEnd
], arcSegment);
}
if (!open_1) {
arc.push(['Z']);
}
}
return arc;
}
/**
* Callout shape used for default tooltips.
*/
function callout(x, y, w, h, options) {
var arrowLength = 6,
halfDistance = 6,
r = Math.min((options && options.r) || 0,
w,
h),
safeDistance = r + halfDistance,
anchorX = options && options.anchorX,
anchorY = options && options.anchorY || 0;
var path = roundedRect(x,
y,
w,
h, { r: r });
if (!Symbols_isNumber(anchorX)) {
return path;
}
// Do not render a connector, if anchor starts inside the label
if (anchorX < w && anchorX > 0 && anchorY < h && anchorY > 0) {
return path;
}
// Anchor on right side
if (x + anchorX > w - safeDistance) {
// Chevron
if (anchorY > y + safeDistance &&
anchorY < y + h - safeDistance) {
path.splice(3, 1, ['L', x + w, anchorY - halfDistance], ['L', x + w + arrowLength, anchorY], ['L', x + w, anchorY + halfDistance], ['L', x + w, y + h - r]);
// Simple connector
}
else {
if (anchorX < w) { // Corner connector
var isTopCorner = anchorY < y + safeDistance,
cornerY = isTopCorner ? y : y + h,
sliceStart = isTopCorner ? 2 : 5;
path.splice(sliceStart, 0, ['L', anchorX, anchorY], ['L', x + w - r, cornerY]);
}
else { // Side connector
path.splice(3, 1, ['L', x + w, h / 2], ['L', anchorX, anchorY], ['L', x + w, h / 2], ['L', x + w, y + h - r]);
}
}
// Anchor on left side
}
else if (x + anchorX < safeDistance) {
// Chevron
if (anchorY > y + safeDistance &&
anchorY < y + h - safeDistance) {
path.splice(7, 1, ['L', x, anchorY + halfDistance], ['L', x - arrowLength, anchorY], ['L', x, anchorY - halfDistance], ['L', x, y + r]);
// Simple connector
}
else {
if (anchorX > 0) { // Corner connector
var isTopCorner = anchorY < y + safeDistance,
cornerY = isTopCorner ? y : y + h,
sliceStart = isTopCorner ? 1 : 6;
path.splice(sliceStart, 0, ['L', anchorX, anchorY], ['L', x + r, cornerY]);
}
else { // Side connector
path.splice(7, 1, ['L', x, h / 2], ['L', anchorX, anchorY], ['L', x, h / 2], ['L', x, y + r]);
}
}
}
else if ( // Replace bottom
anchorY > h &&
anchorX < w - safeDistance) {
path.splice(5, 1, ['L', anchorX + halfDistance, y + h], ['L', anchorX, y + h + arrowLength], ['L', anchorX - halfDistance, y + h], ['L', x + r, y + h]);
}
else if ( // Replace top
anchorY < 0 &&
anchorX > safeDistance) {
path.splice(1, 1, ['L', anchorX - halfDistance, y], ['L', anchorX, y - arrowLength], ['L', anchorX + halfDistance, y], ['L', w - r, y]);
}
return path;
}
/**
*
*/
function circle(x, y, w, h) {
// Return a full arc
return arc(x + w / 2, y + h / 2, w / 2, h / 2, {
start: Math.PI * 0.5,
end: Math.PI * 2.5,
open: false
});
}
/**
*
*/
function diamond(x, y, w, h) {
return [
['M', x + w / 2, y],
['L', x + w, y + h / 2],
['L', x + w / 2, y + h],
['L', x, y + h / 2],
['Z']
];
}
// #15291
/**
*
*/
function rect(x, y, w, h, options) {
if (options && options.r) {
return roundedRect(x, y, w, h, options);
}
return [
['M', x, y],
['L', x + w, y],
['L', x + w, y + h],
['L', x, y + h],
['Z']
];
}
/**
*
*/
function roundedRect(x, y, w, h, options) {
var r = (options === null || options === void 0 ? void 0 : options.r) || 0;
return [
['M', x + r, y],
['L', x + w - r, y], // Top side
['A', r, r, 0, 0, 1, x + w, y + r], // Top-right corner
['L', x + w, y + h - r], // Right side
['A', r, r, 0, 0, 1, x + w - r, y + h], // Bottom-right corner
['L', x + r, y + h], // Bottom side
['A', r, r, 0, 0, 1, x, y + h - r], // Bottom-left corner
['L', x, y + r], // Left side
['A', r, r, 0, 0, 1, x + r, y],
['Z'] // Top-left corner
];
}
/**
*
*/
function triangle(x, y, w, h) {
return [
['M', x + w / 2, y],
['L', x + w, y + h],
['L', x, y + h],
['Z']
];
}
/**
*
*/
function triangleDown(x, y, w, h) {
return [
['M', x, y],
['L', x + w, y],
['L', x + w / 2, y + h],
['Z']
];
}
var Symbols = {
arc: arc,
callout: callout,
circle: circle,
diamond: diamond,
rect: rect,
roundedRect: roundedRect,
square: rect,
triangle: triangle,
'triangle-down': triangleDown
};
/* *
*
* Default Export
*
* */
/* harmony default export */ var SVG_Symbols = (Symbols);
;// ./code/es5/es-modules/Core/Renderer/SVG/TextBuilder.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var TextBuilder_doc = Core_Globals.doc, TextBuilder_SVG_NS = Core_Globals.SVG_NS, TextBuilder_win = Core_Globals.win;
var TextBuilder_attr = Core_Utilities.attr, TextBuilder_extend = Core_Utilities.extend, TextBuilder_fireEvent = Core_Utilities.fireEvent, TextBuilder_isString = Core_Utilities.isString, TextBuilder_objectEach = Core_Utilities.objectEach, TextBuilder_pick = Core_Utilities.pick;
// Function used to test string length including an ellipsis
var stringWithEllipsis = function (text, currentIndex) {
return text.substring(0, currentIndex) + '\u2026';
};
/* *
*
* Class
*
* */
/**
* SVG Text Builder
* @private
* @class
* @name Highcharts.TextBuilder
*/
var TextBuilder = /** @class */ (function () {
function TextBuilder(svgElement) {
var textStyles = svgElement.styles;
this.renderer = svgElement.renderer;
this.svgElement = svgElement;
this.width = svgElement.textWidth;
this.textLineHeight = textStyles && textStyles.lineHeight;
this.textOutline = textStyles && textStyles.textOutline;
this.ellipsis = Boolean(textStyles && textStyles.textOverflow === 'ellipsis');
this.lineClamp = textStyles === null || textStyles === void 0 ? void 0 : textStyles.lineClamp;
this.noWrap = Boolean(textStyles && textStyles.whiteSpace === 'nowrap');
}
/**
* Build an SVG representation of the pseudo HTML given in the object's
* svgElement.
*
* @private
*
* @return {void}.
*/
TextBuilder.prototype.buildSVG = function () {
var wrapper = this.svgElement, textNode = wrapper.element, renderer = wrapper.renderer, textStr = TextBuilder_pick(wrapper.textStr, '').toString(), hasMarkup = textStr.indexOf('<') !== -1, childNodes = textNode.childNodes, tempParent = !wrapper.added && renderer.box, regexMatchBreaks = /<br.*?>/g,
// The buildText code is quite heavy, so if we're not changing
// something that affects the text, skip it (#6113).
textCache = [
textStr,
this.ellipsis,
this.noWrap,
this.textLineHeight,
this.textOutline,
wrapper.getStyle('font-size'),
wrapper.styles.lineClamp,
this.width
].join(',');
if (textCache === wrapper.textCache) {
return;
}
wrapper.textCache = textCache;
delete wrapper.actualWidth;
// Remove old text
for (var i = childNodes.length; i--;) {
textNode.removeChild(childNodes[i]);
}
// Simple strings, add text directly and return
if (!hasMarkup &&
!this.ellipsis &&
!this.width &&
!wrapper.textPath &&
(textStr.indexOf(' ') === -1 ||
(this.noWrap && !regexMatchBreaks.test(textStr)))) {
textNode.appendChild(TextBuilder_doc.createTextNode(this.unescapeEntities(textStr)));
// Complex strings, add more logic
}
else if (textStr !== '') {
if (tempParent) {
// Attach it to the DOM to read offset width and font size
tempParent.appendChild(textNode);
}
// Step 1. Parse the markup safely and directly into a tree
// structure.
var ast = new HTML_AST(textStr);
// Step 2. Do as many as we can of the modifications to the tree
// structure before it is added to the DOM
this.modifyTree(ast.nodes);
ast.addToDOM(textNode);
// Step 3. Some modifications can't be done until the structure is
// in the DOM, because we need to read computed metrics.
this.modifyDOM();
// Add title if an ellipsis was added
if (this.ellipsis &&
(textNode.textContent || '').indexOf('\u2026') !== -1) {
wrapper.attr('title', this.unescapeEntities(wrapper.textStr || '', ['<', '>']) // #7179
);
}
if (tempParent) {
tempParent.removeChild(textNode);
}
}
// Apply the text outline
if (TextBuilder_isString(this.textOutline) && wrapper.applyTextOutline) {
wrapper.applyTextOutline(this.textOutline);
}
};
/**
* Modify the DOM of the generated SVG structure. This function only does
* operations that cannot be done until the elements are attached to the
* DOM, like doing layout based on rendered metrics of the added elements.
*
* @private
*
*/
TextBuilder.prototype.modifyDOM = function () {
var _this = this;
var wrapper = this.svgElement;
var x = TextBuilder_attr(wrapper.element, 'x');
wrapper.firstLineMetrics = void 0;
// Remove empty tspans (including breaks) from the beginning because
// SVG's getBBox doesn't count empty lines. The use case is tooltip
// where the header is empty. By doing this in the DOM rather than in
// the AST, we can inspect the textContent directly and don't have to
// recurse down to look for valid content.
var firstChild;
while ((firstChild = wrapper.element.firstChild)) {
if (/^[\s\u200B]*$/.test(firstChild.textContent || ' ')) {
wrapper.element.removeChild(firstChild);
}
else {
break;
}
}
// Modify hard line breaks by applying the rendered line height
[].forEach.call(wrapper.element.querySelectorAll('tspan.highcharts-br'), function (br, i) {
if (br.nextSibling && br.previousSibling) { // #5261
if (i === 0 && br.previousSibling.nodeType === 1) {
wrapper.firstLineMetrics = wrapper.renderer
.fontMetrics(br.previousSibling);
}
TextBuilder_attr(br, {
// Since the break is inserted in front of the next
// line, we need to use the next sibling for the line
// height
dy: _this.getLineHeight(br.nextSibling),
x: x
});
}
});
// Constrain the line width, either by ellipsis or wrapping
var width = this.width || 0;
if (!width) {
return;
}
// Insert soft line breaks into each text node
var modifyTextNode = function (textNode,
parentElement) {
var _a;
var text = textNode.textContent || '';
var words = text
.replace(/([^\^])-/g, '$1- ') // Split on hyphens
// .trim()
.split(' '); // #1273
var hasWhiteSpace = !_this.noWrap && (words.length > 1 || wrapper.element.childNodes.length > 1);
var dy = _this.getLineHeight(parentElement),
ellipsisWidth = Math.max(0,
// Subtract the font face to make room for
// the ellipsis itself
width - 0.8 * dy);
var lineNo = 0;
var startAt = wrapper.actualWidth;
if (hasWhiteSpace) {
var lines = [];
// Remove preceding siblings in order to make the text length
// calculation correct in the truncate function
var precedingSiblings = [];
while (parentElement.firstChild &&
parentElement.firstChild !== textNode) {
precedingSiblings.push(parentElement.firstChild);
parentElement.removeChild(parentElement.firstChild);
}
while (words.length) {
// Apply the previous line
if (words.length && !_this.noWrap && lineNo > 0) {
lines.push(textNode.textContent || '');
textNode.textContent = words.join(' ')
.replace(/- /g, '-');
}
// For each line, truncate the remaining
// words into the line length.
_this.truncate(textNode, void 0, words, lineNo === 0 ? (startAt || 0) : 0, width, ellipsisWidth,
// Build the text to test for
function (t, currentIndex) {
return words
.slice(0, currentIndex)
.join(' ')
.replace(/- /g, '-');
});
startAt = wrapper.actualWidth;
lineNo++;
// Line clamp. Break out after n lines and append an
// ellipsis regardless of the text length.
if (_this.lineClamp && lineNo >= _this.lineClamp) {
// Only if there are remaining words that should have
// been rendered.
if (words.length) {
_this.truncate(textNode, textNode.textContent || '', void 0, 0,
// Target width
width, ellipsisWidth, stringWithEllipsis);
textNode.textContent = ((_a = textNode.textContent) === null || _a === void 0 ? void 0 : _a.replace('\u2026', '')) + '\u2026';
}
break;
}
}
// Reinsert the preceding child nodes
precedingSiblings.forEach(function (childNode) {
parentElement.insertBefore(childNode, textNode);
});
// Insert the previous lines before the original text node
lines.forEach(function (line) {
// Insert the line
parentElement.insertBefore(TextBuilder_doc.createTextNode(line), textNode);
// Insert a break
var br = TextBuilder_doc.createElementNS(TextBuilder_SVG_NS, 'tspan');
br.textContent = '\u200B'; // Zero-width space
TextBuilder_attr(br, { dy: dy, x: x });
parentElement.insertBefore(br, textNode);
});
}
else if (_this.ellipsis) {
if (text) {
_this.truncate(textNode, text, void 0, 0,
// Target width
width, ellipsisWidth, stringWithEllipsis);
}
}
};
// Recurse down the DOM tree and handle line breaks for each text node
var modifyChildren = (function (node) {
var childNodes = [].slice.call(node.childNodes);
childNodes.forEach(function (childNode) {
if (childNode.nodeType === TextBuilder_win.Node.TEXT_NODE) {
modifyTextNode(childNode, node);
}
else {
// Reset word-wrap width readings after hard breaks
if (childNode.className.baseVal
.indexOf('highcharts-br') !== -1) {
wrapper.actualWidth = 0;
}
// Recurse down to child node
modifyChildren(childNode);
}
});
});
modifyChildren(wrapper.element);
};
/**
* Get the rendered line height of a <text>, <tspan> or pure text node.
* @private
* @param {DOMElementType|Text} node The node to check for
* @return {number} The rendered line height
*/
TextBuilder.prototype.getLineHeight = function (node) {
// If the node is a text node, use its parent
var element = (node.nodeType === TextBuilder_win.Node.TEXT_NODE) ?
node.parentElement :
node;
return this.textLineHeight ?
parseInt(this.textLineHeight.toString(), 10) :
this.renderer.fontMetrics(element || this.svgElement.element).h;
};
/**
* Transform a pseudo HTML AST node tree into an SVG structure. We do as
* much heavy lifting as we can here, before doing the final processing in
* the modifyDOM function. The original data is mutated.
*
* @private
*
* @param {ASTNode[]} nodes The AST nodes
*
*/
TextBuilder.prototype.modifyTree = function (nodes) {
var _this = this;
var modifyChild = function (node,
i) {
var _a = node.attributes,
attributes = _a === void 0 ? {} : _a,
children = node.children,
_b = node.style,
style = _b === void 0 ? {} : _b,
tagName = node.tagName,
styledMode = _this.renderer.styledMode;
// Apply styling to text tags
if (tagName === 'b' || tagName === 'strong') {
if (styledMode) {
// eslint-disable-next-line dot-notation
attributes['class'] = 'highcharts-strong';
}
else {
style.fontWeight = 'bold';
}
}
else if (tagName === 'i' || tagName === 'em') {
if (styledMode) {
// eslint-disable-next-line dot-notation
attributes['class'] = 'highcharts-emphasized';
}
else {
style.fontStyle = 'italic';
}
}
// Modify styling
if (style && style.color) {
style.fill = style.color;
}
// Handle breaks
if (tagName === 'br') {
attributes['class'] = 'highcharts-br'; // eslint-disable-line dot-notation
node.textContent = '\u200B'; // Zero-width space
// Trim whitespace off the beginning of new lines
var nextNode = nodes[i + 1];
if (nextNode && nextNode.textContent) {
nextNode.textContent =
nextNode.textContent.replace(/^ +/gm, '');
}
// If an anchor has direct text node children, the text is unable to
// wrap because there is no `getSubStringLength` function on the
// element. Therefore we need to wrap the child text node or nodes
// in a tspan. #16173.
}
else if (tagName === 'a' &&
children &&
children.some(function (child) { return child.tagName === '#text'; })) {
node.children = [{ children: children, tagName: 'tspan' }];
}
if (tagName !== '#text' && tagName !== 'a') {
node.tagName = 'tspan';
}
TextBuilder_extend(node, { attributes: attributes, style: style });
// Recurse
if (children) {
children
.filter(function (c) { return c.tagName !== '#text'; })
.forEach(modifyChild);
}
};
nodes.forEach(modifyChild);
TextBuilder_fireEvent(this.svgElement, 'afterModifyTree', { nodes: nodes });
};
/*
* Truncate the text node contents to a given length. Used when the css
* width is set. If the `textOverflow` is `ellipsis`, the text is truncated
* character by character to the given length. If not, the text is
* word-wrapped line by line.
*/
TextBuilder.prototype.truncate = function (textNode, text, words, startAt, width, ellipsisWidth, getString) {
var svgElement = this.svgElement;
var rotation = svgElement.rotation;
// Cache the lengths to avoid checking the same twice
var lengths = [];
// Word wrap cannot be truncated to shorter than one word, ellipsis
// text can be completely blank.
var minIndex = words && !startAt ? 1 : 0;
var maxIndex = (text || words || '').length;
var currentIndex = maxIndex;
var str;
var actualWidth;
if (!words) {
width = ellipsisWidth;
}
var getSubStringLength = function (charEnd,
concatenatedEnd) {
// `charEnd` is used when finding the character-by-character
// break for ellipsis, concatenatedEnd is used for word-by-word
// break for word wrapping.
var end = concatenatedEnd || charEnd;
var parentNode = textNode.parentNode;
if (parentNode && typeof lengths[end] === 'undefined') {
// Modern browsers
if (parentNode.getSubStringLength) {
// Fails with DOM exception on unit-tests/legend/members
// of unknown reason. Desired width is 0, text content
// is "5" and end is 1.
try {
lengths[end] = startAt +
parentNode.getSubStringLength(0, words ? end + 1 : end);
}
catch (e) {
'';
}
}
}
return lengths[end];
};
svgElement.rotation = 0; // Discard rotation when computing box
actualWidth = getSubStringLength(textNode.textContent.length);
if (startAt + actualWidth > width) {
// Do a binary search for the index where to truncate the text
while (minIndex <= maxIndex) {
currentIndex = Math.ceil((minIndex + maxIndex) / 2);
// When checking words for word-wrap, we need to build the
// string and measure the subStringLength at the concatenated
// word length.
if (words) {
str = getString(words, currentIndex);
}
actualWidth = getSubStringLength(currentIndex, str && str.length - 1);
if (minIndex === maxIndex) {
// Complete
minIndex = maxIndex + 1;
}
else if (actualWidth > width) {
// Too large. Set max index to current.
maxIndex = currentIndex - 1;
}
else {
// Within width. Set min index to current.
minIndex = currentIndex;
}
}
// If max index was 0 it means the shortest possible text was also
// too large. For ellipsis that means only the ellipsis, while for
// word wrap it means the whole first word.
if (maxIndex === 0) {
// Remove ellipsis
textNode.textContent = '';
// If the new text length is one less than the original, we don't
// need the ellipsis
}
else if (!(text && maxIndex === text.length - 1)) {
textNode.textContent = str || getString(text || words, currentIndex);
}
// Add ellipsis on individual lines
if (this.ellipsis && actualWidth > width) {
this.truncate(textNode, textNode.textContent || '', void 0, 0, width, ellipsisWidth, stringWithEllipsis);
}
}
// When doing line wrapping, prepare for the next line by removing the
// items from this line.
if (words) {
words.splice(0, currentIndex);
}
svgElement.actualWidth = actualWidth;
svgElement.rotation = rotation; // Apply rotation again.
};
/*
* Un-escape HTML entities based on the public `renderer.escapes` list
*
* @private
*
* @param {string} inputStr The string to unescape
* @param {Array<string>} [except] Exceptions
*
* @return {string} The processed string
*/
TextBuilder.prototype.unescapeEntities = function (inputStr, except) {
TextBuilder_objectEach(this.renderer.escapes, function (value, key) {
if (!except || except.indexOf(value) === -1) {
inputStr = inputStr.toString().replace(new RegExp(value, 'g'), key);
}
});
return inputStr;
};
return TextBuilder;
}());
/* harmony default export */ var SVG_TextBuilder = (TextBuilder);
;// ./code/es5/es-modules/Core/Renderer/SVG/SVGRenderer.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var SVGRenderer_spreadArray = (undefined && undefined.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var SVGRenderer_defaultOptions = Defaults.defaultOptions;
var SVGRenderer_charts = Core_Globals.charts, SVGRenderer_deg2rad = Core_Globals.deg2rad, SVGRenderer_doc = Core_Globals.doc, SVGRenderer_isFirefox = Core_Globals.isFirefox, isMS = Core_Globals.isMS, isWebKit = Core_Globals.isWebKit, noop = Core_Globals.noop, SVGRenderer_SVG_NS = Core_Globals.SVG_NS, symbolSizes = Core_Globals.symbolSizes, SVGRenderer_win = Core_Globals.win;
var SVGRenderer_addEvent = Core_Utilities.addEvent, SVGRenderer_attr = Core_Utilities.attr, SVGRenderer_createElement = Core_Utilities.createElement, SVGRenderer_crisp = Core_Utilities.crisp, SVGRenderer_css = Core_Utilities.css, SVGRenderer_defined = Core_Utilities.defined, SVGRenderer_destroyObjectProperties = Core_Utilities.destroyObjectProperties, SVGRenderer_extend = Core_Utilities.extend, SVGRenderer_isArray = Core_Utilities.isArray, SVGRenderer_isNumber = Core_Utilities.isNumber, SVGRenderer_isObject = Core_Utilities.isObject, SVGRenderer_isString = Core_Utilities.isString, SVGRenderer_merge = Core_Utilities.merge, SVGRenderer_pick = Core_Utilities.pick, SVGRenderer_pInt = Core_Utilities.pInt, SVGRenderer_replaceNested = Core_Utilities.replaceNested, SVGRenderer_uniqueKey = Core_Utilities.uniqueKey;
/* *
*
* Variables
*
* */
var hasInternalReferenceBug;
/* *
*
* Class
*
* */
/* eslint-disable no-invalid-this, valid-jsdoc */
/**
* Allows direct access to the Highcharts rendering layer in order to draw
* primitive shapes like circles, rectangles, paths or text directly on a chart,
* or independent from any chart. The SVGRenderer represents a wrapper object
* for SVG in modern browsers.
*
* An existing chart's renderer can be accessed through {@link Chart.renderer}.
* The renderer can also be used completely decoupled from a chart.
*
* See [How to use the SVG Renderer](
* https://www.highcharts.com/docs/advanced-chart-features/renderer) for a
* comprehensive tutorial.
*
* @sample highcharts/members/renderer-on-chart
* Annotating a chart programmatically.
* @sample highcharts/members/renderer-basic
* Independent SVG drawing.
*
* @example
* // Use directly without a chart object.
* let renderer = new Highcharts.Renderer(parentNode, 600, 400);
*
* @class
* @name Highcharts.SVGRenderer
*
* @param {Highcharts.HTMLDOMElement} container
* Where to put the SVG in the web page.
*
* @param {number} width
* The width of the SVG.
*
* @param {number} height
* The height of the SVG.
*
* @param {Highcharts.CSSObject} [style]
* The box style, if not in styleMode
*
* @param {boolean} [forExport=false]
* Whether the rendered content is intended for export.
*
* @param {boolean} [allowHTML=true]
* Whether the renderer is allowed to include HTML text, which will be
* projected on top of the SVG.
*
* @param {boolean} [styledMode=false]
* Whether the renderer belongs to a chart that is in styled mode.
* If it does, it will avoid setting presentational attributes in
* some cases, but not when set explicitly through `.attr` and `.css`
* etc.
*/
var SVGRenderer = /** @class */ (function () {
/**
* The root `svg` node of the renderer.
*
* @name Highcharts.SVGRenderer#box
* @type {Highcharts.SVGDOMElement}
*/
/**
* The wrapper for the root `svg` node of the renderer.
*
* @name Highcharts.SVGRenderer#boxWrapper
* @type {Highcharts.SVGElement}
*/
/**
* A pointer to the `defs` node of the root SVG.
*
* @name Highcharts.SVGRenderer#defs
* @type {Highcharts.SVGElement}
*/
/**
* Whether the rendered content is intended for export.
*
* @name Highcharts.SVGRenderer#forExport
* @type {boolean | undefined}
*/
/**
* Page url used for internal references.
*
* @private
* @name Highcharts.SVGRenderer#url
* @type {string}
*/
/**
* Initialize the SVGRenderer. Overridable initializer function that takes
* the same parameters as the constructor.
*
* @function Highcharts.SVGRenderer#init
*
* @param {Highcharts.HTMLDOMElement} container
* Where to put the SVG in the web page.
*
* @param {number} width
* The width of the SVG.
*
* @param {number} height
* The height of the SVG.
*
* @param {Highcharts.CSSObject} [style]
* The box style, if not in styleMode
*
* @param {boolean} [forExport=false]
* Whether the rendered content is intended for export.
*
* @param {boolean} [allowHTML=true]
* Whether the renderer is allowed to include HTML text, which will be
* projected on top of the SVG.
*
* @param {boolean} [styledMode=false]
* Whether the renderer belongs to a chart that is in styled mode. If it
* does, it will avoid setting presentational attributes in some cases, but
* not when set explicitly through `.attr` and `.css` etc.
*/
function SVGRenderer(container, width, height, style, forExport, allowHTML, styledMode) {
var renderer = this,
boxWrapper = renderer
.createElement('svg')
.attr({
version: '1.1',
'class': 'highcharts-root'
}),
element = boxWrapper.element;
if (!styledMode) {
boxWrapper.css(this.getStyle(style || {}));
}
container.appendChild(element);
// Always use ltr on the container, otherwise text-anchor will be
// flipped and text appear outside labels, buttons, tooltip etc (#3482)
SVGRenderer_attr(container, 'dir', 'ltr');
// For browsers other than IE, add the namespace attribute (#1978)
if (container.innerHTML.indexOf('xmlns') === -1) {
SVGRenderer_attr(element, 'xmlns', this.SVG_NS);
}
this.box = element;
this.boxWrapper = boxWrapper;
this.alignedObjects = [];
this.url = this.getReferenceURL();
// Add description
var desc = this.createElement('desc').add();
desc.element.appendChild(SVGRenderer_doc.createTextNode('Created with Highcharts 12.1.2'));
this.defs = this.createElement('defs').add();
this.allowHTML = allowHTML;
this.forExport = forExport;
this.styledMode = styledMode;
this.gradients = {}; // Object where gradient SvgElements are stored
this.cache = {}; // Cache for numerical bounding boxes
this.cacheKeys = [];
this.imgCount = 0;
this.rootFontSize = boxWrapper.getStyle('font-size');
renderer.setSize(width, height, false);
// Issue 110 workaround:
// In Firefox, if a div is positioned by percentage, its pixel position
// may land between pixels. The container itself doesn't display this,
// but an SVG element inside this container will be drawn at subpixel
// precision. In order to draw sharp lines, this must be compensated
// for. This doesn't seem to work inside iframes though (like in
// jsFiddle).
var subPixelFix,
rect;
if (SVGRenderer_isFirefox && container.getBoundingClientRect) {
subPixelFix = function () {
SVGRenderer_css(container, { left: 0, top: 0 });
rect = container.getBoundingClientRect();
SVGRenderer_css(container, {
left: (Math.ceil(rect.left) - rect.left) + 'px',
top: (Math.ceil(rect.top) - rect.top) + 'px'
});
};
// Run the fix now
subPixelFix();
// Run it on resize
renderer.unSubPixelFix = SVGRenderer_addEvent(SVGRenderer_win, 'resize', subPixelFix);
}
}
/* *
*
* Functions
*
* */
/**
* General method for adding a definition to the SVG `defs` tag. Can be used
* for gradients, fills, filters etc. Styled mode only. A hook for adding
* general definitions to the SVG's defs tag. Definitions can be referenced
* from the CSS by its `id`. Read more in
* [gradients, shadows and patterns](https://www.highcharts.com/docs/chart-design-and-style/gradients-shadows-and-patterns).
* Styled mode only.
*
* @function Highcharts.SVGRenderer#definition
*
* @param {Highcharts.ASTNode} def
* A serialized form of an SVG definition, including children.
*
* @return {Highcharts.SVGElement}
* The inserted node.
*/
SVGRenderer.prototype.definition = function (def) {
var ast = new HTML_AST([def]);
return ast.addToDOM(this.defs.element);
};
/**
* Get the prefix needed for internal URL references to work in certain
* cases. Some older browser versions had a bug where internal url
* references in SVG attributes, on the form `url(#some-id)`, would fail if
* a base tag was present in the page. There were also issues with
* `history.pushState` related to this prefix.
*
* Related issues: #24, #672, #1070, #5244.
*
* The affected browsers are:
* - Chrome <= 53 (May 2018)
* - Firefox <= 51 (January 2017)
* - Safari/Mac <= 12.1 (2018 or 2019)
* - Safari/iOS <= 13
*
* @todo Remove this hack when time has passed. All the affected browsers
* are evergreens, so it is increasingly unlikely that users are affected by
* the bug.
*
* @return {string}
* The prefix to use. An empty string for modern browsers.
*/
SVGRenderer.prototype.getReferenceURL = function () {
if ((SVGRenderer_isFirefox || isWebKit) &&
SVGRenderer_doc.getElementsByTagName('base').length) {
// Detect if a clip path is taking effect by performing a hit test
// outside the clipped area. If the hit element is the rectangle
// that was supposed to be clipped, the bug is present. This only
// has to be performed once per page load, so we store the result
// locally in the module.
if (!SVGRenderer_defined(hasInternalReferenceBug)) {
var id = SVGRenderer_uniqueKey();
var ast = new HTML_AST([{
tagName: 'svg',
attributes: {
width: 8,
height: 8
},
children: [{
tagName: 'defs',
children: [{
tagName: 'clipPath',
attributes: {
id: id
},
children: [{
tagName: 'rect',
attributes: {
width: 4,
height: 4
}
}]
}]
}, {
tagName: 'rect',
attributes: {
id: 'hitme',
width: 8,
height: 8,
'clip-path': "url(#".concat(id, ")"),
fill: 'rgba(0,0,0,0.001)'
}
}]
}]);
var svg = ast.addToDOM(SVGRenderer_doc.body);
SVGRenderer_css(svg, {
position: 'fixed',
top: 0,
left: 0,
zIndex: 9e5
});
var hitElement = SVGRenderer_doc.elementFromPoint(6, 6);
hasInternalReferenceBug = (hitElement && hitElement.id) === 'hitme';
SVGRenderer_doc.body.removeChild(svg);
}
if (hasInternalReferenceBug) {
// Scan alert #[72]: Loop for nested patterns
return SVGRenderer_replaceNested(SVGRenderer_win.location.href.split('#')[0], // Remove hash
[/<[^>]*>/g, ''], // Wing cut HTML
[/([\('\)])/g, '\\$1'], // Escape parantheses and quotes
[/ /g, '%20'] // Replace spaces (needed for Safari only)
);
}
}
return '';
};
/**
* Get the global style setting for the renderer.
*
* @private
* @function Highcharts.SVGRenderer#getStyle
*
* @param {Highcharts.CSSObject} style
* Style settings.
*
* @return {Highcharts.CSSObject}
* The style settings mixed with defaults.
*/
SVGRenderer.prototype.getStyle = function (style) {
this.style = SVGRenderer_extend({
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", ' +
'Roboto, Helvetica, Arial, "Apple Color Emoji", ' +
'"Segoe UI Emoji", "Segoe UI Symbol", sans-serif',
fontSize: '1rem'
}, style);
return this.style;
};
/**
* Apply the global style on the renderer, mixed with the default styles.
*
* @function Highcharts.SVGRenderer#setStyle
*
* @param {Highcharts.CSSObject} style
* CSS to apply.
*/
SVGRenderer.prototype.setStyle = function (style) {
this.boxWrapper.css(this.getStyle(style));
};
/**
* Detect whether the renderer is hidden. This happens when one of the
* parent elements has `display: none`. Used internally to detect when we
* need to render preliminarily in another div to get the text bounding
* boxes right.
*
* @function Highcharts.SVGRenderer#isHidden
*
* @return {boolean}
* True if it is hidden.
*/
SVGRenderer.prototype.isHidden = function () {
return !this.boxWrapper.getBBox().width;
};
/**
* Destroys the renderer and its allocated members.
*
* @function Highcharts.SVGRenderer#destroy
*
* @return {null}
* Pass through value.
*/
SVGRenderer.prototype.destroy = function () {
var renderer = this,
rendererDefs = renderer.defs;
renderer.box = null;
renderer.boxWrapper = renderer.boxWrapper.destroy();
// Call destroy on all gradient elements
SVGRenderer_destroyObjectProperties(renderer.gradients || {});
renderer.gradients = null;
renderer.defs = rendererDefs.destroy();
// Remove sub pixel fix handler (#982)
if (renderer.unSubPixelFix) {
renderer.unSubPixelFix();
}
renderer.alignedObjects = null;
return null;
};
/**
* Create a wrapper for an SVG element. Serves as a factory for
* {@link SVGElement}, but this function is itself mostly called from
* primitive factories like {@link SVGRenderer#path}, {@link
* SVGRenderer#rect} or {@link SVGRenderer#text}.
*
* @function Highcharts.SVGRenderer#createElement
*
* @param {string} nodeName
* The node name, for example `rect`, `g` etc.
*
* @return {Highcharts.SVGElement}
* The generated SVGElement.
*/
SVGRenderer.prototype.createElement = function (nodeName) {
return new this.Element(this, nodeName);
};
/**
* Get converted radial gradient attributes according to the radial
* reference. Used internally from the {@link SVGElement#colorGradient}
* function.
*
* @private
* @function Highcharts.SVGRenderer#getRadialAttr
*/
SVGRenderer.prototype.getRadialAttr = function (radialReference, gradAttr) {
return {
cx: (radialReference[0] - radialReference[2] / 2) +
(gradAttr.cx || 0) * radialReference[2],
cy: (radialReference[1] - radialReference[2] / 2) +
(gradAttr.cy || 0) * radialReference[2],
r: (gradAttr.r || 0) * radialReference[2]
};
};
/**
* Create a drop shadow definition and return its id
*
* @private
* @function Highcharts.SVGRenderer#shadowDefinition
*
* @param {boolean|Highcharts.ShadowOptionsObject} [shadowOptions] The
* shadow options. If `true`, the default options are applied
*/
SVGRenderer.prototype.shadowDefinition = function (shadowOptions) {
var id = SVGRenderer_spreadArray([
"highcharts-drop-shadow-".concat(this.chartIndex)
],
Object.keys(shadowOptions)
.map(function (key) {
return "" + key + "-".concat(shadowOptions[key]);
}), true).join('-').toLowerCase().replace(/[^a-z\d\-]/g, ''), options = SVGRenderer_merge({
color: '#000000',
offsetX: 1,
offsetY: 1,
opacity: 0.15,
width: 5
}, shadowOptions);
if (!this.defs.element.querySelector("#".concat(id))) {
this.definition({
tagName: 'filter',
attributes: {
id: id,
filterUnits: options.filterUnits
},
children: this.getShadowFilterContent(options)
});
}
return id;
};
/**
* Get shadow filter content.
* NOTE! Overridden in es5 module for IE11 compatibility.
*
* @private
* @function Highcharts.SVGRenderer#getShadowFilterContent
*
* @param {ShadowOptionsObject} options
* The shadow options.
* @return {Array<AST.Node>}
* The shadow filter content.
*/
SVGRenderer.prototype.getShadowFilterContent = function (options) {
return [{
tagName: 'feDropShadow',
attributes: {
dx: options.offsetX,
dy: options.offsetY,
'flood-color': options.color,
// Tuned and modified to keep a preserve compatibility
// with the old settings
'flood-opacity': Math.min(options.opacity * 5, 1),
stdDeviation: options.width / 2
}
}];
};
/**
* Parse a simple HTML string into SVG tspans. Called internally when text
* is set on an SVGElement. The function supports a subset of HTML tags, CSS
* text features like `width`, `text-overflow`, `white-space`, and also
* attributes like `href` and `style`.
*
* @private
* @function Highcharts.SVGRenderer#buildText
*
* @param {Highcharts.SVGElement} wrapper
* The parent SVGElement.
*/
SVGRenderer.prototype.buildText = function (wrapper) {
new SVG_TextBuilder(wrapper).buildSVG();
};
/**
* Returns white for dark colors and black for bright colors, based on W3C's
* definition of [Relative luminance](
* https://www.w3.org/WAI/GL/wiki/Relative_luminance).
*
* @function Highcharts.SVGRenderer#getContrast
*
* @param {Highcharts.ColorString} color
* The color to get the contrast for.
*
* @return {Highcharts.ColorString}
* The contrast color, either `#000000` or `#FFFFFF`.
*/
SVGRenderer.prototype.getContrast = function (color) {
// #6216, #17273
var rgba = Color_Color.parse(color).rgba
.map(function (b8) {
var c = b8 / 255;
return c <= 0.03928 ?
c / 12.92 :
Math.pow((c + 0.055) / 1.055, 2.4);
});
// Relative luminance
var l = 0.2126 * rgba[0] + 0.7152 * rgba[1] + 0.0722 * rgba[2];
// Use white or black based on which provides more contrast
return 1.05 / (l + 0.05) > (l + 0.05) / 0.05 ? '#FFFFFF' : '#000000';
};
/**
* Create a button with preset states. Styles for the button can either be
* set as arguments, or a general theme for all buttons can be set by the
* `global.buttonTheme` option.
*
* @function Highcharts.SVGRenderer#button
*
* @param {string} text
* The text or HTML to draw.
*
* @param {number} x
* The x position of the button's left side.
*
* @param {number} y
* The y position of the button's top side.
*
* @param {Highcharts.EventCallbackFunction<Highcharts.SVGElement>} callback
* The function to execute on button click or touch.
*
* @param {Highcharts.SVGAttributes} [theme]
* SVG attributes for the normal state.
*
* @param {Highcharts.SVGAttributes} [hoverState]
* SVG attributes for the hover state.
*
* @param {Highcharts.SVGAttributes} [selectState]
* SVG attributes for the pressed state.
*
* @param {Highcharts.SVGAttributes} [disabledState]
* SVG attributes for the disabled state.
*
* @param {Highcharts.SymbolKeyValue} [shape=rect]
* The shape type.
*
* @param {boolean} [useHTML=false]
* Whether to use HTML to render the label.
*
* @return {Highcharts.SVGElement}
* The button element.
*/
SVGRenderer.prototype.button = function (text, x, y, callback, theme, hoverState, selectState, disabledState, shape, useHTML) {
if (theme === void 0) { theme = {}; }
var label = this.label(text,
x,
y,
shape,
void 0,
void 0,
useHTML,
void 0, 'button'),
styledMode = this.styledMode,
args = arguments;
var curState = 0;
theme = SVGRenderer_merge(SVGRenderer_defaultOptions.global.buttonTheme, theme);
// @todo Consider moving this to a lower level, like .attr
if (styledMode) {
delete theme.fill;
delete theme.stroke;
delete theme['stroke-width'];
}
var states = theme.states || {},
normalStyle = theme.style || {};
delete theme.states;
delete theme.style;
// Presentational
var stateAttribs = [
HTML_AST.filterUserAttributes(theme)
],
// The string type is a mistake, it is just for compliance with
// SVGAttribute and is not used in button theme.
stateStyles = [normalStyle];
if (!styledMode) {
['hover', 'select', 'disabled'].forEach(function (stateName, i) {
stateAttribs.push(SVGRenderer_merge(stateAttribs[0], HTML_AST.filterUserAttributes(args[i + 5] || states[stateName] || {})));
stateStyles.push(stateAttribs[i + 1].style);
delete stateAttribs[i + 1].style;
});
}
// Add the events. IE9 and IE10 need mouseover and mouseout to function
// (#667).
SVGRenderer_addEvent(label.element, isMS ? 'mouseover' : 'mouseenter', function () {
if (curState !== 3) {
label.setState(1);
}
});
SVGRenderer_addEvent(label.element, isMS ? 'mouseout' : 'mouseleave', function () {
if (curState !== 3) {
label.setState(curState);
}
});
label.setState = function (state) {
if (state === void 0) { state = 0; }
// Hover state is temporary, don't record it
if (state !== 1) {
label.state = curState = state;
}
// Update visuals
label
.removeClass(/highcharts-button-(normal|hover|pressed|disabled)/)
.addClass('highcharts-button-' +
['normal', 'hover', 'pressed', 'disabled'][state]);
if (!styledMode) {
label.attr(stateAttribs[state]);
var css_1 = stateStyles[state];
if (SVGRenderer_isObject(css_1)) {
label.css(css_1);
}
}
};
label.attr(stateAttribs[0]);
// Presentational attributes
if (!styledMode) {
label.css(SVGRenderer_extend({ cursor: 'default' }, normalStyle));
// HTML labels don't need to handle pointer events because click and
// mouseenter/mouseleave is bound to the underlying <g> element.
// Should this be reconsidered, we need more complex logic to share
// events between the <g> and its <div> counterpart, and avoid
// triggering mouseenter/mouseleave when hovering from one to the
// other (#17440).
if (useHTML) {
label.text.css({ pointerEvents: 'none' });
}
}
return label
.on('touchstart', function (e) { return e.stopPropagation(); })
.on('click', function (e) {
if (curState !== 3) {
callback.call(label, e);
}
});
};
/**
* Make a straight line crisper by not spilling out to neighbour pixels.
*
* @function Highcharts.SVGRenderer#crispLine
*
* @param {Highcharts.SVGPathArray} points
* The original points on the format `[['M', 0, 0], ['L', 100, 0]]`.
*
* @param {number} width
* The width of the line.
*
* @return {Highcharts.SVGPathArray}
* The original points array, but modified to render crisply.
*/
SVGRenderer.prototype.crispLine = function (points, width) {
var start = points[0],
end = points[1];
// Normalize to a crisp line
if (SVGRenderer_defined(start[1]) && start[1] === end[1]) {
start[1] = end[1] = SVGRenderer_crisp(start[1], width);
}
if (SVGRenderer_defined(start[2]) && start[2] === end[2]) {
start[2] = end[2] = SVGRenderer_crisp(start[2], width);
}
return points;
};
/**
* Draw a path, wraps the SVG `path` element.
*
* @sample highcharts/members/renderer-path-on-chart/
* Draw a path in a chart
* @sample highcharts/members/renderer-path/
* Draw a path independent from a chart
*
* @example
* let path = renderer.path(['M', 10, 10, 'L', 30, 30, 'z'])
* .attr({ stroke: '#ff00ff' })
* .add();
*
* @function Highcharts.SVGRenderer#path
*
* @param {Highcharts.SVGPathArray} [path]
* An SVG path definition in array form.
*
* @return {Highcharts.SVGElement}
* The generated wrapper element.
*
*/ /**
* Draw a path, wraps the SVG `path` element.
*
* @function Highcharts.SVGRenderer#path
*
* @param {Highcharts.SVGAttributes} [attribs]
* The initial attributes.
*
* @return {Highcharts.SVGElement}
* The generated wrapper element.
*/
SVGRenderer.prototype.path = function (path) {
var attribs = (this.styledMode ? {} : {
fill: 'none'
});
if (SVGRenderer_isArray(path)) {
attribs.d = path;
}
else if (SVGRenderer_isObject(path)) { // Attributes
SVGRenderer_extend(attribs, path);
}
return this.createElement('path').attr(attribs);
};
/**
* Draw a circle, wraps the SVG `circle` element.
*
* @sample highcharts/members/renderer-circle/
* Drawing a circle
*
* @function Highcharts.SVGRenderer#circle
*
* @param {number} [x]
* The center x position.
*
* @param {number} [y]
* The center y position.
*
* @param {number} [r]
* The radius.
*
* @return {Highcharts.SVGElement}
* The generated wrapper element.
*/ /**
* Draw a circle, wraps the SVG `circle` element.
*
* @function Highcharts.SVGRenderer#circle
*
* @param {Highcharts.SVGAttributes} [attribs]
* The initial attributes.
*
* @return {Highcharts.SVGElement}
* The generated wrapper element.
*/
SVGRenderer.prototype.circle = function (x, y, r) {
var attribs = (SVGRenderer_isObject(x) ?
x :
typeof x === 'undefined' ? {} : { x: x, y: y, r: r }), wrapper = this.createElement('circle');
// Setting x or y translates to cx and cy
wrapper.xSetter = wrapper.ySetter = function (value, key, element) {
element.setAttribute('c' + key, value);
};
return wrapper.attr(attribs);
};
/**
* Draw and return an arc.
*
* @sample highcharts/members/renderer-arc/
* Drawing an arc
*
* @function Highcharts.SVGRenderer#arc
*
* @param {number} [x=0]
* Center X position.
*
* @param {number} [y=0]
* Center Y position.
*
* @param {number} [r=0]
* The outer radius' of the arc.
*
* @param {number} [innerR=0]
* Inner radius like used in donut charts.
*
* @param {number} [start=0]
* The starting angle of the arc in radians, where 0 is to the right and
* `-Math.PI/2` is up.
*
* @param {number} [end=0]
* The ending angle of the arc in radians, where 0 is to the right and
* `-Math.PI/2` is up.
*
* @return {Highcharts.SVGElement}
* The generated wrapper element.
*/ /**
* Draw and return an arc. Overloaded function that takes arguments object.
*
* @function Highcharts.SVGRenderer#arc
*
* @param {Highcharts.SVGAttributes} attribs
* Initial SVG attributes.
*
* @return {Highcharts.SVGElement}
* The generated wrapper element.
*/
SVGRenderer.prototype.arc = function (x, y, r, innerR, start, end) {
var options;
if (SVGRenderer_isObject(x)) {
options = x;
y = options.y;
r = options.r;
innerR = options.innerR;
start = options.start;
end = options.end;
x = options.x;
}
else {
options = { innerR: innerR, start: start, end: end };
}
// Arcs are defined as symbols for the ability to set
// attributes in attr and animate
var arc = this.symbol('arc',
x,
y,
r,
r,
options);
arc.r = r; // #959
return arc;
};
/**
* Draw and return a rectangle.
*
* @function Highcharts.SVGRenderer#rect
*
* @param {number} [x]
* Left position.
*
* @param {number} [y]
* Top position.
*
* @param {number} [width]
* Width of the rectangle.
*
* @param {number} [height]
* Height of the rectangle.
*
* @param {number} [r]
* Border corner radius.
*
* @param {number} [strokeWidth]
* A stroke width can be supplied to allow crisp drawing.
*
* @return {Highcharts.SVGElement}
* The generated wrapper element.
*/ /**
* Draw and return a rectangle.
*
* @sample highcharts/members/renderer-rect-on-chart/
* Draw a rectangle in a chart
* @sample highcharts/members/renderer-rect/
* Draw a rectangle independent from a chart
*
* @function Highcharts.SVGRenderer#rect
*
* @param {Highcharts.SVGAttributes} [attributes]
* General SVG attributes for the rectangle.
*
* @return {Highcharts.SVGElement}
* The generated wrapper element.
*/
SVGRenderer.prototype.rect = function (x, y, width, height, r, strokeWidth) {
var attribs = (SVGRenderer_isObject(x) ?
x :
typeof x === 'undefined' ?
{} :
{
x: x,
y: y,
r: r,
width: Math.max(width || 0, 0),
height: Math.max(height || 0, 0)
}),
wrapper = this.createElement('rect');
if (!this.styledMode) {
if (typeof strokeWidth !== 'undefined') {
attribs['stroke-width'] = strokeWidth;
SVGRenderer_extend(attribs, wrapper.crisp(attribs));
}
attribs.fill = 'none';
}
wrapper.rSetter = function (value, _key, element) {
wrapper.r = value;
SVGRenderer_attr(element, {
rx: value,
ry: value
});
};
wrapper.rGetter = function () {
return wrapper.r || 0;
};
return wrapper.attr(attribs);
};
/**
* Draw and return a rectangle with advanced corner rounding options.
*
* @function Highcharts.SVGRenderer#roundedRect
*
* @param {Highcharts.SVGAttributes} attribs
* Attributes
* @return {Highcharts.SVGElement}
* The generated wrapper element.
*/
SVGRenderer.prototype.roundedRect = function (attribs) {
return this.symbol('roundedRect').attr(attribs);
};
/**
* Resize the {@link SVGRenderer#box} and re-align all aligned child
* elements.
*
* @sample highcharts/members/renderer-g/
* Show and hide grouped objects
*
* @function Highcharts.SVGRenderer#setSize
*
* @param {number} width
* The new pixel width.
*
* @param {number} height
* The new pixel height.
*
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animate=true]
* Whether and how to animate.
*/
SVGRenderer.prototype.setSize = function (width, height, animate) {
var renderer = this;
renderer.width = width;
renderer.height = height;
renderer.boxWrapper.animate({
width: width,
height: height
}, {
step: function () {
this.attr({
viewBox: '0 0 ' + this.attr('width') + ' ' +
this.attr('height')
});
},
duration: SVGRenderer_pick(animate, true) ? void 0 : 0
});
renderer.alignElements();
};
/**
* Create and return an svg group element. Child
* {@link Highcharts.SVGElement} objects are added to the group by using the
* group as the first parameter in {@link Highcharts.SVGElement#add|add()}.
*
* @function Highcharts.SVGRenderer#g
*
* @param {string} [name]
* The group will be given a class name of `highcharts-{name}`. This
* can be used for styling and scripting.
*
* @return {Highcharts.SVGElement}
* The generated wrapper element.
*/
SVGRenderer.prototype.g = function (name) {
var elem = this.createElement('g');
return name ?
elem.attr({ 'class': 'highcharts-' + name }) :
elem;
};
/**
* Display an image.
*
* @sample highcharts/members/renderer-image-on-chart/
* Add an image in a chart
* @sample highcharts/members/renderer-image/
* Add an image independent of a chart
*
* @function Highcharts.SVGRenderer#image
*
* @param {string} href
* The image source.
*
* @param {number} [x]
* The X position.
*
* @param {number} [y]
* The Y position.
*
* @param {number} [width]
* The image width. If omitted, it defaults to the image file width.
*
* @param {number} [height]
* The image height. If omitted it defaults to the image file
* height.
*
* @param {Function} [onload]
* Event handler for image load.
*
* @return {Highcharts.SVGElement}
* The generated wrapper element.
*/
SVGRenderer.prototype.image = function (href, x, y, width, height, onload) {
var attribs = { preserveAspectRatio: 'none' };
// Optional properties (#11756)
if (SVGRenderer_isNumber(x)) {
attribs.x = x;
}
if (SVGRenderer_isNumber(y)) {
attribs.y = y;
}
if (SVGRenderer_isNumber(width)) {
attribs.width = width;
}
if (SVGRenderer_isNumber(height)) {
attribs.height = height;
}
var elemWrapper = this.createElement('image').attr(attribs),
onDummyLoad = function (e) {
elemWrapper.attr({ href: href });
onload.call(elemWrapper, e);
};
// Add load event if supplied
if (onload) {
// We have to use a dummy HTML image since IE support for SVG image
// load events is very buggy. First set a transparent src, wait for
// dummy to load, and then add the real src to the SVG image.
elemWrapper.attr({
/* eslint-disable-next-line max-len */
href: 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='
});
var dummy = new SVGRenderer_win.Image();
SVGRenderer_addEvent(dummy, 'load', onDummyLoad);
dummy.src = href;
if (dummy.complete) {
onDummyLoad({});
}
}
else {
elemWrapper.attr({ href: href });
}
return elemWrapper;
};
/**
* Draw a symbol out of pre-defined shape paths from
* {@link SVGRenderer#symbols}.
* It is used in Highcharts for point makers, which cake a `symbol` option,
* and label and button backgrounds like in the tooltip and stock flags.
*
* @function Highcharts.SVGRenderer#symbol
*
* @param {string} symbol
* The symbol name.
*
* @param {number} [x]
* The X coordinate for the top left position.
*
* @param {number} [y]
* The Y coordinate for the top left position.
*
* @param {number} [width]
* The pixel width.
*
* @param {number} [height]
* The pixel height.
*
* @param {Highcharts.SymbolOptionsObject} [options]
* Additional options, depending on the actual symbol drawn.
*
* @return {Highcharts.SVGElement}
* SVG symbol.
*/
SVGRenderer.prototype.symbol = function (symbol, x, y, width, height, options) {
var ren = this, imageRegex = /^url\((.*?)\)$/, isImage = imageRegex.test(symbol), sym = (!isImage && (this.symbols[symbol] ? symbol : 'circle')),
// Get the symbol definition function
symbolFn = (sym && this.symbols[sym]);
var obj,
path,
imageSrc,
centerImage;
if (symbolFn) {
// Check if there's a path defined for this symbol
if (typeof x === 'number') {
path = symbolFn.call(this.symbols, x || 0, y || 0, width || 0, height || 0, options);
}
obj = this.path(path);
if (!ren.styledMode) {
obj.attr('fill', 'none');
}
// Expando properties for use in animate and attr
SVGRenderer_extend(obj, {
symbolName: (sym || void 0),
x: x,
y: y,
width: width,
height: height
});
if (options) {
SVGRenderer_extend(obj, options);
}
// Image symbols
}
else if (isImage) {
imageSrc = symbol.match(imageRegex)[1];
// Create the image synchronously, add attribs async
var img_1 = obj = this.image(imageSrc);
// The image width is not always the same as the symbol width. The
// image may be centered within the symbol, as is the case when
// image shapes are used as label backgrounds, for example in flags.
img_1.imgwidth = SVGRenderer_pick(options && options.width, symbolSizes[imageSrc] && symbolSizes[imageSrc].width);
img_1.imgheight = SVGRenderer_pick(options && options.height, symbolSizes[imageSrc] && symbolSizes[imageSrc].height);
/**
* Set the size and position
*/
centerImage = function (obj) { return obj.attr({
width: obj.width,
height: obj.height
}); };
/**
* Width and height setters that take both the image's physical size
* and the label size into consideration, and translates the image
* to center within the label.
*/
['width', 'height'].forEach(function (key) {
img_1["" + key + "Setter"] = function (value, key) {
this[key] = value;
var _a = this,
alignByTranslate = _a.alignByTranslate,
element = _a.element,
width = _a.width,
height = _a.height,
imgwidth = _a.imgwidth,
imgheight = _a.imgheight,
imgSize = key === 'width' ? imgwidth : imgheight;
var scale = 1;
// Scale and center the image within its container. The name
// `backgroundSize` is taken from the CSS spec, but the
// value `within` is made up. Other possible values in the
// spec, `cover` and `contain`, can be implemented if
// needed.
if (options &&
options.backgroundSize === 'within' &&
width &&
height &&
imgwidth &&
imgheight) {
scale = Math.min(width / imgwidth, height / imgheight);
// Update both width and height to keep the ratio
// correct (#17315)
SVGRenderer_attr(element, {
width: Math.round(imgwidth * scale),
height: Math.round(imgheight * scale)
});
}
else if (element && imgSize) {
element.setAttribute(key, imgSize);
}
if (!alignByTranslate && imgwidth && imgheight) {
this.translate(((width || 0) - (imgwidth * scale)) / 2, ((height || 0) - (imgheight * scale)) / 2);
}
};
});
if (SVGRenderer_defined(x)) {
img_1.attr({
x: x,
y: y
});
}
img_1.isImg = true;
img_1.symbolUrl = symbol;
if (SVGRenderer_defined(img_1.imgwidth) && SVGRenderer_defined(img_1.imgheight)) {
centerImage(img_1);
}
else {
// Initialize image to be 0 size so export will still function
// if there's no cached sizes.
img_1.attr({ width: 0, height: 0 });
// Create a dummy JavaScript image to get the width and height.
SVGRenderer_createElement('img', {
onload: function () {
var chart = SVGRenderer_charts[ren.chartIndex];
// Special case for SVGs on IE11, the width is not
// accessible until the image is part of the DOM
// (#2854).
if (this.width === 0) {
SVGRenderer_css(this, {
position: 'absolute',
top: '-999em'
});
SVGRenderer_doc.body.appendChild(this);
}
// Center the image
symbolSizes[imageSrc] = {
width: this.width,
height: this.height
};
img_1.imgwidth = this.width;
img_1.imgheight = this.height;
if (img_1.element) {
centerImage(img_1);
}
// Clean up after #2854 workaround.
if (this.parentNode) {
this.parentNode.removeChild(this);
}
// Fire the load event when all external images are
// loaded
ren.imgCount--;
if (!ren.imgCount && chart && !chart.hasLoaded) {
chart.onload();
}
},
src: imageSrc
});
this.imgCount++;
}
}
return obj;
};
/**
* Define a clipping rectangle. The clipping rectangle is later applied
* to {@link SVGElement} objects through the {@link SVGElement#clip}
* function.
*
* This function is deprecated as of v11.2. Instead, use a regular shape
* (`rect`, `path` etc), and the `SVGElement.clipTo` function.
*
* @example
* let circle = renderer.circle(100, 100, 100)
* .attr({ fill: 'red' })
* .add();
* let clipRect = renderer.clipRect(100, 100, 100, 100);
*
* // Leave only the lower right quarter visible
* circle.clip(clipRect);
*
* @deprecated
*
* @function Highcharts.SVGRenderer#clipRect
*
* @param {number} [x]
*
* @param {number} [y]
*
* @param {number} [width]
*
* @param {number} [height]
*
* @return {Highcharts.ClipRectElement}
* A clipping rectangle.
*/
SVGRenderer.prototype.clipRect = function (x, y, width, height) {
return this.rect(x, y, width, height, 0);
};
/**
* Draw text. The text can contain a subset of HTML, like spans and anchors
* and some basic text styling of these. For more advanced features like
* border and background, use {@link Highcharts.SVGRenderer#label} instead.
* To update the text after render, run `text.attr({ text: 'New text' })`.
*
* @sample highcharts/members/renderer-text-on-chart/
* Annotate the chart freely
* @sample highcharts/members/renderer-on-chart/
* Annotate with a border and in response to the data
* @sample highcharts/members/renderer-text/
* Formatted text
*
* @function Highcharts.SVGRenderer#text
*
* @param {string} [str]
* The text of (subset) HTML to draw.
*
* @param {number} [x]
* The x position of the text's lower left corner.
*
* @param {number} [y]
* The y position of the text's lower left corner.
*
* @param {boolean} [useHTML=false]
* Use HTML to render the text.
*
* @return {Highcharts.SVGElement}
* The text object.
*/
SVGRenderer.prototype.text = function (str, x, y, useHTML) {
var renderer = this,
attribs = {};
if (useHTML && (renderer.allowHTML || !renderer.forExport)) {
return renderer.html(str, x, y);
}
attribs.x = Math.round(x || 0); // X always needed for line-wrap logic
if (y) {
attribs.y = Math.round(y);
}
if (SVGRenderer_defined(str)) {
attribs.text = str;
}
var wrapper = renderer.createElement('text').attr(attribs);
if (!useHTML || (renderer.forExport && !renderer.allowHTML)) {
wrapper.xSetter = function (value, key, element) {
var tspans = element.getElementsByTagName('tspan'),
parentVal = element.getAttribute(key);
for (var i = 0, tspan = void 0; i < tspans.length; i++) {
tspan = tspans[i];
// If the x values are equal, the tspan represents a line
// break
if (tspan.getAttribute(key) === parentVal) {
tspan.setAttribute(key, value);
}
}
element.setAttribute(key, value);
};
}
return wrapper;
};
/**
* Utility to return the baseline offset and total line height from the font
* size.
*
* @function Highcharts.SVGRenderer#fontMetrics
*
* @param {Highcharts.SVGElement|Highcharts.SVGDOMElement|number} [element]
* The element to inspect for a current font size. If a number is
* given, it's used as a fall back for direct font size in pixels.
*
* @return {Highcharts.FontMetricsObject}
* The font metrics.
*/
SVGRenderer.prototype.fontMetrics = function (element) {
var f = SVGRenderer_pInt(SVG_SVGElement.prototype.getStyle.call(element, 'font-size') || 0);
// Empirical values found by comparing font size and bounding box
// height. Applies to the default font family.
// https://jsfiddle.net/highcharts/7xvn7/
var h = f < 24 ? f + 3 : Math.round(f * 1.2),
b = Math.round(h * 0.8);
return {
// Line height
h: h,
// Baseline
b: b,
// Font size
f: f
};
};
/**
* Correct X and Y positioning of a label for rotation (#1764).
*
* @private
* @function Highcharts.SVGRenderer#rotCorr
*/
SVGRenderer.prototype.rotCorr = function (baseline, rotation, alterY) {
var y = baseline;
if (rotation && alterY) {
y = Math.max(y * Math.cos(rotation * SVGRenderer_deg2rad), 4);
}
return {
x: (-baseline / 3) * Math.sin(rotation * SVGRenderer_deg2rad),
y: y
};
};
/**
* Compatibility function to convert the legacy one-dimensional path array
* into an array of segments.
*
* It is used in maps to parse the `path` option, and in SVGRenderer.dSetter
* to support legacy paths from demos.
*
* @private
* @function Highcharts.SVGRenderer#pathToSegments
*/
SVGRenderer.prototype.pathToSegments = function (path) {
var ret = [];
var segment = [];
var commandLength = {
A: 8,
C: 7,
H: 2,
L: 3,
M: 3,
Q: 5,
S: 5,
T: 3,
V: 2
};
// Short, non-typesafe parsing of the one-dimensional array. It splits
// the path on any string. This is not type checked against the tuple
// types, but is shorter, and doesn't require specific checks for any
// command type in SVG.
for (var i = 0; i < path.length; i++) {
// Command skipped, repeat previous or insert L/l for M/m
if (SVGRenderer_isString(segment[0]) &&
SVGRenderer_isNumber(path[i]) &&
segment.length === commandLength[(segment[0].toUpperCase())]) {
path.splice(i, 0, segment[0].replace('M', 'L').replace('m', 'l'));
}
// Split on string
if (typeof path[i] === 'string') {
if (segment.length) {
ret.push(segment.slice(0));
}
segment.length = 0;
}
segment.push(path[i]);
}
ret.push(segment.slice(0));
return ret;
/*
// Fully type-safe version where each tuple type is checked. The
// downside is filesize and a lack of flexibility for unsupported
// commands
const ret: SVGPath = [],
commands = {
A: 7,
C: 6,
H: 1,
L: 2,
M: 2,
Q: 4,
S: 4,
T: 2,
V: 1,
Z: 0
};
let i = 0,
lastI = 0,
lastCommand;
while (i < path.length) {
const item = path[i];
let command;
if (typeof item === 'string') {
command = item;
i += 1;
} else {
command = lastCommand || 'M';
}
// Upper case
const commandUC = command.toUpperCase();
if (commandUC in commands) {
// No numeric parameters
if (command === 'Z' || command === 'z') {
ret.push([command]);
// One numeric parameter
} else {
const val0 = path[i];
if (typeof val0 === 'number') {
// Horizontal line to
if (command === 'H' || command === 'h') {
ret.push([command, val0]);
i += 1;
// Vertical line to
} else if (command === 'V' || command === 'v') {
ret.push([command, val0]);
i += 1;
// Two numeric parameters
} else {
const val1 = path[i + 1];
if (typeof val1 === 'number') {
// lineTo
if (command === 'L' || command === 'l') {
ret.push([command, val0, val1]);
i += 2;
// moveTo
} else if (command === 'M' || command === 'm') {
ret.push([command, val0, val1]);
i += 2;
// Smooth quadratic bezier
} else if (command === 'T' || command === 't') {
ret.push([command, val0, val1]);
i += 2;
// Four numeric parameters
} else {
const val2 = path[i + 2],
val3 = path[i + 3];
if (
typeof val2 === 'number' &&
typeof val3 === 'number'
) {
// Quadratic bezier to
if (
command === 'Q' ||
command === 'q'
) {
ret.push([
command,
val0,
val1,
val2,
val3
]);
i += 4;
// Smooth cubic bezier to
} else if (
command === 'S' ||
command === 's'
) {
ret.push([
command,
val0,
val1,
val2,
val3
]);
i += 4;
// Six numeric parameters
} else {
const val4 = path[i + 4],
val5 = path[i + 5];
if (
typeof val4 === 'number' &&
typeof val5 === 'number'
) {
// Curve to
if (
command === 'C' ||
command === 'c'
) {
ret.push([
command,
val0,
val1,
val2,
val3,
val4,
val5
]);
i += 6;
// Seven numeric parameters
} else {
const val6 = path[i + 6];
// Arc to
if (
typeof val6 ===
'number' &&
(
command === 'A' ||
command === 'a'
)
) {
ret.push([
command,
val0,
val1,
val2,
val3,
val4,
val5,
val6
]);
i += 7;
}
}
}
}
}
}
}
}
}
}
}
// An unmarked command following a moveTo is a lineTo
lastCommand = command === 'M' ? 'L' : command;
if (i === lastI) {
break;
}
lastI = i;
}
return ret;
*/
};
/**
* Draw a label, which is an extended text element with support for border
* and background. Highcharts creates a `g` element with a text and a `path`
* or `rect` inside, to make it behave somewhat like a HTML div. Border and
* background are set through `stroke`, `stroke-width` and `fill` attributes
* using the {@link Highcharts.SVGElement#attr|attr} method. To update the
* text after render, run `label.attr({ text: 'New text' })`.
*
* @sample highcharts/members/renderer-label-on-chart/
* A label on the chart
*
* @function Highcharts.SVGRenderer#label
*
* @param {string} str
* The initial text string or (subset) HTML to render.
*
* @param {number} x
* The x position of the label's left side.
*
* @param {number} [y]
* The y position of the label's top side or baseline, depending on
* the `baseline` parameter.
*
* @param {string} [shape='rect']
* The shape of the label's border/background, if any. Defaults to
* `rect`. Other possible values are `callout` or other shapes
* defined in {@link Highcharts.SVGRenderer#symbols}.
*
* @param {number} [anchorX]
* In case the `shape` has a pointer, like a flag, this is the
* coordinates it should be pinned to.
*
* @param {number} [anchorY]
* In case the `shape` has a pointer, like a flag, this is the
* coordinates it should be pinned to.
*
* @param {boolean} [useHTML=false]
* Whether to use HTML to render the label.
*
* @param {boolean} [baseline=false]
* Whether to position the label relative to the text baseline,
* like {@link Highcharts.SVGRenderer#text|renderer.text}, or to the
* upper border of the rectangle.
*
* @param {string} [className]
* Class name for the group.
*
* @return {Highcharts.SVGElement}
* The generated label.
*/
SVGRenderer.prototype.label = function (str, x, y, shape, anchorX, anchorY, useHTML, baseline, className) {
return new SVG_SVGLabel(this, str, x, y, shape, anchorX, anchorY, useHTML, baseline, className);
};
/**
* Re-align all aligned elements.
*
* @private
* @function Highcharts.SVGRenderer#alignElements
*/
SVGRenderer.prototype.alignElements = function () {
this.alignedObjects.forEach(function (el) { return el.align(); });
};
return SVGRenderer;
}());
SVGRenderer_extend(SVGRenderer.prototype, {
/**
* A pointer to the renderer's associated Element class.
*
* @name Highcharts.SVGRenderer#Element
* @type {Highcharts.SVGElement}
*/
Element: SVG_SVGElement,
SVG_NS: SVGRenderer_SVG_NS,
/**
* A collection of characters mapped to HTML entities. When `useHTML` on an
* element is true, these entities will be rendered correctly by HTML. In
* the SVG pseudo-HTML, they need to be unescaped back to simple characters,
* so for example `<` will render as `<`.
*
* @example
* // Add support for unescaping quotes
* Highcharts.SVGRenderer.prototype.escapes['"'] = '"';
*
* @name Highcharts.SVGRenderer#escapes
* @type {Highcharts.Dictionary<string>}
*/
escapes: {
'&': '&',
'<': '<',
'>': '>',
"'": ''', // eslint-disable-line quotes
'"': '"'
},
/**
* An extendable collection of functions for defining symbol paths.
*
* @name Highcharts.SVGRenderer#symbols
* @type {Highcharts.SymbolDictionary}
*/
symbols: SVG_Symbols,
/**
* Dummy function for plugins, called every time the renderer is updated.
* Prior to Highcharts 5, this was used for the canvg renderer.
*
* @deprecated
* @function Highcharts.SVGRenderer#draw
*/
draw: noop
});
/* *
*
* Registry
*
* */
Renderer_RendererRegistry.registerRendererType('svg', SVGRenderer, true);
/* *
*
* Export Default
*
* */
/* harmony default export */ var SVG_SVGRenderer = (SVGRenderer);
/* *
*
* API Declarations
*
* */
/**
* A clipping rectangle that can be applied to one or more {@link SVGElement}
* instances. It is instantiated with the {@link SVGRenderer#clipRect} function
* and applied with the {@link SVGElement#clip} function.
*
* @example
* let circle = renderer.circle(100, 100, 100)
* .attr({ fill: 'red' })
* .add();
* let clipRect = renderer.clipRect(100, 100, 100, 100);
*
* // Leave only the lower right quarter visible
* circle.clip(clipRect);
*
* @typedef {Highcharts.SVGElement} Highcharts.ClipRectElement
*/
/**
* The font metrics.
*
* @interface Highcharts.FontMetricsObject
*/ /**
* The baseline relative to the top of the box.
*
* @name Highcharts.FontMetricsObject#b
* @type {number}
*/ /**
* The font size.
*
* @name Highcharts.FontMetricsObject#f
* @type {number}
*/ /**
* The line height.
*
* @name Highcharts.FontMetricsObject#h
* @type {number}
*/
/**
* An object containing `x` and `y` properties for the position of an element.
*
* @interface Highcharts.PositionObject
*/ /**
* X position of the element.
* @name Highcharts.PositionObject#x
* @type {number}
*/ /**
* Y position of the element.
* @name Highcharts.PositionObject#y
* @type {number}
*/
/**
* A rectangle.
*
* @interface Highcharts.RectangleObject
*/ /**
* Height of the rectangle.
* @name Highcharts.RectangleObject#height
* @type {number}
*/ /**
* Width of the rectangle.
* @name Highcharts.RectangleObject#width
* @type {number}
*/ /**
* Horizontal position of the rectangle.
* @name Highcharts.RectangleObject#x
* @type {number}
*/ /**
* Vertical position of the rectangle.
* @name Highcharts.RectangleObject#y
* @type {number}
*/
/**
* The shadow options.
*
* @interface Highcharts.ShadowOptionsObject
*/ /**
* The shadow color.
* @name Highcharts.ShadowOptionsObject#color
* @type {Highcharts.ColorString|undefined}
* @default ${palette.neutralColor100}
*/ /**
* The horizontal offset from the element.
*
* @name Highcharts.ShadowOptionsObject#offsetX
* @type {number|undefined}
* @default 1
*/ /**
* The vertical offset from the element.
* @name Highcharts.ShadowOptionsObject#offsetY
* @type {number|undefined}
* @default 1
*/ /**
* The shadow opacity.
*
* @name Highcharts.ShadowOptionsObject#opacity
* @type {number|undefined}
* @default 0.15
*/ /**
* The shadow width or distance from the element.
* @name Highcharts.ShadowOptionsObject#width
* @type {number|undefined}
* @default 3
*/
/**
* @interface Highcharts.SizeObject
*/ /**
* @name Highcharts.SizeObject#height
* @type {number}
*/ /**
* @name Highcharts.SizeObject#width
* @type {number}
*/
/**
* Array of path commands, that will go into the `d` attribute of an SVG
* element.
*
* @typedef {Array<(Array<Highcharts.SVGPathCommand>|Array<Highcharts.SVGPathCommand,number>|Array<Highcharts.SVGPathCommand,number,number>|Array<Highcharts.SVGPathCommand,number,number,number,number>|Array<Highcharts.SVGPathCommand,number,number,number,number,number,number>|Array<Highcharts.SVGPathCommand,number,number,number,number,number,number,number>)>} Highcharts.SVGPathArray
*/
/**
* Possible path commands in an SVG path array. Valid values are `A`, `C`, `H`,
* `L`, `M`, `Q`, `S`, `T`, `V`, `Z`.
*
* @typedef {string} Highcharts.SVGPathCommand
* @validvalue ["a","c","h","l","m","q","s","t","v","z","A","C","H","L","M","Q","S","T","V","Z"]
*/
/**
* An extendable collection of functions for defining symbol paths. Symbols are
* used internally for point markers, button and label borders and backgrounds,
* or custom shapes. Extendable by adding to {@link SVGRenderer#symbols}.
*
* @interface Highcharts.SymbolDictionary
*/ /**
* @name Highcharts.SymbolDictionary#[key:string]
* @type {Function|undefined}
*/ /**
* @name Highcharts.SymbolDictionary#arc
* @type {Function|undefined}
*/ /**
* @name Highcharts.SymbolDictionary#callout
* @type {Function|undefined}
*/ /**
* @name Highcharts.SymbolDictionary#circle
* @type {Function|undefined}
*/ /**
* @name Highcharts.SymbolDictionary#diamond
* @type {Function|undefined}
*/ /**
* @name Highcharts.SymbolDictionary#square
* @type {Function|undefined}
*/ /**
* @name Highcharts.SymbolDictionary#triangle
* @type {Function|undefined}
*/
/**
* Can be one of `arc`, `callout`, `circle`, `diamond`, `square`, `triangle`,
* and `triangle-down`. Symbols are used internally for point markers, button
* and label borders and backgrounds, or custom shapes. Extendable by adding to
* {@link SVGRenderer#symbols}.
*
* @typedef {"arc"|"callout"|"circle"|"diamond"|"square"|"triangle"|"triangle-down"} Highcharts.SymbolKeyValue
*/
/**
* Additional options, depending on the actual symbol drawn.
*
* @interface Highcharts.SymbolOptionsObject
*/ /**
* The anchor X position for the `callout` symbol. This is where the chevron
* points to.
*
* @name Highcharts.SymbolOptionsObject#anchorX
* @type {number|undefined}
*/ /**
* The anchor Y position for the `callout` symbol. This is where the chevron
* points to.
*
* @name Highcharts.SymbolOptionsObject#anchorY
* @type {number|undefined}
*/ /**
* The end angle of an `arc` symbol.
*
* @name Highcharts.SymbolOptionsObject#end
* @type {number|undefined}
*/ /**
* Whether to draw `arc` symbol open or closed.
*
* @name Highcharts.SymbolOptionsObject#open
* @type {boolean|undefined}
*/ /**
* The radius of an `arc` symbol, or the border radius for the `callout` symbol.
*
* @name Highcharts.SymbolOptionsObject#r
* @type {number|undefined}
*/ /**
* The start angle of an `arc` symbol.
*
* @name Highcharts.SymbolOptionsObject#start
* @type {number|undefined}
*/
(''); // Keeps doclets above in transpiled file
;// ./code/es5/es-modules/Core/Renderer/HTML/HTMLElement.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var HTMLElement_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b,
p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (undefined && undefined.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var composed = Core_Globals.composed;
var HTMLElement_attr = Core_Utilities.attr, HTMLElement_css = Core_Utilities.css, HTMLElement_createElement = Core_Utilities.createElement, HTMLElement_defined = Core_Utilities.defined, HTMLElement_extend = Core_Utilities.extend, HTMLElement_getAlignFactor = Core_Utilities.getAlignFactor, HTMLElement_isNumber = Core_Utilities.isNumber, HTMLElement_pInt = Core_Utilities.pInt, HTMLElement_pushUnique = Core_Utilities.pushUnique;
/**
* The opacity and visibility properties are set as attributes on the main
* element and SVG groups, and as identical CSS properties on the HTML element
* and the ancestry divs. (#3542)
*
* @private
*/
function commonSetter(value, key, elem) {
var _a;
var style = ((_a = this.div) === null || _a === void 0 ? void 0 : _a.style) || elem.style;
SVG_SVGElement.prototype["" + key + "Setter"].call(this, value, key, elem);
if (style) {
style[key] = value;
}
}
/**
* Decorate each SVG group in the ancestry line. Each SVG `g` element that
* contains children with useHTML, will receive a `div` element counterpart to
* contain the HTML span. These div elements are translated and styled like
* original `g` counterparts.
*
* @private
*/
var decorateSVGGroup = function (g, container) {
var _a;
if (!g.div) {
var className = HTMLElement_attr(g.element, 'class'),
cssProto_1 = g.css;
// Create the parallel HTML group
var div_1 = HTMLElement_createElement('div', className ? { className: className } : void 0, __assign(__assign({
// Add HTML specific styles
position: 'absolute', left: "" + (g.translateX || 0) + "px", top: "" + (g.translateY || 0) + "px" }, g.styles), {
// Add g attributes that correspond to CSS
display: g.display, opacity: g.opacity, visibility: g.visibility }),
// The top group is appended to container
((_a = g.parentGroup) === null || _a === void 0 ? void 0 : _a.div) || container);
g.classSetter = function (value, key, element) {
element.setAttribute('class', value);
div_1.className = value;
};
/**
* Common translate setter for X and Y on the HTML group.
*
* Reverted the fix for #6957 due to positioning problems and offline
* export (#7254, #7280, #7529)
* @private
*/
g.translateXSetter = g.translateYSetter = function (value, key) {
g[key] = value;
div_1.style[key === 'translateX' ? 'left' : 'top'] = "" + value + "px";
g.doTransform = true;
};
g.opacitySetter = g.visibilitySetter = commonSetter;
// Extend the parent group's css function by updating the parallel div
// counterpart with the same style.
g.css = function (styles) {
// Call the base css method. The `parentGroup` can be either an
// SVGElement or an SVGLabel, in which the css method is extended
// (#19200).
cssProto_1.call(g, styles);
// #6794
if (styles.cursor) {
div_1.style.cursor = styles.cursor;
}
// #18821
if (styles.pointerEvents) {
div_1.style.pointerEvents = styles.pointerEvents;
}
return g;
};
// Event handling
g.on = function () {
SVG_SVGElement.prototype.on.apply({
element: div_1,
onEvents: g.onEvents
}, arguments);
return g;
};
g.div = div_1;
}
return g.div;
};
/* *
*
* Class
*
* */
var HTMLElement = /** @class */ (function (_super) {
HTMLElement_extends(HTMLElement, _super);
/* *
*
* Functions
*
* */
function HTMLElement(renderer, nodeName) {
var _this = _super.call(this,
renderer,
nodeName) || this;
_this.css(__assign({ position: 'absolute' }, (renderer.styledMode ? {} : {
fontFamily: renderer.style.fontFamily,
fontSize: renderer.style.fontSize
})));
return _this;
}
/* *
*
* Static Functions
*
* */
/**
* Compose
* @private
*/
HTMLElement.compose = function (SVGRendererClass) {
if (HTMLElement_pushUnique(composed, this.compose)) {
/**
* Create a HTML text node. This is used by the SVG renderer `text`
* and `label` functions through the `useHTML` parameter.
*
* @private
*/
SVGRendererClass.prototype.html = function (str, x, y) {
return new HTMLElement(this, 'span')
// Set the default attributes
.attr({
text: str,
x: Math.round(x),
y: Math.round(y)
});
};
}
};
/**
* Get the correction in X and Y positioning as the element is rotated.
* @private
*/
HTMLElement.prototype.getSpanCorrection = function (width, baseline, alignCorrection) {
this.xCorr = -width * alignCorrection;
this.yCorr = -baseline;
};
/**
* Apply CSS to HTML elements. This is used in text within SVG rendering.
* @private
*/
HTMLElement.prototype.css = function (styles) {
var element = this.element,
// When setting or unsetting the width style, we need to update
// transform (#8809)
isSettingWidth = (element.tagName === 'SPAN' &&
styles &&
'width' in styles),
textWidth = isSettingWidth && styles.width;
var doTransform;
if (isSettingWidth) {
delete styles.width;
this.textWidth = HTMLElement_pInt(textWidth) || void 0;
doTransform = true;
}
// Some properties require other properties to be set
if ((styles === null || styles === void 0 ? void 0 : styles.textOverflow) === 'ellipsis') {
styles.overflow = 'hidden';
}
if (styles === null || styles === void 0 ? void 0 : styles.lineClamp) {
styles.display = '-webkit-box';
styles.WebkitLineClamp = styles.lineClamp;
styles.WebkitBoxOrient = 'vertical';
styles.overflow = 'hidden';
}
// SVG natively supports setting font size as numbers. With HTML, the
// font size should behave in the same way (#21624).
if (HTMLElement_isNumber(Number(styles === null || styles === void 0 ? void 0 : styles.fontSize))) {
styles.fontSize = styles.fontSize + 'px';
}
HTMLElement_extend(this.styles, styles);
HTMLElement_css(element, styles);
// Now that all styles are applied, to the transform
if (doTransform) {
this.updateTransform();
}
return this;
};
/**
* The useHTML method for calculating the bounding box based on offsets.
* Called internally from the `SVGElement.getBBox` function and subsequently
* rotated.
*
* @private
*/
HTMLElement.prototype.htmlGetBBox = function () {
var element = this.element;
return {
x: element.offsetLeft,
y: element.offsetTop,
width: element.offsetWidth,
height: element.offsetHeight
};
};
/**
* Batch update styles and attributes related to transform
*
* @private
*/
HTMLElement.prototype.updateTransform = function () {
var _this = this;
var _a;
// Aligning non added elements is expensive
if (!this.added) {
this.alignOnAdd = true;
return;
}
var _b = this, element = _b.element, renderer = _b.renderer, rotation = _b.rotation, rotationOriginX = _b.rotationOriginX, rotationOriginY = _b.rotationOriginY, scaleX = _b.scaleX, scaleY = _b.scaleY, styles = _b.styles, _c = _b.textAlign, textAlign = _c === void 0 ? 'left' : _c, textWidth = _b.textWidth, _d = _b.translateX, translateX = _d === void 0 ? 0 : _d, _e = _b.translateY, translateY = _e === void 0 ? 0 : _e, _f = _b.x, x = _f === void 0 ? 0 : _f, _g = _b.y, y = _g === void 0 ? 0 : _g, _h = styles.display, display = _h === void 0 ? 'block' : _h, whiteSpace = styles.whiteSpace;
// Get the pixel length of the text
var getTextPxLength = function () {
if (_this.textPxLength) {
return _this.textPxLength;
}
// Reset multiline/ellipsis in order to read width (#4928,
// #5417)
HTMLElement_css(element, {
width: '',
whiteSpace: whiteSpace || 'nowrap'
});
return element.offsetWidth;
};
// Apply translate
HTMLElement_css(element, {
marginLeft: "" + translateX + "px",
marginTop: "" + translateY + "px"
});
if (element.tagName === 'SPAN') {
var currentTextTransform = [
rotation,
textAlign,
element.innerHTML,
textWidth,
this.textAlign
].join(','), parentPadding = (((_a = this.parentGroup) === null || _a === void 0 ? void 0 : _a.padding) * -1) || 0;
var baseline = void 0;
// Update textWidth. Use the memoized textPxLength if possible, to
// avoid the getTextPxLength function using elem.offsetWidth.
// Calling offsetWidth affects rendering time as it forces layout
// (#7656).
if (textWidth !== this.oldTextWidth) { // #983, #1254
var textPxLength = getTextPxLength(),
textWidthNum = textWidth || 0;
if (((textWidthNum > this.oldTextWidth) ||
textPxLength > textWidthNum) && (
// Only set the width if the text is able to word-wrap,
// or text-overflow is ellipsis (#9537)
/[ \-]/.test(element.textContent || element.innerText) ||
element.style.textOverflow === 'ellipsis')) {
HTMLElement_css(element, {
width: ((textPxLength > textWidthNum) ||
rotation ||
scaleX) ?
textWidth + 'px' :
'auto', // #16261
display: display,
whiteSpace: whiteSpace || 'normal' // #3331
});
this.oldTextWidth = textWidth;
}
}
// Do the calculations and DOM access only if properties changed
if (currentTextTransform !== this.cTT) {
baseline = renderer.fontMetrics(element).b;
// Renderer specific handling of span rotation, but only if we
// have something to update.
if (HTMLElement_defined(rotation) &&
((rotation !== (this.oldRotation || 0)) ||
(textAlign !== this.oldAlign))) {
this.setSpanRotation(rotation, parentPadding, parentPadding);
}
this.getSpanCorrection(
// Avoid elem.offsetWidth if we can, it affects rendering
// time heavily (#7656)
((!HTMLElement_defined(rotation) &&
!this.textWidth &&
this.textPxLength) || // #7920
element.offsetWidth), baseline, HTMLElement_getAlignFactor(textAlign));
}
// Apply position with correction and rotation origin
var _j = this,
_k = _j.xCorr,
xCorr = _k === void 0 ? 0 : _k,
_l = _j.yCorr,
yCorr = _l === void 0 ? 0 : _l,
rotOriginX = (rotationOriginX !== null && rotationOriginX !== void 0 ? rotationOriginX : x) - xCorr - x - parentPadding,
rotOriginY = (rotationOriginY !== null && rotationOriginY !== void 0 ? rotationOriginY : y) - yCorr - y - parentPadding,
styles_1 = {
left: "" + (x + xCorr) + "px",
top: "" + (y + yCorr) + "px",
textAlign: textAlign,
transformOrigin: "" + rotOriginX + "px " + rotOriginY + "px"
};
if (scaleX || scaleY) {
styles_1.transform = "scale(".concat(scaleX !== null && scaleX !== void 0 ? scaleX : 1, ",").concat(scaleY !== null && scaleY !== void 0 ? scaleY : 1, ")");
}
HTMLElement_css(element, styles_1);
// Record current text transform
this.cTT = currentTextTransform;
this.oldRotation = rotation;
this.oldAlign = textAlign;
}
};
/**
* Set the rotation of an individual HTML span.
* @private
*/
HTMLElement.prototype.setSpanRotation = function (rotation, originX, originY) {
// CSS transform and transform-origin both supported without prefix
// since Firefox 16 (2012), IE 10 (2012), Chrome 36 (2014), Safari 9
// (2015).;
HTMLElement_css(this.element, {
transform: "rotate(".concat(rotation, "deg)"),
transformOrigin: "" + originX + "% " + originY + "px"
});
};
/**
* Add the element to a group wrapper. For HTML elements, a parallel div
* will be created for each ancenstor SVG `g` element.
*
* @private
*/
HTMLElement.prototype.add = function (parentGroup) {
var container = this.renderer.box
.parentNode,
parents = [];
var div;
this.parentGroup = parentGroup;
// Create a parallel divs to hold the HTML elements
if (parentGroup) {
div = parentGroup.div;
if (!div) {
// Read the parent chain into an array and read from top
// down
var svgGroup = parentGroup;
while (svgGroup) {
parents.push(svgGroup);
// Move up to the next parent group
svgGroup = svgGroup.parentGroup;
}
// Decorate each of the ancestor group elements with a parallel
// div that reflects translation and styling
for (var _i = 0, _a = parents.reverse(); _i < _a.length; _i++) {
var parentGroup_1 = _a[_i];
div = decorateSVGGroup(parentGroup_1, container);
}
}
}
(div || container).appendChild(this.element);
this.added = true;
if (this.alignOnAdd) {
this.updateTransform();
}
return this;
};
/**
* Text setter
* @private
*/
HTMLElement.prototype.textSetter = function (value) {
if (value !== this.textStr) {
delete this.bBox;
delete this.oldTextWidth;
HTML_AST.setElementHTML(this.element, value !== null && value !== void 0 ? value : '');
this.textStr = value;
this.doTransform = true;
}
};
/**
* Align setter
*
* @private
*/
HTMLElement.prototype.alignSetter = function (value) {
this.alignValue = this.textAlign = value;
this.doTransform = true;
};
/**
* Various setters which rely on update transform
* @private
*/
HTMLElement.prototype.xSetter = function (value, key) {
this[key] = value;
this.doTransform = true;
};
return HTMLElement;
}(SVG_SVGElement));
// Some shared setters
var proto = HTMLElement.prototype;
proto.visibilitySetter = proto.opacitySetter = commonSetter;
proto.ySetter =
proto.rotationSetter =
proto.rotationOriginXSetter =
proto.rotationOriginYSetter = proto.xSetter;
/* *
*
* Default Export
*
* */
/* harmony default export */ var HTML_HTMLElement = (HTMLElement);
;// ./code/es5/es-modules/Core/Axis/AxisDefaults.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* Namespace
*
* */
var AxisDefaults;
(function (AxisDefaults) {
/* *
*
* Constants
*
* */
/**
* The X axis or category axis. Normally this is the horizontal axis,
* though if the chart is inverted this is the vertical axis. In case of
* multiple axes, the xAxis node is an array of configuration objects.
*
* See the [Axis class](/class-reference/Highcharts.Axis) for programmatic
* access to the axis.
*
* @productdesc {highmaps}
* In Highmaps, the axis is hidden, but it is used behind the scenes to
* control features like zooming and panning. Zooming is in effect the same
* as setting the extremes of one of the exes.
*
* @type {*|Array<*>}
* @optionparent xAxis
*/
AxisDefaults.xAxis = {
/**
* When using multiple axis, 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 an Axis options are set to false,
* then the `alignTicks ` will be disabled for the Axis.
*
* Disabled for logarithmic axes.
*
* @product highcharts highstock gantt
*/
alignTicks: true,
/**
* Whether to allow decimals in this axis' ticks. When counting
* integers, like persons or hits on a web page, decimals should
* be avoided in the labels. By default, decimals are allowed on small
* scale axes.
*
* @see [minTickInterval](#xAxis.minTickInterval)
*
* @sample {highcharts|highstock} highcharts/yaxis/allowdecimals-true/
* True by default
* @sample {highcharts|highstock} highcharts/yaxis/allowdecimals-false/
* False
*
* @type {boolean|undefined}
* @default undefined
* @since 2.0
*/
allowDecimals: void 0,
/**
* When using an alternate grid color, a band is painted across the
* plot area between every other grid line.
*
* @sample {highcharts} highcharts/yaxis/alternategridcolor/
* Alternate grid color on the Y axis
* @sample {highstock} stock/xaxis/alternategridcolor/
* Alternate grid color on the Y axis
*
* @type {Highcharts.ColorType}
* @apioption xAxis.alternateGridColor
*/
/**
* An array defining breaks in the axis, the sections defined will be
* left out and all the points shifted closer to each other.
*
* @productdesc {highcharts}
* Requires that the broken-axis.js module is loaded.
*
* @sample {highcharts} highcharts/axisbreak/break-simple/
* Simple break
* @sample {highcharts|highstock} highcharts/axisbreak/break-visualized/
* Advanced with callback
* @sample {highstock} stock/demo/intraday-breaks/
* Break on nights and weekends
*
* @type {Array<*>}
* @since 4.1.0
* @product highcharts highstock gantt
* @apioption xAxis.breaks
*/
/**
* A number indicating how much space should be left between the start
* and the end of the break. The break size is given in axis units,
* so for instance on a `datetime` axis, a break size of 3600000 would
* indicate the equivalent of an hour.
*
* @type {number}
* @default 0
* @since 4.1.0
* @product highcharts highstock gantt
* @apioption xAxis.breaks.breakSize
*/
/**
* The axis value where the break starts. On datetime axes, this may be
* a date string.
*
* @type {number|string}
* @since 4.1.0
* @product highcharts highstock gantt
* @apioption xAxis.breaks.from
*/
/**
* Defines an interval after which the break appears again. By default
* the breaks do not repeat.
*
* @type {number}
* @default 0
* @since 4.1.0
* @product highcharts highstock gantt
* @apioption xAxis.breaks.repeat
*/
/**
* The axis value where the break ends. On datetime axes, this may be
* a date string.
*
* @type {number|string}
* @since 4.1.0
* @product highcharts highstock gantt
* @apioption xAxis.breaks.to
*/
/**
* If categories are present for the xAxis, names are used instead of
* numbers for that axis.
*
* Since Highcharts 3.0, categories can also
* be extracted by giving each point a [name](#series.data) and setting
* axis [type](#xAxis.type) to `category`. However, if you have multiple
* series, best practice remains defining the `categories` array.
*
* Example: `categories: ['Apples', 'Bananas', 'Oranges']`
*
* @sample {highcharts} highcharts/demo/line-labels/
* With
* @sample {highcharts} highcharts/xaxis/categories/
* Without
*
* @type {Array<string>}
* @product highcharts gantt
* @apioption xAxis.categories
*/
/**
* The highest allowed value for automatically computed axis extremes.
*
* @see [floor](#xAxis.floor)
*
* @sample {highcharts|highstock} highcharts/yaxis/floor-ceiling/
* Floor and ceiling
*
* @type {number}
* @since 4.0
* @product highcharts highstock gantt
* @apioption xAxis.ceiling
*/
/**
* A class name that opens for styling the axis by CSS, especially in
* Highcharts styled mode. The class name is applied to group elements
* for the grid, axis elements and labels.
*
* @sample {highcharts|highstock|highmaps} highcharts/css/axis/
* Multiple axes with separate styling
*
* @type {string}
* @since 5.0.0
* @apioption xAxis.className
*/
/**
* Configure a crosshair that follows either the mouse pointer or the
* hovered point.
*
* In styled mode, the crosshairs are styled in the
* `.highcharts-crosshair`, `.highcharts-crosshair-thin` or
* `.highcharts-xaxis-category` classes.
*
* @productdesc {highstock}
* In Highcharts stock, by default, the crosshair is enabled on the
* X axis and disabled on the Y axis.
*
* @sample {highcharts} highcharts/xaxis/crosshair-both/
* Crosshair on both axes
* @sample {highstock} stock/xaxis/crosshairs-xy/
* Crosshair on both axes, with y axis label
* @sample {highmaps} highcharts/xaxis/crosshair-both/
* Crosshair on both axes
*
* @declare Highcharts.AxisCrosshairOptions
* @type {boolean|*}
* @default false
* @since 4.1
* @apioption xAxis.crosshair
*/
/**
* The value on a perpendicular axis where this axis should cross. This
* is typically used on mathematical plots where the axes cross at 0.
* When `crossing` is set, space will not be reserved at the sides of
* the chart for axis labels and title, so those may be clipped. In this
* case it is better to place the axes without the `crossing` option.
*
* @type {number}
* @sample highcharts/xaxis/crossing
* Function plot with axes crossing at 0
* @since 11.0.1
* @apioption xAxis.crossing
*/
/**
* A class name for the crosshair, especially as a hook for styling.
*
* @type {string}
* @since 5.0.0
* @apioption xAxis.crosshair.className
*/
/**
* The color of the crosshair. Defaults to `#cccccc` for numeric and
* datetime axes, and `rgba(204,214,235,0.25)` for category axes, where
* the crosshair by default highlights the whole category.
*
* @sample {highcharts|highstock|highmaps} highcharts/xaxis/crosshair-customized/
* Customized crosshairs
*
* @type {Highcharts.ColorType}
* @default #cccccc
* @since 4.1
* @apioption xAxis.crosshair.color
*/
/**
* The dash style for the crosshair. See
* [plotOptions.series.dashStyle](#plotOptions.series.dashStyle)
* for possible values.
*
* @sample {highcharts|highmaps} highcharts/xaxis/crosshair-dotted/
* Dotted crosshair
* @sample {highstock} stock/xaxis/crosshair-dashed/
* Dashed X axis crosshair
*
* @type {Highcharts.DashStyleValue}
* @default Solid
* @since 4.1
* @apioption xAxis.crosshair.dashStyle
*/
/**
* A label on the axis next to the crosshair.
*
* In styled mode, the label is styled with the
* `.highcharts-crosshair-label` class.
*
* @sample {highstock} stock/xaxis/crosshair-label/
* Crosshair labels
* @sample {highstock} highcharts/css/crosshair-label/
* Style mode
*
* @declare Highcharts.AxisCrosshairLabelOptions
* @since 2.1
* @product highstock
* @apioption xAxis.crosshair.label
*/
/**
* Alignment of the label compared to the axis. Defaults to `"left"` for
* right-side axes, `"right"` for left-side axes and `"center"` for
* horizontal axes.
*
* @type {Highcharts.AlignValue}
* @since 2.1
* @product highstock
* @apioption xAxis.crosshair.label.align
*/
/**
* The background color for the label. Defaults to the related series
* color, or `#666666` if that is not available.
*
* @type {Highcharts.ColorType}
* @since 2.1
* @product highstock
* @apioption xAxis.crosshair.label.backgroundColor
*/
/**
* The border color for the crosshair label
*
* @type {Highcharts.ColorType}
* @since 2.1
* @product highstock
* @apioption xAxis.crosshair.label.borderColor
*/
/**
* The border corner radius of the crosshair label.
*
* @type {number}
* @default 3
* @since 2.1.10
* @product highstock
* @apioption xAxis.crosshair.label.borderRadius
*/
/**
* The border width for the crosshair label.
*
* @type {number}
* @default 0
* @since 2.1
* @product highstock
* @apioption xAxis.crosshair.label.borderWidth
*/
/**
* Flag to enable crosshair's label.
*
* @sample {highstock} stock/xaxis/crosshairs-xy/
* Enabled label for yAxis' crosshair
*
* @type {boolean}
* @default false
* @since 2.1
* @product highstock
* @apioption xAxis.crosshair.label.enabled
*/
/**
* A format string for the crosshair label. Defaults to `{value}` for
* numeric axes and `{value:%b %d, %Y}` for datetime axes.
*
* @type {string}
* @since 2.1
* @product highstock
* @apioption xAxis.crosshair.label.format
*/
/**
* Formatter function for the label text.
*
* @type {Highcharts.XAxisCrosshairLabelFormatterCallbackFunction}
* @since 2.1
* @product highstock
* @apioption xAxis.crosshair.label.formatter
*/
/**
* Padding inside the crosshair label.
*
* @type {number}
* @default 8
* @since 2.1
* @product highstock
* @apioption xAxis.crosshair.label.padding
*/
/**
* The shape to use for the label box.
*
* @type {string}
* @default callout
* @since 2.1
* @product highstock
* @apioption xAxis.crosshair.label.shape
*/
/**
* Text styles for the crosshair label.
*
* @type {Highcharts.CSSObject}
* @default {"color": "white", "fontWeight": "normal", "fontSize": "11px", "textAlign": "center"}
* @since 2.1
* @product highstock
* @apioption xAxis.crosshair.label.style
*/
/**
* Whether the crosshair should snap to the point or follow the pointer
* independent of points.
*
* @sample {highcharts|highstock} highcharts/xaxis/crosshair-snap-false/
* True by default
* @sample {highmaps} maps/demo/latlon-advanced/
* Snap is false
*
* @type {boolean}
* @default true
* @since 4.1
* @apioption xAxis.crosshair.snap
*/
/**
* The pixel width of the crosshair. Defaults to 1 for numeric or
* datetime axes, and for one category width for category axes.
*
* @sample {highcharts} highcharts/xaxis/crosshair-customized/
* Customized crosshairs
* @sample {highstock} highcharts/xaxis/crosshair-customized/
* Customized crosshairs
* @sample {highmaps} highcharts/xaxis/crosshair-customized/
* Customized crosshairs
*
* @type {number}
* @default 1
* @since 4.1
* @apioption xAxis.crosshair.width
*/
/**
* The Z index of the crosshair. Higher Z indices allow drawing the
* crosshair on top of the series or behind the grid lines.
*
* @type {number}
* @default 2
* @since 4.1
* @apioption xAxis.crosshair.zIndex
*/
/**
* Whether to pan axis. If `chart.panning` is enabled, the option
* allows to disable panning on an individual axis.
*/
panningEnabled: true,
/**
* The Z index for the axis group.
*
* @see [axis.gridZIndex](#xAxis.gridZIndex)
* @see [axis.labels.zIndex](#xAxis.labels.zIndex)
*/
zIndex: 2,
/**
* Whether to zoom axis. If `chart.zoomType` is set, the option allows
* to disable zooming on an individual axis.
*
* @sample {highcharts} highcharts/xaxis/zoomenabled/
* Zoom enabled is false
*/
zoomEnabled: true,
/**
* For a datetime axis, the scale will automatically adjust to the
* appropriate unit. This member gives the default string
* representations used for each unit. For intermediate values,
* different units may be used, for example the `day` unit can be used
* on midnight and `hour` unit be used for intermediate values on the
* same axis.
*
* For an overview of the string or object configuration, see
* [dateFormat](/class-reference/Highcharts.Time#dateFormat).
*
* Defaults to:
* ```js
* {
* millisecond: '%[HMSL]',
* second: '%[HMS]',
* minute: '%[HM]',
* hour: '%[HM]',
* day: '%[eb]',
* week: '%[eb]',
* month: '%[bY]',
* year: '%Y'
* }
* ```
*
* @sample {highcharts} highcharts/xaxis/datetimelabelformats-object/
* Object day format on X axis
* @sample {highcharts} highcharts/xaxis/datetimelabelformats/
* String day format on X axis
* @sample {highstock} stock/xaxis/datetimelabelformats/
* More information in x axis labels
*
* @declare Highcharts.AxisDateTimeLabelFormatsOptions
* @product highcharts highstock gantt
*/
dateTimeLabelFormats: {
/**
* @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
* @type {string|*}
*/
millisecond: {
/**
* @type {Array<string|Highcharts.DateTimeFormatOptions>}
* @default undefined
* @apioption xAxis.dateTimeLabelFormats.millisecond.list
*/
/**
* @type {string|Highcharts.DateTimeFormatOptions}
* @apioption xAxis.dateTimeLabelFormats.millisecond.main
*/
main: '%[HMSL]',
range: false
},
/**
* @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
* @type {string|*}
*/
second: {
/**
* @type {Array<string|Highcharts.DateTimeFormatOptions>}
* @default undefined
* @apioption xAxis.dateTimeLabelFormats.second.list
*/
/**
* @type {string|Highcharts.DateTimeFormatOptions}
* @apioption xAxis.dateTimeLabelFormats.second.main
*/
main: '%[HMS]',
range: false
},
/**
* @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
* @type {string|*}
*/
minute: {
/**
* @type {Array<string|Highcharts.DateTimeFormatOptions>}
* @default undefined
* @apioption xAxis.dateTimeLabelFormats.minute.list
*/
/**
* @type {string|Highcharts.DateTimeFormatOptions}
* @apioption xAxis.dateTimeLabelFormats.minute.main
*/
main: '%[HM]',
range: false
},
/**
* @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
* @type {string|*}
*/
hour: {
/**
* @type {Array<string|Highcharts.DateTimeFormatOptions>}
* @default undefined
* @apioption xAxis.dateTimeLabelFormats.hour.list
*/
/**
* @type {string|Highcharts.DateTimeFormatOptions}
* @apioption xAxis.dateTimeLabelFormats.hour.main
*/
main: '%[HM]',
range: false
},
/**
* @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
* @type {string|*}
*/
day: {
/**
* @type {Array<string|Highcharts.DateTimeFormatOptions>}
* @default undefined
* @apioption xAxis.dateTimeLabelFormats.day.list
*/
/**
* @type {string|Highcharts.DateTimeFormatOptions}
* @apioption xAxis.dateTimeLabelFormats.day.main
*/
main: '%[eb]'
},
/**
* @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
* @type {string|*}
*/
week: {
/**
* @type {Array<string|Highcharts.DateTimeFormatOptions>}
* @default undefined
* @apioption xAxis.dateTimeLabelFormats.week.list
*/
/**
* @type {string|Highcharts.DateTimeFormatOptions}
* @apioption xAxis.dateTimeLabelFormats.week.main
*/
main: '%[eb]'
},
/**
* @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
* @type {string|*}
*/
month: {
/**
* @type {Array<string|Highcharts.DateTimeFormatOptions>}
* @default undefined
* @apioption xAxis.dateTimeLabelFormats.month.list
*/
/**
* @type {string|Highcharts.DateTimeFormatOptions}
* @apioption xAxis.dateTimeLabelFormats.month.main
*/
main: '%[bY]'
},
/**
* @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
* @type {string|*}
*/
year: {
/**
* @type {Array<string|Highcharts.DateTimeFormatOptions>}
* @default undefined
* @apioption xAxis.dateTimeLabelFormats.year.list
*/
/**
* @type {string|Highcharts.DateTimeFormatOptions}
* @apioption xAxis.dateTimeLabelFormats.year.main
*/
main: '%Y'
}
},
/**
* Whether to force the axis to end on a tick. Use this option with
* the `maxPadding` option to control the axis end.
*
* @productdesc {highstock}
* In Highcharts Stock, `endOnTick` is always `false` when the navigator
* is enabled, to prevent jumpy scrolling. With disabled navigator
* enabling `endOnTick` may lead to extending the xAxis to show the last
* tick, therefore range selector buttons may not have an active state
* if the axis gets extended.
*
* @sample {highcharts} highcharts/yaxis/endontick/
* True by default
* @sample {highcharts} highcharts/yaxis/endontick-false/
* False
* @sample {highstock} stock/demo/basic-line/
* True by default
* @sample {highstock} stock/xaxis/endontick/
* False
*
* @since 1.2.0
*/
endOnTick: false,
/**
* Event handlers for the axis.
*
* @type {*}
* @apioption xAxis.events
*/
/**
* An event fired after the breaks have rendered.
*
* @see [breaks](#xAxis.breaks)
*
* @sample {highcharts} highcharts/axisbreak/break-event/
* AfterBreak Event
*
* @type {Highcharts.AxisEventCallbackFunction}
* @since 4.1.0
* @product highcharts gantt
* @apioption xAxis.events.afterBreaks
*/
/**
* As opposed to the `setExtremes` event, this event fires after the
* final min and max values are computed and corrected for `minRange`.
*
* Fires when the minimum and maximum is set for the axis, either by
* calling the `.setExtremes()` method or by selecting an area in the
* chart. One parameter, `event`, is passed to the function, containing
* common event information.
*
* The new user set minimum and maximum values can be found by
* `event.min` and `event.max`. These reflect the axis minimum and
* maximum in axis values. The actual data extremes are found in
* `event.dataMin` and `event.dataMax`.
*
* @type {Highcharts.AxisSetExtremesEventCallbackFunction}
* @since 2.3
* @context Highcharts.Axis
* @apioption xAxis.events.afterSetExtremes
*/
/**
* An event fired when a break from this axis occurs on a point.
*
* @see [breaks](#xAxis.breaks)
*
* @sample {highcharts} highcharts/axisbreak/break-visualized/
* Visualization of a Break
*
* @type {Highcharts.AxisPointBreakEventCallbackFunction}
* @since 4.1.0
* @product highcharts gantt
* @context Highcharts.Axis
* @apioption xAxis.events.pointBreak
*/
/**
* An event fired when a point falls inside a break from this axis.
*
* @type {Highcharts.AxisPointBreakEventCallbackFunction}
* @product highcharts highstock gantt
* @context Highcharts.Axis
* @apioption xAxis.events.pointInBreak
*/
/**
* An event fired when a point is outside a break after zoom.
*
* @type {Highcharts.AxisPointBreakEventCallbackFunction}
* @product highcharts highstock gantt
* @context Highcharts.Axis
* @apioption xAxis.events.pointBreakOut
*/
/**
* Fires when the minimum and maximum is set for the axis, either by
* calling the `.setExtremes()` method or by selecting an area in the
* chart. One parameter, `event`, is passed to the function,
* containing common event information.
*
* The new user set minimum and maximum values can be found by
* `event.min` and `event.max`. These reflect the axis minimum and
* maximum in data values. When an axis is zoomed all the way out from
* the "Reset zoom" button, `event.min` and `event.max` are null, and
* the new extremes are set based on `this.dataMin` and `this.dataMax`.
*
* @sample {highstock} stock/xaxis/events-setextremes/
* Log new extremes on x axis
*
* @type {Highcharts.AxisSetExtremesEventCallbackFunction}
* @since 1.2.0
* @context Highcharts.Axis
* @apioption xAxis.events.setExtremes
*/
/**
* The lowest allowed value for automatically computed axis extremes.
*
* @see [ceiling](#yAxis.ceiling)
*
* @sample {highcharts} highcharts/yaxis/floor-ceiling/
* Floor and ceiling
* @sample {highstock} stock/demo/lazy-loading/
* Prevent negative stock price on Y axis
*
* @type {number}
* @since 4.0
* @product highcharts highstock gantt
* @apioption xAxis.floor
*/
/**
* The dash or dot style of the grid lines. For possible values, see
* [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
*
* @sample {highcharts} highcharts/yaxis/gridlinedashstyle/
* Long dashes
* @sample {highstock} stock/xaxis/gridlinedashstyle/
* Long dashes
*
* @type {Highcharts.DashStyleValue}
* @since 1.2
*/
gridLineDashStyle: 'Solid',
/**
* The Z index of the grid lines.
*
* @sample {highcharts|highstock} highcharts/xaxis/gridzindex/
* A Z index of 4 renders the grid above the graph
*
* @product highcharts highstock gantt
*
* @see [axis.zIndex](#xAxis.zIndex)
* @see [axis.labels.zIndex](#xAxis.labels.zIndex)
*/
gridZIndex: 1,
/**
* An id for the axis. This can be used after render time to get
* a pointer to the axis object through `chart.get()`.
*
* @sample {highcharts} highcharts/xaxis/id/
* Get the object
* @sample {highstock} stock/xaxis/id/
* Get the object
*
* @type {string}
* @since 1.2.0
* @apioption xAxis.id
*/
/**
* The axis labels show the number or category for each tick.
*
* Since v8.0.0: Labels are animated in categorized x-axis with
* updating data if `tickInterval` and `step` is set to 1.
*
* @productdesc {highmaps}
* X and Y axis labels are by default disabled in Highmaps, but the
* functionality is inherited from Highcharts and used on `colorAxis`,
* and can be enabled on X and Y axes too.
*/
labels: {
/**
* What part of the string the given position is anchored to.
* If `left`, the left side of the string is at the axis position.
* Can be one of `"left"`, `"center"` or `"right"`. Defaults to
* an intelligent guess based on which side of the chart the axis
* is on and the rotation of the label.
*
* @see [reserveSpace](#xAxis.labels.reserveSpace)
*
* @sample {highcharts} highcharts/xaxis/labels-align-left/
* Left
* @sample {highcharts} highcharts/xaxis/labels-align-right/
* Right
* @sample {highcharts} highcharts/xaxis/labels-reservespace-true/
* Left-aligned labels on a vertical category axis
*
* @type {Highcharts.AlignValue}
* @apioption xAxis.labels.align
*/
/**
* Whether to allow the axis labels to overlap. When false,
* overlapping labels are hidden.
*
* @sample {highcharts} highcharts/xaxis/labels-allowoverlap-true/
* X axis labels overlap enabled
*
* @type {boolean}
* @default false
* @apioption xAxis.labels.allowOverlap
*/
/**
* For horizontal axes, the allowed degrees of label rotation
* to prevent overlapping labels. If there is enough space,
* labels are not rotated. As the chart gets narrower, it
* will start rotating the labels -45 degrees, then remove
* every second label and try again with rotations 0 and -45 etc.
* Set it to `undefined` to disable rotation, which will
* cause the labels to word-wrap if possible. Defaults to `[-45]``
* on bottom and top axes, `undefined` on left and right axes.
*
* @sample {highcharts|highstock} highcharts/xaxis/labels-autorotation-default/
* Default auto rotation of 0 or -45
* @sample {highcharts|highstock} highcharts/xaxis/labels-autorotation-0-90/
* Custom graded auto rotation
*
* @type {Array<number>}
* @default undefined
* @since 4.1.0
* @product highcharts highstock gantt
* @apioption xAxis.labels.autoRotation
*/
/**
* When each category width is more than this many pixels, we don't
* apply auto rotation. Instead, we lay out the axis label with word
* wrap. A lower limit makes sense when the label contains multiple
* short words that don't extend the available horizontal space for
* each label.
*
* @sample {highcharts} highcharts/xaxis/labels-autorotationlimit/
* Lower limit
*
* @since 4.1.5
* @product highcharts gantt
*/
autoRotationLimit: 80,
/**
* The label's pixel distance from the perimeter of the plot area.
* On cartesian charts, this is overridden if the `labels.y` setting
* is set.
*
* @sample {highcharts} highcharts/yaxis/labels-distance/
* Polar chart, labels centered under the arc
*
* @type {number}
* @product highcharts gantt
*/
distance: 15,
/**
* Enable or disable the axis labels.
*
* @sample {highcharts} highcharts/xaxis/labels-enabled/
* X axis labels disabled
* @sample {highstock} stock/xaxis/labels-enabled/
* X axis labels disabled
*
*/
enabled: true,
/**
* A format string for the axis label. The context is available as
* format string variables. For example, you can use `{text}` to
* insert the default formatted text. The recommended way of adding
* units for the label is using `text`, for example `{text} km`.
*
* To add custom numeric or datetime formatting, use `{value}` with
* formatting, for example `{value:.1f}` or `{value:%Y-%m-%d}`.
*
* See
* [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
* for more examples of formatting.
*
* The default value is not specified due to the dynamic
* nature of the default implementation.
*
* @sample {highcharts|highstock} highcharts/yaxis/labels-format/
* Add units to Y axis label
* @sample {highcharts} highcharts/xaxis/labels-format-linked/
* Linked category names
* @sample {highcharts} highcharts/xaxis/labels-format-custom/
* Custom number format
*
* @type {string}
* @since 3.0
* @apioption xAxis.labels.format
*/
/**
* Callback JavaScript function to format the label. The value
* is given by `this.value`. Additional properties for `this` are
* `axis`, `chart`, `isFirst`, `isLast` and `text` which holds the
* value of the default formatter.
*
* Defaults to a built in function returning a formatted string
* depending on whether the axis is `category`, `datetime`,
* `numeric` or other.
*
* @sample {highcharts} highcharts/xaxis/labels-formatter-linked/
* Linked category names
* @sample {highcharts} highcharts/xaxis/labels-formatter-extended/
* Modified numeric labels
* @sample {highstock} stock/xaxis/labels-formatter/
* Added units on Y axis
*
* @type {Highcharts.AxisLabelsFormatterCallbackFunction}
* @apioption xAxis.labels.formatter
*/
/**
* The number of pixels to indent the labels per level in a treegrid
* axis.
*
* @sample gantt/treegrid-axis/demo
* Indentation 10px by default.
* @sample gantt/treegrid-axis/indentation-0px
* Indentation set to 0px.
*
* @product gantt
*/
indentation: 10,
/**
* Horizontal axis only. When `staggerLines` is not set,
* `maxStaggerLines` defines how many lines the axis is allowed to
* add to automatically avoid overlapping X labels. Set to `1` to
* disable overlap detection.
*
* @deprecated
* @type {number}
* @default 5
* @since 1.3.3
* @apioption xAxis.labels.maxStaggerLines
*/
/**
* How to handle overflowing labels on horizontal axis. If set to
* `"allow"`, it will not be aligned at all. By default it
* `"justify"` labels inside the chart area. If there is room to
* move it, it will be aligned to the edge, else it will be removed.
*
* @since 2.2.5
* @validvalue ["allow", "justify"]
*/
overflow: 'justify',
/**
* The pixel padding for axis labels, to ensure white space between
* them. Defaults to 4 for horizontal axes, 1 for vertical.
*
* @type {number}
* @default undefined
* @product highcharts gantt
* @apioption xAxis.labels.padding
*/
/**
* Whether to reserve space for the labels. By default, space is
* reserved for the labels in these cases:
*
* * On all horizontal axes.
* * On vertical axes if `label.align` is `right` on a left-side
* axis or `left` on a right-side axis.
* * On vertical axes if `label.align` is `center`.
*
* This can be turned off when for example the labels are rendered
* inside the plot area instead of outside.
*
* @see [labels.align](#xAxis.labels.align)
*
* @sample {highcharts} highcharts/xaxis/labels-reservespace/
* No reserved space, labels inside plot
* @sample {highcharts} highcharts/xaxis/labels-reservespace-true/
* Left-aligned labels on a vertical category axis
*
* @type {boolean}
* @since 4.1.10
* @product highcharts highstock gantt
* @apioption xAxis.labels.reserveSpace
*/
reserveSpace: void 0,
/**
* Rotation of the labels in degrees. When `undefined`, the
* `autoRotation` option takes precedence.
*
* @sample {highcharts} highcharts/xaxis/labels-rotation/
* X axis labels rotated 90°
*
* @type {number}
* @default 0
* @apioption xAxis.labels.rotation
*/
rotation: void 0,
/**
* Horizontal axes only. The number of lines to spread the labels
* over to make room or tighter labels. 0 disables staggering.
*
* @sample {highcharts} highcharts/xaxis/labels-staggerlines/
* Show labels over two lines
* @sample {highstock} stock/xaxis/labels-staggerlines/
* Show labels over two lines
*
* @since 2.1
*/
staggerLines: 0,
/**
* To show only every _n_'th label on the axis, set the step to _n_.
* Setting the step to 2 shows every other label.
*
* By default, when 0, the step is calculated automatically to avoid
* overlap. To prevent this, set it to 1\. This usually only
* happens on a category axis, and is often a sign that you have
* chosen the wrong axis type.
*
* Read more at
* [Axis docs](https://www.highcharts.com/docs/chart-concepts/axes)
* => What axis should I use?
*
* @sample {highcharts} highcharts/xaxis/labels-step/
* Showing only every other axis label on a categorized
* x-axis
* @sample {highcharts} highcharts/xaxis/labels-step-auto/
* Auto steps on a category axis
*
* @since 2.1
*/
step: 0,
/**
* Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
* to render the labels.
*/
useHTML: false,
/**
* The x position offset of all labels relative to the tick
* positions on the axis. Overrides the `labels.distance` option.
*
* @type {number}
* @apioption xAxis.labels.x
*/
/**
* The y position offset of all labels relative to the tick
* positions on the axis. Overrides the `labels.distance` option.
*
* @sample {highcharts} highcharts/xaxis/labels-x/
* X axis labels placed on grid lines
*
* @type {number}
* @apioption xAxis.labels.y
*/
/**
* The Z index for the axis labels.
*
* @see [axis.zIndex](#xAxis.zIndex)
* @see [axis.gridZIndex](#xAxis.gridZIndex)
*/
zIndex: 7,
/**
* CSS styles for the label. Use `lineClamp` to control wrapping of
* category labels. Use `textOverflow: 'none'` to prevent ellipsis
* (dots).
*
* In styled mode, the labels are styled with the
* `.highcharts-axis-labels` class.
*
* @sample {highcharts} highcharts/xaxis/labels-style/
* Red X axis labels
*
* @type {Highcharts.CSSObject}
*/
style: {
/** @internal */
color: "#333333" /* Palette.neutralColor80 */,
/** @internal */
cursor: 'default',
/**
* @type {number|string}
*/
fontSize: '0.8em',
/** @internal */
textOverflow: 'ellipsis'
}
},
/**
* The left position as the horizontal axis. If it's a number, it is
* interpreted as pixel position relative to the chart.
*
* Since Highcharts v5.0.13: If it's a percentage string, it is
* interpreted as percentages of the plot width, offset from plot area
* left.
*
* @sample {highcharts} highcharts/xaxis/axis-position-properties
* Different axis position properties
*
* @type {number|string}
* @product highcharts highstock
* @apioption xAxis.left
*/
/**
* The top position as the vertical axis. If it's a number, it is
* interpreted as pixel position relative to the chart.
*
* Since Highcharts 2: If it's a percentage string, it is interpreted
* as percentages of the plot height, offset from plot area top.
*
* @sample {highcharts} highcharts/xaxis/axis-position-properties
* Different axis position properties
*
* @type {number|string}
* @product highcharts highstock
* @apioption xAxis.top
*/
/**
* Index of another axis that this axis is linked to. When an axis is
* linked to a master axis, it will take the same extremes as
* the master, but as assigned by min or max or by setExtremes.
* It can be used to show additional info, or to ease reading the
* chart by duplicating the scales.
*
* @sample {highcharts} highcharts/xaxis/linkedto/
* Different string formats of the same date
* @sample {highcharts} highcharts/yaxis/linkedto/
* Y values on both sides
*
* @type {number}
* @since 2.0.2
* @product highcharts highstock gantt
* @apioption xAxis.linkedTo
*/
/**
* The maximum value of the axis. If `undefined`, the max value is
* automatically calculated.
*
* If a datetime string is passed, it is parsed into epoch time
* according to the time zone given in [time.timezone](#time.timezone).
*
* If the [endOnTick](#yAxis.endOnTick) option is true, the `max` value
* might be rounded up.
*
* If a [tickAmount](#yAxis.tickAmount) is set, the axis may be extended
* beyond the set max in order to reach the given number of ticks. The
* same may happen in a chart with multiple axes, determined by [chart.
* alignTicks](#chart), where a `tickAmount` is applied internally.
*
* @sample {highcharts} highcharts/yaxis/max-200/
* Y axis max of 200
* @sample {highcharts} highcharts/yaxis/max-logarithmic/
* Y axis max on logarithmic axis
* @sample {highstock} stock/xaxis/min-max/
* Fixed min and max on X axis
*
* @type {number|string|null}
* @apioption xAxis.max
*/
/**
* Padding of the max value relative to the length of the axis. A
* padding of 0.05 will make a 100px axis 5px longer. This is useful
* when you don't want the highest data value to appear on the edge
* of the plot area. When the axis' `max` option is set or a max extreme
* is set using `axis.setExtremes()`, the maxPadding will be ignored.
*
* @productdesc {highstock}
* For an [ordinal](#xAxis.ordinal) axis, `minPadding` and `maxPadding`
* are ignored. Use [overscroll](#xAxis.overscroll) instead.
*
* @sample {highcharts} highcharts/yaxis/maxpadding/
* Max padding of 0.25 on y axis
* @sample {highstock} stock/xaxis/minpadding-maxpadding/
* Greater min- and maxPadding
* @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
* Add some padding
*
* @default {highcharts} 0.01
* @default {highstock|highmaps} 0
* @since 1.2.0
*/
maxPadding: 0.01,
/**
* Deprecated. Use `minRange` instead.
*
* @deprecated
* @type {number}
* @product highcharts highstock
* @apioption xAxis.maxZoom
*/
/**
* The minimum value of the axis. If `undefined`, the min value is
* automatically calculated.
*
* If a datetime string is passed, it is parsed into epoch time
* according to the time zone given in [time.timezone](#time.timezone).
*
* If the [startOnTick](#yAxis.startOnTick) option is true (default),
* the `min` value might be rounded down.
*
* The automatically calculated minimum value is also affected by
* [floor](#yAxis.floor), [softMin](#yAxis.softMin),
* [minPadding](#yAxis.minPadding), [minRange](#yAxis.minRange)
* as well as [series.threshold](#plotOptions.series.threshold)
* and [series.softThreshold](#plotOptions.series.softThreshold).
*
* @sample {highcharts} highcharts/yaxis/min-startontick-false/
* -50 with startOnTick to false
* @sample {highcharts} highcharts/yaxis/min-startontick-true/
* -50 with startOnTick true by default
* @sample {highstock} stock/xaxis/min-max/
* Set min and max on X axis
*
* @type {number|string|null}
* @apioption xAxis.min
*/
/**
* The dash or dot style of the minor grid lines. For possible values,
* see [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
*
* @sample {highcharts} highcharts/yaxis/minorgridlinedashstyle/
* Long dashes on minor grid lines
* @sample {highstock} stock/xaxis/minorgridlinedashstyle/
* Long dashes on minor grid lines
*
* @type {Highcharts.DashStyleValue}
* @since 1.2
*/
minorGridLineDashStyle: 'Solid',
/**
* Specific tick interval in axis units for the minor ticks. On a linear
* axis, if `"auto"`, the minor tick interval is calculated as a fifth
* of the tickInterval. If `undefined`, minor ticks are not shown.
*
* On logarithmic axes, the unit is the power of the value. For example,
* setting the minorTickInterval to 1 puts one tick on each of 0.1, 1,
* 10, 100 etc. Setting the minorTickInterval to 0.1 produces 9 ticks
* between 1 and 10, 10 and 100 etc.
*
* If user settings dictate minor ticks to become too dense, they don't
* make sense, and will be ignored to prevent performance problems.
*
* @sample {highcharts} highcharts/yaxis/minortickinterval-null/
* Undefined by default
* @sample {highcharts} highcharts/yaxis/minortickinterval-5/ 5 units
* @sample {highcharts} highcharts/yaxis/minortickinterval-log-auto/
* "auto"
* @sample {highcharts} highcharts/yaxis/minortickinterval-log/ 0.1
* @sample {highstock} stock/demo/basic-line/ Null by default
* @sample {highstock} stock/xaxis/minortickinterval-auto/ "auto"
*
* @type {number|'auto'}
* @apioption xAxis.minorTickInterval
*/
/**
* The pixel length of the minor tick marks.
*
* @sample {highcharts} highcharts/yaxis/minorticklength/
* 10px on Y axis
* @sample {highstock} stock/xaxis/minorticks/
* 10px on Y axis
*/
minorTickLength: 2,
/**
* The position of the minor tick marks relative to the axis line.
* Can be one of `inside` and `outside`.
*
* @sample {highcharts} highcharts/yaxis/minortickposition-outside/
* Outside by default
* @sample {highcharts} highcharts/yaxis/minortickposition-inside/
* Inside
* @sample {highstock} stock/xaxis/minorticks/
* Inside
*
* @validvalue ["inside", "outside"]
*/
minorTickPosition: 'outside',
/**
* Enable or disable minor ticks. The interval between the minor ticks
* can be controlled either by the
* [minorTicksPerMajor](#xAxis.minorTicksPerMajor) setting, or as an
* absolute [minorTickInterval](#xAxis.minorTickInterval) value.
*
* On a logarithmic axis, minor ticks are laid out based on a best
* guess, attempting to enter an approximate number of minor ticks
* between each major tick based on
* [minorTicksPerMajor](#xAxis.minorTicksPerMajor).
*
* Prior to v6.0.0, ticks were enabled in auto layout by setting
* `minorTickInterval` to `"auto"`.
*
* @productdesc {highcharts} On axes using
* [categories](#xAxis.categories), minor ticks are not supported.
*
* @sample {highcharts} highcharts/yaxis/minorticks-true/ Enabled on
* linear Y axis
*
* @type {boolean}
* @default false
* @since 6.0.0
* @apioption xAxis.minorTicks
*/
/**
* The number of minor ticks per major tick. Works for `linear`,
* `logarithmic` and `datetime` axes.
*
* @sample {highcharts} highcharts/yaxis/minortickspermajor/
* 2 minor ticks per major tick on Y axis
*
* @since 11.0.0
*
* @type {number}
*/
minorTicksPerMajor: 5,
/**
* The pixel width of the minor tick mark.
*
* @sample {highcharts} highcharts/yaxis/minortickwidth/
* 3px width
* @sample {highstock} stock/xaxis/minorticks/
* 1px width
*
* @type {number}
* @default 0
* @apioption xAxis.minorTickWidth
*/
/**
* Padding of the min value relative to the length of the axis. A
* padding of 0.05 will make a 100px axis 5px longer. This is useful
* when you don't want the lowest data value to appear on the edge
* of the plot area. When the axis' `min` option is set or a min extreme
* is set using `axis.setExtremes()`, the minPadding will be ignored.
*
* @productdesc {highstock}
* For an [ordinal](#xAxis.ordinal) axis, `minPadding` and `maxPadding`
* are ignored. Use [overscroll](#xAxis.overscroll) instead.
*
* @sample {highcharts} highcharts/yaxis/minpadding/
* Min padding of 0.2
* @sample {highstock} stock/xaxis/minpadding-maxpadding/
* Greater min- and maxPadding
* @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
* Add some padding
*
* @default {highcharts} 0.01
* @default {highstock|highmaps} 0
* @since 1.2.0
* @product highcharts highstock gantt
*/
minPadding: 0.01,
/**
* The minimum range to display on this axis. The entire axis will not
* be allowed to span over a smaller interval than this. For example,
* for a datetime axis the main unit is milliseconds. If minRange is
* set to 3600000, you can't zoom in more than to one hour.
*
* The default minRange for the x axis is five times the smallest
* interval between any of the data points.
*
* On a logarithmic axis, the unit for the minimum range is the power.
* So a minRange of 1 means that the axis can be zoomed to 10-100,
* 100-1000, 1000-10000 etc.
*
* **Note**: The `minPadding`, `maxPadding`, `startOnTick` and
* `endOnTick` settings also affect how the extremes of the axis
* are computed.
*
* @sample {highcharts} highcharts/xaxis/minrange/
* Minimum range of 5
* @sample {highstock} stock/xaxis/minrange/
* Max zoom of 6 months overrides user selections
*
* @type {number}
* @apioption xAxis.minRange
*/
/**
* The minimum tick interval allowed in axis values. For example on
* zooming in on an axis with daily data, this can be used to prevent
* the axis from showing hours. Defaults to the closest distance between
* two points on the axis.
*
* @type {number}
* @since 2.3.0
* @apioption xAxis.minTickInterval
*/
/**
* The distance in pixels from the plot area to the axis line.
* A positive offset moves the axis with it's line, labels and ticks
* away from the plot area. This is typically used when two or more
* axes are displayed on the same side of the plot. With multiple
* axes the offset is dynamically adjusted to avoid collision, this
* can be overridden by setting offset explicitly.
*
* @sample {highcharts} highcharts/yaxis/offset/
* Y axis offset of 70
* @sample {highcharts} highcharts/yaxis/offset-centered/
* Axes positioned in the center of the plot
* @sample {highstock} stock/xaxis/offset/
* Y axis offset by 70 px
*
* @type {number}
*/
offset: void 0,
/**
* Whether to display the axis on the opposite side of the normal. The
* normal is on the left side for vertical axes and bottom for
* horizontal, so the opposite sides will be right and top respectively.
* This is typically used with dual or multiple axes.
*
* @sample {highcharts} highcharts/yaxis/opposite/
* Secondary Y axis opposite
* @sample {highstock} stock/xaxis/opposite/
* Y axis on left side
*
* @default {highcharts|highstock|highmaps} false
* @default {gantt} true
* @type Boolean
* @apioption xAxis.opposite
*/
/**
* In an ordinal axis, the points are equally spaced in the chart
* regardless of the actual time or x distance between them. This means
* that missing data periods (e.g. nights or weekends for a stock chart)
* will not take up space in the chart.
* Having `ordinal: false` will show any gaps created by the `gapSize`
* setting proportionate to their duration.
*
* In stock charts the X axis is ordinal by default, unless
* the boost module is used and at least one of the series' data length
* exceeds the [boostThreshold](#series.line.boostThreshold).
*
* For an ordinal axis, `minPadding` and `maxPadding` are ignored. Use
* [overscroll](#xAxis.overscroll) instead.
*
* @sample {highstock} stock/xaxis/ordinal-true/
* True by default
* @sample {highstock} stock/xaxis/ordinal-false/
* False
*
* @see [overscroll](#xAxis.overscroll)
*
* @type {boolean}
* @default true
* @since 1.1
* @product highstock
* @apioption xAxis.ordinal
*/
/**
* Additional range on the right side of the xAxis. Works similar to
* `xAxis.maxPadding`, but the value is set in terms of axis values,
* percentage or pixels.
*
* If it's a number, it is interpreted as axis values, which in a
* datetime axis equals milliseconds.
*
* If it's a percentage string, is interpreted as percentages of axis
* length. An overscroll of 50% will make a 100px axis 50px longer.
*
* If it's a pixel string, it is interpreted as a fixed pixel value, but
* limited to 90% of the axis length.
*
* @sample {highstock} stock/xaxis/overscroll/ One minute overscroll
* with live data
* @sample {highstock} stock/xaxis/overscroll-percent/ Overscroll set in
* percentage
* @sample {highstock} stock/xaxis/overscroll-pixel/ Overscroll set in
* pixels
*
* @type {number | string}
* @default 0
* @since 6.0.0
* @product highstock
* @apioption xAxis.overscroll
*/
/**
* Refers to the index in the [panes](#panes) array. Used for circular
* gauges and polar charts. When the option is not set then first pane
* will be used.
*
* @sample highcharts/demo/gauge-vu-meter
* Two gauges with different center
*
* @type {number}
* @product highcharts
* @apioption xAxis.pane
*/
/**
* The zoomed range to display when only defining one or none of `min`
* or `max`. For example, to show the latest month, a range of one month
* can be set.
*
* @sample {highstock} stock/xaxis/range/
* Setting a zoomed range when the rangeSelector is disabled
*
* @type {number}
* @product highstock
* @apioption xAxis.range
*/
/**
* Whether to reverse the axis so that the highest number is closest
* to the origin. If the chart is inverted, the x axis is reversed by
* default.
*
* @sample {highcharts} highcharts/yaxis/reversed/
* Reversed Y axis
* @sample {highstock} stock/xaxis/reversed/
* Reversed Y axis
*
* @type {boolean}
* @default undefined
* @apioption xAxis.reversed
*/
reversed: void 0,
/**
* This option determines how stacks should be ordered within a group.
* For example reversed xAxis also reverses stacks, so first series
* comes last in a group. To keep order like for non-reversed xAxis
* enable this option.
*
* @sample {highcharts} highcharts/xaxis/reversedstacks/
* Reversed stacks comparison
* @sample {highstock} highcharts/xaxis/reversedstacks/
* Reversed stacks comparison
*
* @since 6.1.1
* @product highcharts highstock
*/
reversedStacks: false,
/**
* An optional scrollbar to display on the X axis in response to
* limiting the minimum and maximum of the axis values.
*
* In styled mode, all the presentational options for the scrollbar are
* replaced by the classes `.highcharts-scrollbar-thumb`,
* `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
* `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
*
* @sample {highstock} stock/yaxis/heatmap-scrollbars/
* Heatmap with both scrollbars
*
* @extends scrollbar
* @since 4.2.6
* @product highstock
* @apioption xAxis.scrollbar
*/
/**
* Whether to show the axis line and title when the axis has no data.
*
* @sample {highcharts} highcharts/yaxis/showempty/
* When clicking the legend to hide series, one axis preserves
* line and title, the other doesn't
* @sample {highstock} highcharts/yaxis/showempty/
* When clicking the legend to hide series, one axis preserves
* line and title, the other doesn't
*
* @since 1.1
*/
showEmpty: true,
/**
* Whether to show the first tick label.
*
* @sample {highcharts} highcharts/xaxis/showfirstlabel-false/
* Set to false on X axis
* @sample {highstock} stock/xaxis/showfirstlabel/
* Labels below plot lines on Y axis
*/
showFirstLabel: true,
/**
* Whether to show the last tick label. Defaults to `true` on cartesian
* charts, and `false` on polar charts.
*
* @sample {highcharts} highcharts/xaxis/showlastlabel-true/
* Set to true on X axis
* @sample {highstock} stock/xaxis/showfirstlabel/
* Labels below plot lines on Y axis
*
* @type {boolean}
* @default undefined
* @product highcharts highstock gantt
*/
showLastLabel: true,
/**
* A soft maximum for the axis. If the series data maximum is less than
* this, the axis will stay at this maximum, but if the series data
* maximum is higher, the axis will flex to show all data.
*
* @sample highcharts/yaxis/softmin-softmax/
* Soft min and max
*
* @type {number}
* @since 5.0.1
* @product highcharts highstock gantt
* @apioption xAxis.softMax
*/
/**
* A soft minimum for the axis. If the series data minimum is greater
* than this, the axis will stay at this minimum, but if the series
* data minimum is lower, the axis will flex to show all data.
*
* @sample highcharts/yaxis/softmin-softmax/
* Soft min and max
*
* @type {number}
* @since 5.0.1
* @product highcharts highstock gantt
* @apioption xAxis.softMin
*/
/**
* For datetime axes, this decides where to put the tick between weeks.
* 0 = Sunday, 1 = Monday.
*
* @sample {highcharts} highcharts/xaxis/startofweek-monday/
* Monday by default
* @sample {highcharts} highcharts/xaxis/startofweek-sunday/
* Sunday
* @sample {highstock} stock/xaxis/startofweek-1
* Monday by default
* @sample {highstock} stock/xaxis/startofweek-0
* Sunday
*
* @product highcharts highstock gantt
*/
startOfWeek: 1,
/**
* Whether to force the axis to start on a tick. Use this option with
* the `minPadding` option to control the axis start.
*
* @productdesc {highstock}
* In Highcharts Stock, `startOnTick` is always `false` when
* the navigator is enabled, to prevent jumpy scrolling.
*
* @sample {highcharts} highcharts/xaxis/startontick-false/
* False by default
* @sample {highcharts} highcharts/xaxis/startontick-true/
* True
*
* @since 1.2.0
*/
startOnTick: false,
/**
* The amount of ticks to draw on the axis. This opens up for aligning
* the ticks of multiple charts or panes within a chart. This option
* overrides the `tickPixelInterval` option.
*
* This option only has an effect on linear axes. Datetime, logarithmic
* or category axes are not affected.
*
* @sample {highcharts} highcharts/yaxis/tickamount/
* 8 ticks on Y axis
* @sample {highstock} highcharts/yaxis/tickamount/
* 8 ticks on Y axis
*
* @type {number}
* @since 4.1.0
* @product highcharts highstock gantt
* @apioption xAxis.tickAmount
*/
/**
* The interval of the tick marks in axis units. When `undefined`, the
* tick interval is computed to approximately follow the
* [tickPixelInterval](#xAxis.tickPixelInterval) on linear and datetime
* axes. On categorized axes, a `undefined` tickInterval will default to
* 1, one category. Note that datetime axes are based on milliseconds,
* so for example an interval of one day is expressed as
* `24 * 3600 * 1000`.
*
* On logarithmic axes, the tickInterval is based on powers, so a
* tickInterval of 1 means one tick on each of 0.1, 1, 10, 100 etc. A
* tickInterval of 2 means a tick of 0.1, 10, 1000 etc. A tickInterval
* of 0.2 puts a tick on 0.1, 0.2, 0.4, 0.6, 0.8, 1, 2, 4, 6, 8, 10, 20,
* 40 etc.
*
*
* If the tickInterval is too dense for labels to be drawn, Highcharts
* may remove ticks.
*
* If the chart has multiple axes, the [alignTicks](#chart.alignTicks)
* option may interfere with the `tickInterval` setting.
*
* @see [tickPixelInterval](#xAxis.tickPixelInterval)
* @see [tickPositions](#xAxis.tickPositions)
* @see [tickPositioner](#xAxis.tickPositioner)
*
* @sample {highcharts} highcharts/xaxis/tickinterval-5/
* Tick interval of 5 on a linear axis
* @sample {highstock} stock/xaxis/tickinterval/
* Tick interval of 0.01 on Y axis
*
* @type {number}
* @apioption xAxis.tickInterval
*/
/**
* The pixel length of the main tick marks.
*
* @sample {highcharts} highcharts/xaxis/ticklength/
* 20 px tick length on the X axis
* @sample {highstock} stock/xaxis/ticks/
* Formatted ticks on X axis
*/
tickLength: 10,
/**
* If tickInterval is `null` this option sets the approximate pixel
* interval of the tick marks. Not applicable to categorized axis.
*
* The tick interval is also influenced by the [minTickInterval](
* #xAxis.minTickInterval) option, that, by default prevents ticks from
* being denser than the data points.
*
* @see [tickInterval](#xAxis.tickInterval)
* @see [tickPositioner](#xAxis.tickPositioner)
* @see [tickPositions](#xAxis.tickPositions)
*
* @sample {highcharts} highcharts/xaxis/tickpixelinterval-50/
* 50 px on X axis
* @sample {highstock} stock/xaxis/tickpixelinterval/
* 200 px on X axis
*/
tickPixelInterval: 100,
/**
* For categorized axes only. If `on` the tick mark is placed in the
* center of the category, if `between` the tick mark is placed between
* categories. The default is `between` if the `tickInterval` is 1, else
* `on`. In order to render tick marks on a category axis it is necessary
* to provide a [tickWidth](#xAxis.tickWidth).
*
* @sample {highcharts} highcharts/xaxis/tickmarkplacement-between/
* "between" by default
* @sample {highcharts} highcharts/xaxis/tickmarkplacement-on/
* "on"
*
* @product highcharts gantt
* @validvalue ["on", "between"]
*/
tickmarkPlacement: 'between',
/**
* The position of the major tick marks relative to the axis line.
* Can be one of `inside` and `outside`.
*
* @sample {highcharts} highcharts/xaxis/tickposition-outside/
* "outside" by default
* @sample {highcharts} highcharts/xaxis/tickposition-inside/
* "inside"
* @sample {highstock} stock/xaxis/ticks/
* Formatted ticks on X axis
*
* @validvalue ["inside", "outside"]
*/
tickPosition: 'outside',
/**
* A callback function returning array defining where the ticks are
* laid out on the axis. This overrides the default behaviour of
* [tickPixelInterval](#xAxis.tickPixelInterval) and [tickInterval](
* #xAxis.tickInterval). The automatic tick positions are accessible
* through `this.tickPositions` and can be modified by the callback.
*
* @see [tickPositions](#xAxis.tickPositions)
*
* @sample {highcharts} highcharts/xaxis/tickpositions-tickpositioner/
* Demo of tickPositions and tickPositioner
* @sample {highstock} highcharts/xaxis/tickpositions-tickpositioner/
* Demo of tickPositions and tickPositioner
*
* @type {Highcharts.AxisTickPositionerCallbackFunction}
* @apioption xAxis.tickPositioner
*/
/**
* An array defining where the ticks are laid out on the axis. This
* overrides the default behaviour of [tickPixelInterval](
* #xAxis.tickPixelInterval) and [tickInterval](#xAxis.tickInterval).
*
* @see [tickPositioner](#xAxis.tickPositioner)
*
* @sample {highcharts} highcharts/xaxis/tickpositions-tickpositioner/
* Demo of tickPositions and tickPositioner
* @sample {highstock} highcharts/xaxis/tickpositions-tickpositioner/
* Demo of tickPositions and tickPositioner
*
* @type {Array<number>}
* @apioption xAxis.tickPositions
*/
/**
* The pixel width of the major tick marks. Defaults to 0 on category
* axes, otherwise 1.
*
* In styled mode, the stroke width is given in the `.highcharts-tick`
* class, but in order for the element to be generated on category axes,
* the option must be explicitly set to 1.
*
* @sample {highcharts} highcharts/xaxis/tickwidth/
* 10 px width
* @sample {highcharts} highcharts/css/axis-grid/
* Styled mode
* @sample {highstock} stock/xaxis/ticks/
* Formatted ticks on X axis
* @sample {highstock} highcharts/css/axis-grid/
* Styled mode
*
* @type {undefined|number}
* @default {highstock} 1
* @default {highmaps} 0
* @apioption xAxis.tickWidth
*/
/**
* The axis title, showing next to the axis line.
*
* @productdesc {highmaps}
* In Highmaps, the axis is hidden by default, but adding an axis title
* is still possible. X axis and Y axis titles will appear at the bottom
* and left by default.
*/
title: {
/**
* Alignment of the title relative to the axis values. Possible
* values are "low", "middle" or "high".
*
* @sample {highcharts} highcharts/xaxis/title-align-low/
* "low"
* @sample {highcharts} highcharts/xaxis/title-align-center/
* "middle" by default
* @sample {highcharts} highcharts/xaxis/title-align-high/
* "high"
* @sample {highcharts} highcharts/yaxis/title-offset/
* Place the Y axis title on top of the axis
* @sample {highstock} stock/xaxis/title-align/
* Aligned to "high" value
*
* @type {Highcharts.AxisTitleAlignValue}
*/
align: 'middle',
/**
* Deprecated. Set the `text` to `undefined` to disable the title.
*
* @deprecated
* @type {boolean}
* @product highcharts
* @apioption xAxis.title.enabled
*/
/**
* The pixel distance between the axis labels or line and the title.
* Defaults to 0 for horizontal axes, 10 for vertical
*
* @sample {highcharts} highcharts/xaxis/title-margin/
* Y axis title margin of 60
*
* @type {number}
* @apioption xAxis.title.margin
*/
/**
* The distance of the axis title from the axis line. By default,
* this distance is computed from the offset width of the labels,
* the labels' distance from the axis and the title's margin.
* However when the offset option is set, it overrides all this.
*
* @sample {highcharts} highcharts/yaxis/title-offset/
* Place the axis title on top of the axis
* @sample {highstock} highcharts/yaxis/title-offset/
* Place the axis title on top of the Y axis
*
* @type {number}
* @since 2.2.0
* @apioption xAxis.title.offset
*/
/**
* Whether to reserve space for the title when laying out the axis.
*
* @type {boolean}
* @default true
* @since 5.0.11
* @product highcharts highstock gantt
* @apioption xAxis.title.reserveSpace
*/
/**
* The rotation of the text in degrees. 0 is horizontal, 270 is
* vertical reading from bottom to top. Defaults to 0 for horizontal
* axes, 270 for left-side axes and 90 for right-side axes.
*
* @sample {highcharts} highcharts/yaxis/title-offset/
* Horizontal
*
* @type {number}
* @default undefined
* @apioption xAxis.title.rotation
*/
/**
* The actual text of the axis title. It can contain basic HTML tags
* like `b`, `i` and `span` with style.
*
* @sample {highcharts} highcharts/xaxis/title-text/
* Custom HTML
* @sample {highstock} stock/xaxis/title-text/
* Titles for both axes
*
* @type {string|null}
* @apioption xAxis.title.text
*/
/**
* Alignment of the text, can be `"left"`, `"right"` or `"center"`.
* Default alignment depends on the
* [title.align](xAxis.title.align):
*
* Horizontal axes:
* - for `align` = `"low"`, `textAlign` is set to `left`
* - for `align` = `"middle"`, `textAlign` is set to `center`
* - for `align` = `"high"`, `textAlign` is set to `right`
*
* Vertical axes:
* - for `align` = `"low"` and `opposite` = `true`, `textAlign` is
* set to `right`
* - for `align` = `"low"` and `opposite` = `false`, `textAlign` is
* set to `left`
* - for `align` = `"middle"`, `textAlign` is set to `center`
* - for `align` = `"high"` and `opposite` = `true` `textAlign` is
* set to `left`
* - for `align` = `"high"` and `opposite` = `false` `textAlign` is
* set to `right`
*
* @type {Highcharts.AlignValue}
* @apioption xAxis.title.textAlign
*/
/**
* Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
* to render the axis title.
*
* @product highcharts highstock gantt
*/
useHTML: false,
/**
* Horizontal pixel offset of the title position.
*
* @since 4.1.6
* @product highcharts highstock gantt
*/
x: 0,
/**
* Vertical pixel offset of the title position.
*
* @product highcharts highstock gantt
*/
y: 0,
/**
* CSS styles for the title. If the title text is longer than the
* axis length, it will wrap to multiple lines by default. This can
* be customized by setting the `lineClamp` property, by setting a
* specific `width` or by setting `whiteSpace: 'nowrap'`.
*
* In styled mode, the stroke width is given in the
* `.highcharts-axis-title` class.
*
* @sample {highcharts} highcharts/xaxis/title-style/
* Red
* @sample {highcharts} highcharts/css/axis/
* Styled mode
*
* @type {Highcharts.CSSObject}
*/
style: {
/** @internal */
color: "#666666" /* Palette.neutralColor60 */,
/**
* @type {number|string}
*/
fontSize: '0.8em'
}
},
/**
* The type of axis. Can be one of `linear`, `logarithmic`, `datetime`
* or `category`. In a datetime axis, the numbers are given in
* milliseconds, and tick marks are placed on appropriate values like
* full hours or days. In a category axis, the
* [point names](#series.line.data.name) of the chart's series are used
* for categories, if not a [categories](#xAxis.categories) array is
* defined.
*
* @sample {highcharts} highcharts/xaxis/type-linear/
* Linear
* @sample {highcharts} highcharts/yaxis/type-log/
* Logarithmic
* @sample {highcharts} highcharts/yaxis/type-log-minorgrid/
* Logarithmic with minor grid lines
* @sample {highcharts} highcharts/xaxis/type-log-both/
* Logarithmic on two axes
* @sample {highcharts} highcharts/yaxis/type-log-negative/
* Logarithmic with extension to emulate negative values
*
* @type {Highcharts.AxisTypeValue}
* @default linear
* @product highcharts gantt
* @apioption xAxis.type
*/
/**
* If there are multiple axes on the same side of the chart, the pixel
* margin between the axes. Defaults to 0 on vertical axes, 15 on
* horizontal axes.
*
* @type {number}
* @since 7.0.3
* @apioption xAxis.margin
*/
/**
* Applies only when the axis `type` is `category`. When `uniqueNames`
* is true, points are placed on the X axis according to their names.
* If the same point name is repeated in the same or another series,
* the point is placed on the same X position as other points of the
* same name. When `uniqueNames` is false, the points are laid out in
* increasing X positions regardless of their names, and the X axis
* category will take the name of the last point in each position.
*
* @sample {highcharts} highcharts/xaxis/uniquenames-true/
* True by default
* @sample {highcharts} highcharts/xaxis/uniquenames-false/
* False
*
* @since 4.2.7
* @product highcharts gantt
* @type {boolean}
* @default true
* @apioption xAxis.uniqueNames
*/
/**
* Datetime axis only. An array determining what time intervals the
* ticks are allowed to fall on. Each array item is an array where the
* first value is the time unit and the second value another array of
* allowed multiples.
*
* Defaults to:
* ```js
* units: [[
* 'millisecond', // unit name
* [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
* ], [
* 'second',
* [1, 2, 5, 10, 15, 30]
* ], [
* 'minute',
* [1, 2, 5, 10, 15, 30]
* ], [
* 'hour',
* [1, 2, 3, 4, 6, 8, 12]
* ], [
* 'day',
* [1, 2]
* ], [
* 'week',
* [1, 2]
* ], [
* 'month',
* [1, 2, 3, 4, 6]
* ], [
* 'year',
* null
* ]]
* ```
*
* @sample {highcharts} highcharts/xaxis/units/
* Axis units demonstrated
*
* @type {Array<Array<string,(Array<number>|null)>>}
* @product highcharts highstock gantt
* @apioption xAxis.units
*/
/**
* Whether axis, including axis title, line, ticks and labels, should
* be visible.
*
* @since 4.1.9
* @product highcharts highstock gantt
*/
visible: true,
/**
* Color of the minor, secondary grid lines.
*
* In styled mode, the stroke width is given in the
* `.highcharts-minor-grid-line` class.
*
* @sample {highcharts} highcharts/yaxis/minorgridlinecolor/
* Bright grey lines from Y axis
* @sample {highcharts|highstock} highcharts/css/axis-grid/
* Styled mode
* @sample {highstock} stock/xaxis/minorgridlinecolor/
* Bright grey lines from Y axis
*
* @type {Highcharts.ColorType}
* @default #f2f2f2
*/
minorGridLineColor: "#f2f2f2" /* Palette.neutralColor5 */,
/**
* Width of the minor, secondary grid lines.
*
* In styled mode, the stroke width is given in the
* `.highcharts-grid-line` class.
*
* @sample {highcharts} highcharts/yaxis/minorgridlinewidth/
* 2px lines from Y axis
* @sample {highcharts|highstock} highcharts/css/axis-grid/
* Styled mode
* @sample {highstock} stock/xaxis/minorgridlinewidth/
* 2px lines from Y axis
*/
minorGridLineWidth: 1,
/**
* Color for the minor tick marks.
*
* @sample {highcharts} highcharts/yaxis/minortickcolor/
* Black tick marks on Y axis
* @sample {highstock} stock/xaxis/minorticks/
* Black tick marks on Y axis
*
* @type {Highcharts.ColorType}
* @default #999999
*/
minorTickColor: "#999999" /* Palette.neutralColor40 */,
/**
* The color of the line marking the axis itself.
*
* In styled mode, the line stroke is given in the
* `.highcharts-axis-line` or `.highcharts-xaxis-line` class.
*
* @sample {highcharts} highcharts/yaxis/linecolor/
* A red line on Y axis
* @sample {highcharts|highstock} highcharts/css/axis/
* Axes in styled mode
* @sample {highstock} stock/xaxis/linecolor/
* A red line on X axis
*
* @type {Highcharts.ColorType}
*/
lineColor: "#333333" /* Palette.neutralColor80 */,
/**
* The width of the line marking the axis itself.
*
* In styled mode, the stroke width is given in the
* `.highcharts-axis-line` or `.highcharts-xaxis-line` class.
*
* @sample {highcharts} highcharts/yaxis/linecolor/
* A 1px line on Y axis
* @sample {highcharts|highstock} highcharts/css/axis/
* Axes in styled mode
* @sample {highstock} stock/xaxis/linewidth/
* A 2px line on X axis
*
* @default {highcharts|highstock} 1
* @default {highmaps} 0
*/
lineWidth: 1,
/**
* Color of the grid lines extending the ticks across the plot area.
*
* In styled mode, the stroke is given in the `.highcharts-grid-line`
* class.
*
* @productdesc {highmaps}
* In Highmaps, the grid lines are hidden by default.
*
* @sample {highcharts} highcharts/yaxis/gridlinecolor/
* Green lines
* @sample {highcharts|highstock} highcharts/css/axis-grid/
* Styled mode
* @sample {highstock} stock/xaxis/gridlinecolor/
* Green lines
*
* @type {Highcharts.ColorType}
* @default #e6e6e6
*/
gridLineColor: "#e6e6e6" /* Palette.neutralColor10 */,
/**
* The width of the grid lines extending the ticks across the plot area.
* Defaults to 1 on the Y axis and 0 on the X axis, except for 3d
* charts.
*
* In styled mode, the stroke width is given in the
* `.highcharts-grid-line` class.
*
* @sample {highcharts} highcharts/yaxis/gridlinewidth/
* 2px lines
* @sample {highcharts|highstock} highcharts/css/axis-grid/
* Styled mode
* @sample {highstock} stock/xaxis/gridlinewidth/
* 2px lines
*
* @type {number}
* @apioption xAxis.gridLineWidth
*/
gridLineWidth: void 0,
/**
* The height as the vertical axis. If it's a number, it is
* interpreted as pixels.
*
* Since Highcharts 2: If it's a percentage string, it is interpreted
* as percentages of the total plot height.
*
* @sample {highcharts} highcharts/xaxis/axis-position-properties
* Different axis position properties
*
* @type {number|string}
* @product highcharts highstock
* @apioption xAxis.height
*/
/**
* The width as the horizontal axis. If it's a number, it is interpreted
* as pixels.
*
* Since Highcharts v5.0.13: If it's a percentage string, it is
* interpreted as percentages of the total plot width.
*
* @sample {highcharts} highcharts/xaxis/axis-position-properties
* Different axis position properties
*
* @type {number|string}
* @product highcharts highstock
* @apioption xAxis.width
*/
/**
* Color for the main tick marks.
*
* In styled mode, the stroke is given in the `.highcharts-tick`
* class.
*
* @sample {highcharts} highcharts/xaxis/tickcolor/
* Red ticks on X axis
* @sample {highcharts|highstock} highcharts/css/axis-grid/
* Styled mode
* @sample {highstock} stock/xaxis/ticks/
* Formatted ticks on X axis
*
* @type {Highcharts.ColorType}
*/
tickColor: "#333333" /* Palette.neutralColor80 */
// `tickWidth: 1`
};
/**
* The Z axis or depth axis for 3D plots.
*
* See the [Axis class](/class-reference/Highcharts.Axis) for programmatic
* access to the axis.
*
* @sample {highcharts} highcharts/3d/scatter-zaxis-categories/
* Z-Axis with Categories
* @sample {highcharts} highcharts/3d/scatter-zaxis-grid/
* Z-Axis with styling
*
* @type {*|Array<*>}
* @extends xAxis
* @since 5.0.0
* @product highcharts
* @excluding breaks, crosshair, height, left, lineColor, lineWidth,
* nameToX, showEmpty, top, width
* @apioption zAxis
*/
/**
* The Y axis or value axis. Normally this is the vertical axis,
* though if the chart is inverted this is the horizontal axis.
* In case of multiple axes, the yAxis node is an array of
* configuration objects.
*
* See [the Axis object](/class-reference/Highcharts.Axis) for programmatic
* access to the axis.
*
* @type {*|Array<*>}
* @extends xAxis
* @excluding currentDateIndicator,ordinal,overscroll
* @optionparent yAxis
*/
AxisDefaults.yAxis = {
/**
* The type of axis. Can be one of `linear`, `logarithmic`, `datetime`,
* `category` or `treegrid`. Defaults to `treegrid` for Gantt charts,
* `linear` for other chart types.
*
* In a datetime axis, the numbers are given in milliseconds, and tick
* marks are placed on appropriate values, like full hours or days. In a
* category or treegrid axis, the [point names](#series.line.data.name)
* of the chart's series are used for categories, if a
* [categories](#xAxis.categories) array is not defined.
*
* @sample {highcharts} highcharts/yaxis/type-log-minorgrid/
* Logarithmic with minor grid lines
* @sample {highcharts} highcharts/yaxis/type-log-negative/
* Logarithmic with extension to emulate negative values
* @sample {gantt} gantt/treegrid-axis/demo
* Treegrid axis
*
* @type {Highcharts.AxisTypeValue}
* @default {highcharts} linear
* @default {gantt} treegrid
* @product highcharts gantt
* @apioption yAxis.type
*/
/**
* The height of the Y axis. If it's a number, it is interpreted as
* pixels.
*
* Since Highcharts 2: If it's a percentage string, it is interpreted as
* percentages of the total plot height.
*
* @see [yAxis.top](#yAxis.top)
*
* @sample {highstock} stock/demo/candlestick-and-volume/
* Percentage height panes
*
* @type {number|string}
* @product highcharts highstock
* @apioption yAxis.height
*/
/**
* Solid gauge only. Unless [stops](#yAxis.stops) are set, the color
* to represent the maximum value of the Y axis.
*
* @sample {highcharts} highcharts/yaxis/mincolor-maxcolor/
* Min and max colors
*
* @type {Highcharts.ColorType}
* @default #003399
* @since 4.0
* @product highcharts
* @apioption yAxis.maxColor
*/
/**
* Solid gauge only. Unless [stops](#yAxis.stops) are set, the color
* to represent the minimum value of the Y axis.
*
* @sample {highcharts} highcharts/yaxis/mincolor-maxcolor/
* Min and max color
*
* @type {Highcharts.ColorType}
* @default #e6ebf5
* @since 4.0
* @product highcharts
* @apioption yAxis.minColor
*/
/**
* Whether to reverse the axis so that the highest number is closest
* to the origin.
*
* @sample {highcharts} highcharts/yaxis/reversed/
* Reversed Y axis
* @sample {highstock} stock/xaxis/reversed/
* Reversed Y axis
*
* @type {boolean}
* @default {highcharts} false
* @default {highstock} false
* @default {highmaps} true
* @default {gantt} true
* @apioption yAxis.reversed
*/
/**
* If `true`, the first series in a stack will be drawn on top in a
* positive, non-reversed Y axis. If `false`, the first series is in
* the base of the stack.
*
* @sample {highcharts} highcharts/yaxis/reversedstacks-false/
* Non-reversed stacks
* @sample {highstock} highcharts/yaxis/reversedstacks-false/
* Non-reversed stacks
*
* @type {boolean}
* @default true
* @since 3.0.10
* @product highcharts highstock
* @apioption yAxis.reversedStacks
*/
reversedStacks: true,
/**
* Solid gauge series only. Color stops for the solid gauge. Use this
* in cases where a linear gradient between a `minColor` and `maxColor`
* is not sufficient. The stops is an array of tuples, where the first
* item is a float between 0 and 1 assigning the relative position in
* the gradient, and the second item is the color.
*
* For solid gauges, the Y axis also inherits the concept of
* [data classes](https://api.highcharts.com/highmaps#colorAxis.dataClasses)
* from the Highmaps color axis.
*
* @sample {highcharts} highcharts/demo/gauge-solid/
* Gauge with stops
*
* @see [minColor](#yAxis.minColor)
* @see [maxColor](#yAxis.maxColor)
*
* @type {Array<Array<number,Highcharts.ColorType>>}
* @since 4.0
* @product highcharts
* @apioption yAxis.stops
*/
/**
* The pixel width of the major tick marks.
*
* @sample {highcharts} highcharts/xaxis/tickwidth/ 10 px width
* @sample {highstock} stock/xaxis/ticks/ Formatted ticks on X axis
*
* @type {number}
* @default 0
* @product highcharts highstock gantt
* @apioption yAxis.tickWidth
*/
/**
* Whether to force the axis to end on a tick. Use this option with
* the `maxPadding` option to control the axis end.
*
* This option is always disabled, when panning type is
* either `y` or `xy`.
*
* @see [type](#chart.panning.type)
*
*
* @sample {highcharts} highcharts/yaxis/endontick/
* True by default
* @sample {highcharts} highcharts/yaxis/endontick-false/
* False
* @sample {highstock} stock/demo/basic-line/
* True by default
* @sample {highstock} stock/xaxis/endontick/
* False for Y axis
*
* @since 1.2.0
*/
endOnTick: true,
/**
* Padding of the max value relative to the length of the axis. A
* padding of 0.05 will make a 100px axis 5px longer. This is useful
* when you don't want the highest data value to appear on the edge
* of the plot area. When the axis' `max` option is set or a max extreme
* is set using `axis.setExtremes()`, the maxPadding will be ignored.
*
* Also the `softThreshold` option takes precedence over `maxPadding`,
* so if the data is tangent to the threshold, `maxPadding` may not
* apply unless `softThreshold` is set to false.
*
* @sample {highcharts} highcharts/yaxis/maxpadding-02/
* Max padding of 0.2
* @sample {highstock} stock/xaxis/minpadding-maxpadding/
* Greater min- and maxPadding
*
* @since 1.2.0
* @product highcharts highstock gantt
*/
maxPadding: 0.05,
/**
* Padding of the min value relative to the length of the axis. A
* padding of 0.05 will make a 100px axis 5px longer. This is useful
* when you don't want the lowest data value to appear on the edge
* of the plot area. When the axis' `min` option is set or a max extreme
* is set using `axis.setExtremes()`, the maxPadding will be ignored.
*
* Also the `softThreshold` option takes precedence over `minPadding`,
* so if the data is tangent to the threshold, `minPadding` may not
* apply unless `softThreshold` is set to false.
*
* @sample {highcharts} highcharts/yaxis/minpadding/
* Min padding of 0.2
* @sample {highstock} stock/xaxis/minpadding-maxpadding/
* Greater min- and maxPadding
*
* @since 1.2.0
* @product highcharts highstock gantt
*/
minPadding: 0.05,
/**
* @productdesc {highstock}
* In Highcharts Stock 1.x, the Y axis was placed
* on the left side by default.
*
* @sample {highcharts} highcharts/yaxis/opposite/
* Secondary Y axis opposite
* @sample {highstock} stock/xaxis/opposite/
* Y axis on left side
*
* @type {boolean}
* @default {highstock} true
* @default {highcharts} false
* @product highstock highcharts gantt
* @apioption yAxis.opposite
*/
/**
* @see [tickInterval](#xAxis.tickInterval)
* @see [tickPositioner](#xAxis.tickPositioner)
* @see [tickPositions](#xAxis.tickPositions)
*/
tickPixelInterval: 72,
/**
* Whether to show the last tick label.
*
* @productdesc {highcharts|gantt}
* Defaults to `true` on cartesian charts, and `false` on polar charts.
*
* @productdesc {highstock}
* Defaults to `true` for categorized yAxis and `false` for other types
* of yAxis.
*
* @default undefined
*/
showLastLabel: true,
/**
* @extends xAxis.labels
*/
labels: {
/**
* The label's pixel distance from the perimeter of the plot area.
* On cartesian charts, this is overridden if the `labels.y` setting
* is set.
*
* On polar charts, if it's a percentage string, it is interpreted
* the same as [series.radius](#plotOptions.gauge.radius), so the
* label can be aligned under the gauge's shape.
*
* @sample {highcharts} highcharts/yaxis/labels-distance/
* Polar chart, labels centered under the arc
*
* @type {number|string}
* @product highcharts
* @apioption yAxis.labels.distance
*/
/**
* The y position offset of all labels relative to the tick
* positions on the axis. For polar and radial axis consider the use
* of the [distance](#yAxis.labels.distance) option.
*
* @sample {highcharts} highcharts/xaxis/labels-x/
* Y axis labels placed on grid lines
*
* @type {number}
* @default {highcharts} 3
* @default {highstock} -2
* @default {highmaps} 3
* @apioption yAxis.labels.y
*/
/**
* What part of the string the given position is anchored to. Can
* be one of `"left"`, `"center"` or `"right"`. The exact position
* also depends on the `labels.x` setting.
*
* Angular gauges and solid gauges defaults to `"center"`.
* Solid gauges with two labels have additional option `"auto"`
* for automatic horizontal and vertical alignment.
*
* @sample {highcharts} highcharts/yaxis/labels-align-left/
* Left
* @sample {highcharts} highcharts/series-solidgauge/labels-auto-aligned/
* Solid gauge labels auto aligned
*
* @type {Highcharts.AlignValue}
* @default {highstock} right
* @apioption yAxis.labels.align
*/
/**
* The x position offset of all labels relative to the tick
* positions on the axis. Defaults to -15 for left axis, 15 for
* right axis.
*
* @sample {highcharts} highcharts/xaxis/labels-x/
* Y axis labels placed on grid lines
*
* @type {number}
*/
x: void 0
},
/**
* @sample {highcharts} highcharts/yaxis/max-200/
* Y axis max of 200
* @sample {highcharts} highcharts/yaxis/max-logarithmic/
* Y axis max on logarithmic axis
* @sample {highstock} stock/yaxis/min-max/
* Fixed min and max on Y axis
*
* @apioption yAxis.max
*/
/**
* @sample {highcharts} highcharts/yaxis/min-startontick-false/
* -50 with startOnTick to false
* @sample {highcharts} highcharts/yaxis/min-startontick-true/
* -50 with startOnTick true by default
* @sample {highstock} stock/yaxis/min-max/
* Fixed min and max on Y axis
*
* @apioption yAxis.min
*/
/**
* An optional scrollbar to display on the Y axis in response to
* limiting the minimum an maximum of the axis values.
*
* In styled mode, all the presentational options for the scrollbar
* are replaced by the classes `.highcharts-scrollbar-thumb`,
* `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
* `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
*
* @sample {highstock} stock/yaxis/scrollbar/
* Scrollbar on the Y axis
*
* @extends scrollbar
* @since 4.2.6
* @product highstock
* @excluding height
* @apioption yAxis.scrollbar
*/
/**
* Enable the scrollbar on the Y axis.
*
* @sample {highstock} stock/yaxis/scrollbar/
* Enabled on Y axis
*
* @type {boolean}
* @default false
* @since 4.2.6
* @product highstock
* @apioption yAxis.scrollbar.enabled
*/
/**
* Pixel margin between the scrollbar and the axis elements.
*
* @type {number}
* @default 10
* @since 4.2.6
* @product highstock
* @apioption yAxis.scrollbar.margin
*/
/* eslint-disable highcharts/doclet-apioption-last */
/**
* Defines the position of the scrollbar. By default, it is positioned
* on the opposite of the main axis (right side of the chart).
* However, in the case of RTL languages could be set to `false`
* which positions the scrollbar on the left.
*
* Works only for vertical axes.
* This means yAxis in a non-inverted chart and xAxis in the inverted.
*
* @sample stock/yaxis/scrollbar-opposite/
* A scrollbar not on the opposite side
*
* @type {boolean}
* @default true
* @since 9.3.0
*
* @apioption yAxis.scrollbar.opposite
* @apioption xAxis.scrollbar.opposite
*
*/
/* eslint-enable highcharts/doclet-apioption-last */
/**
* Whether to show the scrollbar when it is fully zoomed out at max
* range. Setting it to `false` on the Y axis makes the scrollbar stay
* hidden until the user zooms in, like common in browsers.
*
* @type {boolean}
* @default true
* @since 4.2.6
* @product highstock
* @apioption yAxis.scrollbar.showFull
*/
/**
* The width of a vertical scrollbar or height of a horizontal
* scrollbar. Defaults to 20 on touch devices.
*
* @type {number}
* @default 14
* @since 4.2.6
* @product highstock
* @apioption yAxis.scrollbar.size
*/
/**
* Z index of the scrollbar elements.
*
* @type {number}
* @default 3
* @since 4.2.6
* @product highstock
* @apioption yAxis.scrollbar.zIndex
*/
/**
* A soft maximum for the axis. If the series data maximum is less
* than this, the axis will stay at this maximum, but if the series
* data maximum is higher, the axis will flex to show all data.
*
* **Note**: The [series.softThreshold](
* #plotOptions.series.softThreshold) option takes precedence over this
* option.
*
* @sample highcharts/yaxis/softmin-softmax/
* Soft min and max
*
* @type {number}
* @since 5.0.1
* @product highcharts highstock gantt
* @apioption yAxis.softMax
*/
/**
* A soft minimum for the axis. If the series data minimum is greater
* than this, the axis will stay at this minimum, but if the series
* data minimum is lower, the axis will flex to show all data.
*
* **Note**: The [series.softThreshold](
* #plotOptions.series.softThreshold) option takes precedence over this
* option.
*
* @sample highcharts/yaxis/softmin-softmax/
* Soft min and max
*
* @type {number}
* @since 5.0.1
* @product highcharts highstock gantt
* @apioption yAxis.softMin
*/
/**
* Defines the horizontal alignment of the stack total label. Can be one
* of `"left"`, `"center"` or `"right"`. The default value is calculated
* at runtime and depends on orientation and whether the stack is
* positive or negative.
*
* @sample {highcharts} highcharts/yaxis/stacklabels-align-left/
* Aligned to the left
* @sample {highcharts} highcharts/yaxis/stacklabels-align-center/
* Aligned in center
* @sample {highcharts} highcharts/yaxis/stacklabels-align-right/
* Aligned to the right
*
* @type {Highcharts.AlignValue}
* @since 2.1.5
* @product highcharts
* @apioption yAxis.stackLabels.align
*/
/**
* A format string for the data label. Available variables are the same
* as for `formatter`.
*
* @type {string}
* @default {total}
* @since 3.0.2
* @product highcharts highstock
* @apioption yAxis.stackLabels.format
*/
/**
* Rotation of the labels in degrees.
*
* @sample {highcharts} highcharts/yaxis/stacklabels-rotation/
* Labels rotated 45°
*
* @type {number}
* @default 0
* @since 2.1.5
* @product highcharts
* @apioption yAxis.stackLabels.rotation
*/
/**
* The text alignment for the label. While `align` determines where the
* texts anchor point is placed with regards to the stack, `textAlign`
* determines how the text is aligned against its anchor point. Possible
* values are `"left"`, `"center"` and `"right"`. The default value is
* calculated at runtime and depends on orientation and whether the
* stack is positive or negative.
*
* @sample {highcharts} highcharts/yaxis/stacklabels-textalign-left/
* Label in center position but text-aligned left
*
* @type {Highcharts.AlignValue}
* @since 2.1.5
* @product highcharts
* @apioption yAxis.stackLabels.textAlign
*/
/**
* Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
* to render the labels.
*
* @type {boolean}
* @default false
* @since 3.0
* @product highcharts highstock
* @apioption yAxis.stackLabels.useHTML
*/
/**
* Defines the vertical alignment of the stack total label. Can be one
* of `"top"`, `"middle"` or `"bottom"`. The default value is calculated
* at runtime and depends on orientation and whether the stack is
* positive or negative.
*
* @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-top/
* Vertically aligned top
* @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-middle/
* Vertically aligned middle
* @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-bottom/
* Vertically aligned bottom
*
* @type {Highcharts.VerticalAlignValue}
* @since 2.1.5
* @product highcharts
* @apioption yAxis.stackLabels.verticalAlign
*/
/**
* The x position offset of the label relative to the left of the
* stacked bar. The default value is calculated at runtime and depends
* on orientation and whether the stack is positive or negative.
*
* @sample {highcharts} highcharts/yaxis/stacklabels-x/
* Stack total labels with x offset
*
* @type {number}
* @since 2.1.5
* @product highcharts
* @apioption yAxis.stackLabels.x
*/
/**
* The y position offset of the label relative to the tick position
* on the axis. The default value is calculated at runtime and depends
* on orientation and whether the stack is positive or negative.
*
* @sample {highcharts} highcharts/yaxis/stacklabels-y/
* Stack total labels with y offset
*
* @type {number}
* @since 2.1.5
* @product highcharts
* @apioption yAxis.stackLabels.y
*/
/**
* Whether to force the axis to start on a tick. Use this option with
* the `maxPadding` option to control the axis start.
*
* This option is always disabled, when panning type is
* either `y` or `xy`.
*
* @see [type](#chart.panning.type)
*
* @sample {highcharts} highcharts/xaxis/startontick-false/
* False by default
* @sample {highcharts} highcharts/xaxis/startontick-true/
* True
* @sample {highstock} stock/xaxis/endontick/
* False for Y axis
*
* @since 1.2.0
* @product highcharts highstock gantt
*/
startOnTick: true,
title: {
/**
* The pixel distance between the axis labels and the title.
* Positive values are outside the axis line, negative are inside.
*
* @sample {highcharts} highcharts/xaxis/title-margin/
* Y axis title margin of 60
*
* @type {number}
* @default 40
* @apioption yAxis.title.margin
*/
/**
* The actual text of the axis title. Horizontal texts can contain
* HTML, but rotated texts are painted using vector techniques and
* must be clean text. The Y axis title is disabled by setting the
* `text` option to `undefined`.
*
* @sample {highcharts} highcharts/xaxis/title-text/
* Custom HTML
*
* @type {string|null}
* @default {highcharts} Values
* @default {highstock} undefined
* @product highcharts highstock gantt
*/
text: 'Values'
},
/**
* The top position of the Y axis. If it's a number, it is interpreted
* as pixel position relative to the chart.
*
* Since Highcharts 2: If it's a percentage string, it is interpreted as
* percentages of the plot height, offset from plot area top.
*
* @see [yAxis.height](#yAxis.height)
*
* @sample {highstock} stock/demo/candlestick-and-volume/
* Percentage height panes
*
* @type {number|string}
* @product highcharts highstock
* @apioption yAxis.top
*/
/**
* The stack labels show the total value for each bar in a stacked
* column or bar chart. The label will be placed on top of positive
* columns and below negative columns. In case of an inverted column
* chart or a bar chart the label is placed to the right of positive
* bars and to the left of negative bars.
*
* @product highcharts
*/
stackLabels: {
/**
* Enable or disable the initial animation when a series is
* displayed for the `stackLabels`. The animation can also be set as
* a configuration object. Please note that this option only
* applies to the initial animation.
* For other animations, see [chart.animation](#chart.animation)
* and the animation parameter under the API methods.
* The following properties are supported:
*
* - `defer`: The animation delay time in milliseconds.
*
* @sample {highcharts} highcharts/plotoptions/animation-defer/
* Animation defer settings
* @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
* @since 8.2.0
* @apioption yAxis.stackLabels.animation
*/
animation: {},
/**
* The animation delay time in milliseconds.
* Set to `0` renders stackLabel immediately.
* As `undefined` inherits defer time from the [series.animation.defer](#plotOptions.series.animation.defer).
*
* @type {number}
* @since 8.2.0
* @apioption yAxis.stackLabels.animation.defer
*/
/**
* Allow the stack labels to overlap.
*
* @sample {highcharts} highcharts/yaxis/stacklabels-allowoverlap-false/
* Default false
*
* @since 5.0.13
* @product highcharts
*/
allowOverlap: false,
/**
* The background color or gradient for the stack label.
*
* @sample {highcharts} highcharts/yaxis/stacklabels-box/
* Stack labels box options
* @type {Highcharts.ColorType}
* @since 8.1.0
* @apioption yAxis.stackLabels.backgroundColor
*/
/**
* The border color for the stack label. Defaults to `undefined`.
*
* @sample {highcharts} highcharts/yaxis/stacklabels-box/
* Stack labels box options
* @type {Highcharts.ColorType}
* @since 8.1.0
* @apioption yAxis.stackLabels.borderColor
*/
/**
* The border radius in pixels for the stack label.
*
* @sample {highcharts} highcharts/yaxis/stacklabels-box/
* Stack labels box options
* @type {number}
* @default 0
* @since 8.1.0
* @apioption yAxis.stackLabels.borderRadius
*/
/**
* The border width in pixels for the stack label.
*
* @sample {highcharts} highcharts/yaxis/stacklabels-box/
* Stack labels box options
* @type {number}
* @default 0
* @since 8.1.0
* @apioption yAxis.stackLabels.borderWidth
*/
/**
* Enable or disable the stack total labels.
*
* @sample {highcharts} highcharts/yaxis/stacklabels-enabled/
* Enabled stack total labels
* @sample {highcharts} highcharts/yaxis/stacklabels-enabled-waterfall/
* Enabled stack labels in waterfall chart
*
* @since 2.1.5
* @product highcharts
*/
enabled: false,
/**
* Whether to hide stack labels that are outside the plot area.
* By default, the stack label is moved
* inside the plot area according to the
* [overflow](/highcharts/#yAxis/stackLabels/overflow)
* option.
*
* @type {boolean}
* @since 7.1.3
*/
crop: true,
/**
* How to handle stack total labels that flow outside the plot area.
* The default is set to `"justify"`,
* which aligns them inside the plot area.
* For columns and bars, this means it will be moved inside the bar.
* To display stack labels outside the plot area,
* set `crop` to `false` and `overflow` to `"allow"`.
*
* @sample highcharts/yaxis/stacklabels-overflow/
* Stack labels flows outside the plot area.
*
* @type {Highcharts.DataLabelsOverflowValue}
* @since 7.1.3
*/
overflow: 'justify',
/* eslint-disable valid-jsdoc */
/**
* Callback JavaScript function to format the label. The value is
* given by `this.total`.
*
* @sample {highcharts} highcharts/yaxis/stacklabels-formatter/
* Added units to stack total value
*
* @type {Highcharts.FormatterCallbackFunction<Highcharts.StackItemObject>}
* @since 2.1.5
* @product highcharts
*/
formatter: function () {
var numberFormatter = this.axis.chart.numberFormatter;
/* eslint-enable valid-jsdoc */
return numberFormatter(this.total || 0, -1);
},
/**
* CSS styles for the label.
*
* In styled mode, the styles are set in the
* `.highcharts-stack-label` class.
*
* @sample {highcharts} highcharts/yaxis/stacklabels-style/
* Red stack total labels
*
* @type {Highcharts.CSSObject}
* @since 2.1.5
* @product highcharts
*/
style: {
/** @internal */
color: "#000000" /* Palette.neutralColor100 */,
/**
* @type {number|string}
*/
fontSize: '0.7em',
/** @internal */
fontWeight: 'bold',
/** @internal */
textOutline: '1px contrast'
}
},
gridLineWidth: 1,
lineWidth: 0
};
})(AxisDefaults || (AxisDefaults = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Axis_AxisDefaults = (AxisDefaults);
;// ./code/es5/es-modules/Core/Foundation.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var Foundation_addEvent = Core_Utilities.addEvent, Foundation_isFunction = Core_Utilities.isFunction, Foundation_objectEach = Core_Utilities.objectEach, Foundation_removeEvent = Core_Utilities.removeEvent;
/* *
*
* Class Namespace
*
* */
var Foundation;
(function (Foundation) {
/* *
*
* Functions
*
* */
/* eslint-disable valid-jsdoc */
/**
* Register event options. If an event handler is set on the options, it
* should be subject to Chart.update, Axis.update and Series.update. This is
* contrary to general handlers that are set directly using addEvent either
* on the class or on the instance. #6538, #6943, #10861.
* @private
*/
function registerEventOptions(component, options) {
// A lookup over those events that are added by _options_ (not
// programmatically). These are updated through .update()
component.eventOptions = component.eventOptions || {};
// Register event listeners
Foundation_objectEach(options.events, function (event, eventType) {
// If event does not exist, or is changed by the .update()
// function
if (component.eventOptions[eventType] !== event) {
// Remove existing if set by option
if (component.eventOptions[eventType]) {
Foundation_removeEvent(component, eventType, component.eventOptions[eventType]);
delete component.eventOptions[eventType];
}
if (Foundation_isFunction(event)) {
component.eventOptions[eventType] = event;
Foundation_addEvent(component, eventType, event, {
order: 0 // #14080 fire those events as firsts
});
}
}
});
}
Foundation.registerEventOptions = registerEventOptions;
})(Foundation || (Foundation = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Core_Foundation = (Foundation);
;// ./code/es5/es-modules/Core/Axis/Tick.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var Tick_deg2rad = Core_Globals.deg2rad;
var Tick_clamp = Core_Utilities.clamp, Tick_correctFloat = Core_Utilities.correctFloat, Tick_defined = Core_Utilities.defined, Tick_destroyObjectProperties = Core_Utilities.destroyObjectProperties, Tick_extend = Core_Utilities.extend, Tick_fireEvent = Core_Utilities.fireEvent, Tick_getAlignFactor = Core_Utilities.getAlignFactor, Tick_isNumber = Core_Utilities.isNumber, Tick_merge = Core_Utilities.merge, Tick_objectEach = Core_Utilities.objectEach, Tick_pick = Core_Utilities.pick;
/* *
*
* Class
*
* */
/* eslint-disable no-invalid-this, valid-jsdoc */
/**
* The Tick class.
*
* @class
* @name Highcharts.Tick
*
* @param {Highcharts.Axis} axis
* The axis of the tick.
*
* @param {number} pos
* The position of the tick on the axis in terms of axis values.
*
* @param {string} [type]
* The type of tick, either 'minor' or an empty string
*
* @param {boolean} [noLabel=false]
* Whether to disable the label or not. Defaults to false.
*
* @param {Object} [parameters]
* Optional parameters for the tick.
*/
var Tick = /** @class */ (function () {
/* *
*
* Constructors
*
* */
function Tick(axis, pos, type, noLabel, parameters) {
this.isNew = true;
this.isNewLabel = true;
/**
* The related axis of the tick.
* @name Highcharts.Tick#axis
* @type {Highcharts.Axis}
*/
this.axis = axis;
/**
* The logical position of the tick on the axis in terms of axis values.
* @name Highcharts.Tick#pos
* @type {number}
*/
this.pos = pos;
/**
* The tick type, which can be `"minor"`, or an empty string.
* @name Highcharts.Tick#type
* @type {string}
*/
this.type = type || '';
this.parameters = parameters || {};
/**
* The mark offset of the tick on the axis. Usually `undefined`, numeric
* for grid axes.
* @name Highcharts.Tick#tickmarkOffset
* @type {number|undefined}
*/
this.tickmarkOffset = this.parameters.tickmarkOffset;
this.options = this.parameters.options;
Tick_fireEvent(this, 'init');
if (!type && !noLabel) {
this.addLabel();
}
}
/* *
*
* Functions
*
* */
/**
* Write the tick label.
*
* @private
* @function Highcharts.Tick#addLabel
*/
Tick.prototype.addLabel = function () {
var tick = this,
axis = tick.axis,
options = axis.options,
chart = axis.chart,
categories = axis.categories,
log = axis.logarithmic,
names = axis.names,
pos = tick.pos,
labelOptions = Tick_pick(tick.options && tick.options.labels,
options.labels),
tickPositions = axis.tickPositions,
isFirst = pos === tickPositions[0],
isLast = pos === tickPositions[tickPositions.length - 1],
animateLabels = (!labelOptions.step || labelOptions.step === 1) &&
axis.tickInterval === 1,
tickPositionInfo = tickPositions.info;
var label = tick.label,
dateTimeLabelFormat,
dateTimeLabelFormats,
i;
// The context value
var value = this.parameters.category || (categories ?
Tick_pick(categories[pos],
names[pos],
pos) :
pos);
if (log && Tick_isNumber(value)) {
value = Tick_correctFloat(log.lin2log(value));
}
// Set the datetime label format. If a higher rank is set for this
// position, use that. If not, use the general format.
if (axis.dateTime) {
if (tickPositionInfo) {
dateTimeLabelFormats = chart.time.resolveDTLFormat(options.dateTimeLabelFormats[(!options.grid &&
tickPositionInfo.higherRanks[pos]) ||
tickPositionInfo.unitName]);
dateTimeLabelFormat = dateTimeLabelFormats.main;
}
else if (Tick_isNumber(value)) { // #1441
dateTimeLabelFormat = axis.dateTime.getXDateFormat(value, options.dateTimeLabelFormats ||
{});
}
}
// Set properties for access in render method
/**
* True if the tick is the first one on the axis.
* @name Highcharts.Tick#isFirst
* @readonly
* @type {boolean|undefined}
*/
tick.isFirst = isFirst;
/**
* True if the tick is the last one on the axis.
* @name Highcharts.Tick#isLast
* @readonly
* @type {boolean|undefined}
*/
tick.isLast = isLast;
// Get the string
var ctx = {
axis: axis,
chart: chart,
dateTimeLabelFormat: dateTimeLabelFormat,
isFirst: isFirst,
isLast: isLast,
pos: pos,
tick: tick,
tickPositionInfo: tickPositionInfo,
value: value
};
// Fire an event that allows modifying the context for use in
// `labels.format` and `labels.formatter`.
Tick_fireEvent(this, 'labelFormat', ctx);
// Label formatting. When `labels.format` is given, we first run the
// defaultFormatter and append the result to the context as `text`.
// Handy for adding prefix or suffix while keeping default number
// formatting.
var labelFormatter = function (ctx) {
if (labelOptions.formatter) {
return labelOptions.formatter.call(ctx,
ctx);
}
if (labelOptions.format) {
ctx.text = axis.defaultLabelFormatter.call(ctx);
return Core_Templating.format(labelOptions.format, ctx, chart);
}
return axis.defaultLabelFormatter.call(ctx);
};
var str = labelFormatter.call(ctx,
ctx);
// Set up conditional formatting based on the format list if existing.
var list = dateTimeLabelFormats && dateTimeLabelFormats.list;
if (list) {
tick.shortenLabel = function () {
for (i = 0; i < list.length; i++) {
Tick_extend(ctx, { dateTimeLabelFormat: list[i] });
label.attr({
text: labelFormatter.call(ctx, ctx)
});
if (label.getBBox().width <
axis.getSlotWidth(tick) - 2 *
(labelOptions.padding || 0)) {
return;
}
}
label.attr({
text: ''
});
};
}
else {
// #15692
tick.shortenLabel = void 0;
}
// Call only after first render
if (animateLabels && axis._addedPlotLB) {
tick.moveLabel(str, labelOptions);
}
// First call
if (!Tick_defined(label) && !tick.movedLabel) {
/**
* The rendered text label of the tick.
* @name Highcharts.Tick#label
* @type {Highcharts.SVGElement|undefined}
*/
tick.label = label = tick.createLabel(str, labelOptions);
// Base value to detect change for new calls to getBBox
tick.rotation = 0;
// Update
}
else if (label && label.textStr !== str && !animateLabels) {
// When resetting text, also reset the width if dynamically set
// (#8809)
if (label.textWidth &&
!labelOptions.style.width &&
!label.styles.width) {
label.css({ width: null });
}
label.attr({ text: str });
label.textPxLength = label.getBBox().width;
}
};
/**
* Render and return the label of the tick.
*
* @private
* @function Highcharts.Tick#createLabel
*/
Tick.prototype.createLabel = function (str, labelOptions, xy) {
var axis = this.axis,
_a = axis.chart,
renderer = _a.renderer,
styledMode = _a.styledMode,
label = Tick_defined(str) && labelOptions.enabled ?
renderer
.text(str,
xy === null || xy === void 0 ? void 0 : xy.x,
xy === null || xy === void 0 ? void 0 : xy.y,
labelOptions.useHTML)
.add(axis.labelGroup) :
void 0;
// Un-rotated length
if (label) {
var whiteSpace = labelOptions.style.whiteSpace || 'normal';
// Without position absolute, IE export sometimes is wrong
if (!styledMode) {
label.css(Tick_merge(labelOptions.style, { whiteSpace: 'nowrap' }));
}
label.textPxLength = label.getBBox().width;
if (!styledMode) {
label.css({ whiteSpace: whiteSpace });
}
}
return label;
};
/**
* Destructor for the tick prototype
*
* @private
* @function Highcharts.Tick#destroy
*/
Tick.prototype.destroy = function () {
Tick_destroyObjectProperties(this, this.axis);
};
/**
* Gets the x and y positions for ticks in terms of pixels.
*
* @private
* @function Highcharts.Tick#getPosition
*
* @param {boolean} horiz
* Whether the tick is on an horizontal axis or not.
*
* @param {number} tickPos
* Position of the tick.
*
* @param {number} tickmarkOffset
* Tickmark offset for all ticks.
*
* @param {boolean} [old]
* Whether the axis has changed or not.
*
* @return {Highcharts.PositionObject}
* The tick position.
*
* @emits Highcharts.Tick#event:afterGetPosition
*/
Tick.prototype.getPosition = function (horiz, tickPos, tickmarkOffset, old) {
var axis = this.axis,
chart = axis.chart,
cHeight = (old && chart.oldChartHeight) || chart.chartHeight,
pos = {
x: horiz ?
Tick_correctFloat(axis.translate(tickPos + tickmarkOffset,
void 0,
void 0,
old) +
axis.transB) :
(axis.left +
axis.offset +
(axis.opposite ?
(((old && chart.oldChartWidth) ||
chart.chartWidth) -
axis.right -
axis.left) :
0)),
y: horiz ?
(cHeight -
axis.bottom +
axis.offset -
(axis.opposite ? axis.height : 0)) :
Tick_correctFloat(cHeight -
axis.translate(tickPos + tickmarkOffset,
void 0,
void 0,
old) -
axis.transB)
};
// Chrome workaround for #10516
pos.y = Tick_clamp(pos.y, -1e9, 1e9);
Tick_fireEvent(this, 'afterGetPosition', { pos: pos });
return pos;
};
/**
* Get the x, y position of the tick label
* @private
*/
Tick.prototype.getLabelPosition = function (x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
var axis = this.axis,
transA = axis.transA,
reversed = ( // #7911
axis.isLinked && axis.linkedParent ?
axis.linkedParent.reversed :
axis.reversed),
staggerLines = axis.staggerLines,
rotCorr = axis.tickRotCorr || { x: 0,
y: 0 },
// Adjust for label alignment if we use reserveSpace: true (#5286)
labelOffsetCorrection = (!horiz && !axis.reserveSpaceDefault ?
-axis.labelOffset * (axis.labelAlign === 'center' ? 0.5 : 1) :
0),
distance = labelOptions.distance,
pos = {};
var yOffset,
line;
if (axis.side === 0) {
yOffset = label.rotation ? -distance : -label.getBBox().height;
}
else if (axis.side === 2) {
yOffset = rotCorr.y + distance;
}
else {
// #3140, #3140
yOffset = Math.cos(label.rotation * Tick_deg2rad) *
(rotCorr.y - label.getBBox(false, 0).height / 2);
}
if (Tick_defined(labelOptions.y)) {
yOffset = axis.side === 0 && axis.horiz ?
labelOptions.y + yOffset :
labelOptions.y;
}
x = x +
Tick_pick(labelOptions.x, [0, 1, 0, -1][axis.side] * distance) +
labelOffsetCorrection +
rotCorr.x -
(tickmarkOffset && horiz ?
tickmarkOffset * transA * (reversed ? -1 : 1) :
0);
y = y + yOffset - (tickmarkOffset && !horiz ?
tickmarkOffset * transA * (reversed ? 1 : -1) : 0);
// Correct for staggered labels
if (staggerLines) {
line = (index / (step || 1) % staggerLines);
if (axis.opposite) {
line = staggerLines - line - 1;
}
y += line * (axis.labelOffset / staggerLines);
}
pos.x = x;
pos.y = Math.round(y);
Tick_fireEvent(this, 'afterGetLabelPosition', { pos: pos, tickmarkOffset: tickmarkOffset, index: index });
return pos;
};
/**
* Get the offset height or width of the label
*
* @private
* @function Highcharts.Tick#getLabelSize
*/
Tick.prototype.getLabelSize = function () {
return this.label ?
this.label.getBBox()[this.axis.horiz ? 'height' : 'width'] :
0;
};
/**
* Extendible method to return the path of the marker
* @private
*/
Tick.prototype.getMarkPath = function (x, y, tickLength, tickWidth, horiz, renderer) {
if (horiz === void 0) { horiz = false; }
return renderer.crispLine([[
'M',
x,
y
], [
'L',
x + (horiz ? 0 : -tickLength),
y + (horiz ? tickLength : 0)
]], tickWidth);
};
/**
* Handle the label overflow by adjusting the labels to the left and right
* edge, or hide them if they collide into the neighbour label.
*
* @private
* @function Highcharts.Tick#handleOverflow
*/
Tick.prototype.handleOverflow = function (xy) {
var tick = this,
axis = this.axis,
labelOptions = axis.options.labels,
pxPos = xy.x,
chartWidth = axis.chart.chartWidth,
spacing = axis.chart.spacing,
leftBound = Tick_pick(axis.labelLeft,
Math.min(axis.pos,
spacing[3])),
rightBound = Tick_pick(axis.labelRight,
Math.max(!axis.isRadial ? axis.pos + axis.len : 0,
chartWidth - spacing[1])),
label = this.label,
rotation = this.rotation,
factor = Tick_getAlignFactor(axis.labelAlign || label.attr('align')),
labelWidth = label.getBBox().width,
slotWidth = axis.getSlotWidth(tick),
xCorrection = factor,
css = {};
var modifiedSlotWidth = slotWidth,
goRight = 1,
leftPos,
rightPos,
textWidth;
// Check if the label overshoots the chart spacing box. If it does, move
// it. If it now overshoots the slotWidth, add ellipsis.
if (!rotation && labelOptions.overflow === 'justify') {
leftPos = pxPos - factor * labelWidth;
rightPos = pxPos + (1 - factor) * labelWidth;
if (leftPos < leftBound) {
modifiedSlotWidth =
xy.x + modifiedSlotWidth * (1 - factor) - leftBound;
}
else if (rightPos > rightBound) {
modifiedSlotWidth =
rightBound - xy.x + modifiedSlotWidth * factor;
goRight = -1;
}
modifiedSlotWidth = Math.min(slotWidth, modifiedSlotWidth); // #4177
if (modifiedSlotWidth < slotWidth && axis.labelAlign === 'center') {
xy.x += (goRight *
(slotWidth -
modifiedSlotWidth -
xCorrection * (slotWidth - Math.min(labelWidth, modifiedSlotWidth))));
}
// If the label width exceeds the available space, set a text width
// to be picked up below. Also, if a width has been set before, we
// need to set a new one because the reported labelWidth will be
// limited by the box (#3938).
if (labelWidth > modifiedSlotWidth ||
(axis.autoRotation && (label.styles || {}).width)) {
textWidth = modifiedSlotWidth;
}
// Add ellipsis to prevent rotated labels to be clipped against the edge
// of the chart
}
else if (rotation < 0 &&
pxPos - factor * labelWidth < leftBound) {
textWidth = Math.round(pxPos / Math.cos(rotation * Tick_deg2rad) - leftBound);
}
else if (rotation > 0 &&
pxPos + factor * labelWidth > rightBound) {
textWidth = Math.round((chartWidth - pxPos) /
Math.cos(rotation * Tick_deg2rad));
}
if (textWidth && label) {
if (tick.shortenLabel) {
tick.shortenLabel();
}
else {
label.css(Tick_extend(css, {
width: Math.floor(textWidth) + 'px',
lineClamp: axis.isRadial ? 0 : 1
}));
}
}
};
/**
* Try to replace the label if the same one already exists.
*
* @private
* @function Highcharts.Tick#moveLabel
*/
Tick.prototype.moveLabel = function (str, labelOptions) {
var tick = this,
label = tick.label,
axis = tick.axis;
var moved = false,
labelPos;
if (label && label.textStr === str) {
tick.movedLabel = label;
moved = true;
delete tick.label;
}
else { // Find a label with the same string
Tick_objectEach(axis.ticks, function (currentTick) {
if (!moved &&
!currentTick.isNew &&
currentTick !== tick &&
currentTick.label &&
currentTick.label.textStr === str) {
tick.movedLabel = currentTick.label;
moved = true;
currentTick.labelPos = tick.movedLabel.xy;
delete currentTick.label;
}
});
}
// Create new label if the actual one is moved
if (!moved && (tick.labelPos || label)) {
labelPos = tick.labelPos || label.xy;
tick.movedLabel = tick.createLabel(str, labelOptions, labelPos);
if (tick.movedLabel) {
tick.movedLabel.attr({ opacity: 0 });
}
}
};
/**
* Put everything in place
*
* @private
* @param {number} index
*
* @param {boolean} [old]
* Use old coordinates to prepare an animation into new position
*
* @param {number} [opacity]
*/
Tick.prototype.render = function (index, old, opacity) {
var tick = this,
axis = tick.axis,
horiz = axis.horiz,
pos = tick.pos,
tickmarkOffset = Tick_pick(tick.tickmarkOffset,
axis.tickmarkOffset),
xy = tick.getPosition(horiz,
pos,
tickmarkOffset,
old),
x = xy.x,
y = xy.y,
axisStart = axis.pos,
axisEnd = axisStart + axis.len,
pxPos = horiz ? x : y;
// Anything that is not between `axis.pos` and `axis.pos + axis.length`
// should not be visible (#20166). The `correctFloat` is for reversed
// axes in Safari.
if (!axis.chart.polar &&
tick.isNew &&
(Tick_correctFloat(pxPos) < axisStart || pxPos > axisEnd)) {
opacity = 0;
}
var labelOpacity = Tick_pick(opacity,
tick.label && tick.label.newOpacity, // #15528
1);
opacity = Tick_pick(opacity, 1);
this.isActive = true;
// Create the grid line
this.renderGridLine(old, opacity);
// Create the tick mark
this.renderMark(xy, opacity);
// The label is created on init - now move it into place
this.renderLabel(xy, old, labelOpacity, index);
tick.isNew = false;
Tick_fireEvent(this, 'afterRender');
};
/**
* Renders the gridLine.
*
* @private
* @function Highcharts.Tick#renderGridLine
* @param {boolean} old Whether or not the tick is old
* @param {number} opacity The opacity of the grid line
*/
Tick.prototype.renderGridLine = function (old, opacity) {
var tick = this,
axis = tick.axis,
options = axis.options,
attribs = {},
pos = tick.pos,
type = tick.type,
tickmarkOffset = Tick_pick(tick.tickmarkOffset,
axis.tickmarkOffset),
renderer = axis.chart.renderer;
var gridLine = tick.gridLine,
gridLinePath,
gridLineWidth = options.gridLineWidth,
gridLineColor = options.gridLineColor,
dashStyle = options.gridLineDashStyle;
if (tick.type === 'minor') {
gridLineWidth = options.minorGridLineWidth;
gridLineColor = options.minorGridLineColor;
dashStyle = options.minorGridLineDashStyle;
}
if (!gridLine) {
if (!axis.chart.styledMode) {
attribs.stroke = gridLineColor;
attribs['stroke-width'] = gridLineWidth || 0;
attribs.dashstyle = dashStyle;
}
if (!type) {
attribs.zIndex = 1;
}
if (old) {
opacity = 0;
}
/**
* The rendered grid line of the tick.
* @name Highcharts.Tick#gridLine
* @type {Highcharts.SVGElement|undefined}
*/
tick.gridLine = gridLine = renderer.path()
.attr(attribs)
.addClass('highcharts-' + (type ? type + '-' : '') + 'grid-line')
.add(axis.gridGroup);
}
if (gridLine) {
gridLinePath = axis.getPlotLinePath({
value: pos + tickmarkOffset,
lineWidth: gridLine.strokeWidth(),
force: 'pass',
old: old,
acrossPanes: false // #18025
});
// If the parameter 'old' is set, the current call will be followed
// by another call, therefore do not do any animations this time
if (gridLinePath) {
gridLine[old || tick.isNew ? 'attr' : 'animate']({
d: gridLinePath,
opacity: opacity
});
}
}
};
/**
* Renders the tick mark.
*
* @private
* @function Highcharts.Tick#renderMark
* @param {Highcharts.PositionObject} xy The position vector of the mark
* @param {number} opacity The opacity of the mark
*/
Tick.prototype.renderMark = function (xy, opacity) {
var tick = this, axis = tick.axis, options = axis.options, renderer = axis.chart.renderer, type = tick.type, tickSize = axis.tickSize(type ? type + 'Tick' : 'tick'), x = xy.x, y = xy.y, tickWidth = Tick_pick(options[type !== 'minor' ? 'tickWidth' : 'minorTickWidth'], !type && axis.isXAxis ? 1 : 0), // X axis defaults to 1
tickColor = options[type !== 'minor' ? 'tickColor' : 'minorTickColor'];
var mark = tick.mark;
var isNewMark = !mark;
if (tickSize) {
// Negate the length
if (axis.opposite) {
tickSize[0] = -tickSize[0];
}
// First time, create it
if (!mark) {
/**
* The rendered mark of the tick.
* @name Highcharts.Tick#mark
* @type {Highcharts.SVGElement|undefined}
*/
tick.mark = mark = renderer.path()
.addClass('highcharts-' + (type ? type + '-' : '') + 'tick')
.add(axis.axisGroup);
if (!axis.chart.styledMode) {
mark.attr({
stroke: tickColor,
'stroke-width': tickWidth
});
}
}
mark[isNewMark ? 'attr' : 'animate']({
d: tick.getMarkPath(x, y, tickSize[0], mark.strokeWidth(), axis.horiz, renderer),
opacity: opacity
});
}
};
/**
* Renders the tick label.
* Note: The label should already be created in init(), so it should only
* have to be moved into place.
*
* @private
* @function Highcharts.Tick#renderLabel
* @param {Highcharts.PositionObject} xy The position vector of the label
* @param {boolean} old Whether or not the tick is old
* @param {number} opacity The opacity of the label
* @param {number} index The index of the tick
*/
Tick.prototype.renderLabel = function (xy, old, opacity, index) {
var tick = this,
axis = tick.axis,
horiz = axis.horiz,
options = axis.options,
label = tick.label,
labelOptions = options.labels,
step = labelOptions.step,
tickmarkOffset = Tick_pick(tick.tickmarkOffset,
axis.tickmarkOffset),
x = xy.x,
y = xy.y;
var show = true;
if (label && Tick_isNumber(x)) {
label.xy = xy = tick.getLabelPosition(x, y, label, horiz, labelOptions, tickmarkOffset, index, step);
// Apply show first and show last. If the tick is both first and
// last, it is a single centered tick, in which case we show the
// label anyway (#2100).
if ((tick.isFirst &&
!tick.isLast &&
!options.showFirstLabel) ||
(tick.isLast &&
!tick.isFirst &&
!options.showLastLabel)) {
show = false;
// Handle label overflow and show or hide accordingly
}
else if (horiz &&
!labelOptions.step &&
!labelOptions.rotation &&
!old &&
opacity !== 0) {
tick.handleOverflow(xy);
}
// Apply step
if (step && index % step) {
// Show those indices dividable by step
show = false;
}
// Set the new position, and show or hide
if (show && Tick_isNumber(xy.y)) {
xy.opacity = opacity;
label[tick.isNewLabel ? 'attr' : 'animate'](xy).show(true);
tick.isNewLabel = false;
}
else {
label.hide(); // #1338, #15863
tick.isNewLabel = true;
}
}
};
/**
* Replace labels with the moved ones to perform animation. Additionally
* destroy unused labels.
*
* @private
* @function Highcharts.Tick#replaceMovedLabel
*/
Tick.prototype.replaceMovedLabel = function () {
var tick = this,
label = tick.label,
axis = tick.axis;
// Animate and destroy
if (label && !tick.isNew) {
label.animate({ opacity: 0 }, void 0, label.destroy);
delete tick.label;
}
axis.isDirty = true;
tick.label = tick.movedLabel;
delete tick.movedLabel;
};
return Tick;
}());
/* *
*
* Default Export
*
* */
/* harmony default export */ var Axis_Tick = (Tick);
/* *
*
* API Declarations
*
* */
/**
* Optional parameters for the tick.
* @private
* @interface Highcharts.TickParametersObject
*/ /**
* Set category for the tick.
* @name Highcharts.TickParametersObject#category
* @type {string|undefined}
*/ /**
* @name Highcharts.TickParametersObject#options
* @type {Highcharts.Dictionary<any>|undefined}
*/ /**
* Set tickmarkOffset for the tick.
* @name Highcharts.TickParametersObject#tickmarkOffset
* @type {number|undefined}
*/
/**
* Additional time tick information.
*
* @interface Highcharts.TimeTicksInfoObject
* @extends Highcharts.TimeNormalizedObject
*/ /**
* @name Highcharts.TimeTicksInfoObject#higherRanks
* @type {Array<string>}
*/ /**
* @name Highcharts.TimeTicksInfoObject#totalRange
* @type {number}
*/
(''); // Keeps doclets above in JS file
;// ./code/es5/es-modules/Core/Axis/Axis.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var Axis_animObject = AnimationUtilities.animObject;
var xAxis = Axis_AxisDefaults.xAxis, yAxis = Axis_AxisDefaults.yAxis;
var Axis_defaultOptions = Defaults.defaultOptions;
var registerEventOptions = Core_Foundation.registerEventOptions;
var Axis_deg2rad = Core_Globals.deg2rad;
var Axis_arrayMax = Core_Utilities.arrayMax, Axis_arrayMin = Core_Utilities.arrayMin, Axis_clamp = Core_Utilities.clamp, Axis_correctFloat = Core_Utilities.correctFloat, Axis_defined = Core_Utilities.defined, Axis_destroyObjectProperties = Core_Utilities.destroyObjectProperties, Axis_erase = Core_Utilities.erase, Axis_error = Core_Utilities.error, Axis_extend = Core_Utilities.extend, Axis_fireEvent = Core_Utilities.fireEvent, Axis_getClosestDistance = Core_Utilities.getClosestDistance, Axis_insertItem = Core_Utilities.insertItem, Axis_isArray = Core_Utilities.isArray, Axis_isNumber = Core_Utilities.isNumber, Axis_isString = Core_Utilities.isString, Axis_merge = Core_Utilities.merge, Axis_normalizeTickInterval = Core_Utilities.normalizeTickInterval, Axis_objectEach = Core_Utilities.objectEach, Axis_pick = Core_Utilities.pick, Axis_relativeLength = Core_Utilities.relativeLength, Axis_removeEvent = Core_Utilities.removeEvent, Axis_splat = Core_Utilities.splat, Axis_syncTimeout = Core_Utilities.syncTimeout;
var getNormalizedTickInterval = function (axis, tickInterval) { return Axis_normalizeTickInterval(tickInterval, void 0, void 0, Axis_pick(axis.options.allowDecimals,
// If the tick interval is greater than 0.5, avoid decimals, as
// linear axes are often used to render discrete values (#3363). If
// a tick amount is set, allow decimals by default, as it increases
// the chances for a good fit.
tickInterval < 0.5 || axis.tickAmount !== void 0), !!axis.tickAmount); };
Axis_extend(Axis_defaultOptions, { xAxis: xAxis, yAxis: Axis_merge(xAxis, yAxis) });
/* *
*
* Class
*
* */
/**
* Create a new axis object. Called internally when instantiating a new chart or
* adding axes by {@link Highcharts.Chart#addAxis}.
*
* A chart can have from 0 axes (pie chart) to multiples. In a normal, single
* series cartesian chart, there is one X axis and one Y axis.
*
* The X axis or axes are referenced by {@link Highcharts.Chart.xAxis}, which is
* an array of Axis objects. If there is only one axis, it can be referenced
* through `chart.xAxis[0]`, and multiple axes have increasing indices. The same
* pattern goes for Y axes.
*
* If you need to get the axes from a series object, use the `series.xAxis` and
* `series.yAxis` properties. These are not arrays, as one series can only be
* associated to one X and one Y axis.
*
* A third way to reference the axis programmatically is by `id`. Add an `id` in
* the axis configuration options, and get the axis by
* {@link Highcharts.Chart#get}.
*
* Configuration options for the axes are given in options.xAxis and
* options.yAxis.
*
* @class
* @name Highcharts.Axis
*
* @param {Highcharts.Chart} chart
* The Chart instance to apply the axis on.
*
* @param {Highcharts.AxisOptions} userOptions
* Axis options
*/
var Axis = /** @class */ (function () {
/* *
*
* Constructors
*
* */
function Axis(chart, userOptions, coll) {
this.init(chart, userOptions, coll);
}
/* *
*
* Functions
*
* */
/**
* Overrideable function to initialize the axis.
*
* @see {@link Axis}
*
* @function Highcharts.Axis#init
*
* @param {Highcharts.Chart} chart
* The Chart instance to apply the axis on.
*
* @param {AxisOptions} userOptions
* Axis options.
*
* @emits Highcharts.Axis#event:afterInit
* @emits Highcharts.Axis#event:init
*/
Axis.prototype.init = function (chart, userOptions, coll) {
var _a,
_b,
_c,
_d;
if (coll === void 0) { coll = this.coll; }
var isXAxis = coll === 'xAxis',
axis = this,
horiz = axis.isZAxis || (chart.inverted ? !isXAxis : isXAxis);
/**
* The Chart that the axis belongs to.
*
* @name Highcharts.Axis#chart
* @type {Highcharts.Chart}
*/
axis.chart = chart;
/**
* Whether the axis is horizontal.
*
* @name Highcharts.Axis#horiz
* @type {boolean|undefined}
*/
axis.horiz = horiz;
/**
* Whether the axis is the x-axis.
*
* @name Highcharts.Axis#isXAxis
* @type {boolean|undefined}
*/
axis.isXAxis = isXAxis;
/**
* The collection where the axis belongs, for example `xAxis`, `yAxis`
* or `colorAxis`. Corresponds to properties on Chart, for example
* {@link Chart.xAxis}.
*
* @name Highcharts.Axis#coll
* @type {string}
*/
axis.coll = coll;
Axis_fireEvent(this, 'init', { userOptions: userOptions });
// Needed in setOptions
axis.opposite = Axis_pick(userOptions.opposite, axis.opposite);
/**
* The side on which the axis is rendered. 0 is top, 1 is right, 2
* is bottom and 3 is left.
*
* @name Highcharts.Axis#side
* @type {number}
*/
axis.side = Axis_pick(userOptions.side, axis.side, (horiz ?
(axis.opposite ? 0 : 2) : // Top : bottom
(axis.opposite ? 1 : 3)) // Right : left
);
/**
* Current options for the axis after merge of defaults and user's
* options.
*
* @name Highcharts.Axis#options
* @type {Highcharts.AxisOptions}
*/
axis.setOptions(userOptions);
var options = axis.options,
labelsOptions = options.labels;
// Set the type and fire an event
(_a = axis.type) !== null && _a !== void 0 ? _a : (axis.type = options.type || 'linear');
(_b = axis.uniqueNames) !== null && _b !== void 0 ? _b : (axis.uniqueNames = (_c = options.uniqueNames) !== null && _c !== void 0 ? _c : true);
Axis_fireEvent(axis, 'afterSetType');
/**
* User's options for this axis without defaults.
*
* @name Highcharts.Axis#userOptions
* @type {Highcharts.AxisOptions}
*/
axis.userOptions = userOptions;
axis.minPixelPadding = 0;
/**
* Whether the axis is reversed. Based on the `axis.reversed`,
* option, but inverted charts have reversed xAxis by default.
*
* @name Highcharts.Axis#reversed
* @type {boolean}
*/
axis.reversed = Axis_pick(options.reversed, axis.reversed);
axis.visible = options.visible;
axis.zoomEnabled = options.zoomEnabled;
// Initial categories
axis.hasNames = this.type === 'category' || options.categories === true;
/**
* If categories are present for the axis, names are used instead of
* numbers for that axis.
*
* Since Highcharts 3.0, categories can also be extracted by giving each
* point a name and setting axis type to `category`. However, if you
* have multiple series, best practice remains defining the `categories`
* array.
*
* @see [xAxis.categories](/highcharts/xAxis.categories)
*
* @name Highcharts.Axis#categories
* @type {Array<string>}
* @readonly
*/
axis.categories = (Axis_isArray(options.categories) && options.categories) ||
(axis.hasNames ? [] : void 0);
if (!axis.names) { // Preserve on update (#3830)
axis.names = [];
axis.names.keys = {};
}
// Placeholder for plotlines and plotbands groups
axis.plotLinesAndBandsGroups = {};
// Shorthand types
axis.positiveValuesOnly = !!axis.logarithmic;
// Flag, if axis is linked to another axis
axis.isLinked = Axis_defined(options.linkedTo);
/**
* List of major ticks mapped by position on axis.
*
* @see {@link Highcharts.Tick}
*
* @name Highcharts.Axis#ticks
* @type {Highcharts.Dictionary<Highcharts.Tick>}
*/
axis.ticks = {};
axis.labelEdge = [];
/**
* List of minor ticks mapped by position on the axis.
*
* @see {@link Highcharts.Tick}
*
* @name Highcharts.Axis#minorTicks
* @type {Highcharts.Dictionary<Highcharts.Tick>}
*/
axis.minorTicks = {};
// List of plotLines/Bands
axis.plotLinesAndBands = [];
// Alternate bands
axis.alternateBands = {};
/**
* The length of the axis in terms of pixels.
*
* @name Highcharts.Axis#len
* @type {number}
*/
(_d = axis.len) !== null && _d !== void 0 ? _d : (axis.len = 0);
axis.minRange = axis.userMinRange = options.minRange || options.maxZoom;
axis.range = options.range;
axis.offset = options.offset || 0;
/**
* The maximum value of the axis. In a logarithmic axis, this is the
* logarithm of the real value, and the real value can be obtained from
* {@link Axis#getExtremes}.
*
* @name Highcharts.Axis#max
* @type {number|undefined}
*/
axis.max = void 0;
/**
* The minimum value of the axis. In a logarithmic axis, this is the
* logarithm of the real value, and the real value can be obtained from
* {@link Axis#getExtremes}.
*
* @name Highcharts.Axis#min
* @type {number|undefined}
*/
axis.min = void 0;
/**
* The processed crosshair options.
*
* @name Highcharts.Axis#crosshair
* @type {boolean|Highcharts.AxisCrosshairOptions}
*/
var crosshair = Axis_pick(options.crosshair,
Axis_splat(chart.options.tooltip.crosshairs)[isXAxis ? 0 : 1]);
axis.crosshair = crosshair === true ? {} : crosshair;
// Register. Don't add it again on Axis.update().
if (chart.axes.indexOf(axis) === -1) { //
if (isXAxis) { // #2713
chart.axes.splice(chart.xAxis.length, 0, axis);
}
else {
chart.axes.push(axis);
}
Axis_insertItem(this, chart[this.coll]);
}
chart.orderItems(axis.coll);
/**
* All series associated to the axis.
*
* @name Highcharts.Axis#series
* @type {Array<Highcharts.Series>}
*/
axis.series = axis.series || []; // Populated by Series
// Reversed axis
if (chart.inverted &&
!axis.isZAxis &&
isXAxis &&
!Axis_defined(axis.reversed)) {
axis.reversed = true;
}
axis.labelRotation = Axis_isNumber(labelsOptions.rotation) ?
labelsOptions.rotation :
void 0;
// Register event listeners
registerEventOptions(axis, options);
Axis_fireEvent(this, 'afterInit');
};
/**
* Merge and set options.
*
* @private
* @function Highcharts.Axis#setOptions
*
* @param {Highcharts.AxisOptions} userOptions
* Axis options.
*
* @emits Highcharts.Axis#event:afterSetOptions
*/
Axis.prototype.setOptions = function (userOptions) {
var sideSpecific = this.horiz ?
// Top and bottom axis defaults
{
labels: {
autoRotation: [-45],
padding: 3
},
margin: 15
} :
// Left and right axis, title rotated 90 or 270 degrees
// respectively
{
labels: {
padding: 1
},
title: {
rotation: 90 * this.side
}
};
this.options = Axis_merge(sideSpecific, Axis_defaultOptions[this.coll], userOptions);
Axis_fireEvent(this, 'afterSetOptions', { userOptions: userOptions });
};
/**
* The default label formatter. The context is a special config object for
* the label. In apps, use the
* [labels.formatter](https://api.highcharts.com/highcharts/xAxis.labels.formatter)
* instead, except when a modification is needed.
*
* @function Highcharts.Axis#defaultLabelFormatter
*
* @param {Highcharts.AxisLabelsFormatterContextObject} this
* Formatter context of axis label.
*
* @param {Highcharts.AxisLabelsFormatterContextObject} [ctx]
* Formatter context of axis label.
*
* @return {string}
* The formatted label content.
*/
Axis.prototype.defaultLabelFormatter = function () {
var axis = this.axis,
chart = this.chart,
numberFormatter = chart.numberFormatter,
value = Axis_isNumber(this.value) ? this.value : NaN,
time = axis.chart.time,
categories = axis.categories,
dateTimeLabelFormat = this.dateTimeLabelFormat,
lang = Axis_defaultOptions.lang,
numericSymbols = lang.numericSymbols,
numSymMagnitude = lang.numericSymbolMagnitude || 1000,
// Make sure the same symbol is added for all labels on a linear
// axis
numericSymbolDetector = axis.logarithmic ?
Math.abs(value) :
axis.tickInterval;
var i = numericSymbols && numericSymbols.length,
multi,
ret;
if (categories) {
ret = "".concat(this.value);
}
else if (dateTimeLabelFormat) { // Datetime axis
ret = time.dateFormat(dateTimeLabelFormat, value, true);
}
else if (i &&
numericSymbols &&
numericSymbolDetector >= 1000) {
// Decide whether we should add a numeric symbol like k (thousands)
// or M (millions). If we are to enable this in tooltip or other
// places as well, we can move this logic to the numberFormatter and
// enable it by a parameter.
while (i-- && typeof ret === 'undefined') {
multi = Math.pow(numSymMagnitude, i + 1);
if (
// Only accept a numeric symbol when the distance is more
// than a full unit. So for example if the symbol is k, we
// don't accept numbers like 0.5k.
numericSymbolDetector >= multi &&
// Accept one decimal before the symbol. Accepts 0.5k but
// not 0.25k. How does this work with the previous?
(value * 10) % multi === 0 &&
numericSymbols[i] !== null &&
value !== 0) { // #5480
ret = numberFormatter(value / multi, -1) + numericSymbols[i];
}
}
}
if (typeof ret === 'undefined') {
if (Math.abs(value) >= 10000) { // Add thousands separators
ret = numberFormatter(value, -1);
}
else { // Small numbers
ret = numberFormatter(value, -1, void 0, ''); // #2466
}
}
return ret;
};
/**
* Get the minimum and maximum for the series of each axis. The function
* analyzes the axis series and updates `this.dataMin` and `this.dataMax`.
*
* @private
* @function Highcharts.Axis#getSeriesExtremes
*
* @emits Highcharts.Axis#event:afterGetSeriesExtremes
* @emits Highcharts.Axis#event:getSeriesExtremes
*/
Axis.prototype.getSeriesExtremes = function () {
var axis = this;
var xExtremes;
Axis_fireEvent(this, 'getSeriesExtremes', null, function () {
axis.hasVisibleSeries = false;
// Reset properties in case we're redrawing (#3353)
axis.dataMin = axis.dataMax = axis.threshold = void 0;
axis.softThreshold = !axis.isXAxis;
// Loop through this axis' series
axis.series.forEach(function (series) {
if (series.reserveSpace()) {
var seriesOptions = series.options;
var xData = void 0,
threshold = seriesOptions.threshold,
seriesDataMin = void 0,
seriesDataMax = void 0;
axis.hasVisibleSeries = true;
// Validate threshold in logarithmic axes
if (axis.positiveValuesOnly && (threshold || 0) <= 0) {
threshold = void 0;
}
// Get dataMin and dataMax for X axes
if (axis.isXAxis) {
xData = series.getColumn('x');
if (xData.length) {
xData = axis.logarithmic ?
xData.filter(function (x) { return x > 0; }) :
xData;
xExtremes = series.getXExtremes(xData);
// If xData contains values which is not numbers,
// then filter them out. To prevent performance hit,
// we only do this after we have already found
// seriesDataMin because in most cases all data is
// valid. #5234.
seriesDataMin = xExtremes.min;
seriesDataMax = xExtremes.max;
if (!Axis_isNumber(seriesDataMin) &&
// #5010:
!(seriesDataMin instanceof Date)) {
xData = xData.filter(Axis_isNumber);
xExtremes = series.getXExtremes(xData);
// Do it again with valid data
seriesDataMin = xExtremes.min;
seriesDataMax = xExtremes.max;
}
if (xData.length) {
axis.dataMin = Math.min(Axis_pick(axis.dataMin, seriesDataMin), seriesDataMin);
axis.dataMax = Math.max(Axis_pick(axis.dataMax, seriesDataMax), seriesDataMax);
}
}
// Get dataMin and dataMax for Y axes, as well as handle
// stacking and processed data
}
else {
// Get this particular series extremes
var dataExtremes = series.applyExtremes();
// Get the dataMin and dataMax so far. If percentage is
// used, the min and max are always 0 and 100. If
// seriesDataMin and seriesDataMax is null, then series
// doesn't have active y data, we continue with nulls
if (Axis_isNumber(dataExtremes.dataMin)) {
seriesDataMin = dataExtremes.dataMin;
axis.dataMin = Math.min(Axis_pick(axis.dataMin, seriesDataMin), seriesDataMin);
}
if (Axis_isNumber(dataExtremes.dataMax)) {
seriesDataMax = dataExtremes.dataMax;
axis.dataMax = Math.max(Axis_pick(axis.dataMax, seriesDataMax), seriesDataMax);
}
// Adjust to threshold
if (Axis_defined(threshold)) {
axis.threshold = threshold;
}
// If any series has a hard threshold, it takes
// precedence
if (!seriesOptions.softThreshold ||
axis.positiveValuesOnly) {
axis.softThreshold = false;
}
}
}
});
});
Axis_fireEvent(this, 'afterGetSeriesExtremes');
};
/**
* Translate from axis value to pixel position on the chart, or back. Use
* the `toPixels` and `toValue` functions in applications.
*
* @private
* @function Highcharts.Axis#translate
*/
Axis.prototype.translate = function (val, backwards, cvsCoord, old, handleLog, pointPlacement) {
var _a;
var axis = (this.linkedParent || this), // #1417
localMin = (old && axis.old ? axis.old.min : axis.min);
if (!Axis_isNumber(localMin)) {
return NaN;
}
var minPixelPadding = axis.minPixelPadding,
doPostTranslate = (axis.isOrdinal ||
((_a = axis.brokenAxis) === null || _a === void 0 ? void 0 : _a.hasBreaks) ||
(axis.logarithmic && handleLog)) && axis.lin2val;
var sign = 1,
cvsOffset = 0,
localA = old && axis.old ? axis.old.transA : axis.transA,
returnValue = 0;
if (!localA) {
localA = axis.transA;
}
// In vertical axes, the canvas coordinates start from 0 at the top like
// in SVG.
if (cvsCoord) {
sign *= -1; // Canvas coordinates inverts the value
cvsOffset = axis.len;
}
// Handle reversed axis
if (axis.reversed) {
sign *= -1;
cvsOffset -= sign * (axis.sector || axis.len);
}
// From pixels to value
if (backwards) { // Reverse translation
val = val * sign + cvsOffset;
val -= minPixelPadding;
// From chart pixel to value:
returnValue = val / localA + localMin;
if (doPostTranslate) { // Log, ordinal and broken axis
returnValue = axis.lin2val(returnValue);
}
// From value to pixels
}
else {
if (doPostTranslate) { // Log, ordinal and broken axis
val = axis.val2lin(val);
}
var value = sign * (val - localMin) * localA;
returnValue = value +
cvsOffset +
(sign * minPixelPadding) +
(Axis_isNumber(pointPlacement) ? localA * pointPlacement : 0);
if (!axis.isRadial) {
returnValue = Axis_correctFloat(returnValue);
}
}
return returnValue;
};
/**
* Translate a value in terms of axis units into pixels within the chart.
*
* @function Highcharts.Axis#toPixels
*
* @param {number|string} value
* A value in terms of axis units. For datetime axes, a timestamp or
* date/time string is accepted.
*
* @param {boolean} [paneCoordinates=false]
* Whether to return the pixel coordinate relative to the chart or just the
* axis/pane itself.
*
* @return {number}
* Pixel position of the value on the chart or axis.
*/
Axis.prototype.toPixels = function (value, paneCoordinates) {
var _a,
_b;
return this.translate((_b = (_a = this.chart) === null || _a === void 0 ? void 0 : _a.time.parse(value)) !== null && _b !== void 0 ? _b : NaN, false, !this.horiz, void 0, true) + (paneCoordinates ? 0 : this.pos);
};
/**
* Translate a pixel position along the axis to a value in terms of axis
* units.
*
* @function Highcharts.Axis#toValue
*
* @param {number} pixel
* The pixel value coordinate.
*
* @param {boolean} [paneCoordinates=false]
* Whether the input pixel is relative to the chart or just the axis/pane
* itself.
*
* @return {number}
* The axis value.
*/
Axis.prototype.toValue = function (pixel, paneCoordinates) {
return this.translate(pixel - (paneCoordinates ? 0 : this.pos), true, !this.horiz, void 0, true);
};
/**
* Create the path for a plot line that goes from the given value on
* this axis, across the plot to the opposite side. Also used internally for
* grid lines and crosshairs.
*
* @function Highcharts.Axis#getPlotLinePath
*
* @param {Highcharts.AxisPlotLinePathOptionsObject} options
* Options for the path.
*
* @return {Highcharts.SVGPathArray|null}
* The SVG path definition for the plot line.
*/
Axis.prototype.getPlotLinePath = function (options) {
var axis = this,
chart = axis.chart,
axisLeft = axis.left,
axisTop = axis.top,
old = options.old,
value = options.value,
lineWidth = options.lineWidth,
cHeight = (old && chart.oldChartHeight) || chart.chartHeight,
cWidth = (old && chart.oldChartWidth) || chart.chartWidth,
transB = axis.transB;
var translatedValue = options.translatedValue,
force = options.force,
x1,
y1,
x2,
y2,
skip;
// eslint-disable-next-line valid-jsdoc
/**
* Check if x is between a and b. If not, either move to a/b
* or skip, depending on the force parameter.
* @private
*/
function between(x, a, b) {
if (force !== 'pass' && (x < a || x > b)) {
if (force) {
x = Axis_clamp(x, a, b);
}
else {
skip = true;
}
}
return x;
}
var evt = {
value: value,
lineWidth: lineWidth,
old: old,
force: force,
acrossPanes: options.acrossPanes,
translatedValue: translatedValue
};
Axis_fireEvent(this, 'getPlotLinePath', evt, function (e) {
translatedValue = Axis_pick(translatedValue, axis.translate(value, void 0, void 0, old));
// Keep the translated value within sane bounds, and avoid Infinity
// to fail the isNumber test (#7709).
translatedValue = Axis_clamp(translatedValue, -1e9, 1e9);
x1 = x2 = translatedValue + transB;
y1 = y2 = cHeight - translatedValue - transB;
if (!Axis_isNumber(translatedValue)) { // No min or max
skip = true;
force = false; // #7175, don't force it when path is invalid
}
else if (axis.horiz) {
y1 = axisTop;
y2 = cHeight - axis.bottom + (axis.options.isInternal ?
0 :
(chart.scrollablePixelsY || 0)); // #20354, scrollablePixelsY shouldn't be used for navigator
x1 = x2 = between(x1, axisLeft, axisLeft + axis.width);
}
else {
x1 = axisLeft;
x2 = cWidth - axis.right + (chart.scrollablePixelsX || 0);
y1 = y2 = between(y1, axisTop, axisTop + axis.height);
}
e.path = skip && !force ?
void 0 :
chart.renderer.crispLine([['M', x1, y1], ['L', x2, y2]], lineWidth || 1);
});
return evt.path;
};
/**
* Internal function to get the tick positions of a linear axis to round
* values like whole tens or every five.
*
* @function Highcharts.Axis#getLinearTickPositions
*
* @param {number} tickInterval
* The normalized tick interval.
*
* @param {number} min
* Axis minimum.
*
* @param {number} max
* Axis maximum.
*
* @return {Array<number>}
* An array of axis values where ticks should be placed.
*/
Axis.prototype.getLinearTickPositions = function (tickInterval, min, max) {
var roundedMin = Axis_correctFloat(Math.floor(min / tickInterval) * tickInterval), roundedMax = Axis_correctFloat(Math.ceil(max / tickInterval) * tickInterval), tickPositions = [];
var pos,
lastPos,
precision;
// When the precision is higher than what we filter out in
// correctFloat, skip it (#6183).
if (Axis_correctFloat(roundedMin + tickInterval) === roundedMin) {
precision = 20;
}
// For single points, add a tick regardless of the relative position
// (#2662, #6274)
if (this.single) {
return [min];
}
// Populate the intermediate values
pos = roundedMin;
while (pos <= roundedMax) {
// Place the tick on the rounded value
tickPositions.push(pos);
// Always add the raw tickInterval, not the corrected one.
pos = Axis_correctFloat(pos + tickInterval, precision);
// If the interval is not big enough in the current min - max range
// to actually increase the loop variable, we need to break out to
// prevent endless loop. Issue #619
if (pos === lastPos) {
break;
}
// Record the last value
lastPos = pos;
}
return tickPositions;
};
/**
* Resolve the new minorTicks/minorTickInterval options into the legacy
* loosely typed minorTickInterval option.
*
* @function Highcharts.Axis#getMinorTickInterval
*
* @return {number|"auto"|null}
* Legacy option
*/
Axis.prototype.getMinorTickInterval = function () {
var _a = this.options,
minorTicks = _a.minorTicks,
minorTickInterval = _a.minorTickInterval;
if (minorTicks === true) {
return Axis_pick(minorTickInterval, 'auto');
}
if (minorTicks === false) {
return;
}
return minorTickInterval;
};
/**
* Internal function to return the minor tick positions. For logarithmic
* axes, the same logic as for major ticks is reused.
*
* @function Highcharts.Axis#getMinorTickPositions
*
* @return {Array<number>}
* An array of axis values where ticks should be placed.
*/
Axis.prototype.getMinorTickPositions = function () {
var _a;
var axis = this,
options = axis.options,
tickPositions = axis.tickPositions,
minorTickInterval = axis.minorTickInterval,
pointRangePadding = axis.pointRangePadding || 0,
min = (axis.min || 0) - pointRangePadding, // #1498
max = (axis.max || 0) + pointRangePadding, // #1498
range = ((_a = axis.brokenAxis) === null || _a === void 0 ? void 0 : _a.hasBreaks) ?
axis.brokenAxis.unitLength : max - min;
var minorTickPositions = [],
pos;
// If minor ticks get too dense, they are hard to read, and may cause
// long running script. So we don't draw them.
if (range && range / minorTickInterval < axis.len / 3) { // #3875
var logarithmic_1 = axis.logarithmic;
if (logarithmic_1) {
// For each interval in the major ticks, compute the minor ticks
// separately.
this.paddedTicks.forEach(function (_pos, i, paddedTicks) {
if (i) {
minorTickPositions.push.apply(minorTickPositions, logarithmic_1.getLogTickPositions(minorTickInterval, paddedTicks[i - 1], paddedTicks[i], true));
}
});
}
else if (axis.dateTime &&
this.getMinorTickInterval() === 'auto') { // #1314
minorTickPositions = minorTickPositions.concat(axis.getTimeTicks(axis.dateTime.normalizeTimeTickInterval(minorTickInterval), min, max, options.startOfWeek));
}
else {
for (pos = min + (tickPositions[0] - min) % minorTickInterval; pos <= max; pos += minorTickInterval) {
// Very, very, tight grid lines (#5771)
if (pos === minorTickPositions[0]) {
break;
}
minorTickPositions.push(pos);
}
}
}
if (minorTickPositions.length !== 0) {
axis.trimTicks(minorTickPositions); // #3652 #3743 #1498 #6330
}
return minorTickPositions;
};
/**
* Adjust the min and max for the minimum range. Keep in mind that the
* series data is not yet processed, so we don't have information on data
* cropping and grouping, or updated `axis.pointRange` or
* `series.pointRange`. The data can't be processed until we have finally
* established min and max.
*
* @private
* @function Highcharts.Axis#adjustForMinRange
*/
Axis.prototype.adjustForMinRange = function () {
var _a,
_b,
_c;
var axis = this,
options = axis.options,
logarithmic = axis.logarithmic,
time = axis.chart.time;
var max = axis.max,
min = axis.min,
minRange = axis.minRange,
zoomOffset,
spaceAvailable,
closestDataRange,
minArgs,
maxArgs;
// Set the automatic minimum range based on the closest point distance
if (axis.isXAxis &&
typeof minRange === 'undefined' &&
!logarithmic) {
if (Axis_defined(options.min) ||
Axis_defined(options.max) ||
Axis_defined(options.floor) ||
Axis_defined(options.ceiling)) {
// Setting it to null, as opposed to undefined, signals we don't
// run this block again as per the condition above.
minRange = null;
}
else {
// Find the closest distance between raw data points, as opposed
// to closestPointRange that applies to processed points
// (cropped and grouped)
closestDataRange = Axis_getClosestDistance(axis.series.map(function (s) {
var xData = s.getColumn('x');
// If xIncrement, we only need to measure the two first
// points to get the distance. Saves processing time.
return s.xIncrement ? xData.slice(0, 2) : xData;
})) || 0;
minRange = Math.min(closestDataRange * 5, axis.dataMax - axis.dataMin);
}
}
// If minRange is exceeded, adjust
if (Axis_isNumber(max) &&
Axis_isNumber(min) &&
Axis_isNumber(minRange) &&
max - min < minRange) {
spaceAvailable =
axis.dataMax - axis.dataMin >=
minRange;
zoomOffset = (minRange - max + min) / 2;
// If min and max options have been set, don't go beyond it
minArgs = [
min - zoomOffset,
(_a = time.parse(options.min)) !== null && _a !== void 0 ? _a : (min - zoomOffset)
];
// If space is available, stay within the data range
if (spaceAvailable) {
minArgs[2] = logarithmic ?
logarithmic.log2lin(axis.dataMin) :
axis.dataMin;
}
min = Axis_arrayMax(minArgs);
maxArgs = [
min + minRange,
(_b = time.parse(options.max)) !== null && _b !== void 0 ? _b : (min + minRange)
];
// If space is available, stay within the data range
if (spaceAvailable) {
maxArgs[2] = logarithmic ?
logarithmic.log2lin(axis.dataMax) :
axis.dataMax;
}
max = Axis_arrayMin(maxArgs);
// Now if the max is adjusted, adjust the min back
if (max - min < minRange) {
minArgs[0] = max - minRange;
minArgs[1] = (_c = time.parse(options.min)) !== null && _c !== void 0 ? _c : (max - minRange);
min = Axis_arrayMax(minArgs);
}
}
// Record modified extremes
axis.minRange = minRange;
axis.min = min;
axis.max = max;
};
/**
* Find the closestPointRange across all series, including the single data
* series.
*
* @private
* @function Highcharts.Axis#getClosest
*/
Axis.prototype.getClosest = function () {
var closestSingleDistance,
closestDistance;
if (this.categories) {
closestDistance = 1;
}
else {
var singleXs_1 = [];
this.series.forEach(function (series) {
var seriesClosest = series.closestPointRange,
xData = series.getColumn('x');
if (xData.length === 1) {
singleXs_1.push(xData[0]);
}
else if (series.sorted &&
Axis_defined(seriesClosest) &&
series.reserveSpace()) {
closestDistance = Axis_defined(closestDistance) ?
Math.min(closestDistance, seriesClosest) :
seriesClosest;
}
});
if (singleXs_1.length) {
singleXs_1.sort(function (a, b) { return a - b; });
closestSingleDistance = Axis_getClosestDistance([singleXs_1]);
}
}
if (closestSingleDistance && closestDistance) {
return Math.min(closestSingleDistance, closestDistance);
}
return closestSingleDistance || closestDistance;
};
/**
* When a point name is given and no x, search for the name in the existing
* categories, or if categories aren't provided, search names or create a
* new category (#2522).
*
* @private
* @function Highcharts.Axis#nameToX
*
* @param {Highcharts.Point} point
* The point to inspect.
*
* @return {number}
* The X value that the point is given.
*/
Axis.prototype.nameToX = function (point) {
var explicitCategories = Axis_isArray(this.options.categories),
names = explicitCategories ? this.categories : this.names;
var nameX = point.options.x,
x;
point.series.requireSorting = false;
if (!Axis_defined(nameX)) {
nameX = this.uniqueNames && names ?
(explicitCategories ?
names.indexOf(point.name) :
Axis_pick(names.keys[point.name], -1)) :
point.series.autoIncrement();
}
if (nameX === -1) { // Not found in current categories
if (!explicitCategories && names) {
x = names.length;
}
}
else if (Axis_isNumber(nameX)) {
x = nameX;
}
// Write the last point's name to the names array
if (typeof x !== 'undefined') {
this.names[x] = point.name;
// Backwards mapping is much faster than array searching (#7725)
this.names.keys[point.name] = x;
}
else if (point.x) {
x = point.x; // #17438
}
return x;
};
/**
* When changes have been done to series data, update the axis.names.
*
* @private
* @function Highcharts.Axis#updateNames
*/
Axis.prototype.updateNames = function () {
var axis = this,
names = this.names,
i = names.length;
if (i > 0) {
Object.keys(names.keys).forEach(function (key) {
delete (names.keys)[key];
});
names.length = 0;
this.minRange = this.userMinRange; // Reset
(this.series || []).forEach(function (series) {
// Reset incrementer (#5928)
series.xIncrement = null;
// When adding a series, points are not yet generated
if (!series.points || series.isDirtyData) {
// When we're updating the series with data that is longer
// than it was, and cropThreshold is passed, we need to make
// sure that the axis.max is increased _before_ running the
// premature processData. Otherwise this early iteration of
// processData will crop the points to axis.max, and the
// names array will be too short (#5857).
axis.max = Math.max(axis.max || 0, series.dataTable.rowCount - 1);
series.processData();
series.generatePoints();
}
var xData = series.getColumn('x').slice();
series.data.forEach(function (point, i) {
var x = xData[i];
if ((point === null || point === void 0 ? void 0 : point.options) &&
typeof point.name !== 'undefined' // #9562
) {
x = axis.nameToX(point);
if (typeof x !== 'undefined' && x !== point.x) {
xData[i] = point.x = x;
}
}
});
series.dataTable.setColumn('x', xData);
});
}
};
/**
* Update translation information.
*
* @private
* @function Highcharts.Axis#setAxisTranslation
*
* @emits Highcharts.Axis#event:afterSetAxisTranslation
*/
Axis.prototype.setAxisTranslation = function () {
var axis = this,
range = axis.max - axis.min,
linkedParent = axis.linkedParent,
hasCategories = !!axis.categories,
isXAxis = axis.isXAxis;
var pointRange = axis.axisPointRange || 0,
closestPointRange,
minPointOffset = 0,
pointRangePadding = 0,
ordinalCorrection,
transA = axis.transA;
// Adjust translation for padding. Y axis with categories need to go
// through the same (#1784).
if (isXAxis || hasCategories || pointRange) {
// Get the closest points
closestPointRange = axis.getClosest();
if (linkedParent) {
minPointOffset = linkedParent.minPointOffset;
pointRangePadding = linkedParent.pointRangePadding;
}
else {
axis.series.forEach(function (series) {
var seriesPointRange = hasCategories ?
1 :
(isXAxis ?
Axis_pick(series.options.pointRange,
closestPointRange, 0) :
(axis.axisPointRange || 0)), // #2806
pointPlacement = series.options.pointPlacement;
pointRange = Math.max(pointRange, seriesPointRange);
if (!axis.single || hasCategories) {
// TODO: series should internally set x- and y-
// pointPlacement to simplify this logic.
var isPointPlacementAxis = series.is('xrange') ?
!isXAxis :
isXAxis;
// The `minPointOffset` is the value padding to the left
// of the axis in order to make room for points with a
// pointRange, typically columns, or line/scatter points
// on a category axis. When the `pointPlacement` option
// is 'between' or 'on', this padding does not apply.
minPointOffset = Math.max(minPointOffset, isPointPlacementAxis && Axis_isString(pointPlacement) ?
0 :
seriesPointRange / 2);
// Determine the total padding needed to the length of
// the axis to make room for the pointRange. If the
// series' pointPlacement is 'on', no padding is added.
pointRangePadding = Math.max(pointRangePadding, isPointPlacementAxis && pointPlacement === 'on' ?
0 :
seriesPointRange);
}
});
}
// Record minPointOffset and pointRangePadding
ordinalCorrection = (axis.ordinal && axis.ordinal.slope && closestPointRange) ?
axis.ordinal.slope / closestPointRange :
1; // #988, #1853
axis.minPointOffset = minPointOffset =
minPointOffset * ordinalCorrection;
axis.pointRangePadding =
pointRangePadding = pointRangePadding * ordinalCorrection;
// The `pointRange` is the width reserved for each point, like in a
// column chart
axis.pointRange = Math.min(pointRange, axis.single && hasCategories ? 1 : range);
// The `closestPointRange` is the closest distance between points.
// In columns it is mostly equal to pointRange, but in lines
// pointRange is 0 while closestPointRange is some other value
if (isXAxis && closestPointRange) {
axis.closestPointRange = closestPointRange;
}
}
// Secondary values
axis.translationSlope = axis.transA = transA =
axis.staticScale ||
axis.len / ((range + pointRangePadding) || 1);
// Translation addend
axis.transB = axis.horiz ? axis.left : axis.bottom;
axis.minPixelPadding = transA * minPointOffset;
Axis_fireEvent(this, 'afterSetAxisTranslation');
};
/**
* @private
* @function Highcharts.Axis#minFromRange
*/
Axis.prototype.minFromRange = function () {
var _a = this,
max = _a.max,
min = _a.min;
return Axis_isNumber(max) && Axis_isNumber(min) && max - min || void 0;
};
/**
* Set the tick positions to round values and optionally extend the extremes
* to the nearest tick.
*
* @private
* @function Highcharts.Axis#setTickInterval
*
* @param {boolean} secondPass
* TO-DO: parameter description
*
* @emits Highcharts.Axis#event:foundExtremes
*/
Axis.prototype.setTickInterval = function (secondPass) {
var _a,
_b,
_c,
_d;
var axis = this,
categories = axis.categories,
chart = axis.chart,
dataMax = axis.dataMax,
dataMin = axis.dataMin,
dateTime = axis.dateTime,
isXAxis = axis.isXAxis,
logarithmic = axis.logarithmic,
options = axis.options,
softThreshold = axis.softThreshold,
time = chart.time,
threshold = Axis_isNumber(axis.threshold) ? axis.threshold : void 0,
minRange = axis.minRange || 0,
ceiling = options.ceiling,
floor = options.floor,
linkedTo = options.linkedTo,
softMax = options.softMax,
softMin = options.softMin,
linkedParent = Axis_isNumber(linkedTo) && ((_a = chart[axis.coll]) === null || _a === void 0 ? void 0 : _a[linkedTo]),
tickPixelIntervalOption = options.tickPixelInterval;
var maxPadding = options.maxPadding,
minPadding = options.minPadding,
range = 0,
linkedParentExtremes,
// Only non-negative tickInterval is valid, #12961
tickIntervalOption = Axis_isNumber(options.tickInterval) && options.tickInterval >= 0 ?
options.tickInterval : void 0,
thresholdMin,
thresholdMax,
hardMin,
hardMax;
if (!dateTime && !categories && !linkedParent) {
this.getTickAmount();
}
// Min or max set either by zooming/setExtremes or initial options
hardMin = Axis_pick(axis.userMin, time.parse(options.min));
hardMax = Axis_pick(axis.userMax, time.parse(options.max));
// Linked axis gets the extremes from the parent axis
if (linkedParent) {
axis.linkedParent = linkedParent;
linkedParentExtremes = linkedParent.getExtremes();
axis.min = Axis_pick(linkedParentExtremes.min, linkedParentExtremes.dataMin);
axis.max = Axis_pick(linkedParentExtremes.max, linkedParentExtremes.dataMax);
if (this.type !== linkedParent.type) {
// Can't link axes of different type
Axis_error(11, true, chart);
}
// Initial min and max from the extreme data values
}
else {
// Adjust to hard threshold
if (softThreshold &&
Axis_defined(threshold) &&
Axis_isNumber(dataMax) &&
Axis_isNumber(dataMin)) {
if (dataMin >= threshold) {
thresholdMin = threshold;
minPadding = 0;
}
else if (dataMax <= threshold) {
thresholdMax = threshold;
maxPadding = 0;
}
}
axis.min = Axis_pick(hardMin, thresholdMin, dataMin);
axis.max = Axis_pick(hardMax, thresholdMax, dataMax);
}
if (Axis_isNumber(axis.max) && Axis_isNumber(axis.min)) {
if (logarithmic) {
if (axis.positiveValuesOnly &&
!secondPass &&
Math.min(axis.min, Axis_pick(dataMin, axis.min)) <= 0) { // #978
// Can't plot negative values on log axis
Axis_error(10, true, chart);
}
// The correctFloat cures #934, float errors on full tens. But
// it was too aggressive for #4360 because of conversion back to
// lin, therefore use precision 15.
axis.min = Axis_correctFloat(logarithmic.log2lin(axis.min), 16);
axis.max = Axis_correctFloat(logarithmic.log2lin(axis.max), 16);
}
// Handle zoomed range
if (axis.range && Axis_isNumber(dataMin)) {
// #618, #6773:
axis.userMin = axis.min = hardMin = Math.max(dataMin, axis.minFromRange() || 0);
axis.userMax = hardMax = axis.max;
axis.range = void 0; // Don't use it when running setExtremes
}
}
// Hook for Highcharts Stock Scroller and bubble axis padding
Axis_fireEvent(axis, 'foundExtremes');
// Adjust min and max for the minimum range
axis.adjustForMinRange();
if (Axis_isNumber(axis.min) && Axis_isNumber(axis.max)) {
// Handle options for floor, ceiling, softMin and softMax (#6359)
if (!Axis_isNumber(axis.userMin) &&
Axis_isNumber(softMin) &&
softMin < axis.min) {
axis.min = hardMin = softMin; // #6894
}
if (!Axis_isNumber(axis.userMax) &&
Axis_isNumber(softMax) &&
softMax > axis.max) {
axis.max = hardMax = softMax; // #6894
}
// Pad the values to get clear of the chart's edges. To avoid
// tickInterval taking the padding into account, we do this after
// computing tick interval (#1337).
if (!categories &&
!axis.axisPointRange &&
!((_b = axis.stacking) === null || _b === void 0 ? void 0 : _b.usePercentage) &&
!linkedParent) {
range = axis.max - axis.min;
if (range) {
if (!Axis_defined(hardMin) && minPadding) {
axis.min -= range * minPadding;
}
if (!Axis_defined(hardMax) && maxPadding) {
axis.max += range * maxPadding;
}
}
}
if (!Axis_isNumber(axis.userMin) && Axis_isNumber(floor)) {
axis.min = Math.max(axis.min, floor);
}
if (!Axis_isNumber(axis.userMax) && Axis_isNumber(ceiling)) {
axis.max = Math.min(axis.max, ceiling);
}
// When the threshold is soft, adjust the extreme value only if the
// data extreme and the padded extreme land on either side of the
// threshold. For example, a series of [0, 1, 2, 3] would make the
// yAxis add a tick for -1 because of the default `minPadding` and
// `startOnTick` options. This is prevented by the `softThreshold`
// option.
if (softThreshold &&
Axis_isNumber(dataMin) &&
Axis_isNumber(dataMax)) {
var numThreshold = threshold || 0;
if (!Axis_defined(hardMin) &&
axis.min < numThreshold &&
dataMin >= numThreshold) {
axis.min = options.minRange ?
Math.min(numThreshold, axis.max - minRange) :
numThreshold;
}
else if (!Axis_defined(hardMax) &&
axis.max > numThreshold &&
dataMax <= numThreshold) {
axis.max = options.minRange ?
Math.max(numThreshold, axis.min + minRange) :
numThreshold;
}
}
// If min is bigger than highest, or if max less than lowest value,
// the chart should not render points. (#14417)
if (!chart.polar && axis.min > axis.max) {
if (Axis_defined(options.min)) {
axis.max = axis.min;
}
else if (Axis_defined(options.max)) {
axis.min = axis.max;
}
}
range = axis.max - axis.min;
}
// Get tickInterval
if (axis.min === axis.max ||
!Axis_isNumber(axis.min) ||
!Axis_isNumber(axis.max)) {
axis.tickInterval = 1;
}
else if (linkedParent &&
!tickIntervalOption &&
tickPixelIntervalOption === linkedParent.options.tickPixelInterval) {
axis.tickInterval = tickIntervalOption = linkedParent.tickInterval;
}
else {
axis.tickInterval = Axis_pick(tickIntervalOption, this.tickAmount ?
range / Math.max(this.tickAmount - 1, 1) :
void 0,
// For categorized axis, 1 is default, for linear axis use
// tickPix
categories ?
1 :
// Don't let it be more than the data range
range * tickPixelIntervalOption /
Math.max(axis.len, tickPixelIntervalOption));
}
// Now we're finished detecting min and max, crop and group series data.
// This is in turn needed in order to find tick positions in ordinal
// axes.
if (isXAxis && !secondPass) {
var hasExtremesChanged_1 = axis.min !== ((_c = axis.old) === null || _c === void 0 ? void 0 : _c.min) ||
axis.max !== ((_d = axis.old) === null || _d === void 0 ? void 0 : _d.max);
// First process all series assigned to that axis.
axis.series.forEach(function (series) {
var _a;
// Allows filtering out points outside the plot area.
series.forceCrop = (_a = series.forceCropping) === null || _a === void 0 ? void 0 : _a.call(series);
series.processData(hasExtremesChanged_1);
});
// Then apply grouping if needed. The hasExtremesChanged helps to
// decide if the data grouping should be skipped in the further
// calculations #16319.
Axis_fireEvent(this, 'postProcessData', { hasExtremesChanged: hasExtremesChanged_1 });
}
// Set the translation factor used in translate function
axis.setAxisTranslation();
// Hook for ordinal axes and radial axes
Axis_fireEvent(this, 'initialAxisTranslation');
// In column-like charts, don't cramp in more ticks than there are
// points (#1943, #4184)
if (axis.pointRange && !tickIntervalOption) {
axis.tickInterval = Math.max(axis.pointRange, axis.tickInterval);
}
// Before normalizing the tick interval, handle minimum tick interval.
// This applies only if tickInterval is not defined.
var minTickInterval = Axis_pick(options.minTickInterval,
// In datetime axes, don't go below the data interval, except when
// there are scatter-like series involved (#13369).
dateTime &&
!axis.series.some(function (s) { return !s.sorted; }) ?
axis.closestPointRange : 0);
if (!tickIntervalOption && axis.tickInterval < minTickInterval) {
axis.tickInterval = minTickInterval;
}
// For linear axes, normalize the interval
if (!dateTime && !logarithmic && !tickIntervalOption) {
axis.tickInterval = getNormalizedTickInterval(axis, axis.tickInterval);
}
// Prevent ticks from getting so close that we can't draw the labels
if (!this.tickAmount) {
axis.tickInterval = axis.unsquish();
}
this.setTickPositions();
};
/**
* Now we have computed the normalized tickInterval, get the tick positions.
*
* @private
* @function Highcharts.Axis#setTickPositions
*
* @emits Highcharts.Axis#event:afterSetTickPositions
*/
Axis.prototype.setTickPositions = function () {
var _a,
_b;
var axis = this,
options = this.options,
tickPositionsOption = options.tickPositions,
tickPositioner = options.tickPositioner,
minorTickIntervalOption = this.getMinorTickInterval(),
allowEndOnTick = !this.isPanning,
startOnTick = allowEndOnTick && options.startOnTick,
endOnTick = allowEndOnTick && options.endOnTick;
var tickPositions = [],
tickPositionerResult;
// Set the tickmarkOffset
this.tickmarkOffset = (this.categories &&
options.tickmarkPlacement === 'between' &&
this.tickInterval === 1) ? 0.5 : 0; // #3202
// When there is only one point, or all points have the same value on
// this axis, then min and max are equal and tickPositions.length is 0
// or 1. In this case, add some padding in order to center the point,
// but leave it with one tick. #1337.
this.single =
this.min === this.max &&
Axis_defined(this.min) &&
!this.tickAmount &&
(
// Data is on integer (#6563)
this.min % 1 === 0 ||
// Between integers and decimals are not allowed (#6274)
options.allowDecimals !== false);
/**
* Contains the current positions that are laid out on the axis. The
* positions are numbers in terms of axis values. In a category axis
* they are integers, in a datetime axis they are also integers, but
* designating milliseconds.
*
* This property is read only - for modifying the tick positions, use
* the `tickPositioner` callback or [axis.tickPositions(
* https://api.highcharts.com/highcharts/xAxis.tickPositions) option
* instead.
*
* @name Highcharts.Axis#tickPositions
* @type {Highcharts.AxisTickPositionsArray|undefined}
*/
if (tickPositionsOption) {
// Find the tick positions. Work on a copy (#1565)
tickPositions = tickPositionsOption.slice();
}
else if (Axis_isNumber(this.min) && Axis_isNumber(this.max)) {
// Too many ticks (#6405). Create a friendly warning and provide two
// ticks so at least we can show the data series.
if (!((_a = axis.ordinal) === null || _a === void 0 ? void 0 : _a.positions) &&
((this.max - this.min) /
this.tickInterval >
Math.max(2 * this.len, 200))) {
tickPositions = [this.min, this.max];
Axis_error(19, false, this.chart);
}
else if (axis.dateTime) {
tickPositions = axis.getTimeTicks(axis.dateTime.normalizeTimeTickInterval(this.tickInterval, options.units), this.min, this.max, options.startOfWeek, (_b = axis.ordinal) === null || _b === void 0 ? void 0 : _b.positions, this.closestPointRange, true);
}
else if (axis.logarithmic) {
tickPositions = axis.logarithmic.getLogTickPositions(this.tickInterval, this.min, this.max);
}
else {
var startingTickInterval = this.tickInterval;
var adjustedTickInterval = startingTickInterval;
while (adjustedTickInterval <= startingTickInterval * 2) {
tickPositions = this.getLinearTickPositions(this.tickInterval, this.min, this.max);
// If there are more tick positions than the set tickAmount,
// increase the tickInterval and continue until it fits.
// (#17100)
if (this.tickAmount &&
tickPositions.length > this.tickAmount) {
this.tickInterval = getNormalizedTickInterval(this, adjustedTickInterval *= 1.1);
}
else {
break;
}
}
}
// Too dense ticks, keep only the first and last (#4477)
if (tickPositions.length > this.len) {
tickPositions = [
tickPositions[0],
tickPositions[tickPositions.length - 1]
];
// Reduce doubled value (#7339)
if (tickPositions[0] === tickPositions[1]) {
tickPositions.length = 1;
}
}
// Run the tick positioner callback, that allows modifying auto tick
// positions.
if (tickPositioner) {
// Make it available to the positioner
this.tickPositions = tickPositions;
tickPositionerResult = tickPositioner.apply(axis, [this.min, this.max]);
if (tickPositionerResult) {
tickPositions = tickPositionerResult;
}
}
}
this.tickPositions = tickPositions;
// Get minorTickInterval
this.minorTickInterval =
minorTickIntervalOption === 'auto' && this.tickInterval ?
this.tickInterval / options.minorTicksPerMajor :
minorTickIntervalOption;
// Reset min/max or remove extremes based on start/end on tick
this.paddedTicks = tickPositions.slice(0); // Used for logarithmic minor
this.trimTicks(tickPositions, startOnTick, endOnTick);
if (!this.isLinked && Axis_isNumber(this.min) && Axis_isNumber(this.max)) {
// Substract half a unit (#2619, #2846, #2515, #3390), but not in
// case of multiple ticks (#6897)
if (this.single &&
tickPositions.length < 2 &&
!this.categories &&
!this.series.some(function (s) {
return (s.is('heatmap') && s.options.pointPlacement === 'between');
})) {
this.min -= 0.5;
this.max += 0.5;
}
if (!tickPositionsOption && !tickPositionerResult) {
this.adjustTickAmount();
}
}
Axis_fireEvent(this, 'afterSetTickPositions');
};
/**
* Handle startOnTick and endOnTick by either adapting to padding min/max or
* rounded min/max. Also handle single data points.
*
* @private
* @function Highcharts.Axis#trimTicks
*
* @param {Array<number>} tickPositions
* TO-DO: parameter description
*
* @param {boolean} [startOnTick]
* TO-DO: parameter description
*
* @param {boolean} [endOnTick]
* TO-DO: parameter description
*/
Axis.prototype.trimTicks = function (tickPositions, startOnTick, endOnTick) {
var roundedMin = tickPositions[0],
roundedMax = tickPositions[tickPositions.length - 1],
minPointOffset = (!this.isOrdinal && this.minPointOffset) || 0; // (#12716)
Axis_fireEvent(this, 'trimTicks');
if (!this.isLinked) {
if (startOnTick && roundedMin !== -Infinity) { // #6502
this.min = roundedMin;
}
else {
while (this.min - minPointOffset > tickPositions[0]) {
tickPositions.shift();
}
}
if (endOnTick) {
this.max = roundedMax;
}
else {
while (this.max + minPointOffset <
tickPositions[tickPositions.length - 1]) {
tickPositions.pop();
}
}
// If no tick are left, set one tick in the middle (#3195)
if (tickPositions.length === 0 &&
Axis_defined(roundedMin) &&
!this.options.tickPositions) {
tickPositions.push((roundedMax + roundedMin) / 2);
}
}
};
/**
* Check if there are multiple axes in the same pane.
*
* @private
* @function Highcharts.Axis#alignToOthers
*
* @return {boolean|undefined}
* True if there are other axes.
*/
Axis.prototype.alignToOthers = function () {
var axis = this,
chart = axis.chart,
alignedAxes = [this],
options = axis.options,
chartOptions = chart.options.chart,
alignThresholds = (this.coll === 'yAxis' &&
chartOptions.alignThresholds),
thresholdAlignments = [];
var hasOther;
axis.thresholdAlignment = void 0;
if ((
// Only if alignTicks or alignThresholds is true
(chartOptions.alignTicks !== false && options.alignTicks) ||
alignThresholds) &&
// Disabled when startOnTick or endOnTick are false (#7604)
options.startOnTick !== false &&
options.endOnTick !== false &&
// Don't try to align ticks on a log axis, they are not evenly
// spaced (#6021)
!axis.logarithmic) {
// Get a key identifying which pane the axis belongs to
var getKey_1 = function (axis) {
var horiz = axis.horiz,
options = axis.options;
return [
horiz ? options.left : options.top,
options.width,
options.height,
options.pane
].join(',');
};
var thisKey_1 = getKey_1(this);
chart[this.coll].forEach(function (otherAxis) {
var series = otherAxis.series;
if (
// #4442
series.length &&
series.some(function (s) { return s.visible; }) &&
otherAxis !== axis &&
getKey_1(otherAxis) === thisKey_1) {
hasOther = true; // #4201
alignedAxes.push(otherAxis);
}
});
}
if (hasOther && alignThresholds) {
// Handle alignThresholds. The `thresholdAlignments` array keeps
// records of where each axis in the group wants its threshold, from
// 0 which is on `axis.min`, to 1 which is on `axis.max`.
alignedAxes.forEach(function (otherAxis) {
var threshAlign = otherAxis.getThresholdAlignment(axis);
if (Axis_isNumber(threshAlign)) {
thresholdAlignments.push(threshAlign);
}
});
// For each of the axes in the group, record the average
// `thresholdAlignment`.
var thresholdAlignment_1 = thresholdAlignments.length > 1 ?
thresholdAlignments.reduce(function (sum,
n) { return (sum += n); }, 0) / thresholdAlignments.length :
void 0;
alignedAxes.forEach(function (axis) {
axis.thresholdAlignment = thresholdAlignment_1;
});
}
return hasOther;
};
/**
* Where the axis wants its threshold, from 0 which is on `axis.min`, to 1 which
* is on `axis.max`.
*
* @private
* @function Highcharts.Axis#getThresholdAlignment
*/
Axis.prototype.getThresholdAlignment = function (callerAxis) {
if (!Axis_isNumber(this.dataMin) ||
(this !== callerAxis &&
this.series.some(function (s) { return (s.isDirty || s.isDirtyData); }))) {
this.getSeriesExtremes();
}
if (Axis_isNumber(this.threshold)) {
var thresholdAlignment = Axis_clamp(((this.threshold - (this.dataMin || 0)) /
((this.dataMax || 0) - (this.dataMin || 0))), 0, 1);
if (this.options.reversed) {
thresholdAlignment = 1 - thresholdAlignment;
}
return thresholdAlignment;
}
};
/**
* Find the max ticks of either the x and y axis collection, and record it
* in `this.tickAmount`.
*
* @private
* @function Highcharts.Axis#getTickAmount
*/
Axis.prototype.getTickAmount = function () {
var axis = this,
options = this.options,
tickPixelInterval = options.tickPixelInterval;
var tickAmount = options.tickAmount;
if (!Axis_defined(options.tickInterval) &&
!tickAmount &&
this.len < tickPixelInterval &&
!this.isRadial &&
!axis.logarithmic &&
options.startOnTick &&
options.endOnTick) {
tickAmount = 2;
}
if (!tickAmount && this.alignToOthers()) {
// Add 1 because 4 tick intervals require 5 ticks (including first
// and last)
tickAmount = Math.ceil(this.len / tickPixelInterval) + 1;
}
// For tick amounts of 2 and 3, compute five ticks and remove the
// intermediate ones. This prevents the axis from adding ticks that are
// too far away from the data extremes.
if (tickAmount < 4) {
this.finalTickAmt = tickAmount;
tickAmount = 5;
}
this.tickAmount = tickAmount;
};
/**
* When using multiple axes, adjust the number of ticks to match the highest
* number of ticks in that group.
*
* @private
* @function Highcharts.Axis#adjustTickAmount
*/
Axis.prototype.adjustTickAmount = function () {
var axis = this,
finalTickAmt = axis.finalTickAmt,
max = axis.max,
min = axis.min,
options = axis.options,
tickPositions = axis.tickPositions,
tickAmount = axis.tickAmount,
thresholdAlignment = axis.thresholdAlignment,
currentTickAmount = tickPositions === null || tickPositions === void 0 ? void 0 : tickPositions.length,
threshold = Axis_pick(axis.threshold,
axis.softThreshold ? 0 : null);
var len,
i,
tickInterval = axis.tickInterval,
thresholdTickIndex;
var
// Extend the tickPositions by appending a position
append = function () { return tickPositions.push(Axis_correctFloat(tickPositions[tickPositions.length - 1] +
tickInterval)); },
// Extend the tickPositions by prepending a position
prepend = function () { return tickPositions.unshift(Axis_correctFloat(tickPositions[0] - tickInterval)); };
// If `thresholdAlignment` is a number, it means the `alignThresholds`
// option is true. The `thresholdAlignment` is a scalar value between 0
// and 1 for where the threshold should be relative to `axis.min` and
// `axis.max`. Now that we know the tick amount, convert this to the
// tick index. Unless `thresholdAlignment` is exactly 0 or 1, avoid the
// first or last tick because that would lead to series being clipped.
if (Axis_isNumber(thresholdAlignment)) {
thresholdTickIndex = thresholdAlignment < 0.5 ?
Math.ceil(thresholdAlignment * (tickAmount - 1)) :
Math.floor(thresholdAlignment * (tickAmount - 1));
if (options.reversed) {
thresholdTickIndex = tickAmount - 1 - thresholdTickIndex;
}
}
if (axis.hasData() && Axis_isNumber(min) && Axis_isNumber(max)) { // #14769
// Adjust extremes and translation to the modified tick positions
var adjustExtremes = function () {
axis.transA *= (currentTickAmount - 1) / (tickAmount - 1);
// Do not crop when ticks are not extremes (#9841)
axis.min = options.startOnTick ?
tickPositions[0] :
Math.min(min, tickPositions[0]);
axis.max = options.endOnTick ?
tickPositions[tickPositions.length - 1] :
Math.max(max, tickPositions[tickPositions.length - 1]);
};
// When the axis is subject to the alignThresholds option. Use
// axis.threshold because the local threshold includes the
// `softThreshold`.
if (Axis_isNumber(thresholdTickIndex) && Axis_isNumber(axis.threshold)) {
// Throw away the previously computed tickPositions and start
// from scratch with only the threshold itself, then add ticks
// below the threshold first, then fill up above the threshold.
// If we are not able to fill up to axis.max, double the
// tickInterval and run again.
while (tickPositions[thresholdTickIndex] !== threshold ||
tickPositions.length !== tickAmount ||
tickPositions[0] > min ||
tickPositions[tickPositions.length - 1] < max) {
tickPositions.length = 0;
tickPositions.push(axis.threshold);
while (tickPositions.length < tickAmount) {
if (
// Start by prepending positions until the threshold
// is at the required index...
tickPositions[thresholdTickIndex] === void 0 ||
tickPositions[thresholdTickIndex] > axis.threshold) {
prepend();
}
else {
// ... then append positions until we have the
// required length
append();
}
}
// Safety vent
if (tickInterval > axis.tickInterval * 8) {
break;
}
tickInterval *= 2;
}
adjustExtremes();
}
else if (currentTickAmount < tickAmount) {
while (tickPositions.length < tickAmount) {
// Extend evenly for both sides unless we're on the
// threshold (#3965)
if (tickPositions.length % 2 || min === threshold) {
append();
}
else {
prepend();
}
}
adjustExtremes();
}
// The finalTickAmt property is set in getTickAmount
if (Axis_defined(finalTickAmt)) {
i = len = tickPositions.length;
while (i--) {
if (
// Remove every other tick
(finalTickAmt === 3 && i % 2 === 1) ||
// Remove all but first and last
(finalTickAmt <= 2 && i > 0 && i < len - 1)) {
tickPositions.splice(i, 1);
}
}
axis.finalTickAmt = void 0;
}
}
};
/**
* Set the scale based on data min and max, user set min and max or options.
*
* @private
* @function Highcharts.Axis#setScale
*
* @emits Highcharts.Axis#event:afterSetScale
*/
Axis.prototype.setScale = function () {
var _a,
_b;
var axis = this,
coll = axis.coll,
stacking = axis.stacking;
var isDirtyData = false,
isXAxisDirty = false;
axis.series.forEach(function (series) {
isDirtyData = isDirtyData || series.isDirtyData || series.isDirty;
// When x axis is dirty, we need new data extremes for y as
// well:
isXAxisDirty = (isXAxisDirty ||
(series.xAxis && series.xAxis.isDirty) ||
false);
});
// Set the new axisLength
axis.setAxisSize();
var isDirtyAxisLength = axis.len !== (axis.old && axis.old.len);
// Do we really need to go through all this?
if (isDirtyAxisLength ||
isDirtyData ||
isXAxisDirty ||
axis.isLinked ||
axis.forceRedraw ||
axis.userMin !== (axis.old && axis.old.userMin) ||
axis.userMax !== (axis.old && axis.old.userMax) ||
axis.alignToOthers()) {
if (stacking && coll === 'yAxis') {
stacking.buildStacks();
}
axis.forceRedraw = false;
// #18066 delete minRange property to ensure that it will be
// calculated again after dirty data in series
if (!axis.userMinRange) {
axis.minRange = void 0;
}
// Get data extremes if needed
axis.getSeriesExtremes();
// Get fixed positions based on tickInterval
axis.setTickInterval();
if (stacking && coll === 'xAxis') {
stacking.buildStacks();
}
// Mark as dirty if it is not already set to dirty and extremes have
// changed. #595.
if (!axis.isDirty) {
axis.isDirty =
isDirtyAxisLength ||
axis.min !== ((_a = axis.old) === null || _a === void 0 ? void 0 : _a.min) ||
axis.max !== ((_b = axis.old) === null || _b === void 0 ? void 0 : _b.max);
}
}
else if (stacking) {
stacking.cleanStacks();
}
// Recalculate all extremes object when the data has changed. It is
// required when vertical panning is enabled.
if (isDirtyData) {
delete axis.allExtremes;
}
Axis_fireEvent(this, 'afterSetScale');
};
/**
* Set the minimum and maximum of the axes after render time. If the
* `startOnTick` and `endOnTick` options are true, the minimum and maximum
* values are rounded off to the nearest tick. To prevent this, these
* options can be set to false before calling setExtremes. Also, setExtremes
* will not allow a range lower than the `minRange` option, which by default
* is the range of five points.
*
* @sample highcharts/members/axis-setextremes/
* Set extremes from a button
* @sample highcharts/members/axis-setextremes-datetime/
* Set extremes on a datetime axis
* @sample highcharts/members/axis-setextremes-off-ticks/
* Set extremes off ticks
* @sample stock/members/axis-setextremes/
* Set extremes in Highcharts Stock
*
* @function Highcharts.Axis#setExtremes
*
* @param {number|string} [newMin]
* The new minimum value. For datetime axes, date strings are accepted.
*
* @param {number|string} [newMax]
* The new maximum value. For datetime axes, date strings are accepted.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart or wait for an explicit call to
* {@link Highcharts.Chart#redraw}
*
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation=true]
* Enable or modify animations.
*
* @param {*} [eventArguments]
* Arguments to be accessed in event handler.
*
* @emits Highcharts.Axis#event:setExtremes
*/
Axis.prototype.setExtremes = function (min, max, redraw, animation, eventArguments) {
var _this = this;
if (redraw === void 0) { redraw = true; }
var chart = this.chart;
this.series.forEach(function (serie) {
delete serie.kdTree;
});
min = chart.time.parse(min);
max = chart.time.parse(max);
// Extend the arguments with min and max
eventArguments = Axis_extend(eventArguments, { min: min, max: max });
// Fire the event
Axis_fireEvent(this, 'setExtremes', eventArguments, function (e) {
_this.userMin = e.min;
_this.userMax = e.max;
_this.eventArgs = e;
if (redraw) {
chart.redraw(animation);
}
});
};
/**
* Update the axis metrics.
*
* @private
* @function Highcharts.Axis#setAxisSize
*/
Axis.prototype.setAxisSize = function () {
var chart = this.chart,
options = this.options,
// [top, right, bottom, left]
offsets = options.offsets || [0, 0, 0, 0],
horiz = this.horiz,
// Check for percentage based input values. Rounding fixes problems
// with column overflow and plot line filtering (#4898, #4899)
width = this.width = Math.round(Axis_relativeLength(Axis_pick(options.width,
chart.plotWidth - offsets[3] + offsets[1]),
chart.plotWidth)),
height = this.height = Math.round(Axis_relativeLength(Axis_pick(options.height,
chart.plotHeight - offsets[0] + offsets[2]),
chart.plotHeight)),
top = this.top = Math.round(Axis_relativeLength(Axis_pick(options.top,
chart.plotTop + offsets[0]),
chart.plotHeight,
chart.plotTop)),
left = this.left = Math.round(Axis_relativeLength(Axis_pick(options.left,
chart.plotLeft + offsets[3]),
chart.plotWidth,
chart.plotLeft));
// Expose basic values to use in Series object and navigator
this.bottom = chart.chartHeight - height - top;
this.right = chart.chartWidth - width - left;
// Direction agnostic properties
this.len = Math.max(horiz ? width : height, 0); // Math.max fixes #905
/**
* The position of the axis in terms of pixels, compared to the chart
* edge. In a horizontal axis it is the same as `chart.plotLeft` unless
* the axis is explicitly positioned, and in a default vertical axis it
* is the same as `chart.plotTop`.
*
* @name Highcharts.Axis#pos
* @type {number}
*/
this.pos = horiz ? left : top; // Distance from SVG origin
};
/**
* Get the current extremes for the axis.
*
* @sample highcharts/members/axis-getextremes/
* Report extremes by click on a button
*
* @function Highcharts.Axis#getExtremes
*
* @return {Highcharts.ExtremesObject}
* An object containing extremes information.
*/
Axis.prototype.getExtremes = function () {
var axis = this,
log = axis.logarithmic;
return {
min: log ?
Axis_correctFloat(log.lin2log(axis.min)) :
axis.min,
max: log ?
Axis_correctFloat(log.lin2log(axis.max)) :
axis.max,
dataMin: axis.dataMin,
dataMax: axis.dataMax,
userMin: axis.userMin,
userMax: axis.userMax
};
};
/**
* Get the zero plane either based on zero or on the min or max value.
* Used in bar and area plots.
*
* @function Highcharts.Axis#getThreshold
*
* @param {number} threshold
* The threshold in axis values.
*
* @return {number}
* The translated threshold position in terms of pixels, and corrected to
* stay within the axis bounds.
*/
Axis.prototype.getThreshold = function (threshold) {
var axis = this,
log = axis.logarithmic,
realMin = log ? log.lin2log(axis.min) : axis.min,
realMax = log ? log.lin2log(axis.max) : axis.max;
if (threshold === null || threshold === -Infinity) {
threshold = realMin;
}
else if (threshold === Infinity) {
threshold = realMax;
}
else if (realMin > threshold) {
threshold = realMin;
}
else if (realMax < threshold) {
threshold = realMax;
}
return axis.translate(threshold, 0, 1, 0, 1);
};
/**
* Compute auto alignment for the axis label based on which side the axis is
* on and the given rotation for the label.
*
* @private
* @function Highcharts.Axis#autoLabelAlign
*
* @param {number} rotation
* The rotation in degrees as set by either the `rotation` or `autoRotation`
* options.
*
* @return {Highcharts.AlignValue}
* Can be `"center"`, `"left"` or `"right"`.
*/
Axis.prototype.autoLabelAlign = function (rotation) {
var angle = (Axis_pick(rotation, 0) - (this.side * 90) + 720) % 360,
evt = { align: 'center' };
Axis_fireEvent(this, 'autoLabelAlign', evt, function (e) {
if (angle > 15 && angle < 165) {
e.align = 'right';
}
else if (angle > 195 && angle < 345) {
e.align = 'left';
}
});
return evt.align;
};
/**
* Get the tick length and width for the axis based on axis options.
*
* @private
* @function Highcharts.Axis#tickSize
*
* @param {string} [prefix]
* 'tick' or 'minorTick'
*
* @return {Array<number,number>|undefined}
* An array of tickLength and tickWidth
*/
Axis.prototype.tickSize = function (prefix) {
var options = this.options,
tickWidth = Axis_pick(options[prefix === 'tick' ? 'tickWidth' : 'minorTickWidth'],
// Default to 1 on linear and datetime X axes
prefix === 'tick' && this.isXAxis && !this.categories ? 1 : 0);
var tickLength = options[prefix === 'tick' ? 'tickLength' : 'minorTickLength'],
tickSize;
if (tickWidth && tickLength) {
// Negate the length
if (options[prefix + 'Position'] === 'inside') {
tickLength = -tickLength;
}
tickSize = [tickLength, tickWidth];
}
var e = { tickSize: tickSize };
Axis_fireEvent(this, 'afterTickSize', e);
return e.tickSize;
};
/**
* Return the size of the labels.
*
* @private
* @function Highcharts.Axis#labelMetrics
*/
Axis.prototype.labelMetrics = function () {
var renderer = this.chart.renderer,
ticks = this.ticks,
tick = ticks[Object.keys(ticks)[0]] || {};
return this.chart.renderer.fontMetrics(tick.label ||
tick.movedLabel ||
renderer.box);
};
/**
* Prevent the ticks from getting so close we can't draw the labels. On a
* horizontal axis, this is handled by rotating the labels, removing ticks
* and adding ellipsis. On a vertical axis remove ticks and add ellipsis.
*
* @private
* @function Highcharts.Axis#unsquish
*/
Axis.prototype.unsquish = function () {
var labelOptions = this.options.labels,
padding = labelOptions.padding || 0,
horiz = this.horiz,
tickInterval = this.tickInterval,
slotSize = this.len / (((this.categories ? 1 : 0) +
this.max -
this.min) /
tickInterval),
rotationOption = labelOptions.rotation,
// We don't know the actual rendered line height at this point, but
// it defaults to 0.8em
lineHeight = Axis_correctFloat(this.labelMetrics().h * 0.8),
range = Math.max(this.max - this.min, 0),
// Return the multiple of tickInterval that is needed to avoid
// collision
getStep = function (spaceNeeded) {
var step = (spaceNeeded + 2 * padding) / (slotSize || 1);
step = step > 1 ? Math.ceil(step) : 1;
// Guard for very small or negative angles (#9835)
if (step * tickInterval > range &&
spaceNeeded !== Infinity &&
slotSize !== Infinity &&
range) {
step = Math.ceil(range / tickInterval);
}
return Axis_correctFloat(step * tickInterval);
};
var newTickInterval = tickInterval,
rotation,
bestScore = Number.MAX_VALUE,
autoRotation;
if (horiz) {
if (!labelOptions.staggerLines) {
if (Axis_isNumber(rotationOption)) {
autoRotation = [rotationOption];
}
else if (slotSize < labelOptions.autoRotationLimit) {
autoRotation = labelOptions.autoRotation;
}
}
if (autoRotation) {
var step = void 0,
score = void 0;
// Loop over the given autoRotation options, and determine which
// gives the best score. The best score is that with the lowest
// number of steps and a rotation closest to horizontal.
for (var _i = 0, autoRotation_1 = autoRotation; _i < autoRotation_1.length; _i++) {
var rot = autoRotation_1[_i];
if (rot === rotationOption ||
(rot && rot >= -90 && rot <= 90)) { // #3891
step = getStep(Math.abs(lineHeight / Math.sin(Axis_deg2rad * rot)));
score = step + Math.abs(rot / 360);
if (score < bestScore) {
bestScore = score;
rotation = rot;
newTickInterval = step;
}
}
}
}
}
else { // #4411
newTickInterval = getStep(lineHeight * 0.75);
}
this.autoRotation = autoRotation;
this.labelRotation = Axis_pick(rotation, Axis_isNumber(rotationOption) ? rotationOption : 0);
return labelOptions.step ? tickInterval : newTickInterval;
};
/**
* Get the general slot width for labels/categories on this axis. This may
* change between the pre-render (from Axis.getOffset) and the final tick
* rendering and placement.
*
* @private
* @function Highcharts.Axis#getSlotWidth
*
* @param {Highcharts.Tick} [tick] Optionally, calculate the slot width
* basing on tick label. It is used in highcharts-3d module, where the slots
* has different widths depending on perspective angles.
*
* @return {number}
* The pixel width allocated to each axis label.
*/
Axis.prototype.getSlotWidth = function (tick) {
// #5086, #1580, #1931
var chart = this.chart,
horiz = this.horiz,
labelOptions = this.options.labels,
slotCount = Math.max(this.tickPositions.length - (this.categories ? 0 : 1), 1),
marginLeft = chart.margin[3];
// Used by grid axis
if (tick && Axis_isNumber(tick.slotWidth)) { // #13221, can be 0
return tick.slotWidth;
}
if (horiz && labelOptions.step < 2 && !this.isRadial) {
if (labelOptions.rotation) { // #4415
return 0;
}
return ((this.staggerLines || 1) * this.len) / slotCount;
}
if (!horiz) {
// #7028
var cssWidth = labelOptions.style.width;
if (cssWidth !== void 0) {
return parseInt(String(cssWidth), 10);
}
if (marginLeft) {
return marginLeft - chart.spacing[3];
}
}
// Last resort, a fraction of the available size
return chart.chartWidth * 0.33;
};
/**
* Render the axis labels and determine whether ellipsis or rotation need to
* be applied.
*
* @private
* @function Highcharts.Axis#renderUnsquish
*/
Axis.prototype.renderUnsquish = function () {
var chart = this.chart,
renderer = chart.renderer,
tickPositions = this.tickPositions,
ticks = this.ticks,
labelOptions = this.options.labels,
labelStyleOptions = labelOptions.style,
horiz = this.horiz,
slotWidth = this.getSlotWidth(),
innerWidth = Math.max(1,
Math.round(slotWidth - (horiz ?
2 * (labelOptions.padding || 0) :
labelOptions.distance || 0 // #21172
))),
attr = {},
labelMetrics = this.labelMetrics(),
lineClampOption = labelStyleOptions.lineClamp;
var commonWidth,
lineClamp = lineClampOption !== null && lineClampOption !== void 0 ? lineClampOption : (Math.floor(this.len / (tickPositions.length * labelMetrics.h)) || 1),
maxLabelLength = 0;
// Set rotation option unless it is "auto", like in gauges
if (!Axis_isString(labelOptions.rotation)) {
// #4443
attr.rotation = labelOptions.rotation || 0;
}
// Get the longest label length
tickPositions.forEach(function (tickPosition) {
var _a;
var tick = ticks[tickPosition];
// Replace label - sorting animation
if (tick.movedLabel) {
tick.replaceMovedLabel();
}
var textPxLength = ((_a = tick.label) === null || _a === void 0 ? void 0 : _a.textPxLength) || 0;
if (textPxLength > maxLabelLength) {
maxLabelLength = textPxLength;
}
});
this.maxLabelLength = maxLabelLength;
// Handle auto rotation on horizontal axis
if (this.autoRotation) {
// Apply rotation only if the label is too wide for the slot, and
// the label is wider than its height.
if (maxLabelLength > innerWidth &&
maxLabelLength > labelMetrics.h) {
attr.rotation = this.labelRotation;
}
else {
this.labelRotation = 0;
}
// Handle word-wrap or ellipsis on vertical axis
}
else if (slotWidth) {
// For word-wrap or ellipsis
commonWidth = innerWidth;
}
// Add ellipsis if the label length is significantly longer than ideal
if (attr.rotation) {
commonWidth = (maxLabelLength > chart.chartHeight * 0.5 ?
chart.chartHeight * 0.33 :
maxLabelLength);
if (!lineClampOption) {
lineClamp = 1;
}
}
// Set the explicit or automatic label alignment
this.labelAlign = labelOptions.align ||
this.autoLabelAlign(this.labelRotation);
if (this.labelAlign) {
attr.align = this.labelAlign;
}
// Apply general and specific CSS
tickPositions.forEach(function (pos) {
var tick = ticks[pos],
label = tick && tick.label,
widthOption = labelStyleOptions.width,
css = {};
if (label) {
// This needs to go before the CSS in old IE (#4502)
label.attr(attr);
if (tick.shortenLabel) {
tick.shortenLabel();
}
else if (commonWidth &&
!widthOption &&
// Setting width in this case messes with the bounding box
// (#7975)
labelStyleOptions.whiteSpace !== 'nowrap' &&
(
// Speed optimizing, #7656
commonWidth < (label.textPxLength || 0) ||
// Resetting CSS, #4928
label.element.tagName === 'SPAN')) {
label.css(Axis_extend(css, {
width: "" + commonWidth + "px",
lineClamp: lineClamp
}));
// Reset previously shortened label (#8210)
}
else if (label.styles.width && !css.width && !widthOption) {
label.css({ width: 'auto' });
}
tick.rotation = attr.rotation;
}
}, this);
// Note: Why is this not part of getLabelPosition?
this.tickRotCorr = renderer.rotCorr(labelMetrics.b, this.labelRotation || 0, this.side !== 0);
};
/**
* Return true if the axis has associated data.
*
* @function Highcharts.Axis#hasData
*
* @return {boolean}
* True if the axis has associated visible series and those series have
* either valid data points or explicit `min` and `max` settings.
*/
Axis.prototype.hasData = function () {
return this.series.some(function (s) {
return s.hasData();
}) ||
(this.options.showEmpty &&
Axis_defined(this.min) &&
Axis_defined(this.max));
};
/**
* Adds the title defined in axis.options.title.
*
* @function Highcharts.Axis#addTitle
*
* @param {boolean} [display]
* Whether or not to display the title.
*/
Axis.prototype.addTitle = function (display) {
var axis = this,
renderer = axis.chart.renderer,
horiz = axis.horiz,
opposite = axis.opposite,
options = axis.options,
axisTitleOptions = options.title,
styledMode = axis.chart.styledMode;
var textAlign;
if (!axis.axisTitle) {
textAlign = axisTitleOptions.textAlign;
if (!textAlign) {
textAlign = (horiz ? {
low: 'left',
middle: 'center',
high: 'right'
} : {
low: opposite ? 'right' : 'left',
middle: 'center',
high: opposite ? 'left' : 'right'
})[axisTitleOptions.align];
}
axis.axisTitle = renderer
.text(axisTitleOptions.text || '', 0, 0, axisTitleOptions.useHTML)
.attr({
zIndex: 7,
rotation: axisTitleOptions.rotation || 0,
align: textAlign
})
.addClass('highcharts-axis-title');
// #7814, don't mutate style option
if (!styledMode) {
axis.axisTitle.css(Axis_merge(axisTitleOptions.style));
}
axis.axisTitle.add(axis.axisGroup);
axis.axisTitle.isNew = true;
}
// Max width defaults to the length of the axis
if (!styledMode &&
!axisTitleOptions.style.width &&
!axis.isRadial) {
axis.axisTitle.css({
width: axis.len + 'px'
});
}
// Hide or show the title depending on whether showEmpty is set
axis.axisTitle[display ? 'show' : 'hide'](display);
};
/**
* Generates a tick for initial positioning.
*
* @private
* @function Highcharts.Axis#generateTick
*
* @param {number} pos
* The tick position in axis values.
*
* @param {number} [i]
* The index of the tick in {@link Axis.tickPositions}.
*/
Axis.prototype.generateTick = function (pos) {
var axis = this,
ticks = axis.ticks;
if (!ticks[pos]) {
ticks[pos] = new Axis_Tick(axis, pos);
}
else {
ticks[pos].addLabel(); // Update labels depending on tick interval
}
};
/**
* Create the axisGroup and gridGroup elements on first iteration.
*
* @private
* @function Highcharts.Axis#getOffset
*
* @emits Highcharts.Axis#event:afterGetOffset
*/
Axis.prototype.createGroups = function () {
var _this = this;
var _a = this,
axisParent = _a.axisParent, // Used in color axis
chart = _a.chart,
coll = _a.coll,
options = _a.options,
renderer = chart.renderer;
var createGroup = function (name, suffix, zIndex) { return renderer.g(name)
.attr({ zIndex: zIndex })
.addClass("highcharts-".concat(coll.toLowerCase()).concat(suffix, " ") +
(_this.isRadial ? "highcharts-radial-axis".concat(suffix, " ") : '') +
(options.className || ''))
.add(axisParent); };
if (!this.axisGroup) {
this.gridGroup = createGroup('grid', '-grid', options.gridZIndex);
this.axisGroup = createGroup('axis', '', options.zIndex);
this.labelGroup = createGroup('axis-labels', '-labels', options.labels.zIndex);
}
};
/**
* Render the tick labels to a preliminary position to get their sizes
*
* @private
* @function Highcharts.Axis#getOffset
*
* @emits Highcharts.Axis#event:afterGetOffset
*/
Axis.prototype.getOffset = function () {
var axis = this,
chart = axis.chart,
horiz = axis.horiz,
options = axis.options,
side = axis.side,
ticks = axis.ticks,
tickPositions = axis.tickPositions,
coll = axis.coll,
invertedSide = (chart.inverted && !axis.isZAxis ?
[1, 0, 3, 2][side] :
side),
hasData = axis.hasData(),
axisTitleOptions = options.title,
labelOptions = options.labels,
hasCrossing = Axis_isNumber(options.crossing),
axisOffset = chart.axisOffset,
clipOffset = chart.clipOffset,
directionFactor = [-1, 1, 1, -1][side];
var showAxis,
titleOffset = 0,
titleOffsetOption,
titleMargin = 0,
labelOffset = 0, // Reset
labelOffsetPadded,
lineHeightCorrection;
// For reuse in Axis.render
axis.showAxis = showAxis = hasData || options.showEmpty;
// Set/reset staggerLines
axis.staggerLines = (axis.horiz && labelOptions.staggerLines) || void 0;
axis.createGroups();
if (hasData || axis.isLinked) {
// Generate ticks
tickPositions.forEach(function (pos) {
axis.generateTick(pos);
});
axis.renderUnsquish();
// Left side must be align: right and right side must
// have align: left for labels
axis.reserveSpaceDefault = (side === 0 ||
side === 2 ||
{ 1: 'left', 3: 'right' }[side] === axis.labelAlign);
if (Axis_pick(labelOptions.reserveSpace, hasCrossing ? false : null, axis.labelAlign === 'center' ? true : null, axis.reserveSpaceDefault)) {
tickPositions.forEach(function (pos) {
// Get the highest offset
labelOffset = Math.max(ticks[pos].getLabelSize(), labelOffset);
});
}
if (axis.staggerLines) {
labelOffset *= axis.staggerLines;
}
axis.labelOffset = labelOffset * (axis.opposite ? -1 : 1);
}
else { // Doesn't have data
Axis_objectEach(ticks, function (tick, n) {
tick.destroy();
delete ticks[n];
});
}
if ((axisTitleOptions === null || axisTitleOptions === void 0 ? void 0 : axisTitleOptions.text) &&
axisTitleOptions.enabled !== false) {
axis.addTitle(showAxis);
if (showAxis &&
!hasCrossing &&
axisTitleOptions.reserveSpace !== false) {
axis.titleOffset = titleOffset =
axis.axisTitle.getBBox()[horiz ? 'height' : 'width'];
titleOffsetOption = axisTitleOptions.offset;
titleMargin = Axis_defined(titleOffsetOption) ?
0 :
Axis_pick(axisTitleOptions.margin, horiz ? 5 : 10);
}
}
// Render the axis line
axis.renderLine();
// Handle automatic or user set offset
axis.offset = directionFactor * Axis_pick(options.offset, axisOffset[side] ? axisOffset[side] + (options.margin || 0) : 0);
axis.tickRotCorr = axis.tickRotCorr || { x: 0, y: 0 }; // Polar
if (side === 0) {
lineHeightCorrection = -axis.labelMetrics().h;
}
else if (side === 2) {
lineHeightCorrection = axis.tickRotCorr.y;
}
else {
lineHeightCorrection = 0;
}
// Find the padded label offset
labelOffsetPadded = Math.abs(labelOffset) + titleMargin;
if (labelOffset) {
labelOffsetPadded -= lineHeightCorrection;
labelOffsetPadded += directionFactor * (horiz ?
Axis_pick(labelOptions.y, axis.tickRotCorr.y +
directionFactor * labelOptions.distance) :
Axis_pick(labelOptions.x, directionFactor * labelOptions.distance));
}
axis.axisTitleMargin = Axis_pick(titleOffsetOption, labelOffsetPadded);
if (axis.getMaxLabelDimensions) {
axis.maxLabelDimensions = axis.getMaxLabelDimensions(ticks, tickPositions);
}
// Due to GridAxis.tickSize, tickSize should be calculated after ticks
// has rendered.
if (coll !== 'colorAxis' && clipOffset) {
var tickSize = this.tickSize('tick');
axisOffset[side] = Math.max(axisOffset[side], (axis.axisTitleMargin || 0) + titleOffset +
directionFactor * axis.offset, labelOffsetPadded, // #3027
tickPositions && tickPositions.length && tickSize ?
tickSize[0] + directionFactor * axis.offset :
0 // #4866
);
// Decide the clipping needed to keep the graph inside
// the plot area and axis lines
var clip = !axis.axisLine || options.offset ?
0 :
// #4308, #4371
axis.axisLine.strokeWidth() / 2;
clipOffset[invertedSide] = Math.max(clipOffset[invertedSide], clip);
}
Axis_fireEvent(this, 'afterGetOffset');
};
/**
* Internal function to get the path for the axis line. Extended for polar
* charts.
*
* @function Highcharts.Axis#getLinePath
*
* @param {number} lineWidth
* The line width in pixels.
*
* @return {Highcharts.SVGPathArray}
* The SVG path definition in array form.
*/
Axis.prototype.getLinePath = function (lineWidth) {
var chart = this.chart,
opposite = this.opposite,
offset = this.offset,
horiz = this.horiz,
lineLeft = this.left + (opposite ? this.width : 0) + offset,
lineTop = chart.chartHeight - this.bottom -
(opposite ? this.height : 0) + offset;
if (opposite) {
lineWidth *= -1; // Crispify the other way - #1480, #1687
}
return chart.renderer
.crispLine([
[
'M',
horiz ?
this.left :
lineLeft,
horiz ?
lineTop :
this.top
],
[
'L',
horiz ?
chart.chartWidth - this.right :
lineLeft,
horiz ?
lineTop :
chart.chartHeight - this.bottom
]
], lineWidth);
};
/**
* Render the axis line. Called internally when rendering and redrawing the
* axis.
*
* @function Highcharts.Axis#renderLine
*/
Axis.prototype.renderLine = function () {
if (!this.axisLine) {
this.axisLine = this.chart.renderer.path()
.addClass('highcharts-axis-line')
.add(this.axisGroup);
if (!this.chart.styledMode) {
this.axisLine.attr({
stroke: this.options.lineColor,
'stroke-width': this.options.lineWidth,
zIndex: 7
});
}
}
};
/**
* Position the axis title.
*
* @private
* @function Highcharts.Axis#getTitlePosition
*
* @return {Highcharts.PositionObject}
* X and Y positions for the title.
*/
Axis.prototype.getTitlePosition = function (axisTitle) {
// Compute anchor points for each of the title align options
var horiz = this.horiz,
axisLeft = this.left,
axisTop = this.top,
axisLength = this.len,
axisTitleOptions = this.options.title,
margin = horiz ? axisLeft : axisTop,
opposite = this.opposite,
offset = this.offset,
xOption = axisTitleOptions.x,
yOption = axisTitleOptions.y,
fontMetrics = this.chart.renderer.fontMetrics(axisTitle),
// The part of a multiline text that is below the baseline of the
// first line. Subtract 1 to preserve pixel-perfectness from the
// old behaviour (v5.0.12), where only one line was allowed.
textHeightOvershoot = axisTitle ? Math.max(axisTitle.getBBox(false, 0).height - fontMetrics.h - 1, 0) : 0,
// The position in the length direction of the axis
alongAxis = ({
low: margin + (horiz ? 0 : axisLength),
middle: margin + axisLength / 2,
high: margin + (horiz ? axisLength : 0)
})[axisTitleOptions.align],
// The position in the perpendicular direction of the axis
offAxis = (horiz ? axisTop + this.height : axisLeft) +
(horiz ? 1 : -1) * // Horizontal axis reverses the margin
(opposite ? -1 : 1) * // So does opposite axes
(this.axisTitleMargin || 0) +
[
-textHeightOvershoot, // Top
textHeightOvershoot, // Right
fontMetrics.f, // Bottom
-textHeightOvershoot // Left
][this.side],
titlePosition = {
x: horiz ?
alongAxis + xOption :
offAxis + (opposite ? this.width : 0) + offset + xOption,
y: horiz ?
offAxis + yOption - (opposite ? this.height : 0) + offset :
alongAxis + yOption
};
Axis_fireEvent(this, 'afterGetTitlePosition', { titlePosition: titlePosition });
return titlePosition;
};
/**
* Render a minor tick into the given position. If a minor tick already
* exists in this position, move it.
*
* @function Highcharts.Axis#renderMinorTick
*
* @param {number} pos
* The position in axis values.
*
* @param {boolean} slideIn
* Whether the tick should animate in from last computed position
*/
Axis.prototype.renderMinorTick = function (pos, slideIn) {
var axis = this;
var minorTicks = axis.minorTicks;
if (!minorTicks[pos]) {
minorTicks[pos] = new Axis_Tick(axis, pos, 'minor');
}
// Render new ticks in old position
if (slideIn && minorTicks[pos].isNew) {
minorTicks[pos].render(null, true);
}
minorTicks[pos].render(null, false, 1);
};
/**
* Render a major tick into the given position. If a tick already exists
* in this position, move it.
*
* @function Highcharts.Axis#renderTick
*
* @param {number} pos
* The position in axis values.
*
* @param {number} i
* The tick index.
*
* @param {boolean} slideIn
* Whether the tick should animate in from last computed position
*/
Axis.prototype.renderTick = function (pos, i, slideIn) {
var axis = this,
isLinked = axis.isLinked,
ticks = axis.ticks;
// Linked axes need an extra check to find out if
if (!isLinked ||
(pos >= axis.min && pos <= axis.max) ||
(axis.grid && axis.grid.isColumn)) {
if (!ticks[pos]) {
ticks[pos] = new Axis_Tick(axis, pos);
}
// NOTE this seems like overkill. Could be handled in tick.render by
// setting old position in attr, then set new position in animate.
// render new ticks in old position
if (slideIn && ticks[pos].isNew) {
// Start with negative opacity so that it is visible from
// halfway into the animation
ticks[pos].render(i, true, -1);
}
ticks[pos].render(i);
}
};
/**
* Render the axis.
*
* @private
* @function Highcharts.Axis#render
*
* @emits Highcharts.Axis#event:afterRender
*/
Axis.prototype.render = function () {
var axis = this,
chart = axis.chart,
log = axis.logarithmic,
renderer = chart.renderer,
options = axis.options,
isLinked = axis.isLinked,
tickPositions = axis.tickPositions,
axisTitle = axis.axisTitle,
ticks = axis.ticks,
minorTicks = axis.minorTicks,
alternateBands = axis.alternateBands,
stackLabelOptions = options.stackLabels,
alternateGridColor = options.alternateGridColor,
crossing = options.crossing,
tickmarkOffset = axis.tickmarkOffset,
axisLine = axis.axisLine,
showAxis = axis.showAxis,
animation = Axis_animObject(renderer.globalAnimation);
var from,
to;
// Reset
axis.labelEdge.length = 0;
axis.overlap = false;
// Mark all elements inActive before we go over and mark the active ones
[ticks, minorTicks, alternateBands].forEach(function (coll) {
Axis_objectEach(coll, function (tick) {
tick.isActive = false;
});
});
// Crossing
if (Axis_isNumber(crossing)) {
var otherAxis = this.isXAxis ? chart.yAxis[0] : chart.xAxis[0],
directionFactor = [1, -1, -1, 1][this.side];
if (otherAxis) {
var px = otherAxis.toPixels(crossing,
true);
if (axis.horiz) {
px = otherAxis.len - px;
}
axis.offset = directionFactor * px;
}
}
// If the series has data draw the ticks. Else only the line and title
if (axis.hasData() || isLinked) {
var slideInTicks_1 = axis.chart.hasRendered &&
axis.old && Axis_isNumber(axis.old.min);
// Minor ticks
if (axis.minorTickInterval && !axis.categories) {
axis.getMinorTickPositions().forEach(function (pos) {
axis.renderMinorTick(pos, slideInTicks_1);
});
}
// Major ticks. Pull out the first item and render it last so that
// we can get the position of the neighbour label. #808.
if (tickPositions.length) { // #1300
tickPositions.forEach(function (pos, i) {
axis.renderTick(pos, i, slideInTicks_1);
});
// In a categorized axis, the tick marks are displayed
// between labels. So we need to add a tick mark and
// grid line at the left edge of the X axis.
if (tickmarkOffset && (axis.min === 0 || axis.single)) {
if (!ticks[-1]) {
ticks[-1] = new Axis_Tick(axis, -1, null, true);
}
ticks[-1].render(-1);
}
}
// Alternate grid color
if (alternateGridColor) {
tickPositions.forEach(function (pos, i) {
to = typeof tickPositions[i + 1] !== 'undefined' ?
tickPositions[i + 1] + tickmarkOffset :
axis.max - tickmarkOffset;
if (i % 2 === 0 &&
pos < axis.max &&
to <= axis.max + (chart.polar ?
-tickmarkOffset :
tickmarkOffset)) { // #2248, #4660
if (!alternateBands[pos]) {
// Should be imported from PlotLineOrBand.js, but
// the dependency cycle with axis is a problem
alternateBands[pos] = new Core_Globals.PlotLineOrBand(axis, {});
}
from = pos + tickmarkOffset; // #949
alternateBands[pos].options = {
from: log ? log.lin2log(from) : from,
to: log ? log.lin2log(to) : to,
color: alternateGridColor,
className: 'highcharts-alternate-grid'
};
alternateBands[pos].render();
alternateBands[pos].isActive = true;
}
});
}
// Custom plot lines and bands
if (!axis._addedPlotLB) { // Only first time
axis._addedPlotLB = true;
(options.plotLines || [])
.concat(options.plotBands || [])
.forEach(function (plotLineOptions) {
axis
.addPlotBandOrLine(plotLineOptions);
});
}
} // End if hasData
// Remove inactive ticks
[ticks, minorTicks, alternateBands].forEach(function (coll) {
var forDestruction = [],
delay = animation.duration,
destroyInactiveItems = function () {
var i = forDestruction.length;
while (i--) {
// When resizing rapidly, the same items
// may be destroyed in different timeouts,
// or the may be reactivated
if (coll[forDestruction[i]] &&
!coll[forDestruction[i]].isActive) {
coll[forDestruction[i]].destroy();
delete coll[forDestruction[i]];
}
}
};
Axis_objectEach(coll, function (tick, pos) {
if (!tick.isActive) {
// Render to zero opacity
tick.render(pos, false, 0);
tick.isActive = false;
forDestruction.push(pos);
}
});
// When the objects are finished fading out, destroy them
Axis_syncTimeout(destroyInactiveItems, coll === alternateBands ||
!chart.hasRendered ||
!delay ?
0 :
delay);
});
// Set the axis line path
if (axisLine) {
axisLine[axisLine.isPlaced ? 'animate' : 'attr']({
d: this.getLinePath(axisLine.strokeWidth())
});
axisLine.isPlaced = true;
// Show or hide the line depending on options.showEmpty
axisLine[showAxis ? 'show' : 'hide'](showAxis);
}
if (axisTitle && showAxis) {
axisTitle[axisTitle.isNew ? 'attr' : 'animate'](axis.getTitlePosition(axisTitle));
axisTitle.isNew = false;
}
// Stacked totals:
if (stackLabelOptions && stackLabelOptions.enabled && axis.stacking) {
axis.stacking.renderStackTotals();
}
// End stacked totals
// Record old scaling for updating/animation. Pinch base must be
// preserved until the pinch ends.
axis.old = {
len: axis.len,
max: axis.max,
min: axis.min,
transA: axis.transA,
userMax: axis.userMax,
userMin: axis.userMin
};
axis.isDirty = false;
Axis_fireEvent(this, 'afterRender');
};
/**
* Redraw the axis to reflect changes in the data or axis extremes. Called
* internally from Highcharts.Chart#redraw.
*
* @private
* @function Highcharts.Axis#redraw
*/
Axis.prototype.redraw = function () {
if (this.visible) {
// Render the axis
this.render();
// Move plot lines and bands
this.plotLinesAndBands.forEach(function (plotLine) {
plotLine.render();
});
}
// Mark associated series as dirty and ready for redraw
this.series.forEach(function (series) {
series.isDirty = true;
});
};
/**
* Returns an array of axis properties, that should be untouched during
* reinitialization.
*
* @private
* @function Highcharts.Axis#getKeepProps
*/
Axis.prototype.getKeepProps = function () {
return (this.keepProps || Axis.keepProps);
};
/**
* Destroys an Axis instance. See {@link Axis#remove} for the API endpoint
* to fully remove the axis.
*
* @private
* @function Highcharts.Axis#destroy
*
* @param {boolean} [keepEvents]
* Whether to preserve events, used internally in Axis.update.
*/
Axis.prototype.destroy = function (keepEvents) {
var axis = this,
plotLinesAndBands = axis.plotLinesAndBands,
eventOptions = this.eventOptions;
Axis_fireEvent(this, 'destroy', { keepEvents: keepEvents });
// Remove the events
if (!keepEvents) {
Axis_removeEvent(axis);
}
// Destroy collections
[axis.ticks, axis.minorTicks, axis.alternateBands].forEach(function (coll) {
Axis_destroyObjectProperties(coll);
});
if (plotLinesAndBands) {
var i = plotLinesAndBands.length;
while (i--) { // #1975
plotLinesAndBands[i].destroy();
}
}
// Destroy elements
[
'axisLine', 'axisTitle', 'axisGroup',
'gridGroup', 'labelGroup', 'cross', 'scrollbar'
].forEach(function (prop) {
if (axis[prop]) {
axis[prop] = axis[prop].destroy();
}
});
// Destroy each generated group for plotlines and plotbands
for (var plotGroup in axis.plotLinesAndBandsGroups) { // eslint-disable-line guard-for-in
axis.plotLinesAndBandsGroups[plotGroup] =
axis.plotLinesAndBandsGroups[plotGroup].destroy();
}
// Delete all properties and fall back to the prototype.
Axis_objectEach(axis, function (val, key) {
if (axis.getKeepProps().indexOf(key) === -1) {
delete axis[key];
}
});
this.eventOptions = eventOptions;
};
/**
* Internal function to draw a crosshair.
*
* @function Highcharts.Axis#drawCrosshair
*
* @param {Highcharts.PointerEventObject} [e]
* The event arguments from the modified pointer event, extended with
* `chartX` and `chartY`
*
* @param {Highcharts.Point} [point]
* The Point object if the crosshair snaps to points.
*
* @emits Highcharts.Axis#event:afterDrawCrosshair
* @emits Highcharts.Axis#event:drawCrosshair
*/
Axis.prototype.drawCrosshair = function (e, point) {
var options = this.crosshair,
snap = Axis_pick(options && options.snap,
true),
chart = this.chart;
var path,
pos,
categorized,
graphic = this.cross,
crossOptions;
Axis_fireEvent(this, 'drawCrosshair', { e: e, point: point });
// Use last available event when updating non-snapped crosshairs without
// mouse interaction (#5287)
if (!e) {
e = this.cross && this.cross.e;
}
if (
// Disabled in options
!options ||
// Snap
((Axis_defined(point) || !snap) === false)) {
this.hideCrosshair();
}
else {
// Get the path
if (!snap) {
pos = e &&
(this.horiz ?
e.chartX - this.pos :
this.len - e.chartY + this.pos);
}
else if (Axis_defined(point)) {
// #3834
pos = Axis_pick(this.coll !== 'colorAxis' ?
point.crosshairPos : // 3D axis extension
null, this.isXAxis ?
point.plotX :
this.len - point.plotY);
}
if (Axis_defined(pos)) {
crossOptions = {
// Value, only used on radial
value: point && (this.isXAxis ?
point.x :
Axis_pick(point.stackY, point.y)),
translatedValue: pos
};
if (chart.polar) {
// Additional information required for crosshairs in
// polar chart
Axis_extend(crossOptions, {
isCrosshair: true,
chartX: e && e.chartX,
chartY: e && e.chartY,
point: point
});
}
path = this.getPlotLinePath(crossOptions) ||
null; // #3189
}
if (!Axis_defined(path)) {
this.hideCrosshair();
return;
}
categorized = this.categories && !this.isRadial;
// Draw the cross
if (!graphic) {
this.cross = graphic = chart.renderer
.path()
.addClass('highcharts-crosshair highcharts-crosshair-' +
(categorized ? 'category ' : 'thin ') +
(options.className || ''))
.attr({
zIndex: Axis_pick(options.zIndex, 2)
})
.add();
// Presentational attributes
if (!chart.styledMode) {
graphic.attr({
stroke: options.color ||
(categorized ?
Color_Color
.parse("#ccd3ff" /* Palette.highlightColor20 */)
.setOpacity(0.25)
.get() :
"#cccccc" /* Palette.neutralColor20 */),
'stroke-width': Axis_pick(options.width, 1)
}).css({
'pointer-events': 'none'
});
if (options.dashStyle) {
graphic.attr({
dashstyle: options.dashStyle
});
}
}
}
graphic.show().attr({
d: path
});
if (categorized && !options.width) {
graphic.attr({
'stroke-width': this.transA
});
}
this.cross.e = e;
}
Axis_fireEvent(this, 'afterDrawCrosshair', { e: e, point: point });
};
/**
* Hide the crosshair if visible.
*
* @function Highcharts.Axis#hideCrosshair
*/
Axis.prototype.hideCrosshair = function () {
if (this.cross) {
this.cross.hide();
}
Axis_fireEvent(this, 'afterHideCrosshair');
};
/**
* Update an axis object with a new set of options. The options are merged
* with the existing options, so only new or altered options need to be
* specified.
*
* @sample highcharts/members/axis-update/
* Axis update demo
*
* @function Highcharts.Axis#update
*
* @param {Highcharts.AxisOptions} options
* The new options that will be merged in with existing options on the axis.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart after the axis is altered. If doing more
* operations on the chart, it is a good idea to set redraw to false and
* call {@link Chart#redraw} after.
*/
Axis.prototype.update = function (options, redraw) {
var chart = this.chart;
options = Axis_merge(this.userOptions, options);
this.destroy(true);
this.init(chart, options);
chart.isDirtyBox = true;
if (Axis_pick(redraw, true)) {
chart.redraw();
}
};
/**
* Remove the axis from the chart.
*
* @sample highcharts/members/chart-addaxis/
* Add and remove axes
*
* @function Highcharts.Axis#remove
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart following the remove.
*/
Axis.prototype.remove = function (redraw) {
var chart = this.chart,
coll = this.coll,
axisSeries = this.series;
var i = axisSeries.length;
// Remove associated series (#2687)
while (i--) {
if (axisSeries[i]) {
axisSeries[i].remove(false);
}
}
// Remove the axis
Axis_erase(chart.axes, this);
Axis_erase(chart[coll] || [], this);
chart.orderItems(coll);
this.destroy();
chart.isDirtyBox = true;
if (Axis_pick(redraw, true)) {
chart.redraw();
}
};
/**
* Update the axis title by options after render time.
*
* @sample highcharts/members/axis-settitle/
* Set a new Y axis title
*
* @function Highcharts.Axis#setTitle
*
* @param {Highcharts.AxisTitleOptions} titleOptions
* The additional title options.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart after setting the title.
*/
Axis.prototype.setTitle = function (titleOptions, redraw) {
this.update({ title: titleOptions }, redraw);
};
/**
* Set new axis categories and optionally redraw.
*
* @sample highcharts/members/axis-setcategories/
* Set categories by click on a button
*
* @function Highcharts.Axis#setCategories
*
* @param {Array<string>} categories
* The new categories.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart.
*/
Axis.prototype.setCategories = function (categories, redraw) {
this.update({ categories: categories }, redraw);
};
/* *
*
* Static Properties
*
* */
// Properties to survive after destroy, needed for Axis.update (#4317,
// #5773, #5881).
Axis.keepProps = [
'coll',
'extKey',
'hcEvents',
'len',
'names',
'series',
'userMax',
'userMin'
];
return Axis;
}());
/* *
*
* Default Export
*
* */
/* harmony default export */ var Axis_Axis = (Axis);
/* *
*
* API Declarations
*
* */
/**
* Options for the path on the Axis to be calculated.
* @interface Highcharts.AxisPlotLinePathOptionsObject
*/ /**
* Axis value.
* @name Highcharts.AxisPlotLinePathOptionsObject#value
* @type {number|undefined}
*/ /**
* Line width used for calculation crisp line coordinates. Defaults to 1.
* @name Highcharts.AxisPlotLinePathOptionsObject#lineWidth
* @type {number|undefined}
*/ /**
* If `false`, the function will return null when it falls outside the axis
* bounds. If `true`, the function will return a path aligned to the plot area
* sides if it falls outside. If `pass`, it will return a path outside.
* @name Highcharts.AxisPlotLinePathOptionsObject#force
* @type {string|boolean|undefined}
*/ /**
* Used in Highcharts Stock. When `true`, plot paths
* (crosshair, plotLines, gridLines)
* will be rendered on all axes when defined on the first axis.
* @name Highcharts.AxisPlotLinePathOptionsObject#acrossPanes
* @type {boolean|undefined}
*/ /**
* Use old coordinates (for resizing and rescaling).
* If not set, defaults to `false`.
* @name Highcharts.AxisPlotLinePathOptionsObject#old
* @type {boolean|undefined}
*/ /**
* If given, return the plot line path of a pixel position on the axis.
* @name Highcharts.AxisPlotLinePathOptionsObject#translatedValue
* @type {number|undefined}
*/ /**
* Used in Polar axes. Reverse the positions for concatenation of polygonal
* plot bands
* @name Highcharts.AxisPlotLinePathOptionsObject#reverse
* @type {boolean|undefined}
*/
/**
* Options for crosshairs on axes.
*
* @product highstock
*
* @typedef {Highcharts.XAxisCrosshairOptions|Highcharts.YAxisCrosshairOptions} Highcharts.AxisCrosshairOptions
*/
/**
* @typedef {"navigator"|"pan"|"rangeSelectorButton"|"rangeSelectorInput"|"scrollbar"|"traverseUpButton"|"zoom"} Highcharts.AxisExtremesTriggerValue
*/
/**
* @callback Highcharts.AxisEventCallbackFunction
*
* @param {Highcharts.Axis} this
*/
/**
* @callback Highcharts.AxisLabelsFormatterCallbackFunction
*
* @param {Highcharts.AxisLabelsFormatterContextObject} this
*
* @param {Highcharts.AxisLabelsFormatterContextObject} ctx
*
* @return {string}
*/
/**
* @interface Highcharts.AxisLabelsFormatterContextObject
*/ /**
* The axis item of the label
* @name Highcharts.AxisLabelsFormatterContextObject#axis
* @type {Highcharts.Axis}
*/ /**
* The chart instance.
* @name Highcharts.AxisLabelsFormatterContextObject#chart
* @type {Highcharts.Chart}
*/ /**
* Default formatting of date/time labels.
* @name Highcharts.AxisLabelsFormatterContextObject#dateTimeLabelFormat
* @type {string|undefined}
*/ /**
* Whether the label belongs to the first tick on the axis.
* @name Highcharts.AxisLabelsFormatterContextObject#isFirst
* @type {boolean}
*/ /**
* Whether the label belongs to the last tick on the axis.
* @name Highcharts.AxisLabelsFormatterContextObject#isLast
* @type {boolean}
*/ /**
* The position on the axis in terms of axis values. For category axes, a
* zero-based index. For datetime axes, the JavaScript time in milliseconds
* since 1970.
* @name Highcharts.AxisLabelsFormatterContextObject#pos
* @type {number}
*/ /**
* The preformatted text as the result of the default formatting. For example
* dates will be formatted as strings, and numbers with language-specific comma
* separators, thousands separators and numeric symbols like `k` or `M`.
* @name Highcharts.AxisLabelsFormatterContextObject#text
* @type {string|undefined}
*/ /**
* The Tick instance.
* @name Highcharts.AxisLabelsFormatterContextObject#tick
* @type {Highcharts.Tick}
*/ /**
* This can be either a numeric value or a category string.
* @name Highcharts.AxisLabelsFormatterContextObject#value
* @type {number|string}
*/
/**
* Options for axes.
*
* @typedef {Highcharts.XAxisOptions|Highcharts.YAxisOptions|Highcharts.ZAxisOptions} Highcharts.AxisOptions
*/
/**
* @callback Highcharts.AxisPointBreakEventCallbackFunction
*
* @param {Highcharts.Axis} this
*
* @param {Highcharts.AxisPointBreakEventObject} evt
*/
/**
* @interface Highcharts.AxisPointBreakEventObject
*/ /**
* @name Highcharts.AxisPointBreakEventObject#brk
* @type {Highcharts.Dictionary<number>}
*/ /**
* @name Highcharts.AxisPointBreakEventObject#point
* @type {Highcharts.Point}
*/ /**
* @name Highcharts.AxisPointBreakEventObject#preventDefault
* @type {Function}
*/ /**
* @name Highcharts.AxisPointBreakEventObject#target
* @type {Highcharts.SVGElement}
*/ /**
* @name Highcharts.AxisPointBreakEventObject#type
* @type {"pointBreak"|"pointInBreak"}
*/
/**
* @callback Highcharts.AxisSetExtremesEventCallbackFunction
*
* @param {Highcharts.Axis} this
*
* @param {Highcharts.AxisSetExtremesEventObject} evt
*/
/**
* @interface Highcharts.AxisSetExtremesEventObject
* @extends Highcharts.ExtremesObject
*/ /**
* @name Highcharts.AxisSetExtremesEventObject#preventDefault
* @type {Function}
*/ /**
* @name Highcharts.AxisSetExtremesEventObject#target
* @type {Highcharts.SVGElement}
*/ /**
* @name Highcharts.AxisSetExtremesEventObject#trigger
* @type {Highcharts.AxisExtremesTriggerValue|string}
*/ /**
* @name Highcharts.AxisSetExtremesEventObject#type
* @type {"setExtremes"}
*/
/**
* @callback Highcharts.AxisTickPositionerCallbackFunction
*
* @param {Highcharts.Axis} this
*
* @return {Highcharts.AxisTickPositionsArray}
*/
/**
* @interface Highcharts.AxisTickPositionsArray
* @augments Array<number>
*/
/**
* @typedef {"high"|"low"|"middle"} Highcharts.AxisTitleAlignValue
*/
/**
* @typedef {Highcharts.XAxisTitleOptions|Highcharts.YAxisTitleOptions|Highcharts.ZAxisTitleOptions} Highcharts.AxisTitleOptions
*/
/**
* @typedef {"linear"|"logarithmic"|"datetime"|"category"|"treegrid"} Highcharts.AxisTypeValue
*/
/**
* The returned object literal from the {@link Highcharts.Axis#getExtremes}
* function.
*
* @interface Highcharts.ExtremesObject
*/ /**
* The maximum value of the axis' associated series.
* @name Highcharts.ExtremesObject#dataMax
* @type {number}
*/ /**
* The minimum value of the axis' associated series.
* @name Highcharts.ExtremesObject#dataMin
* @type {number}
*/ /**
* The maximum axis value, either automatic or set manually. If the `max` option
* is not set, `maxPadding` is 0 and `endOnTick` is false, this value will be
* the same as `dataMax`.
* @name Highcharts.ExtremesObject#max
* @type {number}
*/ /**
* The minimum axis value, either automatic or set manually. If the `min` option
* is not set, `minPadding` is 0 and `startOnTick` is false, this value will be
* the same as `dataMin`.
* @name Highcharts.ExtremesObject#min
* @type {number}
*/ /**
* The user defined maximum, either from the `max` option or from a zoom or
* `setExtremes` action.
* @name Highcharts.ExtremesObject#userMax
* @type {number|undefined}
*/ /**
* The user defined minimum, either from the `min` option or from a zoom or
* `setExtremes` action.
* @name Highcharts.ExtremesObject#userMin
* @type {number|undefined}
*/
/**
* Formatter function for the text of a crosshair label.
*
* @callback Highcharts.XAxisCrosshairLabelFormatterCallbackFunction
*
* @param {Highcharts.Axis} this
* Axis context
*
* @param {number} value
* Y value of the data point
*
* @return {string}
*/
''; // Keeps doclets above in JS file
;// ./code/es5/es-modules/Core/Axis/DateTimeAxis.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var DateTimeAxis_addEvent = Core_Utilities.addEvent, DateTimeAxis_getMagnitude = Core_Utilities.getMagnitude, DateTimeAxis_normalizeTickInterval = Core_Utilities.normalizeTickInterval, DateTimeAxis_timeUnits = Core_Utilities.timeUnits;
/* *
*
* Composition
*
* */
/* eslint-disable valid-jsdoc */
var DateTimeAxis;
(function (DateTimeAxis) {
/* *
*
* Declarations
*
* */
/* *
*
* Functions
*
* */
/**
* Extends axis class with date and time support.
* @private
*/
function compose(AxisClass) {
if (!AxisClass.keepProps.includes('dateTime')) {
AxisClass.keepProps.push('dateTime');
var axisProto = AxisClass.prototype;
axisProto.getTimeTicks = getTimeTicks;
DateTimeAxis_addEvent(AxisClass, 'afterSetType', onAfterSetType);
}
return AxisClass;
}
DateTimeAxis.compose = compose;
/**
* Set the tick positions to a time unit that makes sense, for example
* on the first of each month or on every Monday. Return an array with
* the time positions. Used in datetime axes as well as for grouping
* data on a datetime axis.
*
* @private
* @function Highcharts.Axis#getTimeTicks
* @param {Highcharts.TimeNormalizeObject} 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.
*/
function getTimeTicks() {
return this.chart.time.getTimeTicks.apply(this.chart.time, arguments);
}
/**
* @private
*/
function onAfterSetType() {
if (this.type !== 'datetime') {
this.dateTime = void 0;
return;
}
if (!this.dateTime) {
this.dateTime = new Additions(this);
}
}
/* *
*
* Classes
*
* */
var Additions = /** @class */ (function () {
/* *
*
* Constructors
*
* */
function Additions(axis) {
this.axis = axis;
}
/* *
*
* Functions
*
* */
/**
* Get a normalized tick interval for dates. Returns a configuration
* object with unit range (interval), count and name. Used to prepare
* data for `getTimeTicks`. Previously this logic was part of
* getTimeTicks, but as `getTimeTicks` now runs of segments in stock
* charts, the normalizing logic was extracted in order to prevent it
* for running over again for each segment having the same interval.
* #662, #697.
* @private
*/
Additions.prototype.normalizeTimeTickInterval = function (tickInterval, unitsOption) {
var units = (unitsOption || [[
// Unit name
'millisecond',
// Allowed multiples
[1, 2, 5, 10, 20, 25, 50, 100, 200, 500]
],
[
'second',
[1, 2, 5, 10, 15, 30]
],
[
'minute',
[1, 2, 5, 10, 15, 30]
],
[
'hour',
[1, 2, 3, 4, 6, 8, 12]
],
[
'day',
[1, 2]
],
[
'week',
[1, 2]
],
[
'month',
[1, 2, 3, 4, 6]
],
[
'year',
null
]]);
var unit = units[units.length - 1], // Default unit is years
interval = DateTimeAxis_timeUnits[unit[0]],
multiples = unit[1],
i;
// Loop through the units to find the one that best fits the
// tickInterval
for (i = 0; i < units.length; i++) {
unit = units[i];
interval = DateTimeAxis_timeUnits[unit[0]];
multiples = unit[1];
if (units[i + 1]) {
// `lessThan` is in the middle between the highest multiple
// and the next unit.
var lessThan = (interval *
multiples[multiples.length - 1] +
DateTimeAxis_timeUnits[units[i + 1][0]]) / 2;
// Break and keep the current unit
if (tickInterval <= lessThan) {
break;
}
}
}
// Prevent 2.5 years intervals, though 25, 250 etc. are allowed
if (interval === DateTimeAxis_timeUnits.year && tickInterval < 5 * interval) {
multiples = [1, 2, 5];
}
// Get the count
var count = DateTimeAxis_normalizeTickInterval(tickInterval / interval, multiples, unit[0] === 'year' ? // #1913, #2360
Math.max(DateTimeAxis_getMagnitude(tickInterval / interval), 1) :
1);
return {
unitRange: interval,
count: count,
unitName: unit[0]
};
};
/**
* Get the best date format for a specific X value based on the closest
* point range on the axis.
*
* @private
*/
Additions.prototype.getXDateFormat = function (x, dateTimeLabelFormats) {
var axis = this.axis,
time = axis.chart.time;
return axis.closestPointRange ?
time.getDateFormat(axis.closestPointRange, x, axis.options.startOfWeek, dateTimeLabelFormats) ||
// #2546, 2581
time.resolveDTLFormat(dateTimeLabelFormats.year).main :
time.resolveDTLFormat(dateTimeLabelFormats.day).main;
};
return Additions;
}());
DateTimeAxis.Additions = Additions;
})(DateTimeAxis || (DateTimeAxis = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Axis_DateTimeAxis = (DateTimeAxis);
;// ./code/es5/es-modules/Core/Axis/LogarithmicAxis.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var LogarithmicAxis_addEvent = Core_Utilities.addEvent, LogarithmicAxis_normalizeTickInterval = Core_Utilities.normalizeTickInterval, LogarithmicAxis_pick = Core_Utilities.pick;
/* *
*
* Class
*
* */
/**
* @private
*/
var LogarithmicAxis;
(function (LogarithmicAxis) {
/* *
*
* Declarations
*
* */
/* *
*
* Functions
*
* */
/**
* Provides logarithmic support for axes.
* @private
*/
function compose(AxisClass) {
if (!AxisClass.keepProps.includes('logarithmic')) {
AxisClass.keepProps.push('logarithmic');
LogarithmicAxis_addEvent(AxisClass, 'afterSetType', onAfterSetType);
LogarithmicAxis_addEvent(AxisClass, 'afterInit', onAfterInit);
}
return AxisClass;
}
LogarithmicAxis.compose = compose;
/**
* @private
*/
function onAfterSetType() {
var _a;
if (this.type !== 'logarithmic') {
this.logarithmic = void 0;
}
else {
(_a = this.logarithmic) !== null && _a !== void 0 ? _a : (this.logarithmic = new Additions(this));
}
}
/**
* @private
*/
function onAfterInit() {
var axis = this;
var log = axis.logarithmic;
// Extend logarithmic axis
if (log) {
axis.lin2val = function (num) {
return log.lin2log(num);
};
axis.val2lin = function (num) {
return log.log2lin(num);
};
}
}
/* *
*
* Class
*
* */
/**
* Provides logarithmic support for axes.
* @private
* @class
*/
var Additions = /** @class */ (function () {
/* *
*
* Constructors
*
* */
function Additions(axis) {
this.axis = axis;
}
/* *
*
* Functions
*
* */
/**
* Set the tick positions of a logarithmic axis.
*/
Additions.prototype.getLogTickPositions = function (interval, min, max, minor) {
var log = this;
var axis = log.axis;
var axisLength = axis.len;
var options = axis.options;
// Since we use this method for both major and minor ticks,
// use a local variable and return the result
var positions = [];
// Reset
if (!minor) {
log.minorAutoInterval = void 0;
}
// First case: All ticks fall on whole logarithms: 1, 10, 100 etc.
if (interval >= 0.5) {
interval = Math.round(interval);
positions = axis.getLinearTickPositions(interval, min, max);
// Second case: We need intermediary ticks. For example
// 1, 2, 4, 6, 8, 10, 20, 40 etc.
}
else if (interval >= 0.08) {
var roundedMin = Math.floor(min);
var intermediate = void 0,
i = void 0,
j = void 0,
len = void 0,
pos = void 0,
lastPos = void 0,
break2 = void 0;
if (interval > 0.3) {
intermediate = [1, 2, 4];
// 0.2 equals five minor ticks per 1, 10, 100 etc
}
else if (interval > 0.15) {
intermediate = [1, 2, 4, 6, 8];
}
else { // 0.1 equals ten minor ticks per 1, 10, 100 etc
intermediate = [1, 2, 3, 4, 5, 6, 7, 8, 9];
}
for (i = roundedMin; i < max + 1 && !break2; i++) {
len = intermediate.length;
for (j = 0; j < len && !break2; j++) {
pos = log.log2lin(log.lin2log(i) * intermediate[j]);
// #1670, lastPos is #3113
if (pos > min &&
(!minor || lastPos <= max) &&
typeof lastPos !== 'undefined') {
positions.push(lastPos);
}
if (lastPos > max) {
break2 = true;
}
lastPos = pos;
}
}
// Third case: We are so deep in between whole logarithmic values,
// that we might as well handle the tick positions like a linear
// axis. For example 1.01, 1.02, 1.03, 1.04.
}
else {
var realMin = log.lin2log(min),
realMax = log.lin2log(max),
tickIntervalOption = minor ?
axis.getMinorTickInterval() :
options.tickInterval,
filteredTickIntervalOption = tickIntervalOption === 'auto' ?
null :
tickIntervalOption,
tickPixelIntervalOption = options.tickPixelInterval / (minor ? 5 : 1),
totalPixelLength = minor ?
axisLength / axis.tickPositions.length :
axisLength;
interval = LogarithmicAxis_pick(filteredTickIntervalOption, log.minorAutoInterval, (realMax - realMin) *
tickPixelIntervalOption / (totalPixelLength || 1));
interval = LogarithmicAxis_normalizeTickInterval(interval);
positions = axis.getLinearTickPositions(interval, realMin, realMax).map(log.log2lin);
if (!minor) {
log.minorAutoInterval = interval / 5;
}
}
// Set the axis-level tickInterval variable
if (!minor) {
axis.tickInterval = interval;
}
return positions;
};
Additions.prototype.lin2log = function (num) {
return Math.pow(10, num);
};
Additions.prototype.log2lin = function (num) {
return Math.log(num) / Math.LN10;
};
return Additions;
}());
LogarithmicAxis.Additions = Additions;
})(LogarithmicAxis || (LogarithmicAxis = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Axis_LogarithmicAxis = (LogarithmicAxis);
;// ./code/es5/es-modules/Core/Axis/PlotLineOrBand/PlotLineOrBandAxis.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var PlotLineOrBandAxis_erase = Core_Utilities.erase, PlotLineOrBandAxis_extend = Core_Utilities.extend, PlotLineOrBandAxis_isNumber = Core_Utilities.isNumber;
/* *
*
* Composition
*
* */
var PlotLineOrBandAxis;
(function (PlotLineOrBandAxis) {
/* *
*
* Declarations
*
* */
/* *
*
* Variables
*
* */
var PlotLineOrBandClass;
/* *
*
* Functions
*
* */
/**
* Add a plot band after render time.
*
* @sample highcharts/members/axis-addplotband/
* Toggle the plot band from a button
*
* @function Highcharts.Axis#addPlotBand
*
* @param {Highcharts.AxisPlotBandsOptions} options
* A configuration object for the plot band, as defined in
* [xAxis.plotBands](https://api.highcharts.com/highcharts/xAxis.plotBands).
*
* @return {Highcharts.PlotLineOrBand|undefined}
* The added plot band, or `undefined` if the options are not valid.
*/
function addPlotBand(options) {
return this.addPlotBandOrLine(options, 'plotBands');
}
/**
* Add a plot band or plot line after render time. Called from
* addPlotBand and addPlotLine internally.
*
* @private
* @function Highcharts.Axis#addPlotBandOrLine
* @param {Highcharts.AxisPlotBandsOptions|Highcharts.AxisPlotLinesOptions} options
* The plotBand or plotLine configuration object.
*/
function addPlotBandOrLine(options, coll) {
var _this = this;
var userOptions = this.userOptions;
var obj = new PlotLineOrBandClass(this,
options);
if (this.visible) {
obj = obj.render();
}
if (obj) { // #2189
if (!this._addedPlotLB) {
this._addedPlotLB = true;
(userOptions.plotLines || [])
.concat(userOptions.plotBands || [])
.forEach(function (plotLineOptions) {
_this.addPlotBandOrLine(plotLineOptions);
});
}
// Add it to the user options for exporting and Axis.update
if (coll) {
// Workaround Microsoft/TypeScript issue #32693
var updatedOptions = (userOptions[coll] || []);
updatedOptions.push(options);
userOptions[coll] = updatedOptions;
}
this.plotLinesAndBands.push(obj);
}
return obj;
}
/**
* Add a plot line after render time.
*
* @sample highcharts/members/axis-addplotline/
* Toggle the plot line from a button
*
* @function Highcharts.Axis#addPlotLine
*
* @param {Highcharts.AxisPlotLinesOptions} options
* A configuration object for the plot line, as defined in
* [xAxis.plotLines](https://api.highcharts.com/highcharts/xAxis.plotLines).
*
* @return {Highcharts.PlotLineOrBand|undefined}
* The added plot line, or `undefined` if the options are not valid.
*/
function addPlotLine(options) {
return this.addPlotBandOrLine(options, 'plotLines');
}
/**
* @private
*/
function compose(PlotLineOrBandType, AxisClass) {
var axisProto = AxisClass.prototype;
if (!axisProto.addPlotBand) {
PlotLineOrBandClass = PlotLineOrBandType;
PlotLineOrBandAxis_extend(axisProto, {
addPlotBand: addPlotBand,
addPlotLine: addPlotLine,
addPlotBandOrLine: addPlotBandOrLine,
getPlotBandPath: getPlotBandPath,
removePlotBand: removePlotBand,
removePlotLine: removePlotLine,
removePlotBandOrLine: removePlotBandOrLine
});
}
return AxisClass;
}
PlotLineOrBandAxis.compose = compose;
/**
* Internal function to create the SVG path definition for a plot band.
*
* @function Highcharts.Axis#getPlotBandPath
*
* @param {number} from
* The axis value to start from.
*
* @param {number} to
* The axis value to end on.
*
* @param {Highcharts.AxisPlotBandsOptions|Highcharts.AxisPlotLinesOptions} options
* The plotBand or plotLine configuration object.
*
* @return {Highcharts.SVGPathArray}
* The SVG path definition in array form.
*/
function getPlotBandPath(from, to, options) {
options = options || this.options;
var toPath = this.getPlotLinePath({
value: to,
force: true,
acrossPanes: options.acrossPanes
}),
result = [],
horiz = this.horiz,
outside = !PlotLineOrBandAxis_isNumber(this.min) ||
!PlotLineOrBandAxis_isNumber(this.max) ||
(from < this.min && to < this.min) ||
(from > this.max && to > this.max),
path = this.getPlotLinePath({
value: from,
force: true,
acrossPanes: options.acrossPanes
});
var i,
// #4964 check if chart is inverted or plotband is on yAxis
plus = 1,
isFlat;
if (path && toPath) {
// Flat paths don't need labels (#3836)
if (outside) {
isFlat = path.toString() === toPath.toString();
plus = 0;
}
// Go over each subpath - for panes in Highcharts Stock
for (i = 0; i < path.length; i += 2) {
var pathStart = path[i],
pathEnd = path[i + 1],
toPathStart = toPath[i],
toPathEnd = toPath[i + 1];
// Type checking all affected path segments. Consider
// something smarter.
if ((pathStart[0] === 'M' || pathStart[0] === 'L') &&
(pathEnd[0] === 'M' || pathEnd[0] === 'L') &&
(toPathStart[0] === 'M' || toPathStart[0] === 'L') &&
(toPathEnd[0] === 'M' || toPathEnd[0] === 'L')) {
// Add 1 pixel when coordinates are the same
if (horiz && toPathStart[1] === pathStart[1]) {
toPathStart[1] += plus;
toPathEnd[1] += plus;
}
else if (!horiz && toPathStart[2] === pathStart[2]) {
toPathStart[2] += plus;
toPathEnd[2] += plus;
}
result.push(['M', pathStart[1], pathStart[2]], ['L', pathEnd[1], pathEnd[2]], ['L', toPathEnd[1], toPathEnd[2]], ['L', toPathStart[1], toPathStart[2]], ['Z']);
}
result.isFlat = isFlat;
}
}
return result;
}
/**
* Remove a plot band by its id.
*
* @sample highcharts/members/axis-removeplotband/
* Remove plot band by id
* @sample highcharts/members/axis-addplotband/
* Toggle the plot band from a button
*
* @function Highcharts.Axis#removePlotBand
*
* @param {string} id
* The plot band's `id` as given in the original configuration
* object or in the `addPlotBand` option.
*/
function removePlotBand(id) {
this.removePlotBandOrLine(id);
}
/**
* Remove a plot band or plot line from the chart by id. Called
* internally from `removePlotBand` and `removePlotLine`.
* @private
* @function Highcharts.Axis#removePlotBandOrLine
*/
function removePlotBandOrLine(id) {
var plotLinesAndBands = this.plotLinesAndBands,
options = this.options,
userOptions = this.userOptions;
if (plotLinesAndBands) { // #15639
var i_1 = plotLinesAndBands.length;
while (i_1--) {
if (plotLinesAndBands[i_1].id === id) {
plotLinesAndBands[i_1].destroy();
}
}
([
options.plotLines || [],
userOptions.plotLines || [],
options.plotBands || [],
userOptions.plotBands || []
]).forEach(function (arr) {
i_1 = arr.length;
while (i_1--) {
if ((arr[i_1] || {}).id === id) {
PlotLineOrBandAxis_erase(arr, arr[i_1]);
}
}
});
}
}
/**
* Remove a plot line by its id.
*
* @sample highcharts/xaxis/plotlines-id/
* Remove plot line by id
* @sample highcharts/members/axis-addplotline/
* Toggle the plot line from a button
*
* @function Highcharts.Axis#removePlotLine
*
* @param {string} id
* The plot line's `id` as given in the original configuration
* object or in the `addPlotLine` option.
*/
function removePlotLine(id) {
this.removePlotBandOrLine(id);
}
})(PlotLineOrBandAxis || (PlotLineOrBandAxis = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var PlotLineOrBand_PlotLineOrBandAxis = (PlotLineOrBandAxis);
;// ./code/es5/es-modules/Core/Axis/PlotLineOrBand/PlotLineOrBand.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var PlotLineOrBand_assign = (undefined && undefined.__assign) || function () {
PlotLineOrBand_assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return PlotLineOrBand_assign.apply(this, arguments);
};
var PlotLineOrBand_addEvent = Core_Utilities.addEvent, PlotLineOrBand_arrayMax = Core_Utilities.arrayMax, PlotLineOrBand_arrayMin = Core_Utilities.arrayMin, PlotLineOrBand_defined = Core_Utilities.defined, PlotLineOrBand_destroyObjectProperties = Core_Utilities.destroyObjectProperties, PlotLineOrBand_erase = Core_Utilities.erase, PlotLineOrBand_fireEvent = Core_Utilities.fireEvent, PlotLineOrBand_merge = Core_Utilities.merge, PlotLineOrBand_objectEach = Core_Utilities.objectEach, PlotLineOrBand_pick = Core_Utilities.pick;
/* *
*
* Class
*
* */
/**
* The object wrapper for plot lines and plot bands
*
* @class
* @name Highcharts.PlotLineOrBand
*
* @param {Highcharts.Axis} axis
* Related axis.
*
* @param {Highcharts.AxisPlotLinesOptions|Highcharts.AxisPlotBandsOptions} [options]
* Options to use.
*/
var PlotLineOrBand = /** @class */ (function () {
/* *
*
* Constructor
*
* */
function PlotLineOrBand(axis, options) {
/**
* Related axis.
*
* @name Highcharts.PlotLineOrBand#axis
* @type {Highcharts.Axis}
*/
this.axis = axis;
/**
* Options of the plot line or band.
*
* @name Highcharts.PlotLineOrBand#options
* @type {AxisPlotBandsOptions|AxisPlotLinesOptions}
*/
this.options = options;
this.id = options.id;
}
/* *
*
* Static Functions
*
* */
PlotLineOrBand.compose = function (ChartClass, AxisClass) {
PlotLineOrBand_addEvent(ChartClass, 'afterInit', function () {
var _this = this;
this.labelCollectors.push(function () {
var _a;
var labels = [];
for (var _i = 0, _b = _this.axes; _i < _b.length; _i++) {
var axis = _b[_i];
for (var _c = 0, _d = axis.plotLinesAndBands; _c < _d.length; _c++) {
var _e = _d[_c],
label = _e.label,
options = _e.options;
if (label && !((_a = options === null || options === void 0 ? void 0 : options.label) === null || _a === void 0 ? void 0 : _a.allowOverlap)) {
labels.push(label);
}
}
}
return labels;
});
});
return PlotLineOrBand_PlotLineOrBandAxis.compose(PlotLineOrBand, AxisClass);
};
/* *
*
* Functions
*
* */
/* eslint-disable no-invalid-this, valid-jsdoc */
/**
* Render the plot line or plot band. If it is already existing,
* move it.
* @private
* @function Highcharts.PlotLineOrBand#render
*/
PlotLineOrBand.prototype.render = function () {
var _this = this;
var _a,
_b,
_c;
PlotLineOrBand_fireEvent(this, 'render');
var _d = this,
axis = _d.axis,
options = _d.options,
horiz = axis.horiz,
logarithmic = axis.logarithmic,
color = options.color,
events = options.events,
_e = options.zIndex,
zIndex = _e === void 0 ? 0 : _e,
_f = axis.chart,
renderer = _f.renderer,
time = _f.time,
groupAttribs = {},
// These properties only exist on either band or line
to = time.parse(options.to),
from = time.parse(options.from),
value = time.parse(options.value),
borderWidth = options.borderWidth;
var optionsLabel = options.label,
_g = this,
label = _g.label,
svgElem = _g.svgElem,
path = [],
group;
var isBand = PlotLineOrBand_defined(from) && PlotLineOrBand_defined(to),
isLine = PlotLineOrBand_defined(value),
isNew = !svgElem,
attribs = {
'class': 'highcharts-plot-' + (isBand ? 'band ' : 'line ') +
(options.className || '')
};
var groupName = isBand ? 'bands' : 'lines';
// Set the presentational attributes
if (!axis.chart.styledMode) {
if (isLine) {
attribs.stroke = color || "#999999" /* Palette.neutralColor40 */;
attribs['stroke-width'] = PlotLineOrBand_pick(options.width, 1);
if (options.dashStyle) {
attribs.dashstyle = options.dashStyle;
}
}
else if (isBand) { // Plot band
attribs.fill = color || "#e6e9ff" /* Palette.highlightColor10 */;
if (borderWidth) {
attribs.stroke = options.borderColor;
attribs['stroke-width'] = borderWidth;
}
}
}
// Grouping and zIndex
groupAttribs.zIndex = zIndex;
groupName += '-' + zIndex;
group = axis.plotLinesAndBandsGroups[groupName];
if (!group) {
axis.plotLinesAndBandsGroups[groupName] = group =
renderer.g('plot-' + groupName)
.attr(groupAttribs).add();
}
// Create the path
if (!svgElem) {
/**
* SVG element of the plot line or band.
*
* @name Highcharts.PlotLineOrBand#svgElem
* @type {Highcharts.SVGElement}
*/
this.svgElem = svgElem = renderer
.path()
.attr(attribs)
.add(group);
}
// Set the path or return
if (PlotLineOrBand_defined(value)) { // Plot line
path = axis.getPlotLinePath({
value: (_a = logarithmic === null || logarithmic === void 0 ? void 0 : logarithmic.log2lin(value)) !== null && _a !== void 0 ? _a : value,
lineWidth: svgElem.strokeWidth(),
acrossPanes: options.acrossPanes
});
}
else if (PlotLineOrBand_defined(from) && PlotLineOrBand_defined(to)) { // Plot band
path = axis.getPlotBandPath((_b = logarithmic === null || logarithmic === void 0 ? void 0 : logarithmic.log2lin(from)) !== null && _b !== void 0 ? _b : from, (_c = logarithmic === null || logarithmic === void 0 ? void 0 : logarithmic.log2lin(to)) !== null && _c !== void 0 ? _c : to, options);
}
else {
return;
}
// Common for lines and bands. Add events only if they were not added
// before.
if (!this.eventsAdded && events) {
PlotLineOrBand_objectEach(events, function (event, eventType) {
svgElem === null || svgElem === void 0 ? void 0 : svgElem.on(eventType, function (e) {
events[eventType].apply(_this, [e]);
});
});
this.eventsAdded = true;
}
if ((isNew || !svgElem.d) && (path === null || path === void 0 ? void 0 : path.length)) {
svgElem.attr({ d: path });
}
else if (svgElem) {
if (path) {
svgElem.show();
svgElem.animate({ d: path });
}
else if (svgElem.d) {
svgElem.hide();
if (label) {
this.label = label = label.destroy();
}
}
}
// The plot band/line label
if (optionsLabel &&
(PlotLineOrBand_defined(optionsLabel.text) || PlotLineOrBand_defined(optionsLabel.formatter)) &&
(path === null || path === void 0 ? void 0 : path.length) &&
axis.width > 0 &&
axis.height > 0 &&
!path.isFlat) {
// Apply defaults
optionsLabel = PlotLineOrBand_merge(PlotLineOrBand_assign({ align: horiz && isBand ? 'center' : void 0, x: horiz ? !isBand && 4 : 10, verticalAlign: !horiz && isBand ? 'middle' : void 0, y: horiz ? isBand ? 16 : 10 : isBand ? 6 : -4, rotation: horiz && !isBand ? 90 : 0 }, (isBand ? { inside: true } : {})), optionsLabel);
this.renderLabel(optionsLabel, path, isBand, zIndex);
// Move out of sight
}
else if (label) {
label.hide();
}
// Chainable
return this;
};
/**
* Render and align label for plot line or band.
* @private
* @function Highcharts.PlotLineOrBand#renderLabel
*/
PlotLineOrBand.prototype.renderLabel = function (optionsLabel, path, isBand, zIndex) {
var _a;
var plotLine = this,
axis = plotLine.axis,
renderer = axis.chart.renderer,
inside = optionsLabel.inside;
var label = plotLine.label;
// Add the SVG element
if (!label) {
/**
* SVG element of the label.
*
* @name Highcharts.PlotLineOrBand#label
* @type {Highcharts.SVGElement}
*/
plotLine.label = label = renderer
.text(this.getLabelText(optionsLabel), 0, 0, optionsLabel.useHTML)
.attr({
align: optionsLabel.textAlign || optionsLabel.align,
rotation: optionsLabel.rotation,
'class': 'highcharts-plot-' + (isBand ? 'band' : 'line') +
'-label ' + (optionsLabel.className || ''),
zIndex: zIndex
});
if (!axis.chart.styledMode) {
label.css(PlotLineOrBand_merge({
fontSize: '0.8em',
textOverflow: (isBand && !inside) ? '' : 'ellipsis'
}, optionsLabel.style));
}
label.add();
}
// Get the bounding box and align the label
// #3000 changed to better handle choice between plotband or plotline
var xBounds = path.xBounds ||
[path[0][1],
path[1][1], (isBand ? path[2][1] : path[0][1])],
yBounds = path.yBounds ||
[path[0][2],
path[1][2], (isBand ? path[2][2] : path[0][2])],
x = PlotLineOrBand_arrayMin(xBounds),
y = PlotLineOrBand_arrayMin(yBounds),
bBoxWidth = PlotLineOrBand_arrayMax(xBounds) - x;
label.align(optionsLabel, false, {
x: x,
y: y,
width: bBoxWidth,
height: PlotLineOrBand_arrayMax(yBounds) - y
});
if (!label.alignValue ||
label.alignValue === 'left' ||
PlotLineOrBand_defined(inside)) {
label.css({
width: (((_a = optionsLabel.style) === null || _a === void 0 ? void 0 : _a.width) || ((!isBand ||
!inside) ? (label.rotation === 90 ?
axis.height - (label.alignAttr.y -
axis.top) : (optionsLabel.clip ?
axis.width :
axis.chart.chartWidth) - (label.alignAttr.x - axis.left)) :
bBoxWidth)) + 'px'
});
}
label.show(true);
};
/**
* Get label's text content.
* @private
* @function Highcharts.PlotLineOrBand#getLabelText
*/
PlotLineOrBand.prototype.getLabelText = function (optionsLabel) {
return PlotLineOrBand_defined(optionsLabel.formatter) ?
optionsLabel.formatter
.call(this) :
optionsLabel.text;
};
/**
* Remove the plot line or band.
*
* @function Highcharts.PlotLineOrBand#destroy
*/
PlotLineOrBand.prototype.destroy = function () {
// Remove it from the lookup
PlotLineOrBand_erase(this.axis.plotLinesAndBands, this);
delete this.axis;
PlotLineOrBand_destroyObjectProperties(this);
};
return PlotLineOrBand;
}());
/* *
*
* Default Export
*
* */
/* harmony default export */ var PlotLineOrBand_PlotLineOrBand = (PlotLineOrBand);
/* *
*
* API Options
*
* */
/**
* Options for plot bands on axes.
*
* @typedef {Highcharts.XAxisPlotBandsOptions|Highcharts.YAxisPlotBandsOptions|Highcharts.ZAxisPlotBandsOptions} Highcharts.AxisPlotBandsOptions
*/
/**
* Options for plot band labels on axes.
*
* @typedef {Highcharts.XAxisPlotBandsLabelOptions|Highcharts.YAxisPlotBandsLabelOptions|Highcharts.ZAxisPlotBandsLabelOptions} Highcharts.AxisPlotBandsLabelOptions
*/
/**
* Options for plot lines on axes.
*
* @typedef {Highcharts.XAxisPlotLinesOptions|Highcharts.YAxisPlotLinesOptions|Highcharts.ZAxisPlotLinesOptions} Highcharts.AxisPlotLinesOptions
*/
/**
* Options for plot line labels on axes.
*
* @typedef {Highcharts.XAxisPlotLinesLabelOptions|Highcharts.YAxisPlotLinesLabelOptions|Highcharts.ZAxisPlotLinesLabelOptions} Highcharts.AxisPlotLinesLabelOptions
*/
('');
/* *
*
* API Options
*
* */
/**
* An array of colored bands stretching across the plot area marking an
* interval on the axis.
*
* In styled mode, the plot bands are styled by the `.highcharts-plot-band`
* class in addition to the `className` option.
*
* @productdesc {highcharts}
* In a gauge, a plot band on the Y axis (value axis) will stretch along the
* perimeter of the gauge.
*
* @type {Array<*>}
* @product highcharts highstock gantt
* @apioption xAxis.plotBands
*/
/**
* Flag to decide if plotBand should be rendered across all panes.
*
* @since 7.1.2
* @product highstock
* @type {boolean}
* @default true
* @apioption xAxis.plotBands.acrossPanes
*/
/**
* Border color for the plot band. Also requires `borderWidth` to be set.
*
* @type {Highcharts.ColorString}
* @apioption xAxis.plotBands.borderColor
*/
/**
* Border radius for the plot band. Applies only to gauges. Can be a pixel
* value or a percentage, for example `50%`.
*
* @type {number|string}
* @since 11.4.2
* @sample {highcharts} highcharts/xaxis/plotbands-gauge-borderradius
* Angular gauge with rounded plot bands
* @apioption xAxis.plotBands.borderRadius
*/
/**
* Border width for the plot band. Also requires `borderColor` to be set.
*
* @type {number}
* @default 0
* @apioption xAxis.plotBands.borderWidth
*/
/**
* A custom class name, in addition to the default `highcharts-plot-band`,
* to apply to each individual band.
*
* @type {string}
* @since 5.0.0
* @apioption xAxis.plotBands.className
*/
/**
* The color of the plot band.
*
* @sample {highcharts} highcharts/xaxis/plotbands-color/
* Color band
* @sample {highstock} stock/xaxis/plotbands/
* Plot band on Y axis
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @default ${palette.highlightColor10}
* @apioption xAxis.plotBands.color
*/
/**
* An object defining mouse events for the plot band. Supported properties
* are `click`, `mouseover`, `mouseout`, `mousemove`.
*
* @sample {highcharts} highcharts/xaxis/plotbands-events/
* Mouse events demonstrated
*
* @since 1.2
* @apioption xAxis.plotBands.events
*/
/**
* Click event on a plot band.
*
* @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
* @apioption xAxis.plotBands.events.click
*/
/**
* Mouse move event on a plot band.
*
* @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
* @apioption xAxis.plotBands.events.mousemove
*/
/**
* Mouse out event on the corner of a plot band.
*
* @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
* @apioption xAxis.plotBands.events.mouseout
*/
/**
* Mouse over event on a plot band.
*
* @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
* @apioption xAxis.plotBands.events.mouseover
*/
/**
* The start position of the plot band in axis units.
*
* On datetime axes, the value can be given as a timestamp or a date string.
*
* @sample {highcharts} highcharts/xaxis/plotbands-color/
* Datetime axis
* @sample {highcharts} highcharts/xaxis/plotbands-from/
* Categorized axis
* @sample {highstock} stock/xaxis/plotbands/
* Plot band on Y axis
*
* @type {number|string}
* @apioption xAxis.plotBands.from
*/
/**
* An id used for identifying the plot band in Axis.removePlotBand.
*
* @sample {highcharts} highcharts/xaxis/plotbands-id/
* Remove plot band by id
* @sample {highstock} highcharts/xaxis/plotbands-id/
* Remove plot band by id
*
* @type {string}
* @apioption xAxis.plotBands.id
*/
/**
* The end position of the plot band in axis units.
*
* On datetime axes, the value can be given as a timestamp or a date string.
*
* @sample {highcharts} highcharts/xaxis/plotbands-color/
* Datetime axis
* @sample {highcharts} highcharts/xaxis/plotbands-from/
* Categorized axis
* @sample {highstock} stock/xaxis/plotbands/
* Plot band on Y axis
*
* @type {number|string}
* @apioption xAxis.plotBands.to
*/
/**
* The z index of the plot band within the chart, relative to other
* elements. Using the same z index as another element may give
* unpredictable results, as the last rendered element will be on top.
* Values from 0 to 20 make sense.
*
* @sample {highcharts} highcharts/xaxis/plotbands-color/
* Behind plot lines by default
* @sample {highcharts} highcharts/xaxis/plotbands-zindex/
* Above plot lines
* @sample {highcharts} highcharts/xaxis/plotbands-zindex-above-series/
* Above plot lines and series
*
* @type {number}
* @since 1.2
* @apioption xAxis.plotBands.zIndex
*/
/**
* Text labels for the plot bands
*
* @product highcharts highstock gantt
* @apioption xAxis.plotBands.label
*/
/**
* Horizontal alignment of the label. Can be one of "left", "center" or
* "right".
*
* @sample {highcharts} highcharts/xaxis/plotbands-label-align/
* Aligned to the right
* @sample {highstock} stock/xaxis/plotbands-label/
* Plot band with labels
*
* @type {Highcharts.AlignValue}
* @default center
* @since 2.1
* @apioption xAxis.plotBands.label.align
*/
/**
* Whether or not the label can be hidden if it overlaps with another label.
*
* @sample {highcharts} highcharts/xaxis/plotbands-label-allowoverlap/
* A Plotband label overlapping another
*
* @type {boolean}
* @default undefined
* @since 11.4.8
* @apioption xAxis.plotBands.label.allowOverlap
*/
/**
* Wether or not the text of the label can exceed the width of the label.
*
* @type {boolean}
* @product highcharts highstock gantt
* @sample {highcharts} highcharts/xaxis/plotbands-label-textwidth/
* Displaying text with text-wrapping/ellipsis, or the full text.
*
* @default true
* @since 11.4.6
* @apioption xAxis.plotBands.label.inside
*/
/**
* Rotation of the text label in degrees .
*
* @sample {highcharts} highcharts/xaxis/plotbands-label-rotation/
* Vertical text
*
* @type {number}
* @default 0
* @since 2.1
* @apioption xAxis.plotBands.label.rotation
*/
/**
* CSS styles for the text label.
*
* In styled mode, the labels are styled by the
* `.highcharts-plot-band-label` class.
*
* @sample {highcharts} highcharts/xaxis/plotbands-label-style/
* Blue and bold label
*
* @type {Highcharts.CSSObject}
* @since 2.1
* @apioption xAxis.plotBands.label.style
*/
/**
* The string text itself. A subset of HTML is supported.
*
* @type {string}
* @since 2.1
* @apioption xAxis.plotBands.label.text
*/
/**
* The text alignment for the label. While `align` determines where the
* texts anchor point is placed within the plot band, `textAlign` determines
* how the text is aligned against its anchor point. Possible values are
* "left", "center" and "right". Defaults to the same as the `align` option.
*
* @sample {highcharts} highcharts/xaxis/plotbands-label-rotation/
* Vertical text in center position but text-aligned left
*
* @type {Highcharts.AlignValue}
* @since 2.1
* @apioption xAxis.plotBands.label.textAlign
*/
/**
* Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
* to render the labels.
*
* @type {boolean}
* @default false
* @since 3.0.3
* @apioption xAxis.plotBands.label.useHTML
*/
/**
* Vertical alignment of the label relative to the plot band. Can be one of
* "top", "middle" or "bottom".
*
* @sample {highcharts} highcharts/xaxis/plotbands-label-verticalalign/
* Vertically centered label
* @sample {highstock} stock/xaxis/plotbands-label/
* Plot band with labels
*
* @type {Highcharts.VerticalAlignValue}
* @default top
* @since 2.1
* @apioption xAxis.plotBands.label.verticalAlign
*/
/**
* Horizontal position relative the alignment. Default varies by
* orientation.
*
* @sample {highcharts} highcharts/xaxis/plotbands-label-align/
* Aligned 10px from the right edge
* @sample {highstock} stock/xaxis/plotbands-label/
* Plot band with labels
*
* @type {number}
* @since 2.1
* @apioption xAxis.plotBands.label.x
*/
/**
* Vertical position of the text baseline relative to the alignment. Default
* varies by orientation.
*
* @sample {highcharts} highcharts/xaxis/plotbands-label-y/
* Label on x axis
* @sample {highstock} stock/xaxis/plotbands-label/
* Plot band with labels
*
* @type {number}
* @since 2.1
* @apioption xAxis.plotBands.label.y
*/
/**
* An array of lines stretching across the plot area, marking a specific
* value on one of the axes.
*
* In styled mode, the plot lines are styled by the
* `.highcharts-plot-line` class in addition to the `className` option.
*
* @type {Array<*>}
* @product highcharts highstock gantt
* @sample {highcharts} highcharts/xaxis/plotlines-color/
* Basic plot line
* @sample {highcharts} highcharts/series-solidgauge/labels-auto-aligned/
* Solid gauge plot line
* @apioption xAxis.plotLines
*/
/**
* Flag to decide if plotLine should be rendered across all panes.
*
* @sample {highstock} stock/xaxis/plotlines-acrosspanes/
* Plot lines on different panes
*
* @since 7.1.2
* @product highstock
* @type {boolean}
* @default true
* @apioption xAxis.plotLines.acrossPanes
*/
/**
* A custom class name, in addition to the default `highcharts-plot-line`,
* to apply to each individual line.
*
* @type {string}
* @since 5.0.0
* @apioption xAxis.plotLines.className
*/
/**
* The color of the line.
*
* @sample {highcharts} highcharts/xaxis/plotlines-color/
* A red line from X axis
* @sample {highstock} stock/xaxis/plotlines/
* Plot line on Y axis
*
* @type {Highcharts.ColorString}
* @default ${palette.neutralColor40}
* @apioption xAxis.plotLines.color
*/
/**
* The dashing or dot style for the plot line. For possible values see
* [this overview](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
*
* @sample {highcharts} highcharts/xaxis/plotlines-dashstyle/
* Dash and dot pattern
* @sample {highstock} stock/xaxis/plotlines/
* Plot line on Y axis
*
* @type {Highcharts.DashStyleValue}
* @default Solid
* @since 1.2
* @apioption xAxis.plotLines.dashStyle
*/
/**
* An object defining mouse events for the plot line. Supported
* properties are `click`, `mouseover`, `mouseout`, `mousemove`.
*
* @sample {highcharts} highcharts/xaxis/plotlines-events/
* Mouse events demonstrated
*
* @since 1.2
* @apioption xAxis.plotLines.events
*/
/**
* Click event on a plot band.
*
* @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
* @apioption xAxis.plotLines.events.click
*/
/**
* Mouse move event on a plot band.
*
* @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
* @apioption xAxis.plotLines.events.mousemove
*/
/**
* Mouse out event on the corner of a plot band.
*
* @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
* @apioption xAxis.plotLines.events.mouseout
*/
/**
* Mouse over event on a plot band.
*
* @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
* @apioption xAxis.plotLines.events.mouseover
*/
/**
* An id used for identifying the plot line in Axis.removePlotLine.
*
* @sample {highcharts} highcharts/xaxis/plotlines-id/
* Remove plot line by id
*
* @type {string}
* @apioption xAxis.plotLines.id
*/
/**
* The position of the line in axis units.
*
* On datetime axes, the value can be given as a timestamp or a date string.
*
* @sample {highcharts} highcharts/xaxis/plotlines-color/
* Between two categories on X axis
* @sample {highstock} stock/xaxis/plotlines/
* Plot line on Y axis
*
* @type {number|string}
* @apioption xAxis.plotLines.value
*/
/**
* The width or thickness of the plot line.
*
* @sample {highcharts} highcharts/xaxis/plotlines-color/
* 2px wide line from X axis
* @sample {highstock} stock/xaxis/plotlines/
* Plot line on Y axis
*
* @type {number}
* @default 2
* @apioption xAxis.plotLines.width
*/
/**
* The z index of the plot line within the chart.
*
* @sample {highcharts} highcharts/xaxis/plotlines-zindex-behind/
* Behind plot lines by default
* @sample {highcharts} highcharts/xaxis/plotlines-zindex-above/
* Above plot lines
* @sample {highcharts} highcharts/xaxis/plotlines-zindex-above-all/
* Above plot lines and series
*
* @type {number}
* @since 1.2
* @apioption xAxis.plotLines.zIndex
*/
/**
* Text labels for the plot bands
*
* @apioption xAxis.plotLines.label
*/
/**
* Horizontal alignment of the label. Can be one of "left", "center" or
* "right".
*
* @sample {highcharts} highcharts/xaxis/plotlines-label-align-right/
* Aligned to the right
* @sample {highstock} stock/xaxis/plotlines/
* Plot line on Y axis
*
* @type {Highcharts.AlignValue}
* @default left
* @since 2.1
* @apioption xAxis.plotLines.label.align
*/
/**
* Whether to hide labels that are outside the plot area.
*
* @type {boolean}
* @default false
* @since 10.3.3
* @apioption xAxis.plotLines.labels.clip
*/
/**
* Callback JavaScript function to format the label. Useful properties like
* the value of plot line or the range of plot band (`from` & `to`
* properties) can be found in `this.options` object.
*
* @sample {highcharts} highcharts/xaxis/plotlines-plotbands-label-formatter
* Label formatters for plot line and plot band.
* @type {Highcharts.FormatterCallbackFunction<Highcharts.PlotLineOrBand>}
* @apioption xAxis.plotLines.label.formatter
*/
/**
* Rotation of the text label in degrees. Defaults to 0 for horizontal plot
* lines and 90 for vertical lines.
*
* @sample {highcharts} highcharts/xaxis/plotlines-label-verticalalign-middle/
* Slanted text
*
* @type {number}
* @since 2.1
* @apioption xAxis.plotLines.label.rotation
*/
/**
* CSS styles for the text label.
*
* In styled mode, the labels are styled by the
* `.highcharts-plot-line-label` class.
*
* @sample {highcharts} highcharts/xaxis/plotlines-label-style/
* Blue and bold label
*
* @type {Highcharts.CSSObject}
* @since 2.1
* @apioption xAxis.plotLines.label.style
*/
/**
* The text itself. A subset of HTML is supported.
*
* @type {string}
* @since 2.1
* @apioption xAxis.plotLines.label.text
*/
/**
* The text alignment for the label. While `align` determines where the
* texts anchor point is placed within the plot band, `textAlign` determines
* how the text is aligned against its anchor point. Possible values are
* "left", "center" and "right". Defaults to the same as the `align` option.
*
* @sample {highcharts} highcharts/xaxis/plotlines-label-textalign/
* Text label in bottom position
*
* @type {Highcharts.AlignValue}
* @since 2.1
* @apioption xAxis.plotLines.label.textAlign
*/
/**
* Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
* to render the labels.
*
* @type {boolean}
* @default false
* @since 3.0.3
* @apioption xAxis.plotLines.label.useHTML
*/
/**
* Vertical alignment of the label relative to the plot line. Can be
* one of "top", "middle" or "bottom".
*
* @sample {highcharts} highcharts/xaxis/plotlines-label-verticalalign-middle/
* Vertically centered label
*
* @type {Highcharts.VerticalAlignValue}
* @default {highcharts} top
* @default {highstock} top
* @since 2.1
* @apioption xAxis.plotLines.label.verticalAlign
*/
/**
* Horizontal position relative the alignment. Default varies by
* orientation.
*
* @sample {highcharts} highcharts/xaxis/plotlines-label-align-right/
* Aligned 10px from the right edge
* @sample {highstock} stock/xaxis/plotlines/
* Plot line on Y axis
*
* @type {number}
* @since 2.1
* @apioption xAxis.plotLines.label.x
*/
/**
* Vertical position of the text baseline relative to the alignment. Default
* varies by orientation.
*
* @sample {highcharts} highcharts/xaxis/plotlines-label-y/
* Label below the plot line
* @sample {highstock} stock/xaxis/plotlines/
* Plot line on Y axis
*
* @type {number}
* @since 2.1
* @apioption xAxis.plotLines.label.y
*/
/**
* @type {Array<*>}
* @extends xAxis.plotBands
* @apioption yAxis.plotBands
*/
/**
* In a gauge chart, this option determines the inner radius of the
* plot band that stretches along the perimeter. It can be given as
* a percentage string, like `"100%"`, or as a pixel number, like `100`.
* By default, the inner radius is controlled by the [thickness](
* #yAxis.plotBands.thickness) option.
*
* @sample {highcharts} highcharts/xaxis/plotbands-gauge
* Gauge plot band
*
* @type {number|string}
* @since 2.3
* @product highcharts
* @apioption yAxis.plotBands.innerRadius
*/
/**
* In a gauge chart, this option determines the outer radius of the
* plot band that stretches along the perimeter. It can be given as
* a percentage string, like `"100%"`, or as a pixel number, like `100`.
*
* @sample {highcharts} highcharts/xaxis/plotbands-gauge
* Gauge plot band
*
* @type {number|string}
* @default 100%
* @since 2.3
* @product highcharts
* @apioption yAxis.plotBands.outerRadius
*/
/**
* In a gauge chart, this option sets the width of the plot band
* stretching along the perimeter. It can be given as a percentage
* string, like `"10%"`, or as a pixel number, like `10`. The default
* value 10 is the same as the default [tickLength](#yAxis.tickLength),
* thus making the plot band act as a background for the tick markers.
*
* @sample {highcharts} highcharts/xaxis/plotbands-gauge
* Gauge plot band
*
* @type {number|string}
* @default 10
* @since 2.3
* @product highcharts
* @apioption yAxis.plotBands.thickness
*/
/**
* @type {Array<*>}
* @extends xAxis.plotLines
* @apioption yAxis.plotLines
*/
(''); // Keeps doclets above in JS file
;// ./code/es5/es-modules/Core/Tooltip.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var Tooltip_animObject = AnimationUtilities.animObject;
var Tooltip_format = Core_Templating.format;
var Tooltip_composed = Core_Globals.composed, dateFormats = Core_Globals.dateFormats, Tooltip_doc = Core_Globals.doc, isSafari = Core_Globals.isSafari;
var distribute = Renderer_RendererUtilities.distribute;
var Tooltip_addEvent = Core_Utilities.addEvent, Tooltip_clamp = Core_Utilities.clamp, Tooltip_css = Core_Utilities.css, Tooltip_discardElement = Core_Utilities.discardElement, Tooltip_extend = Core_Utilities.extend, Tooltip_fireEvent = Core_Utilities.fireEvent, Tooltip_isArray = Core_Utilities.isArray, Tooltip_isNumber = Core_Utilities.isNumber, Tooltip_isObject = Core_Utilities.isObject, Tooltip_isString = Core_Utilities.isString, Tooltip_merge = Core_Utilities.merge, Tooltip_pick = Core_Utilities.pick, Tooltip_pushUnique = Core_Utilities.pushUnique, Tooltip_splat = Core_Utilities.splat, Tooltip_syncTimeout = Core_Utilities.syncTimeout;
/* *
*
* Class
*
* */
/* eslint-disable no-invalid-this, valid-jsdoc */
/**
* Tooltip of a chart.
*
* @class
* @name Highcharts.Tooltip
*
* @param {Highcharts.Chart} chart
* The chart instance.
*
* @param {Highcharts.TooltipOptions} options
* Tooltip options.
*
* @param {Highcharts.Pointer} pointer
* The pointer instance.
*/
var Tooltip = /** @class */ (function () {
/* *
*
* Constructors
*
* */
function Tooltip(chart, options, pointer) {
/* *
*
* Properties
*
* */
this.allowShared = true;
this.crosshairs = [];
this.distance = 0;
this.isHidden = true;
this.isSticky = false;
this.options = {};
this.outside = false;
this.chart = chart;
this.init(chart, options);
this.pointer = pointer;
}
/* *
*
* Functions
*
* */
/**
* Build the body (lines) of the tooltip by iterating over the items and
* returning one entry for each item, abstracting this functionality allows
* to easily overwrite and extend it.
*
* @private
* @function Highcharts.Tooltip#bodyFormatter
*/
Tooltip.prototype.bodyFormatter = function (points) {
return points.map(function (point) {
var tooltipOptions = point.series.tooltipOptions,
formatPrefix = point.formatPrefix || 'point';
return (tooltipOptions[formatPrefix + 'Formatter'] ||
point.tooltipFormatter).call(point, tooltipOptions[formatPrefix + 'Format'] || '');
});
};
/**
* Destroy the single tooltips in a split tooltip.
* If the tooltip is active then it is not destroyed, unless forced to.
*
* @private
* @function Highcharts.Tooltip#cleanSplit
*
* @param {boolean} [force]
* Force destroy all tooltips.
*/
Tooltip.prototype.cleanSplit = function (force) {
this.chart.series.forEach(function (series) {
var tt = series && series.tt;
if (tt) {
if (!tt.isActive || force) {
series.tt = tt.destroy();
}
else {
tt.isActive = false;
}
}
});
};
/**
* In case no user defined formatter is given, this will be used. Note that
* the context here is an object holding point, series, x, y etc.
*
* @function Highcharts.Tooltip#defaultFormatter
*
* @param {Highcharts.Tooltip} tooltip
*
* @return {string|Array<string>}
* Returns a string (single tooltip and shared)
* or an array of strings (split tooltip)
*/
Tooltip.prototype.defaultFormatter = function (tooltip) {
var hoverPoints = this.points || Tooltip_splat(this);
var s;
// Build the header
s = [tooltip.headerFooterFormatter(hoverPoints[0])];
// Build the values
s = s.concat(tooltip.bodyFormatter(hoverPoints));
// Footer
s.push(tooltip.headerFooterFormatter(hoverPoints[0], true));
return s;
};
/**
* Removes and destroys the tooltip and its elements.
*
* @function Highcharts.Tooltip#destroy
*/
Tooltip.prototype.destroy = function () {
// Destroy and clear local variables
if (this.label) {
this.label = this.label.destroy();
}
if (this.split) {
this.cleanSplit(true);
if (this.tt) {
this.tt = this.tt.destroy();
}
}
if (this.renderer) {
this.renderer = this.renderer.destroy();
Tooltip_discardElement(this.container);
}
Core_Utilities.clearTimeout(this.hideTimer);
};
/**
* Extendable method to get the anchor position of the tooltip
* from a point or set of points
*
* @private
* @function Highcharts.Tooltip#getAnchor
*/
Tooltip.prototype.getAnchor = function (points, mouseEvent) {
var _a = this,
chart = _a.chart,
pointer = _a.pointer,
inverted = chart.inverted,
plotTop = chart.plotTop,
plotLeft = chart.plotLeft;
var ret;
points = Tooltip_splat(points);
// If reversedStacks are false the tooltip position should be taken from
// the last point (#17948)
if (points[0].series &&
points[0].series.yAxis &&
!points[0].series.yAxis.options.reversedStacks) {
points = points.slice().reverse();
}
// When tooltip follows mouse, relate the position to the mouse
if (this.followPointer && mouseEvent) {
if (typeof mouseEvent.chartX === 'undefined') {
mouseEvent = pointer.normalize(mouseEvent);
}
ret = [
mouseEvent.chartX - plotLeft,
mouseEvent.chartY - plotTop
];
// Some series types use a specificly calculated tooltip position for
// each point
}
else if (points[0].tooltipPos) {
ret = points[0].tooltipPos;
// Calculate the average position and adjust for axis positions
}
else {
var chartX_1 = 0,
chartY_1 = 0;
points.forEach(function (point) {
var pos = point.pos(true);
if (pos) {
chartX_1 += pos[0];
chartY_1 += pos[1];
}
});
chartX_1 /= points.length;
chartY_1 /= points.length;
// When shared, place the tooltip next to the mouse (#424)
if (this.shared && points.length > 1 && mouseEvent) {
if (inverted) {
chartX_1 = mouseEvent.chartX;
}
else {
chartY_1 = mouseEvent.chartY;
}
}
// Use the average position for multiple points
ret = [chartX_1 - plotLeft, chartY_1 - plotTop];
}
return ret.map(Math.round);
};
/**
* Get the CSS class names for the tooltip's label. Styles the label
* by `colorIndex` or user-defined CSS.
*
* @function Highcharts.Tooltip#getClassName
*
* @return {string}
* The class names.
*/
Tooltip.prototype.getClassName = function (point, isSplit, isHeader) {
var options = this.options,
series = point.series,
seriesOptions = series.options;
return [
options.className,
'highcharts-label',
isHeader && 'highcharts-tooltip-header',
isSplit ? 'highcharts-tooltip-box' : 'highcharts-tooltip',
!isHeader && 'highcharts-color-' + Tooltip_pick(point.colorIndex, series.colorIndex),
(seriesOptions && seriesOptions.className)
].filter(Tooltip_isString).join(' ');
};
/**
* Creates the Tooltip label element if it does not exist, then returns it.
*
* @function Highcharts.Tooltip#getLabel
*
* @return {Highcharts.SVGElement}
* Tooltip label
*/
Tooltip.prototype.getLabel = function (_a) {
var _b = _a === void 0 ? { anchorX: 0,
anchorY: 0 } : _a,
anchorX = _b.anchorX,
anchorY = _b.anchorY;
var tooltip = this,
styledMode = this.chart.styledMode,
options = this.options,
doSplit = this.split && this.allowShared;
var container = this.container,
renderer = this.chart.renderer;
// If changing from a split tooltip to a non-split tooltip, we must
// destroy it in order to get the SVG right. #13868.
if (this.label) {
var wasSplit = !this.label.hasClass('highcharts-label');
if ((!doSplit && wasSplit) || (doSplit && !wasSplit)) {
this.destroy();
}
}
if (!this.label) {
if (this.outside) {
var chart = this.chart,
chartStyle = chart.options.chart.style,
Renderer = Renderer_RendererRegistry.getRendererType();
/**
* Reference to the tooltip's container, when
* [Highcharts.Tooltip#outside] is set to true, otherwise
* it's undefined.
*
* @name Highcharts.Tooltip#container
* @type {Highcharts.HTMLDOMElement|undefined}
*/
this.container = container = Core_Globals.doc.createElement('div');
container.className = ('highcharts-tooltip-container ' +
(chart.renderTo.className.match(/(highcharts[a-zA-Z0-9-]+)\s?/gm) || [].join(' ')));
// We need to set pointerEvents = 'none' as otherwise it makes
// the area under the tooltip non-hoverable even after the
// tooltip disappears, #19035.
Tooltip_css(container, {
position: 'absolute',
top: '1px',
pointerEvents: 'none',
zIndex: Math.max(this.options.style.zIndex || 0, (chartStyle && chartStyle.zIndex || 0) + 3)
});
/**
* Reference to the tooltip's renderer, when
* [Highcharts.Tooltip#outside] is set to true, otherwise
* it's undefined.
*
* @name Highcharts.Tooltip#renderer
* @type {Highcharts.SVGRenderer|undefined}
*/
this.renderer = renderer = new Renderer(container, 0, 0, chartStyle, void 0, void 0, renderer.styledMode);
}
// Create the label
if (doSplit) {
this.label = renderer.g('tooltip');
}
else {
this.label = renderer
.label('', anchorX, anchorY, options.shape, void 0, void 0, options.useHTML, void 0, 'tooltip')
.attr({
padding: options.padding,
r: options.borderRadius
});
if (!styledMode) {
this.label
.attr({
fill: options.backgroundColor,
'stroke-width': options.borderWidth || 0
})
// #2301, #2657
.css(options.style)
.css({
pointerEvents: (options.style.pointerEvents ||
(this.shouldStickOnContact() ? 'auto' : 'none'))
});
}
}
// Split tooltip use updateTooltipContainer to position the tooltip
// container.
if (tooltip.outside) {
var label_1 = this.label;
[label_1.xSetter, label_1.ySetter].forEach(function (setter, i) {
label_1[i ? 'ySetter' : 'xSetter'] = function (value) {
setter.call(label_1, tooltip.distance);
label_1[i ? 'y' : 'x'] = value;
if (container) {
container.style[i ? 'top' : 'left'] = "" + value + "px";
}
};
});
}
this.label
.attr({ zIndex: 8 })
.shadow(options.shadow)
.add();
}
if (container && !container.parentElement) {
Core_Globals.doc.body.appendChild(container);
}
return this.label;
};
/**
* Get the total area available area to place the tooltip
*
* @private
*/
Tooltip.prototype.getPlayingField = function () {
var body = Tooltip_doc.body,
documentElement = Tooltip_doc.documentElement,
_a = this,
chart = _a.chart,
distance = _a.distance,
outside = _a.outside;
return {
width: outside ?
// Subtract distance to prevent scrollbars
Math.max(body.scrollWidth, documentElement.scrollWidth, body.offsetWidth, documentElement.offsetWidth, documentElement.clientWidth) - (2 * distance) - 2 :
chart.chartWidth,
height: outside ?
Math.max(body.scrollHeight, documentElement.scrollHeight, body.offsetHeight, documentElement.offsetHeight, documentElement.clientHeight) :
chart.chartHeight
};
};
/**
* Place the tooltip in a chart without spilling over and not covering the
* point itself.
*
* @function Highcharts.Tooltip#getPosition
*
* @param {number} boxWidth
* Width of the tooltip box.
*
* @param {number} boxHeight
* Height of the tooltip box.
*
* @param {Highcharts.Point} point
* Tooltip related point.
*
* @return {Highcharts.PositionObject}
* Recommended position of the tooltip.
*/
Tooltip.prototype.getPosition = function (boxWidth, boxHeight, point) {
var _a,
_b;
var _c = this, distance = _c.distance, chart = _c.chart, outside = _c.outside, pointer = _c.pointer, inverted = chart.inverted, plotLeft = chart.plotLeft, plotTop = chart.plotTop, polar = chart.polar, _d = point.plotX, plotX = _d === void 0 ? 0 : _d, _e = point.plotY, plotY = _e === void 0 ? 0 : _e, ret = {},
// Don't use h if chart isn't inverted (#7242) ???
h = (inverted && point.h) || 0, // #4117 ???
_f = this.getPlayingField(), outerHeight = _f.height, outerWidth = _f.width, chartPosition = pointer.getChartPosition(), scaleX = function (val) { return (val * chartPosition.scaleX); }, scaleY = function (val) { return (val * chartPosition.scaleY); },
// Build parameter arrays for firstDimension()/secondDimension()
buildDimensionArray = function (dim) {
var isX = dim === 'x';
return [
dim, // Dimension - x or y
isX ? outerWidth : outerHeight,
isX ? boxWidth : boxHeight
].concat(outside ? [
// If we are using tooltip.outside, we need to scale the
// position to match scaling of the container in case there
// is a transform/zoom on the container. #11329
isX ? scaleX(boxWidth) : scaleY(boxHeight),
isX ? chartPosition.left - distance +
scaleX(plotX + plotLeft) :
chartPosition.top - distance +
scaleY(plotY + plotTop),
0,
isX ? outerWidth : outerHeight
] : [
// Not outside, no scaling is needed
isX ? boxWidth : boxHeight,
isX ? plotX + plotLeft : plotY + plotTop,
isX ? plotLeft : plotTop,
isX ? plotLeft + chart.plotWidth :
plotTop + chart.plotHeight
]);
};
var first = buildDimensionArray('y'), second = buildDimensionArray('x'), swapped;
// Handle negative points or reversed axis (#13780)
var flipped = !!point.negative;
if (!polar &&
((_b = (_a = chart.hoverSeries) === null || _a === void 0 ? void 0 : _a.yAxis) === null || _b === void 0 ? void 0 : _b.reversed)) {
flipped = !flipped;
}
// The far side is right or bottom
var preferFarSide = !this.followPointer &&
Tooltip_pick(point.ttBelow,
polar ? false : !inverted === flipped), // #4984
/*
* Handle the preferred dimension. When the preferred dimension is
* tooltip on top or bottom of the point,
it will look for space
* there.
*
* @private
*/
firstDimension = function (dim,
outerSize,
innerSize,
scaledInnerSize, // #11329
point,
min,
max) {
var scaledDist = outside ?
(dim === 'y' ? scaleY(distance) : scaleX(distance)) :
distance,
scaleDiff = (innerSize - scaledInnerSize) / 2,
roomLeft = scaledInnerSize < point - distance,
roomRight = point + distance + scaledInnerSize < outerSize,
alignedLeft = point - scaledDist - innerSize + scaleDiff,
alignedRight = point + scaledDist - scaleDiff;
if (preferFarSide && roomRight) {
ret[dim] = alignedRight;
}
else if (!preferFarSide && roomLeft) {
ret[dim] = alignedLeft;
}
else if (roomLeft) {
ret[dim] = Math.min(max - scaledInnerSize, alignedLeft - h < 0 ? alignedLeft : alignedLeft - h);
}
else if (roomRight) {
ret[dim] = Math.max(min, alignedRight + h + innerSize > outerSize ?
alignedRight :
alignedRight + h);
}
else {
return false;
}
},
/*
* Handle the secondary dimension. If the preferred dimension is
* tooltip on top or bottom of the point, the second dimension is to
* align the tooltip above the point, trying to align center but
* allowing left or right align within the chart box.
*
* @private
*/
secondDimension = function (dim, outerSize, innerSize, scaledInnerSize, // #11329
point) {
// Too close to the edge, return false and swap dimensions
if (point < distance || point > outerSize - distance) {
return false;
}
// Align left/top
if (point < innerSize / 2) {
ret[dim] = 1;
// Align right/bottom
}
else if (point > outerSize - scaledInnerSize / 2) {
ret[dim] = outerSize - scaledInnerSize - 2;
// Align center
}
else {
ret[dim] = point - innerSize / 2;
}
},
/*
* Swap the dimensions
*/
swap = function (count) {
var _a;
_a = [second, first], first = _a[0], second = _a[1];
swapped = count;
}, run = function () {
if (firstDimension.apply(0, first) !== false) {
if (secondDimension.apply(0, second) === false &&
!swapped) {
swap(true);
run();
}
}
else if (!swapped) {
swap(true);
run();
}
else {
ret.x = ret.y = 0;
}
};
// Under these conditions, prefer the tooltip on the side of the point
if ((inverted && !polar) || this.len > 1) {
swap();
}
run();
return ret;
};
/**
* Hides the tooltip with a fade out animation.
*
* @function Highcharts.Tooltip#hide
*
* @param {number} [delay]
* The fade out in milliseconds. If no value is provided the value
* of the tooltip.hideDelay option is used. A value of 0 disables
* the fade out animation.
*/
Tooltip.prototype.hide = function (delay) {
var tooltip = this;
// Disallow duplicate timers (#1728, #1766)
Core_Utilities.clearTimeout(this.hideTimer);
delay = Tooltip_pick(delay, this.options.hideDelay);
if (!this.isHidden) {
this.hideTimer = Tooltip_syncTimeout(function () {
var label = tooltip.getLabel();
// If there is a delay, fade out with the default duration. If
// the hideDelay is 0, we assume no animation is wanted, so we
// pass 0 duration. #12994.
tooltip.getLabel().animate({
opacity: 0
}, {
duration: delay ? 150 : delay,
complete: function () {
// #3088, assuming we're only using this for tooltips
label.hide();
// Clear the container for outside tooltip (#18490)
if (tooltip.container) {
tooltip.container.remove();
}
}
});
tooltip.isHidden = true;
}, delay);
}
};
/**
* Initialize tooltip.
*
* @private
* @function Highcharts.Tooltip#init
*
* @param {Highcharts.Chart} chart
* The chart instance.
*
* @param {Highcharts.TooltipOptions} options
* Tooltip options.
*/
Tooltip.prototype.init = function (chart, options) {
/**
* Chart of the tooltip.
*
* @readonly
* @name Highcharts.Tooltip#chart
* @type {Highcharts.Chart}
*/
this.chart = chart;
/**
* Used tooltip options.
*
* @readonly
* @name Highcharts.Tooltip#options
* @type {Highcharts.TooltipOptions}
*/
this.options = options;
/**
* List of crosshairs.
*
* @private
* @readonly
* @name Highcharts.Tooltip#crosshairs
* @type {Array<null>}
*/
this.crosshairs = [];
/**
* Tooltips are initially hidden.
*
* @private
* @readonly
* @name Highcharts.Tooltip#isHidden
* @type {boolean}
*/
this.isHidden = true;
/**
* True, if the tooltip is split into one label per series, with the
* header close to the axis.
*
* @readonly
* @name Highcharts.Tooltip#split
* @type {boolean|undefined}
*/
this.split = options.split && !chart.inverted && !chart.polar;
/**
* When the tooltip is shared, the entire plot area will capture mouse
* movement or touch events.
*
* @readonly
* @name Highcharts.Tooltip#shared
* @type {boolean|undefined}
*/
this.shared = options.shared || this.split;
/**
* 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.
*
* @readonly
* @name Highcharts.Tooltip#outside
* @type {boolean}
*
* @todo
* Split tooltip does not support outside in the first iteration. Should
* not be too complicated to implement.
*/
this.outside = Tooltip_pick(options.outside, Boolean(chart.scrollablePixelsX || chart.scrollablePixelsY));
};
Tooltip.prototype.shouldStickOnContact = function (pointerEvent) {
return !!(!this.followPointer &&
this.options.stickOnContact &&
(!pointerEvent || this.pointer.inClass(pointerEvent.target, 'highcharts-tooltip')));
};
/**
* Moves the tooltip with a soft animation to a new position.
*
* @private
* @function Highcharts.Tooltip#move
*
* @param {number} x
*
* @param {number} y
*
* @param {number} anchorX
*
* @param {number} anchorY
*/
Tooltip.prototype.move = function (x, y, anchorX, anchorY) {
var tooltip = this,
animation = Tooltip_animObject(!tooltip.isHidden && tooltip.options.animation),
skipAnchor = tooltip.followPointer || (tooltip.len || 0) > 1,
attr = { x: x,
y: y };
if (!skipAnchor) {
attr.anchorX = anchorX;
attr.anchorY = anchorY;
}
animation.step = function () { return tooltip.drawTracker(); };
tooltip.getLabel().animate(attr, animation);
};
/**
* Refresh the tooltip's text and position.
*
* @function Highcharts.Tooltip#refresh
*
* @param {Highcharts.Point|Array<Highcharts.Point>} pointOrPoints
* Either a point or an array of points.
*
* @param {Highcharts.PointerEventObject} [mouseEvent]
* Mouse event, that is responsible for the refresh and should be
* used for the tooltip update.
*/
Tooltip.prototype.refresh = function (pointOrPoints, mouseEvent) {
var tooltip = this,
_a = this,
chart = _a.chart,
options = _a.options,
pointer = _a.pointer,
shared = _a.shared,
points = Tooltip_splat(pointOrPoints),
point = points[0],
formatString = options.format,
formatter = options.formatter || tooltip.defaultFormatter,
styledMode = chart.styledMode;
var wasShared = tooltip.allowShared;
if (!options.enabled || !point.series) { // #16820
return;
}
Core_Utilities.clearTimeout(this.hideTimer);
// A switch saying if this specific tooltip configuration allows shared
// or split modes
tooltip.allowShared = !(!Tooltip_isArray(pointOrPoints) &&
pointOrPoints.series &&
pointOrPoints.series.noSharedTooltip);
wasShared = wasShared && !tooltip.allowShared;
// Get the reference point coordinates (pie charts use tooltipPos)
tooltip.followPointer = (!tooltip.split && point.series.tooltipOptions.followPointer);
var anchor = tooltip.getAnchor(pointOrPoints,
mouseEvent),
x = anchor[0],
y = anchor[1];
// Shared tooltip, array is sent over
if (shared && tooltip.allowShared) {
pointer.applyInactiveState(points);
// Now set hover state for the chosen ones:
points.forEach(function (item) { return item.setState('hover'); });
point.points = points;
}
this.len = points.length; // #6128
var text = Tooltip_isString(formatString) ?
Tooltip_format(formatString,
point,
chart) :
formatter.call(point,
tooltip);
// Reset the preliminary circular references
point.points = void 0;
// Register the current series
var currentSeries = point.series;
this.distance = Tooltip_pick(currentSeries.tooltipOptions.distance, 16);
// Update the inner HTML
if (text === false) {
this.hide();
}
else {
// Update text
if (tooltip.split && tooltip.allowShared) { // #13868
this.renderSplit(text, points);
}
else {
var checkX_1 = x;
var checkY_1 = y;
if (mouseEvent && pointer.isDirectTouch) {
checkX_1 = mouseEvent.chartX - chart.plotLeft;
checkY_1 = mouseEvent.chartY - chart.plotTop;
}
// #11493, #13095
if (chart.polar ||
currentSeries.options.clip === false ||
points.some(function (p) {
return pointer.isDirectTouch || // ##17929
p.series.shouldShowTooltip(checkX_1, checkY_1);
})) {
var label = tooltip.getLabel(wasShared && tooltip.tt || {});
// Prevent the tooltip from flowing over the chart box
// (#6659)
if (!options.style.width || styledMode) {
label.css({
width: (this.outside ?
this.getPlayingField() :
chart.spacingBox).width + 'px'
});
}
label.attr({
// Add class before the label BBox calculation (#21035)
'class': tooltip.getClassName(point),
text: text && text.join ?
text.join('') :
text
});
// When the length of the label has increased, immediately
// update the x position to prevent tooltip from flowing
// outside the viewport during animation (#21371)
if (this.outside) {
label.attr({
x: Tooltip_clamp(label.x || 0, 0, this.getPlayingField().width -
(label.width || 0) -
1)
});
}
if (!styledMode) {
label.attr({
stroke: (options.borderColor ||
point.color ||
currentSeries.color ||
"#666666" /* Palette.neutralColor60 */)
});
}
tooltip.updatePosition({
plotX: x,
plotY: y,
negative: point.negative,
ttBelow: point.ttBelow,
h: anchor[2] || 0
});
}
else {
tooltip.hide();
return;
}
}
// Show it
if (tooltip.isHidden && tooltip.label) {
tooltip.label.attr({
opacity: 1
}).show();
}
tooltip.isHidden = false;
}
Tooltip_fireEvent(this, 'refresh');
};
/**
* Render the split tooltip. Loops over each point's text and adds
* a label next to the point, then uses the distribute function to
* find best non-overlapping positions.
*
* @private
* @function Highcharts.Tooltip#renderSplit
*
* @param {string|Array<(boolean|string)>} labels
*
* @param {Array<Highcharts.Point>} points
*/
Tooltip.prototype.renderSplit = function (labels, points) {
var _a;
var tooltip = this;
var chart = tooltip.chart,
_b = tooltip.chart,
chartWidth = _b.chartWidth,
chartHeight = _b.chartHeight,
plotHeight = _b.plotHeight,
plotLeft = _b.plotLeft,
plotTop = _b.plotTop,
_c = _b.scrollablePixelsY,
scrollablePixelsY = _c === void 0 ? 0 : _c,
scrollablePixelsX = _b.scrollablePixelsX,
styledMode = _b.styledMode,
distance = tooltip.distance,
options = tooltip.options,
positioner = tooltip.options.positioner,
pointer = tooltip.pointer;
var _d = ((_a = chart.scrollablePlotArea) === null || _a === void 0 ? void 0 : _a.scrollingContainer) || {},
_e = _d.scrollLeft,
scrollLeft = _e === void 0 ? 0 : _e,
_f = _d.scrollTop,
scrollTop = _f === void 0 ? 0 : _f;
// The area which the tooltip should be limited to. Limit to scrollable
// plot area if enabled, otherwise limit to the chart container. If
// outside is true it should be the whole viewport
var bounds = (tooltip.outside &&
typeof scrollablePixelsX !== 'number') ?
Tooltip_doc.documentElement.getBoundingClientRect() : {
left: scrollLeft,
right: scrollLeft + chartWidth,
top: scrollTop,
bottom: scrollTop + chartHeight
};
var tooltipLabel = tooltip.getLabel();
var ren = this.renderer || chart.renderer;
var headerTop = Boolean(chart.xAxis[0] && chart.xAxis[0].opposite);
var _g = pointer.getChartPosition(),
chartLeft = _g.left,
chartTop = _g.top;
var distributionBoxTop = plotTop + scrollTop;
var headerHeight = 0;
var adjustedPlotHeight = plotHeight - scrollablePixelsY;
/**
* Calculates the anchor position for the partial tooltip
*
* @private
* @param {Highcharts.Point} point The point related to the tooltip
* @return {Object} Returns an object with anchorX and anchorY
*/
function getAnchor(point) {
var isHeader = point.isHeader,
_a = point.plotX,
plotX = _a === void 0 ? 0 : _a,
_b = point.plotY,
plotY = _b === void 0 ? 0 : _b,
series = point.series;
var anchorX;
var anchorY;
if (isHeader) {
// Set anchorX to plotX
anchorX = Math.max(plotLeft + plotX, plotLeft);
// Set anchorY to center of visible plot area.
anchorY = plotTop + plotHeight / 2;
}
else {
var xAxis = series.xAxis,
yAxis = series.yAxis;
// Set anchorX to plotX. Limit to within xAxis.
anchorX = xAxis.pos + Tooltip_clamp(plotX, -distance, xAxis.len + distance);
// Set anchorY, limit to the scrollable plot area
if (series.shouldShowTooltip(0, yAxis.pos - plotTop + plotY, {
ignoreX: true
})) {
anchorY = yAxis.pos + plotY;
}
}
// Limit values to plot area
anchorX = Tooltip_clamp(anchorX, bounds.left - distance, bounds.right + distance);
return { anchorX: anchorX, anchorY: anchorY };
}
/**
* Calculates the position of the partial tooltip
*
* @private
* @param {number} anchorX
* The partial tooltip anchor x position
*
* @param {number} anchorY
* The partial tooltip anchor y position
*
* @param {boolean|undefined} isHeader
* Whether the partial tooltip is a header
*
* @param {number} boxWidth
* Width of the partial tooltip
*
* @return {Highcharts.PositionObject}
* Returns the partial tooltip x and y position
*/
function defaultPositioner(anchorX, anchorY, isHeader, boxWidth, alignedLeft) {
if (alignedLeft === void 0) { alignedLeft = true; }
var y;
var x;
if (isHeader) {
y = headerTop ? 0 : adjustedPlotHeight;
x = Tooltip_clamp(anchorX - (boxWidth / 2), bounds.left, bounds.right - boxWidth - (tooltip.outside ? chartLeft : 0));
}
else {
y = anchorY - distributionBoxTop;
x = alignedLeft ?
anchorX - boxWidth - distance :
anchorX + distance;
x = Tooltip_clamp(x, alignedLeft ? x : bounds.left, bounds.right);
}
// NOTE: y is relative to distributionBoxTop
return { x: x, y: y };
}
/**
* Updates the attributes and styling of the partial tooltip. Creates a
* new partial tooltip if it does not exists.
*
* @private
* @param {Highcharts.SVGElement|undefined} partialTooltip
* The partial tooltip to update
* @param {Highcharts.Point} point
* The point related to the partial tooltip
* @param {boolean|string} str The text for the partial tooltip
* @return {Highcharts.SVGElement} Returns the updated partial tooltip
*/
function updatePartialTooltip(partialTooltip, point, str) {
var _a;
var tt = partialTooltip;
var isHeader = point.isHeader,
series = point.series;
if (!tt) {
var attribs = {
padding: options.padding,
r: options.borderRadius
};
if (!styledMode) {
attribs.fill = options.backgroundColor;
attribs['stroke-width'] = (_a = options.borderWidth) !== null && _a !== void 0 ? _a : 1;
}
tt = ren
.label('', 0, 0, (options[isHeader ? 'headerShape' : 'shape']), void 0, void 0, options.useHTML)
.addClass(tooltip.getClassName(point, true, isHeader))
.attr(attribs)
.add(tooltipLabel);
}
tt.isActive = true;
tt.attr({
text: str
});
if (!styledMode) {
tt.css(options.style)
.attr({
stroke: (options.borderColor ||
point.color ||
series.color ||
"#333333" /* Palette.neutralColor80 */)
});
}
return tt;
}
// Graceful degradation for legacy formatters
if (Tooltip_isString(labels)) {
labels = [false, labels];
}
// Create the individual labels for header and points, ignore footer
var boxes = labels.slice(0,
points.length + 1).reduce(function (boxes,
str,
i) {
if (str !== false && str !== '') {
var point = (points[i - 1] ||
{
// Item 0 is the header. Instead of this, we could also
// use the crosshair label
isHeader: true,
plotX: points[0].plotX,
plotY: plotHeight,
series: {}
});
var isHeader = point.isHeader;
// Store the tooltip label reference on the series
var owner = isHeader ? tooltip : point.series;
var tt = owner.tt = updatePartialTooltip(owner.tt,
point,
str.toString());
// Get X position now, so we can move all to the other side in
// case of overflow
var bBox = tt.getBBox();
var boxWidth = bBox.width + tt.strokeWidth();
if (isHeader) {
headerHeight = bBox.height;
adjustedPlotHeight += headerHeight;
if (headerTop) {
distributionBoxTop -= headerHeight;
}
}
var _a = getAnchor(point),
anchorX = _a.anchorX,
anchorY = _a.anchorY;
if (typeof anchorY === 'number') {
var size = bBox.height + 1;
var boxPosition = (positioner ?
positioner.call(tooltip,
boxWidth,
size,
point) :
defaultPositioner(anchorX,
anchorY,
isHeader,
boxWidth));
boxes.push({
// 0-align to the top, 1-align to the bottom
align: positioner ? 0 : void 0,
anchorX: anchorX,
anchorY: anchorY,
boxWidth: boxWidth,
point: point,
rank: Tooltip_pick(boxPosition.rank, isHeader ? 1 : 0),
size: size,
target: boxPosition.y,
tt: tt,
x: boxPosition.x
});
}
else {
// Hide tooltips which anchorY is outside the visible plot
// area
tt.isActive = false;
}
}
return boxes;
}, []);
// Realign the tooltips towards the right if there is not enough space
// to the left and there is space to the right
if (!positioner && boxes.some(function (box) {
// Always realign if the beginning of a label is outside bounds
var outside = tooltip.outside;
var boxStart = (outside ? chartLeft : 0) + box.anchorX;
if (boxStart < bounds.left &&
boxStart + box.boxWidth < bounds.right) {
return true;
}
// Otherwise, check if there is more space available to the right
return boxStart < (chartLeft - bounds.left) + box.boxWidth &&
bounds.right - boxStart > boxStart;
})) {
boxes = boxes.map(function (box) {
var _a = defaultPositioner(box.anchorX,
box.anchorY,
box.point.isHeader,
box.boxWidth,
false),
x = _a.x,
y = _a.y;
return Tooltip_extend(box, {
target: y,
x: x
});
});
}
// Clean previous run (for missing points)
tooltip.cleanSplit();
// Distribute and put in place
distribute(boxes, adjustedPlotHeight);
var boxExtremes = {
left: chartLeft,
right: chartLeft
};
// Get the extremes from series tooltips
boxes.forEach(function (box) {
var x = box.x,
boxWidth = box.boxWidth,
isHeader = box.isHeader;
if (!isHeader) {
if (tooltip.outside && chartLeft + x < boxExtremes.left) {
boxExtremes.left = chartLeft + x;
}
if (!isHeader &&
tooltip.outside &&
boxExtremes.left + boxWidth > boxExtremes.right) {
boxExtremes.right = chartLeft + x;
}
}
});
boxes.forEach(function (box) {
var x = box.x,
anchorX = box.anchorX,
anchorY = box.anchorY,
pos = box.pos,
isHeader = box.point.isHeader;
var attributes = {
visibility: typeof pos === 'undefined' ? 'hidden' : 'inherit',
x: x,
/* NOTE: y should equal pos to be consistent with !split
* tooltip,
but is currently relative to plotTop. Is left as is
* to avoid breaking change. Remove distributionBoxTop to make
* it consistent.
*/
y: (pos || 0) + distributionBoxTop,
anchorX: anchorX,
anchorY: anchorY
};
// Handle left-aligned tooltips overflowing the chart area
if (tooltip.outside && x < anchorX) {
var offset = chartLeft - boxExtremes.left;
// Skip this if there is no overflow
if (offset > 0) {
if (!isHeader) {
attributes.x = x + offset;
attributes.anchorX = anchorX + offset;
}
if (isHeader) {
attributes.x = (boxExtremes.right - boxExtremes.left) / 2;
attributes.anchorX = anchorX + offset;
}
}
}
// Put the label in place
box.tt.attr(attributes);
});
/* If we have a separate tooltip container, then update the necessary
* container properties.
* Test that tooltip has its own container and renderer before executing
* the operation.
*/
var container = tooltip.container,
outside = tooltip.outside,
renderer = tooltip.renderer;
if (outside && container && renderer) {
// Set container size to fit the bounds
var _h = tooltipLabel.getBBox(),
width = _h.width,
height = _h.height,
x = _h.x,
y = _h.y;
renderer.setSize(width + x, height + y, false);
// Position the tooltip container to the chart container
container.style.left = boxExtremes.left + 'px';
container.style.top = chartTop + 'px';
}
// Workaround for #18927, artefacts left by the shadows of split
// tooltips in Safari v16 (2023). Check again with later versions if we
// can remove this.
if (isSafari) {
tooltipLabel.attr({
// Force a redraw of the whole group by chaining the opacity
// slightly
opacity: tooltipLabel.opacity === 1 ? 0.999 : 1
});
}
};
/**
* If the `stickOnContact` option is active, this will add a tracker shape.
*
* @private
* @function Highcharts.Tooltip#drawTracker
*/
Tooltip.prototype.drawTracker = function () {
var tooltip = this;
if (!this.shouldStickOnContact()) {
if (tooltip.tracker) {
tooltip.tracker = tooltip.tracker.destroy();
}
return;
}
var chart = tooltip.chart;
var label = tooltip.label;
var points = tooltip.shared ? chart.hoverPoints : chart.hoverPoint;
if (!label || !points) {
return;
}
var box = {
x: 0,
y: 0,
width: 0,
height: 0
};
// Combine anchor and tooltip
var anchorPos = this.getAnchor(points);
var labelBBox = label.getBBox();
anchorPos[0] += chart.plotLeft - (label.translateX || 0);
anchorPos[1] += chart.plotTop - (label.translateY || 0);
// When the mouse pointer is between the anchor point and the label,
// the label should stick.
box.x = Math.min(0, anchorPos[0]);
box.y = Math.min(0, anchorPos[1]);
box.width = (anchorPos[0] < 0 ?
Math.max(Math.abs(anchorPos[0]), labelBBox.width - anchorPos[0]) :
Math.max(Math.abs(anchorPos[0]), labelBBox.width));
box.height = (anchorPos[1] < 0 ?
Math.max(Math.abs(anchorPos[1]), labelBBox.height - Math.abs(anchorPos[1])) :
Math.max(Math.abs(anchorPos[1]), labelBBox.height));
if (tooltip.tracker) {
tooltip.tracker.attr(box);
}
else {
tooltip.tracker = label.renderer
.rect(box)
.addClass('highcharts-tracker')
.add(label);
if (!chart.styledMode) {
tooltip.tracker.attr({
fill: 'rgba(0,0,0,0)'
});
}
}
};
/**
* @private
*/
Tooltip.prototype.styledModeFormat = function (formatString) {
return formatString
.replace('style="font-size: 0.8em"', 'class="highcharts-header"')
.replace(/style="color:{(point|series)\.color}"/g, 'class="highcharts-color-{$1.colorIndex} ' +
'{series.options.className} ' +
'{point.options.className}"');
};
/**
* Format the footer/header of the tooltip
* #3397: abstraction to enable formatting of footer and header
*
* @private
* @function Highcharts.Tooltip#headerFooterFormatter
*/
Tooltip.prototype.headerFooterFormatter = function (point, isFooter) {
var series = point.series,
tooltipOptions = series.tooltipOptions,
xAxis = series.xAxis,
dateTime = xAxis && xAxis.dateTime,
e = {
isFooter: isFooter,
point: point
};
var xDateFormat = tooltipOptions.xDateFormat || '', formatString = tooltipOptions[isFooter ? 'footerFormat' : 'headerFormat'];
Tooltip_fireEvent(this, 'headerFormatter', e, function (e) {
// Guess the best date format based on the closest point distance
// (#568, #3418)
if (dateTime && !xDateFormat && Tooltip_isNumber(point.key)) {
xDateFormat = dateTime.getXDateFormat(point.key, tooltipOptions.dateTimeLabelFormats);
}
// Insert the footer date format if any
if (dateTime && xDateFormat) {
if (Tooltip_isObject(xDateFormat)) {
var format_1 = xDateFormat;
dateFormats[0] = function (timestamp) {
return series.chart.time.dateFormat(format_1, timestamp);
};
xDateFormat = '%0';
}
(point.tooltipDateKeys || ['key']).forEach(function (key) {
formatString = formatString.replace(new RegExp('point\\.' + key + '([ \\)}])', ''), "(point.".concat(key, ":").concat(xDateFormat, ")$1"));
});
}
// Replace default header style with class name
if (series.chart.styledMode) {
formatString = this.styledModeFormat(formatString);
}
e.text = Tooltip_format(formatString, point, this.chart);
});
return e.text || '';
};
/**
* Updates the tooltip with the provided tooltip options.
*
* @function Highcharts.Tooltip#update
*
* @param {Highcharts.TooltipOptions} options
* The tooltip options to update.
*/
Tooltip.prototype.update = function (options) {
this.destroy();
this.init(this.chart, Tooltip_merge(true, this.options, options));
};
/**
* Find the new position and perform the move
*
* @private
* @function Highcharts.Tooltip#updatePosition
*
* @param {Highcharts.Point} point
*/
Tooltip.prototype.updatePosition = function (point) {
var _a = this,
chart = _a.chart,
container = _a.container,
distance = _a.distance,
options = _a.options,
pointer = _a.pointer,
renderer = _a.renderer,
_b = this.getLabel(),
_c = _b.height,
height = _c === void 0 ? 0 : _c,
_d = _b.width,
width = _d === void 0 ? 0 : _d,
// Needed for outside: true (#11688)
_e = pointer.getChartPosition(),
left = _e.left,
top = _e.top,
scaleX = _e.scaleX,
scaleY = _e.scaleY,
pos = (options.positioner || this.getPosition).call(this,
width,
height,
point),
doc = Core_Globals.doc;
var anchorX = (point.plotX || 0) + chart.plotLeft,
anchorY = (point.plotY || 0) + chart.plotTop,
pad;
// Set the renderer size dynamically to prevent document size to change.
// Renderer only exists when tooltip is outside.
if (renderer && container) {
// Corrects positions, occurs with tooltip positioner (#16944)
if (options.positioner) {
pos.x += left - distance;
pos.y += top - distance;
}
// Pad it by the border width and distance. Add 2 to make room for
// the default shadow (#19314).
pad = (options.borderWidth || 0) + 2 * distance + 2;
renderer.setSize(
// Clamp width to keep tooltip in viewport (#21698)
// and subtract one since tooltip container has 'left: 1px;'
Tooltip_clamp(width + pad, 0, doc.documentElement.clientWidth) - 1, height + pad, false);
// Anchor and tooltip container need scaling if chart container has
// scale transform/css zoom. #11329.
if (scaleX !== 1 || scaleY !== 1) {
Tooltip_css(container, {
transform: "scale(".concat(scaleX, ", ").concat(scaleY, ")")
});
anchorX *= scaleX;
anchorY *= scaleY;
}
anchorX += left - pos.x;
anchorY += top - pos.y;
}
// Do the move
this.move(Math.round(pos.x), Math.round(pos.y || 0), // Can be undefined (#3977)
anchorX, anchorY);
};
return Tooltip;
}());
/* *
*
* Class namespace
*
* */
(function (Tooltip) {
/* *
*
* Declarations
*
* */
/* *
*
* Functions
*
* */
/**
* @private
*/
function compose(PointerClass) {
if (Tooltip_pushUnique(Tooltip_composed, 'Core.Tooltip')) {
Tooltip_addEvent(PointerClass, 'afterInit', function () {
var chart = this.chart;
if (chart.options.tooltip) {
/**
* Tooltip object for points of series.
*
* @name Highcharts.Chart#tooltip
* @type {Highcharts.Tooltip}
*/
chart.tooltip = new Tooltip(chart, chart.options.tooltip, this);
}
});
}
}
Tooltip.compose = compose;
})(Tooltip || (Tooltip = {}));
/* *
*
* Default export
*
* */
/* harmony default export */ var Core_Tooltip = (Tooltip);
/* *
*
* API Declarations
*
* */
/**
* Callback function to format the text of the tooltip from scratch.
*
* In case of single or shared tooltips, a string should be returned. In case
* of 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.
*
* @callback Highcharts.TooltipFormatterCallbackFunction
*
* @param {Highcharts.Point} this
* The formatter's context is the hovered `Point` instance. In case of shared or
* split tooltips, all points are available in `this.points`.
*
* @param {Highcharts.Tooltip} tooltip
* The tooltip instance
*
* @return {false|string|Array<(string|null|undefined)>|null|undefined}
* Formatted text or false
*/
/**
* A callback function to place the tooltip in a specific position.
*
* @callback Highcharts.TooltipPositionerCallbackFunction
*
* @param {Highcharts.Tooltip} this
* Tooltip context of the callback.
*
* @param {number} labelWidth
* Width of the tooltip.
*
* @param {number} labelHeight
* Height of the tooltip.
*
* @param {Highcharts.TooltipPositionerPointObject} point
* Point information for positioning a tooltip.
*
* @return {Highcharts.PositionObject}
* New position for the tooltip.
*/
/**
* Point information for positioning a tooltip.
*
* @interface Highcharts.TooltipPositionerPointObject
* @extends Highcharts.Point
*/ /**
* If `tooltip.split` option is enabled and positioner is called for each of the
* boxes separately, this property indicates the call on the xAxis header, which
* is not a point itself.
* @name Highcharts.TooltipPositionerPointObject#isHeader
* @type {boolean}
*/ /**
* The reference point relative to the plot area. Add chart.plotLeft to get the
* full coordinates.
* @name Highcharts.TooltipPositionerPointObject#plotX
* @type {number}
*/ /**
* The reference point relative to the plot area. Add chart.plotTop to get the
* full coordinates.
* @name Highcharts.TooltipPositionerPointObject#plotY
* @type {number}
*/
/**
* @typedef {"callout"|"circle"|"rect"} Highcharts.TooltipShapeValue
*/
''; // Keeps doclets above in JS file
;// ./code/es5/es-modules/Core/Series/Point.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var Point_animObject = AnimationUtilities.animObject;
var Point_defaultOptions = Defaults.defaultOptions;
var Point_format = Core_Templating.format;
var Point_addEvent = Core_Utilities.addEvent, Point_crisp = Core_Utilities.crisp, Point_erase = Core_Utilities.erase, Point_extend = Core_Utilities.extend, Point_fireEvent = Core_Utilities.fireEvent, Point_getNestedProperty = Core_Utilities.getNestedProperty, Point_isArray = Core_Utilities.isArray, Point_isFunction = Core_Utilities.isFunction, Point_isNumber = Core_Utilities.isNumber, Point_isObject = Core_Utilities.isObject, Point_merge = Core_Utilities.merge, Point_pick = Core_Utilities.pick, Point_syncTimeout = Core_Utilities.syncTimeout, Point_removeEvent = Core_Utilities.removeEvent, Point_uniqueKey = Core_Utilities.uniqueKey;
/* eslint-disable no-invalid-this, valid-jsdoc */
/* *
*
* Class
*
* */
/**
* The Point object. The point objects are generated from the `series.data`
* configuration objects or raw numbers. They can be accessed from the
* `Series.points` array. Other ways to instantiate points are through {@link
* Highcharts.Series#addPoint} or {@link Highcharts.Series#setData}.
*
* @class
* @name Highcharts.Point
*/
var Point = /** @class */ (function () {
/**
* Initialize the point. Called internally based on the `series.data`
* option.
*
* @function Highcharts.Point#init
*
* @param {Highcharts.Series} series
* The series object containing this point.
*
* @param {Highcharts.PointOptionsType} options
* The data in either number, array or object format.
*
* @param {number} [x]
* Optionally, the X value of the point.
*
* @return {Highcharts.Point}
* The Point instance.
*
* @emits Highcharts.Point#event:afterInit
*/
function Point(series, options, x) {
var _a;
this.formatPrefix = 'point';
this.visible = true;
// For tooltip and data label formatting
this.point = this;
this.series = series;
this.applyOptions(options, x);
// Add a unique ID to the point if none is assigned
(_a = this.id) !== null && _a !== void 0 ? _a : (this.id = Point_uniqueKey());
this.resolveColor();
series.chart.pointCount++;
Point_fireEvent(this, 'afterInit');
}
/**
* For categorized axes this property holds the category name for the
* point. For other axes it holds the X value.
*
* @name Highcharts.Point#category
* @type {number|string}
*/
/**
* The name of the point. The name can be given as the first position of the
* point configuration array, or as a `name` property in the configuration:
*
* @example
* // Array config
* data: [
* ['John', 1],
* ['Jane', 2]
* ]
*
* // Object config
* data: [{
* name: 'John',
* y: 1
* }, {
* name: 'Jane',
* y: 2
* }]
*
* @name Highcharts.Point#name
* @type {string}
*/
/**
* The point's name if it is defined, or its category in case of a category,
* otherwise the x value. Convenient for tooltip and data label formatting.
*
* @name Highcharts.Point#key
* @type {number|string}
*/
/**
* The point's options as applied in the initial configuration, or
* extended through `Point.update`.
*
* In TypeScript you have to extend `PointOptionsObject` via an
* additional interface to allow custom data options:
*
* ```
* declare interface PointOptionsObject {
* customProperty: string;
* }
* ```
*
* @name Highcharts.Point#options
* @type {Highcharts.PointOptionsObject}
*/
/**
* The percentage for points in a stacked series, pies or gauges.
*
* @name Highcharts.Point#percentage
* @type {number|undefined}
*/
/**
* The series object associated with the point.
*
* @name Highcharts.Point#series
* @type {Highcharts.Series}
*/
/**
* The attributes of the rendered SVG shape like in `column` or `pie`
* series.
*
* @readonly
* @name Highcharts.Point#shapeArgs
* @type {Readonly<Highcharts.SVGAttributes>|undefined}
*/
/**
* The total of values in either a stack for stacked series, or a pie in a
* pie series.
*
* @name Highcharts.Point#total
* @type {number|undefined}
*/
/**
* For certain series types, like pie charts, where individual points can
* be shown or hidden.
*
* @name Highcharts.Point#visible
* @type {boolean}
* @default true
*/
/* *
*
* Functions
*
* */
/**
* Animate SVG elements associated with the point.
*
* @private
* @function Highcharts.Point#animateBeforeDestroy
*/
Point.prototype.animateBeforeDestroy = function () {
var point = this,
animateParams = { x: point.startXPos,
opacity: 0 },
graphicalProps = point.getGraphicalProps();
graphicalProps.singular.forEach(function (prop) {
var isDataLabel = prop === 'dataLabel';
point[prop] = point[prop].animate(isDataLabel ? {
x: point[prop].startXPos,
y: point[prop].startYPos,
opacity: 0
} : animateParams);
});
graphicalProps.plural.forEach(function (plural) {
point[plural].forEach(function (item) {
if (item.element) {
item.animate(Point_extend({ x: point.startXPos }, (item.startYPos ? {
x: item.startXPos,
y: item.startYPos
} : {})));
}
});
});
};
/**
* Apply the options containing the x and y data and possible some extra
* properties. Called on point init or from point.update.
*
* @private
* @function Highcharts.Point#applyOptions
*
* @param {Highcharts.PointOptionsType} options
* The point options as defined in series.data.
*
* @param {number} [x]
* Optionally, the x value.
*
* @return {Highcharts.Point}
* The Point instance.
*/
Point.prototype.applyOptions = function (options, x) {
var point = this,
series = point.series,
pointValKey = series.options.pointValKey || series.pointValKey;
options = Point.prototype.optionsToObject.call(this, options);
// Copy options directly to point
Point_extend(point, options);
point.options = point.options ?
Point_extend(point.options, options) :
options;
// Since options are copied into the Point instance, some accidental
// options must be shielded (#5681)
if (options.group) {
delete point.group;
}
if (options.dataLabels) {
delete point.dataLabels;
}
/**
* The y value of the point.
* @name Highcharts.Point#y
* @type {number|undefined}
*/
// For higher dimension series types. For instance, for ranges, point.y
// is mapped to point.low.
if (pointValKey) {
point.y = Point.prototype.getNestedProperty.call(point, pointValKey);
}
// The point is initially selected by options (#5777)
if (point.selected) {
point.state = 'select';
}
/**
* The x value of the point.
* @name Highcharts.Point#x
* @type {number}
*/
// If no x is set by now, get auto incremented value. All points must
// have an x value, however the y value can be null to create a gap in
// the series
if ('name' in point &&
typeof x === 'undefined' &&
series.xAxis &&
series.xAxis.hasNames) {
point.x = series.xAxis.nameToX(point);
}
if (typeof point.x === 'undefined' && series) {
point.x = x !== null && x !== void 0 ? x : series.autoIncrement();
}
else if (Point_isNumber(options.x) && series.options.relativeXValue) {
point.x = series.autoIncrement(options.x);
// If x is a string, try to parse it to a datetime
}
else if (typeof point.x === 'string') {
x !== null && x !== void 0 ? x : (x = series.chart.time.parse(point.x));
if (Point_isNumber(x)) {
point.x = x;
}
}
point.isNull = this.isValid && !this.isValid();
point.formatPrefix = point.isNull ? 'null' : 'point'; // #9233, #10874
return point;
};
/**
* Destroy a point to clear memory. Its reference still stays in
* `series.data`.
*
* @private
* @function Highcharts.Point#destroy
*/
Point.prototype.destroy = function () {
if (!this.destroyed) {
var point_1 = this,
series = point_1.series,
chart = series.chart,
dataSorting = series.options.dataSorting,
hoverPoints = chart.hoverPoints,
globalAnimation = point_1.series.chart.renderer.globalAnimation,
animation = Point_animObject(globalAnimation);
/**
* Allow to call after animation.
* @private
*/
var destroyPoint = function () {
// Remove all events and elements
if (point_1.graphic ||
point_1.graphics ||
point_1.dataLabel ||
point_1.dataLabels) {
Point_removeEvent(point_1);
point_1.destroyElements();
}
for (var prop in point_1) { // eslint-disable-line guard-for-in
delete point_1[prop];
}
};
if (point_1.legendItem) {
// Pies have legend items
chart.legend.destroyItem(point_1);
}
if (hoverPoints) {
point_1.setState();
Point_erase(hoverPoints, point_1);
if (!hoverPoints.length) {
chart.hoverPoints = null;
}
}
if (point_1 === chart.hoverPoint) {
point_1.onMouseOut();
}
// Remove properties after animation
if (!dataSorting || !dataSorting.enabled) {
destroyPoint();
}
else {
this.animateBeforeDestroy();
Point_syncTimeout(destroyPoint, animation.duration);
}
chart.pointCount--;
}
this.destroyed = true;
};
/**
* Destroy SVG elements associated with the point.
*
* @private
* @function Highcharts.Point#destroyElements
* @param {Highcharts.Dictionary<number>} [kinds]
*/
Point.prototype.destroyElements = function (kinds) {
var point = this,
props = point.getGraphicalProps(kinds);
props.singular.forEach(function (prop) {
point[prop] = point[prop].destroy();
});
props.plural.forEach(function (plural) {
point[plural].forEach(function (item) {
if (item && item.element) {
item.destroy();
}
});
delete point[plural];
});
};
/**
* Fire an event on the Point object.
*
* @private
* @function Highcharts.Point#firePointEvent
*
* @param {string} eventType
* Type of the event.
*
* @param {Highcharts.Dictionary<any>|Event} [eventArgs]
* Additional event arguments.
*
* @param {Highcharts.EventCallbackFunction<Highcharts.Point>|Function} [defaultFunction]
* Default event handler.
*
* @emits Highcharts.Point#event:*
*/
Point.prototype.firePointEvent = function (eventType, eventArgs, defaultFunction) {
var point = this,
series = this.series,
seriesOptions = series.options;
// Load event handlers on demand to save time on mouseover/out
point.manageEvent(eventType);
// Add default handler if in selection mode
if (eventType === 'click' && seriesOptions.allowPointSelect) {
defaultFunction = function (event) {
// Control key is for Windows, meta (= Cmd key) for Mac, Shift
// for Opera.
if (!point.destroyed && point.select) { // #2911, #19075
point.select(null, event.ctrlKey || event.metaKey || event.shiftKey);
}
};
}
Point_fireEvent(point, eventType, eventArgs, defaultFunction);
};
/**
* Get the CSS class names for individual points. Used internally where the
* returned value is set on every point.
*
* @function Highcharts.Point#getClassName
*
* @return {string}
* The class names.
*/
Point.prototype.getClassName = function () {
var point = this;
return 'highcharts-point' +
(point.selected ? ' highcharts-point-select' : '') +
(point.negative ? ' highcharts-negative' : '') +
(point.isNull ? ' highcharts-null-point' : '') +
(typeof point.colorIndex !== 'undefined' ?
' highcharts-color-' + point.colorIndex : '') +
(point.options.className ? ' ' + point.options.className : '') +
(point.zone && point.zone.className ? ' ' +
point.zone.className.replace('highcharts-negative', '') : '');
};
/**
* Get props of all existing graphical point elements.
*
* @private
* @function Highcharts.Point#getGraphicalProps
*/
Point.prototype.getGraphicalProps = function (kinds) {
var point = this,
props = [],
graphicalProps = { singular: [],
plural: [] };
var prop,
i;
kinds = kinds || { graphic: 1, dataLabel: 1 };
if (kinds.graphic) {
props.push('graphic', 'connector' // Used by dumbbell
);
}
if (kinds.dataLabel) {
props.push('dataLabel', 'dataLabelPath', 'dataLabelUpper');
}
i = props.length;
while (i--) {
prop = props[i];
if (point[prop]) {
graphicalProps.singular.push(prop);
}
}
[
'graphic',
'dataLabel'
].forEach(function (prop) {
var plural = prop + 's';
if (kinds[prop] && point[plural]) {
graphicalProps.plural.push(plural);
}
});
return graphicalProps;
};
/**
* Returns the value of the point property for a given value.
* @private
*/
Point.prototype.getNestedProperty = function (key) {
if (!key) {
return;
}
if (key.indexOf('custom.') === 0) {
return Point_getNestedProperty(key, this.options);
}
return this[key];
};
/**
* In a series with `zones`, return the zone that the point belongs to.
*
* @function Highcharts.Point#getZone
*
* @return {Highcharts.SeriesZonesOptionsObject}
* The zone item.
*/
Point.prototype.getZone = function () {
var series = this.series,
zones = series.zones,
zoneAxis = series.zoneAxis || 'y';
var zone,
i = 0;
zone = zones[i];
while (this[zoneAxis] >= zone.value) {
zone = zones[++i];
}
// For resetting or reusing the point (#8100)
if (!this.nonZonedColor) {
this.nonZonedColor = this.color;
}
if (zone && zone.color && !this.options.color) {
this.color = zone.color;
}
else {
this.color = this.nonZonedColor;
}
return zone;
};
/**
* Utility to check if point has new shape type. Used in column series and
* all others that are based on column series.
* @private
*/
Point.prototype.hasNewShapeType = function () {
var point = this;
var oldShapeType = point.graphic &&
(point.graphic.symbolName || point.graphic.element.nodeName);
return oldShapeType !== this.shapeType;
};
/**
* Determine if point is valid.
* @private
* @function Highcharts.Point#isValid
*/
Point.prototype.isValid = function () {
return ((Point_isNumber(this.x) ||
this.x instanceof Date) &&
Point_isNumber(this.y));
};
/**
* Transform number or array configs into objects. Also called for object
* configs. Used internally to unify the different configuration formats for
* points. For example, a simple number `10` in a line series will be
* transformed to `{ y: 10 }`, and an array config like `[1, 10]` in a
* scatter series will be transformed to `{ x: 1, y: 10 }`.
*
* @function Highcharts.Point#optionsToObject
*
* @param {Highcharts.PointOptionsType} options
* Series data options.
*
* @return {Highcharts.Dictionary<*>}
* Transformed point options.
*/
Point.prototype.optionsToObject = function (options) {
var _a;
var series = this.series,
keys = series.options.keys,
pointArrayMap = keys || series.pointArrayMap || ['y'],
valueCount = pointArrayMap.length;
var ret = {},
firstItemType,
i = 0,
j = 0;
if (Point_isNumber(options) || options === null) {
ret[pointArrayMap[0]] = options;
}
else if (Point_isArray(options)) {
// With leading x value
if (!keys && options.length > valueCount) {
firstItemType = typeof options[0];
if (firstItemType === 'string') {
if ((_a = series.xAxis) === null || _a === void 0 ? void 0 : _a.dateTime) {
ret.x = series.chart.time.parse(options[0]);
}
else {
ret.name = options[0];
}
}
else if (firstItemType === 'number') {
ret.x = options[0];
}
i++;
}
while (j < valueCount) {
// Skip undefined positions for keys
if (!keys || typeof options[i] !== 'undefined') {
if (pointArrayMap[j].indexOf('.') > 0) {
// Handle nested keys, e.g. ['color.pattern.image']
// Avoid function call unless necessary.
Point.prototype.setNestedProperty(ret, options[i], pointArrayMap[j]);
}
else {
ret[pointArrayMap[j]] = options[i];
}
}
i++;
j++;
}
}
else if (typeof options === 'object') {
ret = options;
// This is the fastest way to detect if there are individual point
// dataLabels that need to be considered in drawDataLabels. These
// can only occur in object configs.
if (options.dataLabels) {
// Override the prototype function to always return true,
// regardless of whether data labels are enabled series-wide
series.hasDataLabels = function () { return true; };
}
// Same approach as above for markers
if (options.marker) {
series._hasPointMarkers = true;
}
}
return ret;
};
/**
* Get the pixel position of the point relative to the plot area.
* @function Highcharts.Point#pos
*
* @sample highcharts/point/position
* Get point's position in pixels.
*
* @param {boolean} chartCoordinates
* If true, the returned position is relative to the full chart area.
* If false, it is relative to the plot area determined by the axes.
*
* @param {number|undefined} plotY
* A custom plot y position to be computed. Used internally for some
* series types that have multiple `y` positions, like area range (low
* and high values).
*
* @return {Array<number>|undefined}
* Coordinates of the point if the point exists.
*/
Point.prototype.pos = function (chartCoordinates, plotY) {
if (plotY === void 0) { plotY = this.plotY; }
if (!this.destroyed) {
var _a = this,
plotX = _a.plotX,
series = _a.series,
chart = series.chart,
xAxis = series.xAxis,
yAxis = series.yAxis;
var posX = 0,
posY = 0;
if (Point_isNumber(plotX) && Point_isNumber(plotY)) {
if (chartCoordinates) {
posX = xAxis ? xAxis.pos : chart.plotLeft;
posY = yAxis ? yAxis.pos : chart.plotTop;
}
return chart.inverted && xAxis && yAxis ?
[yAxis.len - plotY + posY, xAxis.len - plotX + posX] :
[plotX + posX, plotY + posY];
}
}
};
/**
* @private
* @function Highcharts.Point#resolveColor
*/
Point.prototype.resolveColor = function () {
var series = this.series,
optionsChart = series.chart.options.chart,
styledMode = series.chart.styledMode;
var color,
colors,
colorCount = optionsChart.colorCount,
colorIndex;
// Remove points nonZonedColor for later recalculation
delete this.nonZonedColor;
if (series.options.colorByPoint) {
if (!styledMode) {
colors = series.options.colors || series.chart.options.colors;
color = colors[series.colorCounter];
colorCount = colors.length;
}
colorIndex = series.colorCounter;
series.colorCounter++;
// Loop back to zero
if (series.colorCounter === colorCount) {
series.colorCounter = 0;
}
}
else {
if (!styledMode) {
color = series.color;
}
colorIndex = series.colorIndex;
}
/**
* The point's current color index, used in styled mode instead of
* `color`. The color index is inserted in class names used for styling.
*
* @name Highcharts.Point#colorIndex
* @type {number|undefined}
*/
this.colorIndex = Point_pick(this.options.colorIndex, colorIndex);
/**
* The point's current color.
*
* @name Highcharts.Point#color
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
*/
this.color = Point_pick(this.options.color, color);
};
/**
* Set a value in an object, on the property defined by key. The key
* supports nested properties using dot notation. The function modifies the
* input object and does not make a copy.
*
* @function Highcharts.Point#setNestedProperty<T>
*
* @param {T} object
* The object to set the value on.
*
* @param {*} value
* The value to set.
*
* @param {string} key
* Key to the property to set.
*
* @return {T}
* The modified object.
*/
Point.prototype.setNestedProperty = function (object, value, key) {
var nestedKeys = key.split('.');
nestedKeys.reduce(function (result, key, i, arr) {
var isLastKey = arr.length - 1 === i;
result[key] = (isLastKey ?
value :
Point_isObject(result[key], true) ?
result[key] :
{});
return result[key];
}, object);
return object;
};
Point.prototype.shouldDraw = function () {
return !this.isNull;
};
/**
* Extendable method for formatting each point's tooltip line.
*
* @function Highcharts.Point#tooltipFormatter
*
* @param {string} pointFormat
* The point format.
*
* @return {string}
* A string to be concatenated in to the common tooltip text.
*/
Point.prototype.tooltipFormatter = function (pointFormat) {
var _a;
// Insert options for valueDecimals, valuePrefix, and valueSuffix
var _b = this.series, chart = _b.chart, _c = _b.pointArrayMap, pointArrayMap = _c === void 0 ? ['y'] : _c, tooltipOptions = _b.tooltipOptions, _d = tooltipOptions.valueDecimals, valueDecimals = _d === void 0 ? '' : _d, _e = tooltipOptions.valuePrefix, valuePrefix = _e === void 0 ? '' : _e, _f = tooltipOptions.valueSuffix, valueSuffix = _f === void 0 ? '' : _f;
// Replace default point style with class name
if (chart.styledMode) {
pointFormat = ((_a = chart.tooltip) === null || _a === void 0 ? void 0 : _a.styledModeFormat(pointFormat)) ||
pointFormat;
}
// Loop over the point array map and replace unformatted values with
// sprintf formatting markup
pointArrayMap.forEach(function (key) {
key = '{point.' + key; // Without the closing bracket
if (valuePrefix || valueSuffix) {
pointFormat = pointFormat.replace(RegExp(key + '}', 'g'), valuePrefix + key + '}' + valueSuffix);
}
pointFormat = pointFormat.replace(RegExp(key + '}', 'g'), key + ':,.' + valueDecimals + 'f}');
});
return Point_format(pointFormat, this, chart);
};
/**
* Update point with new options (typically x/y data) and optionally redraw
* the series.
*
* @sample highcharts/members/point-update-column/
* Update column value
* @sample highcharts/members/point-update-pie/
* Update pie slice
* @sample maps/members/point-update/
* Update map area value in Highmaps
*
* @function Highcharts.Point#update
*
* @param {Highcharts.PointOptionsType} options
* The point options. Point options are handled as described under
* the `series.type.data` item for each series type. For example
* for a line series, if options is a single number, the point will
* be given that number as the marin y value. If it is an array, it
* will be interpreted as x and y values respectively. If it is an
* object, advanced options are applied.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart after the point is updated. If doing
* more operations on the chart, it is best practice to set
* `redraw` to false and call `chart.redraw()` after.
*
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation=true]
* Whether to apply animation, and optionally animation
* configuration.
*
* @emits Highcharts.Point#event:update
*/
Point.prototype.update = function (options, redraw, animation, runEvent) {
var point = this,
series = point.series,
graphic = point.graphic,
chart = series.chart,
seriesOptions = series.options;
var i;
redraw = Point_pick(redraw, true);
/**
* @private
*/
function update() {
point.applyOptions(options);
// Update visuals, #4146
// Handle mock graphic elements for a11y, #12718
var hasMockGraphic = graphic && point.hasMockGraphic;
var shouldDestroyGraphic = point.y === null ?
!hasMockGraphic :
hasMockGraphic;
if (graphic && shouldDestroyGraphic) {
point.graphic = graphic.destroy();
delete point.hasMockGraphic;
}
if (Point_isObject(options, true)) {
// Destroy so we can get new elements
if (graphic && graphic.element) {
// "null" is also a valid symbol
if (options &&
options.marker &&
typeof options.marker.symbol !== 'undefined') {
point.graphic = graphic.destroy();
}
}
if ((options === null || options === void 0 ? void 0 : options.dataLabels) && point.dataLabel) {
point.dataLabel = point.dataLabel.destroy(); // #2468
}
}
// Record changes in the data table
i = point.index;
var row = {};
for (var _i = 0, _a = series.dataColumnKeys(); _i < _a.length; _i++) {
var key = _a[_i];
row[key] = point[key];
}
series.dataTable.setRow(row, i);
// Record the options to options.data. If the old or the new config
// is an object, use point options, otherwise use raw options
// (#4701, #4916).
seriesOptions.data[i] = (Point_isObject(seriesOptions.data[i], true) ||
Point_isObject(options, true)) ?
point.options :
Point_pick(options, seriesOptions.data[i]);
// Redraw
series.isDirty = series.isDirtyData = true;
if (!series.fixedBox && series.hasCartesianSeries) { // #1906, #2320
chart.isDirtyBox = true;
}
if (seriesOptions.legendType === 'point') { // #1831, #1885
chart.isDirtyLegend = true;
}
if (redraw) {
chart.redraw(animation);
}
}
// Fire the event with a default handler of doing the update
if (runEvent === false) { // When called from setData
update();
}
else {
point.firePointEvent('update', { options: options }, update);
}
};
/**
* Remove a point and optionally redraw the series and if necessary the axes
*
* @sample highcharts/plotoptions/series-point-events-remove/
* Remove point and confirm
* @sample highcharts/members/point-remove/
* Remove pie slice
* @sample maps/members/point-remove/
* Remove selected points in Highmaps
*
* @function Highcharts.Point#remove
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart or wait for an explicit call. When
* doing more operations on the chart, for example running
* `point.remove()` in a loop, it is best practice to set `redraw`
* to false and call `chart.redraw()` after.
*
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation=false]
* Whether to apply animation, and optionally animation
* configuration.
*/
Point.prototype.remove = function (redraw, animation) {
this.series.removePoint(this.series.data.indexOf(this), redraw, animation);
};
/**
* Toggle the selection status of a point.
*
* @see Highcharts.Chart#getSelectedPoints
*
* @sample highcharts/members/point-select/
* Select a point from a button
* @sample highcharts/members/point-select-lasso/
* Lasso selection
* @sample highcharts/chart/events-selection-points/
* Rectangle selection
* @sample maps/series/data-id/
* Select a point in Highmaps
*
* @function Highcharts.Point#select
*
* @param {boolean} [selected]
* When `true`, the point is selected. When `false`, the point is
* unselected. When `null` or `undefined`, the selection state is toggled.
*
* @param {boolean} [accumulate=false]
* When `true`, the selection is added to other selected points.
* When `false`, other selected points are deselected. Internally in
* Highcharts, when
* [allowPointSelect](https://api.highcharts.com/highcharts/plotOptions.series.allowPointSelect)
* is `true`, selected points are accumulated on Control, Shift or Cmd
* clicking the point.
*
* @emits Highcharts.Point#event:select
* @emits Highcharts.Point#event:unselect
*/
Point.prototype.select = function (selected, accumulate) {
var point = this,
series = point.series,
chart = series.chart;
selected = Point_pick(selected, !point.selected);
this.selectedStaging = selected;
// Fire the event with the default handler
point.firePointEvent(selected ? 'select' : 'unselect', { accumulate: accumulate }, function () {
/**
* Whether the point is selected or not.
*
* @see Point#select
* @see Chart#getSelectedPoints
*
* @name Highcharts.Point#selected
* @type {boolean}
*/
point.selected = point.options.selected = selected;
series.options.data[series.data.indexOf(point)] =
point.options;
point.setState(selected && 'select');
// Unselect all other points unless Ctrl or Cmd + click
if (!accumulate) {
chart.getSelectedPoints().forEach(function (loopPoint) {
var loopSeries = loopPoint.series;
if (loopPoint.selected && loopPoint !== point) {
loopPoint.selected = loopPoint.options.selected =
false;
loopSeries.options.data[loopSeries.data.indexOf(loopPoint)] = loopPoint.options;
// Programmatically selecting a point should restore
// normal state, but when click happened on other
// point, set inactive state to match other points
loopPoint.setState(chart.hoverPoints &&
loopSeries.options.inactiveOtherPoints ?
'inactive' : '');
loopPoint.firePointEvent('unselect');
}
});
}
});
delete this.selectedStaging;
};
/**
* Runs on mouse over the point. Called internally from mouse and touch
* events.
*
* @function Highcharts.Point#onMouseOver
*
* @param {Highcharts.PointerEventObject} [e]
* The event arguments.
*/
Point.prototype.onMouseOver = function (e) {
var point = this,
series = point.series,
_a = series.chart,
inverted = _a.inverted,
pointer = _a.pointer;
if (pointer) {
e = e ?
pointer.normalize(e) :
// In cases where onMouseOver is called directly without an
// event
pointer.getChartCoordinatesFromPoint(point, inverted);
pointer.runPointActions(e, point);
}
};
/**
* Runs on mouse out from the point. Called internally from mouse and touch
* events.
*
* @function Highcharts.Point#onMouseOut
* @emits Highcharts.Point#event:mouseOut
*/
Point.prototype.onMouseOut = function () {
var point = this,
chart = point.series.chart;
point.firePointEvent('mouseOut');
if (!point.series.options.inactiveOtherPoints) {
(chart.hoverPoints || []).forEach(function (p) {
p.setState();
});
}
chart.hoverPoints = chart.hoverPoint = null;
};
/**
* Manage specific event from the series' and point's options. Only do it on
* demand, to save processing time on hovering.
*
* @private
* @function Highcharts.Point#importEvents
*/
Point.prototype.manageEvent = function (eventType) {
var _a,
_b,
_c,
_d,
_e,
_f,
_g;
var point = this,
options = Point_merge(point.series.options.point,
point.options),
userEvent = (_a = options.events) === null || _a === void 0 ? void 0 : _a[eventType];
if (Point_isFunction(userEvent) &&
(!((_b = point.hcEvents) === null || _b === void 0 ? void 0 : _b[eventType]) ||
// Some HC modules, like marker-clusters, draggable-poins etc.
// use events in their logic, so we need to be sure, that
// callback function is different
((_d = (_c = point.hcEvents) === null || _c === void 0 ? void 0 : _c[eventType]) === null || _d === void 0 ? void 0 : _d.map(function (el) { return el.fn; }).indexOf(userEvent)) === -1)) {
// While updating the existing callback event the old one should be
// removed
(_e = point.importedUserEvent) === null || _e === void 0 ? void 0 : _e.call(point);
point.importedUserEvent = Point_addEvent(point, eventType, userEvent);
if (point.hcEvents) {
point.hcEvents[eventType].userEvent = true;
}
}
else if (point.importedUserEvent &&
!userEvent &&
((_f = point.hcEvents) === null || _f === void 0 ? void 0 : _f[eventType]) &&
((_g = point.hcEvents) === null || _g === void 0 ? void 0 : _g[eventType].userEvent)) {
Point_removeEvent(point, eventType);
delete point.hcEvents[eventType];
if (!Object.keys(point.hcEvents)) {
delete point.importedUserEvent;
}
}
};
/**
* Set the point's state.
*
* @function Highcharts.Point#setState
*
* @param {Highcharts.PointStateValue|""} [state]
* The new state, can be one of `'hover'`, `'select'`, `'inactive'`,
* or `''` (an empty string), `'normal'` or `undefined` to set to
* normal state.
* @param {boolean} [move]
* State for animation.
*
* @emits Highcharts.Point#event:afterSetState
*/
Point.prototype.setState = function (state, move) {
var _a;
var point = this,
series = point.series,
previousState = point.state,
stateOptions = (series.options.states[state || 'normal'] ||
{}),
markerOptions = (Point_defaultOptions.plotOptions[series.type].marker &&
series.options.marker),
normalDisabled = (markerOptions && markerOptions.enabled === false),
markerStateOptions = ((markerOptions &&
markerOptions.states &&
markerOptions.states[state || 'normal']) || {}),
stateDisabled = markerStateOptions.enabled === false,
pointMarker = point.marker || {},
chart = series.chart,
hasMarkers = (markerOptions && series.markerAttribs);
var halo = series.halo,
markerAttribs,
pointAttribs,
pointAttribsAnimation,
stateMarkerGraphic = series.stateMarkerGraphic,
newSymbol;
state = state || ''; // Empty string
if (
// Already has this state
(state === point.state && !move) ||
// Selected points don't respond to hover
(point.selected && state !== 'select') ||
// Series' state options is disabled
(stateOptions.enabled === false) ||
// General point marker's state options is disabled
(state && (stateDisabled ||
(normalDisabled &&
markerStateOptions.enabled === false))) ||
// Individual point marker's state options is disabled
(state &&
pointMarker.states &&
pointMarker.states[state] &&
pointMarker.states[state].enabled === false) // #1610
) {
return;
}
point.state = state;
if (hasMarkers) {
markerAttribs = series.markerAttribs(point, state);
}
// Apply hover styles to the existing point
// Prevent from mocked null points (#14966)
if (point.graphic && !point.hasMockGraphic) {
if (previousState) {
point.graphic.removeClass('highcharts-point-' + previousState);
}
if (state) {
point.graphic.addClass('highcharts-point-' + state);
}
if (!chart.styledMode) {
pointAttribs = series.pointAttribs(point, state);
pointAttribsAnimation = Point_pick(chart.options.chart.animation, stateOptions.animation);
var opacity_1 = pointAttribs.opacity;
// Some inactive points (e.g. slices in pie) should apply
// opacity also for their labels
if (series.options.inactiveOtherPoints && Point_isNumber(opacity_1)) {
(point.dataLabels || []).forEach(function (label) {
if (label &&
!label.hasClass('highcharts-data-label-hidden')) {
label.animate({ opacity: opacity_1 }, pointAttribsAnimation);
if (label.connector) {
label.connector.animate({ opacity: opacity_1 }, pointAttribsAnimation);
}
}
});
}
point.graphic.animate(pointAttribs, pointAttribsAnimation);
}
if (markerAttribs) {
point.graphic.animate(markerAttribs, Point_pick(
// Turn off globally:
chart.options.chart.animation, markerStateOptions.animation, markerOptions.animation));
}
// Zooming in from a range with no markers to a range with markers
if (stateMarkerGraphic) {
stateMarkerGraphic.hide();
}
}
else {
// If a graphic is not applied to each point in the normal state,
// create a shared graphic for the hover state
if (state && markerStateOptions) {
newSymbol = pointMarker.symbol || series.symbol;
// If the point has another symbol than the previous one, throw
// away the state marker graphic and force a new one (#1459)
if (stateMarkerGraphic &&
stateMarkerGraphic.currentSymbol !== newSymbol) {
stateMarkerGraphic = stateMarkerGraphic.destroy();
}
// Add a new state marker graphic
if (markerAttribs) {
if (!stateMarkerGraphic) {
if (newSymbol) {
series.stateMarkerGraphic = stateMarkerGraphic =
chart.renderer
.symbol(newSymbol, markerAttribs.x, markerAttribs.y, markerAttribs.width, markerAttribs.height, Point_merge(markerOptions, markerStateOptions))
.add(series.markerGroup);
stateMarkerGraphic.currentSymbol = newSymbol;
}
// Move the existing graphic
}
else {
stateMarkerGraphic[move ? 'animate' : 'attr']({
x: markerAttribs.x,
y: markerAttribs.y
});
}
}
if (!chart.styledMode && stateMarkerGraphic &&
point.state !== 'inactive') {
stateMarkerGraphic.attr(series.pointAttribs(point, state));
}
}
if (stateMarkerGraphic) {
stateMarkerGraphic[state && point.isInside ? 'show' : 'hide'](); // #2450
stateMarkerGraphic.element.point = point; // #4310
stateMarkerGraphic.addClass(point.getClassName(), true);
}
}
// Show me your halo
var haloOptions = stateOptions.halo;
var markerGraphic = (point.graphic || stateMarkerGraphic);
var markerVisibility = (markerGraphic && markerGraphic.visibility || 'inherit');
if (haloOptions &&
haloOptions.size &&
markerGraphic &&
markerVisibility !== 'hidden' &&
!point.isCluster) {
if (!halo) {
series.halo = halo = chart.renderer.path()
// #5818, #5903, #6705
.add(markerGraphic.parentGroup);
}
halo.show()[move ? 'animate' : 'attr']({
d: point.haloPath(haloOptions.size)
});
halo.attr({
'class': 'highcharts-halo highcharts-color-' +
Point_pick(point.colorIndex, series.colorIndex) +
(point.className ? ' ' + point.className : ''),
'visibility': markerVisibility,
'zIndex': -1 // #4929, #8276
});
halo.point = point; // #6055
if (!chart.styledMode) {
halo.attr(Point_extend({
'fill': point.color || series.color,
'fill-opacity': haloOptions.opacity
}, HTML_AST.filterUserAttributes(haloOptions.attributes || {})));
}
}
else if (((_a = halo === null || halo === void 0 ? void 0 : halo.point) === null || _a === void 0 ? void 0 : _a.haloPath) &&
!halo.point.destroyed) {
// Animate back to 0 on the current halo point (#6055)
halo.animate({ d: halo.point.haloPath(0) }, null,
// Hide after unhovering. The `complete` callback runs in the
// halo's context (#7681).
halo.hide);
}
Point_fireEvent(point, 'afterSetState', { state: state });
};
/**
* Get the path definition for the halo, which is usually a shadow-like
* circle around the currently hovered point.
*
* @function Highcharts.Point#haloPath
*
* @param {number} size
* The radius of the circular halo.
*
* @return {Highcharts.SVGPathArray}
* The path definition.
*/
Point.prototype.haloPath = function (size) {
var pos = this.pos();
return pos ? this.series.chart.renderer.symbols.circle(Point_crisp(pos[0], 1) - size, pos[1] - size, size * 2, size * 2) : [];
};
return Point;
}());
/* *
*
* Default Export
*
* */
/* harmony default export */ var Series_Point = (Point);
/* *
*
* API Declarations
*
* */
/**
* Function callback when a series point is clicked. Return false to cancel the
* action.
*
* @callback Highcharts.PointClickCallbackFunction
*
* @param {Highcharts.Point} this
* The point where the event occurred.
*
* @param {Highcharts.PointClickEventObject} event
* Event arguments.
*/
/**
* Common information for a click event on a series point.
*
* @interface Highcharts.PointClickEventObject
* @extends Highcharts.PointerEventObject
*/ /**
* Clicked point.
* @name Highcharts.PointClickEventObject#point
* @type {Highcharts.Point}
*/
/**
* Gets fired when the mouse leaves the area close to the point.
*
* @callback Highcharts.PointMouseOutCallbackFunction
*
* @param {Highcharts.Point} this
* Point where the event occurred.
*
* @param {global.PointerEvent} event
* Event that occurred.
*/
/**
* Gets fired when the mouse enters the area close to the point.
*
* @callback Highcharts.PointMouseOverCallbackFunction
*
* @param {Highcharts.Point} this
* Point where the event occurred.
*
* @param {global.Event} event
* Event that occurred.
*/
/**
* The generic point options for all series.
*
* In TypeScript you have to extend `PointOptionsObject` with an additional
* declaration to allow custom data options:
*
* ```
* declare interface PointOptionsObject {
* customProperty: string;
* }
* ```
*
* @interface Highcharts.PointOptionsObject
*/
/**
* Possible option types for a data point. Use `null` to indicate a gap.
*
* @typedef {number|string|Highcharts.PointOptionsObject|Array<(number|string|null)>|null} Highcharts.PointOptionsType
*/
/**
* Gets fired when the point is removed using the `.remove()` method.
*
* @callback Highcharts.PointRemoveCallbackFunction
*
* @param {Highcharts.Point} this
* Point where the event occurred.
*
* @param {global.Event} event
* Event that occurred.
*/
/**
* Possible key values for the point state options.
*
* @typedef {"hover"|"inactive"|"normal"|"select"} Highcharts.PointStateValue
*/
/**
* Gets fired when the point is updated programmatically through the `.update()`
* method.
*
* @callback Highcharts.PointUpdateCallbackFunction
*
* @param {Highcharts.Point} this
* Point where the event occurred.
*
* @param {Highcharts.PointUpdateEventObject} event
* Event that occurred.
*/
/**
* Information about the update event.
*
* @interface Highcharts.PointUpdateEventObject
* @extends global.Event
*/ /**
* Options data of the update event.
* @name Highcharts.PointUpdateEventObject#options
* @type {Highcharts.PointOptionsType}
*/
/**
* @interface Highcharts.PointEventsOptionsObject
*/ /**
* Fires when the point is selected either programmatically or following a click
* on the point. One parameter, `event`, is passed to the function. Returning
* `false` cancels the operation.
* @name Highcharts.PointEventsOptionsObject#select
* @type {Highcharts.PointSelectCallbackFunction|undefined}
*/ /**
* Fires when the point is unselected either programmatically or following a
* click on the point. One parameter, `event`, is passed to the function.
* Returning `false` cancels the operation.
* @name Highcharts.PointEventsOptionsObject#unselect
* @type {Highcharts.PointUnselectCallbackFunction|undefined}
*/
/**
* Information about the select/unselect event.
*
* @interface Highcharts.PointInteractionEventObject
* @extends global.Event
*/ /**
* @name Highcharts.PointInteractionEventObject#accumulate
* @type {boolean}
*/
/**
* Gets fired when the point is selected either programmatically or following a
* click on the point.
*
* @callback Highcharts.PointSelectCallbackFunction
*
* @param {Highcharts.Point} this
* Point where the event occurred.
*
* @param {Highcharts.PointInteractionEventObject} event
* Event that occurred.
*/
/**
* Fires when the point is unselected either programmatically or following a
* click on the point.
*
* @callback Highcharts.PointUnselectCallbackFunction
*
* @param {Highcharts.Point} this
* Point where the event occurred.
*
* @param {Highcharts.PointInteractionEventObject} event
* Event that occurred.
*/
''; // Keeps doclets above in JS file.
;// ./code/es5/es-modules/Core/Pointer.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var Pointer_assign = (undefined && undefined.__assign) || function () {
Pointer_assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return Pointer_assign.apply(this, arguments);
};
var Pointer_color = Color_Color.parse;
var Pointer_charts = Core_Globals.charts, Pointer_composed = Core_Globals.composed, Pointer_isTouchDevice = Core_Globals.isTouchDevice;
var Pointer_addEvent = Core_Utilities.addEvent, Pointer_attr = Core_Utilities.attr, Pointer_css = Core_Utilities.css, Pointer_extend = Core_Utilities.extend, Pointer_find = Core_Utilities.find, Pointer_fireEvent = Core_Utilities.fireEvent, Pointer_isNumber = Core_Utilities.isNumber, Pointer_isObject = Core_Utilities.isObject, Pointer_objectEach = Core_Utilities.objectEach, Pointer_offset = Core_Utilities.offset, Pointer_pick = Core_Utilities.pick, Pointer_pushUnique = Core_Utilities.pushUnique, Pointer_splat = Core_Utilities.splat;
/* *
*
* Class
*
* */
/**
* The mouse and touch tracker object. Each {@link Chart} item has one
* associated Pointer item that can be accessed from the {@link Chart.pointer}
* property.
*
* @class
* @name Highcharts.Pointer
*
* @param {Highcharts.Chart} chart
* The chart instance.
*
* @param {Highcharts.Options} options
* The root options object. The pointer uses options from the chart and tooltip
* structures.
*/
var Pointer = /** @class */ (function () {
/**
* Initialize the Pointer.
*
* @private
* @function Highcharts.Pointer#init
*
* @param {Highcharts.Chart} chart
* The Chart instance.
*
* @param {Highcharts.Options} options
* The root options object. The pointer uses options from the chart and
* tooltip structures.
*/
function Pointer(chart, options) {
var _a;
this.hasDragged = 0;
this.pointerCaptureEventsToUnbind = [];
this.eventsToUnbind = [];
// Store references
this.options = options;
this.chart = chart;
// Do we need to handle click on a touch device?
this.runChartClick = Boolean((_a = options.chart.events) === null || _a === void 0 ? void 0 : _a.click);
this.pinchDown = [];
this.setDOMEvents();
Pointer_fireEvent(this, 'afterInit');
}
/* *
*
* Functions
*
* */
/**
* Set inactive state to all series that are not currently hovered,
* or, if `inactiveOtherPoints` is set to true, set inactive state to
* all points within that series.
*
* @private
* @function Highcharts.Pointer#applyInactiveState
*
* @param {Array<Highcharts.Point>} points
* Currently hovered points
*/
Pointer.prototype.applyInactiveState = function (points) {
var activeSeries = [],
series;
// Get all active series from the hovered points
(points || []).forEach(function (item) {
series = item.series;
// Include itself
activeSeries.push(series);
// Include parent series
if (series.linkedParent) {
activeSeries.push(series.linkedParent);
}
// Include all child series
if (series.linkedSeries) {
activeSeries = activeSeries.concat(series.linkedSeries);
}
// Include navigator series
if (series.navigatorSeries) {
activeSeries.push(series.navigatorSeries);
}
});
// Now loop over all series, filtering out active series
this.chart.series.forEach(function (inactiveSeries) {
if (activeSeries.indexOf(inactiveSeries) === -1) {
// Inactive series
inactiveSeries.setState('inactive', true);
}
else if (inactiveSeries.options.inactiveOtherPoints) {
// Active series, but other points should be inactivated
inactiveSeries.setAllPointsToState('inactive');
}
});
};
/**
* Destroys the Pointer object and disconnects DOM events.
*
* @function Highcharts.Pointer#destroy
*/
Pointer.prototype.destroy = function () {
var pointer = this;
this.eventsToUnbind.forEach(function (unbind) { return unbind(); });
this.eventsToUnbind = [];
if (!Core_Globals.chartCount) {
Pointer.unbindDocumentMouseUp.forEach(function (el) { return el.unbind(); });
Pointer.unbindDocumentMouseUp.length = 0;
if (Pointer.unbindDocumentTouchEnd) {
Pointer.unbindDocumentTouchEnd = (Pointer.unbindDocumentTouchEnd());
}
}
// Memory and CPU leak
clearInterval(pointer.tooltipTimeout);
Pointer_objectEach(pointer, function (_val, prop) {
pointer[prop] = void 0;
});
};
/**
* Calculate attrs for selection marker.
* @private
* @function Highcharts.Pointer#getSelectionMarkerAttrs
* @emits getSelectionMarkerAttrs
*/
Pointer.prototype.getSelectionMarkerAttrs = function (chartX, chartY) {
var _this = this;
var e = {
args: { chartX: chartX,
chartY: chartY },
attrs: {},
shapeType: 'rect'
};
Pointer_fireEvent(this, 'getSelectionMarkerAttrs', e, function (e) {
var _a = _this,
chart = _a.chart,
zoomHor = _a.zoomHor,
zoomVert = _a.zoomVert,
_b = chart.mouseDownX,
mouseDownX = _b === void 0 ? 0 : _b,
_c = chart.mouseDownY,
mouseDownY = _c === void 0 ? 0 : _c,
attrs = e.attrs;
var size;
attrs.x = chart.plotLeft;
attrs.y = chart.plotTop;
attrs.width = zoomHor ? 1 : chart.plotWidth;
attrs.height = zoomVert ? 1 : chart.plotHeight;
// Adjust the width of the selection marker. Firefox needs at
// least one pixel width or height in order to return a bounding
// box.
if (zoomHor) {
size = chartX - mouseDownX;
attrs.width = Math.max(1, Math.abs(size));
attrs.x = (size > 0 ? 0 : size) + mouseDownX;
}
// Adjust the height of the selection marker
if (zoomVert) {
size = chartY - mouseDownY;
attrs.height = Math.max(1, Math.abs(size));
attrs.y = (size > 0 ? 0 : size) + mouseDownY;
}
});
return e;
};
/**
* Perform a drag operation in response to a mousemove event while the mouse
* is down.
* @private
* @function Highcharts.Pointer#drag
*/
Pointer.prototype.drag = function (e) {
var chart = this.chart,
_a = chart.mouseDownX,
mouseDownX = _a === void 0 ? 0 : _a,
_b = chart.mouseDownY,
mouseDownY = _b === void 0 ? 0 : _b,
_c = chart.options.chart,
panning = _c.panning,
panKey = _c.panKey,
selectionMarkerFill = _c.selectionMarkerFill,
plotLeft = chart.plotLeft,
plotTop = chart.plotTop,
plotWidth = chart.plotWidth,
plotHeight = chart.plotHeight,
panningEnabled = Pointer_isObject(panning) ?
panning.enabled :
panning,
panKeyPressed = panKey && e["" + panKey + "Key"];
var chartX = e.chartX,
chartY = e.chartY,
clickedInside,
selectionMarker = this.selectionMarker;
// If the device supports both touch and mouse (like IE11), and we are
// touch-dragging inside the plot area, don't handle the mouse event.
// #4339.
if (selectionMarker && selectionMarker.touch) {
return;
}
// If the mouse is outside the plot area, adjust to coordinates
// inside to prevent the selection marker from going outside
if (chartX < plotLeft) {
chartX = plotLeft;
}
else if (chartX > plotLeft + plotWidth) {
chartX = plotLeft + plotWidth;
}
if (chartY < plotTop) {
chartY = plotTop;
}
else if (chartY > plotTop + plotHeight) {
chartY = plotTop + plotHeight;
}
// Determine if the mouse has moved more than 10px
this.hasDragged = Math.sqrt(Math.pow(mouseDownX - chartX, 2) +
Math.pow(mouseDownY - chartY, 2));
if (this.hasDragged > 10) {
clickedInside = chart.isInsidePlot(mouseDownX - plotLeft, mouseDownY - plotTop, {
visiblePlotOnly: true
});
var _d = this.getSelectionMarkerAttrs(chartX,
chartY),
shapeType = _d.shapeType,
attrs = _d.attrs;
// Make a selection
if ((chart.hasCartesianSeries || chart.mapView) &&
this.hasZoom &&
clickedInside &&
!panKeyPressed) {
if (!selectionMarker) {
this.selectionMarker = selectionMarker =
chart.renderer[shapeType]();
selectionMarker
.attr({
'class': 'highcharts-selection-marker',
zIndex: 7
})
.add();
if (!chart.styledMode) {
selectionMarker.attr({
fill: selectionMarkerFill ||
Pointer_color("#334eff" /* Palette.highlightColor80 */)
.setOpacity(0.25).get()
});
}
}
}
if (selectionMarker) {
selectionMarker.attr(attrs);
}
// Panning
if (clickedInside && !selectionMarker && panningEnabled) {
chart.pan(e, panning);
}
}
};
/**
* Start a drag operation.
* @private
* @function Highcharts.Pointer#dragStart
*/
Pointer.prototype.dragStart = function (e) {
var chart = this.chart;
// Record the start position
chart.mouseIsDown = e.type;
chart.cancelClick = false;
chart.mouseDownX = e.chartX;
chart.mouseDownY = e.chartY;
};
/**
* Get selection box to calculate extremes
* @private
* @function Highcharts.Pointer#getSelectionBox
* @emits getSelectionBox
*/
Pointer.prototype.getSelectionBox = function (marker) {
var e = {
args: { marker: marker },
result: marker.getBBox()
};
Pointer_fireEvent(this, 'getSelectionBox', e);
return e.result;
};
/**
* On mouse up or touch end across the entire document, drop the selection.
* @private
* @function Highcharts.Pointer#drop
*/
Pointer.prototype.drop = function (e) {
var _this = this;
var _a = this,
chart = _a.chart,
selectionMarker = _a.selectionMarker;
// During a mouse, touch or mousewheel pan, the `startOnTick` and
// `endOnTick` options are ignored. Otherwise the zooming or panning
// would be jumpy, or even not performed because the end ticks would
// block it. After the touch has ended, we undo this and render again.
var redraw;
for (var _i = 0, _b = chart.axes; _i < _b.length; _i++) {
var axis = _b[_i];
if (axis.isPanning) {
axis.isPanning = false;
if (axis.options.startOnTick ||
axis.options.endOnTick ||
axis.series.some(function (s) { return s.boosted; })) {
axis.forceRedraw = true;
axis.setExtremes(axis.userMin, axis.userMax, false);
redraw = true;
}
}
}
if (redraw) {
chart.redraw();
}
if (selectionMarker && e) {
// A selection has been made
if (this.hasDragged) {
var from = this.getSelectionBox(selectionMarker);
chart.transform({
axes: chart.axes.filter(function (a) {
return a.zoomEnabled &&
((a.coll === 'xAxis' && _this.zoomX) ||
(a.coll === 'yAxis' && _this.zoomY));
}),
selection: Pointer_assign({ originalEvent: e, xAxis: [], yAxis: [] }, from),
from: from
});
}
if (Pointer_isNumber(chart.index)) {
this.selectionMarker = selectionMarker.destroy();
}
}
// Reset all. Check isNumber because it may be destroyed on mouse up
// (#877)
if (chart && Pointer_isNumber(chart.index)) {
Pointer_css(chart.container, { cursor: chart._cursor });
chart.cancelClick = this.hasDragged > 10; // #370
chart.mouseIsDown = false;
this.hasDragged = 0;
this.pinchDown = [];
}
};
/**
* Finds the closest point to a set of coordinates, using the k-d-tree
* algorithm.
*
* @function Highcharts.Pointer#findNearestKDPoint
*
* @param {Array<Highcharts.Series>} series
* All the series to search in.
*
* @param {boolean|undefined} shared
* Whether it is a shared tooltip or not.
*
* @param {Highcharts.PointerEventObject} e
* The pointer event object, containing chart coordinates of the pointer.
*
* @return {Highcharts.Point|undefined}
* The point closest to given coordinates.
*/
Pointer.prototype.findNearestKDPoint = function (series, shared, e) {
var closest;
/** @private */
function sort(p1, p2) {
var _a,
_b;
var isCloserX = p1.distX - p2.distX,
isCloser = p1.dist - p2.dist,
isAbove = (((_a = p2.series.group) === null || _a === void 0 ? void 0 : _a.zIndex) -
((_b = p1.series.group) === null || _b === void 0 ? void 0 : _b.zIndex));
var result;
// We have two points which are not in the same place on xAxis
// and shared tooltip:
if (isCloserX !== 0 && shared) { // #5721
result = isCloserX;
// Points are not exactly in the same place on x/yAxis:
}
else if (isCloser !== 0) {
result = isCloser;
// The same xAxis and yAxis position, sort by z-index:
}
else if (isAbove !== 0) {
result = isAbove;
// The same zIndex, sort by array index:
}
else {
result =
p1.series.index > p2.series.index ?
-1 :
1;
}
return result;
}
series.forEach(function (s) {
var noSharedTooltip = s.noSharedTooltip && shared,
compareX = (!noSharedTooltip &&
s.options.findNearestPointBy.indexOf('y') < 0),
point = s.searchPoint(e,
compareX);
if ( // Check that we actually found a point on the series.
Pointer_isObject(point, true) && point.series &&
// Use the new point if it is closer.
(!Pointer_isObject(closest, true) ||
(sort(closest, point) > 0))) {
closest = point;
}
});
return closest;
};
/**
* @private
* @function Highcharts.Pointer#getChartCoordinatesFromPoint
*/
Pointer.prototype.getChartCoordinatesFromPoint = function (point, inverted) {
var _a,
_b;
var _c = point.series,
xAxis = _c.xAxis,
yAxis = _c.yAxis,
shapeArgs = point.shapeArgs;
if (xAxis && yAxis) {
var x = (_b = (_a = point.clientX) !== null && _a !== void 0 ? _a : point.plotX) !== null && _b !== void 0 ? _b : 0,
y = point.plotY || 0;
if (point.isNode &&
shapeArgs &&
Pointer_isNumber(shapeArgs.x) &&
Pointer_isNumber(shapeArgs.y)) {
x = shapeArgs.x;
y = shapeArgs.y;
}
return inverted ? {
chartX: yAxis.len + yAxis.pos - y,
chartY: xAxis.len + xAxis.pos - x
} : {
chartX: x + xAxis.pos,
chartY: y + yAxis.pos
};
}
if (shapeArgs && shapeArgs.x && shapeArgs.y) {
// E.g. pies do not have axes
return {
chartX: shapeArgs.x,
chartY: shapeArgs.y
};
}
};
/**
* Return the cached chartPosition if it is available on the Pointer,
* otherwise find it. Running offset is quite expensive, so it should be
* avoided when we know the chart hasn't moved.
*
* @function Highcharts.Pointer#getChartPosition
*
* @return {Highcharts.ChartPositionObject}
* The offset of the chart container within the page
*/
Pointer.prototype.getChartPosition = function () {
if (this.chartPosition) {
return this.chartPosition;
}
var container = this.chart.container;
var pos = Pointer_offset(container);
this.chartPosition = {
left: pos.left,
top: pos.top,
scaleX: 1,
scaleY: 1
};
var offsetHeight = container.offsetHeight,
offsetWidth = container.offsetWidth;
// #13342 - tooltip was not visible in Chrome, when chart
// updates height.
if (offsetWidth > 2 && // #13342
offsetHeight > 2 // #13342
) {
this.chartPosition.scaleX = pos.width / offsetWidth;
this.chartPosition.scaleY = pos.height / offsetHeight;
}
return this.chartPosition;
};
/**
* Get the click position in terms of axis values.
*
* @function Highcharts.Pointer#getCoordinates
*
* @param {Highcharts.PointerEventObject} e
* Pointer event, extended with `chartX` and `chartY` properties.
*
* @return {Highcharts.PointerAxisCoordinatesObject}
* Axis coordinates.
*/
Pointer.prototype.getCoordinates = function (e) {
var coordinates = {
xAxis: [],
yAxis: []
};
for (var _i = 0, _a = this.chart.axes; _i < _a.length; _i++) {
var axis = _a[_i];
coordinates[axis.isXAxis ? 'xAxis' : 'yAxis'].push({
axis: axis,
value: axis.toValue(e[axis.horiz ? 'chartX' : 'chartY'])
});
}
return coordinates;
};
/**
* Calculates what is the current hovered point/points and series.
*
* @private
* @function Highcharts.Pointer#getHoverData
*
* @param {Highcharts.Point|undefined} existingHoverPoint
* The point currently being hovered.
*
* @param {Highcharts.Series|undefined} existingHoverSeries
* The series currently being hovered.
*
* @param {Array<Highcharts.Series>} series
* All the series in the chart.
*
* @param {boolean} isDirectTouch
* Is the pointer directly hovering the point.
*
* @param {boolean|undefined} shared
* Whether it is a shared tooltip or not.
*
* @param {Highcharts.PointerEventObject} [e]
* The triggering event, containing chart coordinates of the pointer.
*
* @return {Object}
* Object containing resulting hover data: hoverPoint, hoverSeries, and
* hoverPoints.
*/
Pointer.prototype.getHoverData = function (existingHoverPoint, existingHoverSeries, series, isDirectTouch, shared, e) {
var hoverPoints = [],
useExisting = !!(isDirectTouch && existingHoverPoint),
filter = function (s) {
return (s.visible &&
!(!shared && s.directTouch) && // #3821
Pointer_pick(s.options.enableMouseTracking,
true));
};
var hoverSeries = existingHoverSeries,
// Which series to look in for the hover point
searchSeries,
// Parameters needed for beforeGetHoverData event.
eventArgs = {
chartX: e ? e.chartX : void 0,
chartY: e ? e.chartY : void 0,
shared: shared
};
// Find chart.hoverPane and update filter method in polar.
Pointer_fireEvent(this, 'beforeGetHoverData', eventArgs);
var notSticky = hoverSeries && !hoverSeries.stickyTracking;
searchSeries = notSticky ?
// Only search on hovered series if it has stickyTracking false
[hoverSeries] :
// Filter what series to look in.
series.filter(function (s) { return s.stickyTracking &&
(eventArgs.filter || filter)(s); });
// Use existing hovered point or find the one closest to coordinates.
var hoverPoint = useExisting || !e ?
existingHoverPoint :
this.findNearestKDPoint(searchSeries,
shared,
e);
// Assign hover series
hoverSeries = hoverPoint && hoverPoint.series;
// If we have a hoverPoint, assign hoverPoints.
if (hoverPoint) {
// When tooltip is shared, it displays more than one point
if (shared && !hoverSeries.noSharedTooltip) {
searchSeries = series.filter(function (s) {
return eventArgs.filter ?
eventArgs.filter(s) : filter(s) && !s.noSharedTooltip;
});
// Get all points with the same x value as the hoverPoint
searchSeries.forEach(function (s) {
var point = Pointer_find(s.points,
function (p) {
return p.x === hoverPoint.x && !p.isNull;
});
if (Pointer_isObject(point)) {
/*
* Boost returns a minimal point. Convert it to a usable
* point for tooltip and states.
*/
if (s.boosted && s.boost) {
point = s.boost.getPoint(point);
}
hoverPoints.push(point);
}
});
}
else {
hoverPoints.push(hoverPoint);
}
}
// Check whether the hoverPoint is inside pane we are hovering over.
eventArgs = { hoverPoint: hoverPoint };
Pointer_fireEvent(this, 'afterGetHoverData', eventArgs);
return {
hoverPoint: eventArgs.hoverPoint,
hoverSeries: hoverSeries,
hoverPoints: hoverPoints
};
};
/**
* @private
* @function Highcharts.Pointer#getPointFromEvent
*/
Pointer.prototype.getPointFromEvent = function (e) {
var target = e.target,
point;
while (target && !point) {
point = target.point;
target = target.parentNode;
}
return point;
};
/**
* @private
* @function Highcharts.Pointer#onTrackerMouseOut
*/
Pointer.prototype.onTrackerMouseOut = function (e) {
var chart = this.chart;
var relatedTarget = e.relatedTarget;
var series = chart.hoverSeries;
this.isDirectTouch = false;
if (series &&
relatedTarget &&
!series.stickyTracking &&
!this.inClass(relatedTarget, 'highcharts-tooltip') &&
(!this.inClass(relatedTarget, 'highcharts-series-' + series.index) || // #2499, #4465, #5553
!this.inClass(relatedTarget, 'highcharts-tracker'))) {
series.onMouseOut();
}
};
/**
* Utility to detect whether an element has, or has a parent with, a
* specific class name. Used on detection of tracker objects and on deciding
* whether hovering the tooltip should cause the active series to mouse out.
*
* @function Highcharts.Pointer#inClass
*
* @param {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement} element
* The element to investigate.
*
* @param {string} className
* The class name to look for.
*
* @return {boolean|undefined}
* True if either the element or one of its parents has the given class
* name.
*/
Pointer.prototype.inClass = function (element, className) {
var elem = element,
elemClassName;
while (elem) {
elemClassName = Pointer_attr(elem, 'class');
if (elemClassName) {
if (elemClassName.indexOf(className) !== -1) {
return true;
}
if (elemClassName.indexOf('highcharts-container') !== -1) {
return false;
}
}
elem = elem.parentElement;
}
};
/**
* Takes a browser event object and extends it with custom Highcharts
* properties `chartX` and `chartY` in order to work on the internal
* coordinate system.
*
* On map charts, the properties `lon` and `lat` are added to the event
* object given that the chart has projection information.
*
* @function Highcharts.Pointer#normalize
*
* @param {global.MouseEvent|global.PointerEvent|global.TouchEvent} e
* Event object in standard browsers.
*
* @param {Highcharts.OffsetObject} [chartPosition]
* Additional chart offset.
*
* @return {Highcharts.PointerEventObject}
* A browser event with extended properties `chartX` and `chartY`.
*/
Pointer.prototype.normalize = function (e, chartPosition) {
var touches = e.touches;
// Position for iOS (#2757)
var ePos = (touches ?
touches.length ?
touches.item(0) :
(Pointer_pick(// #13534
touches.changedTouches,
e.changedTouches))[0] :
e);
// Get mouse position
if (!chartPosition) {
chartPosition = this.getChartPosition();
}
var chartX = ePos.pageX - chartPosition.left,
chartY = ePos.pageY - chartPosition.top;
// #11329 - when there is scaling on a parent element, we need to take
// this into account
chartX /= chartPosition.scaleX;
chartY /= chartPosition.scaleY;
return Pointer_extend(e, {
chartX: Math.round(chartX),
chartY: Math.round(chartY)
});
};
/**
* @private
* @function Highcharts.Pointer#onContainerClick
*/
Pointer.prototype.onContainerClick = function (e) {
var chart = this.chart;
var hoverPoint = chart.hoverPoint;
var pEvt = this.normalize(e);
var plotLeft = chart.plotLeft;
var plotTop = chart.plotTop;
if (!chart.cancelClick) {
// On tracker click, fire the series and point events. #783, #1583
if (hoverPoint &&
this.inClass(pEvt.target, 'highcharts-tracker')) {
// The series click event
Pointer_fireEvent(hoverPoint.series, 'click', Pointer_extend(pEvt, {
point: hoverPoint
}));
// The point click event
if (chart.hoverPoint) { // It may be destroyed (#1844)
hoverPoint.firePointEvent('click', pEvt);
}
// When clicking outside a tracker, fire a chart event
}
else {
Pointer_extend(pEvt, this.getCoordinates(pEvt));
// Fire a click event in the chart
if (chart.isInsidePlot(pEvt.chartX - plotLeft, pEvt.chartY - plotTop, {
visiblePlotOnly: true
})) {
Pointer_fireEvent(chart, 'click', pEvt);
}
}
}
};
/**
* @private
* @function Highcharts.Pointer#onContainerMouseDown
*/
Pointer.prototype.onContainerMouseDown = function (e) {
var _a;
var isPrimaryButton = ((e.buttons || e.button) & 1) === 1;
e = this.normalize(e);
// #11635, Firefox does not reliably fire move event after click scroll
if (Core_Globals.isFirefox &&
e.button !== 0) {
this.onContainerMouseMove(e);
}
// #11635, limiting to primary button
if (typeof e.button === 'undefined' ||
isPrimaryButton) {
this.zoomOption(e);
// #295, #13737 solve conflict between container drag and chart zoom
if (isPrimaryButton) {
(_a = e.preventDefault) === null || _a === void 0 ? void 0 : _a.call(e);
}
this.dragStart(e);
}
};
/**
* When mouse leaves the container, hide the tooltip.
* @private
* @function Highcharts.Pointer#onContainerMouseLeave
*/
Pointer.prototype.onContainerMouseLeave = function (e) {
var pointer = (Pointer_charts[Pointer_pick(Pointer.hoverChartIndex, -1)] || {}).pointer;
e = this.normalize(e);
this.onContainerMouseMove(e);
// #4886, MS Touch end fires mouseleave but with no related target
if (pointer &&
!this.inClass(e.relatedTarget, 'highcharts-tooltip')) {
pointer.reset();
// Also reset the chart position, used in #149 fix
pointer.chartPosition = void 0;
}
};
/**
* When mouse enters the container, delete pointer's chartPosition.
* @private
* @function Highcharts.Pointer#onContainerMouseEnter
*/
Pointer.prototype.onContainerMouseEnter = function () {
delete this.chartPosition;
};
/**
* The mousemove, touchmove and touchstart event handler
* @private
* @function Highcharts.Pointer#onContainerMouseMove
*/
Pointer.prototype.onContainerMouseMove = function (e) {
var chart = this.chart,
tooltip = chart.tooltip,
pEvt = this.normalize(e);
this.setHoverChartIndex(e);
if (chart.mouseIsDown === 'mousedown' || this.touchSelect(pEvt)) {
this.drag(pEvt);
}
// Show the tooltip and run mouse over events (#977)
if (!chart.openMenu &&
(this.inClass(pEvt.target, 'highcharts-tracker') ||
chart.isInsidePlot(pEvt.chartX - chart.plotLeft, pEvt.chartY - chart.plotTop, {
visiblePlotOnly: true
})) &&
// If the tooltip has stickOnContact enabled, do nothing. This
// applies regardless of any combinations of the `split` and
// `useHTML` options.
!(tooltip &&
tooltip.shouldStickOnContact(pEvt))) {
if (this.inClass(pEvt.target, 'highcharts-no-tooltip')) {
this.reset(false, 0);
}
else {
this.runPointActions(pEvt);
}
}
};
/**
* @private
* @function Highcharts.Pointer#onDocumentTouchEnd
*/
Pointer.prototype.onDocumentTouchEnd = function (e) {
this.onDocumentMouseUp(e);
};
/**
* @private
* @function Highcharts.Pointer#onContainerTouchMove
*/
Pointer.prototype.onContainerTouchMove = function (e) {
if (this.touchSelect(e)) {
this.onContainerMouseMove(e);
}
else {
this.touch(e);
}
};
/**
* @private
* @function Highcharts.Pointer#onContainerTouchStart
*/
Pointer.prototype.onContainerTouchStart = function (e) {
if (this.touchSelect(e)) {
this.onContainerMouseDown(e);
}
else {
this.zoomOption(e);
this.touch(e, true);
}
};
/**
* Special handler for mouse move that will hide the tooltip when the mouse
* leaves the plotarea. Issue #149 workaround. The mouseleave event does not
* always fire.
* @private
* @function Highcharts.Pointer#onDocumentMouseMove
*/
Pointer.prototype.onDocumentMouseMove = function (e) {
var chart = this.chart;
var tooltip = chart.tooltip;
var chartPosition = this.chartPosition;
var pEvt = this.normalize(e,
chartPosition);
// If we're outside, hide the tooltip
if (chartPosition &&
!chart.isInsidePlot(pEvt.chartX - chart.plotLeft, pEvt.chartY - chart.plotTop, {
visiblePlotOnly: true
}) &&
!(tooltip &&
tooltip.shouldStickOnContact(pEvt)) && (pEvt.target === chart.container.ownerDocument ||
!this.inClass(pEvt.target, 'highcharts-tracker'))) {
this.reset();
}
};
/**
* @private
* @function Highcharts.Pointer#onDocumentMouseUp
*/
Pointer.prototype.onDocumentMouseUp = function (e) {
var _a,
_b;
(_b = (_a = Pointer_charts[Pointer_pick(Pointer.hoverChartIndex, -1)]) === null || _a === void 0 ? void 0 : _a.pointer) === null || _b === void 0 ? void 0 : _b.drop(e);
};
/**
* Handle touch events with two touches
* @private
* @function Highcharts.Pointer#pinch
*/
Pointer.prototype.pinch = function (e) {
var _this = this;
var pointer = this,
chart = pointer.chart,
hasZoom = pointer.hasZoom,
lastTouches = pointer.lastTouches,
touches = [].map.call(e.touches || [],
// Normalize each touch
function (touch) { return pointer.normalize(touch); }),
touchesLength = touches.length,
fireClickEvent = touchesLength === 1 && ((pointer.inClass(e.target, 'highcharts-tracker') &&
chart.runTrackerClick) ||
pointer.runChartClick),
tooltip = chart.tooltip,
followTouchMove = touchesLength === 1 &&
Pointer_pick(tooltip === null || tooltip === void 0 ? void 0 : tooltip.options.followTouchMove,
true);
// Don't initiate panning until the user has pinched. This prevents us
// from blocking page scrolling as users scroll down a long page
// (#4210).
if (touchesLength > 1) {
pointer.initiated = true;
}
else if (followTouchMove) {
// #16119: Prevent blocking scroll when single-finger panning is
// not enabled
pointer.initiated = false;
}
// On touch devices, only proceed to trigger click if a handler is
// defined
if (hasZoom &&
pointer.initiated &&
!fireClickEvent &&
e.cancelable !== false) {
e.preventDefault();
}
// Register the touch start position
if (e.type === 'touchstart') {
pointer.pinchDown = touches;
pointer.res = true; // Reset on next move
chart.mouseDownX = e.chartX;
// Optionally move the tooltip on touchmove
}
else if (followTouchMove) {
this.runPointActions(pointer.normalize(e));
// Event type is touchmove, handle panning and pinching. The length can
// be 0 when releasing, if touchend fires first
}
else if (lastTouches) {
Pointer_fireEvent(chart, 'touchpan', {
originalEvent: e,
touches: touches
}, function () {
var boxFromTouches = function (touches) {
var finger0 = touches[0],
finger1 = touches[1] || finger0;
return {
x: finger0.chartX,
y: finger0.chartY,
width: finger1.chartX - finger0.chartX,
height: finger1.chartY - finger0.chartY
};
};
chart.transform({
axes: chart.axes
.filter(function (axis) {
return axis.zoomEnabled &&
((_this.zoomHor && axis.horiz) ||
(_this.zoomVert && !axis.horiz));
}),
to: boxFromTouches(touches),
from: boxFromTouches(lastTouches),
trigger: e.type
});
});
if (pointer.res) {
pointer.res = false;
this.reset(false, 0);
}
}
pointer.lastTouches = touches;
};
/**
* Run translation operations
* @private
* @function Highcharts.Pointer#pinchTranslate
* /
public pinchTranslate(
pinchDown: Array<any>,
touches: Array<PointerEvent>,
transform: any,
selectionMarker: any,
clip: any,
lastValidTouch: any
): void {
if (this.zoomHor) {
this.pinchTranslateDirection(
true,
pinchDown,
touches,
transform,
selectionMarker,
clip,
lastValidTouch
);
}
if (this.zoomVert) {
this.pinchTranslateDirection(
false,
pinchDown,
touches,
transform,
selectionMarker,
clip,
lastValidTouch
);
}
}
*/
/**
* Run translation operations for each direction (horizontal and vertical)
* independently.
* @private
* @function Highcharts.Pointer#pinchTranslateDirection
* /
public pinchTranslateDirection(
horiz: boolean,
pinchDown: Array<any>,
touches: Array<PointerEvent>,
transform: any,
selectionMarker: any,
clip: any,
lastValidTouch: any,
forcedScale?: number
): void {
const chart = this.chart,
xy: ('x'|'y') = horiz ? 'x' : 'y',
XY: ('X'|'Y') = horiz ? 'X' : 'Y',
sChartXY: ('chartX'|'chartY') = ('chart' + XY) as any,
wh = horiz ? 'width' : 'height',
plotLeftTop = (chart as any)['plot' + (horiz ? 'Left' : 'Top')],
inverted = chart.inverted,
bounds = chart.bounds[horiz ? 'h' : 'v'],
singleTouch = pinchDown.length === 1,
touch0Start = pinchDown[0][sChartXY],
touch1Start = !singleTouch && pinchDown[1][sChartXY],
setScale = function (): void {
// Don't zoom if fingers are too close on this axis
if (
typeof touch1Now === 'number' &&
Math.abs(touch0Start - touch1Start) > 20
) {
scale = forcedScale ||
Math.abs(touch0Now - touch1Now) /
Math.abs(touch0Start - touch1Start);
}
clipXY = ((plotLeftTop - touch0Now) / scale) + touch0Start;
selectionWH = (chart as any)[
'plot' + (horiz ? 'Width' : 'Height')
] / scale;
};
let selectionWH: any,
selectionXY,
clipXY: any,
scale = forcedScale || 1,
touch0Now = touches[0][sChartXY],
touch1Now = !singleTouch && touches[1][sChartXY],
outOfBounds;
// Set the scale, first pass
setScale();
// The clip position (x or y) is altered if out of bounds, the selection
// position is not
selectionXY = clipXY;
// Out of bounds
if (selectionXY < bounds.min) {
selectionXY = bounds.min;
outOfBounds = true;
} else if (selectionXY + selectionWH > bounds.max) {
selectionXY = bounds.max - selectionWH;
outOfBounds = true;
}
// Is the chart dragged off its bounds, determined by dataMin and
// dataMax?
if (outOfBounds) {
// Modify the touchNow position in order to create an elastic drag
// movement. This indicates to the user that the chart is responsive
// but can't be dragged further.
touch0Now -= 0.8 * (touch0Now - lastValidTouch[xy][0]);
if (typeof touch1Now === 'number') {
touch1Now -= 0.8 * (touch1Now - lastValidTouch[xy][1]);
}
// Set the scale, second pass to adapt to the modified touchNow
// positions
setScale();
} else {
lastValidTouch[xy] = [touch0Now, touch1Now];
}
// Set geometry for clipping, selection and transformation
if (!inverted) {
clip[xy] = clipXY - plotLeftTop;
clip[wh] = selectionWH;
}
const scaleKey = inverted ?
(horiz ? 'scaleY' : 'scaleX') : 'scale' + XY;
const transformScale = inverted ? 1 / scale : scale;
selectionMarker[wh] = selectionWH;
selectionMarker[xy] = selectionXY;
// Invert scale if needed (#19217)
transform[scaleKey] = scale * (inverted && !horiz ? -1 : 1);
transform['translate' + XY] = (transformScale * plotLeftTop) +
(touch0Now - (transformScale * touch0Start));
}
*/
/**
* Reset the tracking by hiding the tooltip, the hover series state and the
* hover point
*
* @function Highcharts.Pointer#reset
*
* @param {boolean} [allowMove]
* Instead of destroying the tooltip altogether, allow moving it if
* possible.
*
* @param {number} [delay]
*/
Pointer.prototype.reset = function (allowMove, delay) {
var pointer = this,
chart = pointer.chart,
hoverSeries = chart.hoverSeries,
hoverPoint = chart.hoverPoint,
hoverPoints = chart.hoverPoints,
tooltip = chart.tooltip,
tooltipPoints = tooltip && tooltip.shared ?
hoverPoints :
hoverPoint;
// Check if the points have moved outside the plot area (#1003, #4736,
// #5101)
if (allowMove && tooltipPoints) {
Pointer_splat(tooltipPoints).forEach(function (point) {
if (point.series.isCartesian &&
typeof point.plotX === 'undefined') {
allowMove = false;
}
});
}
// Just move the tooltip, #349
if (allowMove) {
if (tooltip && tooltipPoints && Pointer_splat(tooltipPoints).length) {
tooltip.refresh(tooltipPoints);
if (tooltip.shared && hoverPoints) { // #8284
hoverPoints.forEach(function (point) {
point.setState(point.state, true);
if (point.series.isCartesian) {
if (point.series.xAxis.crosshair) {
point.series.xAxis
.drawCrosshair(null, point);
}
if (point.series.yAxis.crosshair) {
point.series.yAxis
.drawCrosshair(null, point);
}
}
});
}
else if (hoverPoint) { // #2500
hoverPoint.setState(hoverPoint.state, true);
chart.axes.forEach(function (axis) {
if (axis.crosshair &&
hoverPoint.series[axis.coll] === axis) {
axis.drawCrosshair(null, hoverPoint);
}
});
}
}
// Full reset
}
else {
if (hoverPoint) {
hoverPoint.onMouseOut();
}
if (hoverPoints) {
hoverPoints.forEach(function (point) {
point.setState();
});
}
if (hoverSeries) {
hoverSeries.onMouseOut();
}
if (tooltip) {
tooltip.hide(delay);
}
if (pointer.unDocMouseMove) {
pointer.unDocMouseMove = pointer.unDocMouseMove();
}
// Remove crosshairs
chart.axes.forEach(function (axis) {
axis.hideCrosshair();
});
chart.hoverPoints = chart.hoverPoint = void 0;
}
};
/**
* With line type charts with a single tracker, get the point closest to the
* mouse. Run Point.onMouseOver and display tooltip for the point or points.
*
* @private
* @function Highcharts.Pointer#runPointActions
*
* @emits Highcharts.Point#event:mouseOut
* @emits Highcharts.Point#event:mouseOver
*/
Pointer.prototype.runPointActions = function (e, p, force) {
var pointer = this,
chart = pointer.chart,
series = chart.series,
tooltip = (chart.tooltip && chart.tooltip.options.enabled ?
chart.tooltip :
void 0),
shared = (tooltip ?
tooltip.shared :
false);
var hoverPoint = p || chart.hoverPoint,
hoverSeries = hoverPoint && hoverPoint.series || chart.hoverSeries;
var // `onMouseOver` or already hovering a series with directTouch
isDirectTouch = (!e || e.type !== 'touchmove') && (!!p || ((hoverSeries && hoverSeries.directTouch) &&
pointer.isDirectTouch)),
hoverData = this.getHoverData(hoverPoint,
hoverSeries,
series,
isDirectTouch,
shared,
e);
// Update variables from hoverData.
hoverPoint = hoverData.hoverPoint;
hoverSeries = hoverData.hoverSeries;
var points = hoverData.hoverPoints,
followPointer = hoverSeries &&
hoverSeries.tooltipOptions.followPointer &&
!hoverSeries.tooltipOptions.split,
useSharedTooltip = (shared &&
hoverSeries &&
!hoverSeries.noSharedTooltip);
// Refresh tooltip for kdpoint if new hover point or tooltip was hidden
// #3926, #4200
if (hoverPoint &&
(force ||
hoverPoint !== chart.hoverPoint ||
(tooltip && tooltip.isHidden))) {
(chart.hoverPoints || []).forEach(function (p) {
if (points.indexOf(p) === -1) {
p.setState();
}
});
// Set normal state to previous series
if (chart.hoverSeries !== hoverSeries) {
hoverSeries.onMouseOver();
}
pointer.applyInactiveState(points);
// Do mouseover on all points (#3919, #3985, #4410, #5622)
(points || []).forEach(function (p) {
p.setState('hover');
});
// If tracking is on series in stead of on each point,
// fire mouseOver on hover point. // #4448
if (chart.hoverPoint) {
chart.hoverPoint.firePointEvent('mouseOut');
}
// Hover point may have been destroyed in the event handlers (#7127)
if (!hoverPoint.series) {
return;
}
/**
* Contains all hovered points.
*
* @name Highcharts.Chart#hoverPoints
* @type {Array<Highcharts.Point>|null}
*/
chart.hoverPoints = points;
/**
* Contains the original hovered point.
*
* @name Highcharts.Chart#hoverPoint
* @type {Highcharts.Point|null}
*/
chart.hoverPoint = hoverPoint;
/**
* Hover state should not be lost when axis is updated (#12569)
* Axis.update runs pointer.reset which uses chart.hoverPoint.state
* to apply state which does not exist in hoverPoint yet.
* The mouseOver event should be triggered when hoverPoint
* is correct.
*/
hoverPoint.firePointEvent('mouseOver', void 0, function () {
// Draw tooltip if necessary
if (tooltip && hoverPoint) {
tooltip.refresh(useSharedTooltip ? points : hoverPoint, e);
}
});
// Update positions (regardless of kdpoint or hoverPoint)
}
else if (followPointer && tooltip && !tooltip.isHidden) {
var anchor = tooltip.getAnchor([{}],
e);
if (chart.isInsidePlot(anchor[0], anchor[1], {
visiblePlotOnly: true
})) {
tooltip.updatePosition({ plotX: anchor[0], plotY: anchor[1] });
}
}
// Start the event listener to pick up the tooltip and crosshairs
if (!pointer.unDocMouseMove) {
pointer.unDocMouseMove = Pointer_addEvent(chart.container.ownerDocument, 'mousemove', function (e) {
var _a,
_b,
_c;
return (_c = (_b = Pointer_charts[(_a = Pointer.hoverChartIndex) !== null && _a !== void 0 ? _a : -1]) === null || _b === void 0 ? void 0 : _b.pointer) === null || _c === void 0 ? void 0 : _c.onDocumentMouseMove(e);
});
pointer.eventsToUnbind.push(pointer.unDocMouseMove);
}
// Issues related to crosshair #4927, #5269 #5066, #5658
chart.axes.forEach(function drawAxisCrosshair(axis) {
var snap = Pointer_pick((axis.crosshair || {}).snap,
true);
var point;
if (snap) {
point = chart.hoverPoint; // #13002
if (!point || point.series[axis.coll] !== axis) {
point = Pointer_find(points, function (p) {
return p.series && p.series[axis.coll] === axis;
});
}
}
// Axis has snapping crosshairs, and one of the hover points belongs
// to axis. Always call drawCrosshair when it is not snap.
if (point || !snap) {
axis.drawCrosshair(e, point);
// Axis has snapping crosshairs, but no hover point belongs to axis
}
else {
axis.hideCrosshair();
}
});
};
/**
* Set the JS DOM events on the container and document. This method should
* contain a one-to-one assignment between methods and their handlers. Any
* advanced logic should be moved to the handler reflecting the event's
* name.
* @private
* @function Highcharts.Pointer#setDOMEvents
*/
Pointer.prototype.setDOMEvents = function () {
var _this = this;
var container = this.chart.container,
ownerDoc = container.ownerDocument;
container.onmousedown = this.onContainerMouseDown.bind(this);
container.onmousemove = this.onContainerMouseMove.bind(this);
container.onclick = this.onContainerClick.bind(this);
this.eventsToUnbind.push(Pointer_addEvent(container, 'mouseenter', this.onContainerMouseEnter.bind(this)), Pointer_addEvent(container, 'mouseleave', this.onContainerMouseLeave.bind(this)));
if (!Pointer.unbindDocumentMouseUp.some(function (el) { return el.doc === ownerDoc; })) {
Pointer.unbindDocumentMouseUp.push({
doc: ownerDoc,
unbind: Pointer_addEvent(ownerDoc, 'mouseup', this.onDocumentMouseUp.bind(this))
});
}
// In case we are dealing with overflow, reset the chart position when
// scrolling parent elements
var parent = this.chart.renderTo.parentElement;
while (parent && parent.tagName !== 'BODY') {
this.eventsToUnbind.push(Pointer_addEvent(parent, 'scroll', function () {
delete _this.chartPosition;
}));
parent = parent.parentElement;
}
this.eventsToUnbind.push(Pointer_addEvent(container, 'touchstart', this.onContainerTouchStart.bind(this), { passive: false }), Pointer_addEvent(container, 'touchmove', this.onContainerTouchMove.bind(this), { passive: false }));
if (!Pointer.unbindDocumentTouchEnd) {
Pointer.unbindDocumentTouchEnd = Pointer_addEvent(ownerDoc, 'touchend', this.onDocumentTouchEnd.bind(this), { passive: false });
}
this.setPointerCapture();
Pointer_addEvent(this.chart, 'redraw', this.setPointerCapture.bind(this));
};
/**
* Sets, or removes on update, pointer events using pointer capture for
* tooltip.followTouchMove if any series has findNearestPointBy that
* includes the y dimension.
* @private
* @function Highcharts.Pointer#setPointerCapture
*/
Pointer.prototype.setPointerCapture = function () {
var _a,
_b;
// Only for touch
if (!Pointer_isTouchDevice) {
return;
}
var pointer = this,
events = pointer.pointerCaptureEventsToUnbind,
chart = pointer.chart,
container = chart.container,
followTouchMove = Pointer_pick((_a = chart.options.tooltip) === null || _a === void 0 ? void 0 : _a.followTouchMove,
true),
shouldHave = followTouchMove && chart.series.some(function (series) { return series.options.findNearestPointBy
.indexOf('y') > -1; });
if (!pointer.hasPointerCapture && shouldHave) {
// Add
// Bind
events.push(Pointer_addEvent(container, 'pointerdown', function (e) {
var _a,
_b;
if ((_a = e.target) === null || _a === void 0 ? void 0 : _a.hasPointerCapture(e.pointerId)) {
(_b = e.target) === null || _b === void 0 ? void 0 : _b.releasePointerCapture(e.pointerId);
}
}), Pointer_addEvent(container, 'pointermove', function (e) {
var _a,
_b;
(_b = (_a = chart.pointer) === null || _a === void 0 ? void 0 : _a.getPointFromEvent(e)) === null || _b === void 0 ? void 0 : _b.onMouseOver(e);
}));
if (!chart.styledMode) {
Pointer_css(container, { 'touch-action': 'none' });
}
// Mostly for styled mode
container.className += ' highcharts-no-touch-action';
pointer.hasPointerCapture = true;
}
else if (pointer.hasPointerCapture && !shouldHave) {
// Remove
// Unbind
events.forEach(function (e) { return e(); });
events.length = 0;
if (!chart.styledMode) {
Pointer_css(container, {
'touch-action': Pointer_pick((_b = chart.options.chart.style) === null || _b === void 0 ? void 0 : _b['touch-action'], 'manipulation')
});
}
// Mostly for styled mode
container.className = container.className.replace(' highcharts-no-touch-action', '');
pointer.hasPointerCapture = false;
}
};
/**
* Sets the index of the hovered chart and leaves the previous hovered
* chart, to reset states like tooltip.
* @private
* @function Highcharts.Pointer#setHoverChartIndex
*/
Pointer.prototype.setHoverChartIndex = function (e) {
var _a;
var chart = this.chart;
var hoverChart = Core_Globals.charts[Pointer_pick(Pointer.hoverChartIndex, -1)];
if (hoverChart &&
hoverChart !== chart) {
var relatedTargetObj = { relatedTarget: chart.container };
if (e && !(e === null || e === void 0 ? void 0 : e.relatedTarget)) {
// #17192, Non-enumerable properties of "e" are dropped with
// spreading (...e). Using Object.assign ensures integrity.
Object.assign({}, e, relatedTargetObj);
}
(_a = hoverChart.pointer) === null || _a === void 0 ? void 0 : _a.onContainerMouseLeave(e || relatedTargetObj);
}
if (!hoverChart ||
!hoverChart.mouseIsDown) {
Pointer.hoverChartIndex = chart.index;
}
};
/**
* General touch handler shared by touchstart and touchmove.
* @private
* @function Highcharts.Pointer#touch
*/
Pointer.prototype.touch = function (e, start) {
var _a = this,
chart = _a.chart,
_b = _a.pinchDown,
pinchDown = _b === void 0 ? [] : _b;
var hasMoved,
isInside;
this.setHoverChartIndex();
e = this.normalize(e);
if (e.touches.length === 1) {
isInside = chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop, {
visiblePlotOnly: true
});
if (isInside && !chart.openMenu) {
// Run mouse events and display tooltip etc
if (start) {
this.runPointActions(e);
}
// Android fires touchmove events after the touchstart even if
// the finger hasn't moved, or moved only a pixel or two. In iOS
// however, the touchmove doesn't fire unless the finger moves
// more than ~4px. So we emulate this behaviour in Android by
// checking how much it moved, and cancelling on small
// distances. #3450. Tested and still relevant as of 2024.
if (e.type === 'touchmove') {
hasMoved = pinchDown[0] ? // #5266
(Math.pow(pinchDown[0].chartX - e.chartX, 2) +
Math.pow(pinchDown[0].chartY - e.chartY, 2)) >= 16 :
false;
}
if (Pointer_pick(hasMoved, true)) {
this.pinch(e);
}
}
else if (start) {
// Hide the tooltip on touching outside the plot area (#1203)
this.reset();
}
}
else if (e.touches.length === 2) {
this.pinch(e);
}
};
/**
* Returns true if the chart is set up for zooming by single touch and the
* event is capable
* @private
* @function Highcharts.Pointer#touchSelect
*/
Pointer.prototype.touchSelect = function (e) {
return Boolean(this.chart.zooming.singleTouch &&
e.touches &&
e.touches.length === 1);
};
/**
* Resolve the zoomType option, this is reset on all touch start and mouse
* down events.
* @private
* @function Highcharts.Pointer#zoomOption
*/
Pointer.prototype.zoomOption = function (e) {
var chart = this.chart,
inverted = chart.inverted;
var zoomType = chart.zooming.type || '',
zoomX,
zoomY;
// Look for the pinchType option
if (/touch/.test(e.type)) {
zoomType = Pointer_pick(chart.zooming.pinchType, zoomType);
}
this.zoomX = zoomX = /x/.test(zoomType);
this.zoomY = zoomY = /y/.test(zoomType);
this.zoomHor = (zoomX && !inverted) || (zoomY && inverted);
this.zoomVert = (zoomY && !inverted) || (zoomX && inverted);
this.hasZoom = zoomX || zoomY;
};
Pointer.unbindDocumentMouseUp = [];
return Pointer;
}());
/* *
*
* Class Namespace
*
* */
(function (Pointer) {
/* *
*
* Declarations
*
* */
/* *
*
* Functions
*
* */
/**
* @private
*/
function compose(ChartClass) {
if (Pointer_pushUnique(Pointer_composed, 'Core.Pointer')) {
Pointer_addEvent(ChartClass, 'beforeRender', function () {
/**
* The Pointer that keeps track of mouse and touch
* interaction.
*
* @memberof Highcharts.Chart
* @name pointer
* @type {Highcharts.Pointer}
* @instance
*/
this.pointer = new Pointer(this, this.options);
});
}
}
Pointer.compose = compose;
})(Pointer || (Pointer = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Core_Pointer = (Pointer);
/* *
*
* API Declarations
*
* */
/**
* Chart position and scale.
*
* @interface Highcharts.ChartPositionObject
*/ /**
* @name Highcharts.ChartPositionObject#left
* @type {number}
*/ /**
* @name Highcharts.ChartPositionObject#scaleX
* @type {number}
*/ /**
* @name Highcharts.ChartPositionObject#scaleY
* @type {number}
*/ /**
* @name Highcharts.ChartPositionObject#top
* @type {number}
*/
/**
* One position in relation to an axis.
*
* @interface Highcharts.PointerAxisCoordinateObject
*/ /**
* Related axis.
*
* @name Highcharts.PointerAxisCoordinateObject#axis
* @type {Highcharts.Axis}
*/ /**
* Axis value.
*
* @name Highcharts.PointerAxisCoordinateObject#value
* @type {number}
*/
/**
* Positions in terms of axis values.
*
* @interface Highcharts.PointerAxisCoordinatesObject
*/ /**
* Positions on the x-axis.
* @name Highcharts.PointerAxisCoordinatesObject#xAxis
* @type {Array<Highcharts.PointerAxisCoordinateObject>}
*/ /**
* Positions on the y-axis.
* @name Highcharts.PointerAxisCoordinatesObject#yAxis
* @type {Array<Highcharts.PointerAxisCoordinateObject>}
*/
/**
* Pointer coordinates.
*
* @interface Highcharts.PointerCoordinatesObject
*/ /**
* @name Highcharts.PointerCoordinatesObject#chartX
* @type {number}
*/ /**
* @name Highcharts.PointerCoordinatesObject#chartY
* @type {number}
*/
/**
* A native browser mouse or touch event, extended with position information
* relative to the {@link Chart.container}.
*
* @interface Highcharts.PointerEventObject
* @extends global.PointerEvent
*/ /**
* The X coordinate of the pointer interaction relative to the chart.
*
* @name Highcharts.PointerEventObject#chartX
* @type {number}
*/ /**
* The Y coordinate of the pointer interaction relative to the chart.
*
* @name Highcharts.PointerEventObject#chartY
* @type {number}
*/
/**
* Axis-specific data of a selection.
*
* @interface Highcharts.SelectDataObject
*/ /**
* The selected Axis.
* @name Highcharts.SelectDataObject#axis
* @type {Highcharts.Axis}
*/ /**
* The maximum axis value, either automatic or set manually.
* @name Highcharts.SelectDataObject#max
* @type {number}
*/ /**
* The minimum axis value, either automatic or set manually.
* @name Highcharts.SelectDataObject#min
* @type {number}
*/
/**
* Object for select events.
* The primary axes are `xAxis[0]` and `yAxis[0]`. Remember the unit of a
* datetime axis is milliseconds since 1970-01-01 00:00:00.
*
* @interface Highcharts.SelectEventObject
*/ /**
* The related browser event.
* @name Highcharts.SelectEventObject#originalEvent
* @type {global.Event}
*/ /**
* Prevents the default action for the event, if called.
* @name Highcharts.SelectEventObject#preventDefault
* @type {Function}
*/ /**
* Indicates a reset event to restore default state.
* @name Highcharts.SelectEventObject#resetSelection
* @type {boolean|undefined}
*/ /**
* Arrays containing the axes of each dimension and each axis' min and max
* values.
* @name Highcharts.SelectEventObject#xAxis
* @type {Array<Highcharts.SelectDataObject>}
*/ /**
* Arrays containing the axes of each dimension and each axis' min and max
* values.
* @name Highcharts.SelectEventObject#yAxis
* @type {Array<Highcharts.SelectDataObject>}
*/
''; // Keeps doclets above in JS file
;// ./code/es5/es-modules/Data/DataTableCore.js
/* *
*
* (c) 2009-2024 Highsoft AS
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* Authors:
* - Sophie Bremer
* - Gøran Slettemark
* - Torstein Hønsi
*
* */
var DataTableCore_fireEvent = Core_Utilities.fireEvent, DataTableCore_isArray = Core_Utilities.isArray, DataTableCore_objectEach = Core_Utilities.objectEach, DataTableCore_uniqueKey = Core_Utilities.uniqueKey;
/* *
*
* 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.
*/
var DataTableCore = /** @class */ (function () {
/**
* 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.
*/
function DataTableCore(options) {
if (options === void 0) { options = {}; }
var _this = this;
/**
* 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 indentification purposes.
*
* @name Highcharts.DataTable#id
* @type {string}
*/
this.id = (options.id || DataTableCore_uniqueKey());
this.modified = this;
this.rowCount = 0;
this.versionTag = DataTableCore_uniqueKey();
var rowCount = 0;
DataTableCore_objectEach(options.columns || {}, function (column, columnName) {
_this.columns[columnName] = 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.
*/
DataTableCore.prototype.applyRowCount = function (rowCount) {
this.rowCount = rowCount;
DataTableCore_objectEach(this.columns, function (column) {
if (DataTableCore_isArray(column)) { // Not on typed array
column.length = rowCount;
}
});
};
/**
* Fetches the given column by the canonical column name. Simplified version
* of the full `DataTable.getRow` method, always returning by reference.
*
* @param {string} columnName
* Name of the column to get.
*
* @return {Highcharts.DataTableColumn|undefined}
* A copy of the column, or `undefined` if not found.
*/
DataTableCore.prototype.getColumn = function (columnName,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
asReference) {
return this.columns[columnName];
};
/**
* Retrieves all or the given columns. Simplified version of the full
* `DataTable.getColumns` method, always returning by reference.
*
* @param {Array<string>} [columnNames]
* Column names to retrieve.
*
* @return {Highcharts.DataTableColumnCollection}
* Collection of columns. If a requested column was not found, it is
* `undefined`.
*/
DataTableCore.prototype.getColumns = function (columnNames,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
asReference) {
var _this = this;
return (columnNames || Object.keys(this.columns)).reduce(function (columns, columnName) {
columns[columnName] = _this.columns[columnName];
return columns;
}, {});
};
/**
* Retrieves the row at a given index.
*
* @param {number} rowIndex
* Row index to retrieve. First row has index 0.
*
* @param {Array<string>} [columnNames]
* Column names to retrieve.
*
* @return {Record<string, number|string|undefined>|undefined}
* Returns the row values, or `undefined` if not found.
*/
DataTableCore.prototype.getRow = function (rowIndex, columnNames) {
var _this = this;
return (columnNames || Object.keys(this.columns)).map(function (key) { var _a; return (_a = _this.columns[key]) === null || _a === void 0 ? void 0 : _a[rowIndex]; });
};
/**
* Sets cell values for a column. Will insert a new column, if not found.
*
* @param {string} columnName
* Column name to set.
*
* @param {Highcharts.DataTableColumn} [column]
* Values to set in the column.
*
* @param {number} [rowIndex=0]
* 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
*/
DataTableCore.prototype.setColumn = function (columnName, column, rowIndex, eventDetail) {
var _a;
if (column === void 0) { column = []; }
if (rowIndex === void 0) { rowIndex = 0; }
this.setColumns((_a = {}, _a[columnName] = column, _a), rowIndex, eventDetail);
};
/**
* * Sets cell values for multiple columns. Will insert new columns, if not
* found. Simplified version of the full `DataTable.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. Keep undefined to reset.
*
* @param {Record<string, (boolean|number|string|null|undefined)>} [eventDetail]
* Custom information for pending events.
*
* @emits #setColumns
* @emits #afterSetColumns
*/
DataTableCore.prototype.setColumns = function (columns, rowIndex, eventDetail) {
var _this = this;
var rowCount = this.rowCount;
DataTableCore_objectEach(columns, function (column, columnName) {
_this.columns[columnName] = column.slice();
rowCount = column.length;
});
this.applyRowCount(rowCount);
if (!(eventDetail === null || eventDetail === void 0 ? void 0 : eventDetail.silent)) {
DataTableCore_fireEvent(this, 'afterSetColumns');
this.versionTag = DataTableCore_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 `undefind` 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
*/
DataTableCore.prototype.setRow = function (row, rowIndex, insert, eventDetail) {
if (rowIndex === void 0) { rowIndex = this.rowCount; }
var columns = this.columns,
indexRowCount = insert ? this.rowCount + 1 : rowIndex + 1;
DataTableCore_objectEach(row, function (cellValue, columnName) {
var column = columns[columnName] ||
(eventDetail === null || eventDetail === void 0 ? void 0 : eventDetail.addColumns) !== false && new Array(indexRowCount);
if (column) {
if (insert) {
column.splice(rowIndex, 0, cellValue);
}
else {
column[rowIndex] = cellValue;
}
columns[columnName] = column;
}
});
if (indexRowCount > this.rowCount) {
this.applyRowCount(indexRowCount);
}
if (!(eventDetail === null || eventDetail === void 0 ? void 0 : eventDetail.silent)) {
DataTableCore_fireEvent(this, 'afterSetRows');
this.versionTag = DataTableCore_uniqueKey();
}
};
return DataTableCore;
}());
/* *
*
* Default Export
*
* */
/* harmony default export */ var Data_DataTableCore = (DataTableCore);
/* *
*
* API Declarations
*
* */
/**
* A column of values in a data table.
* @typedef {Array<boolean|null|number|string|undefined>} 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/es5/es-modules/Core/Legend/LegendSymbol.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var LegendSymbol_spreadArray = (undefined && undefined.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var LegendSymbol_extend = Core_Utilities.extend, LegendSymbol_merge = Core_Utilities.merge, LegendSymbol_pick = Core_Utilities.pick;
/* *
*
* Namespace
*
* */
var LegendSymbol;
(function (LegendSymbol) {
/* *
*
* Functions
*
* */
/**
* Draw a line, a point marker and an area in the legend.
*
* @private
* @function Highcharts.LegendSymbolMixin.areaMarker
*
* @param {Highcharts.Legend} legend
* The legend object.
*/
function areaMarker(legend, item) {
lineMarker.call(this, legend, item, true);
}
LegendSymbol.areaMarker = areaMarker;
/**
* Draw a line and a point marker in the legend.
*
* @private
* @function Highcharts.LegendSymbolMixin.lineMarker
*
* @param {Highcharts.Legend} legend
* The legend object.
*/
function lineMarker(legend, item, hasArea) {
var _a,
_b;
var legendItem = this.legendItem = this.legendItem || {},
_c = this,
chart = _c.chart,
options = _c.options,
_d = legend.baseline,
baseline = _d === void 0 ? 0 : _d,
symbolWidth = legend.symbolWidth,
symbolHeight = legend.symbolHeight,
symbol = this.symbol || 'circle',
generalRadius = symbolHeight / 2,
renderer = chart.renderer,
legendItemGroup = legendItem.group,
verticalCenter = baseline - Math.round((((_a = legend.fontMetrics) === null || _a === void 0 ? void 0 : _a.b) || symbolHeight) *
// Render line and marker slightly higher to make room for the
// area
(hasArea ? 0.4 : 0.3)),
attr = {};
var legendSymbol,
markerOptions = options.marker,
lineSizer = 0;
// Draw the line
if (!chart.styledMode) {
attr['stroke-width'] = Math.min(options.lineWidth || 0, 24);
if (options.dashStyle) {
attr.dashstyle = options.dashStyle;
}
else if (options.linecap !== 'square') {
attr['stroke-linecap'] = 'round';
}
}
legendItem.line = renderer
.path()
.addClass('highcharts-graph')
.attr(attr)
.add(legendItemGroup);
if (hasArea) {
legendItem.area = renderer
.path()
.addClass('highcharts-area')
.add(legendItemGroup);
}
if (attr['stroke-linecap']) {
lineSizer = Math.min(legendItem.line.strokeWidth(), symbolWidth) / 2;
}
if (symbolWidth) {
var d = [
['M',
lineSizer,
verticalCenter],
['L',
symbolWidth - lineSizer,
verticalCenter]
];
legendItem.line.attr({ d: d });
(_b = legendItem.area) === null || _b === void 0 ? void 0 : _b.attr({
d: LegendSymbol_spreadArray(LegendSymbol_spreadArray([], d, true), [
['L', symbolWidth - lineSizer, baseline],
['L', lineSizer, baseline]
], false)
});
}
// Draw the marker
if (markerOptions && markerOptions.enabled !== false && symbolWidth) {
// Do not allow the marker to be larger than the symbolHeight
var radius = Math.min(LegendSymbol_pick(markerOptions.radius,
generalRadius),
generalRadius);
// Restrict symbol markers size
if (symbol.indexOf('url') === 0) {
markerOptions = LegendSymbol_merge(markerOptions, {
width: symbolHeight,
height: symbolHeight
});
radius = 0;
}
legendItem.symbol = legendSymbol = renderer
.symbol(symbol, (symbolWidth / 2) - radius, verticalCenter - radius, 2 * radius, 2 * radius, LegendSymbol_extend({ context: 'legend' }, markerOptions))
.addClass('highcharts-point')
.add(legendItemGroup);
legendSymbol.isMarker = true;
}
}
LegendSymbol.lineMarker = lineMarker;
/**
* Get the series' symbol in the legend.
*
* This method should be overridable to create custom symbols through
* Highcharts.seriesTypes[type].prototype.drawLegendSymbol.
*
* @private
* @function Highcharts.LegendSymbolMixin.rectangle
*
* @param {Highcharts.Legend} legend
* The legend object
*
* @param {Highcharts.Point|Highcharts.Series} item
* The series (this) or point
*/
function rectangle(legend, item) {
var legendItem = item.legendItem || {},
options = legend.options,
symbolHeight = legend.symbolHeight,
square = options.squareSymbol,
symbolWidth = square ? symbolHeight : legend.symbolWidth;
legendItem.symbol = this.chart.renderer
.rect(square ? (legend.symbolWidth - symbolHeight) / 2 : 0, legend.baseline - symbolHeight + 1, // #3988
symbolWidth, symbolHeight, LegendSymbol_pick(legend.options.symbolRadius, symbolHeight / 2))
.addClass('highcharts-point')
.attr({
zIndex: 3
})
.add(legendItem.group);
}
LegendSymbol.rectangle = rectangle;
})(LegendSymbol || (LegendSymbol = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Legend_LegendSymbol = (LegendSymbol);
;// ./code/es5/es-modules/Core/Series/SeriesDefaults.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* API Options
*
* */
/**
* General options for all series types.
*
* @optionparent plotOptions.series
*/
var seriesDefaults = {
// Base series options
/**
* The SVG value used for the `stroke-linecap` and `stroke-linejoin`
* of a line graph. Round means that lines are rounded in the ends and
* bends.
*
* @type {Highcharts.SeriesLinecapValue}
* @default round
* @since 3.0.7
* @apioption plotOptions.line.linecap
*/
/**
* Pixel width of the graph line.
*
* @see In styled mode, the line stroke-width can be set with the
* `.highcharts-graph` class name.
*
* @sample {highcharts} highcharts/plotoptions/series-linewidth-general/
* On all series
* @sample {highcharts} highcharts/plotoptions/series-linewidth-specific/
* On one single series
*
* @product highcharts highstock
*/
lineWidth: 2,
/**
* For some series, there is a limit that shuts down animation
* by default when the total number of points in the chart is too high.
* For example, for a column chart and its derivatives, animation does
* not run if there is more than 250 points totally. To disable this
* cap, set `animationLimit` to `Infinity`. This option works if animation
* is fired on individual points, not on a group of points like e.g. during
* the initial animation.
*
* @sample {highcharts} highcharts/plotoptions/series-animationlimit/
* Animation limit on updating individual points
*
* @type {number}
* @apioption plotOptions.series.animationLimit
*/
/**
* Allow this series' points to be selected by clicking on the graphic
* (columns, point markers, pie slices, map areas etc).
*
* The selected points can be handled by point select and unselect
* events, or collectively by the [getSelectedPoints
* ](/class-reference/Highcharts.Chart#getSelectedPoints) function.
*
* And alternative way of selecting points is through dragging.
*
* @sample {highcharts} highcharts/plotoptions/series-allowpointselect-line/
* Line
* @sample {highcharts} highcharts/plotoptions/series-allowpointselect-column/
* Column
* @sample {highcharts} highcharts/plotoptions/series-allowpointselect-pie/
* Pie
* @sample {highcharts} highcharts/chart/events-selection-points/
* Select a range of points through a drag selection
* @sample {highmaps} maps/plotoptions/series-allowpointselect/
* Map area
* @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
* Map bubble
*
* @since 1.2.0
*
* @private
*/
allowPointSelect: false,
/**
* When true, each point or column edge is rounded to its nearest pixel
* in order to render sharp on screen. In some cases, when there are a
* lot of densely packed columns, this leads to visible difference
* in column widths or distance between columns. In these cases,
* setting `crisp` to `false` may look better, even though each column
* is rendered blurry.
*
* @sample {highcharts} highcharts/plotoptions/column-crisp-false/
* Crisp is false
*
* @since 5.0.10
* @product highcharts highstock gantt
*
* @private
*/
crisp: true,
/**
* If true, a checkbox is displayed next to the legend item to allow
* selecting the series. The state of the checkbox is determined by
* the `selected` option.
*
* @productdesc {highmaps}
* Note that if a `colorAxis` is defined, the color axis is represented
* in the legend, not the series.
*
* @sample {highcharts} highcharts/plotoptions/series-showcheckbox-true/
* Show select box
*
* @since 1.2.0
*
* @private
*/
showCheckbox: false,
/**
* Enable or disable the initial animation when a series is displayed.
* The animation can also be set as a configuration object. Please
* note that this option only applies to the initial animation of the
* series itself. For other animations, see [chart.animation](
* #chart.animation) and the animation parameter under the API methods.
* The following properties are supported:
*
* - `defer`: The animation delay time in milliseconds.
*
* - `duration`: The duration of the animation in milliseconds. (Defaults to
* `1000`)
*
* - `easing`: Can be a string reference to an easing function set on
* the `Math` object or a function. See the _Custom easing function_
* demo below. (Defaults to `easeInOutSine`)
*
* Due to poor performance, animation is disabled in old IE browsers
* for several chart types.
*
* @sample {highcharts} highcharts/plotoptions/series-animation-disabled/
* Animation disabled
* @sample {highcharts} highcharts/plotoptions/series-animation-slower/
* Slower animation
* @sample {highcharts} highcharts/plotoptions/series-animation-easing/
* Custom easing function
* @sample {highstock} stock/plotoptions/animation-slower/
* Slower animation
* @sample {highstock} stock/plotoptions/animation-easing/
* Custom easing function
* @sample {highmaps} maps/plotoptions/series-animation-true/
* Animation enabled on map series
* @sample {highmaps} maps/plotoptions/mapbubble-animation-false/
* Disabled on mapbubble series
*
* @type {boolean|Highcharts.AnimationOptionsObject}
* @default {highcharts} true
* @default {highstock} true
* @default {highmaps} false
*
* @private
*/
animation: {
/** @ignore-option */
duration: 1000
},
/**
* An additional class name to apply to the series' graphical elements.
* This option does not replace default class names of the graphical
* element. Changes to the series' color will also be reflected in a
* chart's legend and tooltip.
*
* @sample {highcharts} highcharts/css/point-series-classname
* Series and point class name
*
* @type {string}
* @since 5.0.0
* @apioption plotOptions.series.className
*/
/**
* Disable this option to allow series rendering in the whole plotting
* area.
*
* **Note:** Clipping should be always enabled when
* [chart.zoomType](#chart.zoomType) is set
*
* @sample {highcharts} highcharts/plotoptions/series-clip/
* Disabled clipping
*
* @default true
* @type {boolean}
* @since 3.0.0
* @apioption plotOptions.series.clip
*/
/**
* The main color of the series. In line type series it applies to the
* line and the point markers unless otherwise specified. In bar type
* series it applies to the bars unless a color is specified per point.
* The default value is pulled from the `options.colors` array.
*
* In styled mode, the color can be defined by the
* [colorIndex](#plotOptions.series.colorIndex) option. Also, the series
* color can be set with the `.highcharts-series`,
* `.highcharts-color-{n}`, `.highcharts-{type}-series` or
* `.highcharts-series-{n}` class, or individual classes given by the
* `className` option.
*
* @productdesc {highmaps}
* In maps, the series color is rarely used, as most choropleth maps use
* the color to denote the value of each point. The series color can
* however be used in a map with multiple series holding categorized
* data.
*
* @sample {highcharts} highcharts/plotoptions/series-color-general/
* General plot option
* @sample {highcharts} highcharts/plotoptions/series-color-specific/
* One specific series
* @sample {highcharts} highcharts/plotoptions/series-color-area/
* Area color
* @sample {highcharts} highcharts/series/infographic/
* Pattern fill
* @sample {highmaps} maps/demo/category-map/
* Category map by multiple series
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @apioption plotOptions.series.color
*/
/**
* Styled mode only. A specific color index to use for the series, so its
* graphic representations are given the class name `highcharts-color-{n}`.
*
* Since v11, CSS variables on the form `--highcharts-color-{n}` make
* changing the color scheme very convenient.
*
* @sample {highcharts} highcharts/css/colorindex/ Series and point color
* index
*
* @type {number}
* @since 5.0.0
* @apioption plotOptions.series.colorIndex
*/
/**
* Whether to connect a graph line across null points, or render a gap
* between the two points on either side of the null.
*
* In stacked area chart, if `connectNulls` is set to true,
* null points are interpreted as 0.
*
* @sample {highcharts} highcharts/plotoptions/series-connectnulls-false/
* False by default
* @sample {highcharts} highcharts/plotoptions/series-connectnulls-true/
* True
*
* @type {boolean}
* @default false
* @product highcharts highstock
* @apioption plotOptions.series.connectNulls
*/
/**
* You can set the cursor to "pointer" if you have click events attached
* to the series, to signal to the user that the points and lines can
* be clicked.
*
* In styled mode, the series cursor can be set with the same classes
* as listed under [series.color](#plotOptions.series.color).
*
* @sample {highcharts} highcharts/plotoptions/series-cursor-line/
* On line graph
* @sample {highcharts} highcharts/plotoptions/series-cursor-column/
* On columns
* @sample {highcharts} highcharts/plotoptions/series-cursor-scatter/
* On scatter markers
* @sample {highstock} stock/plotoptions/cursor/
* Pointer on a line graph
* @sample {highmaps} maps/plotoptions/series-allowpointselect/
* Map area
* @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
* Map bubble
*
* @type {string|Highcharts.CursorValue}
* @apioption plotOptions.series.cursor
*/
/**
* A reserved subspace to store options and values for customized
* functionality. Here you can add additional data for your own event
* callbacks and formatter callbacks.
*
* @sample {highcharts} highcharts/point/custom/
* Point and series with custom data
*
* @type {Highcharts.Dictionary<*>}
* @apioption plotOptions.series.custom
*/
/**
* Name of the dash style to use for the graph, or for some series types
* the outline of each shape.
*
* In styled mode, the
* [stroke dash-array](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/series-dashstyle/)
* can be set with the same classes as listed under
* [series.color](#plotOptions.series.color).
*
* @sample {highcharts} highcharts/plotoptions/series-dashstyle-all/
* Possible values demonstrated
* @sample {highcharts} highcharts/plotoptions/series-dashstyle/
* Chart suitable for printing in black and white
* @sample {highstock} highcharts/plotoptions/series-dashstyle-all/
* Possible values demonstrated
* @sample {highmaps} highcharts/plotoptions/series-dashstyle-all/
* Possible values demonstrated
* @sample {highmaps} maps/plotoptions/series-dashstyle/
* Dotted borders on a map
*
* @type {Highcharts.DashStyleValue}
* @default Solid
* @since 2.1
* @apioption plotOptions.series.dashStyle
*/
/**
* A description of the series to add to the screen reader information
* about the series.
*
* @type {string}
* @since 5.0.0
* @requires modules/accessibility
* @apioption plotOptions.series.description
*/
/**
* Options for the series data sorting.
*
* @type {Highcharts.DataSortingOptionsObject}
* @since 8.0.0
* @product highcharts highstock
* @apioption plotOptions.series.dataSorting
*/
/**
* Enable or disable data sorting for the series. Use [xAxis.reversed](
* #xAxis.reversed) to change the sorting order.
*
* @sample {highcharts} highcharts/datasorting/animation/
* Data sorting in scatter-3d
* @sample {highcharts} highcharts/datasorting/labels-animation/
* Axis labels animation
* @sample {highcharts} highcharts/datasorting/dependent-sorting/
* Dependent series sorting
* @sample {highcharts} highcharts/datasorting/independent-sorting/
* Independent series sorting
*
* @type {boolean}
* @since 8.0.0
* @apioption plotOptions.series.dataSorting.enabled
*/
/**
* Whether to allow matching points by name in an update. If this option
* is disabled, points will be matched by order.
*
* @sample {highcharts} highcharts/datasorting/match-by-name/
* Enabled match by name
*
* @type {boolean}
* @since 8.0.0
* @apioption plotOptions.series.dataSorting.matchByName
*/
/**
* Determines what data value should be used to sort by.
*
* @sample {highcharts} highcharts/datasorting/sort-key/
* Sort key as `z` value
*
* @type {string}
* @since 8.0.0
* @default y
* @apioption plotOptions.series.dataSorting.sortKey
*/
/**
* Enable or disable the mouse tracking for a specific series. This
* includes point tooltips and click events on graphs and points. For
* large datasets it improves performance.
*
* @sample {highcharts} highcharts/plotoptions/series-enablemousetracking-false/
* No mouse tracking
* @sample {highmaps} maps/plotoptions/series-enablemousetracking-false/
* No mouse tracking
*
* @type {boolean}
* @default true
* @apioption plotOptions.series.enableMouseTracking
*/
enableMouseTracking: true,
/**
* Whether to use the Y extremes of the total chart width or only the
* zoomed area when zooming in on parts of the X axis. By default, the
* Y axis adjusts to the min and max of the visible data. Cartesian
* series only.
*
* @type {boolean}
* @default false
* @since 4.1.6
* @product highcharts highstock gantt
* @apioption plotOptions.series.getExtremesFromAll
*/
/**
* Highlight only the hovered point and fade the remaining points.
*
* Scatter-type series require enabling the 'inactive' marker state and
* adjusting opacity. Note that this approach could affect performance
* with large datasets.
*
* @sample {highcharts} highcharts/plotoptions/series-inactiveotherpoints-enabled/
* Chart with inactiveOtherPoints option enabled.
*
* @type {boolean}
* @default false
* @apioption plotOptions.series.inactiveOtherPoints
*/
/**
* An array specifying which option maps to which key in the data point
* array. This makes it convenient to work with unstructured data arrays
* from different sources.
*
* @see [series.data](#series.line.data)
*
* @sample {highcharts|highstock} highcharts/series/data-keys/
* An extended data array with keys
* @sample {highcharts|highstock} highcharts/series/data-nested-keys/
* Nested keys used to access object properties
*
* @type {Array<string>}
* @since 4.1.6
* @apioption plotOptions.series.keys
*/
/**
* The line cap used for line ends and line joins on the graph.
*
* @sample highcharts/series-line/linecap/
* Line cap comparison
*
* @type {Highcharts.SeriesLinecapValue}
* @default round
* @product highcharts highstock
* @apioption plotOptions.series.linecap
*/
/**
* The [id](#series.id) of another series to link to. Additionally,
* the value can be ":previous" to link to the previous series. When
* two series are linked, only the first one appears in the legend.
* Toggling the visibility of this also toggles the linked series.
*
* If master series uses data sorting and linked series does not have
* its own sorting definition, the linked series will be sorted in the
* same order as the master one.
*
* @sample {highcharts|highstock} highcharts/demo/arearange-line/
* Linked series
*
* @type {string}
* @since 3.0
* @product highcharts highstock gantt
* @apioption plotOptions.series.linkedTo
*/
/**
* Options for the corresponding navigator series if `showInNavigator`
* is `true` for this series. Available options are the same as any
* series, documented at [plotOptions](#plotOptions.series) and
* [series](#series).
*
* These options are merged with options in [navigator.series](
* #navigator.series), and will take precedence if the same option is
* defined both places.
*
* @see [navigator.series](#navigator.series)
*
* @type {Highcharts.PlotSeriesOptions}
* @since 5.0.0
* @product highstock
* @apioption plotOptions.series.navigatorOptions
*/
/**
* The color for the parts of the graph or points that are below the
* [threshold](#plotOptions.series.threshold). Note that `zones` takes
* precedence over the negative color. Using `negativeColor` is
* equivalent to applying a zone with value of 0.
*
* @see In styled mode, a negative color is applied by setting this option
* to `true` combined with the `.highcharts-negative` class name.
*
* @sample {highcharts} highcharts/plotoptions/series-negative-color/
* Spline, area and column
* @sample {highcharts} highcharts/plotoptions/arearange-negativecolor/
* Arearange
* @sample {highcharts} highcharts/css/series-negative-color/
* Styled mode
* @sample {highstock} highcharts/plotoptions/series-negative-color/
* Spline, area and column
* @sample {highstock} highcharts/plotoptions/arearange-negativecolor/
* Arearange
* @sample {highmaps} highcharts/plotoptions/series-negative-color/
* Spline, area and column
* @sample {highmaps} highcharts/plotoptions/arearange-negativecolor/
* Arearange
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @since 3.0
* @apioption plotOptions.series.negativeColor
*/
/**
* Same as
* [accessibility.point.descriptionFormat](#accessibility.point.descriptionFormat),
* but for an individual series. Overrides the chart wide configuration.
*
* @type {Function}
* @since 11.1.0
* @apioption plotOptions.series.pointDescriptionFormat
*/
/**
* Same as
* [accessibility.series.descriptionFormatter](#accessibility.series.descriptionFormatter),
* but for an individual series. Overrides the chart wide configuration.
*
* @type {Function}
* @since 5.0.12
* @apioption plotOptions.series.pointDescriptionFormatter
*/
/**
* If no x values are given for the points in a series, `pointInterval`
* defines the interval of the x values. For example, if a series
* contains one value every decade starting from year 0, set
* `pointInterval` to `10`. In true `datetime` axes, the `pointInterval`
* is set in milliseconds.
*
* It can be also be combined with `pointIntervalUnit` to draw irregular
* time intervals.
*
* If combined with `relativeXValue`, an x value can be set on each
* point, and the `pointInterval` is added x times to the `pointStart`
* setting.
*
* Please note that this options applies to the _series data_, not the
* interval of the axis ticks, which is independent.
*
* @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
* Datetime X axis
* @sample {highcharts} highcharts/plotoptions/series-relativexvalue/
* Relative x value
* @sample {highstock} stock/plotoptions/pointinterval-pointstart/
* Using pointStart and pointInterval
* @sample {highstock} stock/plotoptions/relativexvalue/
* Relative x value
*
* @type {number}
* @default 1
* @product highcharts highstock gantt
* @apioption plotOptions.series.pointInterval
*/
/**
* On datetime series, this allows for setting the
* [pointInterval](#plotOptions.series.pointInterval) to irregular time
* units, `day`, `month` and `year`. A day is usually the same as 24
* hours, but `pointIntervalUnit` also takes the DST crossover into
* consideration when dealing with local time. Combine this option with
* `pointInterval` to draw weeks, quarters, 6 months, 10 years etc.
*
* Please note that this options applies to the _series data_, not the
* interval of the axis ticks, which is independent.
*
* @sample {highcharts} highcharts/plotoptions/series-pointintervalunit/
* One point a month
* @sample {highstock} highcharts/plotoptions/series-pointintervalunit/
* One point a month
*
* @type {string}
* @since 4.1.0
* @product highcharts highstock gantt
* @validvalue ["day", "month", "year"]
* @apioption plotOptions.series.pointIntervalUnit
*/
/**
* Possible values: `"on"`, `"between"`, `number`.
*
* In a column chart, when pointPlacement is `"on"`, the point will not
* create any padding of the X axis. In a polar column chart this means
* that the first column points directly north. If the pointPlacement is
* `"between"`, the columns will be laid out between ticks. This is
* useful for example for visualising an amount between two points in
* time or in a certain sector of a polar chart.
*
* Since Highcharts 3.0.2, the point placement can also be numeric,
* where 0 is on the axis value, -0.5 is between this value and the
* previous, and 0.5 is between this value and the next. Unlike the
* textual options, numeric point placement options won't affect axis
* padding.
*
* Note that pointPlacement needs a [pointRange](
* #plotOptions.series.pointRange) to work. For column series this is
* computed, but for line-type series it needs to be set.
*
* For the `xrange` series type and gantt charts, if the Y axis is a
* category axis, the `pointPlacement` applies to the Y axis rather than
* the (typically datetime) X axis.
*
* Defaults to `undefined` in cartesian charts, `"between"` in polar
* charts.
*
* @see [xAxis.tickmarkPlacement](#xAxis.tickmarkPlacement)
*
* @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-between/
* Between in a column chart
* @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-numeric/
* Numeric placement for custom layout
* @sample {highcharts|highstock} maps/plotoptions/heatmap-pointplacement/
* Placement in heatmap
*
* @type {string|number}
* @since 2.3.0
* @product highcharts highstock gantt
* @apioption plotOptions.series.pointPlacement
*/
/**
* If no x values are given for the points in a series, `pointStart`
* defines on what value to start. For example, if a series contains one
* yearly value starting from 1945, set `pointStart` to 1945.
*
* The `pointStart` setting can be a number, or a datetime string that is
* parsed according to the `time.timezone` setting.
*
* If combined with `relativeXValue`, an x value can be set on each
* point. The x value from the point options is multiplied by
* `pointInterval` and added to `pointStart` to produce a modified x
* value.
*
* @sample {highcharts} highcharts/plotoptions/series-pointstart-linear/
* Linear
* @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
* Datetime
* @sample {highcharts} highcharts/plotoptions/series-relativexvalue/
* Relative x value
* @sample {highstock} stock/plotoptions/pointinterval-pointstart/
* Using pointStart and pointInterval
* @sample {highstock} stock/plotoptions/relativexvalue/
* Relative x value
*
* @type {number|string}
* @default 0
* @product highcharts highstock gantt
* @apioption plotOptions.series.pointStart
*/
/**
* When true, X values in the data set are relative to the current
* `pointStart`, `pointInterval` and `pointIntervalUnit` settings. This
* allows compression of the data for datasets with irregular X values.
*
* The real X values are computed on the formula `f(x) = ax + b`, where
* `a` is the `pointInterval` (optionally with a time unit given by
* `pointIntervalUnit`), and `b` is the `pointStart`.
*
* @sample {highcharts} highcharts/plotoptions/series-relativexvalue/
* Relative X value
* @sample {highstock} stock/plotoptions/relativexvalue/
* Relative X value
*
* @type {boolean}
* @default false
* @product highcharts highstock
* @apioption plotOptions.series.relativeXValue
*/
/**
* Whether to select the series initially. If `showCheckbox` is true,
* the checkbox next to the series name in the legend will be checked
* for a selected series.
*
* @sample {highcharts} highcharts/plotoptions/series-selected/
* One out of two series selected
*
* @type {boolean}
* @default false
* @since 1.2.0
* @apioption plotOptions.series.selected
*/
/**
* Whether to apply a drop shadow to the graph line. Since 2.3 the
* shadow can be an object configuration containing `color`, `offsetX`,
* `offsetY`, `opacity` and `width`.
*
* Note that in some cases, like stacked columns or other dense layouts, the
* series may cast shadows on each other. In that case, the
* `chart.seriesGroupShadow` allows applying a common drop shadow to the
* whole series group.
*
* @sample {highcharts} highcharts/plotoptions/series-shadow/
* Shadow enabled
*
* @type {boolean|Highcharts.ShadowOptionsObject}
* @default false
* @apioption plotOptions.series.shadow
*/
/**
* Whether to display this particular series or series type in the
* legend. Standalone series are shown in legend by default, and linked
* series are not. Since v7.2.0 it is possible to show series that use
* colorAxis by setting this option to `true`.
*
* @sample {highcharts} highcharts/plotoptions/series-showinlegend/
* One series in the legend, one hidden
*
* @type {boolean}
* @apioption plotOptions.series.showInLegend
*/
/**
* Whether or not to show the series in the navigator. Takes precedence
* over [navigator.baseSeries](#navigator.baseSeries) if defined.
*
* @type {boolean}
* @since 5.0.0
* @product highstock
* @apioption plotOptions.series.showInNavigator
*/
/**
* If set to `true`, the accessibility module will skip past the points
* in this series for keyboard navigation.
*
* @type {boolean}
* @since 5.0.12
* @apioption plotOptions.series.skipKeyboardNavigation
*/
/**
* Whether to stack the values of each series on top of each other.
* Possible values are `undefined` to disable, `"normal"` to stack by
* value or `"percent"`.
*
* When stacking is enabled, data must be sorted
* in ascending X order.
*
* Some stacking options are related to specific series types. In the
* streamgraph series type, the stacking option is set to `"stream"`.
* The second one is `"overlap"`, which only applies to waterfall
* series.
*
* @see [yAxis.reversedStacks](#yAxis.reversedStacks)
*
* @sample {highcharts} highcharts/plotoptions/series-stacking-line/
* Line
* @sample {highcharts} highcharts/plotoptions/series-stacking-column/
* Column
* @sample {highcharts} highcharts/plotoptions/series-stacking-bar/
* Bar
* @sample {highcharts} highcharts/plotoptions/series-stacking-area/
* Area
* @sample {highcharts} highcharts/plotoptions/series-stacking-percent-line/
* Line
* @sample {highcharts} highcharts/plotoptions/series-stacking-percent-column/
* Column
* @sample {highcharts} highcharts/plotoptions/series-stacking-percent-bar/
* Bar
* @sample {highcharts} highcharts/plotoptions/series-stacking-percent-area/
* Area
* @sample {highcharts} highcharts/plotoptions/series-waterfall-with-normal-stacking
* Waterfall with normal stacking
* @sample {highcharts} highcharts/plotoptions/series-waterfall-with-overlap-stacking
* Waterfall with overlap stacking
* @sample {highstock} stock/plotoptions/stacking/
* Area
*
* @type {string}
* @product highcharts highstock
* @validvalue ["normal", "overlap", "percent", "stream"]
* @apioption plotOptions.series.stacking
*/
/**
* Whether to apply steps to the line. Possible values are `left`,
* `center` and `right`.
*
* @sample {highcharts} highcharts/plotoptions/line-step/
* Different step line options
* @sample {highcharts} highcharts/plotoptions/area-step/
* Stepped, stacked area
* @sample {highstock} stock/plotoptions/line-step/
* Step line
*
* @type {string}
* @since 1.2.5
* @product highcharts highstock
* @validvalue ["left", "center", "right"]
* @apioption plotOptions.series.step
*/
/**
* The threshold, also called zero level or base level. For line type
* series this is only used in conjunction with
* [negativeColor](#plotOptions.series.negativeColor).
*
* @see [softThreshold](#plotOptions.series.softThreshold).
*
* @type {number|null}
* @default 0
* @since 3.0
* @product highcharts highstock
* @apioption plotOptions.series.threshold
*/
/**
* Set the initial visibility of the series.
*
* @sample {highcharts} highcharts/plotoptions/series-visible/
* Two series, one hidden and one visible
* @sample {highstock} stock/plotoptions/series-visibility/
* Hidden series
*
* @type {boolean}
* @default true
* @apioption plotOptions.series.visible
*/
/**
* Defines the Axis on which the zones are applied.
*
* @see [zones](#plotOptions.series.zones)
*
* @sample {highcharts} highcharts/series/color-zones-zoneaxis-x/
* Zones on the X-Axis
* @sample {highstock} highcharts/series/color-zones-zoneaxis-x/
* Zones on the X-Axis
*
* @type {string}
* @default y
* @since 4.1.0
* @product highcharts highstock
* @apioption plotOptions.series.zoneAxis
*/
/**
* General event handlers for the series items. These event hooks can
* also be attached to the series at run time using the
* `Highcharts.addEvent` function.
*
* @declare Highcharts.SeriesEventsOptionsObject
*
* @private
*/
events: {},
/**
* Fires after the series has finished its initial animation, or in case
* animation is disabled, immediately as the series is displayed.
*
* @sample {highcharts} highcharts/plotoptions/series-events-afteranimate/
* Show label after animate
* @sample {highstock} highcharts/plotoptions/series-events-afteranimate/
* Show label after animate
*
* @type {Highcharts.SeriesAfterAnimateCallbackFunction}
* @since 4.0
* @product highcharts highstock gantt
* @context Highcharts.Series
* @apioption plotOptions.series.events.afterAnimate
*/
/**
* Fires when the checkbox next to the series' name in the legend is
* clicked. One parameter, `event`, is passed to the function. The state
* of the checkbox is found by `event.checked`. The checked item is
* found by `event.item`. Return `false` to prevent the default action
* which is to toggle the select state of the series.
*
* @sample {highcharts} highcharts/plotoptions/series-events-checkboxclick/
* Alert checkbox status
*
* @type {Highcharts.SeriesCheckboxClickCallbackFunction}
* @since 1.2.0
* @context Highcharts.Series
* @apioption plotOptions.series.events.checkboxClick
*/
/**
* Fires when the series is clicked. One parameter, `event`, is passed
* to the function, containing common event information. Additionally,
* `event.point` holds a pointer to the nearest point on the graph.
*
* @sample {highcharts} highcharts/plotoptions/series-events-click/
* Alert click info
* @sample {highstock} stock/plotoptions/series-events-click/
* Alert click info
* @sample {highmaps} maps/plotoptions/series-events-click/
* Display click info in subtitle
*
* @type {Highcharts.SeriesClickCallbackFunction}
* @context Highcharts.Series
* @apioption plotOptions.series.events.click
*/
/**
* Fires when the series is hidden after chart generation time, either
* by clicking the legend item or by calling `.hide()`.
*
* @sample {highcharts} highcharts/plotoptions/series-events-hide/
* Alert when the series is hidden by clicking the legend item
*
* @type {Highcharts.SeriesHideCallbackFunction}
* @since 1.2.0
* @context Highcharts.Series
* @apioption plotOptions.series.events.hide
*/
/**
* 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. This can be prevented
* by returning `false` or calling `event.preventDefault()`.
*
* **Note:** This option is deprecated in favor of
* [legend.events.itemClick](#legend.events.itemClick).
*
* @type {Highcharts.SeriesLegendItemClickCallbackFunction}
* @deprecated 11.4.4
* @context Highcharts.Series
* @apioption plotOptions.series.events.legendItemClick
*/
/**
* Fires when the mouse leaves the graph. One parameter, `event`, is
* passed to the function, containing common event information. If the
* [stickyTracking](#plotOptions.series) option is true, `mouseOut`
* doesn't happen before the mouse enters another graph or leaves the
* plot area.
*
* @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
* With sticky tracking by default
* @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
* Without sticky tracking
*
* @type {Highcharts.SeriesMouseOutCallbackFunction}
* @context Highcharts.Series
* @apioption plotOptions.series.events.mouseOut
*/
/**
* Fires when the mouse enters the graph. One parameter, `event`, is
* passed to the function, containing common event information.
*
* @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
* With sticky tracking by default
* @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
* Without sticky tracking
*
* @type {Highcharts.SeriesMouseOverCallbackFunction}
* @context Highcharts.Series
* @apioption plotOptions.series.events.mouseOver
*/
/**
* Fires when the series is shown after chart generation time, either
* by clicking the legend item or by calling `.show()`.
*
* @sample {highcharts} highcharts/plotoptions/series-events-show/
* Alert when the series is shown by clicking the legend item.
*
* @type {Highcharts.SeriesShowCallbackFunction}
* @since 1.2.0
* @context Highcharts.Series
* @apioption plotOptions.series.events.show
*/
/**
* Options for the point markers of line and scatter-like series. Properties
* like `fillColor`, `lineColor` and `lineWidth` define the visual
* appearance of the markers. The `symbol` option defines the shape. Other
* series types, like column series, don't have markers, but have visual
* options on the series level instead.
*
* In styled mode, the markers can be styled with the `.highcharts-point`,
* `.highcharts-point-hover` and `.highcharts-point-select` class names.
*
* @declare Highcharts.PointMarkerOptionsObject
*
* @sample {highmaps} maps/demo/mappoint-mapmarker
* Using the mapmarker symbol for points
*
* @private
*/
marker: {
/**
* Enable or disable the point marker. If `undefined`, the markers
* are hidden when the data is dense, and shown for more widespread
* data points.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-enabled/
* Disabled markers
* @sample {highcharts} highcharts/plotoptions/series-marker-enabled-false/
* Disabled in normal state but enabled on hover
* @sample {highstock} stock/plotoptions/series-marker/
* Enabled markers
*
* @type {boolean}
* @default {highcharts} undefined
* @default {highstock} false
* @apioption plotOptions.series.marker.enabled
*/
/**
* The threshold for how dense the point markers should be before
* they are hidden, given that `enabled` is not defined. The number
* indicates the horizontal distance between the two closest points
* in the series, as multiples of the `marker.radius`. In other
* words, the default value of 2 means points are hidden if
* overlapping horizontally.
*
* @sample highcharts/plotoptions/series-marker-enabledthreshold
* A higher threshold
*
* @since 6.0.5
*/
enabledThreshold: 2,
/**
* The fill color of the point marker. When `undefined`, the series'
* or point's color is used.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
* White fill
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @apioption plotOptions.series.marker.fillColor
*/
/**
* Image markers only. Set the image width explicitly. When using
* this option, a `width` must also be set.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
* Fixed width and height
* @sample {highstock} highcharts/plotoptions/series-marker-width-height/
* Fixed width and height
*
* @type {number}
* @since 4.0.4
* @apioption plotOptions.series.marker.height
*/
/**
* The color of the point marker's outline. When `undefined`, the
* series' or point's color is used.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
* Inherit from series color (undefined)
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
lineColor: "#ffffff" /* Palette.backgroundColor */,
/**
* The width of the point marker's outline.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
* 2px blue marker
*/
lineWidth: 0,
/**
* The radius of the point marker.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-radius/
* Bigger markers
*
* @default {highstock} 2
* @default {highcharts} 4
*
*/
radius: 4,
/**
* A predefined shape or symbol for the marker. When undefined, the
* symbol is pulled from options.symbols. Other possible values are
* `'circle'`, `'square'`,`'diamond'`, `'triangle'` and
* `'triangle-down'`.
*
* Additionally, the URL to a graphic can be given on this form:
* `'url(graphic.png)'`. Note that for the image to be applied to
* exported charts, its URL needs to be accessible by the export
* server.
*
* Custom callbacks for symbol path generation can also be added to
* `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
* used by its method name, as shown in the demo.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-symbol/
* Predefined, graphic and custom markers
* @sample {highstock} highcharts/plotoptions/series-marker-symbol/
* Predefined, graphic and custom markers
* @sample {highmaps} maps/demo/mappoint-mapmarker
* Using the mapmarker symbol for points
*
* @type {string}
* @apioption plotOptions.series.marker.symbol
*/
/**
* Image markers only. Set the image width explicitly. When using
* this option, a `height` must also be set.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
* Fixed width and height
* @sample {highstock} highcharts/plotoptions/series-marker-width-height/
* Fixed width and height
*
* @type {number}
* @since 4.0.4
* @apioption plotOptions.series.marker.width
*/
/**
* States for a single point marker.
*
* @declare Highcharts.PointStatesOptionsObject
*/
states: {
/**
* The normal state of a single point marker. Currently only
* used for setting animation when returning to normal state
* from hover.
*
* @declare Highcharts.PointStatesNormalOptionsObject
*/
normal: {
/**
* Animation when returning to normal state after hovering.
*
* @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
*/
animation: true
},
/**
* The hover state for a single point marker.
*
* @declare Highcharts.PointStatesHoverOptionsObject
*/
hover: {
/**
* Animation when hovering over the marker.
*
* @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
*/
animation: {
/** @internal */
duration: 150
},
/**
* Enable or disable the point marker.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-enabled/
* Disabled hover state
*/
enabled: true,
/**
* The fill color of the marker in hover state. When
* `undefined`, the series' or point's fillColor for normal
* state is used.
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @apioption plotOptions.series.marker.states.hover.fillColor
*/
/**
* The color of the point marker's outline. When
* `undefined`, the series' or point's lineColor for normal
* state is used.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linecolor/
* White fill color, black line color
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @apioption plotOptions.series.marker.states.hover.lineColor
*/
/**
* The width of the point marker's outline. When
* `undefined`, the series' or point's lineWidth for normal
* state is used.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linewidth/
* 3px line width
*
* @type {number}
* @apioption plotOptions.series.marker.states.hover.lineWidth
*/
/**
* The radius of the point marker. In hover state, it
* defaults to the normal state's radius + 2 as per the
* [radiusPlus](#plotOptions.series.marker.states.hover.radiusPlus)
* option.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-radius/
* 10px radius
*
* @type {number}
* @apioption plotOptions.series.marker.states.hover.radius
*/
/**
* The number of pixels to increase the radius of the
* hovered point.
*
* @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
* 5 pixels greater radius on hover
* @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
* 5 pixels greater radius on hover
*
* @since 4.0.3
*/
radiusPlus: 2,
/**
* The additional line width for a hovered point.
*
* @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
* 2 pixels wider on hover
* @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
* 2 pixels wider on hover
*
* @since 4.0.3
*/
lineWidthPlus: 1
},
/**
* The appearance of the point marker when selected. In order to
* allow a point to be selected, set the
* `series.allowPointSelect` option to true.
*
* @declare Highcharts.PointStatesSelectOptionsObject
*/
select: {
/**
* Enable or disable visible feedback for selection.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-states-select-enabled/
* Disabled select state
*
* @type {boolean}
* @default true
* @apioption plotOptions.series.marker.states.select.enabled
*/
/**
* The radius of the point marker. In hover state, it
* defaults to the normal state's radius + 2.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-states-select-radius/
* 10px radius for selected points
*
* @type {number}
* @apioption plotOptions.series.marker.states.select.radius
*/
/**
* The fill color of the point marker.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-states-select-fillcolor/
* Solid red discs for selected points
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
fillColor: "#cccccc" /* Palette.neutralColor20 */,
/**
* The color of the point marker's outline. When
* `undefined`, the series' or point's color is used.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linecolor/
* Red line color for selected points
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
lineColor: "#000000" /* Palette.neutralColor100 */,
/**
* The width of the point marker's outline.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linewidth/
* 3px line width for selected points
*/
lineWidth: 2
}
}
},
/**
* Properties for each single point.
*
* @declare Highcharts.PlotSeriesPointOptions
*
* @private
*/
point: {
/**
* Fires when a point is clicked. One parameter, `event`, is passed
* to the function, containing common event information.
*
* If the `series.allowPointSelect` option is true, the default
* action for the point's click event is to toggle the point's
* select state. Returning `false` cancels this action.
*
* @sample {highcharts} highcharts/plotoptions/series-point-events-click/
* Click marker to alert values
* @sample {highcharts} highcharts/plotoptions/series-point-events-click-column/
* Click column
* @sample {highcharts} highcharts/plotoptions/series-point-events-click-url/
* Go to URL
* @sample {highmaps} maps/plotoptions/series-point-events-click/
* Click marker to display values
* @sample {highmaps} maps/plotoptions/series-point-events-click-url/
* Go to URL
*
* @type {Highcharts.PointClickCallbackFunction}
* @context Highcharts.Point
* @apioption plotOptions.series.point.events.click
*/
/**
* Fires when the mouse leaves the area close to the point. One
* parameter, `event`, is passed to the function, containing common
* event information.
*
* @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
* Show values in the chart's corner on mouse over
*
* @type {Highcharts.PointMouseOutCallbackFunction}
* @context Highcharts.Point
* @apioption plotOptions.series.point.events.mouseOut
*/
/**
* Fires when the mouse enters the area close to the point. One
* parameter, `event`, is passed to the function, containing common
* event information.
*
* Returning `false` cancels the default behavior, which is to show a
* tooltip for the point.
*
* @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
* Show values in the chart's corner on mouse over
*
* @type {Highcharts.PointMouseOverCallbackFunction}
* @context Highcharts.Point
* @apioption plotOptions.series.point.events.mouseOver
*/
/**
* Fires when the point is removed using the `.remove()` method. One
* parameter, `event`, is passed to the function. Returning `false`
* cancels the operation.
*
* @sample {highcharts} highcharts/plotoptions/series-point-events-remove/
* Remove point and confirm
*
* @type {Highcharts.PointRemoveCallbackFunction}
* @since 1.2.0
* @context Highcharts.Point
* @apioption plotOptions.series.point.events.remove
*/
/**
* Fires when the point is selected either programmatically or
* following a click on the point. One parameter, `event`, is passed
* to the function. Returning `false` cancels the operation.
*
* @sample {highcharts} highcharts/plotoptions/series-point-events-select/
* Report the last selected point
* @sample {highmaps} maps/plotoptions/series-allowpointselect/
* Report select and unselect
*
* @type {Highcharts.PointSelectCallbackFunction}
* @since 1.2.0
* @context Highcharts.Point
* @apioption plotOptions.series.point.events.select
*/
/**
* Fires when the point is unselected either programmatically or
* following a click on the point. One parameter, `event`, is passed
* to the function.
* Returning `false` cancels the operation.
*
* @sample {highcharts} highcharts/plotoptions/series-point-events-unselect/
* Report the last unselected point
* @sample {highmaps} maps/plotoptions/series-allowpointselect/
* Report select and unselect
*
* @type {Highcharts.PointUnselectCallbackFunction}
* @since 1.2.0
* @context Highcharts.Point
* @apioption plotOptions.series.point.events.unselect
*/
/**
* Fires when the point is updated programmatically through the
* `.update()` method. One parameter, `event`, is passed to the
* function. The new point options can be accessed through
* `event.options`. Returning `false` cancels the operation.
*
* @sample {highcharts} highcharts/plotoptions/series-point-events-update/
* Confirm point updating
*
* @type {Highcharts.PointUpdateCallbackFunction}
* @since 1.2.0
* @context Highcharts.Point
* @apioption plotOptions.series.point.events.update
*/
/**
* Events for each single point.
*
* @declare Highcharts.PointEventsOptionsObject
*/
events: {}
},
/**
* Options for the series data labels, appearing next to each data
* point.
*
* Since v6.2.0, multiple data labels can be applied to each single
* point by defining them as an array of configs.
*
* In styled mode, the data labels can be styled with the
* `.highcharts-data-label-box` and `.highcharts-data-label` class names
* ([see example](https://www.highcharts.com/samples/highcharts/css/series-datalabels)).
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-enabled
* Data labels enabled
* @sample {highcharts} highcharts/plotoptions/series-datalabels-multiple
* Multiple data labels on a bar series
* @sample {highcharts} highcharts/css/series-datalabels
* Styled mode example
* @sample {highmaps} maps/demo/color-axis
* Choropleth map with data labels
* @sample {highmaps} maps/demo/mappoint-datalabels-mapmarker
* Using data labels as map markers
*
* @type {*|Array<*>}
* @product highcharts highstock highmaps gantt
*
* @private
*/
dataLabels: {
/**
* Enable or disable the initial animation when a series is displayed
* for the `dataLabels`. The animation can also be set as a
* configuration object. Please note that this option only applies to
* the initial animation.
*
* For other animations, see [chart.animation](#chart.animation) and the
* animation parameter under the API methods. The following properties
* are supported:
*
* - `defer`: The animation delay time in milliseconds.
*
* @sample {highcharts} highcharts/plotoptions/animation-defer/
* Animation defer settings
*
* @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
* @since 8.2.0
* @apioption plotOptions.series.dataLabels.animation
*/
animation: {},
/**
* The animation delay time in milliseconds. Set to `0` to render the
* data labels immediately. As `undefined` inherits defer time from the
* [series.animation.defer](#plotOptions.series.animation.defer).
*
* @type {number}
* @since 8.2.0
* @apioption plotOptions.series.dataLabels.animation.defer
*/
/**
* The alignment of the data label compared to the point. If `right`,
* the right side of the label should be touching the point. For points
* with an extent, like columns, the alignments also dictates how to
* align it inside the box, as given with the
* [inside](#plotOptions.column.dataLabels.inside) option. Can be one of
* `left`, `center` or `right`.
*
* @sample {highcharts}
* highcharts/plotoptions/series-datalabels-align-left/ Left
* aligned
* @sample {highcharts}
* highcharts/plotoptions/bar-datalabels-align-inside-bar/ Data
* labels inside the bar
*
* @type {Highcharts.AlignValue|null}
*/
align: 'center',
/**
* Alignment method for data labels. If set to `plotEdges`, the labels
* are aligned within the plot area in the direction of the y-axis. So
* in a regular column chart, the labels are aligned vertically
* according to the `verticalAlign` setting. In a bar chart, which is
* inverted, the labels are aligned horizontally according to the
* `align` setting. Applies to cartesian series only.
*
* @sample {highcharts} highcharts/series-bar/datalabels-alignto/
* Align to plot edges
*
* @type {string}
* @since 11.4.2
* @apioption plotOptions.series.dataLabels.alignTo
*/
/**
* Whether to allow data labels to overlap. To make the labels less
* sensitive for overlapping, the
* [dataLabels.padding](#plotOptions.series.dataLabels.padding)
* can be set to 0.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-allowoverlap-false/
* Don't allow overlap
*
* @type {boolean}
* @default false
* @since 4.1.0
* @apioption plotOptions.series.dataLabels.allowOverlap
*/
/**
* The background color or gradient for the data label. Setting it to
* `auto` will use the point's color.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
* Data labels box options
* @sample {highmaps} maps/plotoptions/series-datalabels-box/
* Data labels box options
* @sample {highmaps} maps/demo/mappoint-datalabels-mapmarker
* Data labels as map markers
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @since 2.2.1
* @apioption plotOptions.series.dataLabels.backgroundColor
*/
/**
* The border color for the data label. Setting it to `auto` will use
* the point's color. Defaults to `undefined`.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
* Data labels box options
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @since 2.2.1
* @apioption plotOptions.series.dataLabels.borderColor
*/
/**
* The border radius in pixels for the data label.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
* Data labels box options
* @sample {highmaps} maps/plotoptions/series-datalabels-box/
* Data labels box options
*
* @type {number}
* @default 0
* @since 2.2.1
* @apioption plotOptions.series.dataLabels.borderRadius
*/
/**
* The border width in pixels for the data label.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
* Data labels box options
*
* @type {number}
* @default 0
* @since 2.2.1
* @apioption plotOptions.series.dataLabels.borderWidth
*/
borderWidth: 0,
/**
* A class name for the data label. Particularly in styled mode,
* this can be used to give each series' or point's data label
* unique styling. In addition to this option, a default color class
* name is added so that we can give the labels a contrast text
* shadow.
*
* @sample {highcharts} highcharts/css/data-label-contrast/
* Contrast text shadow
* @sample {highcharts} highcharts/css/series-datalabels/
* Styling by CSS
*
* @type {string}
* @since 5.0.0
* @apioption plotOptions.series.dataLabels.className
*/
/**
* This options is deprecated.
* Use [style.color](#plotOptions.series.dataLabels.style) instead.
*
* The text color for the data labels. Defaults to `undefined`. For
* certain series types, like column or map, the data labels can be
* drawn inside the points. In this case the data label will be
* drawn with maximum contrast by default. Additionally, it will be
* given a `text-outline` style with the opposite color, to further
* increase the contrast. This can be overridden by setting the
* `text-outline` style to `none` in the `dataLabels.style` option.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-color/
* Red data labels
* @sample {highmaps} maps/demo/color-axis/
* White data labels
*
* @see [style.color](#plotOptions.series.dataLabels.style)
*
* @type {Highcharts.ColorType}
* @deprecated 10.3
* @apioption plotOptions.series.dataLabels.color
*/
/**
* Whether to hide data labels that are outside the plot area. By
* default, the data label is moved inside the plot area according
* to the
* [overflow](#plotOptions.series.dataLabels.overflow)
* option.
*
* @type {boolean}
* @default true
* @since 2.3.3
* @apioption plotOptions.series.dataLabels.crop
*/
/**
* Whether to defer displaying the data labels until the initial
* series animation has finished. Setting to `false` renders the
* data label immediately. If set to `true` inherits the defer
* time set in [plotOptions.series.animation](#plotOptions.series.animation).
*
* @since 4.0.0
* @type {boolean}
* @product highcharts highstock gantt
*/
defer: true,
/**
* Enable or disable the data labels.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-enabled/
* Data labels enabled
* @sample {highmaps} maps/demo/color-axis/
* Data labels enabled
*
* @type {boolean}
* @default false
* @apioption plotOptions.series.dataLabels.enabled
*/
/**
* A declarative filter to control of which data labels to display.
* The declarative filter is designed for use when callback
* functions are not available, like when the chart options require
* a pure JSON structure or for use with graphical editors. For
* programmatic control, use the `formatter` instead, and return
* `undefined` to disable a single data label.
*
* @example
* filter: {
* property: 'percentage',
* operator: '>',
* value: 4
* }
*
* @sample {highcharts} highcharts/demo/pie-monochrome
* Data labels filtered by percentage
*
* @declare Highcharts.DataLabelsFilterOptionsObject
* @since 6.0.3
* @apioption plotOptions.series.dataLabels.filter
*/
/**
* The operator to compare by. Can be one of `>`, `<`, `>=`, `<=`,
* `==`, `===`, `!=` and `!==`.
*
* @type {string}
* @validvalue [">", "<", ">=", "<=", "==", "===", "!=", "!=="]
* @apioption plotOptions.series.dataLabels.filter.operator
*/
/**
* The point property to filter by. Point options are passed
* directly to properties, additionally there are `y` value,
* `percentage` and others listed under {@link Highcharts.Point}
* members.
*
* @type {string}
* @apioption plotOptions.series.dataLabels.filter.property
*/
/**
* The value to compare against.
*
* @type {number}
* @apioption plotOptions.series.dataLabels.filter.value
*/
/**
* A
* [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
* for the data label. Available variables are the same as for
* `formatter`.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
* Add a unit
* @sample {highcharts} highcharts/plotoptions/series-datalabels-format-subexpression/
* Complex logic in the format string
* @sample {highmaps} maps/plotoptions/series-datalabels-format/
* Formatted value in the data label
*
* @type {string}
* @default y
* @default point.value
* @since 3.0
* @apioption plotOptions.series.dataLabels.format
*/
// eslint-disable-next-line valid-jsdoc
/**
* Callback JavaScript function to format the data label. Note that if a
* `format` is defined, the format takes precedence and the formatter is
* ignored.
*
* @sample {highmaps} maps/plotoptions/series-datalabels-format/
* Formatted value
*
* @type {Highcharts.DataLabelsFormatterCallbackFunction}
*/
formatter: function () {
var numberFormatter = this.series.chart.numberFormatter;
return typeof this.y !== 'number' ?
'' : numberFormatter(this.y, -1);
},
/**
* For points with an extent, like columns or map areas, whether to
* align the data label inside the box or to the actual value point.
* Defaults to `false` in most cases, `true` in stacked columns.
*
* @type {boolean}
* @since 3.0
* @apioption plotOptions.series.dataLabels.inside
*/
/**
* Format for points with the value of null. Works analogously to
* [format](#plotOptions.series.dataLabels.format). `nullFormat` can
* be applied only to series which support displaying null points
* i.e `heatmap` or `tilemap`. Does not work with series that don't
* display null points, like `line`, `column`, `bar` or `pie`.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-nullformat/
* Format data label for null points in heat map
*
* @type {boolean|string}
* @since 7.1.0
* @apioption plotOptions.series.dataLabels.nullFormat
*/
/**
* Callback JavaScript function that defines formatting for points
* with the value of null. Works analogously to
* [formatter](#plotOptions.series.dataLabels.formatter).
* `nullFormatter` can be applied only to series which support
* displaying null points i.e `heatmap` or `tilemap`. Does not work
* with series that don't display null points, like `line`, `column`,
* `bar` or `pie`.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-nullformat/
* Format data label for null points in heat map
*
* @type {Highcharts.DataLabelsFormatterCallbackFunction}
* @since 7.1.0
* @apioption plotOptions.series.dataLabels.nullFormatter
*/
/**
* How to handle data labels that flow outside the plot area. The
* default is `"justify"`, which aligns them inside the plot area.
* For columns and bars, this means it will be moved inside the bar.
* To display data labels outside the plot area, set `crop` to
* `false` and `overflow` to `"allow"`.
*
* @type {Highcharts.DataLabelsOverflowValue}
* @default justify
* @since 3.0.6
* @apioption plotOptions.series.dataLabels.overflow
*/
/**
* When either the `borderWidth` or the `backgroundColor` is set,
* this is the padding within the box.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
* Data labels box options
* @sample {highmaps} maps/plotoptions/series-datalabels-box/
* Data labels box options
*
* @since 2.2.1
*/
padding: 5,
/**
* Aligns data labels relative to points. If `center` alignment is
* not possible, it defaults to `right`.
*
* @type {Highcharts.AlignValue}
* @default center
* @apioption plotOptions.series.dataLabels.position
*/
/**
* Text rotation in degrees. Note that due to a more complex
* structure, backgrounds, borders and padding will be lost on a
* rotated data label.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
* Vertical labels
*
* @type {number}
* @default 0
* @apioption plotOptions.series.dataLabels.rotation
*/
/**
* The shadow of the box. Works best with `borderWidth` or
* `backgroundColor`. Since 2.3 the shadow can be an object
* configuration containing `color`, `offsetX`, `offsetY`, `opacity`
* and `width`.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
* Data labels box options
*
* @type {boolean|Highcharts.ShadowOptionsObject}
* @default false
* @since 2.2.1
* @apioption plotOptions.series.dataLabels.shadow
*/
/**
* The name of a symbol to use for the border around the label.
* Symbols are predefined functions on the Renderer object.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-shape/
* A callout for annotations
*
* @type {string}
* @default square
* @since 4.1.2
* @apioption plotOptions.series.dataLabels.shape
*/
/**
* Styles for the label. The default `color` setting is
* `"contrast"`, which is a pseudo color that Highcharts picks up
* and applies the maximum contrast to the underlying point item,
* for example the bar in a bar chart.
*
* The `textOutline` is a pseudo property that applies an outline of
* the given width with the given color, which by default is the
* maximum contrast to the text. So a bright text color will result
* in a black text outline for maximum readability on a mixed
* background. In some cases, especially with grayscale text, the
* text outline doesn't work well, in which cases it can be disabled
* by setting it to `"none"`. When `useHTML` is true, the
* `textOutline` will not be picked up. In this, case, the same
* effect can be acheived through the `text-shadow` CSS property.
*
* For some series types, where each point has an extent, like for
* example tree maps, the data label may overflow the point. There
* are two strategies for handling overflow. By default, the text
* will wrap to multiple lines. The other strategy is to set
* `style.textOverflow` to `ellipsis`, which will keep the text on
* one line plus it will break inside long words.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-style/
* Bold labels
* @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow/
* Long labels truncated with an ellipsis in a pie
* @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow-wrap/
* Long labels are wrapped in a pie
* @sample {highmaps} maps/demo/color-axis/
* Bold labels
*
* @type {Highcharts.CSSObject}
* @since 4.1.0
* @apioption plotOptions.series.dataLabels.style
*/
style: {
/** @internal */
fontSize: '0.7em',
/** @internal */
fontWeight: 'bold',
/** @internal */
color: 'contrast',
/** @internal */
textOutline: '1px contrast'
},
/**
* Options for a label text which should follow marker's shape.
* Border and background are disabled for a label that follows a
* path.
*
* **Note:** Only SVG-based renderer supports this option. Setting
* `useHTML` to true will disable this option.
*
* @declare Highcharts.DataLabelsTextPathOptionsObject
* @since 7.1.0
* @apioption plotOptions.series.dataLabels.textPath
*/
/**
* Presentation attributes for the text path.
*
* @type {Highcharts.SVGAttributes}
* @since 7.1.0
* @apioption plotOptions.series.dataLabels.textPath.attributes
*/
/**
* Enable or disable `textPath` option for link's or marker's data
* labels.
*
* @type {boolean}
* @since 7.1.0
* @apioption plotOptions.series.dataLabels.textPath.enabled
*/
/**
* Whether to
* [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
* to render the labels.
*
* @type {boolean}
* @default false
* @apioption plotOptions.series.dataLabels.useHTML
*/
/**
* The vertical alignment of a data label. Can be one of `top`,
* `middle` or `bottom`. The default value depends on the data, for
* instance in a column chart, the label is above positive values
* and below negative values.
*
* @type {Highcharts.VerticalAlignValue|null}
* @since 2.3.3
*/
verticalAlign: 'bottom',
/**
* The x position offset of the label relative to the point in
* pixels.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
* Vertical and positioned
* @sample {highcharts} highcharts/plotoptions/bar-datalabels-align-inside-bar/
* Data labels inside the bar
*/
x: 0,
/**
* The z index of the data labels. Use a `zIndex` of 6 to display it above
* the series, or use a `zIndex` of 2 to display it behind the series.
*
* @type {number}
* @default 6
* @since 2.3.5
* @apioption plotOptions.series.dataLabels.zIndex
*/
/**
* The y position offset of the label relative to the point in
* pixels.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
* Vertical and positioned
*/
y: 0
},
/**
* When the series contains less points than the crop threshold, all
* points are drawn, even if the points fall outside the visible plot
* area at the current zoom. The advantage of drawing all points
* (including markers and columns), is that animation is performed on
* updates. On the other hand, when the series contains more points than
* the crop threshold, the series data is cropped to only contain points
* that fall within the plot area. The advantage of cropping away
* invisible points is to increase performance on large series.
*
* @since 2.2
* @product highcharts highstock
*
* @private
*/
cropThreshold: 300,
/**
* Opacity of a series parts: line, fill (e.g. area) and dataLabels.
*
* @see [states.inactive.opacity](#plotOptions.series.states.inactive.opacity)
*
* @since 7.1.0
*
* @private
*/
opacity: 1,
/**
* The width of each point on the x axis. For example in a column chart
* with one value each day, the pointRange would be 1 day (= 24 * 3600
* * 1000 milliseconds). This is normally computed automatically, but
* this option can be used to override the automatic value.
*
* @product highstock
*
* @private
*/
pointRange: 0,
/**
* When this is true, the series will not cause the Y axis to cross
* the zero plane (or [threshold](#plotOptions.series.threshold) option)
* unless the data actually crosses the plane.
*
* For example, if `softThreshold` is `false`, a series of 0, 1, 2,
* 3 will make the Y axis show negative values according to the
* `minPadding` option. If `softThreshold` is `true`, the Y axis starts
* at 0.
*
* @since 4.1.9
* @product highcharts highstock
*
* @private
*/
softThreshold: true,
/**
* @declare Highcharts.SeriesStatesOptionsObject
*
* @private
*/
states: {
/**
* The normal state of a series, or for point items in column, pie
* and similar series. Currently only used for setting animation
* when returning to normal state from hover.
*
* @declare Highcharts.SeriesStatesNormalOptionsObject
*/
normal: {
/**
* Animation when returning to normal state after hovering.
*
* @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
*/
animation: true
},
/**
* Options for the hovered series. These settings override the
* normal state options when a series is moused over or touched.
*
* @declare Highcharts.SeriesStatesHoverOptionsObject
*/
hover: {
/**
* Enable separate styles for the hovered series to visualize
* that the user hovers either the series itself or the legend.
*
* @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled/
* Line
* @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-column/
* Column
* @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-pie/
* Pie
*
* @type {boolean}
* @default true
* @since 1.2
* @apioption plotOptions.series.states.hover.enabled
*/
/**
* Animation setting for hovering the graph in line-type series.
*
* @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
* @since 5.0.8
* @product highcharts highstock
*/
animation: {
/**
* The duration of the hover animation in milliseconds. By
* default the hover state animates quickly in, and slowly
* back to normal.
*
* @internal
*/
duration: 150
},
/**
* Pixel width of the graph line. By default this property is
* undefined, and the `lineWidthPlus` property dictates how much
* to increase the linewidth from normal state.
*
* @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidth/
* 5px line on hover
*
* @type {number}
* @product highcharts highstock
* @apioption plotOptions.series.states.hover.lineWidth
*/
/**
* The additional line width for the graph of a hovered series.
*
* @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
* 5 pixels wider
* @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
* 5 pixels wider
*
* @since 4.0.3
* @product highcharts highstock
*/
lineWidthPlus: 1,
/**
* In Highcharts 1.0, the appearance of all markers belonging
* to the hovered series. For settings on the hover state of the
* individual point, see
* [marker.states.hover](#plotOptions.series.marker.states.hover).
*
* @deprecated
*
* @extends plotOptions.series.marker
* @excluding states, symbol
* @product highcharts highstock
*/
marker: {
// `lineWidth: base + 1`,
// `radius: base + 1`
},
/**
* Options for the halo appearing around the hovered point in
* line-type series as well as outside the hovered slice in pie
* charts. By default the halo is filled by the current point or
* series color with an opacity of 0.25\. The halo can be
* disabled by setting the `halo` option to `null`.
*
* In styled mode, the halo is styled with the
* `.highcharts-halo` class, with colors inherited from
* `.highcharts-color-{n}`.
*
* @sample {highcharts} highcharts/plotoptions/halo/
* Halo options
* @sample {highstock} highcharts/plotoptions/halo/
* Halo options
*
* @declare Highcharts.SeriesStatesHoverHaloOptionsObject
* @type {null|*}
* @since 4.0
* @product highcharts highstock
*/
halo: {
/**
* A collection of SVG attributes to override the appearance
* of the halo, for example `fill`, `stroke` and
* `stroke-width`.
*
* @type {Highcharts.SVGAttributes}
* @since 4.0
* @product highcharts highstock
* @apioption plotOptions.series.states.hover.halo.attributes
*/
/**
* The pixel size of the halo. For point markers this is the
* radius of the halo. For pie slices it is the width of the
* halo outside the slice. For bubbles it defaults to 5 and
* is the width of the halo outside the bubble.
*
* @since 4.0
* @product highcharts highstock
*/
size: 10,
/**
* Opacity for the halo unless a specific fill is overridden
* using the `attributes` setting. Note that Highcharts is
* only able to apply opacity to colors of hex or rgb(a)
* formats.
*
* @since 4.0
* @product highcharts highstock
*/
opacity: 0.25
}
},
/**
* Specific options for point in selected states, after being
* selected by
* [allowPointSelect](#plotOptions.series.allowPointSelect)
* or programmatically.
*
* @sample maps/plotoptions/series-allowpointselect/
* Allow point select demo
*
* @declare Highcharts.SeriesStatesSelectOptionsObject
* @extends plotOptions.series.states.hover
* @excluding brightness
*/
select: {
animation: {
/** @internal */
duration: 0
}
},
/**
* The opposite state of a hover for series.
*
* @sample highcharts/plotoptions/series-states-inactive-disabled
* Disabled inactive state
*
* @declare Highcharts.SeriesStatesInactiveOptionsObject
*/
inactive: {
/**
* Enable or disable the inactive state for a series
*
* @sample highcharts/plotoptions/series-states-inactive-disabled
* Disabled inactive state
*
* @type {boolean}
* @default true
* @apioption plotOptions.series.states.inactive.enabled
*/
/**
* The animation for entering the inactive state.
*
* @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
*/
animation: {
/** @internal */
duration: 150
},
/**
* Opacity of series elements (dataLabels, line, area).
*
* @type {number}
*/
opacity: 0.2
}
},
/**
* Sticky tracking of mouse events. When true, the `mouseOut` event on a
* series isn't triggered until the mouse moves over another series, or
* out of the plot area. When false, the `mouseOut` event on a series is
* triggered when the mouse leaves the area around the series' graph or
* markers. This also implies the tooltip when not shared. When
* `stickyTracking` is false and `tooltip.shared` is false, the tooltip
* will be hidden when moving the mouse between series. Defaults to true
* for line and area type series, but to false for columns, pies etc.
*
* **Note:** The boost module will force this option because of
* technical limitations.
*
* @sample {highcharts} highcharts/plotoptions/series-stickytracking-true/
* True by default
* @sample {highcharts} highcharts/plotoptions/series-stickytracking-false/
* False
*
* @default {highcharts} true
* @default {highstock} true
* @default {highmaps} false
* @since 2.0
*
* @private
*/
stickyTracking: true,
/**
* A configuration object for the tooltip rendering of each single
* series. Properties are inherited from [tooltip](#tooltip), but only
* the following properties can be defined on a series level.
*
* @declare Highcharts.SeriesTooltipOptionsObject
* @since 2.3
* @extends tooltip
* @excluding animation, backgroundColor, borderColor, borderRadius,
* borderWidth, className, crosshairs, enabled, formatter,
* headerShape, hideDelay, outside, padding, positioner,
* shadow, shape, shared, snap, split, stickOnContact,
* style, useHTML
* @apioption plotOptions.series.tooltip
*/
/**
* When a series contains a `data` array that is longer than this, the
* Series class looks for data configurations of plain numbers or arrays of
* numbers. The first and last valid points are checked. If found, the rest
* of the data is assumed to be the same. This saves expensive data checking
* and indexing in long series, and makes data-heavy charts render faster.
*
* Set it to `0` disable.
*
* Note:
* - In boost mode turbo threshold is forced. Only array of numbers or two
* dimensional arrays are allowed.
* - In version 11.4.3 and earlier, if object configurations were passed
* beyond the turbo threshold, a warning was logged in the console and the
* data series didn't render.
*
* @since 2.2
* @product highcharts highstock gantt
*
* @private
*/
turboThreshold: 1000,
/**
* An array defining zones within a series. Zones can be applied to the
* X axis, Y axis or Z axis for bubbles, according to the `zoneAxis`
* option. The zone definitions have to be in ascending order regarding
* to the value.
*
* In styled mode, the color zones are styled with the
* `.highcharts-zone-{n}` class, or custom classed from the `className`
* option
* ([view live demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/color-zones/)).
*
* @see [zoneAxis](#plotOptions.series.zoneAxis)
*
* @sample {highcharts} highcharts/series/color-zones-simple/
* Color zones
* @sample {highstock} highcharts/series/color-zones-simple/
* Color zones
*
* @declare Highcharts.SeriesZonesOptionsObject
* @type {Array<*>}
* @since 4.1.0
* @product highcharts highstock
* @apioption plotOptions.series.zones
*/
/**
* Styled mode only. A custom class name for the zone.
*
* @sample highcharts/css/color-zones/
* Zones styled by class name
*
* @type {string}
* @since 5.0.0
* @apioption plotOptions.series.zones.className
*/
/**
* Defines the color of the series.
*
* @see [series color](#plotOptions.series.color)
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @since 4.1.0
* @product highcharts highstock
* @apioption plotOptions.series.zones.color
*/
/**
* A name for the dash style to use for the graph.
*
* @see [plotOptions.series.dashStyle](#plotOptions.series.dashStyle)
*
* @sample {highcharts|highstock} highcharts/series/color-zones-dashstyle-dot/
* Dashed line indicates prognosis
*
* @type {Highcharts.DashStyleValue}
* @since 4.1.0
* @product highcharts highstock
* @apioption plotOptions.series.zones.dashStyle
*/
/**
* Defines the fill color for the series (in area type series)
*
* @see [fillColor](#plotOptions.area.fillColor)
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @since 4.1.0
* @product highcharts highstock
* @apioption plotOptions.series.zones.fillColor
*/
/**
* The value up to where the zone extends, if undefined the zones
* stretches to the last value in the series.
*
* @type {number}
* @since 4.1.0
* @product highcharts highstock
* @apioption plotOptions.series.zones.value
*/
/**
* When using dual or multiple color axes, this number defines which
* colorAxis the particular series is connected to. It refers to
* either the
* {@link #colorAxis.id|axis id}
* or the index of the axis in the colorAxis array, with 0 being the
* first. Set this option to false to prevent a series from connecting
* to the default color axis.
*
* Since v7.2.0 the option can also be an axis id or an axis index
* instead of a boolean flag.
*
* @sample highcharts/coloraxis/coloraxis-with-pie/
* Color axis with pie series
* @sample highcharts/coloraxis/multiple-coloraxis/
* Multiple color axis
*
* @type {number|string|boolean}
* @default 0
* @product highcharts highstock highmaps
* @apioption plotOptions.series.colorAxis
*/
/**
* Determines what data value should be used to calculate point color
* if `colorAxis` is used. Requires to set `min` and `max` if some
* custom point property is used or if approximation for data grouping
* is set to `'sum'`.
*
* @sample highcharts/coloraxis/custom-color-key/
* Custom color key
* @sample highcharts/coloraxis/color-key-with-stops/
* Custom colorKey with color axis stops
* @sample highcharts/coloraxis/changed-default-color-key/
* Changed default color key
*
* @type {string}
* @default y
* @since 7.2.0
* @product highcharts highstock highmaps
* @apioption plotOptions.series.colorKey
*/
/**
* What type of legend symbol to render for this series. Can be one of
* `areaMarker`, `lineMarker` or `rectangle`.
*
* @validvalue ["areaMarker", "lineMarker", "rectangle"]
*
* @sample {highcharts} highcharts/series/legend-symbol/
* Change the legend symbol
*
* @type {string}
* @default rectangle
* @since 11.0.1
* @apioption plotOptions.series.legendSymbol
*/
/**
* Defines the color of the legend symbol for this series. Defaults to
* undefined, in which case the series color is used. Does not work with
* styled mode.
*
* @sample {highcharts|highstock} highcharts/series/legend-symbol-color/
* Change the legend symbol color
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @default undefined
* @since 12.0.0
* @product highcharts highstock highmaps
* @apioption plotOptions.series.legendSymbolColor
*/
/**
* Determines whether the series should look for the nearest point
* in both dimensions or just the x-dimension when hovering the series.
* Defaults to `'xy'` for scatter series and `'x'` for most other
* series. If the data has duplicate x-values, it is recommended to
* set this to `'xy'` to allow hovering over all points.
*
* Applies only to series types using nearest neighbor search (not
* direct hover) for tooltip.
*
* @sample {highcharts} highcharts/series/findnearestpointby/
* Different hover behaviors
* @sample {highstock} highcharts/series/findnearestpointby/
* Different hover behaviors
* @sample {highmaps} highcharts/series/findnearestpointby/
* Different hover behaviors
*
* @since 5.0.10
* @validvalue ["x", "xy"]
*
* @private
*/
findNearestPointBy: 'x'
};
/* *
*
* Default Export
*
* */
/* harmony default export */ var SeriesDefaults = (seriesDefaults);
;// ./code/es5/es-modules/Core/Series/SeriesRegistry.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var SeriesRegistry_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b,
p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var SeriesRegistry_defaultOptions = Defaults.defaultOptions;
var SeriesRegistry_extend = Core_Utilities.extend, SeriesRegistry_extendClass = Core_Utilities.extendClass, SeriesRegistry_merge = Core_Utilities.merge;
/* *
*
* Namespace
*
* */
var SeriesRegistry;
(function (SeriesRegistry) {
/* *
*
* Properties
*
* */
/**
* @internal
* @todo Move `Globals.seriesTypes` code to her.
*/
SeriesRegistry.seriesTypes = Core_Globals.seriesTypes;
/* *
*
* Functions
*
* */
/**
* Registers class pattern of a series.
*
* @private
*/
function registerSeriesType(seriesType, SeriesClass) {
var defaultPlotOptions = SeriesRegistry_defaultOptions.plotOptions || {},
seriesOptions = SeriesClass.defaultOptions,
seriesProto = SeriesClass.prototype;
seriesProto.type = seriesType;
if (!seriesProto.pointClass) {
seriesProto.pointClass = Series_Point;
}
if (SeriesRegistry.seriesTypes[seriesType]) {
return false;
}
if (seriesOptions) {
defaultPlotOptions[seriesType] = seriesOptions;
}
SeriesRegistry.seriesTypes[seriesType] = SeriesClass;
return true;
}
SeriesRegistry.registerSeriesType = registerSeriesType;
/**
* Old factory to create new series prototypes.
*
* @deprecated
* @function Highcharts.seriesType
*
* @param {string} type
* The series type name.
*
* @param {string} parent
* The parent series type name. Use `line` to inherit from the basic
* {@link Series} object.
*
* @param {Highcharts.SeriesOptionsType|Highcharts.Dictionary<*>} options
* The additional default options that are merged with the parent's options.
*
* @param {Highcharts.Dictionary<*>} [props]
* The properties (functions and primitives) to set on the new prototype.
*
* @param {Highcharts.Dictionary<*>} [pointProps]
* Members for a series-specific extension of the {@link Point} prototype if
* needed.
*
* @return {Highcharts.Series}
* The newly created prototype as extended from {@link Series} or its
* derivatives.
*/
function seriesType(type, parent, options, seriesProto, pointProto) {
var defaultPlotOptions = SeriesRegistry_defaultOptions.plotOptions || {};
parent = parent || '';
// Merge the options
defaultPlotOptions[type] = SeriesRegistry_merge(defaultPlotOptions[parent], options);
// Create the class
delete SeriesRegistry.seriesTypes[type];
registerSeriesType(type, SeriesRegistry_extendClass(SeriesRegistry.seriesTypes[parent] || function () { }, seriesProto));
SeriesRegistry.seriesTypes[type].prototype.type = type;
// Create the point class if needed
if (pointProto) {
var PointClass = /** @class */ (function (_super) {
SeriesRegistry_extends(PointClass, _super);
function PointClass() {
return _super !== null && _super.apply(this, arguments) || this;
}
return PointClass;
}(Series_Point));
SeriesRegistry_extend(PointClass.prototype, pointProto);
SeriesRegistry.seriesTypes[type].prototype.pointClass = PointClass;
}
return SeriesRegistry.seriesTypes[type];
}
SeriesRegistry.seriesType = seriesType;
})(SeriesRegistry || (SeriesRegistry = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Series_SeriesRegistry = (SeriesRegistry);
;// ./code/es5/es-modules/Core/Series/Series.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var Series_assign = (undefined && undefined.__assign) || function () {
Series_assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return Series_assign.apply(this, arguments);
};
var Series_spreadArray = (undefined && undefined.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var Series_animObject = AnimationUtilities.animObject, Series_setAnimation = AnimationUtilities.setAnimation;
var Series_defaultOptions = Defaults.defaultOptions;
var Series_registerEventOptions = Core_Foundation.registerEventOptions;
var Series_svg = Core_Globals.svg, Series_win = Core_Globals.win;
var seriesTypes = Series_SeriesRegistry.seriesTypes;
var Series_arrayMax = Core_Utilities.arrayMax, Series_arrayMin = Core_Utilities.arrayMin, Series_clamp = Core_Utilities.clamp, Series_correctFloat = Core_Utilities.correctFloat, Series_crisp = Core_Utilities.crisp, Series_defined = Core_Utilities.defined, Series_destroyObjectProperties = Core_Utilities.destroyObjectProperties, Series_diffObjects = Core_Utilities.diffObjects, Series_erase = Core_Utilities.erase, Series_error = Core_Utilities.error, Series_extend = Core_Utilities.extend, Series_find = Core_Utilities.find, Series_fireEvent = Core_Utilities.fireEvent, Series_getClosestDistance = Core_Utilities.getClosestDistance, Series_getNestedProperty = Core_Utilities.getNestedProperty, Series_insertItem = Core_Utilities.insertItem, Series_isArray = Core_Utilities.isArray, Series_isNumber = Core_Utilities.isNumber, Series_isString = Core_Utilities.isString, Series_merge = Core_Utilities.merge, Series_objectEach = Core_Utilities.objectEach, Series_pick = Core_Utilities.pick, Series_removeEvent = Core_Utilities.removeEvent, Series_syncTimeout = Core_Utilities.syncTimeout;
/* *
*
* Class
*
* */
/**
* This is the base series prototype that all other series types inherit from.
* A new series is initialized either through the
* [series](https://api.highcharts.com/highcharts/series)
* option structure, or after the chart is initialized, through
* {@link Highcharts.Chart#addSeries}.
*
* The object can be accessed in a number of ways. All series and point event
* handlers give a reference to the `series` object. The chart object has a
* {@link Highcharts.Chart#series|series} property that is a collection of all
* the chart's series. The point objects and axis objects also have the same
* reference.
*
* Another way to reference the series programmatically is by `id`. Add an id
* in the series configuration options, and get the series object by
* {@link Highcharts.Chart#get}.
*
* Configuration options for the series are given in three levels. Options for
* all series in a chart are given in the
* [plotOptions.series](https://api.highcharts.com/highcharts/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, or as
* arguments to `chart.addSeries`.
*
* The data in the series is stored in various arrays.
*
* - First, `series.options.data` contains all the original config options for
* each point whether added by options or methods like `series.addPoint`.
*
* - The `series.dataTable` refers to an instance of [DataTableCore](https://api.highcharts.com/class-reference/Highcharts.Data)
* or `DataTable` that contains the data in a tabular format. Individual
* columns can be read from `series.getColumn()`.
*
* - Next, `series.data` contains those values converted to points, but in case
* the series data length exceeds the `cropThreshold`, or if the data is
* grouped, `series.data` doesn't contain all the points. It only contains the
* points that have been created on demand.
*
* - Then there's `series.points` that contains all currently visible point
* objects. In case of cropping, the cropped-away points are not part of this
* array. The `series.points` array starts at `series.cropStart` compared to
* `series.data` and `series.options.data`. If however the series data is
* grouped, these can't be correlated one to one.
*
* @class
* @name Highcharts.Series
*
* @param {Highcharts.Chart} chart
* The chart instance.
*
* @param {Highcharts.SeriesOptionsType|object} options
* The series options.
*/
var Series = /** @class */ (function () {
function Series() {
/* *
*
* Static Properties
*
* */
this.zoneAxis = 'y';
// eslint-enable valid-jsdoc
}
/* *
*
* Functions
*
* */
/* eslint-disable valid-jsdoc */
Series.prototype.init = function (chart, userOptions) {
var _a;
Series_fireEvent(this, 'init', { options: userOptions });
// Create the data table
(_a = this.dataTable) !== null && _a !== void 0 ? _a : (this.dataTable = new Data_DataTableCore());
var series = this,
chartSeries = chart.series;
// The 'eventsToUnbind' property moved from prototype into the
// Series init to avoid reference to the same array between
// the different series and charts. #12959, #13937
this.eventsToUnbind = [];
/**
* Read only. The chart that the series belongs to.
*
* @name Highcharts.Series#chart
* @type {Highcharts.Chart}
*/
series.chart = chart;
/**
* Read only. The series' type, like "line", "area", "column" etc.
* The type in the series options anc can be altered using
* {@link Series#update}.
*
* @name Highcharts.Series#type
* @type {string}
*/
/**
* Read only. The series' current options. To update, use
* {@link Series#update}.
*
* @name Highcharts.Series#options
* @type {Highcharts.SeriesOptionsType}
*/
series.options = series.setOptions(userOptions);
var options = series.options,
visible = options.visible !== false;
/**
* All child series that are linked to the current series through the
* [linkedTo](https://api.highcharts.com/highcharts/series.line.linkedTo)
* option.
*
* @name Highcharts.Series#linkedSeries
* @type {Array<Highcharts.Series>}
* @readonly
*/
series.linkedSeries = [];
// Bind the axes
series.bindAxes();
Series_extend(series, {
/**
* The series name as given in the options. Defaults to
* "Series {n}".
*
* @name Highcharts.Series#name
* @type {string}
*/
name: options.name,
state: '',
/**
* Read only. The series' visibility state as set by {@link
* Series#show}, {@link Series#hide}, or in the initial
* configuration.
*
* @name Highcharts.Series#visible
* @type {boolean}
*/
visible: visible, // True by default
/**
* Read only. The series' selected state as set by {@link
* Highcharts.Series#select}.
*
* @name Highcharts.Series#selected
* @type {boolean}
*/
selected: options.selected === true // False by default
});
Series_registerEventOptions(this, options);
var events = options.events;
if ((events && events.click) ||
(options.point &&
options.point.events &&
options.point.events.click) ||
options.allowPointSelect) {
chart.runTrackerClick = true;
}
series.getColor();
series.getSymbol();
// Mark cartesian
if (series.isCartesian) {
chart.hasCartesianSeries = true;
}
// Get the index and register the series in the chart. The index is
// one more than the current latest series index (#5960).
var lastSeries;
if (chartSeries.length) {
lastSeries = chartSeries[chartSeries.length - 1];
}
series._i = Series_pick(lastSeries && lastSeries._i, -1) + 1;
series.opacity = series.options.opacity;
// Insert the series and re-order all series above the insertion
// point.
chart.orderItems('series', Series_insertItem(this, chartSeries));
// Set options for series with sorting and set data later.
if (options.dataSorting && options.dataSorting.enabled) {
series.setDataSortingOptions();
}
else if (!series.points && !series.data) {
series.setData(options.data, false);
}
Series_fireEvent(this, 'afterInit');
};
/**
* Check whether the series item is itself or inherits from a certain
* series type.
*
* @function Highcharts.Series#is
* @param {string} type The type of series to check for, can be either
* featured or custom series types. For example `column`, `pie`,
* `ohlc` etc.
*
* @return {boolean}
* True if this item is or inherits from the given type.
*/
Series.prototype.is = function (type) {
return seriesTypes[type] && this instanceof seriesTypes[type];
};
/**
* Set the xAxis and yAxis properties of cartesian series, and register
* the series in the `axis.series` array.
*
* @private
* @function Highcharts.Series#bindAxes
*/
Series.prototype.bindAxes = function () {
var series = this,
seriesOptions = series.options,
chart = series.chart;
var axisOptions;
Series_fireEvent(this, 'bindAxes', null, function () {
// Repeat for xAxis and yAxis
(series.axisTypes || []).forEach(function (coll) {
// Loop through the chart's axis objects
(chart[coll] || []).forEach(function (axis) {
axisOptions = axis.options;
// Apply if the series xAxis or yAxis option matches
// the number of the axis, or if undefined, use the
// first axis
if (Series_pick(seriesOptions[coll], 0) === axis.index ||
(typeof seriesOptions[coll] !==
'undefined' &&
seriesOptions[coll] === axisOptions.id)) {
// Register this series in the axis.series lookup
Series_insertItem(series, axis.series);
// Set this series.xAxis or series.yAxis reference
/**
* Read only. The unique xAxis object associated
* with the series.
*
* @name Highcharts.Series#xAxis
* @type {Highcharts.Axis}
*/
/**
* Read only. The unique yAxis object associated
* with the series.
*
* @name Highcharts.Series#yAxis
* @type {Highcharts.Axis}
*/
series[coll] = axis;
// Mark dirty for redraw
axis.isDirty = true;
}
});
// The series needs an X and an Y axis
if (!series[coll] &&
series.optionalAxis !== coll) {
Series_error(18, true, chart);
}
});
});
Series_fireEvent(this, 'afterBindAxes');
};
/**
* Define hasData functions for series. These return true if there
* are data points on this series within the plot area.
*
* @private
* @function Highcharts.Series#hasData
*/
Series.prototype.hasData = function () {
return ((this.visible &&
typeof this.dataMax !== 'undefined' &&
typeof this.dataMin !== 'undefined') || ( // #3703
this.visible &&
this.dataTable.rowCount > 0 // #9758
));
};
/**
* Determine whether the marker in a series has changed.
*
* @private
* @function Highcharts.Series#hasMarkerChanged
*/
Series.prototype.hasMarkerChanged = function (options, oldOptions) {
var marker = options.marker,
oldMarker = oldOptions.marker || {};
return marker && ((oldMarker.enabled && !marker.enabled) ||
oldMarker.symbol !== marker.symbol || // #10870, #15946
oldMarker.height !== marker.height || // #16274
oldMarker.width !== marker.width // #16274
);
};
/**
* Return an auto incremented x value based on the pointStart and
* pointInterval options. This is only used if an x value is not given
* for the point that calls autoIncrement.
*
* @private
* @function Highcharts.Series#autoIncrement
*/
Series.prototype.autoIncrement = function (x) {
var _a,
_b;
var options = this.options,
_c = this.options,
pointIntervalUnit = _c.pointIntervalUnit,
relativeXValue = _c.relativeXValue,
time = this.chart.time,
xIncrement = (_b = (_a = this.xIncrement) !== null && _a !== void 0 ? _a : time.parse(options.pointStart)) !== null && _b !== void 0 ? _b : 0;
var pointInterval;
this.pointInterval = pointInterval = Series_pick(this.pointInterval, options.pointInterval, 1);
if (relativeXValue && Series_isNumber(x)) {
pointInterval *= x;
}
// Added code for pointInterval strings
if (pointIntervalUnit) {
var d = time.toParts(xIncrement);
if (pointIntervalUnit === 'day') {
d[2] += pointInterval;
}
else if (pointIntervalUnit === 'month') {
d[1] += pointInterval;
}
else if (pointIntervalUnit === 'year') {
d[0] += pointInterval;
}
pointInterval = time.makeTime.apply(time, d) - xIncrement;
}
if (relativeXValue && Series_isNumber(x)) {
return xIncrement + pointInterval;
}
this.xIncrement = xIncrement + pointInterval;
return xIncrement;
};
/**
* Internal function to set properties for series if data sorting is
* enabled.
*
* @private
* @function Highcharts.Series#setDataSortingOptions
*/
Series.prototype.setDataSortingOptions = function () {
var options = this.options;
Series_extend(this, {
requireSorting: false,
sorted: false,
enabledDataSorting: true,
allowDG: false
});
// To allow unsorted data for column series.
if (!Series_defined(options.pointRange)) {
options.pointRange = 1;
}
};
/**
* Set the series options by merging from the options tree. Called
* internally on initializing and updating series. This function will
* not redraw the series. For API usage, use {@link Series#update}.
* @private
* @function Highcharts.Series#setOptions
* @param {Highcharts.SeriesOptionsType} itemOptions
* The series options.
* @emits Highcharts.Series#event:afterSetOptions
*/
Series.prototype.setOptions = function (itemOptions) {
var _a,
_b;
var chart = this.chart,
chartOptions = chart.options,
plotOptions = chartOptions.plotOptions,
userOptions = chart.userOptions || {},
seriesUserOptions = Series_merge(itemOptions),
styledMode = chart.styledMode,
e = {
plotOptions: plotOptions,
userOptions: seriesUserOptions
};
var zone;
Series_fireEvent(this, 'setOptions', e);
// These may be modified by the event
var typeOptions = e.plotOptions[this.type],
userPlotOptions = (userOptions.plotOptions || {}),
userPlotOptionsSeries = userPlotOptions.series || {},
defaultPlotOptionsType = (Series_defaultOptions.plotOptions[this.type] || {}),
userPlotOptionsType = userPlotOptions[this.type] || {};
// Use copy to prevent undetected changes (#9762)
/**
* Contains series options by the user without defaults.
* @name Highcharts.Series#userOptions
* @type {Highcharts.SeriesOptionsType}
*/
this.userOptions = e.userOptions;
var options = Series_merge(typeOptions,
plotOptions.series,
// #3881, chart instance plotOptions[type] should trump
// plotOptions.series
userPlotOptionsType,
seriesUserOptions);
// The tooltip options are merged between global and series specific
// options. Importance order asscendingly:
// globals: (1)tooltip, (2)plotOptions.series,
// (3)plotOptions[this.type]
// init userOptions with possible later updates: 4-6 like 1-3 and
// (7)this series options
this.tooltipOptions = Series_merge(Series_defaultOptions.tooltip, // 1
(_a = Series_defaultOptions.plotOptions.series) === null || _a === void 0 ? void 0 : _a.tooltip, // 2
defaultPlotOptionsType === null || defaultPlotOptionsType === void 0 ? void 0 : defaultPlotOptionsType.tooltip, // 3
chart.userOptions.tooltip, // 4
(_b = userPlotOptions.series) === null || _b === void 0 ? void 0 : _b.tooltip, // 5
userPlotOptionsType.tooltip, // 6
seriesUserOptions.tooltip // 7
);
// When shared tooltip, stickyTracking is true by default,
// unless user says otherwise.
this.stickyTracking = Series_pick(seriesUserOptions.stickyTracking, userPlotOptionsType.stickyTracking, userPlotOptionsSeries.stickyTracking, (this.tooltipOptions.shared && !this.noSharedTooltip ?
true :
options.stickyTracking));
// Delete marker object if not allowed (#1125)
if (typeOptions.marker === null) {
delete options.marker;
}
// Handle color zones
this.zoneAxis = options.zoneAxis || 'y';
var zones = this.zones = // #20440, create deep copy of zones options
(options.zones || []).map(function (z) { return (Series_assign({},
z)); });
if ((options.negativeColor || options.negativeFillColor) &&
!options.zones) {
zone = {
value: options[this.zoneAxis + 'Threshold'] ||
options.threshold ||
0,
className: 'highcharts-negative'
};
if (!styledMode) {
zone.color = options.negativeColor;
zone.fillColor = options.negativeFillColor;
}
zones.push(zone);
}
// Push one extra zone for the rest
if (zones.length && Series_defined(zones[zones.length - 1].value)) {
zones.push(styledMode ? {} : {
color: this.color,
fillColor: this.fillColor
});
}
Series_fireEvent(this, 'afterSetOptions', { options: options });
return options;
};
/**
* Return series name in "Series {Number}" format or the one defined by
* a user. This method can be simply overridden as series name format
* can vary (e.g. technical indicators).
*
* @function Highcharts.Series#getName
*
* @return {string}
* The series name.
*/
Series.prototype.getName = function () {
// #4119
return Series_pick(this.options.name, 'Series ' + (this.index + 1));
};
/**
* @private
* @function Highcharts.Series#getCyclic
*/
Series.prototype.getCyclic = function (prop, value, defaults) {
var chart = this.chart, indexName = "" + prop + "Index", counterName = "" + prop + "Counter", len = (
// Symbol count
(defaults === null || defaults === void 0 ? void 0 : defaults.length) ||
// Color count
chart.options.chart.colorCount);
var i,
setting;
if (!value) {
// Pick up either the colorIndex option, or the series.colorIndex
// after Series.update()
setting = Series_pick(prop === 'color' ? this.options.colorIndex : void 0, this[indexName]);
if (Series_defined(setting)) { // After Series.update()
i = setting;
}
else {
// #6138
if (!chart.series.length) {
chart[counterName] = 0;
}
i = chart[counterName] % len;
chart[counterName] += 1;
}
if (defaults) {
value = defaults[i];
}
}
// Set the colorIndex
if (typeof i !== 'undefined') {
this[indexName] = i;
}
this[prop] = value;
};
/**
* Get the series' color based on either the options or pulled from
* global options.
*
* @private
* @function Highcharts.Series#getColor
*/
Series.prototype.getColor = function () {
if (this.chart.styledMode) {
this.getCyclic('color');
}
else if (this.options.colorByPoint) {
this.color = "#cccccc" /* Palette.neutralColor20 */;
}
else {
this.getCyclic('color', this.options.color ||
Series_defaultOptions.plotOptions[this.type].color, this.chart.options.colors);
}
};
/**
* Get all points' instances created for this series.
*
* @private
* @function Highcharts.Series#getPointsCollection
*/
Series.prototype.getPointsCollection = function () {
return (this.hasGroupedData ? this.points : this.data) || [];
};
/**
* Get the series' symbol based on either the options or pulled from
* global options.
*
* @private
* @function Highcharts.Series#getSymbol
*/
Series.prototype.getSymbol = function () {
var seriesMarkerOption = this.options.marker;
this.getCyclic('symbol', seriesMarkerOption.symbol, this.chart.options.symbols);
};
/**
* Shorthand to get one of the series' data columns from `Series.dataTable`.
*
* @private
* @function Highcharts.Series#getColumn
*/
Series.prototype.getColumn = function (columnName, modified) {
return (modified ? this.dataTable.modified : this.dataTable)
.getColumn(columnName, true) || [];
};
/**
* Finds the index of an existing point that matches the given point
* options.
*
* @private
* @function Highcharts.Series#findPointIndex
* @param {Highcharts.PointOptionsObject} optionsObject
* The options of the point.
* @param {number} fromIndex
* The index to start searching from, used for optimizing series with
* required sorting.
* @return {number|undefined}
* Returns the index of a matching point, or undefined if no match is found.
*/
Series.prototype.findPointIndex = function (optionsObject, fromIndex) {
var id = optionsObject.id,
x = optionsObject.x,
oldData = this.points,
dataSorting = this.options.dataSorting;
var matchingPoint,
matchedById,
pointIndex;
if (id) {
var item = this.chart.get(id);
if (item instanceof Series_Point) {
matchingPoint = item;
}
}
else if (this.linkedParent ||
this.enabledDataSorting ||
this.options.relativeXValue) {
var matcher = function (oldPoint) { return !oldPoint.touched &&
oldPoint.index === optionsObject.index; };
if (dataSorting && dataSorting.matchByName) {
matcher = function (oldPoint) { return !oldPoint.touched &&
oldPoint.name === optionsObject.name; };
}
else if (this.options.relativeXValue) {
matcher = function (oldPoint) { return !oldPoint.touched &&
oldPoint.options.x === optionsObject.x; };
}
matchingPoint = Series_find(oldData, matcher);
// Add unmatched point as a new point
if (!matchingPoint) {
return void 0;
}
}
if (matchingPoint) {
pointIndex = matchingPoint && matchingPoint.index;
if (typeof pointIndex !== 'undefined') {
matchedById = true;
}
}
// Search for the same X in the existing data set
if (typeof pointIndex === 'undefined' && Series_isNumber(x)) {
pointIndex = this.getColumn('x').indexOf(x, fromIndex);
}
// Reduce pointIndex if data is cropped
if (pointIndex !== -1 &&
typeof pointIndex !== 'undefined' &&
this.cropped) {
pointIndex = (pointIndex >= this.cropStart) ?
pointIndex - this.cropStart : pointIndex;
}
if (!matchedById &&
Series_isNumber(pointIndex) &&
oldData[pointIndex] && oldData[pointIndex].touched) {
pointIndex = void 0;
}
return pointIndex;
};
/**
* Internal function called from setData. If the point count is the same
* as it was, or if there are overlapping X values, just run
* Point.update which is cheaper, allows animation, and keeps references
* to points. This also allows adding or removing points if the X-es
* don't match.
*
* @private
* @function Highcharts.Series#updateData
*/
Series.prototype.updateData = function (data, animation) {
var options = this.options,
dataSorting = options.dataSorting,
oldData = this.points,
pointsToAdd = [],
requireSorting = this.requireSorting,
equalLength = data.length === oldData.length;
var hasUpdatedByKey,
i,
point,
lastIndex,
succeeded = true;
this.xIncrement = null;
// Iterate the new data
data.forEach(function (pointOptions, i) {
var optionsObject = (Series_defined(pointOptions) &&
this.pointClass.prototype.optionsToObject.call({ series: this },
pointOptions)) || {};
var pointIndex;
// Get the x of the new data point
var x = optionsObject.x,
id = optionsObject.id;
if (id || Series_isNumber(x)) {
pointIndex = this.findPointIndex(optionsObject, lastIndex);
// Matching X not found
// or used already due to ununique x values (#8995),
// add point (but later)
if (pointIndex === -1 ||
typeof pointIndex === 'undefined') {
pointsToAdd.push(pointOptions);
// Matching X found, update
}
else if (oldData[pointIndex] &&
pointOptions !== options.data[pointIndex]) {
oldData[pointIndex].update(pointOptions, false, null, false);
// Mark it touched, below we will remove all points that
// are not touched.
oldData[pointIndex].touched = true;
// Speed optimize by only searching after last known
// index. Performs ~20% bettor on large data sets.
if (requireSorting) {
lastIndex = pointIndex + 1;
}
// Point exists, no changes, don't remove it
}
else if (oldData[pointIndex]) {
oldData[pointIndex].touched = true;
}
// If the length is equal and some of the nodes had a
// match in the same position, we don't want to remove
// non-matches.
if (!equalLength ||
i !== pointIndex ||
(dataSorting && dataSorting.enabled) ||
this.hasDerivedData) {
hasUpdatedByKey = true;
}
}
else {
// Gather all points that are not matched
pointsToAdd.push(pointOptions);
}
}, this);
// Remove points that don't exist in the updated data set
if (hasUpdatedByKey) {
i = oldData.length;
while (i--) {
point = oldData[i];
if (point && !point.touched && point.remove) {
point.remove(false, animation);
}
}
// If we did not find keys (ids or x-values), and the length is the
// same, update one-to-one
}
else if (equalLength && (!dataSorting || !dataSorting.enabled)) {
data.forEach(function (point, i) {
// .update doesn't exist on a linked, hidden series (#3709)
// (#10187)
if (point !== oldData[i].y && !oldData[i].destroyed) {
oldData[i].update(point, false, null, false);
}
});
// Don't add new points since those configs are used above
pointsToAdd.length = 0;
// Did not succeed in updating data
}
else {
succeeded = false;
}
oldData.forEach(function (point) {
if (point) {
point.touched = false;
}
});
if (!succeeded) {
return false;
}
// Add new points
pointsToAdd.forEach(function (point) {
this.addPoint(point, false, null, null, false);
}, this);
var xData = this.getColumn('x');
if (this.xIncrement === null &&
xData.length) {
this.xIncrement = Series_arrayMax(xData);
this.autoIncrement();
}
return true;
};
Series.prototype.dataColumnKeys = function () {
return Series_spreadArray(['x'], (this.pointArrayMap || ['y']), true);
};
/**
* Apply a new set of data to the series and optionally redraw it. The
* new data array is passed by reference (except in case of
* `updatePoints`), and may later be mutated when updating the chart
* data.
*
* Note the difference in behaviour when setting the same amount of
* points, or a different amount of points, as handled by the
* `updatePoints` parameter.
*
* @sample highcharts/members/series-setdata/
* Set new data from a button
* @sample highcharts/members/series-setdata-pie/
* Set data in a pie
* @sample stock/members/series-setdata/
* Set new data in Highcharts Stock
* @sample maps/members/series-setdata/
* Set new data in Highmaps
*
* @function Highcharts.Series#setData
*
* @param {Array<Highcharts.PointOptionsType>} data
* Takes an array of data in the same format as described under
* `series.{type}.data` for the given series type, for example a
* line series would take data in the form described under
* [series.line.data](https://api.highcharts.com/highcharts/series.line.data).
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart after the series is altered. If
* doing more operations on the chart, it is a good idea to set
* redraw to false and call {@link Chart#redraw} after.
*
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
* When the updated data is the same length as the existing data,
* points will be updated by default, and animation visualizes
* how the points are changed. Set false to disable animation, or
* a configuration object to set duration or easing.
*
* @param {boolean} [updatePoints=true]
* When this is true, points will be updated instead of replaced
* whenever possible. This occurs a) when the updated data is the
* same length as the existing data, b) when points are matched
* by their id's, or c) when points can be matched by X values.
* This allows updating with animation and performs better. In
* this case, the original array is not passed by reference. Set
* `false` to prevent.
*/
Series.prototype.setData = function (data, redraw, animation, updatePoints) {
var _a,
_b;
var _c,
_d;
if (redraw === void 0) { redraw = true; }
var series = this,
oldData = series.points,
oldDataLength = (oldData && oldData.length) || 0,
options = series.options,
chart = series.chart,
dataSorting = options.dataSorting,
xAxis = series.xAxis,
turboThreshold = options.turboThreshold,
table = this.dataTable,
dataColumnKeys = this.dataColumnKeys(),
pointValKey = series.pointValKey || 'y',
pointArrayMap = series.pointArrayMap || [],
valueCount = pointArrayMap.length,
keys = options.keys;
var i,
updatedData,
indexOfX = 0,
indexOfY = 1,
copiedData;
if (!chart.options.chart.allowMutatingData) { // #4259
// Remove old reference
if (options.data) {
delete series.options.data;
}
if (series.userOptions.data) {
delete series.userOptions.data;
}
copiedData = Series_merge(true, data);
}
data = copiedData || data || [];
var dataLength = data.length;
if (dataSorting && dataSorting.enabled) {
data = this.sortData(data);
}
// First try to run Point.update which is cheaper, allows animation, and
// keeps references to points.
if (chart.options.chart.allowMutatingData &&
updatePoints !== false &&
dataLength &&
oldDataLength &&
!series.cropped &&
!series.hasGroupedData &&
series.visible &&
// Soft updating has no benefit in boost, and causes JS error
// (#8355)
!series.boosted) {
updatedData = this.updateData(data, animation);
}
if (!updatedData) {
// Reset properties
series.xIncrement = null;
series.colorCounter = 0; // For series with colorByPoint (#1547)
// In turbo mode, look for one- or twodimensional arrays of numbers.
// The first and the last valid value are tested, and we assume that
// all the rest are defined the same way. Although the 'for' loops
// are similar, they are repeated inside each if-else conditional
// for max performance.
var runTurbo = turboThreshold && dataLength > turboThreshold;
if (runTurbo) {
var firstPoint = series.getFirstValidPoint(data),
lastPoint = series.getFirstValidPoint(data,
dataLength - 1, -1),
isShortArray = function (a) { return Boolean(Series_isArray(a) && (keys || Series_isNumber(a[0]))); };
// Assume all points are numbers
if (Series_isNumber(firstPoint) && Series_isNumber(lastPoint)) {
var x = [],
valueData = [];
for (var _e = 0, data_1 = data; _e < data_1.length; _e++) {
var value = data_1[_e];
x.push(this.autoIncrement());
valueData.push(value);
}
table.setColumns((_a = {
x: x
},
_a[pointValKey] = valueData,
_a));
// Assume all points are arrays when first point is
}
else if (isShortArray(firstPoint) &&
isShortArray(lastPoint)) {
if (valueCount) { // [x, low, high] or [x, o, h, l, c]
// When autoX is 1, the x is skipped: [low, high]. When
// autoX is 0, the x is included: [x, low, high]
var autoX = firstPoint.length === valueCount ?
1 : 0,
colArray_1 = new Array(dataColumnKeys.length)
.fill(0).map(function () { return []; });
for (var _f = 0, _g = data; _f < _g.length; _f++) {
var pt = _g[_f];
if (autoX) {
colArray_1[0].push(this.autoIncrement());
}
for (var j = autoX; j <= valueCount; j++) {
(_c = colArray_1[j]) === null || _c === void 0 ? void 0 : _c.push(pt[j - autoX]);
}
}
table.setColumns(dataColumnKeys.reduce(function (columns, columnName, i) {
columns[columnName] = colArray_1[i];
return columns;
}, {}));
}
else { // [x, y]
if (keys) {
indexOfX = keys.indexOf('x');
indexOfY = keys.indexOf('y');
indexOfX = indexOfX >= 0 ? indexOfX : 0;
indexOfY = indexOfY >= 0 ? indexOfY : 1;
}
if (firstPoint.length === 1) {
indexOfY = 0;
}
var xData = [],
valueData = [];
if (indexOfX === indexOfY) {
for (var _h = 0, data_2 = data; _h < data_2.length; _h++) {
var pt = data_2[_h];
xData.push(this.autoIncrement());
valueData.push(pt[indexOfY]);
}
}
else {
for (var _j = 0, data_3 = data; _j < data_3.length; _j++) {
var pt = data_3[_j];
xData.push(pt[indexOfX]);
valueData.push(pt[indexOfY]);
}
}
table.setColumns((_b = {
x: xData
},
_b[pointValKey] = valueData,
_b));
}
}
else {
// Highcharts expects configs to be numbers or arrays in
// turbo mode
runTurbo = false;
}
}
if (!runTurbo) {
var columns = dataColumnKeys.reduce(function (columns,
columnName) {
columns[columnName] = [];
return columns;
}, {});
for (i = 0; i < dataLength; i++) {
var pt = series.pointClass.prototype.applyOptions.apply({ series: series },
[data[i]]);
for (var _k = 0, dataColumnKeys_1 = dataColumnKeys; _k < dataColumnKeys_1.length; _k++) {
var key = dataColumnKeys_1[_k];
columns[key][i] = pt[key];
}
}
table.setColumns(columns);
}
// Forgetting to cast strings to numbers is a common caveat when
// handling CSV or JSON
if (Series_isString(this.getColumn('y')[0])) {
Series_error(14, true, chart);
}
series.data = [];
series.options.data = series.userOptions.data = data;
// Destroy old points
i = oldDataLength;
while (i--) {
(_d = oldData[i]) === null || _d === void 0 ? void 0 : _d.destroy();
}
// Reset minRange (#878)
if (xAxis) {
xAxis.minRange = xAxis.userMinRange;
}
// Redraw
series.isDirty = chart.isDirtyBox = true;
series.isDirtyData = !!oldData;
animation = false;
}
// Typically for pie series, points need to be processed and
// generated prior to rendering the legend
if (options.legendType === 'point') {
this.processData();
this.generatePoints();
}
if (redraw) {
chart.redraw(animation);
}
};
/**
* Internal function to sort series data
*
* @private
* @function Highcharts.Series#sortData
* @param {Array<Highcharts.PointOptionsType>} data
* Force data grouping.
*/
Series.prototype.sortData = function (data) {
var series = this,
options = series.options,
dataSorting = options.dataSorting,
sortKey = dataSorting.sortKey || 'y',
getPointOptionsObject = function (series,
pointOptions) {
return (Series_defined(pointOptions) &&
series.pointClass.prototype.optionsToObject.call({
series: series
},
pointOptions)) || {};
};
data.forEach(function (pointOptions, i) {
data[i] = getPointOptionsObject(series, pointOptions);
data[i].index = i;
}, this);
// Sorting
var sortedData = data.concat().sort(function (a,
b) {
var aValue = Series_getNestedProperty(sortKey,
a);
var bValue = Series_getNestedProperty(sortKey,
b);
return bValue < aValue ? -1 : bValue > aValue ? 1 : 0;
});
// Set x value depending on the position in the array
sortedData.forEach(function (point, i) {
point.x = i;
}, this);
// Set the same x for linked series points if they don't have their
// own sorting
if (series.linkedSeries) {
series.linkedSeries.forEach(function (linkedSeries) {
var options = linkedSeries.options,
seriesData = options.data;
if ((!options.dataSorting ||
!options.dataSorting.enabled) &&
seriesData) {
seriesData.forEach(function (pointOptions, i) {
seriesData[i] = getPointOptionsObject(linkedSeries, pointOptions);
if (data[i]) {
seriesData[i].x = data[i].x;
seriesData[i].index = i;
}
});
linkedSeries.setData(seriesData, false);
}
});
}
return data;
};
/**
* Internal function to process the data by cropping away unused data
* points if the series is longer than the crop threshold. This saves
* computing time for large series.
*
* @private
* @function Highcharts.Series#getProcessedData
* @param {boolean} [forceExtremesFromAll]
* Force getting extremes of a total series data range.
*/
Series.prototype.getProcessedData = function (forceExtremesFromAll) {
var series = this,
table = series.dataTable,
isCartesian = series.isCartesian,
options = series.options,
xAxis = series.xAxis,
cropThreshold = options.cropThreshold,
getExtremesFromAll = forceExtremesFromAll ||
// X-range series etc, #21003
series.getExtremesFromAll,
logarithmic = xAxis === null || xAxis === void 0 ? void 0 : xAxis.logarithmic,
dataLength = table.rowCount;
var croppedData,
cropped,
cropStart = 0,
xExtremes,
min,
max,
xData = series.getColumn('x'),
modified = table,
updatingNames = false;
if (xAxis) {
// Corrected for log axis (#3053)
xExtremes = xAxis.getExtremes();
min = xExtremes.min;
max = xExtremes.max;
updatingNames = !!(xAxis.categories && !xAxis.names.length);
// Optionally filter out points outside the plot area
if (isCartesian &&
series.sorted &&
!getExtremesFromAll &&
(!cropThreshold ||
dataLength > cropThreshold ||
series.forceCrop)) {
// It's outside current extremes
if (xData[dataLength - 1] < min ||
xData[0] > max) {
modified = new Data_DataTableCore();
// Only crop if it's actually spilling out
}
else if (
// Don't understand why this condition is needed
series.getColumn(series.pointValKey || 'y').length && (xData[0] < min ||
xData[dataLength - 1] > max)) {
croppedData = this.cropData(table, min, max);
modified = croppedData.modified;
cropStart = croppedData.start;
cropped = true;
}
}
}
// Find the closest distance between processed points
xData = modified.getColumn('x') || [];
var closestPointRange = Series_getClosestDistance([
logarithmic ?
xData.map(logarithmic.log2lin) :
xData
],
// Unsorted data is not supported by the line tooltip, as well as
// data grouping and navigation in Stock charts (#725) and width
// calculation of columns (#1900). Avoid warning during the
// premature processing pass in updateNames (#16104).
function () { return (series.requireSorting &&
!updatingNames &&
Series_error(15,
false,
series.chart)); });
return {
modified: modified,
cropped: cropped,
cropStart: cropStart,
closestPointRange: closestPointRange
};
};
/**
* Internal function to apply processed data.
* In Highcharts Stock, this function is extended to provide data grouping.
*
* @private
* @function Highcharts.Series#processData
* @param {boolean} [force]
* Force data grouping.
*/
Series.prototype.processData = function (force) {
var series = this,
xAxis = series.xAxis,
table = series.dataTable;
// If the series data or axes haven't changed, don't go through
// this. Return false to pass the message on to override methods
// like in data grouping.
if (series.isCartesian &&
!series.isDirty &&
!xAxis.isDirty &&
!series.yAxis.isDirty &&
!force) {
return false;
}
var processedData = series.getProcessedData();
// Record the properties
table.modified = processedData.modified;
series.cropped = processedData.cropped; // Undefined or true
series.cropStart = processedData.cropStart;
series.closestPointRange = (series.basePointRange = processedData.closestPointRange);
Series_fireEvent(series, 'afterProcessData');
};
/**
* Iterate over xData and crop values between min and max. Returns
* object containing crop start/end cropped xData with corresponding
* part of yData, dataMin and dataMax within the cropped range.
*
* @private
* @function Highcharts.Series#cropData
*/
Series.prototype.cropData = function (table, min, max) {
var xData = table.getColumn('x',
true) || [],
dataLength = xData.length,
columns = {};
var i,
j,
start = 0,
end = dataLength;
// Iterate up to find slice start
for (i = 0; i < dataLength; i++) {
if (xData[i] >= min) {
start = Math.max(0, i - 1);
break;
}
}
// Proceed to find slice end
for (j = i; j < dataLength; j++) {
if (xData[j] > max) {
end = j + 1;
break;
}
}
for (var _a = 0, _b = this.dataColumnKeys(); _a < _b.length; _a++) {
var key = _b[_a];
var column = table.getColumn(key,
true);
if (column) {
columns[key] = column.slice(start, end);
}
}
return {
modified: new Data_DataTableCore({ columns: columns }),
start: start,
end: end
};
};
/**
* Generate the data point after the data has been processed by cropping
* away unused points and optionally grouped in Highcharts Stock.
*
* @private
* @function Highcharts.Series#generatePoints
*/
Series.prototype.generatePoints = function () {
var _a,
_b,
_c,
_d;
var series = this,
options = series.options,
dataOptions = series.processedData || options.data,
table = series.dataTable.modified,
xData = series.getColumn('x',
true),
PointClass = series.pointClass,
processedDataLength = table.rowCount,
cropStart = series.cropStart || 0,
hasGroupedData = series.hasGroupedData,
keys = options.keys,
points = [],
groupCropStartIndex = (options.dataGrouping &&
options.dataGrouping.groupAll ?
cropStart :
0),
categories = (_a = series.xAxis) === null || _a === void 0 ? void 0 : _a.categories,
pointArrayMap = series.pointArrayMap || ['y'],
// Create a configuration object out of a data row
dataColumnKeys = this.dataColumnKeys();
var dataLength,
cursor,
point,
i,
data = series.data,
pOptions;
if (!data && !hasGroupedData) {
var arr = [];
arr.length = (dataOptions === null || dataOptions === void 0 ? void 0 : dataOptions.length) || 0;
data = series.data = arr;
}
if (keys && hasGroupedData) {
// Grouped data has already applied keys (#6590)
series.options.keys = false;
}
for (i = 0; i < processedDataLength; i++) {
cursor = cropStart + i;
if (!hasGroupedData) {
point = data[cursor];
pOptions = dataOptions ?
dataOptions[cursor] :
table.getRow(i, pointArrayMap);
// #970:
if (!point &&
pOptions !== void 0) {
data[cursor] = point = new PointClass(series, pOptions, xData[i]);
}
}
else {
// Splat the y data in case of ohlc data array
point = new PointClass(series, table.getRow(i, dataColumnKeys) || []);
point.dataGroup = series.groupMap[groupCropStartIndex + i];
if ((_b = point.dataGroup) === null || _b === void 0 ? void 0 : _b.options) {
point.options = point.dataGroup.options;
Series_extend(point, point.dataGroup.options);
// Collision of props and options (#9770)
delete point.dataLabels;
}
}
if (point) { // #6279
/**
* Contains the point's index in the `Series.points` array.
*
* @name Highcharts.Point#index
* @type {number}
* @readonly
*/
// For faster access in Point.update
point.index = hasGroupedData ?
(groupCropStartIndex + i) : cursor;
points[i] = point;
// Set point properties for convenient access in tooltip and
// data labels
point.category = (_c = categories === null || categories === void 0 ? void 0 : categories[point.x]) !== null && _c !== void 0 ? _c : point.x;
point.key = (_d = point.name) !== null && _d !== void 0 ? _d : point.category;
}
}
// Restore keys options (#6590)
series.options.keys = keys;
// Hide cropped-away points - this only runs when the number of
// points is above cropThreshold, or when switching view from
// non-grouped data to grouped data (#637)
if (data &&
(processedDataLength !== (dataLength = data.length) ||
hasGroupedData)) {
for (i = 0; i < dataLength; i++) {
// When has grouped data, clear all points
if (i === cropStart && !hasGroupedData) {
i += processedDataLength;
}
if (data[i]) {
data[i].destroyElements();
data[i].plotX = void 0; // #1003
}
}
}
/**
* Read only. An array containing those values converted to points.
* In case the series data length exceeds the `cropThreshold`, or if
* the data is grouped, `series.data` doesn't contain all the
* points. Also, in case a series is hidden, the `data` array may be
* empty. In case of cropping, the `data` array may contain `undefined`
* values, instead of points. To access raw values,
* `series.options.data` will always be up to date. `Series.data` only
* contains the points that have been created on demand. To modify the
* data, use
* {@link Highcharts.Series#setData} or
* {@link Highcharts.Point#update}.
*
* @see Series.points
*
* @name Highcharts.Series#data
* @type {Array<Highcharts.Point>}
*/
series.data = data;
/**
* An array containing all currently visible point objects. In case
* of cropping, the cropped-away points are not part of this array.
* The `series.points` array starts at `series.cropStart` compared
* to `series.data` and `series.options.data`. If however the series
* data is grouped, these can't be correlated one to one. To modify
* the data, use {@link Highcharts.Series#setData} or
* {@link Highcharts.Point#update}.
*
* @name Highcharts.Series#points
* @type {Array<Highcharts.Point>}
*/
series.points = points;
Series_fireEvent(this, 'afterGeneratePoints');
};
/**
* Get current X extremes for the visible data.
*
* @private
* @function Highcharts.Series#getXExtremes
* @param {Array<number>} xData
* The data to inspect. Defaults to the current data within the visible
* range.
*/
Series.prototype.getXExtremes = function (xData) {
return {
min: Series_arrayMin(xData),
max: Series_arrayMax(xData)
};
};
/**
* Calculate Y extremes for the visible data. The result is returned
* as an object with `dataMin` and `dataMax` properties.
*
* @private
* @function Highcharts.Series#getExtremes
* @param {Array<number>} [yData]
* The data to inspect. Defaults to the current data within the visible
* range.
* @param {boolean} [forceExtremesFromAll]
* Force getting extremes of a total series data range.
*/
Series.prototype.getExtremes = function (yData, forceExtremesFromAll) {
var _a;
var _b = this, xAxis = _b.xAxis, yAxis = _b.yAxis, getExtremesFromAll = forceExtremesFromAll ||
this.getExtremesFromAll ||
this.options.getExtremesFromAll, // #4599, #21003
table = getExtremesFromAll && this.cropped ?
this.dataTable :
this.dataTable.modified, rowCount = table.rowCount, customData = yData || this.stackedYData, yAxisData = customData ?
[customData] :
((_a = (this.keysAffectYAxis || this.pointArrayMap || ['y'])) === null || _a === void 0 ? void 0 : _a.map(function (key) { return table.getColumn(key, true) || []; })) || [], xData = this.getColumn('x', true), activeYData = [],
// Handle X outside the viewed area. This does not work with
// non-sorted data like scatter (#7639).
shoulder = this.requireSorting && !this.is('column') ?
1 : 0,
// #2117, need to compensate for log X axis
positiveValuesOnly = yAxis ? yAxis.positiveValuesOnly : false, doAll = getExtremesFromAll ||
this.cropped ||
!xAxis; // For colorAxis support
var xExtremes, x, i, xMin = 0, xMax = 0;
if (xAxis) {
xExtremes = xAxis.getExtremes();
xMin = xExtremes.min;
xMax = xExtremes.max;
}
for (i = 0; i < rowCount; i++) {
x = xData[i];
// Check if it is within the selected x axis range
if (doAll ||
((xData[i + shoulder] || x) >= xMin &&
(xData[i - shoulder] || x) <= xMax)) {
for (var _c = 0, yAxisData_1 = yAxisData; _c < yAxisData_1.length; _c++) {
var values = yAxisData_1[_c];
var val = values[i];
// For points within the visible range, including the
// first point outside the visible range (#7061),
// consider y extremes.
if (Series_isNumber(val) &&
(val > 0 || !positiveValuesOnly)) {
activeYData.push(val);
}
}
}
}
var dataExtremes = {
activeYData: activeYData, // Needed for Stock Cumulative Sum
dataMin: Series_arrayMin(activeYData),
dataMax: Series_arrayMax(activeYData)
};
Series_fireEvent(this, 'afterGetExtremes', { dataExtremes: dataExtremes });
return dataExtremes;
};
/**
* Set the current data extremes as `dataMin` and `dataMax` on the
* Series item. Use this only when the series properties should be
* updated.
*
* @private
* @function Highcharts.Series#applyExtremes
*/
Series.prototype.applyExtremes = function () {
var dataExtremes = this.getExtremes();
/**
* Contains the minimum value of the series' data point. Some series
* types like `networkgraph` do not support this property as they
* lack a `y`-value.
* @name Highcharts.Series#dataMin
* @type {number|undefined}
* @readonly
*/
this.dataMin = dataExtremes.dataMin;
/**
* Contains the maximum value of the series' data point. Some series
* types like `networkgraph` do not support this property as they
* lack a `y`-value.
* @name Highcharts.Series#dataMax
* @type {number|undefined}
* @readonly
*/
this.dataMax = dataExtremes.dataMax;
return dataExtremes;
};
/**
* Find and return the first non nullish point in the data
*
* @private
* @function Highcharts.Series.getFirstValidPoint
* @param {Array<Highcharts.PointOptionsType>} data
* Array of options for points
* @param {number} [start=0]
* Index to start searching from
* @param {number} [increment=1]
* Index increment, set -1 to search backwards
*/
Series.prototype.getFirstValidPoint = function (data, start, increment) {
if (start === void 0) { start = 0; }
if (increment === void 0) { increment = 1; }
var dataLength = data.length;
var i = start;
while (i >= 0 && i < dataLength) {
if (Series_defined(data[i])) {
return data[i];
}
i += increment;
}
};
/**
* Translate data points from raw data values to chart specific
* positioning data needed later in the `drawPoints` and `drawGraph`
* functions. This function can be overridden in plugins and custom
* series type implementations.
*
* @function Highcharts.Series#translate
*
* @emits Highcharts.Series#events:translate
*/
Series.prototype.translate = function () {
var _a;
this.generatePoints();
var series = this,
options = series.options,
stacking = options.stacking,
xAxis = series.xAxis,
enabledDataSorting = series.enabledDataSorting,
yAxis = series.yAxis,
points = series.points,
dataLength = points.length,
pointPlacement = series.pointPlacementToXValue(), // #7860
dynamicallyPlaced = Boolean(pointPlacement),
threshold = options.threshold,
stackThreshold = options.startFromThreshold ? threshold : 0;
var i,
plotX,
lastPlotX,
stackIndicator,
closestPointRangePx = Number.MAX_VALUE;
/**
* Plotted coordinates need to be within a limited range. Drawing
* too far outside the viewport causes various rendering issues
* (#3201, #3923, #7555).
* @private
*/
function limitedRange(val) {
return Series_clamp(val, -1e9, 1e9);
}
// Translate each point
for (i = 0; i < dataLength; i++) {
var point = points[i],
xValue = point.x;
var stackItem = void 0,
stackValues = void 0,
yValue = point.y,
lowValue = point.low;
var stacks = stacking && ((_a = yAxis.stacking) === null || _a === void 0 ? void 0 : _a.stacks[(series.negStacks &&
yValue <
(stackThreshold ? 0 : threshold) ?
'-' :
'') + series.stackKey]);
plotX = xAxis.translate(// #3923
xValue, false, false, false, true, pointPlacement);
/**
* The translated X value for the point in terms of pixels. Relative
* to the X axis position if the series has one, otherwise relative
* to the plot area. Depending on the series type this value might
* not be defined.
*
* In an inverted chart the x-axis is going from the bottom to the
* top so the `plotX` value is the number of pixels from the bottom
* of the axis.
*
* @see Highcharts.Point#pos
* @name Highcharts.Point#plotX
* @type {number|undefined}
*/
point.plotX = Series_isNumber(plotX) ? Series_correctFloat(// #5236
limitedRange(plotX) // #3923
) : void 0;
// Calculate the bottom y value for stacked series
if (stacking &&
series.visible &&
stacks &&
stacks[xValue]) {
stackIndicator = series.getStackIndicator(stackIndicator, xValue, series.index);
if (!point.isNull && stackIndicator.key) {
stackItem = stacks[xValue];
stackValues = stackItem.points[stackIndicator.key];
}
if (stackItem && Series_isArray(stackValues)) {
lowValue = stackValues[0];
yValue = stackValues[1];
if (lowValue === stackThreshold &&
stackIndicator.key === stacks[xValue].base) {
lowValue = Series_pick(Series_isNumber(threshold) ? threshold : yAxis.min);
}
// #1200, #1232
if (yAxis.positiveValuesOnly &&
Series_defined(lowValue) &&
lowValue <= 0) {
lowValue = void 0;
}
point.total = point.stackTotal = Series_pick(stackItem.total);
point.percentage = Series_defined(point.y) && stackItem.total ?
(point.y / stackItem.total * 100) : void 0;
point.stackY = yValue;
// In case of variwide series (where widths of points are
// different in most cases), stack labels are positioned
// wrongly, so the call of the setOffset is omitted here and
// labels are correctly positioned later, at the end of the
// variwide's translate function (#10962)
if (!series.irregularWidths) {
stackItem.setOffset(series.pointXOffset || 0, series.barW || 0, void 0, void 0, void 0, series.xAxis);
}
}
}
// Set translated yBottom or remove it
point.yBottom = Series_defined(lowValue) ?
limitedRange(yAxis.translate(lowValue, false, true, false, true)) :
void 0;
// General hook, used for Highcharts Stock compare and cumulative
if (series.dataModify) {
yValue = series.dataModify.modifyValue(yValue, i);
}
// Set the plotY value, reset it for redraws #3201, #18422
var plotY = void 0;
if (Series_isNumber(yValue) && point.plotX !== void 0) {
plotY = yAxis.translate(yValue, false, true, false, true);
plotY = Series_isNumber(plotY) ? limitedRange(plotY) : void 0;
}
/**
* The translated Y value for the point in terms of pixels. Relative
* to the Y axis position if the series has one, otherwise relative
* to the plot area. Depending on the series type this value might
* not be defined.
*
* In an inverted chart the y-axis is going from right to left
* so the `plotY` value is the number of pixels from the right
* of the `yAxis`.
*
* @see Highcharts.Point#pos
* @name Highcharts.Point#plotY
* @type {number|undefined}
*/
point.plotY = plotY;
point.isInside = this.isPointInside(point);
// Set client related positions for mouse tracking
point.clientX = dynamicallyPlaced ?
Series_correctFloat(xAxis.translate(xValue, false, false, false, true, pointPlacement)) :
plotX; // #1514, #5383, #5518
// Negative points #19028
point.negative = (point.y || 0) < (threshold || 0);
// Determine auto enabling of markers (#3635, #5099)
if (!point.isNull && point.visible !== false) {
if (typeof lastPlotX !== 'undefined') {
closestPointRangePx = Math.min(closestPointRangePx, Math.abs(plotX - lastPlotX));
}
lastPlotX = plotX;
}
// Find point zone
point.zone = this.zones.length ? point.getZone() : void 0;
// Animate new points with data sorting
if (!point.graphic && series.group && enabledDataSorting) {
point.isNew = true;
}
}
series.closestPointRangePx = closestPointRangePx;
Series_fireEvent(this, 'afterTranslate');
};
/**
* Return the series points with null points filtered out.
*
* @function Highcharts.Series#getValidPoints
*
* @param {Array<Highcharts.Point>} [points]
* The points to inspect, defaults to {@link Series.points}.
*
* @param {boolean} [insideOnly=false]
* Whether to inspect only the points that are inside the visible view.
*
* @param {boolean} [allowNull=false]
* Whether to allow null points to pass as valid points.
*
* @return {Array<Highcharts.Point>}
* The valid points.
*/
Series.prototype.getValidPoints = function (points, insideOnly, allowNull) {
var chart = this.chart;
// #3916, #5029, #5085
return (points || this.points || []).filter(function (point) {
var plotX = point.plotX,
plotY = point.plotY,
// Undefined plotY is treated as null when negative values
// in log axis (#18422)
asNull = !allowNull && (point.isNull || !Series_isNumber(plotY));
if (asNull || (insideOnly && !chart.isInsidePlot(plotX, plotY, { inverted: chart.inverted }))) {
return false;
}
return point.visible !== false;
});
};
/**
* Get the clipping for the series. Could be called for a series to
* initiate animating the clip or to set the final clip (only width
* and x).
*
* @private
* @function Highcharts.Series#getClip
*/
Series.prototype.getClipBox = function () {
var _a;
var _b = this,
chart = _b.chart,
xAxis = _b.xAxis,
yAxis = _b.yAxis;
// If no axes on the series, use global clipBox
var _c = Series_merge(chart.clipBox),
x = _c.x,
y = _c.y,
width = _c.width,
height = _c.height;
// Otherwise, use clipBox.width which is corrected for plotBorderWidth
// and clipOffset
if (xAxis && xAxis.len !== chart.plotSizeX) {
width = xAxis.len;
}
if (yAxis && yAxis.len !== chart.plotSizeY) {
height = yAxis.len;
}
// If the chart is inverted and the series is not invertible, the chart
// clip box should be inverted, but not the series clip box (#20264)
if (chart.inverted && !this.invertible) {
_a = [height, width], width = _a[0], height = _a[1];
}
return { x: x, y: y, width: width, height: height };
};
/**
* Get the shared clip key, creating it if it doesn't exist.
*
* @private
* @function Highcharts.Series#getSharedClipKey
*/
Series.prototype.getSharedClipKey = function () {
this.sharedClipKey = (this.options.xAxis || 0) + ',' +
(this.options.yAxis || 0);
return this.sharedClipKey;
};
/**
* Set the clipping for the series. For animated series the clip is later
* modified.
*
* @private
* @function Highcharts.Series#setClip
*/
Series.prototype.setClip = function () {
var _a = this,
chart = _a.chart,
group = _a.group,
markerGroup = _a.markerGroup,
sharedClips = chart.sharedClips,
renderer = chart.renderer,
clipBox = this.getClipBox(),
sharedClipKey = this.getSharedClipKey(); // #4526
var clipRect = sharedClips[sharedClipKey];
// If a clipping rectangle for the same set of axes does not exist,
// create it
if (!clipRect) {
sharedClips[sharedClipKey] = clipRect = renderer.clipRect(clipBox);
// When setting chart size, or when the series is rendered again before
// starting animating, in compliance to a responsive rule
}
else {
clipRect.animate(clipBox);
}
if (group) {
// When clip is false, reset to no clip after animation
group.clip(this.options.clip === false ? void 0 : clipRect);
}
// Unclip temporary animation clip
if (markerGroup) {
markerGroup.clip();
}
};
/**
* Animate in the series. Called internally twice. First with the `init`
* parameter set to true, which sets up the initial state of the
* animation. Then when ready, it is called with the `init` parameter
* undefined, in order to perform the actual animation.
*
* @function Highcharts.Series#animate
*
* @param {boolean} [init]
* Initialize the animation.
*/
Series.prototype.animate = function (init) {
var _a = this, chart = _a.chart, group = _a.group, markerGroup = _a.markerGroup, inverted = chart.inverted, animation = Series_animObject(this.options.animation),
// The key for temporary animation clips
animationClipKey = [
this.getSharedClipKey(),
animation.duration,
animation.easing,
animation.defer
].join(',');
var animationClipRect = chart.sharedClips[animationClipKey],
markerAnimationClipRect = chart.sharedClips[animationClipKey + 'm'];
// Initialize the animation. Set up the clipping rectangle.
if (init && group) {
var clipBox = this.getClipBox();
// Create temporary animation clips
if (!animationClipRect) {
clipBox.width = 0;
if (inverted) {
clipBox.x = chart.plotHeight;
}
animationClipRect = chart.renderer.clipRect(clipBox);
chart.sharedClips[animationClipKey] = animationClipRect;
// The marker clip box. The number 99 is a safe margin to avoid
// markers being clipped during animation.
var markerClipBox = {
x: inverted ? -99 : -99,
y: inverted ? -99 : -99,
width: inverted ? chart.plotWidth + 199 : 99,
height: inverted ? 99 : chart.plotHeight + 199
};
markerAnimationClipRect = chart.renderer.clipRect(markerClipBox);
chart.sharedClips[animationClipKey + 'm'] = markerAnimationClipRect;
}
else {
// When height changes during animation, typically due to
// responsive settings
animationClipRect.attr('height', clipBox.height);
}
group.clip(animationClipRect);
markerGroup === null || markerGroup === void 0 ? void 0 : markerGroup.clip(markerAnimationClipRect);
// Run the animation
}
else if (animationClipRect &&
// Only first series in this pane
!animationClipRect.hasClass('highcharts-animating')) {
var finalBox = this.getClipBox(),
step_1 = animation.step;
// Only do this when there are actually markers, or we have multiple
// series (#20473)
if ((markerGroup === null || markerGroup === void 0 ? void 0 : markerGroup.element.childNodes.length) ||
chart.series.length > 1) {
// To provide as smooth animation as possible, update the marker
// group clipping in steps of the main group animation
animation.step = function (val, fx) {
if (step_1) {
step_1.apply(fx, arguments);
}
if (fx.prop === 'width' &&
(markerAnimationClipRect === null || markerAnimationClipRect === void 0 ? void 0 : markerAnimationClipRect.element)) {
markerAnimationClipRect.attr(inverted ? 'height' : 'width', val + 99);
}
};
}
animationClipRect
.addClass('highcharts-animating')
.animate(finalBox, animation);
}
};
/**
* This runs after animation to land on the final plot clipping.
*
* @private
* @function Highcharts.Series#afterAnimate
*
* @emits Highcharts.Series#event:afterAnimate
*/
Series.prototype.afterAnimate = function () {
var _this = this;
this.setClip();
// Destroy temporary clip rectangles that are no longer in use
Series_objectEach(this.chart.sharedClips, function (clip, key, sharedClips) {
if (clip && !_this.chart.container.querySelector("[clip-path=\"url(#".concat(clip.id, ")\"]"))) {
clip.destroy();
delete sharedClips[key];
}
});
this.finishedAnimating = true;
Series_fireEvent(this, 'afterAnimate');
};
/**
* Draw the markers for line-like series types, and columns or other
* graphical representation for {@link Point} objects for other series
* types. The resulting element is typically stored as
* {@link Point.graphic}, and is created on the first call and updated
* and moved on subsequent calls.
*
* @function Highcharts.Series#drawPoints
*/
Series.prototype.drawPoints = function (points) {
if (points === void 0) { points = this.points; }
var series = this,
chart = series.chart,
styledMode = chart.styledMode,
colorAxis = series.colorAxis,
options = series.options,
seriesMarkerOptions = options.marker,
markerGroup = series[series.specialGroup || 'markerGroup'],
xAxis = series.xAxis,
globallyEnabled = Series_pick(seriesMarkerOptions.enabled, !xAxis || xAxis.isRadial ? true : null,
// Use larger or equal as radius is null in bubbles (#6321)
series.closestPointRangePx >= (seriesMarkerOptions.enabledThreshold *
seriesMarkerOptions.radius));
var i,
point,
graphic,
verb,
pointMarkerOptions,
hasPointMarker,
markerAttribs;
if (seriesMarkerOptions.enabled !== false ||
series._hasPointMarkers) {
for (i = 0; i < points.length; i++) {
point = points[i];
graphic = point.graphic;
verb = graphic ? 'animate' : 'attr';
pointMarkerOptions = point.marker || {};
hasPointMarker = !!point.marker;
var shouldDrawMarker = ((globallyEnabled &&
typeof pointMarkerOptions.enabled === 'undefined') || pointMarkerOptions.enabled) && !point.isNull && point.visible !== false;
// Only draw the point if y is defined
if (shouldDrawMarker) {
// Shortcuts
var symbol = Series_pick(pointMarkerOptions.symbol,
series.symbol, 'rect');
markerAttribs = series.markerAttribs(point, (point.selected && 'select'));
// Set starting position for point sliding animation.
if (series.enabledDataSorting) {
point.startXPos = xAxis.reversed ?
-(markerAttribs.width || 0) :
xAxis.width;
}
var isInside = point.isInside !== false;
if (!graphic &&
isInside &&
((markerAttribs.width || 0) > 0 || point.hasImage)) {
/**
* SVG graphic representing the point in the chart. In
* some cases it may be a hidden graphic to improve
* accessibility.
*
* Typically this is a simple shape, like a `rect`
* for column charts or `path` for line markers, but
* for some complex series types like boxplot or 3D
* charts, the graphic may be a `g` element
* containing other shapes. The graphic is generated
* the first time {@link Series#drawPoints} runs,
* and updated and moved on subsequent runs.
*
* @see Highcharts.Point#graphics
*
* @name Highcharts.Point#graphic
* @type {Highcharts.SVGElement|undefined}
*/
point.graphic = graphic = chart.renderer
.symbol(symbol, markerAttribs.x, markerAttribs.y, markerAttribs.width, markerAttribs.height, hasPointMarker ?
pointMarkerOptions :
seriesMarkerOptions)
.add(markerGroup);
// Sliding animation for new points
if (series.enabledDataSorting &&
chart.hasRendered) {
graphic.attr({
x: point.startXPos
});
verb = 'animate';
}
}
if (graphic && verb === 'animate') { // Update
// Since the marker group isn't clipped, each
// individual marker must be toggled
graphic[isInside ? 'show' : 'hide'](isInside)
.animate(markerAttribs);
}
// Presentational attributes
if (graphic) {
var pointAttr = series.pointAttribs(point, ((styledMode || !point.selected) ?
void 0 :
'select'));
if (!styledMode) {
graphic[verb](pointAttr);
}
else if (colorAxis) { // #14114
graphic['css']({
fill: pointAttr.fill
});
}
}
if (graphic) {
graphic.addClass(point.getClassName(), true);
}
}
else if (graphic) {
point.graphic = graphic.destroy(); // #1269
}
}
}
};
/**
* Get non-presentational attributes for a point. Used internally for
* both styled mode and classic. Can be overridden for different series
* types.
*
* @see Series#pointAttribs
*
* @function Highcharts.Series#markerAttribs
*
* @param {Highcharts.Point} point
* The Point to inspect.
*
* @param {string} [state]
* The state, can be either `hover`, `select` or undefined.
*
* @return {Highcharts.SVGAttributes}
* A hash containing those attributes that are not settable from CSS.
*/
Series.prototype.markerAttribs = function (point, state) {
var seriesOptions = this.options,
seriesMarkerOptions = seriesOptions.marker,
pointMarkerOptions = point.marker || {},
symbol = (pointMarkerOptions.symbol ||
seriesMarkerOptions.symbol),
attribs = {};
var seriesStateOptions,
pointStateOptions,
radius = Series_pick(pointMarkerOptions.radius,
seriesMarkerOptions && seriesMarkerOptions.radius);
// Handle hover and select states
if (state) {
seriesStateOptions = seriesMarkerOptions.states[state];
pointStateOptions = pointMarkerOptions.states &&
pointMarkerOptions.states[state];
radius = Series_pick(pointStateOptions && pointStateOptions.radius, seriesStateOptions && seriesStateOptions.radius, radius && radius + (seriesStateOptions && seriesStateOptions.radiusPlus ||
0));
}
point.hasImage = symbol && symbol.indexOf('url') === 0;
if (point.hasImage) {
radius = 0; // And subsequently width and height is not set
}
var pos = point.pos();
if (Series_isNumber(radius) && pos) {
if (seriesOptions.crisp) {
pos[0] = Series_crisp(pos[0], point.hasImage ?
0 :
symbol === 'rect' ?
// Rectangle symbols need crisp edges, others don't
(seriesMarkerOptions === null || seriesMarkerOptions === void 0 ? void 0 : seriesMarkerOptions.lineWidth) || 0 :
1);
}
attribs.x = pos[0] - radius;
attribs.y = pos[1] - radius;
}
if (radius) {
attribs.width = attribs.height = 2 * radius;
}
return attribs;
};
/**
* Internal function to get presentational attributes for each point.
* Unlike {@link Series#markerAttribs}, this function should return
* those attributes that can also be set in CSS. In styled mode,
* `pointAttribs` won't be called.
*
* @private
* @function Highcharts.Series#pointAttribs
*
* @param {Highcharts.Point} [point]
* The point instance to inspect.
*
* @param {string} [state]
* The point state, can be either `hover`, `select` or 'normal'. If
* undefined, normal state is assumed.
*
* @return {Highcharts.SVGAttributes}
* The presentational attributes to be set on the point.
*/
Series.prototype.pointAttribs = function (point, state) {
var seriesMarkerOptions = this.options.marker,
pointOptions = point && point.options,
pointMarkerOptions = ((pointOptions && pointOptions.marker) || {}),
pointColorOption = pointOptions && pointOptions.color,
pointColor = point && point.color,
zoneColor = point && point.zone && point.zone.color;
var seriesStateOptions,
pointStateOptions,
color = this.color,
fill,
stroke,
strokeWidth = Series_pick(pointMarkerOptions.lineWidth,
seriesMarkerOptions.lineWidth),
opacity = 1;
color = (pointColorOption ||
zoneColor ||
pointColor ||
color);
fill = (pointMarkerOptions.fillColor ||
seriesMarkerOptions.fillColor ||
color);
stroke = (pointMarkerOptions.lineColor ||
seriesMarkerOptions.lineColor ||
color);
// Handle hover and select states
state = state || 'normal';
if (state) {
seriesStateOptions = (seriesMarkerOptions.states[state] || {});
pointStateOptions = (pointMarkerOptions.states &&
pointMarkerOptions.states[state]) || {};
strokeWidth = Series_pick(pointStateOptions.lineWidth, seriesStateOptions.lineWidth, strokeWidth + Series_pick(pointStateOptions.lineWidthPlus, seriesStateOptions.lineWidthPlus, 0));
fill = (pointStateOptions.fillColor ||
seriesStateOptions.fillColor ||
fill);
stroke = (pointStateOptions.lineColor ||
seriesStateOptions.lineColor ||
stroke);
opacity = Series_pick(pointStateOptions.opacity, seriesStateOptions.opacity, opacity);
}
return {
'stroke': stroke,
'stroke-width': strokeWidth,
'fill': fill,
'opacity': opacity
};
};
/**
* Clear DOM objects and free up memory.
*
* @private
* @function Highcharts.Series#destroy
*
* @emits Highcharts.Series#event:destroy
*/
Series.prototype.destroy = function (keepEventsForUpdate) {
var series = this, chart = series.chart, issue134 = /AppleWebKit\/533/.test(Series_win.navigator.userAgent), data = series.data || [];
var destroy,
i,
point,
axis;
// Add event hook
Series_fireEvent(series, 'destroy', { keepEventsForUpdate: keepEventsForUpdate });
// Remove events
this.removeEvents(keepEventsForUpdate);
// Erase from axes
(series.axisTypes || []).forEach(function (AXIS) {
axis = series[AXIS];
if (axis && axis.series) {
Series_erase(axis.series, series);
axis.isDirty = axis.forceRedraw = true;
}
});
// Remove legend items
if (series.legendItem) {
series.chart.legend.destroyItem(series);
}
// Destroy all points with their elements
i = data.length;
while (i--) {
point = data[i];
if (point && point.destroy) {
point.destroy();
}
}
for (var _a = 0, _b = series.zones; _a < _b.length; _a++) {
var zone = _b[_a];
// Destroy SVGElement's but preserve primitive props (#20426)
Series_destroyObjectProperties(zone, void 0, true);
}
// Clear the animation timeout if we are destroying the series
// during initial animation
Core_Utilities.clearTimeout(series.animationTimeout);
// Destroy all SVGElements associated to the series
Series_objectEach(series, function (val, prop) {
// Survive provides a hook for not destroying
if (val instanceof SVG_SVGElement && !val.survive) {
// Issue 134 workaround
destroy = issue134 && prop === 'group' ?
'hide' :
'destroy';
val[destroy]();
}
});
// Remove from hoverSeries
if (chart.hoverSeries === series) {
chart.hoverSeries = void 0;
}
Series_erase(chart.series, series);
chart.orderItems('series');
// Clear all members
Series_objectEach(series, function (val, prop) {
if (!keepEventsForUpdate || prop !== 'hcEvents') {
delete series[prop];
}
});
};
/**
* Clip the graphs into zones for colors and styling.
*
* @private
* @function Highcharts.Series#applyZones
*/
Series.prototype.applyZones = function () {
var series = this,
area = series.area,
chart = series.chart,
graph = series.graph,
zones = series.zones,
points = series.points,
xAxis = series.xAxis,
yAxis = series.yAxis,
zoneAxis = series.zoneAxis,
inverted = chart.inverted,
renderer = chart.renderer,
axis = this["" + zoneAxis + "Axis"],
_a = axis || {},
isXAxis = _a.isXAxis,
_b = _a.len,
len = _b === void 0 ? 0 : _b,
_c = _a.minPointOffset,
minPointOffset = _c === void 0 ? 0 : _c,
halfWidth = ((graph === null || graph === void 0 ? void 0 : graph.strokeWidth()) || 0) / 2 + 1,
// Avoid points that are so close to the threshold that the graph
// line would be split
avoidClose = function (zone,
plotX,
plotY) {
if (plotX === void 0) { plotX = 0; }
if (plotY === void 0) { plotY = 0; }
if (inverted) {
plotY = len - plotY;
}
var _a = zone.translated,
translated = _a === void 0 ? 0 : _a,
lineClip = zone.lineClip,
distance = plotY - translated;
lineClip === null || lineClip === void 0 ? void 0 : lineClip.push([
'L',
plotX,
Math.abs(distance) < halfWidth ?
plotY - halfWidth * (distance <= 0 ? -1 : 1) :
translated
]);
};
if (zones.length &&
(graph || area) &&
axis &&
Series_isNumber(axis.min)) {
var axisMax_1 = axis.getExtremes().max + minPointOffset,
// Invert the x and y coordinates of inverted charts
invertPath_1 = function (path) {
path.forEach(function (segment,
i) {
if (segment[0] === 'M' || segment[0] === 'L') {
path[i] = [
segment[0],
isXAxis ? len - segment[1] : segment[1],
isXAxis ? segment[2] : len - segment[2]
];
}
});
};
// Reset
zones.forEach(function (zone) {
zone.lineClip = [];
zone.translated = Series_clamp(axis.toPixels(Series_pick(zone.value, axisMax_1), true) || 0, 0, len);
});
// The use of the Color Threshold assumes there are no gaps so it is
// safe to hide the original graph and area unless it is not
// waterfall series, then use showLine property to set lines between
// columns to be visible (#7862)
if (graph && !this.showLine) {
graph.hide();
}
if (area) {
area.hide();
}
// Prepare for adaptive clips, avoiding segments close to the
// threshold (#19709)
if (zoneAxis === 'y' &&
// Overheat protection
points.length < xAxis.len) {
for (var _d = 0, points_1 = points; _d < points_1.length; _d++) {
var point = points_1[_d];
var plotX = point.plotX,
plotY = point.plotY,
zone = point.zone,
zoneBelow = zone && zones[zones.indexOf(zone) - 1];
// Close to upper boundary
if (zone) {
avoidClose(zone, plotX, plotY);
}
// Close to lower boundary
if (zoneBelow) {
avoidClose(zoneBelow, plotX, plotY);
}
}
}
// Compute and apply the clips
var lastLineClip_1 = [],
// Starting point of the first zone. Offset for category axis
// (#22188).
lastTranslated_1 = axis.toPixels(axis.getExtremes().min - minPointOffset,
true);
zones.forEach(function (zone) {
var _a,
_b;
var lineClip = zone.lineClip || [],
translated = Math.round(zone.translated || 0);
if (xAxis.reversed) {
lineClip.reverse();
}
var clip = zone.clip,
simpleClip = zone.simpleClip,
x1 = 0,
y1 = 0,
x2 = xAxis.len,
y2 = yAxis.len;
if (isXAxis) {
x1 = translated;
x2 = lastTranslated_1;
}
else {
y1 = translated;
y2 = lastTranslated_1;
}
// Adaptive clips
var simplePath = [
['M',
x1,
y1],
['L',
x2,
y1],
['L',
x2,
y2],
['L',
x1,
y2],
['Z']
],
adaptivePath = Series_spreadArray(Series_spreadArray(Series_spreadArray(Series_spreadArray([
simplePath[0]
],
lineClip,
true),
[
simplePath[1],
simplePath[2]
],
false),
lastLineClip_1,
true),
[
simplePath[3],
simplePath[4]
],
false);
lastLineClip_1 = lineClip.reverse();
lastTranslated_1 = translated;
if (inverted) {
invertPath_1(adaptivePath);
if (area) {
invertPath_1(simplePath);
}
}
/* Debug clip paths
zone.path?.destroy();
zone.path = chart.renderer.path(adaptivePath)
.attr({
stroke: zone.color || this.color || 'gray',
'stroke-width': 1,
'dashstyle': 'Dash'
})
.add(series.group);
// */
if (clip) {
clip.animate({ d: adaptivePath });
simpleClip === null || simpleClip === void 0 ? void 0 : simpleClip.animate({ d: simplePath });
}
else {
clip = zone.clip = renderer.path(adaptivePath);
if (area) {
simpleClip = zone.simpleClip = renderer.path(simplePath);
}
}
// When no data, graph zone is not applied and after setData
// clip was ignored. As a result, it should be applied each
// time.
if (graph) {
(_a = zone.graph) === null || _a === void 0 ? void 0 : _a.clip(clip);
}
if (area) {
(_b = zone.area) === null || _b === void 0 ? void 0 : _b.clip(simpleClip);
}
});
}
else if (series.visible) {
// If zones were removed, restore graph and area
if (graph) {
graph.show();
}
if (area) {
area.show();
}
}
};
/**
* General abstraction for creating plot groups like series.group,
* series.dataLabelsGroup and series.markerGroup. On subsequent calls,
* the group will only be adjusted to the updated plot size.
*
* @private
* @function Highcharts.Series#plotGroup
*/
Series.prototype.plotGroup = function (prop, name, visibility, zIndex, parent) {
var group = this[prop];
var isNew = !group,
attrs = {
visibility: visibility,
zIndex: zIndex || 0.1 // Pointer logic uses this
};
// Avoid setting undefined opacity, or in styled mode
if (Series_defined(this.opacity) &&
!this.chart.styledMode && this.state !== 'inactive' // #13719
) {
attrs.opacity = this.opacity;
}
// Generate it on first call
if (!group) {
this[prop] = group = this.chart.renderer
.g()
.add(parent);
}
// Add the class names, and replace existing ones as response to
// Series.update (#6660)
group.addClass(('highcharts-' + name +
' highcharts-series-' + this.index +
' highcharts-' + this.type + '-series ' +
(Series_defined(this.colorIndex) ?
'highcharts-color-' + this.colorIndex + ' ' :
'') +
(this.options.className || '') +
(group.hasClass('highcharts-tracker') ?
' highcharts-tracker' :
'')), true);
// Place it on first and subsequent (redraw) calls
group.attr(attrs)[isNew ? 'attr' : 'animate'](this.getPlotBox(name));
return group;
};
/**
* Get the translation and scale for the plot area of this series.
*
* @function Highcharts.Series#getPlotBox
*/
Series.prototype.getPlotBox = function (name) {
var horAxis = this.xAxis,
vertAxis = this.yAxis;
var chart = this.chart,
inverted = (chart.inverted &&
!chart.polar &&
horAxis &&
this.invertible &&
name === 'series');
// Swap axes for inverted (#2339)
if (chart.inverted) {
horAxis = vertAxis;
vertAxis = this.xAxis;
}
return {
translateX: horAxis ? horAxis.left : chart.plotLeft,
translateY: vertAxis ? vertAxis.top : chart.plotTop,
rotation: inverted ? 90 : 0,
rotationOriginX: inverted ?
(horAxis.len - vertAxis.len) / 2 :
0,
rotationOriginY: inverted ?
(horAxis.len + vertAxis.len) / 2 :
0,
scaleX: inverted ? -1 : 1, // #1623
scaleY: 1
};
};
/**
* Removes the event handlers attached previously with addEvents.
* @private
* @function Highcharts.Series#removeEvents
*/
Series.prototype.removeEvents = function (keepEventsForUpdate) {
var eventsToUnbind = this.eventsToUnbind;
if (!keepEventsForUpdate) {
// Remove all events
Series_removeEvent(this);
}
if (eventsToUnbind.length) {
// Remove only internal events for proper update. #12355 solves
// problem with multiple destroy events
eventsToUnbind.forEach(function (unbind) {
unbind();
});
eventsToUnbind.length = 0;
}
};
/**
* Render the graph and markers. Called internally when first rendering
* and later when redrawing the chart. This function can be extended in
* plugins, but normally shouldn't be called directly.
*
* @function Highcharts.Series#render
*
* @emits Highcharts.Series#event:afterRender
*/
Series.prototype.render = function () {
var _a,
_b,
_c,
_d,
_e;
var series = this,
chart = series.chart,
options = series.options,
hasRendered = series.hasRendered,
animOptions = Series_animObject(options.animation),
visibility = series.visible ?
'inherit' : 'hidden', // #2597
zIndex = options.zIndex,
chartSeriesGroup = chart.seriesGroup;
var animDuration = series.finishedAnimating ?
0 : animOptions.duration;
Series_fireEvent(this, 'render');
// The group
series.plotGroup('group', 'series', visibility, zIndex, chartSeriesGroup);
series.markerGroup = series.plotGroup('markerGroup', 'markers', visibility, zIndex, chartSeriesGroup);
// Initial clipping, applies to columns etc. (#3839).
if (options.clip !== false) {
series.setClip();
}
// Initialize the animation
if (animDuration) {
(_a = series.animate) === null || _a === void 0 ? void 0 : _a.call(series, true);
}
// Draw the graph if any
if (series.drawGraph) {
series.drawGraph();
series.applyZones();
}
// Draw the points
if (series.visible) {
series.drawPoints();
}
// Draw the data labels
(_b = series.drawDataLabels) === null || _b === void 0 ? void 0 : _b.call(series);
// In pie charts, slices are added to the DOM, but actual rendering
// is postponed until labels reserved their space
(_c = series.redrawPoints) === null || _c === void 0 ? void 0 : _c.call(series);
// Draw the mouse tracking area
if (options.enableMouseTracking) {
(_d = series.drawTracker) === null || _d === void 0 ? void 0 : _d.call(series);
}
// Run the animation
if (animDuration) {
(_e = series.animate) === null || _e === void 0 ? void 0 : _e.call(series);
}
// Call the afterAnimate function on animation complete (but don't
// overwrite the animation.complete option which should be available
// to the user).
if (!hasRendered) {
// Additional time if defer is defined before afterAnimate
// will be triggered
if (animDuration && animOptions.defer) {
animDuration += animOptions.defer;
}
series.animationTimeout = Series_syncTimeout(function () {
series.afterAnimate();
}, animDuration || 0);
}
// Means data is in accordance with what you see
series.isDirty = false;
// (See #322) series.isDirty = series.isDirtyData = false; // means
// data is in accordance with what you see
series.hasRendered = true;
Series_fireEvent(series, 'afterRender');
};
/**
* Redraw the series. This function is called internally from
* `chart.redraw` and normally shouldn't be called directly.
* @private
* @function Highcharts.Series#redraw
*/
Series.prototype.redraw = function () {
// Cache it here as it is set to false in render, but used after
var wasDirty = this.isDirty || this.isDirtyData;
this.translate();
this.render();
if (wasDirty) { // #3868, #3945
delete this.kdTree;
}
};
/**
* Whether to reserve space for the series, either because it is visible or
* because the `chart.ignoreHiddenSeries` option is false.
*
* @private
*/
Series.prototype.reserveSpace = function () {
return this.visible || !this.chart.options.chart.ignoreHiddenSeries;
};
/**
* Find the nearest point from a pointer event. This applies to series that
* use k-d-trees to get the nearest point. Native pointer events must be
* normalized using `Pointer.normalize`, that adds `chartX` and `chartY`
* properties.
*
* @sample highcharts/demo/synchronized-charts
* Synchronized charts with tooltips
*
* @function Highcharts.Series#searchPoint
*
* @param {Highcharts.PointerEvent} e
* The normalized pointer event
* @param {boolean} [compareX=false]
* Search only by the X value, not Y
*
* @return {Point|undefined}
* The closest point to the pointer event
*/
Series.prototype.searchPoint = function (e, compareX) {
var _a = this,
xAxis = _a.xAxis,
yAxis = _a.yAxis,
inverted = this.chart.inverted;
return this.searchKDTree({
clientX: inverted ?
xAxis.len - e.chartY + xAxis.pos :
e.chartX - xAxis.pos,
plotY: inverted ?
yAxis.len - e.chartX + yAxis.pos :
e.chartY - yAxis.pos
}, compareX, e);
};
/**
* Build the k-d-tree that is used by mouse and touch interaction to get
* the closest point. Line-like series typically have a one-dimensional
* tree where points are searched along the X axis, while scatter-like
* series typically search in two dimensions, X and Y.
*
* @private
* @function Highcharts.Series#buildKDTree
*/
Series.prototype.buildKDTree = function (e) {
// Prevent multiple k-d-trees from being built simultaneously
// (#6235)
this.buildingKdTree = true;
var series = this,
dimensions = series.options.findNearestPointBy
.indexOf('y') > -1 ? 2 : 1;
/**
* Internal function
* @private
*/
function kdtree(points, depth, dimensions) {
var length = points === null || points === void 0 ? void 0 : points.length;
var axis,
median;
if (length) {
// Alternate between the axis
axis = series.kdAxisArray[depth % dimensions];
// Sort point array
points.sort(function (a, b) { return (a[axis] || 0) - (b[axis] || 0); });
median = Math.floor(length / 2);
// Build and return node
return {
point: points[median],
left: kdtree(points.slice(0, median), depth + 1, dimensions),
right: kdtree(points.slice(median + 1), depth + 1, dimensions)
};
}
}
/**
* Start the recursive build process with a clone of the points
* array and null points filtered out. (#3873)
* @private
*/
function startRecursive() {
series.kdTree = kdtree(series.getValidPoints(void 0,
// For line-type series restrict to plot area, but
// column-type series not (#3916, #4511)
!series.directTouch), dimensions, dimensions);
series.buildingKdTree = false;
}
delete series.kdTree;
// For testing tooltips, don't build async. Also if touchstart, we may
// be dealing with click events on mobile, so don't delay (#6817).
Series_syncTimeout(startRecursive, series.options.kdNow || (e === null || e === void 0 ? void 0 : e.type) === 'touchstart' ? 0 : 1);
};
/**
* @private
* @function Highcharts.Series#searchKDTree
*/
Series.prototype.searchKDTree = function (point, compareX, e, suppliedPointEvaluator, suppliedBSideCheckEvaluator) {
var series = this, _a = this.kdAxisArray, kdX = _a[0], kdY = _a[1], kdComparer = compareX ? 'distX' : 'dist', kdDimensions = (series.options.findNearestPointBy || '')
.indexOf('y') > -1 ? 2 : 1, useRadius = !!series.isBubble, pointEvaluator = suppliedPointEvaluator || (function (p1, p2, comparisonProp) { return [
(p1[comparisonProp] || 0) < (p2[comparisonProp] || 0) ?
p1 :
p2,
false
]; }), bSideCheckEvaluator = suppliedBSideCheckEvaluator || (function (a, b) { return a < b; });
/**
* Set the one and two dimensional distance on the point object.
* @private
*/
function setDistance(p1, p2) {
var _a;
var p1kdX = p1[kdX],
p2kdX = p2[kdX],
x = (Series_defined(p1kdX) && Series_defined(p2kdX)) ? p1kdX - p2kdX : null,
p1kdY = p1[kdY],
p2kdY = p2[kdY],
y = (Series_defined(p1kdY) && Series_defined(p2kdY)) ? p1kdY - p2kdY : 0,
radius = useRadius ? (((_a = p2.marker) === null || _a === void 0 ? void 0 : _a.radius) || 0) : 0;
p2.dist = Math.sqrt(((x && x * x) || 0) + y * y) - radius;
p2.distX = Series_defined(x) ? (Math.abs(x) - radius) : Number.MAX_VALUE;
}
/**
* @private
*/
function doSearch(search, tree, depth, dimensions) {
var _a;
var _b;
var point = tree.point,
axis = series.kdAxisArray[depth % dimensions];
var ret = point,
flip = false;
setDistance(search, point);
// Pick side based on distance to splitting point
var tdist = (search[axis] || 0) - (point[axis] || 0) +
(useRadius ? (((_b = point.marker) === null || _b === void 0 ? void 0 : _b.radius) || 0) : 0), sideA = tdist < 0 ? 'left' : 'right', sideB = tdist < 0 ? 'right' : 'left';
// End of tree
if (tree[sideA]) {
_a = pointEvaluator(point, doSearch(search, tree[sideA], depth + 1, dimensions), kdComparer), ret = _a[0], flip = _a[1];
}
if (tree[sideB]) {
var sqrtTDist = Math.sqrt(tdist * tdist),
retDist = ret[kdComparer];
// Compare distance to current best to splitting point to decide
// whether to check side B or no
if (bSideCheckEvaluator(sqrtTDist, retDist, flip)) {
ret = pointEvaluator(ret, doSearch(search, tree[sideB], depth + 1, dimensions), kdComparer)[0];
}
}
return ret;
}
if (!this.kdTree && !this.buildingKdTree) {
this.buildKDTree(e);
}
if (this.kdTree) {
return doSearch(point, this.kdTree, kdDimensions, kdDimensions);
}
};
/**
* @private
* @function Highcharts.Series#pointPlacementToXValue
*/
Series.prototype.pointPlacementToXValue = function () {
var _a = this,
options = _a.options,
xAxis = _a.xAxis;
var factor = options.pointPlacement;
// Point placement is relative to each series pointRange (#5889)
if (factor === 'between') {
factor = xAxis.reversed ? -0.5 : 0.5; // #11955
}
return Series_isNumber(factor) ?
factor * (options.pointRange || xAxis.pointRange) :
0;
};
/**
* @private
* @function Highcharts.Series#isPointInside
*/
Series.prototype.isPointInside = function (point) {
var _a = this,
chart = _a.chart,
xAxis = _a.xAxis,
yAxis = _a.yAxis,
_b = point.plotX,
plotX = _b === void 0 ? -1 : _b,
_c = point.plotY,
plotY = _c === void 0 ? -1 : _c,
isInside = (plotY >= 0 &&
plotY <= (yAxis ? yAxis.len : chart.plotHeight) &&
plotX >= 0 &&
plotX <= (xAxis ? xAxis.len : chart.plotWidth));
return isInside;
};
/**
* Draw the tracker object that sits above all data labels and markers to
* track mouse events on the graph or points. For the line type charts
* the tracker uses the same graphPath, but with a greater stroke width
* for better control.
* @private
*/
Series.prototype.drawTracker = function () {
var _a;
var series = this,
options = series.options,
trackByArea = options.trackByArea,
trackerPath = [].concat((trackByArea ? series.areaPath : series.graphPath) || []),
chart = series.chart,
pointer = chart.pointer,
renderer = chart.renderer,
snap = ((_a = chart.options.tooltip) === null || _a === void 0 ? void 0 : _a.snap) || 0,
onMouseOver = function () {
if (options.enableMouseTracking &&
chart.hoverSeries !== series) {
series.onMouseOver();
}
},
/*
* Empirical lowest possible opacities for TRACKER_FILL for an
* element to stay invisible but clickable
* IE9: 0.00000000001 (unlimited)
* IE10: 0.0001 (exporting only)
* FF: 0.00000000001 (unlimited)
* Chrome: 0.000001
* Safari: 0.000001
* Opera: 0.00000000001 (unlimited)
*/
TRACKER_FILL = 'rgba(192,192,192,' + (Series_svg ? 0.0001 : 0.002) + ')';
var tracker = series.tracker;
// Draw the tracker
if (tracker) {
tracker.attr({ d: trackerPath });
}
else if (series.graph) { // Create
series.tracker = tracker = renderer.path(trackerPath)
.attr({
visibility: series.visible ? 'inherit' : 'hidden',
zIndex: 2
})
.addClass(trackByArea ?
'highcharts-tracker-area' :
'highcharts-tracker-line')
.add(series.group);
if (!chart.styledMode) {
tracker.attr({
'stroke-linecap': 'round',
'stroke-linejoin': 'round', // #1225
stroke: TRACKER_FILL,
fill: trackByArea ? TRACKER_FILL : 'none',
'stroke-width': series.graph.strokeWidth() +
(trackByArea ? 0 : 2 * snap)
});
}
// The tracker is added to the series group, which is clipped, but
// is covered by the marker group. So the marker group also needs to
// capture events.
[
series.tracker,
series.markerGroup,
series.dataLabelsGroup
].forEach(function (tracker) {
if (tracker) {
tracker.addClass('highcharts-tracker')
.on('mouseover', onMouseOver)
.on('mouseout', function (e) {
pointer === null || pointer === void 0 ? void 0 : pointer.onTrackerMouseOut(e);
});
if (options.cursor && !chart.styledMode) {
tracker.css({ cursor: options.cursor });
}
tracker.on('touchstart', onMouseOver);
}
});
}
Series_fireEvent(this, 'afterDrawTracker');
};
/**
* Add a point to the series after render time. The point can be added at
* the end, or by giving it an X value, to the start or in the middle of the
* series.
*
* @sample highcharts/members/series-addpoint-append/
* Append point
* @sample highcharts/members/series-addpoint-append-and-shift/
* Append and shift
* @sample highcharts/members/series-addpoint-x-and-y/
* Both X and Y values given
* @sample highcharts/members/series-addpoint-pie/
* Append pie slice
* @sample stock/members/series-addpoint/
* Append 100 points in Highcharts Stock
* @sample stock/members/series-addpoint-shift/
* Append and shift in Highcharts Stock
* @sample maps/members/series-addpoint/
* Add a point in Highmaps
*
* @function Highcharts.Series#addPoint
*
* @param {Highcharts.PointOptionsType} options
* The point options. If options is a single number, a point with
* that y value is appended to the series. If it is an array, it will
* be interpreted as x and y values respectively. If it is an
* object, advanced options as outlined under `series.data` are
* applied.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart after the point is added. When adding
* more than one point, it is highly recommended that the redraw
* option be set to false, and instead {@link Chart#redraw} is
* explicitly called after the adding of points is finished.
* Otherwise, the chart will redraw after adding each point.
*
* @param {boolean} [shift=false]
* If true, a point is shifted off the start of the series as one is
* appended to the end.
*
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
* Whether to apply animation, and optionally animation
* configuration.
*
* @param {boolean} [withEvent=true]
* Used internally, whether to fire the series `addPoint` event.
*
* @emits Highcharts.Series#event:addPoint
*/
Series.prototype.addPoint = function (options, redraw, shift, animation, withEvent) {
var series = this,
seriesOptions = series.options,
chart = series.chart,
data = series.data,
table = series.dataTable,
xAxis = series.xAxis,
names = xAxis && xAxis.hasNames && xAxis.names,
dataOptions = seriesOptions.data,
xData = series.getColumn('x');
var isInTheMiddle,
i;
// Optional redraw, defaults to true
redraw = Series_pick(redraw, true);
// Get options and push the point to xData, yData and series.options. In
// series.generatePoints the Point instance will be created on demand
// and pushed to the series.data array.
var point = { series: series };
series.pointClass.prototype.applyOptions.apply(point, [options]);
var x = point.x;
// Get the insertion point
i = xData.length;
if (series.requireSorting && x < xData[i - 1]) {
isInTheMiddle = true;
while (i && xData[i - 1] > x) {
i--;
}
}
// Insert the row at the given index
table.setRow(point, i, true, { addColumns: false });
if (names && point.name) {
names[x] = point.name;
}
dataOptions === null || dataOptions === void 0 ? void 0 : dataOptions.splice(i, 0, options);
if (isInTheMiddle ||
// When processedData is present we need to splice an empty slot
// into series.data, otherwise generatePoints won't pick it up.
series.processedData) {
series.data.splice(i, 0, null);
series.processData();
}
// Generate points to be added to the legend (#1329)
if (seriesOptions.legendType === 'point') {
series.generatePoints();
}
// Shift the first point off the parallel arrays
if (shift) {
if (data[0] && !!data[0].remove) {
data[0].remove(false);
}
else {
Series_spreadArray([
data,
dataOptions
], Object.values(table.getColumns()), true).filter(Series_defined).forEach(function (coll) {
coll.shift();
});
table.rowCount -= 1;
Series_fireEvent(table, 'afterDeleteRows');
}
}
// Fire event
if (withEvent !== false) {
Series_fireEvent(series, 'addPoint', { point: point });
}
// Redraw
series.isDirty = true;
series.isDirtyData = true;
if (redraw) {
chart.redraw(animation); // Animation is set anyway on redraw, #5665
}
};
/**
* Remove a point from the series. Unlike the
* {@link Highcharts.Point#remove} method, this can also be done on a point
* that is not instantiated because it is outside the view or subject to
* Highcharts Stock data grouping.
*
* @sample highcharts/members/series-removepoint/
* Remove cropped point
*
* @function Highcharts.Series#removePoint
*
* @param {number} i
* The index of the point in the {@link Highcharts.Series.data|data}
* array.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart after the point is added. When
* removing more than one point, it is highly recommended that the
* `redraw` option be set to `false`, and instead {@link
* Highcharts.Chart#redraw} is explicitly called after the adding of
* points is finished.
*
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
* Whether and optionally how the series should be animated.
*
* @emits Highcharts.Point#event:remove
*/
Series.prototype.removePoint = function (i, redraw, animation) {
var series = this,
chart = series.chart,
data = series.data,
points = series.points,
table = series.dataTable,
point = data[i],
remove = function () {
// Splice out the point's data from all parallel arrays
Series_spreadArray([
// #4935
(points === null || points === void 0 ? void 0 : points.length) === data.length ? points : void 0,
data,
series.options.data
],
Object.values(table.getColumns()),
true).filter(Series_defined).forEach(function (coll) {
coll.splice(i, 1);
});
// Shorthand row deletion in order to avoid including the whole
// `deleteRows` function in the DataTableCore module.
table.rowCount -= 1;
Series_fireEvent(table, 'afterDeleteRows');
point === null || point === void 0 ? void 0 : point.destroy();
// Redraw
series.isDirty = true;
series.isDirtyData = true;
if (redraw) {
chart.redraw();
}
};
Series_setAnimation(animation, chart);
redraw = Series_pick(redraw, true);
// Fire the event with a default handler of removing the point
if (point) {
point.firePointEvent('remove', null, remove);
}
else {
remove();
}
};
/**
* Remove a series and optionally redraw the chart.
*
* @sample highcharts/members/series-remove/
* Remove first series from a button
*
* @function Highcharts.Series#remove
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart or wait for an explicit call to
* {@link Highcharts.Chart#redraw}.
*
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
* Whether to apply animation, and optionally animation
* configuration.
*
* @param {boolean} [withEvent=true]
* Used internally, whether to fire the series `remove` event.
*
* @emits Highcharts.Series#event:remove
*/
Series.prototype.remove = function (redraw, animation, withEvent, keepEvents) {
var series = this,
chart = series.chart;
/**
* @private
*/
function remove() {
// Destroy elements
series.destroy(keepEvents);
// Redraw
chart.isDirtyLegend = chart.isDirtyBox = true;
chart.linkSeries(keepEvents);
if (Series_pick(redraw, true)) {
chart.redraw(animation);
}
}
// Fire the event with a default handler of removing the point
if (withEvent !== false) {
Series_fireEvent(series, 'remove', null, remove);
}
else {
remove();
}
};
/**
* Update the series with a new set of options. For a clean and precise
* handling of new options, all methods and elements from the series are
* removed, and it is initialized from scratch. Therefore, this method is
* more performance expensive than some other utility methods like {@link
* Series#setData} or {@link Series#setVisible}.
*
* Note that `Series.update` may mutate the passed `data` options.
*
* @sample highcharts/members/series-update/
* Updating series options
* @sample maps/members/series-update/
* Update series options in Highmaps
*
* @function Highcharts.Series#update
*
* @param {Highcharts.SeriesOptionsType} options
* New options that will be merged with the series' existing options.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart after the series is altered. If doing
* more operations on the chart, it is a good idea to set redraw to
* false and call {@link Chart#redraw} after.
*
* @emits Highcharts.Series#event:update
* @emits Highcharts.Series#event:afterUpdate
*/
Series.prototype.update = function (options, redraw) {
var _a,
_b,
_c,
_d;
options = Series_diffObjects(options, this.userOptions);
Series_fireEvent(this, 'update', { options: options });
var series = this,
chart = series.chart,
// Must use user options when changing type because series.options
// is merged in with type specific plotOptions
oldOptions = series.userOptions,
initialType = series.initialType || series.type,
plotOptions = chart.options.plotOptions,
initialSeriesProto = seriesTypes[initialType].prototype,
groups = [
'group',
'markerGroup',
'dataLabelsGroup',
'transformGroup'
],
optionsToCheck = [
'dataGrouping',
'pointStart',
'pointInterval',
'pointIntervalUnit',
'keys'
],
// Animation must be enabled when calling update before the initial
// animation has first run. This happens when calling update
// directly after chart initialization, or when applying responsive
// rules (#6912).
animation = series.finishedAnimating && { animation: false },
kinds = {};
var seriesOptions,
n,
preserve = [
'colorIndex',
'eventOptions',
'navigatorSeries',
'symbolIndex',
'baseSeries'
],
newType = (options.type ||
oldOptions.type ||
chart.options.chart.type);
var keepPoints = !(
// Indicators, histograms etc recalculate the data. It should be
// possible to omit this.
this.hasDerivedData ||
// New type requires new point classes
(newType && newType !== this.type) ||
// New options affecting how the data points are built
typeof options.keys !== 'undefined' ||
typeof options.pointStart !== 'undefined' ||
typeof options.pointInterval !== 'undefined' ||
typeof options.relativeXValue !== 'undefined' ||
options.joinBy ||
options.mapData || // #11636
// Changes to data grouping requires new points in new group
optionsToCheck.some(function (option) { return series.hasOptionChanged(option); }));
newType = newType || initialType;
if (keepPoints) {
preserve.push('data', 'isDirtyData',
// GeoHeatMap interpolation
'isDirtyCanvas', 'points', 'dataTable', 'processedData', // #17057
'xIncrement', 'cropped', '_hasPointMarkers', 'hasDataLabels',
// Networkgraph (#14397)
'nodes', 'layout',
// Treemap
'level',
// Map specific, consider moving it to series-specific preserve-
// properties (#10617)
'mapMap', 'mapData', 'minY', 'maxY', 'minX', 'maxX', 'transformGroups' // #18857
);
if (options.visible !== false) {
preserve.push('area', 'graph');
}
series.parallelArrays.forEach(function (key) {
preserve.push(key + 'Data');
});
if (options.data) {
// `setData` uses `dataSorting` options so we need to update
// them earlier
if (options.dataSorting) {
Series_extend(series.options.dataSorting, options.dataSorting);
}
this.setData(options.data, false);
}
}
else {
this.dataTable.modified = this.dataTable;
}
// Do the merge, with some forced options
options = Series_merge(oldOptions, {
// When oldOptions.index is null it should't be cleared.
// Otherwise navigator series will have wrong indexes (#10193).
index: oldOptions.index === void 0 ?
series.index : oldOptions.index,
pointStart:
// When updating from blank (#7933)
(_c = (_b = (_a = plotOptions === null || plotOptions === void 0 ? void 0 : plotOptions.series) === null || _a === void 0 ? void 0 : _a.pointStart) !== null && _b !== void 0 ? _b : oldOptions.pointStart) !== null && _c !== void 0 ? _c :
// When updating after addPoint
series.getColumn('x')[0]
}, !keepPoints && { data: series.options.data }, options, animation);
// Merge does not merge arrays, but replaces them. Since points were
// updated, `series.options.data` has correct merged options, use it:
if (keepPoints && options.data) {
options.data = series.options.data;
}
// Make sure preserved properties are not destroyed (#3094)
preserve = groups.concat(preserve);
preserve.forEach(function (prop) {
preserve[prop] = series[prop];
delete series[prop];
});
var casting = false;
if (seriesTypes[newType]) {
casting = newType !== series.type;
// Destroy the series and delete all properties, it will be
// reinserted within the `init` call below
series.remove(false, false, false, true);
if (casting) {
// #20264: Re-detect a certain chart properties from new series
chart.propFromSeries();
// Modern browsers including IE11
if (Object.setPrototypeOf) {
Object.setPrototypeOf(series, seriesTypes[newType].prototype);
// Legacy (IE < 11)
}
else {
var ownEvents = Object.hasOwnProperty.call(series, 'hcEvents') && series.hcEvents;
for (n in initialSeriesProto) { // eslint-disable-line guard-for-in
series[n] = void 0;
}
// Reinsert all methods and properties from the new type
// prototype (#2270, #3719).
Series_extend(series, seriesTypes[newType].prototype);
// The events are tied to the prototype chain, don't copy if
// they're not the series' own
if (ownEvents) {
series.hcEvents = ownEvents;
}
else {
delete series.hcEvents;
}
}
}
}
else {
Series_error(17, true, chart, { missingModuleFor: newType });
}
// Re-register groups (#3094) and other preserved properties
preserve.forEach(function (prop) {
series[prop] = preserve[prop];
});
series.init(chart, options);
// Remove particular elements of the points. Check `series.options`
// because we need to consider the options being set on plotOptions as
// well.
if (keepPoints && this.points) {
seriesOptions = series.options;
// What kind of elements to destroy
if (seriesOptions.visible === false) {
kinds.graphic = 1;
kinds.dataLabel = 1;
}
else {
// If the marker got disabled or changed its symbol, width or
// height - destroy
if (this.hasMarkerChanged(seriesOptions, oldOptions)) {
kinds.graphic = 1;
}
if (!((_d = series.hasDataLabels) === null || _d === void 0 ? void 0 : _d.call(series))) {
kinds.dataLabel = 1;
}
}
for (var _e = 0, _f = this.points; _e < _f.length; _e++) {
var point = _f[_e];
if (point && point.series) {
point.resolveColor();
// Destroy elements in order to recreate based on updated
// series options.
if (Object.keys(kinds).length) {
point.destroyElements(kinds);
}
if (seriesOptions.showInLegend === false &&
point.legendItem) {
chart.legend.destroyItem(point);
}
}
}
}
series.initialType = initialType;
chart.linkSeries(); // Links are lost in series.remove (#3028)
// Set data for series with sorting enabled if it isn't set yet (#19715)
chart.setSortedData();
// #15383: Fire updatedData if the type has changed to keep linked
// series such as indicators updated
if (casting && series.linkedSeries.length) {
series.isDirtyData = true;
}
Series_fireEvent(this, 'afterUpdate');
if (Series_pick(redraw, true)) {
chart.redraw(keepPoints ? void 0 : false);
}
};
/**
* Used from within series.update
* @private
*/
Series.prototype.setName = function (name) {
this.name = this.options.name = this.userOptions.name = name;
this.chart.isDirtyLegend = true;
};
/**
* Check if the option has changed.
* @private
*/
Series.prototype.hasOptionChanged = function (optionName) {
var _a,
_b;
var chart = this.chart,
option = this.options[optionName],
plotOptions = chart.options.plotOptions,
oldOption = this.userOptions[optionName],
plotOptionsOption = Series_pick((_a = plotOptions === null || plotOptions === void 0 ? void 0 : plotOptions[this.type]) === null || _a === void 0 ? void 0 : _a[optionName], (_b = plotOptions === null || plotOptions === void 0 ? void 0 : plotOptions.series) === null || _b === void 0 ? void 0 : _b[optionName]);
// Check if `plotOptions` are defined already, #19203
if (oldOption && !Series_defined(plotOptionsOption)) {
return option !== oldOption;
}
return option !== Series_pick(plotOptionsOption, option);
};
/**
* Runs on mouse over the series graphical items.
*
* @function Highcharts.Series#onMouseOver
* @emits Highcharts.Series#event:mouseOver
*/
Series.prototype.onMouseOver = function () {
var series = this,
chart = series.chart,
hoverSeries = chart.hoverSeries,
pointer = chart.pointer;
pointer === null || pointer === void 0 ? void 0 : pointer.setHoverChartIndex();
// Set normal state to previous series
if (hoverSeries && hoverSeries !== series) {
hoverSeries.onMouseOut();
}
// Trigger the event, but to save processing time,
// only if defined
if (series.options.events.mouseOver) {
Series_fireEvent(series, 'mouseOver');
}
// Hover this
series.setState('hover');
/**
* Contains the original hovered series.
*
* @name Highcharts.Chart#hoverSeries
* @type {Highcharts.Series|null}
*/
chart.hoverSeries = series;
};
/**
* Runs on mouse out of the series graphical items.
*
* @function Highcharts.Series#onMouseOut
*
* @emits Highcharts.Series#event:mouseOut
*/
Series.prototype.onMouseOut = function () {
// Trigger the event only if listeners exist
var series = this,
options = series.options,
chart = series.chart,
tooltip = chart.tooltip,
hoverPoint = chart.hoverPoint;
// #182, set to null before the mouseOut event fires
chart.hoverSeries = null;
// Trigger mouse out on the point, which must be in this series
if (hoverPoint) {
hoverPoint.onMouseOut();
}
// Fire the mouse out event
if (series && options.events.mouseOut) {
Series_fireEvent(series, 'mouseOut');
}
// Hide the tooltip
if (tooltip &&
!series.stickyTracking &&
(!tooltip.shared || series.noSharedTooltip)) {
tooltip.hide();
}
// Reset all inactive states
chart.series.forEach(function (s) {
s.setState('', true);
});
};
/**
* Set the state of the series. Called internally on mouse interaction
* operations, but it can also be called directly to visually
* highlight a series.
*
* @function Highcharts.Series#setState
*
* @param {Highcharts.SeriesStateValue|""} [state]
* The new state, can be either `'hover'`, `'inactive'`, `'select'`,
* or `''` (an empty string), `'normal'` or `undefined` to set to
* normal state.
* @param {boolean} [inherit]
* Determines if state should be inherited by points too.
*/
Series.prototype.setState = function (state, inherit) {
var series = this, options = series.options, graph = series.graph, inactiveOtherPoints = options.inactiveOtherPoints, stateOptions = options.states,
// By default a quick animation to hover/inactive,
// slower to un-hover
stateAnimation = Series_pick((stateOptions[state || 'normal'] &&
stateOptions[state || 'normal'].animation), series.chart.options.chart.animation);
var lineWidth = options.lineWidth,
opacity = options.opacity;
state = state || '';
if (series.state !== state) {
// Toggle class names
[
series.group,
series.markerGroup,
series.dataLabelsGroup
].forEach(function (group) {
if (group) {
// Old state
if (series.state) {
group.removeClass('highcharts-series-' + series.state);
}
// New state
if (state) {
group.addClass('highcharts-series-' + state);
}
}
});
series.state = state;
if (!series.chart.styledMode) {
if (stateOptions[state] &&
stateOptions[state].enabled === false) {
return;
}
if (state) {
lineWidth = (stateOptions[state].lineWidth ||
lineWidth + (stateOptions[state].lineWidthPlus || 0)); // #4035
opacity = Series_pick(stateOptions[state].opacity, opacity);
}
if (graph && !graph.dashstyle && Series_isNumber(lineWidth)) {
// Animate the graph stroke-width
for (var _a = 0, _b = Series_spreadArray([
graph
], this.zones.map(function (zone) { return zone.graph; }), true); _a < _b.length; _a++) {
var graphElement = _b[_a];
graphElement === null || graphElement === void 0 ? void 0 : graphElement.animate({
'stroke-width': lineWidth
}, stateAnimation);
}
}
// For some types (pie, networkgraph, sankey) opacity is
// resolved on a point level
if (!inactiveOtherPoints) {
[
series.group,
series.markerGroup,
series.dataLabelsGroup,
series.labelBySeries
].forEach(function (group) {
if (group) {
group.animate({
opacity: opacity
}, stateAnimation);
}
});
}
}
}
// Don't loop over points on a series that doesn't apply inactive state
// to siblings markers (e.g. line, column)
if (inherit && inactiveOtherPoints && series.points) {
series.setAllPointsToState(state || void 0);
}
};
/**
* Set the state for all points in the series.
*
* @function Highcharts.Series#setAllPointsToState
*
* @private
*
* @param {string} [state]
* Can be either `hover` or undefined to set to normal state.
*/
Series.prototype.setAllPointsToState = function (state) {
this.points.forEach(function (point) {
if (point.setState) {
point.setState(state);
}
});
};
/**
* Show or hide the series.
*
* @function Highcharts.Series#setVisible
*
* @param {boolean} [visible]
* True to show the series, false to hide. If undefined, the visibility is
* toggled.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart after the series is altered. If doing more
* operations on the chart, it is a good idea to set redraw to false and
* call {@link Chart#redraw|chart.redraw()} after.
*
* @emits Highcharts.Series#event:hide
* @emits Highcharts.Series#event:show
*/
Series.prototype.setVisible = function (vis, redraw) {
var _a;
var series = this,
chart = series.chart,
ignoreHiddenSeries = chart.options.chart.ignoreHiddenSeries,
oldVisibility = series.visible;
// If called without an argument, toggle visibility
series.visible =
vis =
series.options.visible =
series.userOptions.visible =
typeof vis === 'undefined' ? !oldVisibility : vis; // #5618
var showOrHide = vis ? 'show' : 'hide';
// Show or hide elements
[
'group',
'dataLabelsGroup',
'markerGroup',
'tracker',
'tt'
].forEach(function (key) {
var _a;
(_a = series[key]) === null || _a === void 0 ? void 0 : _a[showOrHide]();
});
// Hide tooltip (#1361)
if (chart.hoverSeries === series ||
((_a = chart.hoverPoint) === null || _a === void 0 ? void 0 : _a.series) === series) {
series.onMouseOut();
}
if (series.legendItem) {
chart.legend.colorizeItem(series, vis);
}
// Rescale or adapt to resized chart
series.isDirty = true;
// In a stack, all other series are affected
if (series.options.stacking) {
chart.series.forEach(function (otherSeries) {
if (otherSeries.options.stacking && otherSeries.visible) {
otherSeries.isDirty = true;
}
});
}
// Show or hide linked series
series.linkedSeries.forEach(function (otherSeries) {
otherSeries.setVisible(vis, false);
});
if (ignoreHiddenSeries) {
chart.isDirtyBox = true;
}
Series_fireEvent(series, showOrHide);
if (redraw !== false) {
chart.redraw();
}
};
/**
* Show the series if hidden.
*
* @sample highcharts/members/series-hide/
* Toggle visibility from a button
*
* @function Highcharts.Series#show
* @emits Highcharts.Series#event:show
*/
Series.prototype.show = function () {
this.setVisible(true);
};
/**
* Hide the series if visible. If the
* [chart.ignoreHiddenSeries](https://api.highcharts.com/highcharts/chart.ignoreHiddenSeries)
* option is true, the chart is redrawn without this series.
*
* @sample highcharts/members/series-hide/
* Toggle visibility from a button
*
* @function Highcharts.Series#hide
* @emits Highcharts.Series#event:hide
*/
Series.prototype.hide = function () {
this.setVisible(false);
};
/**
* Select or unselect the series. This means its
* {@link Highcharts.Series.selected|selected}
* property is set, the checkbox in the legend is toggled and when selected,
* the series is returned by the {@link Highcharts.Chart#getSelectedSeries}
* function.
*
* @sample highcharts/members/series-select/
* Select a series from a button
*
* @function Highcharts.Series#select
*
* @param {boolean} [selected]
* True to select the series, false to unselect. If undefined, the selection
* state is toggled.
*
* @emits Highcharts.Series#event:select
* @emits Highcharts.Series#event:unselect
*/
Series.prototype.select = function (selected) {
var series = this;
series.selected =
selected =
this.options.selected = (typeof selected === 'undefined' ?
!series.selected :
selected);
if (series.checkbox) {
series.checkbox.checked = selected;
}
Series_fireEvent(series, selected ? 'select' : 'unselect');
};
/**
* Checks if a tooltip should be shown for a given point.
*
* @private
*/
Series.prototype.shouldShowTooltip = function (plotX, plotY, options) {
if (options === void 0) { options = {}; }
options.series = this;
options.visiblePlotOnly = true;
return this.chart.isInsidePlot(plotX, plotY, options);
};
/**
* Draws the legend symbol based on the legendSymbol user option.
*
* @private
*/
Series.prototype.drawLegendSymbol = function (legend, item) {
var _a;
(_a = Legend_LegendSymbol[this.options.legendSymbol || 'rectangle']) === null || _a === void 0 ? void 0 : _a.call(this, legend, item);
};
Series.defaultOptions = SeriesDefaults;
/**
* Registry of all available series types.
*
* @name Highcharts.Series.types
* @type {Highcharts.Dictionary<typeof_Highcharts.Series>}
*/
Series.types = Series_SeriesRegistry.seriesTypes;
/* *
*
* Static Functions
*
* */
/**
* Registers a series class to be accessible via `Series.types`.
*
* @function Highcharts.Series.registerType
*
* @param {string} seriesType
* The series type as an identifier string in lower case.
*
* @param {Function} SeriesClass
* The series class as a class pattern or a constructor function with
* prototype.
*/
Series.registerType = Series_SeriesRegistry.registerSeriesType;
return Series;
}());
Series_extend(Series.prototype, {
axisTypes: ['xAxis', 'yAxis'],
coll: 'series',
colorCounter: 0,
directTouch: false,
invertible: true,
isCartesian: true,
kdAxisArray: ['clientX', 'plotY'],
// Each point's x and y values are stored in this.xData and this.yData:
parallelArrays: ['x', 'y'],
pointClass: Series_Point,
requireSorting: true,
// Requires the data to be sorted:
sorted: true
});
/* *
*
* Registry
*
* */
Series_SeriesRegistry.series = Series;
/* *
*
* Default Export
*
* */
/* harmony default export */ var Series_Series = (Series);
/* *
*
* API Declarations
*
* */
/**
* This is a placeholder type of the possible series options for
* [Highcharts](../highcharts/series), [Highcharts Stock](../highstock/series),
* [Highmaps](../highmaps/series), and [Gantt](../gantt/series).
*
* In TypeScript is this dynamically generated to reference all possible types
* of series options.
*
* @ignore-declaration
* @typedef {Highcharts.SeriesOptions|Highcharts.Dictionary<*>} Highcharts.SeriesOptionsType
*/
/**
* Options for `dataSorting`.
*
* @interface Highcharts.DataSortingOptionsObject
* @since 8.0.0
*/ /**
* Enable or disable data sorting for the series.
* @name Highcharts.DataSortingOptionsObject#enabled
* @type {boolean|undefined}
*/ /**
* Whether to allow matching points by name in an update.
* @name Highcharts.DataSortingOptionsObject#matchByName
* @type {boolean|undefined}
*/ /**
* Determines what data value should be used to sort by.
* @name Highcharts.DataSortingOptionsObject#sortKey
* @type {string|undefined}
*/
/**
* Function callback when a series has been animated.
*
* @callback Highcharts.SeriesAfterAnimateCallbackFunction
*
* @param {Highcharts.Series} this
* The series where the event occurred.
*
* @param {Highcharts.SeriesAfterAnimateEventObject} event
* Event arguments.
*/
/**
* Event information regarding completed animation of a series.
*
* @interface Highcharts.SeriesAfterAnimateEventObject
*/ /**
* Animated series.
* @name Highcharts.SeriesAfterAnimateEventObject#target
* @type {Highcharts.Series}
*/ /**
* Event type.
* @name Highcharts.SeriesAfterAnimateEventObject#type
* @type {"afterAnimate"}
*/
/**
* Function callback when the checkbox next to the series' name in the legend is
* clicked.
*
* @callback Highcharts.SeriesCheckboxClickCallbackFunction
*
* @param {Highcharts.Series} this
* The series where the event occurred.
*
* @param {Highcharts.SeriesCheckboxClickEventObject} event
* Event arguments.
*/
/**
* Event information regarding check of a series box.
*
* @interface Highcharts.SeriesCheckboxClickEventObject
*/ /**
* Whether the box has been checked.
* @name Highcharts.SeriesCheckboxClickEventObject#checked
* @type {boolean}
*/ /**
* Related series.
* @name Highcharts.SeriesCheckboxClickEventObject#item
* @type {Highcharts.Series}
*/ /**
* Related series.
* @name Highcharts.SeriesCheckboxClickEventObject#target
* @type {Highcharts.Series}
*/ /**
* Event type.
* @name Highcharts.SeriesCheckboxClickEventObject#type
* @type {"checkboxClick"}
*/
/**
* Function callback when a series is clicked. Return false to cancel toogle
* actions.
*
* @callback Highcharts.SeriesClickCallbackFunction
*
* @param {Highcharts.Series} this
* The series where the event occurred.
*
* @param {Highcharts.SeriesClickEventObject} event
* Event arguments.
*/
/**
* Common information for a click event on a series.
*
* @interface Highcharts.SeriesClickEventObject
* @extends global.Event
*/ /**
* Nearest point on the graph.
* @name Highcharts.SeriesClickEventObject#point
* @type {Highcharts.Point}
*/
/**
* Gets fired when the series is hidden after chart generation time, either by
* clicking the legend item or by calling `.hide()`.
*
* @callback Highcharts.SeriesHideCallbackFunction
*
* @param {Highcharts.Series} this
* The series where the event occurred.
*
* @param {global.Event} event
* The event that occurred.
*/
/**
* The SVG value used for the `stroke-linecap` and `stroke-linejoin` of a line
* graph.
*
* @typedef {"butt"|"round"|"square"|string} Highcharts.SeriesLinecapValue
*/
/**
* Gets fired when the legend item belonging to the series is clicked. The
* default action is to toggle the visibility of the series. This can be
* prevented by returning `false` or calling `event.preventDefault()`.
*
* **Note:** This option is deprecated in favor of
* Highcharts.LegendItemClickCallbackFunction.
*
* @deprecated 11.4.4
* @callback Highcharts.SeriesLegendItemClickCallbackFunction
*
* @param {Highcharts.Series} this
* The series where the event occurred.
*
* @param {Highcharts.SeriesLegendItemClickEventObject} event
* The event that occurred.
*/
/**
* Information about the event.
*
* **Note:** This option is deprecated in favor of
* Highcharts.LegendItemClickEventObject.
*
* @deprecated 11.4.4
* @interface Highcharts.SeriesLegendItemClickEventObject
*/ /**
* Related browser event.
* @name Highcharts.SeriesLegendItemClickEventObject#browserEvent
* @type {global.PointerEvent}
*/ /**
* Prevent the default action of toggle the visibility of the series.
* @name Highcharts.SeriesLegendItemClickEventObject#preventDefault
* @type {Function}
*/ /**
* Related series.
* @name Highcharts.SeriesCheckboxClickEventObject#target
* @type {Highcharts.Series}
*/ /**
* Event type.
* @name Highcharts.SeriesCheckboxClickEventObject#type
* @type {"checkboxClick"}
*/
/**
* Gets fired when the mouse leaves the graph.
*
* @callback Highcharts.SeriesMouseOutCallbackFunction
*
* @param {Highcharts.Series} this
* Series where the event occurred.
*
* @param {global.PointerEvent} event
* Event that occurred.
*/
/**
* Gets fired when the mouse enters the graph.
*
* @callback Highcharts.SeriesMouseOverCallbackFunction
*
* @param {Highcharts.Series} this
* Series where the event occurred.
*
* @param {global.PointerEvent} event
* Event that occurred.
*/
/**
* Translation and scale for the plot area of a series.
*
* @interface Highcharts.SeriesPlotBoxObject
*/ /**
* @name Highcharts.SeriesPlotBoxObject#scaleX
* @type {number}
*/ /**
* @name Highcharts.SeriesPlotBoxObject#scaleY
* @type {number}
*/ /**
* @name Highcharts.SeriesPlotBoxObject#translateX
* @type {number}
*/ /**
* @name Highcharts.SeriesPlotBoxObject#translateY
* @type {number}
*/
/**
* Gets fired when the series is shown after chart generation time, either by
* clicking the legend item or by calling `.show()`.
*
* @callback Highcharts.SeriesShowCallbackFunction
*
* @param {Highcharts.Series} this
* Series where the event occurred.
*
* @param {global.Event} event
* Event that occurred.
*/
/**
* Possible key values for the series state options.
*
* @typedef {"hover"|"inactive"|"normal"|"select"} Highcharts.SeriesStateValue
*/
''; // Detach doclets above
/* *
*
* API Options
*
* */
/**
* Series options for specific data and the data itself. In TypeScript you
* have to cast the series options to specific series types, to get all
* possible options for a series.
*
* @example
* // TypeScript example
* Highcharts.chart('container', {
* series: [{
* color: '#06C',
* data: [[0, 1], [2, 3]]
* } as Highcharts.SeriesLineOptions ]
* });
*
* @type {Array<*>}
* @apioption series
*/
/**
* An id for the series. This can be used after render time to get a pointer
* to the series object through `chart.get()`.
*
* @sample {highcharts} highcharts/plotoptions/series-id/
* Get series by id
*
* @type {string}
* @since 1.2.0
* @apioption series.id
*/
/**
* The index of the series in the chart, affecting the internal index in the
* `chart.series` array, the visible Z index as well as the order in the
* legend.
*
* @type {number}
* @since 2.3.0
* @apioption series.index
*/
/**
* The sequential index of the series in the legend.
*
* @see [legend.reversed](#legend.reversed),
* [yAxis.reversedStacks](#yAxis.reversedStacks)
*
* @sample {highcharts|highstock} highcharts/series/legendindex/
* Legend in opposite order
*
* @type {number}
* @apioption series.legendIndex
*/
/**
* The name of the series as shown in the legend, tooltip etc.
*
* @sample {highcharts} highcharts/series/name/
* Series name
* @sample {highmaps} maps/demo/category-map/
* Series name
*
* @type {string}
* @apioption series.name
*/
/**
* This option allows grouping series in a stacked chart. The stack option
* can be a string or anything else, as long as the grouped series' stack
* options match each other after conversion into a string.
*
* @sample {highcharts} highcharts/series/stack/
* Stacked and grouped columns
* @sample {highcharts} highcharts/series/stack-centerincategory/
* Stacked and grouped, centered in category
*
* @type {number|string}
* @since 2.1
* @product highcharts highstock
* @apioption series.stack
*/
/**
* The type of series, for example `line` or `column`. By default, the
* series type is inherited from [chart.type](#chart.type), so unless the
* chart is a combination of series types, there is no need to set it on the
* series level.
*
* @sample {highcharts} highcharts/series/type/
* Line and column in the same chart
* @sample highcharts/series/type-dynamic/
* Dynamic types with button selector
* @sample {highmaps} maps/demo/mapline-mappoint/
* Multiple types in the same map
*
* @type {string}
* @apioption series.type
*/
/**
* When using dual or multiple x axes, this number defines which xAxis the
* particular series is connected to. It refers to either the
* {@link #xAxis.id|axis id}
* or the index of the axis in the xAxis array, with 0 being the first.
*
* @type {number|string}
* @default 0
* @product highcharts highstock
* @apioption series.xAxis
*/
/**
* When using dual or multiple y axes, this number defines which yAxis the
* particular series is connected to. It refers to either the
* {@link #yAxis.id|axis id}
* or the index of the axis in the yAxis array, with 0 being the first.
*
* @sample {highcharts} highcharts/series/yaxis/
* Apply the column series to the secondary Y axis
*
* @type {number|string}
* @default 0
* @product highcharts highstock
* @apioption series.yAxis
*/
/**
* Define the visual z index of the series.
*
* @sample {highcharts} highcharts/plotoptions/series-zindex-default/
* With no z index, the series defined last are on top
* @sample {highcharts} highcharts/plotoptions/series-zindex/
* With a z index, the series with the highest z index is on top
* @sample {highstock} highcharts/plotoptions/series-zindex-default/
* With no z index, the series defined last are on top
* @sample {highstock} highcharts/plotoptions/series-zindex/
* With a z index, the series with the highest z index is on top
*
* @type {number}
* @product highcharts highstock
* @apioption series.zIndex
*/
''; // Include precedent doclets in transpiled
;// ./code/es5/es-modules/Core/Legend/Legend.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var Legend_animObject = AnimationUtilities.animObject, Legend_setAnimation = AnimationUtilities.setAnimation;
var Legend_registerEventOptions = Core_Foundation.registerEventOptions;
var Legend_composed = Core_Globals.composed, marginNames = Core_Globals.marginNames;
var Legend_distribute = Renderer_RendererUtilities.distribute;
var Legend_format = Core_Templating.format;
var Legend_addEvent = Core_Utilities.addEvent, Legend_createElement = Core_Utilities.createElement, Legend_css = Core_Utilities.css, Legend_defined = Core_Utilities.defined, Legend_discardElement = Core_Utilities.discardElement, Legend_find = Core_Utilities.find, Legend_fireEvent = Core_Utilities.fireEvent, Legend_isNumber = Core_Utilities.isNumber, Legend_merge = Core_Utilities.merge, Legend_pick = Core_Utilities.pick, Legend_pushUnique = Core_Utilities.pushUnique, Legend_relativeLength = Core_Utilities.relativeLength, Legend_stableSort = Core_Utilities.stableSort, Legend_syncTimeout = Core_Utilities.syncTimeout;
/* *
*
* Class
*
* */
/**
* The overview of the chart's series. The legend object is instantiated
* internally in the chart constructor, and is available from the `chart.legend`
* property. Each chart has only one legend.
*
* @class
* @name Highcharts.Legend
*
* @param {Highcharts.Chart} chart
* The chart instance.
*
* @param {Highcharts.LegendOptions} options
* Legend options.
*/
var Legend = /** @class */ (function () {
/* *
*
* Functions
*
* */
/**
* Initialize the legend.
*
* @private
* @function Highcharts.Legend#init
*
* @param {Highcharts.Chart} chart
* The chart instance.
*
* @param {Highcharts.LegendOptions} options
* Legend options.
*/
function Legend(chart, options) {
var _this = this;
/* *
*
* Properties
*
* */
this.allItems = [];
this.initialItemY = 0;
this.itemHeight = 0;
this.itemMarginBottom = 0;
this.itemMarginTop = 0;
this.itemX = 0;
this.itemY = 0;
this.lastItemY = 0;
this.lastLineHeight = 0;
this.legendHeight = 0;
this.legendWidth = 0;
this.maxItemWidth = 0;
this.maxLegendWidth = 0;
this.offsetWidth = 0;
this.padding = 0;
this.pages = [];
this.symbolHeight = 0;
this.symbolWidth = 0;
this.titleHeight = 0;
this.totalItemWidth = 0;
this.widthOption = 0;
/**
* Chart of this legend.
*
* @readonly
* @name Highcharts.Legend#chart
* @type {Highcharts.Chart}
*/
this.chart = chart;
this.setOptions(options);
if (options.enabled) {
// Render it
this.render();
Legend_registerEventOptions(this, options);
// Move checkboxes
Legend_addEvent(this.chart, 'endResize', function () {
this.legend.positionCheckboxes();
});
}
// On Legend.init and Legend.update, make sure that proximate layout
// events are either added or removed (#18362).
Legend_addEvent(this.chart, 'render', function () {
if (_this.options.enabled && _this.proximate) {
_this.proximatePositions();
_this.positionItems();
}
});
}
/**
* @private
* @function Highcharts.Legend#setOptions
* @param {Highcharts.LegendOptions} options
*/
Legend.prototype.setOptions = function (options) {
var padding = Legend_pick(options.padding, 8);
/**
* Legend options.
*
* @readonly
* @name Highcharts.Legend#options
* @type {Highcharts.LegendOptions}
*/
this.options = options;
if (!this.chart.styledMode) {
this.itemStyle = options.itemStyle;
this.itemHiddenStyle = Legend_merge(this.itemStyle, options.itemHiddenStyle);
}
this.itemMarginTop = options.itemMarginTop;
this.itemMarginBottom = options.itemMarginBottom;
this.padding = padding;
this.initialItemY = padding - 5; // 5 is pixels above the text
this.symbolWidth = Legend_pick(options.symbolWidth, 16);
this.pages = [];
this.proximate = options.layout === 'proximate' && !this.chart.inverted;
// #12705: baseline has to be reset on every update
this.baseline = void 0;
};
/**
* Update the legend with new options. Equivalent to running `chart.update`
* with a legend configuration option.
*
* @sample highcharts/legend/legend-update/
* Legend update
*
* @function Highcharts.Legend#update
*
* @param {Highcharts.LegendOptions} options
* Legend options.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart after the axis is altered. If doing more
* operations on the chart, it is a good idea to set redraw to false and
* call {@link Chart#redraw} after. Whether to redraw the chart.
*
* @emits Highcharts.Legends#event:afterUpdate
*/
Legend.prototype.update = function (options, redraw) {
var chart = this.chart;
this.setOptions(Legend_merge(true, this.options, options));
if ('events' in this.options) {
// Legend event handlers
Legend_registerEventOptions(this, this.options);
}
this.destroy();
chart.isDirtyLegend = chart.isDirtyBox = true;
if (Legend_pick(redraw, true)) {
chart.redraw();
}
Legend_fireEvent(this, 'afterUpdate', { redraw: redraw });
};
/**
* Set the colors for the legend item.
*
* @private
* @function Highcharts.Legend#colorizeItem
* @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
* A Series or Point instance
* @param {boolean} [visible=false]
* Dimmed or colored
*
* @todo
* Make events official: Fires the event `afterColorizeItem`.
*/
Legend.prototype.colorizeItem = function (item, visible) {
var _a;
var originalColor = item.color,
_b = item.legendItem || {},
area = _b.area,
group = _b.group,
label = _b.label,
line = _b.line,
symbol = _b.symbol;
if (item instanceof Series_Series || item instanceof Series_Point) {
item.color = ((_a = item.options) === null || _a === void 0 ? void 0 : _a.legendSymbolColor) || originalColor;
}
group === null || group === void 0 ? void 0 : group[visible ? 'removeClass' : 'addClass']('highcharts-legend-item-hidden');
if (!this.chart.styledMode) {
var _c = this.itemHiddenStyle,
itemHiddenStyle = _c === void 0 ? {} : _c,
hiddenColor_1 = itemHiddenStyle.color,
_d = item.options,
fillColor = _d.fillColor,
fillOpacity = _d.fillOpacity,
lineColor = _d.lineColor,
marker = _d.marker,
colorizeHidden = function (attr) {
if (!visible) {
if (attr.fill) {
attr.fill = hiddenColor_1;
}
if (attr.stroke) {
attr.stroke = hiddenColor_1;
}
}
return attr;
};
label === null || label === void 0 ? void 0 : label.css(Legend_merge(visible ? this.itemStyle : itemHiddenStyle));
line === null || line === void 0 ? void 0 : line.attr(colorizeHidden({ stroke: lineColor || item.color }));
if (symbol) {
// Apply marker options
symbol.attr(colorizeHidden(marker && symbol.isMarker ? // #585
item.pointAttribs() :
{ fill: item.color }));
}
area === null || area === void 0 ? void 0 : area.attr(colorizeHidden({
fill: fillColor || item.color,
'fill-opacity': fillColor ? 1 : (fillOpacity !== null && fillOpacity !== void 0 ? fillOpacity : 0.75)
}));
}
item.color = originalColor;
Legend_fireEvent(this, 'afterColorizeItem', { item: item, visible: visible });
};
/**
* @private
* @function Highcharts.Legend#positionItems
*/
Legend.prototype.positionItems = function () {
// Now that the legend width and height are established, put the items
// in the final position
this.allItems.forEach(this.positionItem, this);
if (!this.chart.isResizing) {
this.positionCheckboxes();
}
};
/**
* Position the legend item.
*
* @private
* @function Highcharts.Legend#positionItem
* @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
* The item to position
*/
Legend.prototype.positionItem = function (item) {
var _this = this;
var legend = this,
_a = item.legendItem || {},
group = _a.group,
_b = _a.x,
x = _b === void 0 ? 0 : _b,
_c = _a.y,
y = _c === void 0 ? 0 : _c,
options = legend.options,
symbolPadding = options.symbolPadding,
ltr = !options.rtl,
checkbox = item.checkbox;
if (group && group.element) {
var attribs = {
translateX: ltr ?
x :
legend.legendWidth - x - 2 * symbolPadding - 4,
translateY: y
};
var complete = function () {
Legend_fireEvent(_this, 'afterPositionItem', { item: item });
};
group[Legend_defined(group.translateY) ? 'animate' : 'attr'](attribs, void 0, complete);
}
if (checkbox) {
checkbox.x = x;
checkbox.y = y;
}
};
/**
* Destroy a single legend item, used internally on removing series items.
*
* @private
* @function Highcharts.Legend#destroyItem
* @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
* The item to remove
*/
Legend.prototype.destroyItem = function (item) {
var checkbox = item.checkbox,
legendItem = item.legendItem || {};
// Destroy SVG elements
for (var _i = 0, _a = ['group', 'label', 'line', 'symbol']; _i < _a.length; _i++) {
var key = _a[_i];
if (legendItem[key]) {
legendItem[key] = legendItem[key].destroy();
}
}
if (checkbox) {
Legend_discardElement(checkbox);
}
item.legendItem = void 0;
};
/**
* Destroy the legend. Used internally. To reflow objects, `chart.redraw`
* must be called after destruction.
*
* @private
* @function Highcharts.Legend#destroy
*/
Legend.prototype.destroy = function () {
var legend = this;
// Destroy items
for (var _i = 0, _a = this.getAllItems(); _i < _a.length; _i++) {
var item = _a[_i];
this.destroyItem(item);
}
// Destroy legend elements
for (var _b = 0, _c = [
'clipRect',
'up',
'down',
'pager',
'nav',
'box',
'title',
'group'
]; _b < _c.length; _b++) {
var key = _c[_b];
if (legend[key]) {
legend[key] = legend[key].destroy();
}
}
this.display = null; // Reset in .render on update.
};
/**
* Position the checkboxes after the width is determined.
*
* @private
* @function Highcharts.Legend#positionCheckboxes
*/
Legend.prototype.positionCheckboxes = function () {
var alignAttr = this.group && this.group.alignAttr,
clipHeight = this.clipHeight || this.legendHeight,
titleHeight = this.titleHeight;
var translateY;
if (alignAttr) {
translateY = alignAttr.translateY;
this.allItems.forEach(function (item) {
var checkbox = item.checkbox;
var top;
if (checkbox) {
top = translateY + titleHeight + checkbox.y +
(this.scrollOffset || 0) + 3;
Legend_css(checkbox, {
left: (alignAttr.translateX + item.checkboxOffset +
checkbox.x - 20) + 'px',
top: top + 'px',
display: this.proximate || (top > translateY - 6 &&
top < translateY + clipHeight - 6) ?
'' :
'none'
});
}
}, this);
}
};
/**
* Render the legend title on top of the legend.
*
* @private
* @function Highcharts.Legend#renderTitle
*/
Legend.prototype.renderTitle = function () {
var options = this.options,
padding = this.padding,
titleOptions = options.title;
var bBox,
titleHeight = 0;
if (titleOptions.text) {
if (!this.title) {
/**
* SVG element of the legend title.
*
* @readonly
* @name Highcharts.Legend#title
* @type {Highcharts.SVGElement}
*/
this.title = this.chart.renderer.label(titleOptions.text, padding - 3, padding - 4, void 0, void 0, void 0, options.useHTML, void 0, 'legend-title')
.attr({ zIndex: 1 });
if (!this.chart.styledMode) {
this.title.css(titleOptions.style);
}
this.title.add(this.group);
}
// Set the max title width (#7253)
if (!titleOptions.width) {
this.title.css({
width: this.maxLegendWidth + 'px'
});
}
bBox = this.title.getBBox();
titleHeight = bBox.height;
this.offsetWidth = bBox.width; // #1717
this.contentGroup.attr({ translateY: titleHeight });
}
this.titleHeight = titleHeight;
};
/**
* Set the legend item text.
*
* @function Highcharts.Legend#setText
* @param {Highcharts.Point|Highcharts.Series} item
* The item for which to update the text in the legend.
*/
Legend.prototype.setText = function (item) {
var options = this.options;
item.legendItem.label.attr({
text: options.labelFormat ?
Legend_format(options.labelFormat, item, this.chart) :
options.labelFormatter.call(item)
});
};
/**
* Render a single specific legend item. Called internally from the `render`
* function.
*
* @private
* @function Highcharts.Legend#renderItem
* @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
* The item to render.
*/
Legend.prototype.renderItem = function (item) {
var legend = this,
legendItem = item.legendItem = item.legendItem || {},
chart = legend.chart,
renderer = chart.renderer,
options = legend.options,
horizontal = options.layout === 'horizontal',
symbolWidth = legend.symbolWidth,
symbolPadding = options.symbolPadding || 0,
itemStyle = legend.itemStyle,
itemHiddenStyle = legend.itemHiddenStyle,
itemDistance = horizontal ? Legend_pick(options.itemDistance, 20) : 0,
ltr = !options.rtl,
isSeries = !item.series,
series = !isSeries && item.series.drawLegendSymbol ?
item.series :
item,
seriesOptions = series.options,
showCheckbox = (!!legend.createCheckboxForItem &&
seriesOptions &&
seriesOptions.showCheckbox),
useHTML = options.useHTML,
itemClassName = item.options.className;
var label = legendItem.label,
// Full width minus text width
itemExtraWidth = symbolWidth + symbolPadding +
itemDistance + (showCheckbox ? 20 : 0);
if (!label) { // Generate it once, later move it
// Generate the group box, a group to hold the symbol and text. Text
// is to be appended in Legend class.
legendItem.group = renderer
.g('legend-item')
.addClass('highcharts-' + series.type + '-series ' +
'highcharts-color-' + item.colorIndex +
(itemClassName ? ' ' + itemClassName : '') +
(isSeries ?
' highcharts-series-' + item.index :
''))
.attr({ zIndex: 1 })
.add(legend.scrollGroup);
// Generate the list item text and add it to the group
legendItem.label = label = renderer.text('', ltr ?
symbolWidth + symbolPadding :
-symbolPadding, legend.baseline || 0, useHTML);
if (!chart.styledMode) {
// Merge to prevent modifying original (#1021)
label.css(Legend_merge(item.visible ?
itemStyle :
itemHiddenStyle));
}
label
.attr({
align: ltr ? 'left' : 'right',
zIndex: 2
})
.add(legendItem.group);
// Get the baseline for the first item - the font size is equal for
// all
if (!legend.baseline) {
legend.fontMetrics = renderer.fontMetrics(label);
legend.baseline =
legend.fontMetrics.f + 3 + legend.itemMarginTop;
label.attr('y', legend.baseline);
legend.symbolHeight =
Legend_pick(options.symbolHeight, legend.fontMetrics.f);
if (options.squareSymbol) {
legend.symbolWidth = Legend_pick(options.symbolWidth, Math.max(legend.symbolHeight, 16));
itemExtraWidth = legend.symbolWidth + symbolPadding +
itemDistance + (showCheckbox ? 20 : 0);
if (ltr) {
label.attr('x', legend.symbolWidth + symbolPadding);
}
}
}
// Draw the legend symbol inside the group box
series.drawLegendSymbol(legend, item);
if (legend.setItemEvents) {
legend.setItemEvents(item, label, useHTML);
}
}
// Add the HTML checkbox on top
if (showCheckbox && !item.checkbox && legend.createCheckboxForItem) {
legend.createCheckboxForItem(item);
}
// Colorize the items
legend.colorizeItem(item, item.visible);
// Take care of max width and text overflow (#6659)
if (chart.styledMode || !itemStyle.width) {
label.css({
width: ((options.itemWidth ||
legend.widthOption ||
chart.spacingBox.width) - itemExtraWidth) + 'px'
});
}
// Always update the text
legend.setText(item);
// Calculate the positions for the next line
var bBox = label.getBBox();
var fontMetricsH = (legend.fontMetrics && legend.fontMetrics.h) || 0;
item.itemWidth = item.checkboxOffset =
options.itemWidth ||
legendItem.labelWidth ||
bBox.width + itemExtraWidth;
legend.maxItemWidth = Math.max(legend.maxItemWidth, item.itemWidth);
legend.totalItemWidth += item.itemWidth;
legend.itemHeight = item.itemHeight = Math.round(legendItem.labelHeight ||
// Use bBox for multiline (#16398)
(bBox.height > fontMetricsH * 1.5 ? bBox.height : fontMetricsH));
};
/**
* Get the position of the item in the layout. We now know the
* maxItemWidth from the previous loop.
*
* @private
* @function Highcharts.Legend#layoutItem
* @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
*/
Legend.prototype.layoutItem = function (item) {
var options = this.options,
padding = this.padding,
horizontal = options.layout === 'horizontal',
itemHeight = item.itemHeight,
itemMarginBottom = this.itemMarginBottom,
itemMarginTop = this.itemMarginTop,
itemDistance = horizontal ? Legend_pick(options.itemDistance, 20) : 0,
maxLegendWidth = this.maxLegendWidth,
itemWidth = (options.alignColumns &&
this.totalItemWidth > maxLegendWidth) ?
this.maxItemWidth :
item.itemWidth,
legendItem = item.legendItem || {};
// If the item exceeds the width, start a new line
if (horizontal &&
this.itemX - padding + itemWidth > maxLegendWidth) {
this.itemX = padding;
if (this.lastLineHeight) { // Not for the first line (#10167)
this.itemY += (itemMarginTop +
this.lastLineHeight +
itemMarginBottom);
}
this.lastLineHeight = 0; // Reset for next line (#915, #3976)
}
// Set the edge positions
this.lastItemY = itemMarginTop + this.itemY + itemMarginBottom;
this.lastLineHeight = Math.max(// #915
itemHeight, this.lastLineHeight);
// Cache the position of the newly generated or reordered items
legendItem.x = this.itemX;
legendItem.y = this.itemY;
// Advance
if (horizontal) {
this.itemX += itemWidth;
}
else {
this.itemY +=
itemMarginTop + itemHeight + itemMarginBottom;
this.lastLineHeight = itemHeight;
}
// The width of the widest item
this.offsetWidth = this.widthOption || Math.max((horizontal ? this.itemX - padding - (item.checkbox ?
// Decrease by itemDistance only when no checkbox #4853
0 :
itemDistance) : itemWidth) + padding, this.offsetWidth);
};
/**
* Get all items, which is one item per series for most series and one
* item per point for pie series and its derivatives. Fires the event
* `afterGetAllItems`.
*
* @private
* @function Highcharts.Legend#getAllItems
* @return {Array<(Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series)>}
* The current items in the legend.
* @emits Highcharts.Legend#event:afterGetAllItems
*/
Legend.prototype.getAllItems = function () {
var allItems = [];
this.chart.series.forEach(function (series) {
var seriesOptions = series && series.options;
// Handle showInLegend. If the series is linked to another series,
// defaults to false.
if (series && Legend_pick(seriesOptions.showInLegend, !Legend_defined(seriesOptions.linkedTo) ? void 0 : false, true)) {
// Use points or series for the legend item depending on
// legendType
allItems = allItems.concat((series.legendItem || {}).labels ||
(seriesOptions.legendType === 'point' ?
series.data :
series));
}
});
Legend_fireEvent(this, 'afterGetAllItems', { allItems: allItems });
return allItems;
};
/**
* Get a short, three letter string reflecting the alignment and layout.
*
* @private
* @function Highcharts.Legend#getAlignment
* @return {string}
* The alignment, empty string if floating
*/
Legend.prototype.getAlignment = function () {
var options = this.options;
// Use the first letter of each alignment option in order to detect
// the side. (#4189 - use charAt(x) notation instead of [x] for IE7)
if (this.proximate) {
return options.align.charAt(0) + 'tv';
}
return options.floating ? '' : (options.align.charAt(0) +
options.verticalAlign.charAt(0) +
options.layout.charAt(0));
};
/**
* Adjust the chart margins by reserving space for the legend on only one
* side of the chart. If the position is set to a corner, top or bottom is
* reserved for horizontal legends and left or right for vertical ones.
*
* @private
* @function Highcharts.Legend#adjustMargins
* @param {Array<number>} margin
* @param {Array<number>} spacing
*/
Legend.prototype.adjustMargins = function (margin, spacing) {
var chart = this.chart,
options = this.options,
alignment = this.getAlignment();
if (alignment) {
([
/(lth|ct|rth)/,
/(rtv|rm|rbv)/,
/(rbh|cb|lbh)/,
/(lbv|lm|ltv)/
]).forEach(function (alignments, side) {
if (alignments.test(alignment) && !Legend_defined(margin[side])) {
// Now we have detected on which side of the chart we should
// reserve space for the legend
chart[marginNames[side]] = Math.max(chart[marginNames[side]], (chart.legend[(side + 1) % 2 ? 'legendHeight' : 'legendWidth'] +
[1, -1, -1, 1][side] * options[(side % 2) ? 'x' : 'y'] +
Legend_pick(options.margin, 12) +
spacing[side] +
(chart.titleOffset[side] || 0)));
}
});
}
};
/**
* @private
* @function Highcharts.Legend#proximatePositions
*/
Legend.prototype.proximatePositions = function () {
var chart = this.chart,
boxes = [],
alignLeft = this.options.align === 'left';
this.allItems.forEach(function (item) {
var lastPoint,
height,
useFirstPoint = alignLeft,
target,
top;
if (item.yAxis) {
if (item.xAxis.options.reversed) {
useFirstPoint = !useFirstPoint;
}
if (item.points) {
lastPoint = Legend_find(useFirstPoint ?
item.points :
item.points.slice(0).reverse(), function (item) {
return Legend_isNumber(item.plotY);
});
}
height = this.itemMarginTop +
item.legendItem.label.getBBox().height +
this.itemMarginBottom;
top = item.yAxis.top - chart.plotTop;
if (item.visible) {
target = lastPoint ?
lastPoint.plotY :
item.yAxis.height;
target += top - 0.3 * height;
}
else {
target = top + item.yAxis.height;
}
boxes.push({
target: target,
size: height,
item: item
});
}
}, this);
var legendItem;
for (var _i = 0, _a = Legend_distribute(boxes, chart.plotHeight); _i < _a.length; _i++) {
var box = _a[_i];
legendItem = box.item.legendItem || {};
if (Legend_isNumber(box.pos)) {
legendItem.y = chart.plotTop - chart.spacing[0] + box.pos;
}
}
};
/**
* Render the legend. This method can be called both before and after
* `chart.render`. If called after, it will only rearrange items instead
* of creating new ones. Called internally on initial render and after
* redraws.
*
* @private
* @function Highcharts.Legend#render
*/
Legend.prototype.render = function () {
var legend = this,
chart = legend.chart,
renderer = chart.renderer,
options = legend.options,
padding = legend.padding,
// Add each series or point
allItems = legend.getAllItems();
var display,
legendWidth,
legendHeight,
legendGroup = legend.group,
allowedWidth,
box = legend.box;
legend.itemX = padding;
legend.itemY = legend.initialItemY;
legend.offsetWidth = 0;
legend.lastItemY = 0;
legend.widthOption = Legend_relativeLength(options.width, chart.spacingBox.width - padding);
// Compute how wide the legend is allowed to be
allowedWidth = chart.spacingBox.width - 2 * padding - options.x;
if (['rm', 'lm'].indexOf(legend.getAlignment().substring(0, 2)) > -1) {
allowedWidth /= 2;
}
legend.maxLegendWidth = legend.widthOption || allowedWidth;
if (!legendGroup) {
/**
* SVG group of the legend.
*
* @readonly
* @name Highcharts.Legend#group
* @type {Highcharts.SVGElement}
*/
legend.group = legendGroup = renderer
.g('legend')
.addClass(options.className || '')
.attr({ zIndex: 7 })
.add();
legend.contentGroup = renderer
.g()
.attr({ zIndex: 1 }) // Above background
.add(legendGroup);
legend.scrollGroup = renderer
.g()
.add(legend.contentGroup);
}
legend.renderTitle();
// Sort by legendIndex
Legend_stableSort(allItems, function (a, b) {
return ((a.options && a.options.legendIndex) || 0) -
((b.options && b.options.legendIndex) || 0);
});
// Reversed legend
if (options.reversed) {
allItems.reverse();
}
/**
* All items for the legend, which is an array of series for most series
* and an array of points for pie series and its derivatives.
*
* @readonly
* @name Highcharts.Legend#allItems
* @type {Array<(Highcharts.Point|Highcharts.Series)>}
*/
legend.allItems = allItems;
legend.display = display = !!allItems.length;
// Render the items. First we run a loop to set the text and properties
// and read all the bounding boxes. The next loop computes the item
// positions based on the bounding boxes.
legend.lastLineHeight = 0;
legend.maxItemWidth = 0;
legend.totalItemWidth = 0;
legend.itemHeight = 0;
allItems.forEach(legend.renderItem, legend);
allItems.forEach(legend.layoutItem, legend);
// Get the box
legendWidth = (legend.widthOption || legend.offsetWidth) + padding;
legendHeight = legend.lastItemY + legend.lastLineHeight +
legend.titleHeight;
legendHeight = legend.handleOverflow(legendHeight);
legendHeight += padding;
// Draw the border and/or background
if (!box) {
/**
* SVG element of the legend box.
*
* @readonly
* @name Highcharts.Legend#box
* @type {Highcharts.SVGElement}
*/
legend.box = box = renderer.rect()
.addClass('highcharts-legend-box')
.attr({
r: options.borderRadius
})
.add(legendGroup);
}
// Presentational
if (!chart.styledMode) {
box
.attr({
stroke: options.borderColor,
'stroke-width': options.borderWidth || 0,
fill: options.backgroundColor || 'none'
})
.shadow(options.shadow);
}
if (legendWidth > 0 && legendHeight > 0) {
box[box.placed ? 'animate' : 'attr'](box.crisp.call({}, {
x: 0,
y: 0,
width: legendWidth,
height: legendHeight
}, box.strokeWidth()));
}
// Hide the border if no items
legendGroup[display ? 'show' : 'hide']();
// Open for responsiveness
if (chart.styledMode && legendGroup.getStyle('display') === 'none') {
legendWidth = legendHeight = 0;
}
legend.legendWidth = legendWidth;
legend.legendHeight = legendHeight;
if (display) {
legend.align();
}
if (!this.proximate) {
this.positionItems();
}
Legend_fireEvent(this, 'afterRender');
};
/**
* Align the legend to chart's box.
*
* @private
* @function Highcharts.align
* @param {Highcharts.BBoxObject} alignTo
*/
Legend.prototype.align = function (alignTo) {
if (alignTo === void 0) { alignTo = this.chart.spacingBox; }
var chart = this.chart,
options = this.options;
// If aligning to the top and the layout is horizontal, adjust for
// the title (#7428)
var y = alignTo.y;
if (/(lth|ct|rth)/.test(this.getAlignment()) &&
chart.titleOffset[0] > 0) {
y += chart.titleOffset[0];
}
else if (/(lbh|cb|rbh)/.test(this.getAlignment()) &&
chart.titleOffset[2] > 0) {
y -= chart.titleOffset[2];
}
if (y !== alignTo.y) {
alignTo = Legend_merge(alignTo, { y: y });
}
if (!chart.hasRendered) {
// Avoid animation when adjusting alignment for responsiveness and
// colorAxis label layout
this.group.placed = false;
}
this.group.align(Legend_merge(options, {
width: this.legendWidth,
height: this.legendHeight,
verticalAlign: this.proximate ? 'top' : options.verticalAlign
}), true, alignTo);
};
/**
* Set up the overflow handling by adding navigation with up and down arrows
* below the legend.
*
* @private
* @function Highcharts.Legend#handleOverflow
*/
Legend.prototype.handleOverflow = function (legendHeight) {
var legend = this,
chart = this.chart,
renderer = chart.renderer,
options = this.options,
optionsY = options.y,
alignTop = options.verticalAlign === 'top',
padding = this.padding,
maxHeight = options.maxHeight,
navOptions = options.navigation,
animation = Legend_pick(navOptions.animation,
true),
arrowSize = navOptions.arrowSize || 12,
pages = this.pages,
allItems = this.allItems,
clipToHeight = function (height) {
if (typeof height === 'number') {
clipRect.attr({
height: height
});
}
else if (clipRect) { // Reset (#5912)
legend.clipRect = clipRect.destroy();
legend.contentGroup.clip();
}
// Use HTML
if (legend.contentGroup.div) {
legend.contentGroup.div.style.clip = height ?
'rect(' + padding + 'px,9999px,' +
(padding + height) + 'px,0)' :
'auto';
}
}, addTracker = function (key) {
legend[key] = renderer
.circle(0, 0, arrowSize * 1.3)
.translate(arrowSize / 2, arrowSize / 2)
.add(nav);
if (!chart.styledMode) {
legend[key].attr('fill', 'rgba(0,0,0,0.0001)');
}
return legend[key];
};
var clipHeight,
lastY,
legendItem,
spaceHeight = (chart.spacingBox.height +
(alignTop ? -optionsY : optionsY) - padding),
nav = this.nav,
clipRect = this.clipRect;
// Adjust the height
if (options.layout === 'horizontal' &&
options.verticalAlign !== 'middle' &&
!options.floating) {
spaceHeight /= 2;
}
if (maxHeight) {
spaceHeight = Math.min(spaceHeight, maxHeight);
}
// Reset the legend height and adjust the clipping rectangle
pages.length = 0;
if (legendHeight &&
spaceHeight > 0 &&
legendHeight > spaceHeight &&
navOptions.enabled !== false) {
this.clipHeight = clipHeight =
Math.max(spaceHeight - 20 - this.titleHeight - padding, 0);
this.currentPage = Legend_pick(this.currentPage, 1);
this.fullHeight = legendHeight;
// Fill pages with Y positions so that the top of each a legend item
// defines the scroll top for each page (#2098)
allItems.forEach(function (item, i) {
legendItem = item.legendItem || {};
var y = legendItem.y || 0,
h = Math.round(legendItem.label.getBBox().height);
var len = pages.length;
if (!len || (y - pages[len - 1] > clipHeight &&
(lastY || y) !== pages[len - 1])) {
pages.push(lastY || y);
len++;
}
// Keep track of which page each item is on
legendItem.pageIx = len - 1;
if (lastY) {
(allItems[i - 1].legendItem || {}).pageIx = len - 1;
}
// Add the last page if needed (#2617, #13683)
if (
// Check the last item
i === allItems.length - 1 &&
// If adding next page is needed (#18768)
y + h - pages[len - 1] > clipHeight &&
y > pages[len - 1]) {
pages.push(y);
legendItem.pageIx = len;
}
if (y !== lastY) {
lastY = y;
}
});
// Only apply clipping if needed. Clipping causes blurred legend in
// PDF export (#1787)
if (!clipRect) {
clipRect = legend.clipRect =
renderer.clipRect(0, padding - 2, 9999, 0);
legend.contentGroup.clip(clipRect);
}
clipToHeight(clipHeight);
// Add navigation elements
if (!nav) {
this.nav = nav = renderer.g()
.attr({ zIndex: 1 })
.add(this.group);
this.up = renderer
.symbol('triangle', 0, 0, arrowSize, arrowSize)
.add(nav);
addTracker('upTracker')
.on('click', function () {
legend.scroll(-1, animation);
});
this.pager = renderer.text('', 15, 10)
.addClass('highcharts-legend-navigation');
if (!chart.styledMode && navOptions.style) {
this.pager.css(navOptions.style);
}
this.pager.add(nav);
this.down = renderer
.symbol('triangle-down', 0, 0, arrowSize, arrowSize)
.add(nav);
addTracker('downTracker')
.on('click', function () {
legend.scroll(1, animation);
});
}
// Set initial position
legend.scroll(0);
legendHeight = spaceHeight;
// Reset
}
else if (nav) {
clipToHeight();
this.nav = nav.destroy(); // #6322
this.scrollGroup.attr({
translateY: 1
});
this.clipHeight = 0; // #1379
}
return legendHeight;
};
/**
* Scroll the legend by a number of pages.
*
* @private
* @function Highcharts.Legend#scroll
*
* @param {number} scrollBy
* The number of pages to scroll.
*
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
* Whether and how to apply animation.
*
*/
Legend.prototype.scroll = function (scrollBy, animation) {
var _this = this;
var chart = this.chart,
pages = this.pages,
pageCount = pages.length,
clipHeight = this.clipHeight,
navOptions = this.options.navigation,
pager = this.pager,
padding = this.padding;
var currentPage = this.currentPage + scrollBy;
// When resizing while looking at the last page
if (currentPage > pageCount) {
currentPage = pageCount;
}
if (currentPage > 0) {
if (typeof animation !== 'undefined') {
Legend_setAnimation(animation, chart);
}
this.nav.attr({
translateX: padding,
translateY: clipHeight + this.padding + 7 + this.titleHeight,
visibility: 'inherit'
});
[this.up, this.upTracker].forEach(function (elem) {
elem.attr({
'class': currentPage === 1 ?
'highcharts-legend-nav-inactive' :
'highcharts-legend-nav-active'
});
});
pager.attr({
text: currentPage + '/' + pageCount
});
[this.down, this.downTracker].forEach(function (elem) {
elem.attr({
// Adjust to text width
x: 18 + this.pager.getBBox().width,
'class': currentPage === pageCount ?
'highcharts-legend-nav-inactive' :
'highcharts-legend-nav-active'
});
}, this);
if (!chart.styledMode) {
this.up
.attr({
fill: currentPage === 1 ?
navOptions.inactiveColor :
navOptions.activeColor
});
this.upTracker
.css({
cursor: currentPage === 1 ? 'default' : 'pointer'
});
this.down
.attr({
fill: currentPage === pageCount ?
navOptions.inactiveColor :
navOptions.activeColor
});
this.downTracker
.css({
cursor: currentPage === pageCount ?
'default' :
'pointer'
});
}
this.scrollOffset = -pages[currentPage - 1] + this.initialItemY;
this.scrollGroup.animate({
translateY: this.scrollOffset
});
this.currentPage = currentPage;
this.positionCheckboxes();
// Fire event after scroll animation is complete
var animOptions = Legend_animObject(Legend_pick(animation,
chart.renderer.globalAnimation,
true));
Legend_syncTimeout(function () {
Legend_fireEvent(_this, 'afterScroll', { currentPage: currentPage });
}, animOptions.duration);
}
};
/**
* @private
* @function Highcharts.Legend#setItemEvents
* @param {Highcharts.BubbleLegendItem|Point|Highcharts.Series} item
* @param {Highcharts.SVGElement} legendLabel
* @param {boolean} [useHTML=false]
* @emits Highcharts.Legend#event:itemClick
*/
Legend.prototype.setItemEvents = function (item, legendLabel, useHTML) {
var legend = this,
legendItem = item.legendItem || {},
boxWrapper = legend.chart.renderer.boxWrapper,
isPoint = item instanceof Series_Point,
isSeries = item instanceof Series_Series,
activeClass = 'highcharts-legend-' +
(isPoint ? 'point' : 'series') + '-active',
styledMode = legend.chart.styledMode,
// When `useHTML`, the symbol is rendered in other group, so
// we need to apply events listeners to both places
legendElements = useHTML ?
[legendLabel,
legendItem.symbol] :
[legendItem.group];
var setOtherItemsState = function (state) {
legend.allItems.forEach(function (otherItem) {
if (item !== otherItem) {
[otherItem]
.concat(otherItem.linkedSeries || [])
.forEach(function (otherItem) {
otherItem.setState(state, !isPoint);
});
}
});
};
// Set the events on the item group, or in case of useHTML, the item
// itself (#1249)
for (var _i = 0, legendElements_1 = legendElements; _i < legendElements_1.length; _i++) {
var element = legendElements_1[_i];
if (element) {
element
.on('mouseover', function () {
if (item.visible) {
setOtherItemsState('inactive');
}
item.setState('hover');
// A CSS class to dim or hide other than the hovered
// series.
// Works only if hovered series is visible (#10071).
if (item.visible) {
boxWrapper.addClass(activeClass);
}
if (!styledMode) {
legendLabel.css(legend.options.itemHoverStyle);
}
})
.on('mouseout', function () {
if (!legend.chart.styledMode) {
legendLabel.css(Legend_merge(item.visible ?
legend.itemStyle :
legend.itemHiddenStyle));
}
setOtherItemsState('');
// A CSS class to dim or hide other than the hovered
// series.
boxWrapper.removeClass(activeClass);
item.setState();
})
.on('click', function (event) {
var defaultItemClick = function () {
if (item.setVisible) {
item.setVisible();
}
// Reset inactive state
setOtherItemsState(item.visible ? 'inactive' : '');
};
// A CSS class to dim or hide other than the hovered
// series. Event handling in iOS causes the activeClass
// to be added prior to click in some cases (#7418).
boxWrapper.removeClass(activeClass);
Legend_fireEvent(legend, 'itemClick', {
// Pass over the click/touch event. #4.
browserEvent: event,
legendItem: item
}, defaultItemClick);
// Deprecated logic
// Click the name or symbol
if (isPoint) {
item.firePointEvent('legendItemClick', {
browserEvent: event
});
}
else if (isSeries) {
Legend_fireEvent(item, 'legendItemClick', {
browserEvent: event
});
}
});
}
}
};
/**
* @private
* @function Highcharts.Legend#createCheckboxForItem
* @param {Highcharts.BubbleLegendItem|Point|Highcharts.Series} item
* @emits Highcharts.Series#event:checkboxClick
*/
Legend.prototype.createCheckboxForItem = function (item) {
var legend = this;
item.checkbox = Legend_createElement('input', {
type: 'checkbox',
className: 'highcharts-legend-checkbox',
checked: item.selected,
defaultChecked: item.selected // Required by IE7
}, legend.options.itemCheckboxStyle, legend.chart.container);
Legend_addEvent(item.checkbox, 'click', function (event) {
var target = event.target;
Legend_fireEvent(item.series || item, 'checkboxClick', {
checked: target.checked,
item: item
}, function () {
item.select();
});
});
};
return Legend;
}());
/* *
*
* Class Namespace
*
* */
(function (Legend) {
/* *
*
* Declarations
*
* */
/* *
*
* Functions
*
* */
/**
* @private
*/
function compose(ChartClass) {
if (Legend_pushUnique(Legend_composed, 'Core.Legend')) {
Legend_addEvent(ChartClass, 'beforeMargins', function () {
/**
* The legend contains an interactive overview over chart items,
* usually individual series or points depending on the series
* type. The color axis and bubble legend are also rendered in
* the chart legend.
*
* @name Highcharts.Chart#legend
* @type {Highcharts.Legend}
*/
this.legend = new Legend(this, this.options.legend);
});
}
}
Legend.compose = compose;
})(Legend || (Legend = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Legend_Legend = (Legend);
/* *
*
* API Declarations
*
* */
/**
* @interface Highcharts.LegendItemObject
*/ /**
* @name Highcharts.LegendItemObject#item
* @type {Highcharts.SVGElement|undefined}
*/ /**
* @name Highcharts.LegendItemObject#line
* @type {Highcharts.SVGElement|undefined}
*/ /**
* @name Highcharts.LegendItemObject#symbol
* @type {Highcharts.SVGElement|undefined}
*/
/**
* Gets fired when the legend item is clicked. The default
* action is to toggle the visibility of the series or point. This can be
* prevented by returning `false` or calling `event.preventDefault()`.
*
* @callback Highcharts.LegendItemClickCallbackFunction
*
* @param {Highcharts.Legend} this
* The legend on which the event occurred.
*
* @param {Highcharts.LegendItemClickEventObject} event
* The event that occurred.
*/
/**
* Information about the legend click event.
*
* @interface Highcharts.LegendItemClickEventObject
*/ /**
* Related browser event.
* @name Highcharts.LegendItemClickEventObject#browserEvent
* @type {Highcharts.PointerEvent}
*/ /**
* Prevent the default action of toggle the visibility of the series or point.
* @name Highcharts.LegendItemClickEventObject#preventDefault
* @type {Function}
* */ /**
* Related legend item, it can be series, point, color axis or data class from
* color axis.
* @name Highcharts.LegendItemClickEventObject#legendItem
* @type {Highcharts.Series|Highcharts.Point|Highcharts.LegendItemObject}
* */ /**
* Related legend.
* @name Highcharts.LegendItemClickEventObject#target
* @type {Highcharts.Legend}
*/ /**
* Event type.
* @name Highcharts.LegendItemClickEventObject#type
* @type {"itemClick"}
*/
/**
* Gets fired when the legend item belonging to a point is clicked. The default
* action is to toggle the visibility of the point. This can be prevented by
* returning `false` or calling `event.preventDefault()`.
*
* **Note:** This option is deprecated in favor of
* Highcharts.LegendItemClickCallbackFunction.
*
* @deprecated 11.4.4
* @callback Highcharts.PointLegendItemClickCallbackFunction
*
* @param {Highcharts.Point} this
* The point on which the event occurred.
*
* @param {Highcharts.PointLegendItemClickEventObject} event
* The event that occurred.
*/
/**
* Information about the legend click event.
*
* **Note:** This option is deprecated in favor of
* Highcharts.LegendItemClickEventObject.
*
* @deprecated 11.4.4
* @interface Highcharts.PointLegendItemClickEventObject
*/ /**
* Related browser event.
* @name Highcharts.PointLegendItemClickEventObject#browserEvent
* @type {Highcharts.PointerEvent}
*/ /**
* Prevent the default action of toggle the visibility of the point.
* @name Highcharts.PointLegendItemClickEventObject#preventDefault
* @type {Function}
*/ /**
* Related point.
* @name Highcharts.PointLegendItemClickEventObject#target
* @type {Highcharts.Point}
*/ /**
* Event type.
* @name Highcharts.PointLegendItemClickEventObject#type
* @type {"legendItemClick"}
*/
/**
* Series color as used by the legend and some series types.
* @name Highcharts.Series#color
* @type {Highcharts.ColorType|undefined}
*/ /**
* Legend data for the series.
* @name Highcharts.Series#legendItem
* @type {Highcharts.LegendItemObject|undefined}
* @since 10.3.0
*/
/**
* Gets fired when the legend item belonging to a series is clicked. The default
* action is to toggle the visibility of the series. This can be prevented by
* returning `false` or calling `event.preventDefault()`.
*
* **Note:** This option is deprecated in favor of
* Highcharts.LegendItemClickCallbackFunction.
*
* @deprecated 11.4.4
* @callback Highcharts.SeriesLegendItemClickCallbackFunction
*
* @param {Highcharts.Series} this
* The series where the event occurred.
*
* @param {Highcharts.SeriesLegendItemClickEventObject} event
* The event that occurred.
*/
/**
* Information about the legend click event.
*
* **Note:** This option is deprecated in favor of
* Highcharts.LegendItemClickEventObject.
*
* @deprecated 11.4.4
* @interface Highcharts.SeriesLegendItemClickEventObject
*/ /**
* Related browser event.
* @name Highcharts.SeriesLegendItemClickEventObject#browserEvent
* @type {Highcharts.PointerEvent}
*/ /**
* Prevent the default action of toggle the visibility of the series.
* @name Highcharts.SeriesLegendItemClickEventObject#preventDefault
* @type {Function}
*/ /**
* Related series.
* @name Highcharts.SeriesLegendItemClickEventObject#target
* @type {Highcharts.Series}
*/ /**
* Event type.
* @name Highcharts.SeriesLegendItemClickEventObject#type
* @type {"legendItemClick"}
*/
(''); // Keeps doclets above in JS file
;// ./code/es5/es-modules/Core/Chart/Chart.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var Chart_assign = (undefined && undefined.__assign) || function () {
Chart_assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return Chart_assign.apply(this, arguments);
};
var Chart_spreadArray = (undefined && undefined.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var Chart_animate = AnimationUtilities.animate, Chart_animObject = AnimationUtilities.animObject, Chart_setAnimation = AnimationUtilities.setAnimation;
var Chart_defaultOptions = Defaults.defaultOptions;
var Chart_numberFormat = Core_Templating.numberFormat;
var Chart_registerEventOptions = Core_Foundation.registerEventOptions;
var Chart_charts = Core_Globals.charts, Chart_doc = Core_Globals.doc, Chart_marginNames = Core_Globals.marginNames, Chart_svg = Core_Globals.svg, Chart_win = Core_Globals.win;
var Chart_seriesTypes = Series_SeriesRegistry.seriesTypes;
var Chart_addEvent = Core_Utilities.addEvent, Chart_attr = Core_Utilities.attr, Chart_createElement = Core_Utilities.createElement, Chart_css = Core_Utilities.css, Chart_defined = Core_Utilities.defined, Chart_diffObjects = Core_Utilities.diffObjects, Chart_discardElement = Core_Utilities.discardElement, Chart_erase = Core_Utilities.erase, Chart_error = Core_Utilities.error, Chart_extend = Core_Utilities.extend, Chart_find = Core_Utilities.find, Chart_fireEvent = Core_Utilities.fireEvent, Chart_getAlignFactor = Core_Utilities.getAlignFactor, Chart_getStyle = Core_Utilities.getStyle, Chart_isArray = Core_Utilities.isArray, Chart_isNumber = Core_Utilities.isNumber, Chart_isObject = Core_Utilities.isObject, Chart_isString = Core_Utilities.isString, Chart_merge = Core_Utilities.merge, Chart_objectEach = Core_Utilities.objectEach, Chart_pick = Core_Utilities.pick, Chart_pInt = Core_Utilities.pInt, Chart_relativeLength = Core_Utilities.relativeLength, Chart_removeEvent = Core_Utilities.removeEvent, Chart_splat = Core_Utilities.splat, Chart_syncTimeout = Core_Utilities.syncTimeout, Chart_uniqueKey = Core_Utilities.uniqueKey;
/* *
*
* Class
*
* */
/* eslint-disable no-invalid-this, valid-jsdoc */
/**
* The Chart class. The recommended constructor is {@link Highcharts#chart}.
*
* @example
* let chart = Highcharts.chart('container', {
* title: {
* text: 'My chart'
* },
* series: [{
* data: [1, 3, 2, 4]
* }]
* })
*
* @class
* @name Highcharts.Chart
*
* @param {string|Highcharts.HTMLDOMElement} [renderTo]
* The DOM element to render to, or its id.
*
* @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.
*/
var Chart = /** @class */ (function () {
// Implementation
function Chart(a,
/* eslint-disable @typescript-eslint/no-unused-vars */
b, c
/* eslint-enable @typescript-eslint/no-unused-vars */
) {
this.sharedClips = {};
var args = Chart_spreadArray([],
arguments,
true);
// Remove the optional first argument, renderTo, and set it on this.
if (Chart_isString(a) || a.nodeName) {
this.renderTo = args.shift();
}
this.init(args[0], args[1]);
}
/**
* Factory function for basic charts.
*
* @example
* // Render a chart in to div#container
* let chart = Highcharts.chart('container', {
* title: {
* text: 'My chart'
* },
* series: [{
* data: [1, 3, 2, 4]
* }]
* });
*
* @function Highcharts.chart
*
* @param {string|Highcharts.HTMLDOMElement} [renderTo]
* The DOM element to render to, or its id.
*
* @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 {Highcharts.Chart}
* Returns the Chart object.
*/
Chart.chart = function (a, b, c) {
return new Chart(a, b, c);
};
/* *
*
* Functions
*
* */
/**
* Function setting zoom options after chart init and after chart update.
* Offers support for deprecated options.
*
* @private
* @function Highcharts.Chart#setZoomOptions
*/
Chart.prototype.setZoomOptions = function () {
var chart = this,
options = chart.options.chart,
zooming = options.zooming;
chart.zooming = Chart_assign(Chart_assign({}, zooming), { type: Chart_pick(options.zoomType, zooming.type), key: Chart_pick(options.zoomKey, zooming.key), pinchType: Chart_pick(options.pinchType, zooming.pinchType), singleTouch: Chart_pick(options.zoomBySingleTouch, zooming.singleTouch, false), resetButton: Chart_merge(zooming.resetButton, options.resetZoomButton) });
};
/**
* Overridable function that initializes the chart. The constructor's
* arguments are passed on directly.
*
* @function Highcharts.Chart#init
*
* @param {Highcharts.Options} userOptions
* Custom options.
*
* @param {Function} [callback]
* Function to run when the chart has loaded and all external
* images are loaded.
*
*
* @emits Highcharts.Chart#event:init
* @emits Highcharts.Chart#event:afterInit
*/
Chart.prototype.init = function (userOptions, callback) {
// Fire the event with a default function
Chart_fireEvent(this, 'init', { args: arguments }, function () {
var _a,
_b;
var options = Chart_merge(Chart_defaultOptions,
userOptions), // Do the merge
optionsChart = options.chart,
renderTo = this.renderTo || optionsChart.renderTo;
/**
* The original options given to the constructor or a chart factory
* like {@link Highcharts.chart} and {@link Highcharts.stockChart}.
* The original options are shallow copied to avoid mutation. The
* copy, `chart.userOptions`, may later be mutated to reflect
* updated options throughout the lifetime of the chart.
*
* For collections, like `series`, `xAxis` and `yAxis`, the chart
* user options should always be reflected by the item user option,
* so for example the following should always be true:
*
* `chart.xAxis[0].userOptions === chart.userOptions.xAxis[0]`
*
* @name Highcharts.Chart#userOptions
* @type {Highcharts.Options}
*/
this.userOptions = Chart_extend({}, userOptions);
if (!(this.renderTo = (Chart_isString(renderTo) ?
Chart_doc.getElementById(renderTo) :
renderTo))) {
// Display an error if the renderTo is wrong
Chart_error(13, true, this);
}
this.margin = [];
this.spacing = [];
// An array of functions that returns labels that should be
// considered for anti-collision
this.labelCollectors = [];
this.callback = callback;
this.isResizing = 0;
/**
* The options structure for the chart after merging
* {@link #defaultOptions} and {@link #userOptions}. It contains
* members for the sub elements like series, legend, tooltip etc.
*
* @name Highcharts.Chart#options
* @type {Highcharts.Options}
*/
this.options = options;
/**
* All the axes in the chart.
*
* @see Highcharts.Chart.xAxis
* @see Highcharts.Chart.yAxis
*
* @name Highcharts.Chart#axes
* @type {Array<Highcharts.Axis>}
*/
this.axes = [];
/**
* All the current series in the chart.
*
* @name Highcharts.Chart#series
* @type {Array<Highcharts.Series>}
*/
this.series = [];
this.locale = (_a = options.lang.locale) !== null && _a !== void 0 ? _a : (_b = this.renderTo.closest('[lang]')) === null || _b === void 0 ? void 0 : _b.lang;
/**
* The `Time` object associated with the chart. Since v6.0.5,
* time settings can be applied individually for each chart. If
* no individual settings apply, the `Time` object is shared by
* all instances.
*
* @name Highcharts.Chart#time
* @type {Highcharts.Time}
*/
this.time = new Core_Time(Chart_extend(options.time || {}, {
locale: this.locale
}));
options.time = this.time.options;
/**
* Callback function to override the default function that formats
* all the numbers in the chart. Returns a string with the formatted
* number.
*
* @name Highcharts.Chart#numberFormatter
* @type {Highcharts.NumberFormatterCallbackFunction}
*/
this.numberFormatter = (optionsChart.numberFormatter || Chart_numberFormat).bind(this);
/**
* Whether the chart is in styled mode, meaning all presentational
* attributes are avoided.
*
* @name Highcharts.Chart#styledMode
* @type {boolean}
*/
this.styledMode = optionsChart.styledMode;
this.hasCartesianSeries = optionsChart.showAxes;
var chart = this;
/**
* Index position of the chart in the {@link Highcharts#charts}
* property.
*
* @name Highcharts.Chart#index
* @type {number}
* @readonly
*/
chart.index = Chart_charts.length; // Add the chart to the global lookup
Chart_charts.push(chart);
Core_Globals.chartCount++;
// Chart event handlers
Chart_registerEventOptions(this, optionsChart);
/**
* A collection of the X axes in the chart.
*
* @name Highcharts.Chart#xAxis
* @type {Array<Highcharts.Axis>}
*/
chart.xAxis = [];
/**
* A collection of the Y axes in the chart.
*
* @name Highcharts.Chart#yAxis
* @type {Array<Highcharts.Axis>}
*
* @todo
* Make events official: Fire the event `afterInit`.
*/
chart.yAxis = [];
chart.pointCount = chart.colorCounter = chart.symbolCounter = 0;
this.setZoomOptions();
// Fire after init but before first render, before axes and series
// have been initialized.
Chart_fireEvent(chart, 'afterInit');
chart.firstRender();
});
};
/**
* Internal function to unitialize an individual series.
*
* @private
* @function Highcharts.Chart#initSeries
*/
Chart.prototype.initSeries = function (options) {
var chart = this,
optionsChart = chart.options.chart,
type = (options.type ||
optionsChart.type),
SeriesClass = Chart_seriesTypes[type];
// No such series type
if (!SeriesClass) {
Chart_error(17, true, chart, { missingModuleFor: type });
}
var series = new SeriesClass();
if (typeof series.init === 'function') {
series.init(chart, options);
}
return series;
};
/**
* Internal function to set data for all series with enabled sorting.
*
* @private
* @function Highcharts.Chart#setSortedData
*/
Chart.prototype.setSortedData = function () {
this.getSeriesOrderByLinks().forEach(function (series) {
// We need to set data for series with sorting after series init
if (!series.points && !series.data && series.enabledDataSorting) {
series.setData(series.options.data, false);
}
});
};
/**
* Sort and return chart series in order depending on the number of linked
* series.
*
* @private
* @function Highcharts.Series#getSeriesOrderByLinks
*/
Chart.prototype.getSeriesOrderByLinks = function () {
return this.series.concat().sort(function (a, b) {
if (a.linkedSeries.length || b.linkedSeries.length) {
return b.linkedSeries.length - a.linkedSeries.length;
}
return 0;
});
};
/**
* Order all series or axes above a given index. When series or axes are
* added and ordered by configuration, only the last series is handled
* (#248, #1123, #2456, #6112). This function is called on series and axis
* initialization and destroy.
*
* @private
* @function Highcharts.Chart#orderItems
* @param {string} coll The collection name
* @param {number} [fromIndex=0]
* If this is given, only the series above this index are handled.
*/
Chart.prototype.orderItems = function (coll, fromIndex) {
if (fromIndex === void 0) { fromIndex = 0; }
var collection = this[coll],
// Item options should be reflected in chart.options.series,
// chart.options.yAxis etc
optionsArray = this.options[coll] = Chart_splat(this.options[coll])
.slice(),
userOptionsArray = this.userOptions[coll] = this.userOptions[coll] ?
Chart_splat(this.userOptions[coll]).slice() :
[];
if (this.hasRendered) {
// Remove all above index
optionsArray.splice(fromIndex);
userOptionsArray.splice(fromIndex);
}
if (collection) {
for (var i = fromIndex, iEnd = collection.length; i < iEnd; ++i) {
var item = collection[i];
if (item) {
/**
* Contains the series' index in the `Chart.series` array.
*
* @name Highcharts.Series#index
* @type {number}
* @readonly
*/
item.index = i;
if (item instanceof Series_Series) {
item.name = item.getName();
}
if (!item.options.isInternal) {
optionsArray[i] = item.options;
userOptionsArray[i] = item.userOptions;
}
}
}
}
};
/**
* Check whether a given point is within the plot area.
*
* @function Highcharts.Chart#isInsidePlot
*
* @param {number} plotX
* Pixel x relative to the plot area.
*
* @param {number} plotY
* Pixel y relative to the plot area.
*
* @param {Highcharts.ChartIsInsideOptionsObject} [options]
* Options object.
*
* @return {boolean}
* Returns true if the given point is inside the plot area.
*/
Chart.prototype.isInsidePlot = function (plotX, plotY, options) {
var _a;
if (options === void 0) { options = {}; }
var _b = this,
inverted = _b.inverted,
plotBox = _b.plotBox,
plotLeft = _b.plotLeft,
plotTop = _b.plotTop,
scrollablePlotBox = _b.scrollablePlotBox,
_c = (options.visiblePlotOnly &&
((_a = this.scrollablePlotArea) === null || _a === void 0 ? void 0 : _a.scrollingContainer)) || {},
_d = _c.scrollLeft,
scrollLeft = _d === void 0 ? 0 : _d,
_e = _c.scrollTop,
scrollTop = _e === void 0 ? 0 : _e,
series = options.series,
box = (options.visiblePlotOnly && scrollablePlotBox) || plotBox,
x = options.inverted ? plotY : plotX,
y = options.inverted ? plotX : plotY,
e = {
x: x,
y: y,
isInsidePlot: true,
options: options
};
if (!options.ignoreX) {
var xAxis = (series &&
(inverted && !this.polar ? series.yAxis : series.xAxis)) || {
pos: plotLeft,
len: Infinity
};
var chartX = options.paneCoordinates ?
xAxis.pos + x : plotLeft + x;
if (!(chartX >= Math.max(scrollLeft + plotLeft, xAxis.pos) &&
chartX <= Math.min(scrollLeft + plotLeft + box.width, xAxis.pos + xAxis.len))) {
e.isInsidePlot = false;
}
}
if (!options.ignoreY && e.isInsidePlot) {
var yAxis = (!inverted && options.axis &&
!options.axis.isXAxis && options.axis) || (series && (inverted ? series.xAxis : series.yAxis)) || {
pos: plotTop,
len: Infinity
};
var chartY = options.paneCoordinates ?
yAxis.pos + y : plotTop + y;
if (!(chartY >= Math.max(scrollTop + plotTop, yAxis.pos) &&
chartY <= Math.min(scrollTop + plotTop + box.height, yAxis.pos + yAxis.len))) {
e.isInsidePlot = false;
}
}
Chart_fireEvent(this, 'afterIsInsidePlot', e);
return e.isInsidePlot;
};
/**
* Redraw the chart after changes have been done to the data, axis extremes
* chart size or chart elements. All methods for updating axes, series or
* points have a parameter for redrawing the chart. This is `true` by
* default. But in many cases you want to do more than one operation on the
* chart before redrawing, for example add a number of points. In those
* cases it is a waste of resources to redraw the chart for each new point
* added. So you add the points and call `chart.redraw()` after.
*
* @function Highcharts.Chart#redraw
*
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
* If or how to apply animation to the redraw. When `undefined`, it applies
* the animation that is set in the `chart.animation` option.
*
* @emits Highcharts.Chart#event:afterSetExtremes
* @emits Highcharts.Chart#event:beforeRedraw
* @emits Highcharts.Chart#event:predraw
* @emits Highcharts.Chart#event:redraw
* @emits Highcharts.Chart#event:render
* @emits Highcharts.Chart#event:updatedData
*/
Chart.prototype.redraw = function (animation) {
Chart_fireEvent(this, 'beforeRedraw');
var chart = this,
axes = chart.hasCartesianSeries ? chart.axes : chart.colorAxis || [],
series = chart.series,
pointer = chart.pointer,
legend = chart.legend,
legendUserOptions = chart.userOptions.legend,
renderer = chart.renderer,
isHiddenChart = renderer.isHidden(),
afterRedraw = [];
var hasDirtyStacks,
hasStackedSeries,
i,
isDirtyBox = chart.isDirtyBox,
redrawLegend = chart.isDirtyLegend,
serie;
renderer.rootFontSize = renderer.boxWrapper.getStyle('font-size');
// Handle responsive rules, not only on resize (#6130)
if (chart.setResponsive) {
chart.setResponsive(false);
}
// Set the global animation. When chart.hasRendered is not true, the
// redraw call comes from a responsive rule and animation should not
// occur.
Chart_setAnimation(chart.hasRendered ? animation : false, chart);
if (isHiddenChart) {
chart.temporaryDisplay();
}
// Adjust title layout (reflow multiline text)
chart.layOutTitles(false);
// Link stacked series
i = series.length;
while (i--) {
serie = series[i];
if (serie.options.stacking || serie.options.centerInCategory) {
hasStackedSeries = true;
if (serie.isDirty) {
hasDirtyStacks = true;
break;
}
}
}
if (hasDirtyStacks) { // Mark others as dirty
i = series.length;
while (i--) {
serie = series[i];
if (serie.options.stacking) {
serie.isDirty = true;
}
}
}
// Handle updated data in the series
series.forEach(function (serie) {
if (serie.isDirty) {
if (serie.options.legendType === 'point') {
if (typeof serie.updateTotals === 'function') {
serie.updateTotals();
}
redrawLegend = true;
}
else if (legendUserOptions &&
(!!legendUserOptions.labelFormatter ||
legendUserOptions.labelFormat)) {
redrawLegend = true; // #2165
}
}
if (serie.isDirtyData) {
Chart_fireEvent(serie, 'updatedData');
}
});
// Handle added or removed series
if (redrawLegend && legend && legend.options.enabled) {
// Draw legend graphics
legend.render();
chart.isDirtyLegend = false;
}
// Reset stacks
if (hasStackedSeries) {
chart.getStacks();
}
// Set axes scales
axes.forEach(function (axis) {
axis.updateNames();
axis.setScale();
});
chart.getMargins(); // #3098
// If one axis is dirty, all axes must be redrawn (#792, #2169)
axes.forEach(function (axis) {
if (axis.isDirty) {
isDirtyBox = true;
}
});
// Redraw axes
axes.forEach(function (axis) {
// Fire 'afterSetExtremes' only if extremes are set
var key = axis.min + ',' + axis.max;
if (axis.extKey !== key) { // #821, #4452
axis.extKey = key;
// Prevent a recursive call to chart.redraw() (#1119)
afterRedraw.push(function () {
Chart_fireEvent(axis, 'afterSetExtremes', Chart_extend(axis.eventArgs, axis.getExtremes())); // #747, #751
delete axis.eventArgs;
});
}
if (isDirtyBox || hasStackedSeries) {
axis.redraw();
}
});
// The plot areas size has changed
if (isDirtyBox) {
chart.drawChartBox();
}
// Fire an event before redrawing series, used by the boost module to
// clear previous series renderings.
Chart_fireEvent(chart, 'predraw');
// Redraw affected series
series.forEach(function (serie) {
if ((isDirtyBox || serie.isDirty) && serie.visible) {
serie.redraw();
}
// Set it here, otherwise we will have unlimited 'updatedData' calls
// for a hidden series after setData(). Fixes #6012
serie.isDirtyData = false;
});
// Move tooltip or reset
if (pointer) {
pointer.reset(true);
}
// Redraw if canvas
renderer.draw();
// Fire the events
Chart_fireEvent(chart, 'redraw');
Chart_fireEvent(chart, 'render');
if (isHiddenChart) {
chart.temporaryDisplay(true);
}
// Fire callbacks that are put on hold until after the redraw
afterRedraw.forEach(function (callback) {
callback.call();
});
};
/**
* Get an axis, series or point object by `id` as given in the configuration
* options. Returns `undefined` if no item is found.
*
* @sample highcharts/plotoptions/series-id/
* Get series by id
*
* @function Highcharts.Chart#get
*
* @param {string} id
* The id as given in the configuration options.
*
* @return {Highcharts.Axis|Highcharts.Series|Highcharts.Point|undefined}
* The retrieved item.
*/
Chart.prototype.get = function (id) {
var series = this.series;
/**
* @private
*/
function itemById(item) {
return (item.id === id ||
(item.options && item.options.id === id));
}
var ret =
// Search axes
Chart_find(this.axes,
itemById) ||
// Search series
Chart_find(this.series,
itemById);
// Search points
for (var i = 0; !ret && i < series.length; i++) {
ret = Chart_find(series[i].points || [], itemById);
}
return ret;
};
/**
* Create the Axis instances based on the config options.
*
* @private
* @function Highcharts.Chart#createAxes
* @emits Highcharts.Chart#event:afterCreateAxes
* @emits Highcharts.Chart#event:createAxes
*/
Chart.prototype.createAxes = function () {
var options = this.userOptions;
Chart_fireEvent(this, 'createAxes');
for (var _i = 0, _a = ['xAxis', 'yAxis']; _i < _a.length; _i++) {
var coll = _a[_i];
var arr = options[coll] = Chart_splat(options[coll] || {});
for (var _b = 0, arr_1 = arr; _b < arr_1.length; _b++) {
var axisOptions = arr_1[_b];
// eslint-disable-next-line no-new
new Axis_Axis(this, axisOptions, coll);
}
}
Chart_fireEvent(this, 'afterCreateAxes');
};
/**
* Returns an array of all currently selected points in the chart. Points
* can be selected by clicking or programmatically by the
* {@link Highcharts.Point#select}
* function.
*
* @sample highcharts/plotoptions/series-allowpointselect-line/
* Get selected points
* @sample highcharts/members/point-select-lasso/
* Lasso selection
* @sample highcharts/chart/events-selection-points/
* Rectangle selection
*
* @function Highcharts.Chart#getSelectedPoints
*
* @return {Array<Highcharts.Point>}
* The currently selected points.
*/
Chart.prototype.getSelectedPoints = function () {
return this.series.reduce(function (acc, series) {
// For one-to-one points inspect series.data in order to retrieve
// points outside the visible range (#6445). For grouped data,
// inspect the generated series.points.
series.getPointsCollection()
.forEach(function (point) {
if (Chart_pick(point.selectedStaging, point.selected)) {
acc.push(point);
}
});
return acc;
}, []);
};
/**
* Returns an array of all currently selected series in the chart. Series
* can be selected either programmatically by the
* {@link Highcharts.Series#select}
* function or by checking the checkbox next to the legend item if
* [series.showCheckBox](https://api.highcharts.com/highcharts/plotOptions.series.showCheckbox)
* is true.
*
* @sample highcharts/members/chart-getselectedseries/
* Get selected series
*
* @function Highcharts.Chart#getSelectedSeries
*
* @return {Array<Highcharts.Series>}
* The currently selected series.
*/
Chart.prototype.getSelectedSeries = function () {
return this.series.filter(function (s) { return s.selected; });
};
/**
* Set a new title or subtitle for the chart.
*
* @sample highcharts/members/chart-settitle/
* Set title text and styles
*
* @function Highcharts.Chart#setTitle
*
* @param {Highcharts.TitleOptions} [titleOptions]
* New title options. The title text itself is set by the
* `titleOptions.text` property.
*
* @param {Highcharts.SubtitleOptions} [subtitleOptions]
* New subtitle options. The subtitle text itself is set by the
* `subtitleOptions.text` property.
*
* @param {boolean} [redraw]
* Whether to redraw the chart or wait for a later call to
* `chart.redraw()`.
*/
Chart.prototype.setTitle = function (titleOptions, subtitleOptions, redraw) {
this.applyDescription('title', titleOptions);
this.applyDescription('subtitle', subtitleOptions);
// The initial call also adds the caption. On update, chart.update will
// relay to Chart.setCaption.
this.applyDescription('caption', void 0);
this.layOutTitles(redraw);
};
/**
* Apply a title, subtitle or caption for the chart
*
* @private
* @function Highcharts.Chart#applyDescription
* @param key {string}
* Either title, subtitle or caption
* @param {Highcharts.TitleOptions|Highcharts.SubtitleOptions|Highcharts.CaptionOptions|undefined} explicitOptions
* The options to set, will be merged with default options.
*/
Chart.prototype.applyDescription = function (key, explicitOptions) {
var _a;
var chart = this;
// Merge default options with explicit options
var options = this.options[key] = Chart_merge(this.options[key],
explicitOptions);
var elem = this[key];
if (elem && explicitOptions) {
this[key] = elem = elem.destroy(); // Remove old
}
if (options && !elem) {
elem = this.renderer.text(options.text, 0, 0, options.useHTML)
.attr({
align: options.align,
'class': 'highcharts-' + key,
zIndex: options.zIndex || 4
})
.css({
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
})
.add();
// Update methods, relay to `applyDescription`
elem.update = function (updateOptions, redraw) {
chart.applyDescription(key, updateOptions);
chart.layOutTitles(redraw);
};
// Presentational
if (!this.styledMode) {
elem.css(Chart_extend(key === 'title' ? {
// #2944
fontSize: this.options.isStock ? '1em' : '1.2em'
} : {}, options.style));
}
// Get unwrapped text length and reset
elem.textPxLength = elem.getBBox().width;
elem.css({ whiteSpace: (_a = options.style) === null || _a === void 0 ? void 0 : _a.whiteSpace });
/**
* The chart title. The title has an `update` method that allows
* modifying the options directly or indirectly via
* `chart.update`.
*
* @sample highcharts/members/title-update/
* Updating titles
*
* @name Highcharts.Chart#title
* @type {Highcharts.TitleObject}
*/
/**
* The chart subtitle. The subtitle has an `update` method that
* allows modifying the options directly or indirectly via
* `chart.update`.
*
* @name Highcharts.Chart#subtitle
* @type {Highcharts.SubtitleObject}
*/
this[key] = elem;
}
};
/**
* Internal function to lay out the chart title, subtitle and caption, and
* cache the full offset height for use in `getMargins`. The result is
* stored in `this.titleOffset`.
*
* @private
* @function Highcharts.Chart#layOutTitles
*
* @param {boolean} [redraw=true]
* @emits Highcharts.Chart#event:afterLayOutTitles
*/
Chart.prototype.layOutTitles = function (redraw) {
var _this = this;
var _a,
_b,
_c,
_d;
if (redraw === void 0) { redraw = true; }
var titleOffset = [0, 0, 0],
_e = this,
options = _e.options,
renderer = _e.renderer,
spacingBox = _e.spacingBox;
// Lay out the title, subtitle and caption respectively
['title', 'subtitle', 'caption'].forEach(function (key) {
var _a;
var desc = _this[key],
descOptions = _this.options[key],
alignTo = Chart_merge(spacingBox),
textPxLength = (desc === null || desc === void 0 ? void 0 : desc.textPxLength) || 0;
if (desc && descOptions) {
// Provide a hook for the exporting button to shift the title
Chart_fireEvent(_this, 'layOutTitle', { alignTo: alignTo, key: key, textPxLength: textPxLength });
var fontMetrics = renderer.fontMetrics(desc), baseline = fontMetrics.b, lineHeight = fontMetrics.h, verticalAlign = descOptions.verticalAlign || 'top', topAligned = verticalAlign === 'top',
// Use minScale only for top-aligned titles. It is not
// likely that we will need scaling for other positions, but
// if it is requested, we need to adjust the vertical
// position to the scale.
minScale = topAligned && descOptions.minScale || 1, offset = key === 'title' ?
topAligned ? -3 : 0 :
// Floating subtitle (#6574)
topAligned ? titleOffset[0] + 2 : 0, uncappedScale = Math.min(alignTo.width / textPxLength, 1), scale = Math.max(minScale, uncappedScale), alignAttr = Chart_merge({
y: verticalAlign === 'bottom' ?
baseline :
offset + baseline
}, {
align: key === 'title' ?
// Title defaults to center for short titles,
// left for word-wrapped titles
(uncappedScale < minScale ? 'left' : 'center') :
// Subtitle defaults to the title.align
(_a = _this.title) === null || _a === void 0 ? void 0 : _a.alignValue
}, descOptions), width = descOptions.width || ((uncappedScale > minScale ?
// One line
_this.chartWidth :
// Allow word wrap
alignTo.width) / scale);
// No animation when switching alignment
if (desc.alignValue !== alignAttr.align) {
desc.placed = false;
}
// Set the width and read the height
var height = Math.round(desc
.css({ width: "" + width + "px" })
// Skip the cache for HTML (#3481, #11666)
.getBBox(descOptions.useHTML).height);
alignAttr.height = height;
// Perform scaling and alignment
desc
.align(alignAttr, false, alignTo)
.attr({
align: alignAttr.align,
scaleX: scale,
scaleY: scale,
'transform-origin': "" + (alignTo.x +
textPxLength *
scale *
Chart_getAlignFactor(alignAttr.align)) + " ".concat(lineHeight)
});
// Adjust the rendered title offset
if (!descOptions.floating) {
var offset_1 = height * (
// When scaling down the title, preserve the offset as
// long as it's only one line, but scale down the offset
// if the title wraps to multiple lines.
height < lineHeight * 1.2 ? 1 : scale);
if (verticalAlign === 'top') {
titleOffset[0] = Math.ceil(titleOffset[0] + offset_1);
}
else if (verticalAlign === 'bottom') {
titleOffset[2] = Math.ceil(titleOffset[2] + offset_1);
}
}
}
}, this);
// Handle title.margin and caption.margin
if (titleOffset[0] &&
(((_a = options.title) === null || _a === void 0 ? void 0 : _a.verticalAlign) || 'top') === 'top') {
titleOffset[0] += ((_b = options.title) === null || _b === void 0 ? void 0 : _b.margin) || 0;
}
if (titleOffset[2] &&
((_c = options.caption) === null || _c === void 0 ? void 0 : _c.verticalAlign) === 'bottom') {
titleOffset[2] += ((_d = options.caption) === null || _d === void 0 ? void 0 : _d.margin) || 0;
}
var requiresDirtyBox = (!this.titleOffset ||
this.titleOffset.join(',') !== titleOffset.join(','));
// Used in getMargins
this.titleOffset = titleOffset;
Chart_fireEvent(this, 'afterLayOutTitles');
if (!this.isDirtyBox && requiresDirtyBox) {
this.isDirtyBox = this.isDirtyLegend = requiresDirtyBox;
// Redraw if necessary (#2719, #2744)
if (this.hasRendered && redraw && this.isDirtyBox) {
this.redraw();
}
}
};
/**
* Internal function to get the available size of the container element
*
* @private
* @function Highcharts.Chart#getContainerBox
*/
Chart.prototype.getContainerBox = function () {
var _this = this;
// Temporarily hide support divs from a11y and others, #21888
var nonContainers = [].map.call(this.renderTo.children,
function (child) {
if (child !== _this.container) {
var display = child.style.display;
child.style.display = 'none';
return [child, display];
}
}), box = {
width: Chart_getStyle(this.renderTo, 'width', true) || 0,
height: (Chart_getStyle(this.renderTo, 'height', true) || 0)
};
// Restore the non-containers
nonContainers.filter(Boolean).forEach(function (_a) {
var div = _a[0],
display = _a[1];
div.style.display = display;
});
return box;
};
/**
* Internal function to get the chart width and height according to options
* and container size. Sets {@link Chart.chartWidth} and
* {@link Chart.chartHeight}.
*
* @private
* @function Highcharts.Chart#getChartSize
*/
Chart.prototype.getChartSize = function () {
var _a;
var chart = this,
optionsChart = chart.options.chart,
widthOption = optionsChart.width,
heightOption = optionsChart.height,
containerBox = chart.getContainerBox(),
enableDefaultHeight = containerBox.height <= 1 ||
( // #21510, prevent infinite reflow
!((_a = chart.renderTo.parentElement) === null || _a === void 0 ? void 0 : _a.style.height) &&
chart.renderTo.style.height === '100%');
/**
* The current pixel width of the chart.
*
* @name Highcharts.Chart#chartWidth
* @type {number}
*/
chart.chartWidth = Math.max(// #1393
0, widthOption || containerBox.width || 600 // #1460
);
/**
* The current pixel height of the chart.
*
* @name Highcharts.Chart#chartHeight
* @type {number}
*/
chart.chartHeight = Math.max(0, Chart_relativeLength(heightOption, chart.chartWidth) ||
(enableDefaultHeight ? 400 : containerBox.height));
chart.containerBox = containerBox;
};
/**
* If the renderTo element has no offsetWidth, most likely one or more of
* its parents are hidden. Loop up the DOM tree to temporarily display the
* parents, then save the original display properties, and when the true
* size is retrieved, reset them. Used on first render and on redraws.
*
* @private
* @function Highcharts.Chart#temporaryDisplay
*
* @param {boolean} [revert]
* Revert to the saved original styles.
*/
Chart.prototype.temporaryDisplay = function (revert) {
var node = this.renderTo,
tempStyle;
if (!revert) {
while (node && node.style) {
// When rendering to a detached node, it needs to be temporarily
// attached in order to read styling and bounding boxes (#5783,
// #7024).
if (!Chart_doc.body.contains(node) && !node.parentNode) {
node.hcOrigDetached = true;
Chart_doc.body.appendChild(node);
}
if (Chart_getStyle(node, 'display', false) === 'none' ||
node.hcOricDetached) {
node.hcOrigStyle = {
display: node.style.display,
height: node.style.height,
overflow: node.style.overflow
};
tempStyle = {
display: 'block',
overflow: 'hidden'
};
if (node !== this.renderTo) {
tempStyle.height = 0;
}
Chart_css(node, tempStyle);
// If it still doesn't have an offset width after setting
// display to block, it probably has an !important priority
// #2631, 6803
if (!node.offsetWidth) {
node.style.setProperty('display', 'block', 'important');
}
}
node = node.parentNode;
if (node === Chart_doc.body) {
break;
}
}
}
else {
while (node && node.style) {
if (node.hcOrigStyle) {
Chart_css(node, node.hcOrigStyle);
delete node.hcOrigStyle;
}
if (node.hcOrigDetached) {
Chart_doc.body.removeChild(node);
node.hcOrigDetached = false;
}
node = node.parentNode;
}
}
};
/**
* Set the {@link Chart.container|chart container's} class name, in
* addition to `highcharts-container`.
*
* @function Highcharts.Chart#setClassName
*
* @param {string} [className]
* The additional class name.
*/
Chart.prototype.setClassName = function (className) {
this.container.className = 'highcharts-container ' + (className || '');
};
/**
* Get the containing element, determine the size and create the inner
* container div to hold the chart.
*
* @private
* @function Highcharts.Chart#afterGetContainer
* @emits Highcharts.Chart#event:afterGetContainer
*/
Chart.prototype.getContainer = function () {
var _a;
var chart = this,
options = chart.options,
optionsChart = options.chart,
indexAttrName = 'data-highcharts-chart',
containerId = Chart_uniqueKey(),
renderTo = chart.renderTo;
var containerStyle;
// If the container already holds a chart, destroy it. The check for
// hasRendered is there because web pages that are saved to disk from
// the browser, will preserve the data-highcharts-chart attribute and
// the SVG contents, but not an interactive chart. So in this case,
// charts[oldChartIndex] will point to the wrong chart if any (#2609).
var oldChartIndex = Chart_pInt(Chart_attr(renderTo,
indexAttrName));
if (Chart_isNumber(oldChartIndex) &&
Chart_charts[oldChartIndex] &&
Chart_charts[oldChartIndex].hasRendered) {
Chart_charts[oldChartIndex].destroy();
}
// Make a reference to the chart from the div
Chart_attr(renderTo, indexAttrName, chart.index);
// Remove previous chart
renderTo.innerHTML = HTML_AST.emptyHTML;
// If the container doesn't have an offsetWidth, it has or is a child of
// a node that has display:none. We need to temporarily move it out to a
// visible state to determine the size, else the legend and tooltips
// won't render properly. The skipClone option is used in sparklines as
// a micro optimization, saving about 1-2 ms each chart.
if (!optionsChart.skipClone && !renderTo.offsetWidth) {
chart.temporaryDisplay();
}
// Get the width and height
chart.getChartSize();
var chartHeight = chart.chartHeight;
var chartWidth = chart.chartWidth;
// Allow table cells and flex-boxes to shrink without the chart blocking
// them out (#6427)
Chart_css(renderTo, { overflow: 'hidden' });
// Create the inner container
if (!chart.styledMode) {
containerStyle = Chart_extend({
position: 'relative',
// Needed for context menu (avoidscrollbars) and content
// overflow in IE
overflow: 'hidden',
width: chartWidth + 'px',
height: chartHeight + 'px',
textAlign: 'left',
lineHeight: 'normal', // #427
zIndex: 0, // #1072
'-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
userSelect: 'none', // #13503
'touch-action': 'manipulation',
outline: 'none',
padding: '0px'
}, optionsChart.style || {});
}
/**
* The containing HTML element of the chart. The container is
* dynamically inserted into the element given as the `renderTo`
* parameter in the {@link Highcharts#chart} constructor.
*
* @name Highcharts.Chart#container
* @type {Highcharts.HTMLDOMElement}
*/
var container = Chart_createElement('div', {
id: containerId
},
containerStyle,
renderTo);
chart.container = container;
// Adjust width if setting height affected it (#20334)
chart.getChartSize();
if (chartWidth !== chart.chartWidth) {
chartWidth = chart.chartWidth;
if (!chart.styledMode) {
Chart_css(container, {
width: Chart_pick((_a = optionsChart.style) === null || _a === void 0 ? void 0 : _a.width, chartWidth + 'px')
});
}
}
chart.containerBox = chart.getContainerBox();
// Cache the cursor (#1650)
chart._cursor = container.style.cursor;
// Initialize the renderer
var Renderer = optionsChart.renderer || !Chart_svg ?
Renderer_RendererRegistry.getRendererType(optionsChart.renderer) :
SVG_SVGRenderer;
/**
* The renderer instance of the chart. Each chart instance has only one
* associated renderer.
*
* @name Highcharts.Chart#renderer
* @type {Highcharts.SVGRenderer}
*/
chart.renderer = new Renderer(container, chartWidth, chartHeight, void 0, optionsChart.forExport, options.exporting && options.exporting.allowHTML, chart.styledMode);
// Set the initial animation from the options
Chart_setAnimation(void 0, chart);
chart.setClassName(optionsChart.className);
if (!chart.styledMode) {
chart.renderer.setStyle(optionsChart.style);
}
else {
// Initialize definitions
for (var key in options.defs) { // eslint-disable-line guard-for-in
this.renderer.definition(options.defs[key]);
}
}
// Add a reference to the charts index
chart.renderer.chartIndex = chart.index;
Chart_fireEvent(this, 'afterGetContainer');
};
/**
* Calculate margins by rendering axis labels in a preliminary position.
* Title, subtitle and legend have already been rendered at this stage, but
* will be moved into their final positions.
*
* @private
* @function Highcharts.Chart#getMargins
* @emits Highcharts.Chart#event:getMargins
*/
Chart.prototype.getMargins = function (skipAxes) {
var _a = this,
spacing = _a.spacing,
margin = _a.margin,
titleOffset = _a.titleOffset;
this.resetMargins();
// Adjust for title and subtitle
if (titleOffset[0] && !Chart_defined(margin[0])) {
this.plotTop = Math.max(this.plotTop, titleOffset[0] + spacing[0]);
}
if (titleOffset[2] && !Chart_defined(margin[2])) {
this.marginBottom = Math.max(this.marginBottom, titleOffset[2] + spacing[2]);
}
// Adjust for legend
if (this.legend && this.legend.display) {
this.legend.adjustMargins(margin, spacing);
}
Chart_fireEvent(this, 'getMargins');
if (!skipAxes) {
this.getAxisMargins();
}
};
/**
* @private
* @function Highcharts.Chart#getAxisMargins
*/
Chart.prototype.getAxisMargins = function () {
var chart = this,
// [top, right, bottom, left]
axisOffset = chart.axisOffset = [0, 0, 0, 0],
colorAxis = chart.colorAxis,
margin = chart.margin,
getOffset = function (axes) {
axes.forEach(function (axis) {
if (axis.visible) {
axis.getOffset();
}
});
};
// Pre-render axes to get labels offset width
if (chart.hasCartesianSeries) {
getOffset(chart.axes);
}
else if (colorAxis && colorAxis.length) {
getOffset(colorAxis);
}
// Add the axis offsets
Chart_marginNames.forEach(function (m, side) {
if (!Chart_defined(margin[side])) {
chart[m] += axisOffset[side];
}
});
chart.setChartSize();
};
/**
* Return the current options of the chart, but only those that differ from
* default options. Items that can be either an object or an array of
* objects, like `series`, `xAxis` and `yAxis`, are always returned as
* array.
*
* @sample highcharts/members/chart-getoptions
*
* @function Highcharts.Chart#getOptions
*
* @since 11.1.0
*/
Chart.prototype.getOptions = function () {
return Chart_diffObjects(this.userOptions, Chart_defaultOptions);
};
/**
* Reflows the chart to its container. By default, the Resize Observer is
* attached to the chart's div which allows to reflows the chart
* automatically to its container, as per the
* [chart.reflow](https://api.highcharts.com/highcharts/chart.reflow)
* option.
*
* @sample highcharts/chart/events-container/
* Pop up and reflow
*
* @function Highcharts.Chart#reflow
*
* @param {global.Event} [e]
* Event arguments. Used primarily when the function is called
* internally as a response to window resize.
*/
Chart.prototype.reflow = function (e) {
var _a;
var chart = this,
oldBox = chart.containerBox,
containerBox = chart.getContainerBox();
(_a = chart.pointer) === null || _a === void 0 ? true : delete _a.chartPosition;
// Width and height checks for display:none. Target is doc in Opera
// and win in Firefox, Chrome and IE9.
if (!chart.isPrinting &&
!chart.isResizing &&
oldBox &&
// When fired by resize observer inside hidden container
containerBox.width) {
if (containerBox.width !== oldBox.width ||
containerBox.height !== oldBox.height) {
Core_Utilities.clearTimeout(chart.reflowTimeout);
// When called from window.resize, e is set, else it's called
// directly (#2224)
chart.reflowTimeout = Chart_syncTimeout(function () {
// Set size, it may have been destroyed in the meantime
// (#1257)
if (chart.container) {
chart.setSize(void 0, void 0, false);
}
}, e ? 100 : 0);
}
chart.containerBox = containerBox;
}
};
/**
* Toggle the event handlers necessary for auto resizing, depending on the
* `chart.reflow` option.
*
* @private
* @function Highcharts.Chart#setReflow
*/
Chart.prototype.setReflow = function () {
var chart = this;
var runReflow = function (e) {
var _a;
if (((_a = chart.options) === null || _a === void 0 ? void 0 : _a.chart.reflow) && chart.hasLoaded) {
chart.reflow(e);
}
};
if (typeof ResizeObserver === 'function') {
(new ResizeObserver(runReflow)).observe(chart.renderTo);
// Fallback for more legacy browser versions.
}
else {
var unbind = Chart_addEvent(Chart_win, 'resize',
runReflow);
Chart_addEvent(this, 'destroy', unbind);
}
};
/**
* Resize the chart to a given width and height. In order to set the width
* only, the height argument may be skipped. To set the height only, pass
* `undefined` for the width.
*
* @sample highcharts/members/chart-setsize-button/
* Test resizing from buttons
* @sample highcharts/members/chart-setsize-jquery-resizable/
* Add a jQuery UI resizable
* @sample stock/members/chart-setsize/
* Highcharts Stock with UI resizable
*
* @function Highcharts.Chart#setSize
*
* @param {number|null} [width]
* The new pixel width of the chart. Since v4.2.6, the argument can
* be `undefined` in order to preserve the current value (when
* setting height only), or `null` to adapt to the width of the
* containing element.
*
* @param {number|null} [height]
* The new pixel height of the chart. Since v4.2.6, the argument can
* be `undefined` in order to preserve the current value, or `null`
* in order to adapt to the height of the containing element.
*
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
* Whether and how to apply animation. When `undefined`, it applies
* the animation that is set in the `chart.animation` option.
*
*
* @emits Highcharts.Chart#event:endResize
* @emits Highcharts.Chart#event:resize
*/
Chart.prototype.setSize = function (width, height, animation) {
var chart = this,
renderer = chart.renderer;
// Handle the isResizing counter
chart.isResizing += 1;
// Set the animation for the current process
Chart_setAnimation(animation, chart);
var globalAnimation = renderer.globalAnimation;
chart.oldChartHeight = chart.chartHeight;
chart.oldChartWidth = chart.chartWidth;
if (typeof width !== 'undefined') {
chart.options.chart.width = width;
}
if (typeof height !== 'undefined') {
chart.options.chart.height = height;
}
chart.getChartSize();
var chartWidth = chart.chartWidth,
chartHeight = chart.chartHeight,
_a = chart.scrollablePixelsX,
scrollablePixelsX = _a === void 0 ? 0 : _a,
_b = chart.scrollablePixelsY,
scrollablePixelsY = _b === void 0 ? 0 : _b;
// Avoid expensive redrawing if the computed size didn't change
if (chart.isDirtyBox ||
chartWidth !== chart.oldChartWidth ||
chartHeight !== chart.oldChartHeight) {
// Resize the container with the global animation applied if enabled
// (#2503)
if (!chart.styledMode) {
(globalAnimation ? Chart_animate : Chart_css)(chart.container, {
width: "" + (chartWidth + scrollablePixelsX) + "px",
height: "" + (chartHeight + scrollablePixelsY) + "px"
}, globalAnimation);
}
chart.setChartSize(true);
renderer.setSize(chartWidth, chartHeight, globalAnimation);
// Handle axes
chart.axes.forEach(function (axis) {
axis.isDirty = true;
axis.setScale();
});
chart.isDirtyLegend = true; // Force legend redraw
chart.isDirtyBox = true; // Force redraw of plot and chart border
chart.layOutTitles(); // #2857
chart.getMargins();
chart.redraw(globalAnimation);
chart.oldChartHeight = void 0;
Chart_fireEvent(chart, 'resize');
// Fire endResize and set isResizing back. If animation is disabled,
// fire without delay, but in a new thread to avoid triggering the
// resize observer (#19027).
setTimeout(function () {
if (chart) {
Chart_fireEvent(chart, 'endResize');
}
}, Chart_animObject(globalAnimation).duration);
}
// Handle resizing counter even if we've re-rendered or not (#20548).
chart.isResizing -= 1;
};
/**
* Set the public chart properties. This is done before and after the
* pre-render to determine margin sizes.
*
* @private
* @function Highcharts.Chart#setChartSize
* @emits Highcharts.Chart#event:afterSetChartSize
*/
Chart.prototype.setChartSize = function (skipAxes) {
var chart = this,
chartHeight = chart.chartHeight,
chartWidth = chart.chartWidth,
inverted = chart.inverted,
spacing = chart.spacing,
renderer = chart.renderer,
clipOffset = chart.clipOffset,
clipRoundFunc = Math[inverted ? 'floor' : 'round'];
var plotLeft,
plotTop,
plotWidth,
plotHeight;
/**
* The current left position of the plot area in pixels.
*
* @name Highcharts.Chart#plotLeft
* @type {number}
*/
chart.plotLeft = plotLeft = Math.round(chart.plotLeft);
/**
* The current top position of the plot area in pixels.
*
* @name Highcharts.Chart#plotTop
* @type {number}
*/
chart.plotTop = plotTop = Math.round(chart.plotTop);
/**
* The current width of the plot area in pixels.
*
* @name Highcharts.Chart#plotWidth
* @type {number}
*/
chart.plotWidth = plotWidth = Math.max(0, Math.round(chartWidth - plotLeft - chart.marginRight));
/**
* The current height of the plot area in pixels.
*
* @name Highcharts.Chart#plotHeight
* @type {number}
*/
chart.plotHeight = plotHeight = Math.max(0, Math.round(chartHeight - plotTop - chart.marginBottom));
chart.plotSizeX = inverted ? plotHeight : plotWidth;
chart.plotSizeY = inverted ? plotWidth : plotHeight;
// Set boxes used for alignment
chart.spacingBox = renderer.spacingBox = {
x: spacing[3],
y: spacing[0],
width: chartWidth - spacing[3] - spacing[1],
height: chartHeight - spacing[0] - spacing[2]
};
chart.plotBox = renderer.plotBox = {
x: plotLeft,
y: plotTop,
width: plotWidth,
height: plotHeight
};
// Compute the clipping box
if (clipOffset) {
chart.clipBox = {
x: clipRoundFunc(clipOffset[3]),
y: clipRoundFunc(clipOffset[0]),
width: clipRoundFunc(chart.plotSizeX - clipOffset[1] - clipOffset[3]),
height: clipRoundFunc(chart.plotSizeY - clipOffset[0] - clipOffset[2])
};
}
if (!skipAxes) {
chart.axes.forEach(function (axis) {
axis.setAxisSize();
axis.setAxisTranslation();
});
renderer.alignElements();
}
Chart_fireEvent(chart, 'afterSetChartSize', { skipAxes: skipAxes });
};
/**
* Initial margins before auto size margins are applied.
*
* @private
* @function Highcharts.Chart#resetMargins
*/
Chart.prototype.resetMargins = function () {
Chart_fireEvent(this, 'resetMargins');
var chart = this,
chartOptions = chart.options.chart,
plotBorderWidth = chartOptions.plotBorderWidth || 0,
halfWidth = plotBorderWidth / 2;
// Create margin and spacing array
['margin', 'spacing'].forEach(function splashArrays(target) {
var value = chartOptions[target],
values = Chart_isObject(value) ? value : [value,
value,
value,
value];
[
'Top',
'Right',
'Bottom',
'Left'
].forEach(function (sideName, side) {
chart[target][side] = Chart_pick(chartOptions[target + sideName], values[side]);
});
});
// Set margin names like chart.plotTop, chart.plotLeft,
// chart.marginRight, chart.marginBottom.
Chart_marginNames.forEach(function (m, side) {
chart[m] = Chart_pick(chart.margin[side], chart.spacing[side]);
});
chart.axisOffset = [0, 0, 0, 0]; // Top, right, bottom, left
chart.clipOffset = [
halfWidth,
halfWidth,
halfWidth,
halfWidth
];
chart.plotBorderWidth = plotBorderWidth;
};
/**
* Internal function to draw or redraw the borders and backgrounds for chart
* and plot area.
*
* @private
* @function Highcharts.Chart#drawChartBox
* @emits Highcharts.Chart#event:afterDrawChartBox
*/
Chart.prototype.drawChartBox = function () {
var chart = this,
optionsChart = chart.options.chart,
renderer = chart.renderer,
chartWidth = chart.chartWidth,
chartHeight = chart.chartHeight,
styledMode = chart.styledMode,
plotBGImage = chart.plotBGImage,
chartBackgroundColor = optionsChart.backgroundColor,
plotBackgroundColor = optionsChart.plotBackgroundColor,
plotBackgroundImage = optionsChart.plotBackgroundImage,
plotLeft = chart.plotLeft,
plotTop = chart.plotTop,
plotWidth = chart.plotWidth,
plotHeight = chart.plotHeight,
plotBox = chart.plotBox,
clipRect = chart.clipRect,
clipBox = chart.clipBox;
var chartBackground = chart.chartBackground,
plotBackground = chart.plotBackground,
plotBorder = chart.plotBorder,
chartBorderWidth,
mgn,
bgAttr,
verb = 'animate';
// Chart area
if (!chartBackground) {
chart.chartBackground = chartBackground = renderer.rect()
.addClass('highcharts-background')
.add();
verb = 'attr';
}
if (!styledMode) {
// Presentational
chartBorderWidth = optionsChart.borderWidth || 0;
mgn = chartBorderWidth + (optionsChart.shadow ? 8 : 0);
bgAttr = {
fill: chartBackgroundColor || 'none'
};
if (chartBorderWidth || chartBackground['stroke-width']) { // #980
bgAttr.stroke = optionsChart.borderColor;
bgAttr['stroke-width'] = chartBorderWidth;
}
chartBackground
.attr(bgAttr)
.shadow(optionsChart.shadow);
}
else {
chartBorderWidth = mgn = chartBackground.strokeWidth();
}
chartBackground[verb]({
x: mgn / 2,
y: mgn / 2,
width: chartWidth - mgn - chartBorderWidth % 2,
height: chartHeight - mgn - chartBorderWidth % 2,
r: optionsChart.borderRadius
});
// Plot background
verb = 'animate';
if (!plotBackground) {
verb = 'attr';
chart.plotBackground = plotBackground = renderer.rect()
.addClass('highcharts-plot-background')
.add();
}
plotBackground[verb](plotBox);
if (!styledMode) {
// Presentational attributes for the background
plotBackground
.attr({
fill: plotBackgroundColor || 'none'
})
.shadow(optionsChart.plotShadow);
// Create the background image
if (plotBackgroundImage) {
if (!plotBGImage) {
chart.plotBGImage = renderer.image(plotBackgroundImage, plotLeft, plotTop, plotWidth, plotHeight).add();
}
else {
if (plotBackgroundImage !== plotBGImage.attr('href')) {
plotBGImage.attr('href', plotBackgroundImage);
}
plotBGImage.animate(plotBox);
}
}
}
// Plot clip
if (!clipRect) {
chart.clipRect = renderer.clipRect(clipBox);
}
else {
clipRect.animate({
width: clipBox.width,
height: clipBox.height
});
}
// Plot area border
verb = 'animate';
if (!plotBorder) {
verb = 'attr';
chart.plotBorder = plotBorder = renderer.rect()
.addClass('highcharts-plot-border')
.attr({
zIndex: 1 // Above the grid
})
.add();
}
if (!styledMode) {
// Presentational
plotBorder.attr({
stroke: optionsChart.plotBorderColor,
'stroke-width': optionsChart.plotBorderWidth || 0,
fill: 'none'
});
}
plotBorder[verb](plotBorder.crisp({
x: plotLeft,
y: plotTop,
width: plotWidth,
height: plotHeight
}, -plotBorder.strokeWidth())); // #3282 plotBorder should be negative;
// reset
chart.isDirtyBox = false;
Chart_fireEvent(this, 'afterDrawChartBox');
};
/**
* Detect whether a certain chart property is needed based on inspecting its
* options and series. This mainly applies to the chart.inverted property,
* and in extensions to the chart.angular and chart.polar properties.
*
* @private
* @function Highcharts.Chart#propFromSeries
*/
Chart.prototype.propFromSeries = function () {
var chart = this,
optionsChart = chart.options.chart,
seriesOptions = chart.options.series;
var i,
klass,
value;
/**
* The flag is set to `true` if a series of the chart is inverted.
*
* @name Highcharts.Chart#inverted
* @type {boolean|undefined}
*/
['inverted', 'angular', 'polar'].forEach(function (key) {
// The default series type's class
klass = Chart_seriesTypes[optionsChart.type];
// Get the value from available chart-wide properties
value =
// It is set in the options:
optionsChart[key] ||
// The default series class:
(klass && klass.prototype[key]);
// Requires it
// 4. Check if any the chart's series require it
i = seriesOptions && seriesOptions.length;
while (!value && i--) {
klass = Chart_seriesTypes[seriesOptions[i].type];
if (klass && klass.prototype[key]) {
value = true;
}
}
// Set the chart property
chart[key] = value;
});
};
/**
* Internal function to link two or more series together, based on the
* `linkedTo` option. This is done from `Chart.render`, and after
* `Chart.addSeries` and `Series.remove`.
*
* @private
* @function Highcharts.Chart#linkSeries
* @emits Highcharts.Chart#event:afterLinkSeries
*/
Chart.prototype.linkSeries = function (isUpdating) {
var chart = this,
chartSeries = chart.series;
// Reset links
chartSeries.forEach(function (series) {
series.linkedSeries.length = 0;
});
// Apply new links
chartSeries.forEach(function (series) {
var linkedTo = series.options.linkedTo;
if (Chart_isString(linkedTo)) {
var linkedParent = void 0;
if (linkedTo === ':previous') {
linkedParent = chart.series[series.index - 1];
}
else {
linkedParent = chart.get(linkedTo);
}
// #3341 avoid mutual linking
if (linkedParent &&
linkedParent.linkedParent !== series) {
linkedParent.linkedSeries.push(series);
/**
* The parent series of the current series, if the current
* series has a [linkedTo](https://api.highcharts.com/highcharts/series.line.linkedTo)
* setting.
*
* @name Highcharts.Series#linkedParent
* @type {Highcharts.Series}
* @readonly
*/
series.linkedParent = linkedParent;
if (linkedParent.enabledDataSorting) {
series.setDataSortingOptions();
}
series.visible = Chart_pick(series.options.visible, linkedParent.options.visible, series.visible); // #3879
}
}
});
Chart_fireEvent(this, 'afterLinkSeries', { isUpdating: isUpdating });
};
/**
* Render series for the chart.
*
* @private
* @function Highcharts.Chart#renderSeries
*/
Chart.prototype.renderSeries = function () {
this.series.forEach(function (serie) {
serie.translate();
serie.render();
});
};
/**
* Render all graphics for the chart. Runs internally on initialization.
*
* @private
* @function Highcharts.Chart#render
*/
Chart.prototype.render = function () {
var _a;
var chart = this,
axes = chart.axes,
colorAxis = chart.colorAxis,
renderer = chart.renderer,
axisLayoutRuns = chart.options.chart.axisLayoutRuns || 2,
renderAxes = function (axes) {
axes.forEach(function (axis) {
if (axis.visible) {
axis.render();
}
});
};
var expectedSpace = 0, // Correction for X axis labels
// If the plot area size has changed significantly, calculate tick
// positions again
redoHorizontal = true,
redoVertical,
run = 0;
// Title
chart.setTitle();
// Fire an event before the margins are computed. This is where the
// legend is assigned.
Chart_fireEvent(chart, 'beforeMargins');
// Get stacks
(_a = chart.getStacks) === null || _a === void 0 ? void 0 : _a.call(chart);
// Get chart margins
chart.getMargins(true);
chart.setChartSize();
for (var _i = 0, axes_1 = axes; _i < axes_1.length; _i++) {
var axis = axes_1[_i];
var options = axis.options,
labels = options.labels;
if (chart.hasCartesianSeries && // #20948
axis.horiz &&
axis.visible &&
labels.enabled &&
axis.series.length &&
axis.coll !== 'colorAxis' &&
!chart.polar) {
expectedSpace = options.tickLength;
axis.createGroups();
// Calculate expected space based on dummy tick
var mockTick = new Axis_Tick(axis, 0, '', true), label = mockTick.createLabel('x', labels);
mockTick.destroy();
if (label &&
Chart_pick(labels.reserveSpace, !Chart_isNumber(options.crossing))) {
expectedSpace = label.getBBox().height +
labels.distance +
Math.max(options.offset || 0, 0);
}
if (expectedSpace) {
label === null || label === void 0 ? void 0 : label.destroy();
break;
}
}
}
// Use Math.max to prevent negative plotHeight
chart.plotHeight = Math.max(chart.plotHeight - expectedSpace, 0);
while ((redoHorizontal || redoVertical || axisLayoutRuns > 1) &&
run < axisLayoutRuns // #19794
) {
var tempWidth = chart.plotWidth,
tempHeight = chart.plotHeight;
for (var _b = 0, axes_2 = axes; _b < axes_2.length; _b++) {
var axis = axes_2[_b];
if (run === 0) {
// Get margins by pre-rendering axes
axis.setScale();
}
else if ((axis.horiz && redoHorizontal) ||
(!axis.horiz && redoVertical)) {
// Update to reflect the new margins
axis.setTickInterval(true);
}
}
if (run === 0) {
chart.getAxisMargins();
}
else {
// Check again for new, rotated or moved labels
chart.getMargins();
}
redoHorizontal = (tempWidth / chart.plotWidth) > (run ? 1 : 1.1);
redoVertical = (tempHeight / chart.plotHeight) > (run ? 1 : 1.05);
run++;
}
// Draw the borders and backgrounds
chart.drawChartBox();
// Axes
if (chart.hasCartesianSeries) {
renderAxes(axes);
}
else if (colorAxis && colorAxis.length) {
renderAxes(colorAxis);
}
// The series
if (!chart.seriesGroup) {
chart.seriesGroup = renderer.g('series-group')
.attr({ zIndex: 3 })
.shadow(chart.options.chart.seriesGroupShadow)
.add();
}
chart.renderSeries();
// Credits
chart.addCredits();
// Handle responsiveness
if (chart.setResponsive) {
chart.setResponsive();
}
// Set flag
chart.hasRendered = true;
};
/**
* Set a new credits label for the chart.
*
* @sample highcharts/credits/credits-update/
* Add and update credits
*
* @function Highcharts.Chart#addCredits
*
* @param {Highcharts.CreditsOptions} [credits]
* A configuration object for the new credits.
*/
Chart.prototype.addCredits = function (credits) {
var chart = this,
creds = Chart_merge(true,
this.options.credits,
credits);
if (creds.enabled && !this.credits) {
/**
* The chart's credits label. The label has an `update` method that
* allows setting new options as per the
* [credits options set](https://api.highcharts.com/highcharts/credits).
*
* @name Highcharts.Chart#credits
* @type {Highcharts.SVGElement}
*/
this.credits = this.renderer.text(creds.text + (this.mapCredits || ''), 0, 0)
.addClass('highcharts-credits')
.on('click', function () {
if (creds.href) {
Chart_win.location.href = creds.href;
}
})
.attr({
align: creds.position.align,
zIndex: 8
});
if (!chart.styledMode) {
this.credits.css(creds.style);
}
this.credits
.add()
.align(creds.position);
// Dynamically update
this.credits.update = function (options) {
chart.credits = chart.credits.destroy();
chart.addCredits(options);
};
}
};
/**
* Remove the chart and purge memory. This method is called internally
* before adding a second chart into the same container, as well as on
* window unload to prevent leaks.
*
* @sample highcharts/members/chart-destroy/
* Destroy the chart from a button
* @sample stock/members/chart-destroy/
* Destroy with Highcharts Stock
*
* @function Highcharts.Chart#destroy
*
* @emits Highcharts.Chart#event:destroy
*/
Chart.prototype.destroy = function () {
var chart = this,
axes = chart.axes,
series = chart.series,
container = chart.container,
parentNode = container && container.parentNode;
var i;
// Fire the chart.destroy event
Chart_fireEvent(chart, 'destroy');
// Delete the chart from charts lookup array
if (chart.renderer.forExport) {
Chart_erase(Chart_charts, chart); // #6569
}
else {
Chart_charts[chart.index] = void 0;
}
Core_Globals.chartCount--;
chart.renderTo.removeAttribute('data-highcharts-chart');
// Remove events
Chart_removeEvent(chart);
// ==== Destroy collections:
// Destroy axes
i = axes.length;
while (i--) {
axes[i] = axes[i].destroy();
}
// Destroy scroller & scroller series before destroying base series
if (this.scroller && this.scroller.destroy) {
this.scroller.destroy();
}
// Destroy each series
i = series.length;
while (i--) {
series[i] = series[i].destroy();
}
// ==== Destroy chart properties:
[
'title', 'subtitle', 'chartBackground', 'plotBackground',
'plotBGImage', 'plotBorder', 'seriesGroup', 'clipRect', 'credits',
'pointer', 'rangeSelector', 'legend', 'resetZoomButton', 'tooltip',
'renderer'
].forEach(function (name) {
var prop = chart[name];
if (prop && prop.destroy) {
chart[name] = prop.destroy();
}
});
// Remove container and all SVG, check container as it can break in IE
// when destroyed before finished loading
if (container) {
container.innerHTML = HTML_AST.emptyHTML;
Chart_removeEvent(container);
if (parentNode) {
Chart_discardElement(container);
}
}
// Clean it all up
Chart_objectEach(chart, function (val, key) {
delete chart[key];
});
};
/**
* Prepare for first rendering after all data are loaded.
*
* @private
* @function Highcharts.Chart#firstRender
* @emits Highcharts.Chart#event:beforeRender
*/
Chart.prototype.firstRender = function () {
var _a;
var chart = this,
options = chart.options;
// Create the container
chart.getContainer();
chart.resetMargins();
chart.setChartSize();
// Set the common chart properties (mainly invert) from the given series
chart.propFromSeries();
// Get axes
chart.createAxes();
// Initialize the series
var series = Chart_isArray(options.series) ? options.series : [];
options.series = []; // Avoid mutation
series.forEach(
// #9680
function (serieOptions) {
chart.initSeries(serieOptions);
});
chart.linkSeries();
chart.setSortedData();
// Run an event after axes and series are initialized, but before
// render. At this stage, the series data is indexed and cached in the
// xData and yData arrays, so we can access those before rendering. Used
// in Highcharts Stock.
Chart_fireEvent(chart, 'beforeRender');
chart.render();
(_a = chart.pointer) === null || _a === void 0 ? void 0 : _a.getChartPosition(); // #14973
// Fire the load event if there are no external images
if (!chart.renderer.imgCount && !chart.hasLoaded) {
chart.onload();
}
// If the chart was rendered outside the top container, put it back in
// (#3679)
chart.temporaryDisplay(true);
};
/**
* Internal function that runs on chart load, async if any images are loaded
* in the chart. Runs the callbacks and triggers the `load` and `render`
* events.
*
* @private
* @function Highcharts.Chart#onload
* @emits Highcharts.Chart#event:load
* @emits Highcharts.Chart#event:render
*/
Chart.prototype.onload = function () {
// Run callbacks, first the ones registered by modules, then user's one
this.callbacks.concat([this.callback]).forEach(function (fn) {
// Chart destroyed in its own callback (#3600)
if (fn && typeof this.index !== 'undefined') {
fn.apply(this, [this]);
}
}, this);
Chart_fireEvent(this, 'load');
Chart_fireEvent(this, 'render');
// Set up auto resize, check for not destroyed (#6068)
if (Chart_defined(this.index)) {
this.setReflow();
}
this.warnIfA11yModuleNotLoaded();
// Don't run again
this.hasLoaded = true;
};
/**
* Emit console warning if the a11y module is not loaded.
* @private
*/
Chart.prototype.warnIfA11yModuleNotLoaded = function () {
var _a = this,
options = _a.options,
title = _a.title;
if (options && !this.accessibility) {
// Make chart behave as an image with the title as alt text
this.renderer.boxWrapper.attr({
role: 'img',
'aria-label': ((title && title.element.textContent) || ''
// #17753, < is not allowed in SVG attributes
).replace(/</g, '<')
});
if (!(options.accessibility && options.accessibility.enabled === false)) {
Chart_error('Highcharts warning: Consider including the ' +
'"accessibility.js" module to make your chart more ' +
'usable for people with disabilities. Set the ' +
'"accessibility.enabled" option to false to remove this ' +
'warning. See https://www.highcharts.com/docs/accessibility/accessibility-module.', false, this);
}
}
};
/**
* Add a series to the chart after render time. Note that this method should
* never be used when adding data synchronously at chart render time, as it
* adds expense to the calculations and rendering. When adding data at the
* same time as the chart is initialized, add the series as a configuration
* option instead. With multiple axes, the `offset` is dynamically adjusted.
*
* @sample highcharts/members/chart-addseries/
* Add a series from a button
* @sample stock/members/chart-addseries/
* Add a series in Highcharts Stock
*
* @function Highcharts.Chart#addSeries
*
* @param {Highcharts.SeriesOptionsType} options
* The config options for the series.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart after adding.
*
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
* Whether to apply animation, and optionally animation
* configuration. When `undefined`, it applies the animation that is
* set in the `chart.animation` option.
*
* @return {Highcharts.Series}
* The newly created series object.
*
* @emits Highcharts.Chart#event:addSeries
* @emits Highcharts.Chart#event:afterAddSeries
*/
Chart.prototype.addSeries = function (options, redraw, animation) {
var chart = this;
var series;
if (options) { // <- not necessary
redraw = Chart_pick(redraw, true); // Defaults to true
Chart_fireEvent(chart, 'addSeries', { options: options }, function () {
series = chart.initSeries(options);
chart.isDirtyLegend = true;
chart.linkSeries();
if (series.enabledDataSorting) {
// We need to call `setData` after `linkSeries`
series.setData(options.data, false);
}
Chart_fireEvent(chart, 'afterAddSeries', { series: series });
if (redraw) {
chart.redraw(animation);
}
});
}
return series;
};
/**
* Add an axis to the chart after render time. Note that this method should
* never be used when adding data synchronously at chart render time, as it
* adds expense to the calculations and rendering. When adding data at the
* same time as the chart is initialized, add the axis as a configuration
* option instead.
*
* @sample highcharts/members/chart-addaxis/
* Add and remove axes
*
* @function Highcharts.Chart#addAxis
*
* @param {Highcharts.AxisOptions} options
* The axis options.
*
* @param {boolean} [isX=false]
* Whether it is an X axis or a value axis.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart after adding.
*
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
* Whether and how to apply animation in the redraw. When
* `undefined`, it applies the animation that is set in the
* `chart.animation` option.
*
* @return {Highcharts.Axis}
* The newly generated Axis object.
*/
Chart.prototype.addAxis = function (options, isX, redraw, animation) {
return this.createAxis(isX ? 'xAxis' : 'yAxis', { axis: options, redraw: redraw, animation: animation });
};
/**
* Add a color axis to the chart after render time. Note that this method
* should never be used when adding data synchronously at chart render time,
* as it adds expense to the calculations and rendering. When adding data at
* the same time as the chart is initialized, add the axis as a
* configuration option instead.
*
* @sample highcharts/members/chart-addaxis/
* Add and remove axes
*
* @function Highcharts.Chart#addColorAxis
*
* @param {Highcharts.ColorAxisOptions} options
* The axis options.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart after adding.
*
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
* Whether and how to apply animation in the redraw. When
* `undefined`, it applies the animation that is set in the
* `chart.animation` option.
*
* @return {Highcharts.Axis}
* The newly generated Axis object.
*/
Chart.prototype.addColorAxis = function (options, redraw, animation) {
return this.createAxis('colorAxis', { axis: options, redraw: redraw, animation: animation });
};
/**
* Factory for creating different axis types.
*
* @private
* @function Highcharts.Chart#createAxis
*
* @param {string} coll
* An axis type.
*
* @param {...Array<*>} arguments
* All arguments for the constructor.
*
* @return {Highcharts.Axis}
* The newly generated Axis object.
*/
Chart.prototype.createAxis = function (coll, options) {
var axis = new Axis_Axis(this,
options.axis,
coll);
if (Chart_pick(options.redraw, true)) {
this.redraw(options.animation);
}
return axis;
};
/**
* Dim the chart and show a loading text or symbol. Options for the loading
* screen are defined in {@link
* https://api.highcharts.com/highcharts/loading|the loading options}.
*
* @sample highcharts/members/chart-hideloading/
* Show and hide loading from a button
* @sample highcharts/members/chart-showloading/
* Apply different text labels
* @sample stock/members/chart-show-hide-loading/
* Toggle loading in Highcharts Stock
*
* @function Highcharts.Chart#showLoading
*
* @param {string} [str]
* An optional text to show in the loading label instead of the
* default one. The default text is set in
* [lang.loading](https://api.highcharts.com/highcharts/lang.loading).
*/
Chart.prototype.showLoading = function (str) {
var chart = this,
options = chart.options,
loadingOptions = options.loading,
setLoadingSize = function () {
if (loadingDiv) {
Chart_css(loadingDiv, {
left: chart.plotLeft + 'px',
top: chart.plotTop + 'px',
width: chart.plotWidth + 'px',
height: chart.plotHeight + 'px'
});
}
};
var loadingDiv = chart.loadingDiv,
loadingSpan = chart.loadingSpan;
// Create the layer at the first call
if (!loadingDiv) {
chart.loadingDiv = loadingDiv = Chart_createElement('div', {
className: 'highcharts-loading highcharts-loading-hidden'
}, null, chart.container);
}
if (!loadingSpan) {
chart.loadingSpan = loadingSpan = Chart_createElement('span', { className: 'highcharts-loading-inner' }, null, loadingDiv);
Chart_addEvent(chart, 'redraw', setLoadingSize); // #1080
}
loadingDiv.className = 'highcharts-loading';
// Update text
HTML_AST.setElementHTML(loadingSpan, Chart_pick(str, options.lang.loading, ''));
if (!chart.styledMode) {
// Update visuals
Chart_css(loadingDiv, Chart_extend(loadingOptions.style, {
zIndex: 10
}));
Chart_css(loadingSpan, loadingOptions.labelStyle);
// Show it
if (!chart.loadingShown) {
Chart_css(loadingDiv, {
opacity: 0,
display: ''
});
Chart_animate(loadingDiv, {
opacity: loadingOptions.style.opacity || 0.5
}, {
duration: loadingOptions.showDuration || 0
});
}
}
chart.loadingShown = true;
setLoadingSize();
};
/**
* Hide the loading layer.
*
* @see Highcharts.Chart#showLoading
*
* @sample highcharts/members/chart-hideloading/
* Show and hide loading from a button
* @sample stock/members/chart-show-hide-loading/
* Toggle loading in Highcharts Stock
*
* @function Highcharts.Chart#hideLoading
*/
Chart.prototype.hideLoading = function () {
var options = this.options,
loadingDiv = this.loadingDiv;
if (loadingDiv) {
loadingDiv.className =
'highcharts-loading highcharts-loading-hidden';
if (!this.styledMode) {
Chart_animate(loadingDiv, {
opacity: 0
}, {
duration: options.loading.hideDuration || 100,
complete: function () {
Chart_css(loadingDiv, { display: 'none' });
}
});
}
}
this.loadingShown = false;
};
/**
* A generic function to update any element of the chart. Elements can be
* enabled and disabled, moved, re-styled, re-formatted etc.
*
* A special case is configuration objects that take arrays, for example
* [xAxis](https://api.highcharts.com/highcharts/xAxis),
* [yAxis](https://api.highcharts.com/highcharts/yAxis) or
* [series](https://api.highcharts.com/highcharts/series). For these
* collections, an `id` option is used to map the new option set to an
* existing object. If an existing object of the same id is not found, the
* corresponding item is updated. So for example, running `chart.update`
* with a series item without an id, will cause the existing chart's series
* with the same index in the series array to be updated. When the
* `oneToOne` parameter is true, `chart.update` will also take care of
* adding and removing items from the collection. Read more under the
* parameter description below.
*
* Note that when changing series data, `chart.update` may mutate the passed
* data options.
*
* See also the
* [responsive option set](https://api.highcharts.com/highcharts/responsive).
* Switching between `responsive.rules` basically runs `chart.update` under
* the hood.
*
* @sample highcharts/members/chart-update/
* Update chart geometry
*
* @function Highcharts.Chart#update
*
* @param {Highcharts.Options} options
* A configuration object for the new chart options.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart.
*
* @param {boolean} [oneToOne=false]
* When `true`, the `series`, `xAxis`, `yAxis` and `annotations`
* collections will be updated one to one, and items will be either
* added or removed to match the new updated options. For example,
* if the chart has two series and we call `chart.update` with a
* configuration containing three series, one will be added. If we
* call `chart.update` with one series, one will be removed. Setting
* an empty `series` array will remove all series, but leaving out
* the`series` property will leave all series untouched. If the
* series have id's, the new series options will be matched by id,
* and the remaining ones removed.
*
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
* Whether to apply animation, and optionally animation
* configuration. When `undefined`, it applies the animation that is
* set in the `chart.animation` option.
*
* @emits Highcharts.Chart#event:update
* @emits Highcharts.Chart#event:afterUpdate
*/
Chart.prototype.update = function (options, redraw, oneToOne, animation) {
var chart = this,
adders = {
credits: 'addCredits',
title: 'setTitle',
subtitle: 'setSubtitle',
caption: 'setCaption'
},
isResponsiveOptions = options.isResponsiveOptions,
itemsForRemoval = [];
var updateAllAxes,
updateAllSeries,
runSetSize;
Chart_fireEvent(chart, 'update', { options: options });
// If there are responsive rules in action, undo the responsive rules
// before we apply the updated options and replay the responsive rules
// on top from the chart.redraw function (#9617).
if (!isResponsiveOptions) {
chart.setResponsive(false, true);
}
options = Chart_diffObjects(options, chart.options);
chart.userOptions = Chart_merge(chart.userOptions, options);
// If the top-level chart option is present, some special updates are
// required
var optionsChart = options.chart;
if (optionsChart) {
Chart_merge(true, chart.options.chart, optionsChart);
// Add support for deprecated zooming options like zoomType, #17861
this.setZoomOptions();
// Setter function
if ('className' in optionsChart) {
chart.setClassName(optionsChart.className);
}
if ('inverted' in optionsChart ||
'polar' in optionsChart ||
'type' in optionsChart) {
// Parse options.chart.inverted and options.chart.polar together
// with the available series.
chart.propFromSeries();
updateAllAxes = true;
}
if ('alignTicks' in optionsChart) { // #6452
updateAllAxes = true;
}
if ('events' in optionsChart) {
// Chart event handlers
Chart_registerEventOptions(this, optionsChart);
}
Chart_objectEach(optionsChart, function (val, key) {
if (chart.propsRequireUpdateSeries.indexOf('chart.' + key) !==
-1) {
updateAllSeries = true;
}
// Only dirty box
if (chart.propsRequireDirtyBox.indexOf(key) !== -1) {
chart.isDirtyBox = true;
}
// Chart setSize
if (chart.propsRequireReflow.indexOf(key) !== -1) {
chart.isDirtyBox = true;
if (!isResponsiveOptions) {
runSetSize = true;
}
}
});
if (!chart.styledMode && optionsChart.style) {
chart.renderer.setStyle(chart.options.chart.style || {});
}
}
// Moved up, because tooltip needs updated plotOptions (#6218)
if (!chart.styledMode && options.colors) {
this.options.colors = options.colors;
}
// Some option structures correspond one-to-one to chart objects that
// have update methods, for example
// options.credits => chart.credits
// options.legend => chart.legend
// options.title => chart.title
// options.tooltip => chart.tooltip
// options.subtitle => chart.subtitle
// options.mapNavigation => chart.mapNavigation
// options.navigator => chart.navigator
// options.scrollbar => chart.scrollbar
Chart_objectEach(options, function (val, key) {
if (chart[key] &&
typeof chart[key].update === 'function') {
chart[key].update(val, false);
// If a one-to-one object does not exist, look for an adder function
}
else if (typeof chart[adders[key]] === 'function') {
chart[adders[key]](val);
// Else, just merge the options. For nodes like loading, noData,
// plotOptions
}
else if (key !== 'colors' &&
chart.collectionsWithUpdate.indexOf(key) === -1) {
Chart_merge(true, chart.options[key], options[key]);
}
if (key !== 'chart' &&
chart.propsRequireUpdateSeries.indexOf(key) !== -1) {
updateAllSeries = true;
}
});
// Setters for collections. For axes and series, each item is referred
// by an id. If the id is not found, it defaults to the corresponding
// item in the collection, so setting one series without an id, will
// update the first series in the chart. Setting two series without
// an id will update the first and the second respectively (#6019)
// chart.update and responsive.
this.collectionsWithUpdate.forEach(function (coll) {
if (options[coll]) {
Chart_splat(options[coll]).forEach(function (newOptions, i) {
var hasId = Chart_defined(newOptions.id);
var item;
// Match by id
if (hasId) {
item = chart.get(newOptions.id);
}
// No match by id found, match by index instead
if (!item && chart[coll]) {
item = chart[coll][Chart_pick(newOptions.index, i)];
// Check if we grabbed an item with an existing but
// different id (#13541). Check that the item in this
// position is not internal (navigator).
if (item && ((hasId && Chart_defined(item.options.id)) ||
item.options.isInternal)) {
item = void 0;
}
}
if (item && item.coll === coll) {
item.update(newOptions, false);
if (oneToOne) {
item.touched = true;
}
}
// If oneToOne and no matching item is found, add one
if (!item && oneToOne && chart.collectionsWithInit[coll]) {
chart.collectionsWithInit[coll][0].apply(chart,
// [newOptions, ...extraArguments, redraw=false]
[
newOptions
].concat(
// Not all initializers require extra args
chart.collectionsWithInit[coll][1] || []).concat([
false
])).touched = true;
}
});
// Add items for removal
if (oneToOne) {
chart[coll].forEach(function (item) {
if (!item.touched && !item.options.isInternal) {
itemsForRemoval.push(item);
}
else {
delete item.touched;
}
});
}
}
});
itemsForRemoval.forEach(function (item) {
if (item.chart && item.remove) { // #9097, avoid removing twice
item.remove(false);
}
});
if (updateAllAxes) {
chart.axes.forEach(function (axis) {
axis.update({}, false);
});
}
// Certain options require the whole series structure to be thrown away
// and rebuilt
if (updateAllSeries) {
chart.getSeriesOrderByLinks().forEach(function (series) {
// Avoid removed navigator series
if (series.chart) {
series.update({}, false);
}
}, this);
}
// Update size. Redraw is forced.
var newWidth = optionsChart && optionsChart.width;
var newHeight = optionsChart && (Chart_isString(optionsChart.height) ?
Chart_relativeLength(optionsChart.height,
newWidth || chart.chartWidth) :
optionsChart.height);
if (
// In this case, run chart.setSize with newWidth and newHeight which
// are undefined, only for reflowing chart elements because margin
// or spacing has been set (#8190)
runSetSize ||
// In this case, the size is actually set
(Chart_isNumber(newWidth) && newWidth !== chart.chartWidth) ||
(Chart_isNumber(newHeight) && newHeight !== chart.chartHeight)) {
chart.setSize(newWidth, newHeight, animation);
}
else if (Chart_pick(redraw, true)) {
chart.redraw(animation);
}
Chart_fireEvent(chart, 'afterUpdate', {
options: options,
redraw: redraw,
animation: animation
});
};
/**
* Shortcut to set the subtitle options. This can also be done from {@link
* Chart#update} or {@link Chart#setTitle}.
*
* @function Highcharts.Chart#setSubtitle
*
* @param {Highcharts.SubtitleOptions} options
* New subtitle options. The subtitle text itself is set by the
* `options.text` property.
*/
Chart.prototype.setSubtitle = function (options, redraw) {
this.applyDescription('subtitle', options);
this.layOutTitles(redraw);
};
/**
* Set the caption options. This can also be done from {@link
* Chart#update}.
*
* @function Highcharts.Chart#setCaption
*
* @param {Highcharts.CaptionOptions} options
* New caption options. The caption text itself is set by the
* `options.text` property.
*/
Chart.prototype.setCaption = function (options, redraw) {
this.applyDescription('caption', options);
this.layOutTitles(redraw);
};
/**
* Display the zoom button, so users can reset zoom to the default view
* settings.
*
* @function Highcharts.Chart#showResetZoom
*
* @emits Highcharts.Chart#event:afterShowResetZoom
* @emits Highcharts.Chart#event:beforeShowResetZoom
*/
Chart.prototype.showResetZoom = function () {
var chart = this,
lang = Chart_defaultOptions.lang,
btnOptions = chart.zooming.resetButton,
theme = btnOptions.theme,
alignTo = (btnOptions.relativeTo === 'chart' ||
btnOptions.relativeTo === 'spacingBox' ?
null :
'plotBox');
/**
* @private
*/
function zoomOut() {
chart.zoomOut();
}
Chart_fireEvent(this, 'beforeShowResetZoom', null, function () {
chart.resetZoomButton = chart.renderer
.button(lang.resetZoom, null, null, zoomOut, theme)
.attr({
align: btnOptions.position.align,
title: lang.resetZoomTitle
})
.addClass('highcharts-reset-zoom')
.add()
.align(btnOptions.position, false, alignTo);
});
Chart_fireEvent(this, 'afterShowResetZoom');
};
/**
* Zoom the chart out after a user has zoomed in. See also
* [Axis.setExtremes](/class-reference/Highcharts.Axis#setExtremes).
*
* @function Highcharts.Chart#zoomOut
*
* @emits Highcharts.Chart#event:selection
*/
Chart.prototype.zoomOut = function () {
var _this = this;
Chart_fireEvent(this, 'selection', { resetSelection: true }, function () { return _this.transform({ reset: true, trigger: 'zoom' }); });
};
/**
* Pan the chart by dragging the mouse across the pane. This function is
* called on mouse move, and the distance to pan is computed from chartX
* compared to the first chartX position in the dragging operation.
*
* @private
* @function Highcharts.Chart#pan
* @param {Highcharts.PointerEventObject} event
* @param {string} panning
*/
Chart.prototype.pan = function (event, panning) {
var chart = this,
panningOptions = (typeof panning === 'object' ?
panning :
{
enabled: panning,
type: 'x'
}),
type = panningOptions.type,
axes = type && chart[{
x: 'xAxis',
xy: 'axes',
y: 'yAxis'
}[type]]
.filter(function (axis) {
return axis.options.panningEnabled && !axis.options.isInternal;
}), chartOptions = chart.options.chart;
if (chartOptions === null || chartOptions === void 0 ? void 0 : chartOptions.panning) {
chartOptions.panning = panningOptions;
}
Chart_fireEvent(this, 'pan', { originalEvent: event }, function () {
chart.transform({
axes: axes,
event: event,
to: {
x: event.chartX - (chart.mouseDownX || 0),
y: event.chartY - (chart.mouseDownY || 0)
},
trigger: 'pan'
});
Chart_css(chart.container, { cursor: 'move' });
});
};
/**
* Pan and scale the chart. Used internally by mouse-pan, touch-pan,
* touch-zoom, and mousewheel zoom.
*
* The main positioning logic is created around two imaginary boxes. What is
* currently within the `from` rectangle, should be transformed to fill up
* the `to` rectangle.
* - In a mouse zoom, the `from` rectangle is the selection, while the `to`
* rectangle is the full plot area.
* - In a touch zoom, the `from` rectangle is made up of the last two-finger
* touch, while the `to`` rectangle is the current touch.
* - In a mousewheel zoom, the `to` rectangle is a 10x10 px square,
* while the `to` rectangle reflects the scale around that.
*
* @private
* @function Highcharts.Chart#transform
*/
Chart.prototype.transform = function (params) {
var _a;
var _this = this;
var _b,
_c,
_d;
var _e = params.axes,
axes = _e === void 0 ? this.axes : _e,
event = params.event,
_f = params.from,
from = _f === void 0 ? {} : _f,
reset = params.reset,
selection = params.selection,
_g = params.to,
to = _g === void 0 ? {} : _g,
trigger = params.trigger,
_h = this,
inverted = _h.inverted,
time = _h.time;
var hasZoomed = false,
displayButton,
isAnyAxisPanning;
// Remove active points for shared tooltip
(_b = this.hoverPoints) === null || _b === void 0 ? void 0 : _b.forEach(function (point) { return point.setState(); });
for (var _i = 0, axes_3 = axes; _i < axes_3.length; _i++) {
var axis = axes_3[_i];
var horiz = axis.horiz, len = axis.len, _j = axis.minPointOffset, minPointOffset = _j === void 0 ? 0 : _j, options = axis.options, reversed = axis.reversed, wh = horiz ? 'width' : 'height', xy = horiz ? 'x' : 'y', toLength = Chart_pick(to[wh], axis.len), fromLength = Chart_pick(from[wh], axis.len),
// If fingers pinched very close on this axis, treat as pan
scale = Math.abs(toLength) < 10 ?
1 :
toLength / fromLength, fromCenter = (from[xy] || 0) + fromLength / 2 - axis.pos, toCenter = ((_c = to[xy]) !== null && _c !== void 0 ? _c : axis.pos) +
toLength / 2 - axis.pos, move = fromCenter - toCenter / scale, pointRangeDirection = (reversed && !inverted) ||
(!reversed && inverted) ?
-1 :
1, minPx = move;
// Zooming in multiple panes, zoom only in the pane that receives
// the input
if (!reset && (fromCenter < 0 || fromCenter > axis.len)) {
continue;
}
var newMin = axis.toValue(minPx,
true) +
// Don't apply offset for selection (#20784)
(selection || axis.isOrdinal ?
0 : minPointOffset * pointRangeDirection),
newMax = axis.toValue(minPx + len / scale,
true) -
(
// Don't apply offset for selection (#20784)
selection || axis.isOrdinal ?
0 :
((minPointOffset * pointRangeDirection) ||
// Polar zoom tests failed when this was not
// commented:
// (axis.isXAxis && axis.pointRangePadding) ||
0)),
allExtremes = axis.allExtremes;
if (newMin > newMax) {
_a = [newMax, newMin], newMin = _a[0], newMax = _a[1];
}
// General calculations of the full data extremes. It is calculated
// on the first call to transform, then reused for subsequent
// touch/pan calls. (#11315).
if (scale === 1 &&
!reset &&
axis.coll === 'yAxis' &&
!allExtremes) {
for (var _k = 0, _l = axis.series; _k < _l.length; _k++) {
var series = _l[_k];
var seriesExtremes = series.getExtremes(series.getProcessedData(true).modified
.getColumn('y') || [],
true);
allExtremes !== null && allExtremes !== void 0 ? allExtremes : (allExtremes = {
dataMin: Number.MAX_VALUE,
dataMax: -Number.MAX_VALUE
});
if (Chart_isNumber(seriesExtremes.dataMin) &&
Chart_isNumber(seriesExtremes.dataMax)) {
allExtremes.dataMin = Math.min(seriesExtremes.dataMin, allExtremes.dataMin);
allExtremes.dataMax = Math.max(seriesExtremes.dataMax, allExtremes.dataMax);
}
}
axis.allExtremes = allExtremes;
}
var _m = Chart_extend(axis.getExtremes(), allExtremes || {}), dataMin = _m.dataMin, dataMax = _m.dataMax, min = _m.min, max = _m.max, optionsMin = time.parse(options.min), optionsMax = time.parse(options.max),
// For boosted chart where data extremes are skipped
safeDataMin = dataMin !== null && dataMin !== void 0 ? dataMin : optionsMin, safeDataMax = dataMax !== null && dataMax !== void 0 ? dataMax : optionsMax, range = newMax - newMin, padRange = axis.categories ? 0 : Math.min(range, safeDataMax - safeDataMin), paddedMin = safeDataMin - padRange * (Chart_defined(optionsMin) ? 0 : options.minPadding), paddedMax = safeDataMax + padRange * (Chart_defined(optionsMax) ? 0 : options.maxPadding),
// We're allowed to zoom outside the data extremes if we're
// dealing with a bubble chart, if we're panning, or if we're
// pinching or mousewheeling in.
allowZoomOutside = axis.allowZoomOutside ||
scale === 1 ||
(trigger !== 'zoom' && scale > 1),
// Calculate the floor and the ceiling
floor = Math.min(optionsMin !== null && optionsMin !== void 0 ? optionsMin : paddedMin, paddedMin, allowZoomOutside ? min : paddedMin), ceiling = Math.max(optionsMax !== null && optionsMax !== void 0 ? optionsMax : paddedMax, paddedMax, allowZoomOutside ? max : paddedMax);
// It is not necessary to calculate extremes on ordinal axis,
// because they are already calculated, so we don't want to override
// them.
if (!axis.isOrdinal ||
axis.options.overscroll || // #21316
scale !== 1 ||
reset) {
// If the new range spills over, either to the min or max,
// adjust it.
if (newMin < floor) {
newMin = floor;
if (scale >= 1) {
newMax = newMin + range;
}
}
if (newMax > ceiling) {
newMax = ceiling;
if (scale >= 1) {
newMin = newMax - range;
}
}
// Set new extremes if they are actually new
if (reset || (axis.series.length &&
(newMin !== min || newMax !== max) &&
newMin >= floor &&
newMax <= ceiling)) {
if (selection) {
selection[axis.coll].push({
axis: axis,
min: newMin,
max: newMax
});
}
else {
// Temporarily flag the axis as `isPanning` in order to
// disallow certain axis padding options that would make
// panning/zooming hard. Reset and redraw after the
// operation has finished.
axis.isPanning = trigger !== 'zoom';
if (axis.isPanning) {
isAnyAxisPanning = true; // #21319
}
axis.setExtremes(reset ? void 0 : newMin, reset ? void 0 : newMax, false, false, { move: move, trigger: trigger, scale: scale });
if (!reset &&
(newMin > floor || newMax < ceiling) &&
trigger !== 'mousewheel') {
displayButton = true;
}
}
hasZoomed = true;
}
if (event) {
this[horiz ? 'mouseDownX' : 'mouseDownY'] =
event[horiz ? 'chartX' : 'chartY'];
}
}
}
if (hasZoomed) {
if (selection) {
Chart_fireEvent(this, 'selection', selection,
// Run transform again, this time without the selection data
// so that the transform is applied.
function () {
delete params.selection;
params.trigger = 'zoom';
_this.transform(params);
});
}
else {
// Show or hide the Reset zoom button, but not while panning
if (displayButton &&
!isAnyAxisPanning &&
!this.resetZoomButton) {
this.showResetZoom();
}
else if (!displayButton && this.resetZoomButton) {
this.resetZoomButton = this.resetZoomButton.destroy();
}
this.redraw(trigger === 'zoom' &&
((_d = this.options.chart.animation) !== null && _d !== void 0 ? _d : this.pointCount < 100));
}
}
return hasZoomed;
};
return Chart;
}());
Chart_extend(Chart.prototype, {
// Hook for adding callbacks in modules
callbacks: [],
/**
* These collections (arrays) implement `Chart.addSomething` method used in
* chart.update() to create new object in the collection. Equivalent for
* deleting is resolved by simple `Something.remove()`.
*
* Note: We need to define these references after initializers are bound to
* chart's prototype.
*
* @private
*/
collectionsWithInit: {
// CollectionName: [ initializingMethod, [extraArguments] ]
xAxis: [Chart.prototype.addAxis, [true]],
yAxis: [Chart.prototype.addAxis, [false]],
series: [Chart.prototype.addSeries]
},
/**
* These collections (arrays) implement update() methods with support for
* one-to-one option.
* @private
*/
collectionsWithUpdate: [
'xAxis',
'yAxis',
'series'
],
/**
* These properties cause isDirtyBox to be set to true when updating. Can be
* extended from plugins.
* @private
*/
propsRequireDirtyBox: [
'backgroundColor',
'borderColor',
'borderWidth',
'borderRadius',
'plotBackgroundColor',
'plotBackgroundImage',
'plotBorderColor',
'plotBorderWidth',
'plotShadow',
'shadow'
],
/**
* These properties require a full reflow of chart elements, best
* implemented through running `Chart.setSize` internally (#8190).
* @private
*/
propsRequireReflow: [
'margin',
'marginTop',
'marginRight',
'marginBottom',
'marginLeft',
'spacing',
'spacingTop',
'spacingRight',
'spacingBottom',
'spacingLeft'
],
/**
* These properties cause all series to be updated when updating. Can be
* extended from plugins.
* @private
*/
propsRequireUpdateSeries: [
'chart.inverted',
'chart.polar',
'chart.ignoreHiddenSeries',
'chart.type',
'colors',
'plotOptions',
'time',
'tooltip'
]
});
/* *
*
* Default Export
*
* */
/* harmony default export */ var Chart_Chart = (Chart);
/* *
*
* API Declarations
*
* */
/**
* Callback for chart constructors.
*
* @callback Highcharts.ChartCallbackFunction
*
* @param {Highcharts.Chart} chart
* Created chart.
*/
/**
* Format a number and return a string based on input settings.
*
* @callback Highcharts.NumberFormatterCallbackFunction
*
* @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.
*/
/**
* The chart title. The title has an `update` method that allows modifying the
* options directly or indirectly via `chart.update`.
*
* @interface Highcharts.TitleObject
* @extends Highcharts.SVGElement
*/ /**
* Modify options for the title.
*
* @function Highcharts.TitleObject#update
*
* @param {Highcharts.TitleOptions} titleOptions
* Options to modify.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart after the title is altered. If doing more
* operations on the chart, it is a good idea to set redraw to false and
* call {@link Chart#redraw} after.
*/
/**
* The chart subtitle. The subtitle has an `update` method that
* allows modifying the options directly or indirectly via
* `chart.update`.
*
* @interface Highcharts.SubtitleObject
* @extends Highcharts.SVGElement
*/ /**
* Modify options for the subtitle.
*
* @function Highcharts.SubtitleObject#update
*
* @param {Highcharts.SubtitleOptions} subtitleOptions
* Options to modify.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart after the subtitle is altered. If doing
* more operations on the chart, it is a good idea to set redraw to false
* and call {@link Chart#redraw} after.
*/
/**
* The chart caption. The caption has an `update` method that
* allows modifying the options directly or indirectly via
* `chart.update`.
*
* @interface Highcharts.CaptionObject
* @extends Highcharts.SVGElement
*/ /**
* Modify options for the caption.
*
* @function Highcharts.CaptionObject#update
*
* @param {Highcharts.CaptionOptions} captionOptions
* Options to modify.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart after the caption is altered. If doing
* more operations on the chart, it is a good idea to set redraw to false
* and call {@link Chart#redraw} after.
*/
/**
* @interface Highcharts.ChartIsInsideOptionsObject
*/ /**
* @name Highcharts.ChartIsInsideOptionsObject#axis
* @type {Highcharts.Axis|undefined}
*/ /**
* @name Highcharts.ChartIsInsideOptionsObject#ignoreX
* @type {boolean|undefined}
*/ /**
* @name Highcharts.ChartIsInsideOptionsObject#ignoreY
* @type {boolean|undefined}
*/ /**
* @name Highcharts.ChartIsInsideOptionsObject#inverted
* @type {boolean|undefined}
*/ /**
* @name Highcharts.ChartIsInsideOptionsObject#paneCoordinates
* @type {boolean|undefined}
*/ /**
* @name Highcharts.ChartIsInsideOptionsObject#series
* @type {Highcharts.Series|undefined}
*/ /**
* @name Highcharts.ChartIsInsideOptionsObject#visiblePlotOnly
* @type {boolean|undefined}
*/
''; // Keeps doclets above in JS file
;// ./code/es5/es-modules/Extensions/ScrollablePlotArea.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* Highcharts feature to make the Y axis stay fixed when scrolling the chart
* horizontally on mobile devices. Supports left and right side axes.
*/
var ScrollablePlotArea_stop = AnimationUtilities.stop;
var ScrollablePlotArea_composed = Core_Globals.composed;
var ScrollablePlotArea_addEvent = Core_Utilities.addEvent, ScrollablePlotArea_createElement = Core_Utilities.createElement, ScrollablePlotArea_css = Core_Utilities.css, ScrollablePlotArea_defined = Core_Utilities.defined, ScrollablePlotArea_erase = Core_Utilities.erase, ScrollablePlotArea_merge = Core_Utilities.merge, ScrollablePlotArea_pushUnique = Core_Utilities.pushUnique;
/* *
*
* Functions
*
* */
/** @private */
function onChartRender() {
var scrollablePlotArea = this.scrollablePlotArea;
if ((this.scrollablePixelsX || this.scrollablePixelsY) &&
!scrollablePlotArea) {
this.scrollablePlotArea = scrollablePlotArea = new ScrollablePlotArea(this);
}
scrollablePlotArea === null || scrollablePlotArea === void 0 ? void 0 : scrollablePlotArea.applyFixed();
}
/** @private */
function markDirty() {
if (this.chart.scrollablePlotArea) {
this.chart.scrollablePlotArea.isDirty = true;
}
}
var ScrollablePlotArea = /** @class */ (function () {
function ScrollablePlotArea(chart) {
var _a,
_b;
var chartOptions = chart.options.chart,
Renderer = Renderer_RendererRegistry.getRendererType(),
scrollableOptions = chartOptions.scrollablePlotArea || {},
moveFixedElements = this.moveFixedElements.bind(this),
styles = {
WebkitOverflowScrolling: 'touch',
overflowX: 'hidden',
overflowY: 'hidden'
};
if (chart.scrollablePixelsX) {
styles.overflowX = 'auto';
}
if (chart.scrollablePixelsY) {
styles.overflowY = 'auto';
}
this.chart = chart;
// Insert a container with relative position that scrolling and fixed
// container renders to (#10555)
var parentDiv = this.parentDiv = ScrollablePlotArea_createElement('div', {
className: 'highcharts-scrolling-parent'
}, {
position: 'relative'
},
chart.renderTo),
// Add the necessary divs to provide scrolling
scrollingContainer = this.scrollingContainer = ScrollablePlotArea_createElement('div', {
'className': 'highcharts-scrolling'
},
styles,
parentDiv),
innerContainer = this.innerContainer = ScrollablePlotArea_createElement('div', {
'className': 'highcharts-inner-container'
},
void 0,
scrollingContainer),
fixedDiv = this.fixedDiv = ScrollablePlotArea_createElement('div', {
className: 'highcharts-fixed'
}, {
position: 'absolute',
overflow: 'hidden',
pointerEvents: 'none',
zIndex: (((_a = chartOptions.style) === null || _a === void 0 ? void 0 : _a.zIndex) || 0) + 2,
top: 0
},
void 0,
true),
fixedRenderer = this.fixedRenderer = new Renderer(fixedDiv,
chart.chartWidth,
chart.chartHeight,
chartOptions.style);
// Mask
this.mask = fixedRenderer
.path()
.attr({
fill: chartOptions.backgroundColor || '#fff',
'fill-opacity': (_b = scrollableOptions.opacity) !== null && _b !== void 0 ? _b : 0.85,
zIndex: -1
})
.addClass('highcharts-scrollable-mask')
.add();
scrollingContainer.parentNode.insertBefore(fixedDiv, scrollingContainer);
ScrollablePlotArea_css(chart.renderTo, { overflow: 'visible' });
ScrollablePlotArea_addEvent(chart, 'afterShowResetZoom', moveFixedElements);
ScrollablePlotArea_addEvent(chart, 'afterApplyDrilldown', moveFixedElements);
ScrollablePlotArea_addEvent(chart, 'afterLayOutTitles', moveFixedElements);
// On scroll, reset the chart position because it applies to the
// scrolled container
var lastHoverPoint;
ScrollablePlotArea_addEvent(scrollingContainer, 'scroll', function () {
var pointer = chart.pointer,
hoverPoint = chart.hoverPoint;
if (pointer) {
delete pointer.chartPosition;
if (hoverPoint) {
lastHoverPoint = hoverPoint;
}
pointer.runPointActions(void 0, lastHoverPoint, true);
}
});
// Now move the container inside
innerContainer.appendChild(chart.container);
}
ScrollablePlotArea.compose = function (AxisClass, ChartClass, SeriesClass) {
var _this = this;
if (ScrollablePlotArea_pushUnique(ScrollablePlotArea_composed, this.compose)) {
ScrollablePlotArea_addEvent(AxisClass, 'afterInit', markDirty);
ScrollablePlotArea_addEvent(ChartClass, 'afterSetChartSize', function (e) {
return _this.afterSetSize(e.target, e);
});
ScrollablePlotArea_addEvent(ChartClass, 'render', onChartRender);
ScrollablePlotArea_addEvent(SeriesClass, 'show', markDirty);
}
};
ScrollablePlotArea.afterSetSize = function (chart, e) {
var _a = chart.options.chart.scrollablePlotArea || {},
minWidth = _a.minWidth,
minHeight = _a.minHeight,
clipBox = chart.clipBox,
plotBox = chart.plotBox,
inverted = chart.inverted,
renderer = chart.renderer;
var scrollablePixelsX,
scrollablePixelsY,
recalculateHoriz;
if (!renderer.forExport) {
// The amount of pixels to scroll, the difference between chart
// width and scrollable width
if (minWidth) {
chart.scrollablePixelsX = scrollablePixelsX = Math.max(0, minWidth - chart.chartWidth);
if (scrollablePixelsX) {
chart.scrollablePlotBox = ScrollablePlotArea_merge(chart.plotBox);
plotBox.width = chart.plotWidth += scrollablePixelsX;
clipBox[inverted ? 'height' : 'width'] += scrollablePixelsX;
recalculateHoriz = true;
}
// Currently we can only do either X or Y
}
else if (minHeight) {
chart.scrollablePixelsY = scrollablePixelsY = Math.max(0, minHeight - chart.chartHeight);
if (ScrollablePlotArea_defined(scrollablePixelsY)) {
chart.scrollablePlotBox = ScrollablePlotArea_merge(chart.plotBox);
plotBox.height = chart.plotHeight += scrollablePixelsY;
clipBox[inverted ? 'width' : 'height'] += scrollablePixelsY;
recalculateHoriz = false;
}
}
if (ScrollablePlotArea_defined(recalculateHoriz) && !e.skipAxes) {
for (var _i = 0, _b = chart.axes; _i < _b.length; _i++) {
var axis = _b[_i];
// Apply the corrected plot size to the axes of the other
// orientation than the scrolling direction
if (axis.horiz === recalculateHoriz ||
// Or parallel axes
(chart.hasParallelCoordinates && axis.coll === 'yAxis')) {
axis.setAxisSize();
axis.setAxisTranslation();
}
}
}
}
};
ScrollablePlotArea.prototype.applyFixed = function () {
var _a;
var _b = this,
chart = _b.chart,
fixedRenderer = _b.fixedRenderer,
isDirty = _b.isDirty,
scrollingContainer = _b.scrollingContainer,
axisOffset = chart.axisOffset,
chartWidth = chart.chartWidth,
chartHeight = chart.chartHeight,
container = chart.container,
plotHeight = chart.plotHeight,
plotLeft = chart.plotLeft,
plotTop = chart.plotTop,
plotWidth = chart.plotWidth,
_c = chart.scrollablePixelsX,
scrollablePixelsX = _c === void 0 ? 0 : _c,
_d = chart.scrollablePixelsY,
scrollablePixelsY = _d === void 0 ? 0 : _d,
chartOptions = chart.options.chart,
scrollableOptions = chartOptions.scrollablePlotArea || {},
_e = scrollableOptions.scrollPositionX,
scrollPositionX = _e === void 0 ? 0 : _e,
_f = scrollableOptions.scrollPositionY,
scrollPositionY = _f === void 0 ? 0 : _f,
scrollableWidth = chartWidth + scrollablePixelsX,
scrollableHeight = chartHeight + scrollablePixelsY;
// Set the size of the fixed renderer to the visible width
fixedRenderer.setSize(chartWidth, chartHeight);
if (isDirty !== null && isDirty !== void 0 ? isDirty : true) {
this.isDirty = false;
this.moveFixedElements();
}
// Increase the size of the scrollable renderer and background
ScrollablePlotArea_stop(chart.container);
ScrollablePlotArea_css(container, {
width: "" + scrollableWidth + "px",
height: "" + scrollableHeight + "px"
});
chart.renderer.boxWrapper.attr({
width: scrollableWidth,
height: scrollableHeight,
viewBox: [0, 0, scrollableWidth, scrollableHeight].join(' ')
});
(_a = chart.chartBackground) === null || _a === void 0 ? void 0 : _a.attr({
width: scrollableWidth,
height: scrollableHeight
});
ScrollablePlotArea_css(scrollingContainer, {
width: "" + chartWidth + "px",
height: "" + chartHeight + "px"
});
// Set scroll position the first time (this.isDirty was undefined at
// the top of this function)
if (!ScrollablePlotArea_defined(isDirty)) {
scrollingContainer.scrollLeft = scrollablePixelsX * scrollPositionX;
scrollingContainer.scrollTop = scrollablePixelsY * scrollPositionY;
}
// Mask behind the left and right side
var maskTop = plotTop - axisOffset[0] - 1,
maskLeft = plotLeft - axisOffset[3] - 1,
maskBottom = plotTop + plotHeight + axisOffset[2] + 1,
maskRight = plotLeft + plotWidth + axisOffset[1] + 1,
maskPlotRight = plotLeft + plotWidth - scrollablePixelsX,
maskPlotBottom = plotTop + plotHeight - scrollablePixelsY;
var d = [['M', 0, 0]];
if (scrollablePixelsX) {
d = [
// Left side
['M', 0, maskTop],
['L', plotLeft - 1, maskTop],
['L', plotLeft - 1, maskBottom],
['L', 0, maskBottom],
['Z'],
// Right side
['M', maskPlotRight, maskTop],
['L', chartWidth, maskTop],
['L', chartWidth, maskBottom],
['L', maskPlotRight, maskBottom],
['Z']
];
}
else if (scrollablePixelsY) {
d = [
// Top side
['M', maskLeft, 0],
['L', maskLeft, plotTop - 1],
['L', maskRight, plotTop - 1],
['L', maskRight, 0],
['Z'],
// Bottom side
['M', maskLeft, maskPlotBottom],
['L', maskLeft, chartHeight],
['L', maskRight, chartHeight],
['L', maskRight, maskPlotBottom],
['Z']
];
}
if (chart.redrawTrigger !== 'adjustHeight') {
this.mask.attr({ d: d });
}
};
/**
* These elements are moved over to the fixed renderer and stay fixed when
* the user scrolls the chart
* @private
*/
ScrollablePlotArea.prototype.moveFixedElements = function () {
var _a = this.chart,
container = _a.container,
inverted = _a.inverted,
scrollablePixelsX = _a.scrollablePixelsX,
scrollablePixelsY = _a.scrollablePixelsY,
fixedRenderer = this.fixedRenderer,
fixedSelectors = ScrollablePlotArea.fixedSelectors;
var axisClass;
if (scrollablePixelsX && !inverted) {
axisClass = '.highcharts-yaxis';
}
else if (scrollablePixelsX && inverted) {
axisClass = '.highcharts-xaxis';
}
else if (scrollablePixelsY && !inverted) {
axisClass = '.highcharts-xaxis';
}
else if (scrollablePixelsY && inverted) {
axisClass = '.highcharts-yaxis';
}
if (axisClass && !(this.chart.hasParallelCoordinates &&
axisClass === '.highcharts-yaxis')) {
// Add if not added yet
for (var _i = 0, _b = [
"" + axisClass + ":not(.highcharts-radial-axis)",
"" + axisClass + "-labels:not(.highcharts-radial-axis-labels)"
]; _i < _b.length; _i++) {
var className = _b[_i];
ScrollablePlotArea_pushUnique(fixedSelectors, className);
}
}
else {
// Clear all axis related selectors
for (var _c = 0, _d = [
'.highcharts-xaxis',
'.highcharts-yaxis'
]; _c < _d.length; _c++) {
var classBase = _d[_c];
for (var _e = 0, _f = [
"" + classBase + ":not(.highcharts-radial-axis)",
"" + classBase + "-labels:not(.highcharts-radial-axis-labels)"
]; _e < _f.length; _e++) {
var className = _f[_e];
ScrollablePlotArea_erase(fixedSelectors, className);
}
}
}
for (var _g = 0, fixedSelectors_1 = fixedSelectors; _g < fixedSelectors_1.length; _g++) {
var className = fixedSelectors_1[_g];
[].forEach.call(container.querySelectorAll(className), function (elem) {
(elem.namespaceURI === fixedRenderer.SVG_NS ?
fixedRenderer.box :
fixedRenderer.box.parentNode).appendChild(elem);
elem.style.pointerEvents = 'auto';
});
}
};
ScrollablePlotArea.fixedSelectors = [
'.highcharts-breadcrumbs-group',
'.highcharts-contextbutton',
'.highcharts-caption',
'.highcharts-credits',
'.highcharts-drillup-button',
'.highcharts-legend',
'.highcharts-legend-checkbox',
'.highcharts-navigator-series',
'.highcharts-navigator-xaxis',
'.highcharts-navigator-yaxis',
'.highcharts-navigator',
'.highcharts-range-selector-group',
'.highcharts-reset-zoom',
'.highcharts-scrollbar',
'.highcharts-subtitle',
'.highcharts-title'
];
return ScrollablePlotArea;
}());
/* *
*
* Default Export
*
* */
/* harmony default export */ var Extensions_ScrollablePlotArea = (ScrollablePlotArea);
/* *
*
* API Declarations
*
* */
/**
* Options for a scrollable plot area. This feature provides a minimum size for
* the plot area of the chart. If the size gets smaller than this, typically
* on mobile devices, a native browser scrollbar is presented. This scrollbar
* provides smooth scrolling for the contents of the plot area, whereas the
* title, legend and unaffected axes are fixed.
*
* Since v7.1.2, a scrollable plot area can be defined for either horizontal or
* vertical scrolling, depending on whether the `minWidth` or `minHeight`
* option is set.
*
* @sample highcharts/chart/scrollable-plotarea
* Scrollable plot area
* @sample highcharts/chart/scrollable-plotarea-vertical
* Vertically scrollable plot area
* @sample {gantt} gantt/chart/scrollable-plotarea-vertical
* Gantt chart with vertically scrollable plot area
*
* @since 6.1.0
* @product highcharts gantt
* @apioption chart.scrollablePlotArea
*/
/**
* The minimum height for the plot area. If it gets smaller than this, the plot
* area will become scrollable.
*
* @type {number}
* @since 7.1.2
* @apioption chart.scrollablePlotArea.minHeight
*/
/**
* The minimum width for the plot area. If it gets smaller than this, the plot
* area will become scrollable.
*
* @type {number}
* @since 6.1.0
* @apioption chart.scrollablePlotArea.minWidth
*/
/**
* The initial scrolling position of the scrollable plot area. Ranges from 0 to
* 1, where 0 aligns the plot area to the left and 1 aligns it to the right.
* Typically we would use 1 if the chart has right aligned Y axes.
*
* @type {number}
* @since 6.1.0
* @apioption chart.scrollablePlotArea.scrollPositionX
*/
/**
* The initial scrolling position of the scrollable plot area. Ranges from 0 to
* 1, where 0 aligns the plot area to the top and 1 aligns it to the bottom.
*
* @type {number}
* @since 7.1.2
* @apioption chart.scrollablePlotArea.scrollPositionY
*/
/**
* The opacity of mask applied on one of the sides of the plot
* area.
*
* @sample {highcharts} highcharts/chart/scrollable-plotarea-opacity
* Disabled opacity for the mask
*
* @type {number}
* @default 0.85
* @since 7.1.1
* @apioption chart.scrollablePlotArea.opacity
*/
(''); // Keep doclets above in transpiled file
;// ./code/es5/es-modules/Core/Axis/Stacking/StackItem.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var StackItem_format = Core_Templating.format;
var StackItem_Series = Series_SeriesRegistry.series;
var StackItem_destroyObjectProperties = Core_Utilities.destroyObjectProperties, StackItem_fireEvent = Core_Utilities.fireEvent, StackItem_getAlignFactor = Core_Utilities.getAlignFactor, StackItem_isNumber = Core_Utilities.isNumber, StackItem_pick = Core_Utilities.pick;
/* *
*
* Class
*
* */
/**
* The class for stacks. Each stack, on a specific X value and either negative
* or positive, has its own stack item.
* @private
*/
var StackItem = /** @class */ (function () {
/* *
*
* Constructor
*
* */
function StackItem(axis, options, negativeValue, x, stackOption) {
var inverted = axis.chart.inverted,
reversed = axis.reversed;
this.axis = axis;
// The stack goes to the left either if the stack has negative value
// or when axis is reversed. XOR operator.
var isNegative = (this.isNegative = !!negativeValue !== !!reversed);
// Save the options to be able to style the label
this.options = options = options || {};
// Save the x value to be able to position the label later
this.x = x;
// Initialize total value
this.total = null;
this.cumulative = null;
// This will keep each points' extremes stored by series.index and point
// index
this.points = {};
this.hasValidPoints = false;
// Save the stack option on the series configuration object,
// and whether to treat it as percent
this.stack = stackOption;
this.leftCliff = 0;
this.rightCliff = 0;
// The align options and text align varies on whether the stack is
// negative and if the chart is inverted or not.
// First test the user supplied value, then use the dynamic.
this.alignOptions = {
align: options.align ||
(inverted ? (isNegative ? 'left' : 'right') : 'center'),
verticalAlign: options.verticalAlign ||
(inverted ? 'middle' : isNegative ? 'bottom' : 'top'),
y: options.y,
x: options.x
};
this.textAlign =
options.textAlign ||
(inverted ? (!isNegative ? 'left' : 'right') : 'center');
}
/* *
*
* Functions
*
* */
/**
* @private
*/
StackItem.prototype.destroy = function () {
StackItem_destroyObjectProperties(this, this.axis);
};
/**
* Renders the stack total label and adds it to the stack label group.
* @private
*/
StackItem.prototype.render = function (group) {
var chart = this.axis.chart,
options = this.options,
formatOption = options.format,
// Format the text in the label.
str = formatOption ?
StackItem_format(formatOption,
this,
chart) :
options.formatter.call(this);
// Change the text to reflect the new total and set visibility to hidden
// in case the series is hidden
if (this.label) {
this.label.attr({ text: str, visibility: 'hidden' });
}
else {
// Create new label
this.label = chart.renderer.label(str, null, void 0, options.shape, void 0, void 0, options.useHTML, false, 'stack-labels');
var attr = {
r: options.borderRadius || 0,
text: str,
// Set default padding to 5 as it is in datalabels #12308
padding: StackItem_pick(options.padding, 5),
visibility: 'hidden' // Hidden until setOffset is called
};
if (!chart.styledMode) {
attr.fill = options.backgroundColor;
attr.stroke = options.borderColor;
attr['stroke-width'] = options.borderWidth;
this.label.css(options.style || {});
}
this.label.attr(attr);
if (!this.label.added) {
this.label.add(group); // Add to the labels-group
}
}
// Rank it higher than data labels (#8742)
this.label.labelrank = chart.plotSizeY;
StackItem_fireEvent(this, 'afterRender');
};
/**
* Sets the offset that the stack has from the x value and repositions the
* label.
* @private
*/
StackItem.prototype.setOffset = function (xOffset, width, boxBottom, boxTop, defaultX, xAxis) {
var _a = this,
alignOptions = _a.alignOptions,
axis = _a.axis,
label = _a.label,
options = _a.options,
textAlign = _a.textAlign,
chart = axis.chart,
stackBox = this.getStackBox({
xOffset: xOffset,
width: width,
boxBottom: boxBottom,
boxTop: boxTop,
defaultX: defaultX,
xAxis: xAxis
}),
verticalAlign = alignOptions.verticalAlign;
if (label && stackBox) {
var labelBox = label.getBBox(void 0, 0),
padding = label.padding;
var isJustify = StackItem_pick(options.overflow, 'justify') === 'justify',
visible = void 0;
// Reset alignOptions property after justify #12337
alignOptions.x = options.x || 0;
alignOptions.y = options.y || 0;
// Calculate the adjusted Stack position, to take into consideration
// The size if the labelBox and vertical alignment as
// well as the text alignment. It's need to be done to work with
// default SVGLabel.align/justify methods.
var _b = this.adjustStackPosition({
labelBox: labelBox,
verticalAlign: verticalAlign,
textAlign: textAlign
}),
x = _b.x,
y = _b.y;
stackBox.x -= x;
stackBox.y -= y;
// Align the label to the adjusted box.
label.align(alignOptions, false, stackBox);
// Check if label is inside the plotArea #12294
visible = chart.isInsidePlot(label.alignAttr.x + alignOptions.x + x, label.alignAttr.y + alignOptions.y + y);
if (!visible) {
isJustify = false;
}
if (isJustify) {
// Justify stackLabel into the alignBox
StackItem_Series.prototype.justifyDataLabel.call(axis, label, alignOptions, label.alignAttr, labelBox, stackBox);
}
// Add attr to avoid the default animation of justifyDataLabel.
// Also add correct rotation with its rotation origin. #15129
label.attr({
x: label.alignAttr.x,
y: label.alignAttr.y,
rotation: options.rotation,
rotationOriginX: labelBox.width *
StackItem_getAlignFactor(options.textAlign || 'center'),
rotationOriginY: labelBox.height / 2
});
// Check if the dataLabel should be visible.
if (StackItem_pick(!isJustify && options.crop, true)) {
visible =
StackItem_isNumber(label.x) &&
StackItem_isNumber(label.y) &&
chart.isInsidePlot(label.x - padding + (label.width || 0), label.y) &&
chart.isInsidePlot(label.x + padding, label.y);
}
label[visible ? 'show' : 'hide']();
}
StackItem_fireEvent(this, 'afterSetOffset', { xOffset: xOffset, width: width });
};
/**
* Adjust the stack BBox position, to take into consideration the alignment
* of the dataLabel. This is necessary to make the stackDataLabel work with
* core methods like `SVGLabel.adjust` and `Series.justifyDataLabel`.
* @param AdjustStackPositionProps
* @return {{x: number, y: number}} Adjusted BBox position of the stack.
*/
StackItem.prototype.adjustStackPosition = function (_a) {
var labelBox = _a.labelBox,
verticalAlign = _a.verticalAlign,
textAlign = _a.textAlign;
return {
x: labelBox.width / 2 +
(labelBox.width / 2) * (2 * StackItem_getAlignFactor(textAlign) - 1),
y: (labelBox.height / 2) * 2 * (1 - StackItem_getAlignFactor(verticalAlign))
};
};
/**
* Get the bbox of the stack.
* @private
* @function Highcharts.StackItem#getStackBox
* @return {BBoxObject} The x, y, height, width of the stack.
*/
StackItem.prototype.getStackBox = function (stackBoxProps) {
var stackItem = this,
axis = this.axis,
chart = axis.chart,
boxTop = stackBoxProps.boxTop,
defaultX = stackBoxProps.defaultX,
xOffset = stackBoxProps.xOffset,
width = stackBoxProps.width,
boxBottom = stackBoxProps.boxBottom,
totalStackValue = axis.stacking.usePercentage ?
100 :
StackItem_pick(boxTop,
this.total, 0),
y = axis.toPixels(totalStackValue),
xAxis = stackBoxProps.xAxis || chart.xAxis[0],
x = StackItem_pick(defaultX,
xAxis.translate(this.x)) + xOffset,
yZero = axis.toPixels(boxBottom ||
(StackItem_isNumber(axis.min) &&
axis.logarithmic &&
axis.logarithmic.lin2log(axis.min)) ||
0),
height = Math.abs(y - yZero),
inverted = chart.inverted,
neg = stackItem.isNegative;
return inverted ?
{
x: (neg ? y : y - height) - chart.plotLeft,
y: xAxis.height - x - width + xAxis.top - chart.plotTop,
width: height,
height: width
} : {
x: x + xAxis.transB - chart.plotLeft,
y: (neg ? y - height : y) - chart.plotTop,
width: width,
height: height
};
};
return StackItem;
}());
/* *
*
* Default Export
*
* */
/* harmony default export */ var Stacking_StackItem = (StackItem);
/* *
*
* API Declarations
*
* */
/**
* Stack of data points
*
* @product highcharts
*
* @interface Highcharts.StackItemObject
*/ /**
* Alignment settings
* @name Highcharts.StackItemObject#alignOptions
* @type {Highcharts.AlignObject}
*/ /**
* Related axis
* @name Highcharts.StackItemObject#axis
* @type {Highcharts.Axis}
*/ /**
* Cumulative value of the stacked data points
* @name Highcharts.StackItemObject#cumulative
* @type {number}
*/ /**
* True if on the negative side
* @name Highcharts.StackItemObject#isNegative
* @type {boolean}
*/ /**
* Related SVG element
* @name Highcharts.StackItemObject#label
* @type {Highcharts.SVGElement}
*/ /**
* Related stack options
* @name Highcharts.StackItemObject#options
* @type {Highcharts.YAxisStackLabelsOptions}
*/ /**
* Total value of the stacked data points
* @name Highcharts.StackItemObject#total
* @type {number}
*/ /**
* Shared x value of the stack
* @name Highcharts.StackItemObject#x
* @type {number}
*/
''; // Keeps doclets above in JS file
;// ./code/es5/es-modules/Core/Axis/Stacking/StackingAxis.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var StackingAxis_getDeferredAnimation = AnimationUtilities.getDeferredAnimation;
var seriesProto = Series_SeriesRegistry.series.prototype;
var StackingAxis_addEvent = Core_Utilities.addEvent, StackingAxis_correctFloat = Core_Utilities.correctFloat, StackingAxis_defined = Core_Utilities.defined, StackingAxis_destroyObjectProperties = Core_Utilities.destroyObjectProperties, StackingAxis_fireEvent = Core_Utilities.fireEvent, StackingAxis_isNumber = Core_Utilities.isNumber, StackingAxis_objectEach = Core_Utilities.objectEach, StackingAxis_pick = Core_Utilities.pick;
/* *
*
* Functions
*
* */
/**
* Generate stacks for each series and calculate stacks total values
*
* @private
* @function Highcharts.Chart#getStacks
*/
function chartGetStacks() {
var chart = this,
inverted = chart.inverted;
// Reset stacks for each axis
chart.axes.forEach(function (axis) {
if (axis.stacking && axis.stacking.stacks && axis.hasVisibleSeries) {
axis.stacking.oldStacks = axis.stacking.stacks;
}
});
chart.series.forEach(function (series) {
var xAxisOptions = series.xAxis && series.xAxis.options || {};
if (series.options.stacking && series.reserveSpace()) {
series.stackKey = [
series.type,
StackingAxis_pick(series.options.stack, ''),
inverted ? xAxisOptions.top : xAxisOptions.left,
inverted ? xAxisOptions.height : xAxisOptions.width
].join(',');
}
});
}
/**
* @private
*/
function onAxisDestroy() {
var _a;
var stacking = this.stacking;
if (stacking) {
var stacks_1 = stacking.stacks;
// Destroy each stack total
StackingAxis_objectEach(stacks_1, function (stack, stackKey) {
StackingAxis_destroyObjectProperties(stack);
delete stacks_1[stackKey];
});
(_a = stacking.stackTotalGroup) === null || _a === void 0 ? void 0 : _a.destroy();
}
}
/**
* @private
*/
function onAxisInit() {
if (!this.stacking) {
this.stacking = new AxisAdditions(this);
}
}
/**
* Get stack indicator, according to it's x-value, to determine points with the
* same x-value
*
* @private
* @function Highcharts.Series#getStackIndicator
*/
function seriesGetStackIndicator(stackIndicator, x, index, key) {
// Update stack indicator, when:
// first point in a stack || x changed || stack type (negative vs positive)
// changed:
if (!StackingAxis_defined(stackIndicator) ||
stackIndicator.x !== x ||
(key && stackIndicator.stackKey !== key)) {
stackIndicator = {
x: x,
index: 0,
key: key,
stackKey: key
};
}
else {
stackIndicator.index++;
}
stackIndicator.key = [index, x, stackIndicator.index].join(',');
return stackIndicator;
}
/**
* Iterate over all stacks and compute the absolute values to percent
*
* @private
* @function Highcharts.Series#modifyStacks
*/
function seriesModifyStacks() {
var series = this, yAxis = series.yAxis, stackKey = series.stackKey || '', stacks = yAxis.stacking.stacks, processedXData = series.getColumn('x', true), stacking = series.options.stacking, stacker = series[stacking + 'Stacker'];
var stackIndicator;
if (stacker) { // Modifier function exists (Series.percentStacker etc.)
[stackKey, '-' + stackKey].forEach(function (key) {
var _a;
var i = processedXData.length,
x,
stackItem,
pointExtremes;
while (i--) {
x = processedXData[i];
stackIndicator = series.getStackIndicator(stackIndicator, x, series.index, key);
stackItem = (_a = stacks[key]) === null || _a === void 0 ? void 0 : _a[x];
pointExtremes = stackItem === null || stackItem === void 0 ? void 0 : stackItem.points[stackIndicator.key || ''];
if (pointExtremes) {
stacker.call(series, pointExtremes, stackItem, i);
}
}
});
}
}
/**
* Modifier function for percent stacks. Blows up the stack to 100%.
*
* @private
* @function Highcharts.Series#percentStacker
*/
function seriesPercentStacker(pointExtremes, stack, i) {
var totalFactor = stack.total ? 100 / stack.total : 0;
// Y bottom value
pointExtremes[0] = StackingAxis_correctFloat(pointExtremes[0] * totalFactor);
// Y value
pointExtremes[1] = StackingAxis_correctFloat(pointExtremes[1] * totalFactor);
this.stackedYData[i] = pointExtremes[1];
}
/**
* Set grouped points in a stack-like object. When `centerInCategory` is true,
* and `stacking` is not enabled, we need a pseudo (horizontal) stack in order
* to handle grouping of points within the same category.
*
* @private
* @function Highcharts.Series#setGroupedPoints
* @return {void}
*/
function seriesSetGroupedPoints(axis) {
// Only series types supporting centerInCategory need to do this. That also
// applies to resetting (#20221).
if (this.is('column') || this.is('columnrange')) {
if (this.options.centerInCategory &&
// With only one series, we don't need to consider centerInCategory
this.chart.series.length > 1) {
seriesProto.setStackedPoints.call(this, axis, 'group');
// After updating, if we now have proper stacks, we must delete the
// group pseudo stacks (#14980)
}
else {
axis.stacking.resetStacks();
}
}
}
/**
* Adds series' points value to corresponding stack
*
* @private
* @function Highcharts.Series#setStackedPoints
*/
function seriesSetStackedPoints(axis, stackingParam) {
var _a,
_b;
var type = stackingParam || this.options.stacking;
if (!type ||
!this.reserveSpace() ||
// Group stacks (centerInCategory) belong on the x-axis, other stacks on
// the y-axis.
({ group: 'xAxis' }[type] || 'yAxis') !== axis.coll) {
return;
}
var series = this, xData = series.getColumn('x', true), yData = series.getColumn(series.pointValKey || 'y', true), stackedYData = [], yDataLength = yData.length, seriesOptions = series.options, threshold = seriesOptions.threshold || 0, stackThreshold = seriesOptions.startFromThreshold ? threshold : 0, stackOption = seriesOptions.stack, stackKey = stackingParam ?
"" + series.type + ",".concat(type) : (series.stackKey || ''), negKey = '-' + stackKey, negStacks = series.negStacks, stacking = axis.stacking, stacks = stacking.stacks, oldStacks = stacking.oldStacks;
var stackIndicator,
isNegative,
stack,
other,
key,
pointKey,
i;
stacking.stacksTouched += 1;
// Loop over the non-null y values and read them into a local array
for (i = 0; i < yDataLength; i++) {
var x = xData[i] || 0,
y = yData[i],
yNumber = StackingAxis_isNumber(y) && y || 0;
stackIndicator = series.getStackIndicator(stackIndicator, x, series.index);
pointKey = stackIndicator.key || '';
// Read stacked values into a stack based on the x value,
// the sign of y and the stack key. Stacking is also handled for null
// values (#739)
isNegative = negStacks && yNumber < (stackThreshold ? 0 : threshold);
key = isNegative ? negKey : stackKey;
// Create empty object for this stack if it doesn't exist yet
if (!stacks[key]) {
stacks[key] = {};
}
// Initialize StackItem for this x
if (!stacks[key][x]) {
if ((_a = oldStacks[key]) === null || _a === void 0 ? void 0 : _a[x]) {
stacks[key][x] = oldStacks[key][x];
stacks[key][x].total = null;
}
else {
stacks[key][x] = new Stacking_StackItem(axis, axis.options.stackLabels, !!isNegative, x, stackOption);
}
}
// If the StackItem doesn't exist, create it first
stack = stacks[key][x];
if (y !== null) {
stack.points[pointKey] = stack.points[series.index] = [
StackingAxis_pick(stack.cumulative, stackThreshold)
];
// Record the base of the stack
if (!StackingAxis_defined(stack.cumulative)) {
stack.base = pointKey;
}
stack.touched = stacking.stacksTouched;
// In area charts, if there are multiple points on the same X value,
// let the area fill the full span of those points
if (stackIndicator.index > 0 && series.singleStacks === false) {
stack.points[pointKey][0] = stack.points[series.index + ',' + x + ',0'][0];
}
// When updating to null, reset the point stack (#7493)
}
else {
delete stack.points[pointKey];
delete stack.points[series.index];
}
// Add value to the stack total
var total = stack.total || 0;
if (type === 'percent') {
// Percent stacked column, totals are the same for the positive and
// negative stacks
other = isNegative ? stackKey : negKey;
if (negStacks && ((_b = stacks[other]) === null || _b === void 0 ? void 0 : _b[x])) {
other = stacks[other][x];
total = other.total = Math.max(other.total || 0, total) +
Math.abs(yNumber);
// Percent stacked areas
}
else {
total = StackingAxis_correctFloat(total + Math.abs(yNumber));
}
}
else if (type === 'group') {
// In this stack, the total is the number of valid points
if (StackingAxis_isNumber(y)) {
total++;
}
}
else {
total = StackingAxis_correctFloat(total + yNumber);
}
if (type === 'group') {
// This point's index within the stack, pushed to stack.points[1]
stack.cumulative = (total || 1) - 1;
}
else {
stack.cumulative = StackingAxis_correctFloat(StackingAxis_pick(stack.cumulative, stackThreshold) + yNumber);
}
stack.total = total;
if (y !== null) {
stack.points[pointKey].push(stack.cumulative);
stackedYData[i] = stack.cumulative;
stack.hasValidPoints = true;
}
}
if (type === 'percent') {
stacking.usePercentage = true;
}
if (type !== 'group') {
this.stackedYData = stackedYData; // To be used in getExtremes
}
// Reset old stacks
stacking.oldStacks = {};
}
/* *
*
* Classes
*
* */
/**
* Adds stacking support to axes.
* @private
* @class
*/
var AxisAdditions = /** @class */ (function () {
/* *
*
* Constructors
*
* */
function AxisAdditions(axis) {
this.oldStacks = {};
this.stacks = {};
this.stacksTouched = 0;
this.axis = axis;
}
/* *
*
* Functions
*
* */
/**
* Build the stacks from top down
* @private
*/
AxisAdditions.prototype.buildStacks = function () {
var stacking = this,
axis = stacking.axis,
axisSeries = axis.series,
isXAxis = axis.coll === 'xAxis',
reversedStacks = axis.options.reversedStacks,
len = axisSeries.length;
var actualSeries,
i;
this.resetStacks();
stacking.usePercentage = false;
i = len;
while (i--) {
actualSeries = axisSeries[reversedStacks ? i : len - i - 1];
if (isXAxis) {
actualSeries.setGroupedPoints(axis);
}
actualSeries.setStackedPoints(axis);
}
// Loop up again to compute percent and stream stack
if (!isXAxis) {
for (i = 0; i < len; i++) {
axisSeries[i].modifyStacks();
}
}
StackingAxis_fireEvent(axis, 'afterBuildStacks');
};
/**
* @private
*/
AxisAdditions.prototype.cleanStacks = function () {
if (this.oldStacks) {
this.stacks = this.oldStacks;
// Reset stacks
StackingAxis_objectEach(this.stacks, function (type) {
StackingAxis_objectEach(type, function (stack) {
stack.cumulative = stack.total;
});
});
}
};
/**
* Set all the stacks to initial states and destroy unused ones.
* @private
*/
AxisAdditions.prototype.resetStacks = function () {
var _this = this;
StackingAxis_objectEach(this.stacks, function (type) {
StackingAxis_objectEach(type, function (stack, x) {
// Clean up memory after point deletion (#1044, #4320)
if (StackingAxis_isNumber(stack.touched) &&
stack.touched < _this.stacksTouched) {
stack.destroy();
delete type[x];
// Reset stacks
}
else {
stack.total = null;
stack.cumulative = null;
}
});
});
};
/**
* @private
*/
AxisAdditions.prototype.renderStackTotals = function () {
var _a;
var stacking = this,
axis = stacking.axis,
chart = axis.chart,
renderer = chart.renderer,
stacks = stacking.stacks,
stackLabelsAnim = (_a = axis.options.stackLabels) === null || _a === void 0 ? void 0 : _a.animation,
animationConfig = StackingAxis_getDeferredAnimation(chart,
stackLabelsAnim || false),
stackTotalGroup = stacking.stackTotalGroup = (stacking.stackTotalGroup ||
renderer
.g('stack-labels')
.attr({
zIndex: 6,
opacity: 0
})
.add());
// The plotLeft/Top will change when y axis gets wider so we need to
// translate the stackTotalGroup at every render call. See bug #506
// and #516
stackTotalGroup.translate(chart.plotLeft, chart.plotTop);
// Render each stack total
StackingAxis_objectEach(stacks, function (type) {
StackingAxis_objectEach(type, function (stack) {
stack.render(stackTotalGroup);
});
});
stackTotalGroup.animate({
opacity: 1
}, animationConfig);
};
return AxisAdditions;
}());
/* *
*
* Composition
*
* */
var StackingAxis;
(function (StackingAxis) {
/* *
*
* Functions
*
* */
/**
* Extends axis with stacking support.
* @private
*/
function compose(AxisClass, ChartClass, SeriesClass) {
var chartProto = ChartClass.prototype,
seriesProto = SeriesClass.prototype;
if (!chartProto.getStacks) {
StackingAxis_addEvent(AxisClass, 'init', onAxisInit);
StackingAxis_addEvent(AxisClass, 'destroy', onAxisDestroy);
chartProto.getStacks = chartGetStacks;
seriesProto.getStackIndicator = seriesGetStackIndicator;
seriesProto.modifyStacks = seriesModifyStacks;
seriesProto.percentStacker = seriesPercentStacker;
seriesProto.setGroupedPoints = seriesSetGroupedPoints;
seriesProto.setStackedPoints = seriesSetStackedPoints;
}
}
StackingAxis.compose = compose;
})(StackingAxis || (StackingAxis = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Stacking_StackingAxis = (StackingAxis);
;// ./code/es5/es-modules/Series/Line/LineSeries.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var LineSeries_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b,
p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var LineSeries_spreadArray = (undefined && undefined.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var LineSeries_defined = Core_Utilities.defined, LineSeries_merge = Core_Utilities.merge, LineSeries_isObject = Core_Utilities.isObject;
/* *
*
* Class
*
* */
/**
* The line series is the base type and is therefor the series base prototype.
*
* @private
*/
var LineSeries = /** @class */ (function (_super) {
LineSeries_extends(LineSeries, _super);
function LineSeries() {
return _super !== null && _super.apply(this, arguments) || this;
}
/* *
*
* Functions
*
* */
/**
* Draw the graph. Called internally when rendering line-like series
* types. The first time it generates the `series.graph` item and
* optionally other series-wide items like `series.area` for area
* charts. On subsequent calls these items are updated with new
* positions and attributes.
*
* @function Highcharts.Series#drawGraph
*/
LineSeries.prototype.drawGraph = function () {
var _this = this;
var options = this.options,
graphPath = (this.gappedPath || this.getGraphPath).call(this),
styledMode = this.chart.styledMode;
// Draw the graph
LineSeries_spreadArray([this], this.zones, true).forEach(function (owner, i) {
var attribs,
graph = owner.graph;
var verb = graph ? 'animate' : 'attr',
dashStyle = owner.dashStyle ||
options.dashStyle;
if (graph) {
graph.endX = _this.preventGraphAnimation ?
null :
graphPath.xMap;
graph.animate({ d: graphPath });
}
else if (graphPath.length) { // #1487
/**
* SVG element of line-based charts. Can be used for styling
* purposes. If zones are configured, this element will be
* hidden and replaced by multiple zone lines, accessible
* via `series.zones[i].graph`.
*
* @name Highcharts.Series#graph
* @type {Highcharts.SVGElement|undefined}
*/
owner.graph = graph = _this.chart.renderer
.path(graphPath)
.addClass('highcharts-graph' +
(i ? " highcharts-zone-graph-".concat(i - 1, " ") : ' ') +
((i && owner.className) || ''))
.attr({ zIndex: 1 }) // #1069
.add(_this.group);
}
if (graph && !styledMode) {
attribs = {
'stroke': ((!i && options.lineColor) || // Series only
owner.color ||
_this.color ||
"#cccccc" /* Palette.neutralColor20 */),
'stroke-width': options.lineWidth || 0,
// Polygon series use filled graph
'fill': (_this.fillGraph && _this.color) || 'none'
};
// Apply dash style
if (dashStyle) {
attribs.dashstyle = dashStyle;
// The reason for the `else if` is that linecaps don't mix well
// with dashstyle. The gaps get partially filled by the
// linecap.
}
else if (options.linecap !== 'square') {
attribs['stroke-linecap'] =
attribs['stroke-linejoin'] = 'round';
}
graph[verb](attribs)
// Add shadow to normal series as well as zones
.shadow(options.shadow &&
// If shadow is defined, call function with
// `filterUnits: 'userSpaceOnUse'` to avoid known
// SVG filter bug (#19093)
LineSeries_merge({ filterUnits: 'userSpaceOnUse' }, LineSeries_isObject(options.shadow) ? options.shadow : {}));
}
// Helpers for animation
if (graph) {
graph.startX = graphPath.xMap;
graph.isArea = graphPath.isArea; // For arearange animation
}
});
};
// eslint-disable-next-line valid-jsdoc
/**
* Get the graph path.
*
* @private
*/
LineSeries.prototype.getGraphPath = function (points, nullsAsZeroes, connectCliffs) {
var series = this,
options = series.options,
graphPath = [],
xMap = [];
var gap,
step = options.step;
points = points || series.points;
// Bottom of a stack is reversed
var reversed = points.reversed;
if (reversed) {
points.reverse();
}
// Reverse the steps (#5004)
step = {
right: 1,
center: 2
}[step] || (step && 3);
if (step && reversed) {
step = 4 - step;
}
// Remove invalid points, especially in spline (#5015)
points = this.getValidPoints(points, false, !(options.connectNulls && !nullsAsZeroes && !connectCliffs));
// Build the line
points.forEach(function (point, i) {
var plotX = point.plotX,
plotY = point.plotY,
lastPoint = points[i - 1],
isNull = point.isNull || typeof plotY !== 'number';
// The path to this point from the previous
var pathToPoint;
if ((point.leftCliff || (lastPoint && lastPoint.rightCliff)) &&
!connectCliffs) {
gap = true; // ... and continue
}
// Line series, nullsAsZeroes is not handled
if (isNull && !LineSeries_defined(nullsAsZeroes) && i > 0) {
gap = !options.connectNulls;
// Area series, nullsAsZeroes is set
}
else if (isNull && !nullsAsZeroes) {
gap = true;
}
else {
if (i === 0 || gap) {
pathToPoint = [[
'M',
point.plotX,
point.plotY
]];
// Generate the spline as defined in the SplineSeries object
}
else if (series.getPointSpline) {
pathToPoint = [series.getPointSpline(points, point, i)];
}
else if (step) {
if (step === 1) { // Right
pathToPoint = [[
'L',
lastPoint.plotX,
plotY
]];
}
else if (step === 2) { // Center
pathToPoint = [[
'L',
(lastPoint.plotX + plotX) / 2,
lastPoint.plotY
], [
'L',
(lastPoint.plotX + plotX) / 2,
plotY
]];
}
else {
pathToPoint = [[
'L',
plotX,
lastPoint.plotY
]];
}
pathToPoint.push([
'L',
plotX,
plotY
]);
}
else {
// Normal line to next point
pathToPoint = [[
'L',
plotX,
plotY
]];
}
// Prepare for animation. When step is enabled, there are
// two path nodes for each x value.
xMap.push(point.x);
if (step) {
xMap.push(point.x);
if (step === 2) { // Step = center (#8073)
xMap.push(point.x);
}
}
graphPath.push.apply(graphPath, pathToPoint);
gap = false;
}
});
graphPath.xMap = xMap;
series.graphPath = graphPath;
return graphPath;
};
/* *
*
* Static Functions
*
* */
LineSeries.defaultOptions = LineSeries_merge(Series_Series.defaultOptions,
/**
* General options for all series types.
*
* @optionparent plotOptions.series
*/
{
legendSymbol: 'lineMarker'
});
return LineSeries;
}(Series_Series));
Series_SeriesRegistry.registerSeriesType('line', LineSeries);
/* *
*
* Default Export
*
* */
/* harmony default export */ var Line_LineSeries = ((/* unused pure expression or super */ null && (LineSeries)));
/* *
*
* API Options
*
* */
/**
* A line series displays information as a series of data points connected by
* straight line segments.
*
* @sample {highcharts} highcharts/demo/line-chart/
* Line chart
* @sample {highstock} stock/demo/basic-line/
* Line chart
*
* @extends plotOptions.series
* @product highcharts highstock
* @apioption plotOptions.line
*/
/**
* The SVG value used for the `stroke-linecap` and `stroke-linejoin`
* of a line graph. Round means that lines are rounded in the ends and
* bends.
*
* @type {Highcharts.SeriesLinecapValue}
* @default round
* @since 3.0.7
* @apioption plotOptions.line.linecap
*/
/**
* A `line` series. If the [type](#series.line.type) option is not
* specified, it is inherited from [chart.type](#chart.type).
*
* @extends series,plotOptions.line
* @excluding dataParser,dataURL
* @product highcharts highstock
* @apioption series.line
*/
/**
* An array of data points for the series. For the `line` series type,
* points can be given in the following ways:
*
* 1. An array of numerical values. In this case, the numerical values will be
* interpreted as `y` options. The `x` values will be automatically
* calculated, either starting at 0 and incremented by 1, or from
* `pointStart` and `pointInterval` given in the series options. If the axis
* has categories, these will be used. Example:
* ```js
* data: [0, 5, 3, 5]
* ```
*
* 2. An array of arrays with 2 values. In this case, the values correspond to
* `x,y`. If the first value is a string, it is applied as the name of the
* point, and the `x` value is inferred.
* ```js
* data: [
* [0, 1],
* [1, 2],
* [2, 8]
* ]
* ```
*
* 3. An array of objects with named values. The following snippet shows only a
* few settings, see the complete options set below. If the total number of
* data points exceeds the series'
* [turboThreshold](#series.line.turboThreshold),
* this option is not available.
* ```js
* data: [{
* x: 1,
* y: 9,
* name: "Point2",
* color: "#00FF00"
* }, {
* x: 1,
* y: 6,
* name: "Point1",
* color: "#FF00FF"
* }]
* ```
*
* **Note:** In TypeScript you have to extend `PointOptionsObject` with an
* additional declaration to allow custom data types:
* ```ts
* declare module `highcharts` {
* interface PointOptionsObject {
* custom: Record<string, (boolean|number|string)>;
* }
* }
* ```
*
* @sample {highcharts} highcharts/chart/reflow-true/
* Numerical values
* @sample {highcharts} highcharts/series/data-array-of-arrays/
* Arrays of numeric x and y
* @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
* Arrays of datetime x and y
* @sample {highcharts} highcharts/series/data-array-of-name-value/
* Arrays of point.name and y
* @sample {highcharts} highcharts/series/data-array-of-objects/
* Config objects
*
* @declare Highcharts.PointOptionsObject
* @type {Array<number|Array<(number|string),(number|null)>|null|*>}
* @apioption series.line.data
*/
/**
* An additional, individual class name for the data point's graphic
* representation. Changes to a point's color will also be reflected in a
* chart's legend and tooltip.
*
* @sample {highcharts} highcharts/css/point-series-classname
* Series and point class name
*
* @type {string}
* @since 5.0.0
* @product highcharts gantt
* @apioption series.line.data.className
*/
/**
* Individual color for the point. By default the color is pulled from
* the global `colors` array.
*
* In styled mode, the `color` option doesn't take effect. Instead, use
* `colorIndex`.
*
* @sample {highcharts} highcharts/point/color/
* Mark the highest point
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @product highcharts highstock gantt
* @apioption series.line.data.color
*/
/**
* A specific color index to use for the point, so its graphic representations
* are given the class name `highcharts-color-{n}`. In styled mode this will
* change the color of the graphic. In non-styled mode, the color is set by the
* `fill` attribute, so the change in class name won't have a visual effect by
* default.
*
* Since v11, CSS variables on the form `--highcharts-color-{n}` make changing
* the color scheme very convenient.
*
* @sample {highcharts} highcharts/css/colorindex/
* Series and point color index
*
* @type {number}
* @since 5.0.0
* @product highcharts gantt
* @apioption series.line.data.colorIndex
*/
/**
* A reserved subspace to store options and values for customized functionality.
* Here you can add additional data for your own event callbacks and formatter
* callbacks.
*
* @sample {highcharts} highcharts/point/custom/
* Point and series with custom data
*
* @type {Highcharts.Dictionary<*>}
* @apioption series.line.data.custom
*/
/**
* Individual data label for each point. The options are the same as
* the ones for [plotOptions.series.dataLabels](
* #plotOptions.series.dataLabels).
*
* @sample highcharts/point/datalabels/
* Show a label for the last value
*
* @type {*|Array<*>}
* @declare Highcharts.DataLabelsOptions
* @extends plotOptions.line.dataLabels
* @product highcharts highstock gantt
* @apioption series.line.data.dataLabels
*/
/**
* A description of the point to add to the screen reader information
* about the point.
*
* @type {string}
* @since 5.0.0
* @requires modules/accessibility
* @apioption series.line.data.description
*/
/**
* An id for the point. This can be used after render time to get a
* pointer to the point object through `chart.get()`.
*
* @sample {highcharts} highcharts/point/id/
* Remove an id'd point
*
* @type {string}
* @since 1.2.0
* @product highcharts highstock gantt
* @apioption series.line.data.id
*/
/**
* The rank for this point's data label in case of collision. If two
* data labels are about to overlap, only the one with the highest `labelrank`
* will be drawn.
*
* @type {number}
* @apioption series.line.data.labelrank
*/
/**
* The name of the point as shown in the legend, tooltip, dataLabels, etc.
*
* @see [xAxis.uniqueNames](#xAxis.uniqueNames)
*
* @sample {highcharts} highcharts/series/data-array-of-objects/
* Point names
*
* @type {string}
* @apioption series.line.data.name
*/
/**
* Whether the data point is selected initially.
*
* @type {boolean}
* @default false
* @product highcharts highstock gantt
* @apioption series.line.data.selected
*/
/**
* The x value of the point.
*
* For datetime axes, a number value is the timestamp in milliseconds since
* 1970, while a date string is parsed according to the [current time zone]
* (https://api.highcharts.com/highcharts/time.timezone) of the
* chart. Date strings are supported since v12.
*
* @type {number|string}
* @product highcharts highstock
* @apioption series.line.data.x
*/
/**
* The y value of the point.
*
* @type {number|null}
* @product highcharts highstock
* @apioption series.line.data.y
*/
/**
* The individual point events.
*
* @extends plotOptions.series.point.events
* @product highcharts highstock gantt
* @apioption series.line.data.events
*/
/**
* Options for the point markers of line-like series.
*
* @declare Highcharts.PointMarkerOptionsObject
* @extends plotOptions.series.marker
* @product highcharts highstock
* @apioption series.line.data.marker
*/
''; // Include precedent doclets in transpiled
;// ./code/es5/es-modules/Series/Area/AreaSeriesDefaults.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* API Options
*
* */
/**
* The area series type.
*
* @sample {highcharts} highcharts/demo/area-chart/
* Area chart
* @sample {highstock} stock/demo/area/
* Area chart
*
* @extends plotOptions.line
* @excluding useOhlcData
* @product highcharts highstock
* @optionparent plotOptions.area
*/
var AreaSeriesDefaults = {
/**
* @see [fillColor](#plotOptions.area.fillColor)
* @see [fillOpacity](#plotOptions.area.fillOpacity)
*
* @apioption plotOptions.area.color
*/
/**
* Fill color or gradient for the area. When `undefined`, the series'
* `color` is used with the series' `fillOpacity`.
*
* In styled mode, the fill color can be set with the `.highcharts-area`
* class name.
*
* @see [color](#plotOptions.area.color)
* @see [fillOpacity](#plotOptions.area.fillOpacity)
*
* @sample {highcharts} highcharts/plotoptions/area-fillcolor-default/
* Undefined by default
* @sample {highcharts} highcharts/plotoptions/area-fillcolor-gradient/
* Gradient
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @product highcharts highstock
* @apioption plotOptions.area.fillColor
*/
/**
* Fill opacity for the area. When you set an explicit `fillColor`,
* the `fillOpacity` is not applied. Instead, you should define the
* opacity in the `fillColor` with an rgba color definition. The
* `fillOpacity` setting, also the default setting, overrides the alpha
* component of the `color` setting.
*
* In styled mode, the fill opacity can be set with the
* `.highcharts-area` class name.
*
* @see [color](#plotOptions.area.color)
* @see [fillColor](#plotOptions.area.fillColor)
*
* @sample {highcharts} highcharts/plotoptions/area-fillopacity/
* Automatic fill color and fill opacity of 0.1
*
* @type {number}
* @default {highcharts} 0.75
* @default {highstock} 0.75
* @product highcharts highstock
* @apioption plotOptions.area.fillOpacity
*/
/**
* A separate color for the graph line. By default the line takes the
* `color` of the series, but the lineColor setting allows setting a
* separate color for the line without altering the `fillColor`.
*
* In styled mode, the line stroke can be set with the
* `.highcharts-graph` class name.
*
* @sample {highcharts} highcharts/plotoptions/area-linecolor/
* Dark gray line
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @product highcharts highstock
* @apioption plotOptions.area.lineColor
*/
/**
* A separate color for the negative part of the area. Note that `zones`
* takes precedence over the negative fill color.
*
* In styled mode, a negative color is set with the
* `.highcharts-negative` class name.
*
* @see [negativeColor](#plotOptions.area.negativeColor)
*
* @sample {highcharts} highcharts/css/series-negative-color/
* Negative color in styled mode
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @since 3.0
* @product highcharts
* @apioption plotOptions.area.negativeFillColor
*/
/**
* Whether the whole area or just the line should respond to mouseover
* tooltips and other mouse or touch events.
*
* @sample {highcharts|highstock} highcharts/plotoptions/area-trackbyarea/
* Display the tooltip when the area is hovered
*
* @type {boolean}
* @default false
* @since 1.1.6
* @product highcharts highstock
* @apioption plotOptions.area.trackByArea
*/
/**
* The Y axis value to serve as the base for the area, for
* distinguishing between values above and below a threshold. The area
* between the graph and the threshold is filled.
*
* * If a number is given, the Y axis will scale to the threshold.
* * If `null`, the scaling behaves like a line series with fill between
* the graph and the Y axis minimum.
* * If `Infinity` or `-Infinity`, the area between the graph and the
* corresponding Y axis extreme is filled (since v6.1.0).
*
* @sample {highcharts} highcharts/plotoptions/area-threshold/
* A threshold of 100
* @sample {highcharts} highcharts/plotoptions/area-threshold-infinity/
* A threshold of Infinity
*
* @type {number|null}
* @since 2.0
* @product highcharts highstock
*/
threshold: 0,
legendSymbol: 'areaMarker'
};
/**
* A `area` series. If the [type](#series.area.type) option is not
* specified, it is inherited from [chart.type](#chart.type).
*
* @extends series,plotOptions.area
* @excluding dataParser, dataURL, useOhlcData
* @product highcharts highstock
* @apioption series.area
*/
/**
* @see [fillColor](#series.area.fillColor)
* @see [fillOpacity](#series.area.fillOpacity)
*
* @apioption series.area.color
*/
/**
* An array of data points for the series. For the `area` series type,
* points can be given in the following ways:
*
* 1. An array of numerical values. In this case, the numerical values will be
* interpreted as `y` options. The `x` values will be automatically
* calculated, either starting at 0 and incremented by 1, or from
* `pointStart` * and `pointInterval` given in the series options. If the
* axis has categories, these will be used. Example:
* ```js
* data: [0, 5, 3, 5]
* ```
*
* 2. An array of arrays with 2 values. In this case, the values correspond to
* `x,y`. If the first value is a string, it is applied as the name of the
* point, and the `x` value is inferred.
* ```js
* data: [
* [0, 9],
* [1, 7],
* [2, 6]
* ]
* ```
*
* 3. An array of objects with named values. The following snippet shows only a
* few settings, see the complete options set below. If the total number of
* data points exceeds the series'
* [turboThreshold](#series.area.turboThreshold), this option is not
* available.
* ```js
* data: [{
* x: 1,
* y: 9,
* name: "Point2",
* color: "#00FF00"
* }, {
* x: 1,
* y: 6,
* name: "Point1",
* color: "#FF00FF"
* }]
* ```
*
* @sample {highcharts} highcharts/chart/reflow-true/
* Numerical values
* @sample {highcharts} highcharts/series/data-array-of-arrays/
* Arrays of numeric x and y
* @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
* Arrays of datetime x and y
* @sample {highcharts} highcharts/series/data-array-of-name-value/
* Arrays of point.name and y
* @sample {highcharts} highcharts/series/data-array-of-objects/
* Config objects
*
* @type {Array<number|Array<(number|string),(number|null)>|null|*>}
* @extends series.line.data
* @product highcharts highstock
* @apioption series.area.data
*/
/**
* @see [color](#series.area.color)
* @see [fillOpacity](#series.area.fillOpacity)
*
* @apioption series.area.fillColor
*/
/**
* @see [color](#series.area.color)
* @see [fillColor](#series.area.fillColor)
*
* @default {highcharts} 0.75
* @default {highstock} 0.75
* @apioption series.area.fillOpacity
*/
''; // Adds doclets above to transpiled
/* *
*
* Default Export
*
* */
/* harmony default export */ var Area_AreaSeriesDefaults = (AreaSeriesDefaults);
;// ./code/es5/es-modules/Series/Area/AreaSeries.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var AreaSeries_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b,
p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var AreaSeries_spreadArray = (undefined && undefined.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var AreaSeries_LineSeries = Series_SeriesRegistry.seriesTypes.line;
var AreaSeries_extend = Core_Utilities.extend, AreaSeries_merge = Core_Utilities.merge, AreaSeries_objectEach = Core_Utilities.objectEach, AreaSeries_pick = Core_Utilities.pick;
/* *
*
* Class
*
* */
/**
* Area series type.
*
* @private
* @class
* @name AreaSeries
*
* @augments LineSeries
*/
var AreaSeries = /** @class */ (function (_super) {
AreaSeries_extends(AreaSeries, _super);
function AreaSeries() {
return _super !== null && _super.apply(this, arguments) || this;
}
/* *
*
* Functions
*
* */
/* eslint-disable valid-jsdoc */
/**
* Draw the graph and the underlying area. This method calls the Series
* base function and adds the area. The areaPath is calculated in the
* getSegmentPath method called from Series.prototype.drawGraph.
* @private
*/
AreaSeries.prototype.drawGraph = function () {
var _this = this;
// Define or reset areaPath
this.areaPath = [];
// Call the base method
_super.prototype.drawGraph.apply(this);
// Define local variables
var _a = this,
areaPath = _a.areaPath,
options = _a.options;
AreaSeries_spreadArray([this], this.zones, true).forEach(function (owner, i) {
var _a;
var attribs = {},
fillColor = owner.fillColor || options.fillColor;
var area = owner.area;
var verb = area ? 'animate' : 'attr';
// Create or update the area
if (area) { // Update
area.endX = _this.preventGraphAnimation ?
null :
areaPath.xMap;
area.animate({ d: areaPath });
}
else { // Create
attribs.zIndex = 0; // #1069
/**
* SVG element of area-based charts. Can be used for styling
* purposes. If zones are configured, this element will be
* hidden and replaced by multiple zone areas, accessible
* via `series.zones[i].area`.
*
* @name Highcharts.Series#area
* @type {Highcharts.SVGElement|undefined}
*/
area = owner.area = _this.chart.renderer
.path(areaPath)
.addClass('highcharts-area' +
(i ? " highcharts-zone-area-".concat(i - 1, " ") : ' ') +
((i && owner.className) || ''))
.add(_this.group);
area.isArea = true;
}
if (!_this.chart.styledMode) {
// If there is fillColor defined for the area, set it.
// Otherwise, we set it to the zone/series color and add
// fill-opacity (#18939).
attribs.fill = fillColor || owner.color || _this.color;
attribs['fill-opacity'] = fillColor ?
1 : ((_a = options.fillOpacity) !== null && _a !== void 0 ? _a : 0.75);
// Allow clicking through the area if sticky tracking is true
// (#18744)
area.css({
pointerEvents: _this.stickyTracking ? 'none' : 'auto'
});
}
area[verb](attribs);
area.startX = areaPath.xMap;
area.shiftUnit = options.step ? 2 : 1;
});
};
/**
* @private
*/
AreaSeries.prototype.getGraphPath = function (points) {
var getGraphPath = AreaSeries_LineSeries.prototype.getGraphPath, options = this.options, stacking = options.stacking, yAxis = this.yAxis, bottomPoints = [], graphPoints = [], seriesIndex = this.index, stacks = yAxis.stacking.stacks[this.stackKey], threshold = options.threshold, translatedThreshold = Math.round(// #10909
yAxis.getThreshold(options.threshold)), connectNulls = AreaSeries_pick(// #10574
options.connectNulls, stacking === 'percent'),
// To display null points in underlying stacked series, this
// series graph must be broken, and the area also fall down to
// fill the gap left by the null point. #2069
addDummyPoints = function (i, otherI, side) {
var point = points[i], stackedValues = stacking &&
stacks[point.x].points[seriesIndex], nullVal = point[side + 'Null'] || 0, cliffVal = point[side + 'Cliff'] || 0;
var top,
bottom,
isNull = true;
if (cliffVal || nullVal) {
top = (nullVal ?
stackedValues[0] :
stackedValues[1]) + cliffVal;
bottom = stackedValues[0] + cliffVal;
isNull = !!nullVal;
}
else if (!stacking &&
points[otherI] &&
points[otherI].isNull) {
top = bottom = threshold;
}
// Add to the top and bottom line of the area
if (typeof top !== 'undefined') {
graphPoints.push({
plotX: plotX,
plotY: top === null ?
translatedThreshold :
yAxis.getThreshold(top),
isNull: isNull,
isCliff: true
});
bottomPoints.push({
plotX: plotX,
plotY: bottom === null ?
translatedThreshold :
yAxis.getThreshold(bottom),
doCurve: false // #1041, gaps in areaspline areas
});
}
};
var plotX,
isNull,
yBottom;
// Find what points to use
points = points || this.points;
// Fill in missing points
if (stacking) {
points = this.getStackPoints(points);
}
for (var i = 0, iEnd = points.length; i < iEnd; ++i) {
// Reset after series.update of stacking property (#12033)
if (!stacking) {
points[i].leftCliff = points[i].rightCliff =
points[i].leftNull = points[i].rightNull = void 0;
}
isNull = points[i].isNull;
plotX = AreaSeries_pick(points[i].rectPlotX, points[i].plotX);
yBottom = stacking ?
AreaSeries_pick(points[i].yBottom, translatedThreshold) :
translatedThreshold;
if (!isNull || connectNulls) {
if (!connectNulls) {
addDummyPoints(i, i - 1, 'left');
}
// Skip null point when stacking is false and connectNulls
// true
if (!(isNull && !stacking && connectNulls)) {
graphPoints.push(points[i]);
bottomPoints.push({
x: i,
plotX: plotX,
plotY: yBottom
});
}
if (!connectNulls) {
addDummyPoints(i, i + 1, 'right');
}
}
}
var topPath = getGraphPath.call(this,
graphPoints,
true,
true);
bottomPoints.reversed = true;
var bottomPath = getGraphPath.call(this,
bottomPoints,
true,
true);
var firstBottomPoint = bottomPath[0];
if (firstBottomPoint && firstBottomPoint[0] === 'M') {
bottomPath[0] = ['L', firstBottomPoint[1], firstBottomPoint[2]];
}
var areaPath = topPath.concat(bottomPath);
if (areaPath.length) {
areaPath.push(['Z']);
}
// TODO: don't set leftCliff and rightCliff when connectNulls?
var graphPath = getGraphPath
.call(this,
graphPoints,
false,
connectNulls);
if (this.chart.series.length > 1 &&
stacking &&
graphPoints.some(function (point) { return point.isCliff; })) {
areaPath.hasStackedCliffs = graphPath.hasStackedCliffs = true;
}
areaPath.xMap = topPath.xMap;
this.areaPath = areaPath;
return graphPath;
};
/**
* Return an array of stacked points, where null and missing points are
* replaced by dummy points in order for gaps to be drawn correctly in
* stacks.
* @private
*/
AreaSeries.prototype.getStackPoints = function (points) {
var series = this,
segment = [],
keys = [],
xAxis = this.xAxis,
yAxis = this.yAxis,
stack = yAxis.stacking.stacks[this.stackKey],
pointMap = {},
yAxisSeries = yAxis.series,
seriesLength = yAxisSeries.length,
upOrDown = yAxis.options.reversedStacks ? 1 : -1,
seriesIndex = yAxisSeries.indexOf(series);
points = points || this.points;
if (this.options.stacking) {
for (var i = 0; i < points.length; i++) {
// Reset after point update (#7326)
points[i].leftNull = points[i].rightNull = void 0;
// Create a map where we can quickly look up the points by
// their X values.
pointMap[points[i].x] = points[i];
}
// Sort the keys (#1651)
AreaSeries_objectEach(stack, function (stackX, x) {
// Nulled after switching between
// grouping and not (#1651, #2336)
if (stackX.total !== null) {
keys.push(x);
}
});
keys.sort(function (a, b) {
return a - b;
});
var visibleSeries_1 = yAxisSeries.map(function (s) { return s.visible; });
keys.forEach(function (x, idx) {
var y = 0,
stackPoint,
stackedValues;
if (pointMap[x] && !pointMap[x].isNull) {
segment.push(pointMap[x]);
// Find left and right cliff. -1 goes left, 1 goes
// right.
[-1, 1].forEach(function (direction) {
var nullName = direction === 1 ?
'rightNull' :
'leftNull',
cliffName = direction === 1 ?
'rightCliff' :
'leftCliff',
otherStack = stack[keys[idx + direction]];
var cliff = 0;
// If there is a stack next to this one,
// to the left or to the right...
if (otherStack) {
var i = seriesIndex;
// Can go either up or down,
// depending on reversedStacks
while (i >= 0 && i < seriesLength) {
var si = yAxisSeries[i].index;
stackPoint = otherStack.points[si];
if (!stackPoint) {
// If the next point in this series is
// missing, mark the point with
// point.leftNull or point.rightNull = true.
if (si === series.index) {
pointMap[x][nullName] = true;
// If there are missing points in the next
// stack in any of the series below this
// one, we need to subtract the missing
// values and add a hiatus to the left or
// right.
}
else if (visibleSeries_1[i]) {
stackedValues = stack[x].points[si];
if (stackedValues) {
cliff -= (stackedValues[1] -
stackedValues[0]);
}
}
}
// When reversedStacks is true, loop up,
// else loop down
i += upOrDown;
}
}
pointMap[x][cliffName] = cliff;
});
// There is no point for this X value in this series, so we
// insert a dummy point in order for the areas to be drawn
// correctly.
}
else {
// Loop down the stack to find the series below this
// one that has a value (#1991)
var i = seriesIndex;
while (i >= 0 && i < seriesLength) {
var si = yAxisSeries[i].index;
stackPoint = stack[x].points[si];
if (stackPoint) {
y = stackPoint[1];
break;
}
// When reversedStacks is true, loop up, else loop
// down
i += upOrDown;
}
y = AreaSeries_pick(y, 0);
y = yAxis.translate(// #6272
y, 0, 1, 0, 1);
segment.push({
isNull: true,
plotX: xAxis.translate(// #6272
x, 0, 0, 0, 1),
x: x,
plotY: y,
yBottom: y
});
}
});
}
return segment;
};
/* *
*
* Static Properties
*
* */
AreaSeries.defaultOptions = AreaSeries_merge(AreaSeries_LineSeries.defaultOptions, Area_AreaSeriesDefaults);
return AreaSeries;
}(AreaSeries_LineSeries));
AreaSeries_extend(AreaSeries.prototype, {
singleStacks: false
});
Series_SeriesRegistry.registerSeriesType('area', AreaSeries);
/* *
*
* Default Export
*
* */
/* harmony default export */ var Area_AreaSeries = ((/* unused pure expression or super */ null && (AreaSeries)));
;// ./code/es5/es-modules/Series/Spline/SplineSeries.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var SplineSeries_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b,
p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var SplineSeries_LineSeries = Series_SeriesRegistry.seriesTypes.line;
var SplineSeries_merge = Core_Utilities.merge, SplineSeries_pick = Core_Utilities.pick;
/* *
*
* Class
*
* */
/**
* Spline series type.
*
* @private
*/
var SplineSeries = /** @class */ (function (_super) {
SplineSeries_extends(SplineSeries, _super);
function SplineSeries() {
return _super !== null && _super.apply(this, arguments) || this;
}
/* *
*
* Functions
*
* */
/* eslint-disable valid-jsdoc */
/**
* Get the spline segment from a given point's previous neighbour to the
* given point.
*
* @private
* @function Highcharts.seriesTypes.spline#getPointSpline
*/
SplineSeries.prototype.getPointSpline = function (points, point, i) {
var
// 1 means control points midway between points, 2 means 1/3
// from the point, 3 is 1/4 etc
smoothing = 1.5, denom = smoothing + 1, plotX = point.plotX || 0, plotY = point.plotY || 0, lastPoint = points[i - 1], nextPoint = points[i + 1];
var leftContX,
leftContY,
rightContX,
rightContY;
/**
* @private
*/
function doCurve(otherPoint) {
return otherPoint &&
!otherPoint.isNull &&
otherPoint.doCurve !== false &&
// #6387, area splines next to null:
!point.isCliff;
}
// Find control points
if (doCurve(lastPoint) && doCurve(nextPoint)) {
var lastX = lastPoint.plotX || 0,
lastY = lastPoint.plotY || 0,
nextX = nextPoint.plotX || 0,
nextY = nextPoint.plotY || 0;
var correction = 0;
leftContX = (smoothing * plotX + lastX) / denom;
leftContY = (smoothing * plotY + lastY) / denom;
rightContX = (smoothing * plotX + nextX) / denom;
rightContY = (smoothing * plotY + nextY) / denom;
// Have the two control points make a straight line through main
// point
if (rightContX !== leftContX) { // #5016, division by zero
correction = (((rightContY - leftContY) *
(rightContX - plotX)) /
(rightContX - leftContX) + plotY - rightContY);
}
leftContY += correction;
rightContY += correction;
// To prevent false extremes, check that control points are
// between neighbouring points' y values
if (leftContY > lastY && leftContY > plotY) {
leftContY = Math.max(lastY, plotY);
// Mirror of left control point
rightContY = 2 * plotY - leftContY;
}
else if (leftContY < lastY && leftContY < plotY) {
leftContY = Math.min(lastY, plotY);
rightContY = 2 * plotY - leftContY;
}
if (rightContY > nextY && rightContY > plotY) {
rightContY = Math.max(nextY, plotY);
leftContY = 2 * plotY - rightContY;
}
else if (rightContY < nextY && rightContY < plotY) {
rightContY = Math.min(nextY, plotY);
leftContY = 2 * plotY - rightContY;
}
// Record for drawing in next point
point.rightContX = rightContX;
point.rightContY = rightContY;
// Visualize control points for debugging
/*
if (leftContX) {
this.chart.renderer
.circle(
leftContX + this.chart.plotLeft,
leftContY + this.chart.plotTop,
2
)
.attr({
stroke: 'red',
'stroke-width': 2,
fill: 'none',
zIndex: 9
})
.add();
this.chart.renderer
.path([['M', leftContX + this.chart.plotLeft,
leftContY + this.chart.plotTop
], ['L', plotX + this.chart.plotLeft,
plotY + this.chart.plotTop
]])
.attr({
stroke: 'red',
'stroke-width': 2,
zIndex: 9
})
.add();
}
if (rightContX) {
this.chart.renderer
.circle(
rightContX + this.chart.plotLeft,
rightContY + this.chart.plotTop,
2
)
.attr({
stroke: 'green',
'stroke-width': 2,
fill: 'none',
zIndex: 9
})
.add();
this.chart.renderer
.path([[
'M', rightContX + this.chart.plotLeft,
rightContY + this.chart.plotTop
], [
'L', plotX + this.chart.plotLeft,
plotY + this.chart.plotTop
]])
.attr({
stroke: 'green',
'stroke-width': 2,
zIndex: 9
})
.add();
}
// */
point.controlPoints = {
low: [leftContX, leftContY],
high: [rightContX, rightContY]
};
}
var ret = [
'C',
SplineSeries_pick(lastPoint.rightContX,
lastPoint.plotX, 0),
SplineSeries_pick(lastPoint.rightContY,
lastPoint.plotY, 0),
SplineSeries_pick(leftContX,
plotX, 0),
SplineSeries_pick(leftContY,
plotY, 0),
plotX,
plotY
];
// Reset for updating series later
lastPoint.rightContX = lastPoint.rightContY = void 0;
return ret;
};
/* *
*
* Static Properties
*
* */
/**
* A spline series is a special type of line series, where the segments
* between the data points are smoothed.
*
* @sample {highcharts} highcharts/demo/spline-irregular-time/
* Spline chart
* @sample {highstock} stock/demo/spline/
* Spline chart
*
* @extends plotOptions.series
* @excluding step, boostThreshold, boostBlending
* @product highcharts highstock
* @optionparent plotOptions.spline
*/
SplineSeries.defaultOptions = SplineSeries_merge(SplineSeries_LineSeries.defaultOptions);
return SplineSeries;
}(SplineSeries_LineSeries));
Series_SeriesRegistry.registerSeriesType('spline', SplineSeries);
/* *
*
* Default Export
*
* */
/* harmony default export */ var Spline_SplineSeries = (SplineSeries);
/* *
*
* API Options
*
* */
/**
* A `spline` series. If the [type](#series.spline.type) option is
* not specified, it is inherited from [chart.type](#chart.type).
*
* @extends series,plotOptions.spline
* @excluding dataParser, dataURL, step, boostThreshold, boostBlending
* @product highcharts highstock
* @apioption series.spline
*/
/**
* An array of data points for the series. For the `spline` series type,
* points can be given in the following ways:
*
* 1. An array of numerical values. In this case, the numerical values will be
* interpreted as `y` options. The `x` values will be automatically
* calculated, either starting at 0 and incremented by 1, or from
* `pointStart` and `pointInterval` given in the series options. If the axis
* has categories, these will be used. Example:
* ```js
* data: [0, 5, 3, 5]
* ```
*
* 2. An array of arrays with 2 values. In this case, the values correspond to
* `x,y`. If the first value is a string, it is applied as the name of the
* point, and the `x` value is inferred.
* ```js
* data: [
* [0, 9],
* [1, 2],
* [2, 8]
* ]
* ```
*
* 3. An array of objects with named values. The following snippet shows only a
* few settings, see the complete options set below. If the total number of
* data points exceeds the series'
* [turboThreshold](#series.spline.turboThreshold),
* this option is not available.
* ```js
* data: [{
* x: 1,
* y: 9,
* name: "Point2",
* color: "#00FF00"
* }, {
* x: 1,
* y: 0,
* name: "Point1",
* color: "#FF00FF"
* }]
* ```
*
* @sample {highcharts} highcharts/chart/reflow-true/
* Numerical values
* @sample {highcharts} highcharts/series/data-array-of-arrays/
* Arrays of numeric x and y
* @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
* Arrays of datetime x and y
* @sample {highcharts} highcharts/series/data-array-of-name-value/
* Arrays of point.name and y
* @sample {highcharts} highcharts/series/data-array-of-objects/
* Config objects
*
* @type {Array<number|Array<(number|string),(number|null)>|null|*>}
* @extends series.line.data
* @product highcharts highstock
* @apioption series.spline.data
*/
''; // Adds doclets above intro transpiled
;// ./code/es5/es-modules/Series/AreaSpline/AreaSplineSeries.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var AreaSplineSeries_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b,
p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var _a = Series_SeriesRegistry.seriesTypes, AreaSplineSeries_AreaSeries = _a.area, areaProto = _a.area.prototype;
var AreaSplineSeries_extend = Core_Utilities.extend, AreaSplineSeries_merge = Core_Utilities.merge;
/* *
*
* Class
*
* */
/**
* AreaSpline series type.
*
* @private
* @class
* @name Highcharts.seriesTypes.areaspline
*
* @augments Highcharts.Series
*/
var AreaSplineSeries = /** @class */ (function (_super) {
AreaSplineSeries_extends(AreaSplineSeries, _super);
function AreaSplineSeries() {
return _super !== null && _super.apply(this, arguments) || this;
}
/* *
*
* Static Properties
*
* */
AreaSplineSeries.defaultOptions = AreaSplineSeries_merge(Spline_SplineSeries.defaultOptions, AreaSplineSeries_AreaSeries.defaultOptions);
return AreaSplineSeries;
}(Spline_SplineSeries));
AreaSplineSeries_extend(AreaSplineSeries.prototype, {
getGraphPath: areaProto.getGraphPath,
getStackPoints: areaProto.getStackPoints,
drawGraph: areaProto.drawGraph
});
Series_SeriesRegistry.registerSeriesType('areaspline', AreaSplineSeries);
/* *
*
* Default Export
*
* */
/* harmony default export */ var AreaSpline_AreaSplineSeries = ((/* unused pure expression or super */ null && (AreaSplineSeries)));
/* *
*
* API Options
*
* */
/**
* The area spline series is an area series where the graph between the
* points is smoothed into a spline.
*
* @sample {highcharts} highcharts/demo/areaspline/
* Area spline chart
* @sample {highstock} stock/demo/areaspline/
* Area spline chart
*
* @extends plotOptions.area
* @excluding step, boostThreshold, boostBlending
* @product highcharts highstock
* @apioption plotOptions.areaspline
*/
/**
* @see [fillColor](#plotOptions.areaspline.fillColor)
* @see [fillOpacity](#plotOptions.areaspline.fillOpacity)
*
* @apioption plotOptions.areaspline.color
*/
/**
* @see [color](#plotOptions.areaspline.color)
* @see [fillOpacity](#plotOptions.areaspline.fillOpacity)
*
* @apioption plotOptions.areaspline.fillColor
*/
/**
* @see [color](#plotOptions.areaspline.color)
* @see [fillColor](#plotOptions.areaspline.fillColor)
*
* @default 0.75
* @apioption plotOptions.areaspline.fillOpacity
*/
/**
* A `areaspline` series. If the [type](#series.areaspline.type) option
* is not specified, it is inherited from [chart.type](#chart.type).
*
*
* @extends series,plotOptions.areaspline
* @excluding dataParser, dataURL, step, boostThreshold, boostBlending
* @product highcharts highstock
* @apioption series.areaspline
*/
/**
* @see [fillColor](#series.areaspline.fillColor)
* @see [fillOpacity](#series.areaspline.fillOpacity)
*
* @apioption series.areaspline.color
*/
/**
* An array of data points for the series. For the `areaspline` series
* type, points can be given in the following ways:
*
* 1. An array of numerical values. In this case, the numerical values will be
* interpreted as `y` options. The `x` values will be automatically
* calculated, either starting at 0 and incremented by 1, or from
* `pointStart` and `pointInterval` given in the series options. If the axis
* has categories, these will be used. Example:
* ```js
* data: [0, 5, 3, 5]
* ```
*
* 2. An array of arrays with 2 values. In this case, the values correspond to
* `x,y`. If the first value is a string, it is applied as the name of the
* point, and the `x` value is inferred.
* ```js
* data: [
* [0, 10],
* [1, 9],
* [2, 3]
* ]
* ```
*
* 3. An array of objects with named values. The following snippet shows only a
* few settings, see the complete options set below. If the total number of
* data points exceeds the series'
* [turboThreshold](#series.areaspline.turboThreshold), this option is not
* available.
* ```js
* data: [{
* x: 1,
* y: 4,
* name: "Point2",
* color: "#00FF00"
* }, {
* x: 1,
* y: 4,
* name: "Point1",
* color: "#FF00FF"
* }]
* ```
*
* @sample {highcharts} highcharts/chart/reflow-true/
* Numerical values
* @sample {highcharts} highcharts/series/data-array-of-arrays/
* Arrays of numeric x and y
* @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
* Arrays of datetime x and y
* @sample {highcharts} highcharts/series/data-array-of-name-value/
* Arrays of point.name and y
* @sample {highcharts} highcharts/series/data-array-of-objects/
* Config objects
*
* @type {Array<number|Array<(number|string),(number|null)>|null|*>}
* @extends series.line.data
* @product highcharts highstock
* @apioption series.areaspline.data
*/
/**
* @see [color](#series.areaspline.color)
* @see [fillOpacity](#series.areaspline.fillOpacity)
*
* @apioption series.areaspline.fillColor
*/
/**
* @see [color](#series.areaspline.color)
* @see [fillColor](#series.areaspline.fillColor)
*
* @default 0.75
* @apioption series.areaspline.fillOpacity
*/
''; // Adds doclets above into transpiled
;// ./code/es5/es-modules/Series/Column/ColumnSeriesDefaults.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* API Options
*
* */
/**
* Column series display one column per value along an X axis.
*
* @sample {highcharts} highcharts/demo/column-basic/
* Column chart
* @sample {highstock} stock/demo/column/
* Column chart
*
* @extends plotOptions.line
* @excluding connectEnds, connectNulls, gapSize, gapUnit, linecap,
* lineWidth, marker, step, useOhlcData
* @product highcharts highstock
* @optionparent plotOptions.column
*/
var ColumnSeriesDefaults = {
/**
* The corner radius of the border surrounding each column or bar. A number
* signifies pixels. A percentage string, like for example `50%`, signifies
* a relative size. For columns this is relative to the column width, for
* pies it is relative to the radius and the inner radius.
*
* @sample {highcharts} highcharts/plotoptions/column-borderradius/
* Rounded columns
* @sample highcharts/plotoptions/series-border-radius
* Column and pie with rounded border
*
* @type {number|string|Highcharts.BorderRadiusOptionsObject}
* @product highcharts highstock gantt
*/
borderRadius: 3,
/**
* When using automatic point colors pulled from the global
* [colors](colors) or series-specific
* [plotOptions.column.colors](series.colors) collections, this option
* determines whether the chart should receive one color per series or
* one color per point.
*
* In styled mode, the `colors` or `series.colors` arrays are not
* supported, and instead this option gives the points individual color
* class names on the form `highcharts-color-{n}`.
*
* @see [series colors](#plotOptions.column.colors)
*
* @sample {highcharts} highcharts/plotoptions/column-colorbypoint-false/
* False by default
* @sample {highcharts} highcharts/plotoptions/column-colorbypoint-true/
* True
*
* @type {boolean}
* @default false
* @since 2.0
* @product highcharts highstock gantt
* @apioption plotOptions.column.colorByPoint
*/
/**
* A series specific or series type specific color set to apply instead
* of the global [colors](#colors) when [colorByPoint](
* #plotOptions.column.colorByPoint) is true.
*
* @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
* @since 3.0
* @product highcharts highstock gantt
* @apioption plotOptions.column.colors
*/
/**
* When `true`, the columns will center in the category, ignoring null
* or missing points. When `false`, space will be reserved for null or
* missing points.
*
* @sample {highcharts} highcharts/series-column/centerincategory/
* Center in category
* @sample {highcharts} highcharts/series/stack-centerincategory/
* Center in category, stacked and grouped
*
* @since 8.0.1
* @product highcharts highstock gantt
*/
centerInCategory: false,
/**
* Padding between each value groups, in x axis units.
*
* @sample {highcharts} highcharts/plotoptions/column-grouppadding-default/
* 0.2 by default
* @sample {highcharts} highcharts/plotoptions/column-grouppadding-none/
* No group padding - all columns are evenly spaced
*
* @product highcharts highstock gantt
*/
groupPadding: 0.2,
/**
* Whether to group non-stacked columns or to let them render
* independent of each other. Non-grouped columns will be laid out
* individually and overlap each other.
*
* @sample {highcharts} highcharts/plotoptions/column-grouping-false/
* Grouping disabled
* @sample {highstock} highcharts/plotoptions/column-grouping-false/
* Grouping disabled
*
* @type {boolean}
* @default true
* @since 2.3.0
* @product highcharts highstock gantt
* @apioption plotOptions.column.grouping
*/
/** @ignore-option */
marker: null, // Point options are specified in the base options
/**
* The maximum allowed pixel width for a column, translated to the
* height of a bar in a bar chart. This prevents the columns from
* becoming too wide when there is a small number of points in the
* chart.
*
* @see [pointWidth](#plotOptions.column.pointWidth)
*
* @sample {highcharts} highcharts/plotoptions/column-maxpointwidth-20/
* Limited to 50
* @sample {highstock} highcharts/plotoptions/column-maxpointwidth-20/
* Limited to 50
*
* @type {number}
* @since 4.1.8
* @product highcharts highstock gantt
* @apioption plotOptions.column.maxPointWidth
*/
/**
* Padding between each column or bar, in x axis units.
*
* @sample {highcharts} highcharts/plotoptions/column-pointpadding-default/
* 0.1 by default
* @sample {highcharts} highcharts/plotoptions/column-pointpadding-025/
* 0.25
* @sample {highcharts} highcharts/plotoptions/column-pointpadding-none/
* 0 for tightly packed columns
*
* @product highcharts highstock gantt
*/
pointPadding: 0.1,
/**
* A pixel value specifying a fixed width for each column or bar point.
* When set to `undefined`, the width is calculated from the
* `pointPadding` and `groupPadding`. The width effects the dimension
* that is not based on the point value. For column series it is the
* horizontal length and for bar series it is the vertical length.
*
* @see [maxPointWidth](#plotOptions.column.maxPointWidth)
*
* @sample {highcharts} highcharts/plotoptions/column-pointwidth-20/
* 20px wide columns regardless of chart width or the amount of
* data points
*
* @type {number}
* @since 1.2.5
* @product highcharts highstock gantt
* @apioption plotOptions.column.pointWidth
*/
/**
* A pixel value specifying a fixed width for the column or bar.
* Overrides pointWidth on the series.
*
* @see [series.pointWidth](#plotOptions.column.pointWidth)
*
* @type {number}
* @default undefined
* @since 7.0.0
* @product highcharts highstock gantt
* @apioption series.column.data.pointWidth
*/
/**
* The minimal height for a column or width for a bar. By default,
* 0 values are not shown. To visualize a 0 (or close to zero) point,
* set the minimal point length to a pixel value like 3\. In stacked
* column charts, minPointLength might not be respected for tightly
* packed values.
*
* @sample {highcharts} highcharts/plotoptions/column-minpointlength/
* Zero base value
* @sample {highcharts} highcharts/plotoptions/column-minpointlength-pos-and-neg/
* Positive and negative close to zero values
*
* @product highcharts highstock gantt
*/
minPointLength: 0,
/**
* When the series contains less points than the crop threshold, all
* points are drawn, event if the points fall outside the visible plot
* area at the current zoom. The advantage of drawing all points
* (including markers and columns), is that animation is performed on
* updates. On the other hand, when the series contains more points than
* the crop threshold, the series data is cropped to only contain points
* that fall within the plot area. The advantage of cropping away
* invisible points is to increase performance on large series.
*
* @product highcharts highstock gantt
*/
cropThreshold: 50,
/**
* The X axis range that each point is valid for. This determines the
* width of the column. On a categorized axis, the range will be 1
* by default (one category unit). On linear and datetime axes, the
* range will be computed as the distance between the two closest data
* points.
*
* The default `null` means it is computed automatically, but this
* option can be used to override the automatic value.
*
* This option is set by default to 1 if data sorting is enabled.
*
* @sample {highcharts} highcharts/plotoptions/column-pointrange/
* Set the point range to one day on a data set with one week
* between the points
*
* @type {number|null}
* @since 2.3
* @product highcharts highstock gantt
*/
pointRange: null,
states: {
/**
* Options for the hovered point. These settings override the normal
* state options when a point is moused over or touched.
*
* @extends plotOptions.series.states.hover
* @excluding halo, lineWidth, lineWidthPlus, marker
* @product highcharts highstock gantt
*/
hover: {
/** @ignore-option */
halo: false,
/**
* A specific border color for the hovered point. Defaults to
* inherit the normal state border color.
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @product highcharts gantt
* @apioption plotOptions.column.states.hover.borderColor
*/
/**
* A specific color for the hovered point.
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @product highcharts gantt
* @apioption plotOptions.column.states.hover.color
*/
/**
* How much to brighten the point on interaction. Requires the
* main color to be defined in hex or rgb(a) format.
*
* In styled mode, the hover brightening is by default replaced
* with a fill-opacity set in the `.highcharts-point:hover`
* rule.
*
* @sample {highcharts} highcharts/plotoptions/column-states-hover-brightness/
* Brighten by 0.5
*
* @product highcharts highstock gantt
*/
brightness: 0.1
},
/**
* Options for the selected point. These settings override the
* normal state options when a point is selected.
*
* @extends plotOptions.series.states.select
* @excluding halo, lineWidth, lineWidthPlus, marker
* @product highcharts highstock gantt
*/
select: {
/**
* A specific color for the selected point.
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @default #cccccc
* @product highcharts highstock gantt
*/
color: "#cccccc" /* Palette.neutralColor20 */,
/**
* A specific border color for the selected point.
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @default #000000
* @product highcharts highstock gantt
*/
borderColor: "#000000" /* Palette.neutralColor100 */
}
},
dataLabels: {
align: void 0,
verticalAlign: void 0,
/**
* The y position offset of the label relative to the point in
* pixels.
*
* @type {number}
*/
y: void 0
},
// False doesn't work well: https://jsfiddle.net/highcharts/hz8fopan/14/
/** @ignore-option */
startFromThreshold: true,
stickyTracking: false,
tooltip: {
distance: 6
},
/**
* The Y axis value to serve as the base for the columns, for
* distinguishing between values above and below a threshold. If `null`,
* the columns extend from the padding Y axis minimum.
*
* @type {number|null}
* @since 2.0
* @product highcharts
*/
threshold: 0,
/**
* The width of the border surrounding each column or bar. Defaults to
* `1` when there is room for a border, but to `0` when the columns are
* so dense that a border would cover the next column.
*
* In styled mode, the stroke width can be set with the
* `.highcharts-point` rule.
*
* @sample {highcharts} highcharts/plotoptions/column-borderwidth/
* 2px black border
*
* @type {number}
* @default undefined
* @product highcharts highstock gantt
* @apioption plotOptions.column.borderWidth
*/
/**
* The color of the border surrounding each column or bar.
*
* In styled mode, the border stroke can be set with the
* `.highcharts-point` rule.
*
* @sample {highcharts} highcharts/plotoptions/column-bordercolor/
* Dark gray border
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @default #ffffff
* @product highcharts highstock gantt
*/
borderColor: "#ffffff" /* Palette.backgroundColor */
};
/**
* A `column` series. If the [type](#series.column.type) option is
* not specified, it is inherited from [chart.type](#chart.type).
*
* @extends series,plotOptions.column
* @excluding connectNulls, dataParser, dataURL, gapSize, gapUnit, linecap,
* lineWidth, marker, connectEnds, step
* @product highcharts highstock
* @apioption series.column
*/
/**
* An array of data points for the series. For the `column` series type,
* points can be given in the following ways:
*
* 1. An array of numerical values. In this case, the numerical values will be
* interpreted as `y` options. The `x` values will be automatically
* calculated, either starting at 0 and incremented by 1, or from
* `pointStart` and `pointInterval` given in the series options. If the axis
* has categories, these will be used. Example:
* ```js
* data: [0, 5, 3, 5]
* ```
*
* 2. An array of arrays with 2 values. In this case, the values correspond to
* `x,y`. If the first value is a string, it is applied as the name of the
* point, and the `x` value is inferred.
* ```js
* data: [
* [0, 6],
* [1, 2],
* [2, 6]
* ]
* ```
*
* 3. An array of objects with named values. The following snippet shows only a
* few settings, see the complete options set below. If the total number of
* data points exceeds the series'
* [turboThreshold](#series.column.turboThreshold), this option is not
* available.
* ```js
* data: [{
* x: 1,
* y: 9,
* name: "Point2",
* color: "#00FF00"
* }, {
* x: 1,
* y: 6,
* name: "Point1",
* color: "#FF00FF"
* }]
* ```
*
* @sample {highcharts} highcharts/chart/reflow-true/
* Numerical values
* @sample {highcharts} highcharts/series/data-array-of-arrays/
* Arrays of numeric x and y
* @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
* Arrays of datetime x and y
* @sample {highcharts} highcharts/series/data-array-of-name-value/
* Arrays of point.name and y
* @sample {highcharts} highcharts/series/data-array-of-objects/
* Config objects
*
* @type {Array<number|Array<(number|string),(number|null)>|null|*>}
* @extends series.line.data
* @excluding marker
* @product highcharts highstock
* @apioption series.column.data
*/
/**
* The color of the border surrounding the column or bar.
*
* In styled mode, the border stroke can be set with the `.highcharts-point`
* rule.
*
* @sample {highcharts} highcharts/plotoptions/column-bordercolor/
* Dark gray border
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @product highcharts highstock
* @apioption series.column.data.borderColor
*/
/**
* The width of the border surrounding the column or bar.
*
* In styled mode, the stroke width can be set with the `.highcharts-point`
* rule.
*
* @sample {highcharts} highcharts/plotoptions/column-borderwidth/
* 2px black border
*
* @type {number}
* @product highcharts highstock
* @apioption series.column.data.borderWidth
*/
/**
* A name for the dash style to use for the column or bar. Overrides
* dashStyle on the series.
*
* In styled mode, the stroke dash-array can be set with the same classes as
* listed under [data.color](#series.column.data.color).
*
* @see [series.pointWidth](#plotOptions.column.dashStyle)
*
* @type {Highcharts.DashStyleValue}
* @apioption series.column.data.dashStyle
*/
/**
* A pixel value specifying a fixed width for the column or bar. Overrides
* pointWidth on the series. The width effects the dimension that is not based
* on the point value.
*
* @see [series.pointWidth](#plotOptions.column.pointWidth)
*
* @type {number}
* @apioption series.column.data.pointWidth
*/
/**
* @excluding halo, lineWidth, lineWidthPlus, marker
* @product highcharts highstock
* @apioption series.column.states.hover
*/
/**
* @excluding halo, lineWidth, lineWidthPlus, marker
* @product highcharts highstock
* @apioption series.column.states.select
*/
''; // Keeps doclets above in JS file
/* *
*
* Default Export
*
* */
/* harmony default export */ var Column_ColumnSeriesDefaults = (ColumnSeriesDefaults);
;// ./code/es5/es-modules/Series/Column/ColumnSeries.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var ColumnSeries_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b,
p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var ColumnSeries_animObject = AnimationUtilities.animObject;
var ColumnSeries_color = Color_Color.parse;
var ColumnSeries_noop = Core_Globals.noop;
var ColumnSeries_clamp = Core_Utilities.clamp, ColumnSeries_crisp = Core_Utilities.crisp, ColumnSeries_defined = Core_Utilities.defined, ColumnSeries_extend = Core_Utilities.extend, ColumnSeries_fireEvent = Core_Utilities.fireEvent, ColumnSeries_isArray = Core_Utilities.isArray, ColumnSeries_isNumber = Core_Utilities.isNumber, ColumnSeries_merge = Core_Utilities.merge, ColumnSeries_pick = Core_Utilities.pick, ColumnSeries_objectEach = Core_Utilities.objectEach;
/* *
*
* Class
*
* */
/**
* The column series type.
*
* @private
* @class
* @name Highcharts.seriesTypes.column
*
* @augments Highcharts.Series
*/
var ColumnSeries = /** @class */ (function (_super) {
ColumnSeries_extends(ColumnSeries, _super);
function ColumnSeries() {
return _super !== null && _super.apply(this, arguments) || this;
}
/* *
*
* Functions
*
* */
/* eslint-disable valid-jsdoc */
/**
* Animate the column heights one by one from zero.
*
* @private
* @function Highcharts.seriesTypes.column#animate
*
* @param {boolean} init
* Whether to initialize the animation or run it
*/
ColumnSeries.prototype.animate = function (init) {
var series = this,
yAxis = this.yAxis,
yAxisPos = yAxis.pos,
reversed = yAxis.reversed,
options = series.options,
_a = this.chart,
clipOffset = _a.clipOffset,
inverted = _a.inverted,
attr = {},
translateProp = inverted ?
'translateX' :
'translateY';
var translateStart,
translatedThreshold;
if (init && clipOffset) {
attr.scaleY = 0.001;
translatedThreshold = ColumnSeries_clamp(yAxis.toPixels(options.threshold || 0), yAxisPos, yAxisPos + yAxis.len);
if (inverted) {
// Make sure the columns don't cover the axis line during
// entrance animation
translatedThreshold += reversed ?
-Math.floor(clipOffset[0]) :
Math.ceil(clipOffset[2]);
attr.translateX = translatedThreshold - yAxis.len;
}
else {
// Make sure the columns don't cover the axis line during
// entrance animation
translatedThreshold += reversed ?
Math.ceil(clipOffset[0]) :
-Math.floor(clipOffset[2]);
attr.translateY = translatedThreshold;
}
// Apply final clipping (used in Highcharts Stock) (#7083)
// animation is done by scaleY, so clipping is for panes
if (series.clipBox) {
series.setClip();
}
series.group.attr(attr);
}
else { // Run the animation
translateStart = Number(series.group.attr(translateProp));
series.group.animate({ scaleY: 1 }, ColumnSeries_extend(ColumnSeries_animObject(series.options.animation), {
// Do the scale synchronously to ensure smooth
// updating (#5030, #7228)
step: function (val, fx) {
if (series.group) {
attr[translateProp] = translateStart +
fx.pos * (yAxisPos - translateStart);
series.group.attr(attr);
}
}
}));
}
};
/**
* Initialize the series. Extends the basic Series.init method by
* marking other series of the same type as dirty.
*
* @private
* @function Highcharts.seriesTypes.column#init
*/
ColumnSeries.prototype.init = function (chart,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
options) {
_super.prototype.init.apply(this, arguments);
var series = this;
chart = series.chart;
// If the series is added dynamically, force redraw of other
// series affected by a new column
if (chart.hasRendered) {
chart.series.forEach(function (otherSeries) {
if (otherSeries.type === series.type) {
otherSeries.isDirty = true;
}
});
}
};
/**
* Return the width and x offset of the columns adjusted for grouping,
* groupPadding, pointPadding, pointWidth etc.
*
* @private
* @function Highcharts.seriesTypes.column#getColumnMetrics
*/
ColumnSeries.prototype.getColumnMetrics = function () {
var _a,
_b;
var series = this,
options = series.options,
xAxis = series.xAxis,
yAxis = series.yAxis,
reversedStacks = xAxis.options.reversedStacks,
// Keep backward compatibility: reversed xAxis had reversed
// stacks
reverseStacks = (xAxis.reversed && !reversedStacks) ||
(!xAxis.reversed && reversedStacks),
stackGroups = {};
var stackKey,
columnCount = 0;
// Get the total number of column type series. This is called on
// every series. Consider moving this logic to a chart.orderStacks()
// function and call it on init, addSeries and removeSeries
if (options.grouping === false) {
columnCount = 1;
}
else {
series.chart.series.forEach(function (otherSeries) {
var otherYAxis = otherSeries.yAxis,
otherOptions = otherSeries.options;
var columnIndex;
if (otherSeries.type === series.type &&
otherSeries.reserveSpace() &&
yAxis.len === otherYAxis.len &&
yAxis.pos === otherYAxis.pos) { // #642, #2086
if (otherOptions.stacking &&
otherOptions.stacking !== 'group') {
stackKey = otherSeries.stackKey;
if (typeof stackGroups[stackKey] ===
'undefined') {
stackGroups[stackKey] = columnCount++;
}
columnIndex = stackGroups[stackKey];
}
else if (otherOptions.grouping !== false) { // #1162
columnIndex = columnCount++;
}
otherSeries.columnIndex = columnIndex;
}
});
}
var categoryWidth = Math.min(Math.abs(xAxis.transA) * ((!((_a = xAxis.brokenAxis) === null || _a === void 0 ? void 0 : _a.hasBreaks) && ((_b = xAxis.ordinal) === null || _b === void 0 ? void 0 : _b.slope)) ||
options.pointRange ||
xAxis.closestPointRange ||
xAxis.tickInterval ||
1), // #2610
xAxis.len // #1535
), groupPadding = categoryWidth * options.groupPadding, groupWidth = categoryWidth - 2 * groupPadding, pointOffsetWidth = groupWidth / (columnCount || 1), pointWidth = Math.min(options.maxPointWidth || xAxis.len, ColumnSeries_pick(options.pointWidth, pointOffsetWidth * (1 - 2 * options.pointPadding))), pointPadding = (pointOffsetWidth - pointWidth) / 2,
// #1251, #3737
colIndex = (series.columnIndex || 0) + (reverseStacks ? 1 : 0), pointXOffset = pointPadding +
(groupPadding +
colIndex * pointOffsetWidth -
(categoryWidth / 2)) * (reverseStacks ? -1 : 1);
// Save it for reading in linked series (Error bars particularly)
series.columnMetrics = {
width: pointWidth,
offset: pointXOffset,
paddedWidth: pointOffsetWidth,
columnCount: columnCount
};
return series.columnMetrics;
};
/**
* Make the columns crisp. The edges are rounded to the nearest full
* pixel.
*
* @private
* @function Highcharts.seriesTypes.column#crispCol
*/
ColumnSeries.prototype.crispCol = function (x, y, width, height) {
var borderWidth = this.borderWidth,
inverted = this.chart.inverted,
bottom = ColumnSeries_crisp(y + height,
borderWidth,
inverted);
// Vertical
y = ColumnSeries_crisp(y, borderWidth, inverted);
height = bottom - y;
// Horizontal. We need to first compute the exact right edge, then
// round it and compute the width from there.
if (this.options.crisp) {
var right = ColumnSeries_crisp(x + width,
borderWidth);
x = ColumnSeries_crisp(x, borderWidth);
width = right - x;
}
return { x: x, y: y, width: width, height: height };
};
/**
* Adjust for missing columns, according to the `centerInCategory`
* option. Missing columns are either single points or stacks where the
* point or points are either missing or null.
*
* @private
* @function Highcharts.seriesTypes.column#adjustForMissingColumns
* @param {number} x
* The x coordinate of the column, left side
*
* @param {number} pointWidth
* The pointWidth, already computed upstream
*
* @param {Highcharts.ColumnPoint} point
* The point instance
*
* @param {Highcharts.ColumnMetricsObject} metrics
* The series-wide column metrics
*
* @return {number}
* The adjusted x position, or the original if not adjusted
*/
ColumnSeries.prototype.adjustForMissingColumns = function (x, pointWidth, point, metrics) {
var _this = this;
var _a;
if (!point.isNull && metrics.columnCount > 1) {
var visibleSeries_1 = this.xAxis.series
.filter(function (s) { return s.visible; })
.map(function (s) { return s.index; });
var indexInCategory_1 = 0,
totalInCategory_1 = 0;
// Loop over all the stacks on the Y axis. When stacking is enabled,
// these are real point stacks. When stacking is not enabled, but
// `centerInCategory` is true, there is one stack handling the
// grouping of points in each category. This is done in the
// `setGroupedPoints` function.
ColumnSeries_objectEach((_a = this.xAxis.stacking) === null || _a === void 0 ? void 0 : _a.stacks, function (stack) {
var _a;
var points = typeof point.x === 'number' ?
(_a = stack[point.x.toString()]) === null || _a === void 0 ? void 0 : _a.points :
void 0,
pointValues = points === null || points === void 0 ? void 0 : points[_this.index],
yStackMap = {};
// Look for the index
if (points && ColumnSeries_isArray(pointValues)) {
var baseIndex_1 = _this.index;
// If there are multiple points with the same X then
// gather all series in category, and assign index
var seriesIndexes = Object
.keys(points)
.filter(function (pointKey) {
// Filter out duplicate X's
return !pointKey.match(',') &&
// Filter out null points
points[pointKey] &&
points[pointKey].length > 1;
})
.map(parseFloat)
.filter(function (index) {
return visibleSeries_1.indexOf(index) !== -1;
})
// When the series `stack` option is defined, assign
// all subsequent column of the same stack to the
// same index as the base column of the stack, then
// filter out the original series index so that
// `seriesIndexes` is shortened to the amount of
// stacks, not the amount of series (#20550).
.filter(function (index) {
var otherOptions = _this.chart.series[index]
.options,
yStack = otherOptions.stacking &&
otherOptions.stack;
if (ColumnSeries_defined(yStack)) {
if (ColumnSeries_isNumber(yStackMap[yStack])) {
if (baseIndex_1 === index) {
baseIndex_1 = yStackMap[yStack];
}
return false;
}
yStackMap[yStack] = index;
}
return true;
})
.sort(function (a, b) { return b - a; });
indexInCategory_1 = seriesIndexes.indexOf(baseIndex_1);
totalInCategory_1 = seriesIndexes.length;
}
});
indexInCategory_1 = this.xAxis.reversed ?
totalInCategory_1 - 1 - indexInCategory_1 : indexInCategory_1;
// Compute the adjusted x position
var boxWidth = (totalInCategory_1 - 1) * metrics.paddedWidth +
pointWidth;
x = (point.plotX || 0) + boxWidth / 2 - pointWidth -
indexInCategory_1 * metrics.paddedWidth;
}
return x;
};
/**
* Translate each point to the plot area coordinate system and find
* shape positions
*
* @private
* @function Highcharts.seriesTypes.column#translate
*/
ColumnSeries.prototype.translate = function () {
var series = this,
chart = series.chart,
options = series.options,
dense = series.dense =
series.closestPointRange * series.xAxis.transA < 2,
borderWidth = series.borderWidth = ColumnSeries_pick(options.borderWidth,
dense ? 0 : 1 // #3635
),
xAxis = series.xAxis,
yAxis = series.yAxis,
threshold = options.threshold,
minPointLength = ColumnSeries_pick(options.minPointLength, 5),
metrics = series.getColumnMetrics(),
seriesPointWidth = metrics.width,
seriesXOffset = series.pointXOffset = metrics.offset,
dataMin = series.dataMin,
dataMax = series.dataMax,
translatedThreshold = series.translatedThreshold =
yAxis.getThreshold(threshold);
// Postprocessed for border width
var seriesBarW = series.barW =
Math.max(seriesPointWidth, 1 + 2 * borderWidth);
// When the pointPadding is 0, we want the columns to be packed
// tightly, so we allow individual columns to have individual sizes.
// When pointPadding is greater, we strive for equal-width columns
// (#2694).
if (options.pointPadding && options.crisp) {
seriesBarW = Math.ceil(seriesBarW);
}
Series_Series.prototype.translate.apply(series);
// Record the new values
series.points.forEach(function (point) {
var yBottom = ColumnSeries_pick(point.yBottom,
translatedThreshold),
safeDistance = 999 + Math.abs(yBottom),
plotX = point.plotX || 0,
// Don't draw too far outside plot area (#1303, #2241,
// #4264)
plotY = ColumnSeries_clamp(point.plotY, -safeDistance,
yAxis.len + safeDistance);
var up,
barY = Math.min(plotY,
yBottom),
barH = Math.max(plotY,
yBottom) - barY,
pointWidth = seriesPointWidth,
barX = plotX + seriesXOffset,
barW = seriesBarW;
// Handle options.minPointLength
if (minPointLength && Math.abs(barH) < minPointLength) {
barH = minPointLength;
up = (!yAxis.reversed && !point.negative) ||
(yAxis.reversed && point.negative);
// Reverse zeros if there's no positive value in the series
// in visible range (#7046)
if (ColumnSeries_isNumber(threshold) &&
ColumnSeries_isNumber(dataMax) &&
point.y === threshold &&
dataMax <= threshold &&
// And if there's room for it (#7311)
(yAxis.min || 0) < threshold &&
// If all points are the same value (i.e zero) not draw
// as negative points (#10646), but only if there's room
// for it (#14876)
(dataMin !== dataMax || (yAxis.max || 0) <= threshold)) {
up = !up;
point.negative = !point.negative;
}
// If stacked...
barY = (Math.abs(barY - translatedThreshold) > minPointLength ?
// ...keep position
yBottom - minPointLength :
// #1485, #4051
translatedThreshold -
(up ? minPointLength : 0));
}
// Handle point.options.pointWidth
// @todo Handle grouping/stacking too. Calculate offset properly
if (ColumnSeries_defined(point.options.pointWidth)) {
pointWidth = barW =
Math.ceil(point.options.pointWidth);
barX -= Math.round((pointWidth - seriesPointWidth) / 2);
}
// Adjust for null or missing points
if (options.centerInCategory) {
barX = series.adjustForMissingColumns(barX, pointWidth, point, metrics);
}
// Cache for access in polar
point.barX = barX;
point.pointWidth = pointWidth;
// Fix the tooltip on center of grouped columns (#1216, #424,
// #3648)
point.tooltipPos = chart.inverted ?
[
ColumnSeries_clamp(yAxis.len + yAxis.pos - chart.plotLeft - plotY, yAxis.pos - chart.plotLeft, yAxis.len + yAxis.pos - chart.plotLeft),
xAxis.len + xAxis.pos - chart.plotTop - barX - barW / 2,
barH
] :
[
xAxis.left - chart.plotLeft + barX + barW / 2,
ColumnSeries_clamp(plotY + yAxis.pos -
chart.plotTop, yAxis.pos - chart.plotTop, yAxis.len + yAxis.pos - chart.plotTop),
barH
];
// Register shape type and arguments to be used in drawPoints. Allow
// `shapeType` defined on `pointClass` level.
point.shapeType = series.pointClass.prototype.shapeType ||
'roundedRect';
point.shapeArgs = series.crispCol(barX,
// #3169, drilldown from null must have a position to work from.
// #6585, dataLabel should be placed on xAxis, not floating in
// the middle of the chart.
point.isNull ? translatedThreshold : barY, barW, point.isNull ? 0 : barH);
});
// Fire a specific event after column translate. We could instead apply
// all the column logic in an `afterTranslate` event handler, but there
// are so many other series types that use the column translation, that
// it is more convenient to have a specific event for it.
ColumnSeries_fireEvent(this, 'afterColumnTranslate');
};
/**
* Columns have no graph
*
* @private
* @function Highcharts.seriesTypes.column#drawGraph
*/
ColumnSeries.prototype.drawGraph = function () {
this.group[this.dense ? 'addClass' : 'removeClass']('highcharts-dense-data');
};
/**
* Get presentational attributes
*
* @private
* @function Highcharts.seriesTypes.column#pointAttribs
*/
ColumnSeries.prototype.pointAttribs = function (point, state) {
var options = this.options, p2o = this.pointAttrToOptions || {}, strokeOption = p2o.stroke || 'borderColor', strokeWidthOption = p2o['stroke-width'] || 'borderWidth';
var stateOptions,
zone,
brightness,
fill = (point && point.color) || this.color,
// Set to fill when borderColor null:
stroke = ((point && point[strokeOption]) ||
options[strokeOption] ||
fill),
dashstyle = (point && point.options.dashStyle) || options.dashStyle,
strokeWidth = (point && point[strokeWidthOption]) ||
options[strokeWidthOption] ||
this[strokeWidthOption] || 0,
opacity = ColumnSeries_pick(point && point.opacity,
options.opacity, 1);
// Handle zone colors
if (point && this.zones.length) {
zone = point.getZone();
// When zones are present, don't use point.color (#4267).
// Changed order (#6527), added support for colorAxis (#10670)
fill = (point.options.color ||
(zone && (zone.color || point.nonZonedColor)) ||
this.color);
if (zone) {
stroke = zone.borderColor || stroke;
dashstyle = zone.dashStyle || dashstyle;
strokeWidth = zone.borderWidth || strokeWidth;
}
}
// Select or hover states
if (state && point) {
stateOptions = ColumnSeries_merge(options.states[state],
// #6401
point.options.states &&
point.options.states[state] ||
{});
brightness = stateOptions.brightness;
fill =
stateOptions.color || (typeof brightness !== 'undefined' &&
ColumnSeries_color(fill)
.brighten(stateOptions.brightness)
.get()) || fill;
stroke = stateOptions[strokeOption] || stroke;
strokeWidth =
stateOptions[strokeWidthOption] || strokeWidth;
dashstyle = stateOptions.dashStyle || dashstyle;
opacity = ColumnSeries_pick(stateOptions.opacity, opacity);
}
var ret = {
fill: fill,
stroke: stroke,
'stroke-width': strokeWidth,
opacity: opacity
};
if (dashstyle) {
ret.dashstyle = dashstyle;
}
return ret;
};
/**
* Draw the columns. For bars, the series.group is rotated, so the same
* coordinates apply for columns and bars. This method is inherited by
* scatter series.
*
* @private
* @function Highcharts.seriesTypes.column#drawPoints
*/
ColumnSeries.prototype.drawPoints = function (points) {
if (points === void 0) { points = this.points; }
var series = this,
chart = this.chart,
options = series.options,
renderer = chart.renderer,
animationLimit = options.animationLimit || 250;
var shapeArgs;
// Draw the columns
points.forEach(function (point) {
var plotY = point.plotY;
var graphic = point.graphic,
hasGraphic = !!graphic,
verb = graphic && chart.pointCount < animationLimit ?
'animate' : 'attr';
if (ColumnSeries_isNumber(plotY) && point.y !== null) {
shapeArgs = point.shapeArgs;
// When updating a series between 2d and 3d or cartesian and
// polar, the shape type changes.
if (graphic && point.hasNewShapeType()) {
graphic = graphic.destroy();
}
// Set starting position for point sliding animation.
if (series.enabledDataSorting) {
point.startXPos = series.xAxis.reversed ?
-(shapeArgs ? (shapeArgs.width || 0) : 0) :
series.xAxis.width;
}
if (!graphic) {
point.graphic = graphic =
renderer[point.shapeType](shapeArgs)
.add(point.group || series.group);
if (graphic &&
series.enabledDataSorting &&
chart.hasRendered &&
chart.pointCount < animationLimit) {
graphic.attr({
x: point.startXPos
});
hasGraphic = true;
verb = 'animate';
}
}
if (graphic && hasGraphic) { // Update
graphic[verb](ColumnSeries_merge(shapeArgs));
}
// Presentational
if (!chart.styledMode) {
graphic[verb](series.pointAttribs(point, (point.selected && 'select')))
.shadow(point.allowShadow !== false && options.shadow);
}
if (graphic) {
graphic.addClass(point.getClassName(), true);
graphic.attr({
visibility: point.visible ? 'inherit' : 'hidden'
});
}
}
else if (graphic) {
point.graphic = graphic.destroy(); // #1269
}
});
};
/**
* Draw the tracker for a point.
* @private
*/
ColumnSeries.prototype.drawTracker = function (points) {
if (points === void 0) { points = this.points; }
var series = this,
chart = series.chart,
pointer = chart.pointer,
onMouseOver = function (e) {
pointer === null || pointer === void 0 ? void 0 : pointer.normalize(e);
var point = pointer === null || pointer === void 0 ? void 0 : pointer.getPointFromEvent(e),
// Run point events only for points inside plot area, #21136
isInsidePlot = chart.scrollablePlotArea ?
chart.isInsidePlot(e.chartX - chart.plotLeft,
e.chartY - chart.plotTop, {
visiblePlotOnly: true
}) : true;
// Undefined on graph in scatterchart
if (pointer &&
point &&
series.options.enableMouseTracking &&
isInsidePlot) {
pointer.isDirectTouch = true;
point.onMouseOver(e);
}
};
var dataLabels;
// Add reference to the point
points.forEach(function (point) {
dataLabels = (ColumnSeries_isArray(point.dataLabels) ?
point.dataLabels :
(point.dataLabel ? [point.dataLabel] : []));
if (point.graphic) {
point.graphic.element.point = point;
}
dataLabels.forEach(function (dataLabel) {
(dataLabel.div || dataLabel.element).point = point;
});
});
// Add the event listeners, we need to do this only once
if (!series._hasTracking) {
series.trackerGroups.forEach(function (key) {
if (series[key]) {
// We don't always have dataLabelsGroup
series[key]
.addClass('highcharts-tracker')
.on('mouseover', onMouseOver)
.on('mouseout', function (e) {
pointer === null || pointer === void 0 ? void 0 : pointer.onTrackerMouseOut(e);
})
.on('touchstart', onMouseOver);
if (!chart.styledMode && series.options.cursor) {
series[key]
.css({ cursor: series.options.cursor });
}
}
});
series._hasTracking = true;
}
ColumnSeries_fireEvent(this, 'afterDrawTracker');
};
/**
* Remove this series from the chart
*
* @private
* @function Highcharts.seriesTypes.column#remove
*/
ColumnSeries.prototype.remove = function () {
var series = this,
chart = series.chart;
// Column and bar series affects other series of the same type
// as they are either stacked or grouped
if (chart.hasRendered) {
chart.series.forEach(function (otherSeries) {
if (otherSeries.type === series.type) {
otherSeries.isDirty = true;
}
});
}
Series_Series.prototype.remove.apply(series, arguments);
};
/* *
*
* Static Properties
*
* */
ColumnSeries.defaultOptions = ColumnSeries_merge(Series_Series.defaultOptions, Column_ColumnSeriesDefaults);
return ColumnSeries;
}(Series_Series));
ColumnSeries_extend(ColumnSeries.prototype, {
// When tooltip is not shared, this series (and derivatives) requires
// direct touch/hover. KD-tree does not apply.
directTouch: true,
getSymbol: ColumnSeries_noop,
// Use separate negative stacks, unlike area stacks where a negative
// point is subtracted from previous (#1910)
negStacks: true,
trackerGroups: ['group', 'dataLabelsGroup']
});
Series_SeriesRegistry.registerSeriesType('column', ColumnSeries);
/* *
*
* Default Export
*
* */
/* harmony default export */ var Column_ColumnSeries = (ColumnSeries);
/* *
*
* API Declarations
*
* */
/**
* Adjusted width and x offset of the columns for grouping.
*
* @private
* @interface Highcharts.ColumnMetricsObject
*/ /**
* Width of the columns.
* @name Highcharts.ColumnMetricsObject#width
* @type {number}
*/ /**
* Offset of the columns.
* @name Highcharts.ColumnMetricsObject#offset
* @type {number}
*/
''; // Detach doclets above
;// ./code/es5/es-modules/Core/Series/DataLabel.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var DataLabel_getDeferredAnimation = AnimationUtilities.getDeferredAnimation;
var DataLabel_format = Core_Templating.format;
var DataLabel_defined = Core_Utilities.defined, DataLabel_extend = Core_Utilities.extend, DataLabel_fireEvent = Core_Utilities.fireEvent, DataLabel_getAlignFactor = Core_Utilities.getAlignFactor, DataLabel_isArray = Core_Utilities.isArray, DataLabel_isString = Core_Utilities.isString, DataLabel_merge = Core_Utilities.merge, DataLabel_objectEach = Core_Utilities.objectEach, DataLabel_pick = Core_Utilities.pick, DataLabel_pInt = Core_Utilities.pInt, DataLabel_splat = Core_Utilities.splat;
/* *
*
* Composition
*
* */
var DataLabel;
(function (DataLabel) {
/* *
*
* Declarations
*
* */
/* *
*
* Functions
*
* */
/**
* Check if this series has data labels, either a series-level setting, or
* individual. In case of individual point labels, this method is overridden
* to always return true.
* @private
*/
function hasDataLabels() {
return mergedDataLabelOptions(this)
.some(function (o) {
return o === null || o === void 0 ? void 0 : o.enabled;
});
}
/**
* Align each individual data label.
* @private
*/
function alignDataLabel(point, dataLabel, options, alignTo, isNew) {
var _a;
var series = this,
_b = this,
chart = _b.chart,
enabledDataSorting = _b.enabledDataSorting,
inverted = this.isCartesian && chart.inverted,
plotX = point.plotX,
plotY = point.plotY,
rotation = options.rotation || 0,
isInsidePlot = DataLabel_defined(plotX) &&
DataLabel_defined(plotY) &&
chart.isInsidePlot(plotX,
Math.round(plotY), {
inverted: inverted,
paneCoordinates: true,
series: series
}),
setStartPos = function (alignOptions) {
if (enabledDataSorting && series.xAxis && !justify) {
series.setDataLabelStartPos(point,
dataLabel,
isNew,
isInsidePlot,
alignOptions);
}
}, justify = rotation === 0 ? DataLabel_pick(options.overflow, (enabledDataSorting ? 'none' : 'justify')) === 'justify' : false;
// Math.round for rounding errors (#2683), alignTo to allow column
// labels (#2700)
var visible = this.visible &&
point.visible !== false &&
DataLabel_defined(plotX) &&
(point.series.forceDL ||
(enabledDataSorting && !justify) ||
isInsidePlot ||
(
// If the data label is inside the align box, it is enough
// that parts of the align box is inside the plot area
// (#12370). When stacking, it is always inside regardless
// of the option (#15148).
DataLabel_pick(options.inside, !!this.options.stacking) &&
alignTo &&
chart.isInsidePlot(plotX,
inverted ?
alignTo.x + 1 :
alignTo.y + alignTo.height - 1, {
inverted: inverted,
paneCoordinates: true,
series: series
})));
var pos = point.pos();
if (visible && pos) {
var bBox = dataLabel.getBBox(),
unrotatedbBox = dataLabel.getBBox(void 0, 0);
// The alignment box is a singular point
alignTo = DataLabel_extend({
x: pos[0],
y: Math.round(pos[1]),
width: 0,
height: 0
}, alignTo || {});
// Align to plot edges
if (options.alignTo === 'plotEdges' && series.isCartesian) {
alignTo[inverted ? 'x' : 'y'] = 0;
alignTo[inverted ? 'width' : 'height'] = ((_a = this.yAxis) === null || _a === void 0 ? void 0 : _a.len) || 0;
}
// Add the text size for alignment calculation
DataLabel_extend(options, {
width: bBox.width,
height: bBox.height
});
setStartPos(alignTo); // Data sorting
// Align the label to the adjusted box with for unrotated bBox due
// to rotationOrigin, which is based on unrotated label
dataLabel.align(DataLabel_merge(options, {
width: unrotatedbBox.width,
height: unrotatedbBox.height
}), false, alignTo, false);
dataLabel.alignAttr.x += DataLabel_getAlignFactor(options.align) *
(unrotatedbBox.width - bBox.width);
dataLabel.alignAttr.y += DataLabel_getAlignFactor(options.verticalAlign) *
(unrotatedbBox.height - bBox.height);
dataLabel[dataLabel.placed ? 'animate' : 'attr']({
'text-align': dataLabel.alignAttr['text-align'] || 'center',
x: dataLabel.alignAttr.x +
(bBox.width - unrotatedbBox.width) / 2,
y: dataLabel.alignAttr.y +
(bBox.height - unrotatedbBox.height) / 2,
rotationOriginX: (dataLabel.width || 0) / 2,
rotationOriginY: (dataLabel.height || 0) / 2
});
// Uncomment this block to visualize the bounding boxes used for
// determining visibility
// chart.renderer.rect(
// (dataLabel.alignAttr.x || 0) + chart.plotLeft,
// (dataLabel.alignAttr.y || 0) + chart.plotTop,
// bBox.width,
// bBox.height
// ).attr({
// stroke: 'rgba(0, 0, 0, 0.3)',
// 'stroke-width': 1,
// zIndex: 20
// }).add();
// chart.renderer.circle(
// chart.plotLeft + pick(dataLabel.alignAttr.x, 0),
// chart.plotTop + pick(dataLabel.alignAttr.y, 0),
// 2
// ).attr({
// fill: 'red',
// zIndex: 20
// }).add();
if (justify && alignTo.height >= 0) { // #8830
this.justifyDataLabel(dataLabel, options, dataLabel.alignAttr, bBox, alignTo, isNew);
}
else if (DataLabel_pick(options.crop, true)) {
var _c = dataLabel.alignAttr,
x = _c.x,
y = _c.y,
correction = 1;
// Check if the dataLabel should be visible.
visible =
chart.isInsidePlot(x, y, {
paneCoordinates: true,
series: series
}) &&
chart.isInsidePlot(x + bBox.width - correction, y + bBox.height - correction, {
paneCoordinates: true,
series: series
});
}
// When we're using a shape, make it possible with a connector or an
// arrow pointing to this point
if (options.shape && !rotation) {
dataLabel[isNew ? 'attr' : 'animate']({
anchorX: pos[0],
anchorY: pos[1]
});
}
}
// To use alignAttr property in hideOverlappingLabels
if (isNew && enabledDataSorting) {
dataLabel.placed = false;
}
// Show or hide based on the final aligned position
if (!visible && (!enabledDataSorting || justify)) {
dataLabel.hide();
dataLabel.placed = false; // Don't animate back in
}
else {
dataLabel.show();
dataLabel.placed = true; // Flag for overlapping logic
}
}
/**
* Handle the dataLabels.filter option.
* @private
*/
function applyFilter(point, options) {
var filter = options.filter;
if (filter) {
var op = filter.operator,
prop = point[filter.property],
val = filter.value;
if ((op === '>' && prop > val) ||
(op === '<' && prop < val) ||
(op === '>=' && prop >= val) ||
(op === '<=' && prop <= val) ||
(op === '==' && prop == val) || // eslint-disable-line eqeqeq
(op === '===' && prop === val) ||
(op === '!=' && prop != val) || // eslint-disable-line eqeqeq
(op === '!==' && prop !== val)) {
return true;
}
return false;
}
return true;
}
/**
* @private
*/
function compose(SeriesClass) {
var seriesProto = SeriesClass.prototype;
if (!seriesProto.initDataLabels) {
seriesProto.initDataLabels = initDataLabels;
seriesProto.initDataLabelsGroup = initDataLabelsGroup;
seriesProto.alignDataLabel = alignDataLabel;
seriesProto.drawDataLabels = drawDataLabels;
seriesProto.justifyDataLabel = justifyDataLabel;
seriesProto.setDataLabelStartPos = setDataLabelStartPos;
seriesProto.hasDataLabels = hasDataLabels;
}
}
DataLabel.compose = compose;
/**
* Create the SVGElement group for dataLabels
* @private
*/
function initDataLabelsGroup() {
return this.plotGroup('dataLabelsGroup', 'data-labels', this.hasRendered ? 'inherit' : 'hidden', // #5133, #10220
this.options.dataLabels.zIndex || 6);
}
/**
* Init the data labels with the correct animation
* @private
*/
function initDataLabels(animationConfig) {
var series = this,
hasRendered = series.hasRendered || 0;
// Create a separate group for the data labels to avoid rotation
var dataLabelsGroup = this.initDataLabelsGroup()
.attr({ opacity: +hasRendered }); // #3300
if (!hasRendered && dataLabelsGroup) {
if (series.visible) { // #2597, #3023, #3024
dataLabelsGroup.show();
}
if (series.options.animation) {
dataLabelsGroup.animate({ opacity: 1 }, animationConfig);
}
else {
dataLabelsGroup.attr({ opacity: 1 });
}
}
return dataLabelsGroup;
}
/**
* Draw the data labels
* @private
*/
function drawDataLabels(points) {
var _a;
points = points || this.points;
var series = this, chart = series.chart, seriesOptions = series.options, renderer = chart.renderer, _b = chart.options.chart, backgroundColor = _b.backgroundColor, plotBackgroundColor = _b.plotBackgroundColor, contrastColor = renderer.getContrast((DataLabel_isString(plotBackgroundColor) && plotBackgroundColor) ||
(DataLabel_isString(backgroundColor) && backgroundColor) ||
"#000000" /* Palette.neutralColor100 */), seriesDlOptions = mergedDataLabelOptions(series);
var pointOptions,
dataLabelsGroup;
// Resolve the animation
var _c = seriesDlOptions[0],
animation = _c.animation,
defer = _c.defer,
animationConfig = defer ?
DataLabel_getDeferredAnimation(chart,
animation,
series) :
{ defer: 0,
duration: 0 };
DataLabel_fireEvent(this, 'drawDataLabels');
if ((_a = series.hasDataLabels) === null || _a === void 0 ? void 0 : _a.call(series)) {
dataLabelsGroup = this.initDataLabels(animationConfig);
// Make the labels for each point
points.forEach(function (point) {
var _a,
_b;
var dataLabels = point.dataLabels || [];
// Merge in series options for the point.
// @note dataLabelAttribs (like pointAttribs) would eradicate
// the need for dlOptions, and simplify the section below.
pointOptions = DataLabel_splat(mergeArrays(seriesDlOptions,
// The dlOptions prop is used in treemaps
point.dlOptions || ((_a = point.options) === null || _a === void 0 ? void 0 : _a.dataLabels)));
// Handle each individual data label for this point
pointOptions.forEach(function (labelOptions, i) {
// Options for one datalabel
var labelEnabled = (labelOptions.enabled &&
(point.visible || point.dataLabelOnHidden) &&
// #2282, #4641, #7112, #10049
(!point.isNull || point.dataLabelOnNull) &&
applyFilter(point,
labelOptions)),
backgroundColor = labelOptions.backgroundColor,
borderColor = labelOptions.borderColor,
distance = labelOptions.distance,
_a = labelOptions.style,
style = _a === void 0 ? {} : _a;
var formatString,
labelText,
rotation,
attr = {},
dataLabel = dataLabels[i],
isNew = !dataLabel,
labelBgColor;
if (labelEnabled) {
// Create individual options structure that can be
// extended without affecting others
formatString = DataLabel_pick(labelOptions[point.formatPrefix + 'Format'], labelOptions.format);
labelText = DataLabel_defined(formatString) ?
DataLabel_format(formatString, point, chart) :
(labelOptions[point.formatPrefix + 'Formatter'] ||
labelOptions.formatter).call(point, labelOptions);
rotation = labelOptions.rotation;
if (!chart.styledMode) {
// Determine the color
style.color = DataLabel_pick(labelOptions.color, style.color, DataLabel_isString(series.color) ? series.color : void 0, "#000000" /* Palette.neutralColor100 */);
// Get automated contrast color
if (style.color === 'contrast') {
if (backgroundColor !== 'none') {
labelBgColor = backgroundColor;
}
point.contrastColor = renderer.getContrast(labelBgColor !== 'auto' && labelBgColor ||
(point.color || series.color));
style.color = (labelBgColor || // #20007
(!DataLabel_defined(distance) &&
labelOptions.inside) ||
DataLabel_pInt(distance || 0) < 0 ||
seriesOptions.stacking) ?
point.contrastColor :
contrastColor;
}
else {
delete point.contrastColor;
}
if (seriesOptions.cursor) {
style.cursor = seriesOptions.cursor;
}
}
attr = {
r: labelOptions.borderRadius || 0,
rotation: rotation,
padding: labelOptions.padding,
zIndex: 1
};
if (!chart.styledMode) {
attr.fill = backgroundColor === 'auto' ?
point.color :
backgroundColor;
attr.stroke = borderColor === 'auto' ?
point.color :
borderColor;
attr['stroke-width'] = labelOptions.borderWidth;
}
// Remove unused attributes (#947)
DataLabel_objectEach(attr, function (val, name) {
if (typeof val === 'undefined') {
delete attr[name];
}
});
}
// If the point is outside the plot area, or the label
// changes properties that we cannot change, destroy it and
// build a new one below. #678, #820.
if (dataLabel && (!labelEnabled ||
!DataLabel_defined(labelText) ||
!!dataLabel.div !== !!labelOptions.useHTML ||
(
// Change from no rotation to rotation and
// vice versa. Don't use defined() because
// rotation = 0 means also rotation = undefined
(!dataLabel.rotation ||
!labelOptions.rotation) &&
dataLabel.rotation !== labelOptions.rotation))) {
dataLabel = void 0;
isNew = true;
}
// Individual labels are disabled if the are explicitly
// disabled in the point options, or if they fall outside
// the plot area.
if (labelEnabled && DataLabel_defined(labelText)) {
if (!dataLabel) {
// Create new label element
dataLabel = renderer.label(labelText, 0, 0, labelOptions.shape, void 0, void 0, labelOptions.useHTML, void 0, 'data-label');
dataLabel.addClass(' highcharts-data-label-color-' +
point.colorIndex +
' ' + (labelOptions.className || '') +
( // #3398
labelOptions.useHTML ?
' highcharts-tracker' :
''));
}
else {
// Use old element and just update text
attr.text = labelText;
}
// Store data label options for later access
if (dataLabel) {
dataLabel.options = labelOptions;
dataLabel.attr(attr);
if (!chart.styledMode) {
// Styles must be applied before add in order to
// read text bounding box
dataLabel.css(style).shadow(labelOptions.shadow);
}
else if (style.width) {
// In styled mode with a width property set,
// the width should be applied to the
// dataLabel. (#20499). These properties affect
// layout and must be applied also in styled
// mode.
dataLabel.css({
width: style.width,
textOverflow: style.textOverflow,
whiteSpace: style.whiteSpace
});
}
DataLabel_fireEvent(dataLabel, 'beforeAddingDataLabel', { labelOptions: labelOptions, point: point });
if (!dataLabel.added) {
dataLabel.add(dataLabelsGroup);
}
// Now the data label is created and placed at 0,0,
// so we need to align it
series.alignDataLabel(point, dataLabel, labelOptions, void 0, isNew);
dataLabel.isActive = true;
if (dataLabels[i] && dataLabels[i] !== dataLabel) {
dataLabels[i].destroy();
}
dataLabels[i] = dataLabel;
}
}
});
// Destroy and remove the inactive ones
var j = dataLabels.length;
while (j--) {
// The item can be undefined if a disabled data label is
// succeeded by an enabled one (#19457)
if (!dataLabels[j] || !dataLabels[j].isActive) {
(_b = dataLabels[j]) === null || _b === void 0 ? void 0 : _b.destroy();
dataLabels.splice(j, 1);
}
else {
dataLabels[j].isActive = false;
}
}
// Write back
point.dataLabel = dataLabels[0];
point.dataLabels = dataLabels;
});
}
DataLabel_fireEvent(this, 'afterDrawDataLabels');
}
/**
* If data labels fall partly outside the plot area, align them back in, in
* a way that doesn't hide the point.
* @private
*/
function justifyDataLabel(dataLabel, options, alignAttr, bBox, alignTo, isNew) {
var chart = this.chart,
align = options.align,
verticalAlign = options.verticalAlign,
padding = dataLabel.box ? 0 : (dataLabel.padding || 0),
horizontalAxis = chart.inverted ? this.yAxis : this.xAxis,
horizontalAxisShift = horizontalAxis ?
horizontalAxis.left - chart.plotLeft : 0,
verticalAxis = chart.inverted ? this.xAxis : this.yAxis,
verticalAxisShift = verticalAxis ?
verticalAxis.top - chart.plotTop : 0;
var _a = options.x,
x = _a === void 0 ? 0 : _a,
_b = options.y,
y = _b === void 0 ? 0 : _b,
off,
justified;
// Off left
off = (alignAttr.x || 0) + padding + horizontalAxisShift;
if (off < 0) {
if (align === 'right' && x >= 0) {
options.align = 'left';
options.inside = true;
}
else {
x -= off;
}
justified = true;
}
// Off right
off = (alignAttr.x || 0) + bBox.width - padding + horizontalAxisShift;
if (off > chart.plotWidth) {
if (align === 'left' && x <= 0) {
options.align = 'right';
options.inside = true;
}
else {
x += chart.plotWidth - off;
}
justified = true;
}
// Off top
off = alignAttr.y + padding + verticalAxisShift;
if (off < 0) {
if (verticalAlign === 'bottom' && y >= 0) {
options.verticalAlign = 'top';
options.inside = true;
}
else {
y -= off;
}
justified = true;
}
// Off bottom
off = (alignAttr.y || 0) + bBox.height - padding + verticalAxisShift;
if (off > chart.plotHeight) {
if (verticalAlign === 'top' && y <= 0) {
options.verticalAlign = 'bottom';
options.inside = true;
}
else {
y += chart.plotHeight - off;
}
justified = true;
}
if (justified) {
options.x = x;
options.y = y;
dataLabel.placed = !isNew;
dataLabel.align(options, void 0, alignTo);
}
return justified;
}
/**
* Merge two objects that can be arrays. If one of them is an array, the
* other is merged into each element. If both are arrays, each element is
* merged by index. If neither are arrays, we use normal merge.
* @private
*/
function mergeArrays(one, two) {
var res = [],
i;
if (DataLabel_isArray(one) && !DataLabel_isArray(two)) {
res = one.map(function (el) {
return DataLabel_merge(el, two);
});
}
else if (DataLabel_isArray(two) && !DataLabel_isArray(one)) {
res = two.map(function (el) {
return DataLabel_merge(one, el);
});
}
else if (!DataLabel_isArray(one) && !DataLabel_isArray(two)) {
res = DataLabel_merge(one, two);
}
else if (DataLabel_isArray(one) && DataLabel_isArray(two)) {
i = Math.max(one.length, two.length);
while (i--) {
res[i] = DataLabel_merge(one[i], two[i]);
}
}
return res;
}
/**
* Merge plotOptions and series options for dataLabels.
* @private
*/
function mergedDataLabelOptions(series) {
var _a,
_b;
var plotOptions = series.chart.options.plotOptions;
return DataLabel_splat(mergeArrays(mergeArrays((_a = plotOptions === null || plotOptions === void 0 ? void 0 : plotOptions.series) === null || _a === void 0 ? void 0 : _a.dataLabels, (_b = plotOptions === null || plotOptions === void 0 ? void 0 : plotOptions[series.type]) === null || _b === void 0 ? void 0 : _b.dataLabels), series.options.dataLabels));
}
/**
* Set starting position for data label sorting animation.
* @private
*/
function setDataLabelStartPos(point, dataLabel, isNew, isInside, alignOptions) {
var chart = this.chart, inverted = chart.inverted, xAxis = this.xAxis, reversed = xAxis.reversed, labelCenter = ((inverted ? dataLabel.height : dataLabel.width) || 0) / 2, pointWidth = point.pointWidth, halfWidth = pointWidth ? pointWidth / 2 : 0;
dataLabel.startXPos = inverted ?
alignOptions.x :
(reversed ?
-labelCenter - halfWidth :
xAxis.width - labelCenter + halfWidth);
dataLabel.startYPos = inverted ?
(reversed ?
this.yAxis.height - labelCenter + halfWidth :
-labelCenter - halfWidth) : alignOptions.y;
// We need to handle visibility in case of sorting point outside plot
// area
if (!isInside) {
dataLabel
.attr({ opacity: 1 })
.animate({ opacity: 0 }, void 0, dataLabel.hide);
}
else if (dataLabel.visibility === 'hidden') {
dataLabel.show();
dataLabel
.attr({ opacity: 0 })
.animate({ opacity: 1 });
}
// Save start position on first render, but do not change position
if (!chart.hasRendered) {
return;
}
// Set start position
if (isNew) {
dataLabel.attr({ x: dataLabel.startXPos, y: dataLabel.startYPos });
}
dataLabel.placed = true;
}
})(DataLabel || (DataLabel = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Series_DataLabel = (DataLabel);
/* *
*
* API Declarations
*
* */
/**
* Callback JavaScript function to format the data label as a string. Note that
* if a `format` is defined, the format takes precedence and the formatter is
* ignored.
*
* @callback Highcharts.DataLabelsFormatterCallbackFunction
*
* @param {Highcharts.Point} this
* Data label context to format
*
* @param {Highcharts.DataLabelsOptions} options
* [API options](/highcharts/plotOptions.series.dataLabels) of the data label
*
* @return {number|string|null|undefined}
* Formatted data label text
*/
/**
* Values for handling data labels that flow outside the plot area.
*
* @typedef {"allow"|"justify"} Highcharts.DataLabelsOverflowValue
*/
''; // Keeps doclets above in JS file
;// ./code/es5/es-modules/Series/Column/ColumnDataLabel.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var ColumnDataLabel_composed = Core_Globals.composed;
var ColumnDataLabel_Series = Series_SeriesRegistry.series;
var ColumnDataLabel_merge = Core_Utilities.merge, ColumnDataLabel_pick = Core_Utilities.pick, ColumnDataLabel_pushUnique = Core_Utilities.pushUnique;
/* *
*
* Composition
*
* */
var ColumnDataLabel;
(function (ColumnDataLabel) {
/* *
*
* Functions
*
* */
/**
* Override the basic data label alignment by adjusting for the position of
* the column.
* @private
*/
function alignDataLabel(point, dataLabel, options, alignTo, isNew) {
var inverted = this.chart.inverted,
series = point.series,
xLen = (series.xAxis ? series.xAxis.len : this.chart.plotSizeX) || 0,
yLen = (series.yAxis ? series.yAxis.len : this.chart.plotSizeY) || 0,
// Data label box for alignment
dlBox = point.dlBox || point.shapeArgs,
below = ColumnDataLabel_pick(point.below, // Range series
point.plotY >
ColumnDataLabel_pick(this.translatedThreshold,
yLen)),
// Draw it inside the box?
inside = ColumnDataLabel_pick(options.inside, !!this.options.stacking);
// Align to the column itself, or the top of it
if (dlBox) { // Area range uses this method but not alignTo
alignTo = ColumnDataLabel_merge(dlBox);
// Check for specific overflow and crop conditions (#13240)
if (!(options.overflow === 'allow' && options.crop === false)) {
if (alignTo.y < 0) {
alignTo.height += alignTo.y;
alignTo.y = 0;
}
// If parts of the box overshoots outside the plot area, modify
// the box to center the label inside
var overshoot = alignTo.y + alignTo.height - yLen;
if (overshoot > 0 && overshoot < alignTo.height - 1) {
alignTo.height -= overshoot;
}
}
if (inverted) {
alignTo = {
x: yLen - alignTo.y - alignTo.height,
y: xLen - alignTo.x - alignTo.width,
width: alignTo.height,
height: alignTo.width
};
}
// Compute the alignment box
if (!inside) {
if (inverted) {
alignTo.x += below ? 0 : alignTo.width;
alignTo.width = 0;
}
else {
alignTo.y += below ? alignTo.height : 0;
alignTo.height = 0;
}
}
}
// When alignment is undefined (typically columns and bars), display the
// individual point below or above the point depending on the threshold
options.align = ColumnDataLabel_pick(options.align, !inverted || inside ? 'center' : below ? 'right' : 'left');
options.verticalAlign = ColumnDataLabel_pick(options.verticalAlign, inverted || inside ? 'middle' : below ? 'top' : 'bottom');
// Call the parent method
ColumnDataLabel_Series.prototype.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
// If label was justified and we have contrast, set it:
if (options.inside && point.contrastColor) {
dataLabel.css({
color: point.contrastColor
});
}
}
/** @private */
function compose(ColumnSeriesClass) {
Series_DataLabel.compose(ColumnDataLabel_Series);
if (ColumnDataLabel_pushUnique(ColumnDataLabel_composed, 'ColumnDataLabel')) {
ColumnSeriesClass.prototype.alignDataLabel = alignDataLabel;
}
}
ColumnDataLabel.compose = compose;
})(ColumnDataLabel || (ColumnDataLabel = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Column_ColumnDataLabel = (ColumnDataLabel);
;// ./code/es5/es-modules/Series/Bar/BarSeries.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var BarSeries_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b,
p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var BarSeries_extend = Core_Utilities.extend, BarSeries_merge = Core_Utilities.merge;
/* *
*
* Class
*
* */
/**
* Bar series type.
*
* @private
* @class
* @name Highcharts.seriesTypes.bar
*
* @augments Highcharts.Series
*/
var BarSeries = /** @class */ (function (_super) {
BarSeries_extends(BarSeries, _super);
function BarSeries() {
return _super !== null && _super.apply(this, arguments) || this;
}
/* *
*
* Static Properties
*
* */
/**
* A bar series is a special type of column series where the columns are
* horizontal.
*
* @sample highcharts/demo/bar-chart/
* Bar chart
*
* @extends plotOptions.column
* @product highcharts
* @optionparent plotOptions.bar
*/
BarSeries.defaultOptions = BarSeries_merge(Column_ColumnSeries.defaultOptions, {
// Nothing here yet
});
return BarSeries;
}(Column_ColumnSeries));
BarSeries_extend(BarSeries.prototype, {
inverted: true
});
Series_SeriesRegistry.registerSeriesType('bar', BarSeries);
/* *
*
* Default Export
*
* */
/* harmony default export */ var Bar_BarSeries = ((/* unused pure expression or super */ null && (BarSeries)));
/* *
*
* API Options
*
* */
/**
* A `bar` series. If the [type](#series.bar.type) option is not specified,
* it is inherited from [chart.type](#chart.type).
*
* @extends series,plotOptions.bar
* @excluding connectNulls, dashStyle, dataParser, dataURL, gapSize, gapUnit,
* linecap, lineWidth, marker, connectEnds, step
* @product highcharts
* @apioption series.bar
*/
/**
* An array of data points for the series. For the `bar` series type,
* points can be given in the following ways:
*
* 1. An array of numerical values. In this case, the numerical values will be
* interpreted as `y` options. The `x` values will be automatically
* calculated, either starting at 0 and incremented by 1, or from
* `pointStart` and `pointInterval` given in the series options. If the axis
* has categories, these will be used. Example:
* ```js
* data: [0, 5, 3, 5]
* ```
*
* 2. An array of arrays with 2 values. In this case, the values correspond to
* `x,y`. If the first value is a string, it is applied as the name of the
* point, and the `x` value is inferred.
* ```js
* data: [
* [0, 5],
* [1, 10],
* [2, 3]
* ]
* ```
*
* 3. An array of objects with named values. The following snippet shows only a
* few settings, see the complete options set below. If the total number of
* data points exceeds the series'
* [turboThreshold](#series.bar.turboThreshold), this option is not
* available.
* ```js
* data: [{
* x: 1,
* y: 1,
* name: "Point2",
* color: "#00FF00"
* }, {
* x: 1,
* y: 10,
* name: "Point1",
* color: "#FF00FF"
* }]
* ```
*
* @sample {highcharts} highcharts/chart/reflow-true/
* Numerical values
* @sample {highcharts} highcharts/series/data-array-of-arrays/
* Arrays of numeric x and y
* @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
* Arrays of datetime x and y
* @sample {highcharts} highcharts/series/data-array-of-name-value/
* Arrays of point.name and y
* @sample {highcharts} highcharts/series/data-array-of-objects/
* Config objects
*
* @type {Array<number|Array<(number|string),(number|null)>|null|*>}
* @extends series.column.data
* @product highcharts
* @apioption series.bar.data
*/
/**
* @excluding halo,lineWidth,lineWidthPlus,marker
* @product highcharts highstock
* @apioption series.bar.states.hover
*/
/**
* @excluding halo,lineWidth,lineWidthPlus,marker
* @product highcharts highstock
* @apioption series.bar.states.select
*/
''; // Gets doclets above into transpiled
;// ./code/es5/es-modules/Series/Scatter/ScatterSeriesDefaults.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* API Options
*
* */
/**
* A scatter plot uses cartesian coordinates to display values for two
* variables for a set of data.
*
* @sample {highcharts} highcharts/demo/scatter/
* Scatter plot
*
* @extends plotOptions.line
* @excluding cropThreshold, legendSymbolColor, pointPlacement, shadow,
* useOhlcData
* @product highcharts highstock
* @optionparent plotOptions.scatter
*/
var ScatterSeriesDefaults = {
/**
* The width of the line connecting the data points.
*
* @sample {highcharts} highcharts/plotoptions/scatter-linewidth-none/
* 0 by default
* @sample {highcharts} highcharts/plotoptions/scatter-linewidth-1/
* 1px
*
* @product highcharts highstock
*/
lineWidth: 0,
findNearestPointBy: 'xy',
/**
* Apply a jitter effect for the rendered markers. When plotting
* discrete values, a little random noise may help telling the points
* apart. The jitter setting applies a random displacement of up to `n`
* axis units in either direction. So for example on a horizontal X
* axis, setting the `jitter.x` to 0.24 will render the point in a
* random position between 0.24 units to the left and 0.24 units to the
* right of the true axis position. On a category axis, setting it to
* 0.5 will fill up the bin and make the data appear continuous.
*
* When rendered on top of a box plot or a column series, a jitter value
* of 0.24 will correspond to the underlying series' default
* [groupPadding](
* https://api.highcharts.com/highcharts/plotOptions.column.groupPadding)
* and [pointPadding](
* https://api.highcharts.com/highcharts/plotOptions.column.pointPadding)
* settings.
*
* **Note:** With boost mode enabled, the jitter effect is not supported.
*
* @sample {highcharts} highcharts/demo/scatter-jitter
* Jitter on a scatter plot
*
* @sample {highcharts} highcharts/series-scatter/jitter-boxplot
* Jittered scatter plot on top of a box plot
*
* @product highcharts highstock
* @since 7.0.2
*/
jitter: {
/**
* The maximal X offset for the random jitter effect.
*/
x: 0,
/**
* The maximal Y offset for the random jitter effect.
*/
y: 0
},
marker: {
enabled: true // Overrides auto-enabling in line series (#3647)
},
/**
* Sticky tracking of mouse events. When true, the `mouseOut` event
* on a series isn't triggered until the mouse moves over another
* series, or out of the plot area. When false, the `mouseOut` event on
* a series is triggered when the mouse leaves the area around the
* series' graph or markers. This also implies the tooltip. When
* `stickyTracking` is false and `tooltip.shared` is false, the tooltip
* will be hidden when moving the mouse between series.
*
* @type {boolean}
* @default false
* @product highcharts highstock highmaps
* @apioption plotOptions.scatter.stickyTracking
*/
/**
* A configuration object for the tooltip rendering of each single
* series. Properties are inherited from [tooltip](#tooltip).
* Overridable properties are `headerFormat`, `pointFormat`,
* `yDecimals`, `xDateFormat`, `yPrefix` and `ySuffix`. Unlike other
* series, in a scatter plot the series.name by default shows in the
* headerFormat and point.x and point.y in the pointFormat.
*
* @product highcharts highstock highmaps
*/
tooltip: {
/**
* @product highcharts highstock
*/
headerFormat: '<span style="color:{point.color}">\u25CF</span> ' +
'<span style="font-size: 0.8em"> {series.name}</span><br/>',
pointFormat: 'x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>'
}
};
/**
* A `scatter` series. If the [type](#series.scatter.type) option is
* not specified, it is inherited from [chart.type](#chart.type).
*
* @extends series,plotOptions.scatter
* @excluding cropThreshold, dataParser, dataURL, useOhlcData
* @product highcharts highstock
* @apioption series.scatter
*/
/**
* An array of data points for the series. For the `scatter` series
* type, points can be given in the following ways:
*
* 1. An array of numerical values. In this case, the numerical values will be
* interpreted as `y` options. The `x` values will be automatically
* calculated, either starting at 0 and incremented by 1, or from
* `pointStart` and `pointInterval` given in the series options. If the axis
* has categories, these will be used. Example:
* ```js
* data: [0, 5, 3, 5]
* ```
*
* 2. An array of arrays with 2 values. In this case, the values correspond to
* `x,y`. If the first value is a string, it is applied as the name of the
* point, and the `x` value is inferred.
* ```js
* data: [
* [0, 0],
* [1, 8],
* [2, 9]
* ]
* ```
*
* 3. An array of objects with named values. The following snippet shows only a
* few settings, see the complete options set below. If the total number of
* data points exceeds the series'
* [turboThreshold](#series.scatter.turboThreshold), this option is not
* available.
* ```js
* data: [{
* x: 1,
* y: 2,
* name: "Point2",
* color: "#00FF00"
* }, {
* x: 1,
* y: 4,
* name: "Point1",
* color: "#FF00FF"
* }]
* ```
*
* @sample {highcharts} highcharts/chart/reflow-true/
* Numerical values
* @sample {highcharts} highcharts/series/data-array-of-arrays/
* Arrays of numeric x and y
* @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
* Arrays of datetime x and y
* @sample {highcharts} highcharts/series/data-array-of-name-value/
* Arrays of point.name and y
* @sample {highcharts} highcharts/series/data-array-of-objects/
* Config objects
*
* @type {Array<number|Array<(number|string),(number|null)>|null|*>}
* @extends series.line.data
* @product highcharts highstock
* @apioption series.scatter.data
*/
''; // Keeps doclets above in JS file
/* *
*
* Default Export
*
* */
/* harmony default export */ var Scatter_ScatterSeriesDefaults = (ScatterSeriesDefaults);
;// ./code/es5/es-modules/Series/Scatter/ScatterSeries.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var ScatterSeries_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b,
p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var ScatterSeries_a = Series_SeriesRegistry.seriesTypes, ScatterSeries_ColumnSeries = ScatterSeries_a.column, ScatterSeries_LineSeries = ScatterSeries_a.line;
var ScatterSeries_addEvent = Core_Utilities.addEvent, ScatterSeries_extend = Core_Utilities.extend, ScatterSeries_merge = Core_Utilities.merge;
/* *
*
* Class
*
* */
/**
* Scatter series type.
*
* @private
*/
var ScatterSeries = /** @class */ (function (_super) {
ScatterSeries_extends(ScatterSeries, _super);
function ScatterSeries() {
return _super !== null && _super.apply(this, arguments) || this;
}
/* *
*
* Functions
*
* */
/* eslint-disable valid-jsdoc */
/**
* Optionally add the jitter effect.
* @private
*/
ScatterSeries.prototype.applyJitter = function () {
var series = this,
jitter = this.options.jitter,
len = this.points.length;
/**
* Return a repeatable, pseudo-random number based on an integer
* seed.
* @private
*/
function unrandom(seed) {
var rand = Math.sin(seed) * 10000;
return rand - Math.floor(rand);
}
if (jitter) {
this.points.forEach(function (point, i) {
['x', 'y'].forEach(function (dim, j) {
if (jitter[dim] && !point.isNull) {
var plotProp = "plot".concat(dim.toUpperCase()), axis = series["" + dim + "Axis"], translatedJitter = jitter[dim] *
axis.transA;
if (axis && !axis.logarithmic) {
// Identify the outer bounds of the jitter range
var min = Math.max(0, (point[plotProp] || 0) - translatedJitter),
max = Math.min(axis.len, (point[plotProp] || 0) + translatedJitter);
// Find a random position within this range
point[plotProp] = min +
(max - min) * unrandom(i + j * len);
// Update clientX for the tooltip k-d-tree
if (dim === 'x') {
point.clientX = point.plotX;
}
}
}
});
});
}
};
/**
* @private
*/
ScatterSeries.prototype.drawGraph = function () {
if (this.options.lineWidth) {
_super.prototype.drawGraph.call(this);
}
else if (this.graph) {
this.graph = this.graph.destroy();
}
};
/* *
*
* Static Properties
*
* */
ScatterSeries.defaultOptions = ScatterSeries_merge(ScatterSeries_LineSeries.defaultOptions, Scatter_ScatterSeriesDefaults);
return ScatterSeries;
}(ScatterSeries_LineSeries));
ScatterSeries_extend(ScatterSeries.prototype, {
drawTracker: ScatterSeries_ColumnSeries.prototype.drawTracker,
sorted: false,
requireSorting: false,
noSharedTooltip: true,
trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup']
});
/* *
*
* Events
*
* */
/* eslint-disable no-invalid-this */
ScatterSeries_addEvent(ScatterSeries, 'afterTranslate', function () {
this.applyJitter();
});
Series_SeriesRegistry.registerSeriesType('scatter', ScatterSeries);
/* *
*
* Default Export
*
* */
/* harmony default export */ var Scatter_ScatterSeries = ((/* unused pure expression or super */ null && (ScatterSeries)));
;// ./code/es5/es-modules/Series/CenteredUtilities.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var CenteredUtilities_deg2rad = Core_Globals.deg2rad;
var CenteredUtilities_fireEvent = Core_Utilities.fireEvent, CenteredUtilities_isNumber = Core_Utilities.isNumber, CenteredUtilities_pick = Core_Utilities.pick, CenteredUtilities_relativeLength = Core_Utilities.relativeLength;
/**
* @private
*/
var CenteredUtilities;
(function (CenteredUtilities) {
/* *
*
* Declarations
*
* */
/* *
*
* Functions
*
* */
/* eslint-disable valid-jsdoc */
/**
* Get the center of the pie based on the size and center options relative
* to the plot area. Borrowed by the polar and gauge series types.
*
* @private
* @function Highcharts.CenteredSeriesMixin.getCenter
*/
function getCenter() {
var options = this.options,
chart = this.chart,
slicingRoom = 2 * (options.slicedOffset || 0),
plotWidth = chart.plotWidth - 2 * slicingRoom,
plotHeight = chart.plotHeight - 2 * slicingRoom,
centerOption = options.center,
smallestSize = Math.min(plotWidth,
plotHeight),
thickness = options.thickness;
var handleSlicingRoom,
size = options.size,
innerSize = options.innerSize || 0,
i,
value;
if (typeof size === 'string') {
size = parseFloat(size);
}
if (typeof innerSize === 'string') {
innerSize = parseFloat(innerSize);
}
var positions = [
CenteredUtilities_pick(centerOption === null || centerOption === void 0 ? void 0 : centerOption[0], '50%'),
CenteredUtilities_pick(centerOption === null || centerOption === void 0 ? void 0 : centerOption[1], '50%'),
// Prevent from negative values
CenteredUtilities_pick(size && size < 0 ? void 0 : options.size, '100%'),
CenteredUtilities_pick(innerSize && innerSize < 0 ? void 0 : options.innerSize || 0, '0%')
];
// No need for inner size in angular (gauges) series but still required
// for pie series
if (chart.angular && !(this instanceof Series_Series)) {
positions[3] = 0;
}
for (i = 0; i < 4; ++i) {
value = positions[i];
handleSlicingRoom = i < 2 || (i === 2 && /%$/.test(value));
// I == 0: centerX, relative to width
// i == 1: centerY, relative to height
// i == 2: size, relative to smallestSize
// i == 3: innerSize, relative to size
positions[i] = CenteredUtilities_relativeLength(value, [plotWidth, plotHeight, smallestSize, positions[2]][i]) + (handleSlicingRoom ? slicingRoom : 0);
}
// Inner size cannot be larger than size (#3632)
if (positions[3] > positions[2]) {
positions[3] = positions[2];
}
// Thickness overrides innerSize, need to be less than pie size (#6647)
if (CenteredUtilities_isNumber(thickness) &&
thickness * 2 < positions[2] && thickness > 0) {
positions[3] = positions[2] - thickness * 2;
}
CenteredUtilities_fireEvent(this, 'afterGetCenter', { positions: positions });
return positions;
}
CenteredUtilities.getCenter = getCenter;
/**
* GetStartAndEndRadians - Calculates start and end angles in radians.
* Used in series types such as pie and sunburst.
*
* @private
* @function Highcharts.CenteredSeriesMixin.getStartAndEndRadians
*
* @param {number} [start]
* Start angle in degrees.
*
* @param {number} [end]
* Start angle in degrees.
*
* @return {Highcharts.RadianAngles}
* Returns an object containing start and end angles as radians.
*/
function getStartAndEndRadians(start, end) {
var startAngle = CenteredUtilities_isNumber(start) ? start : 0, // Must be a number
endAngle = ((CenteredUtilities_isNumber(end) && // Must be a number
end > startAngle && // Must be larger than the start angle
// difference must be less than 360 degrees
(end - startAngle) < 360) ?
end :
startAngle + 360),
correction = -90;
return {
start: CenteredUtilities_deg2rad * (startAngle + correction),
end: CenteredUtilities_deg2rad * (endAngle + correction)
};
}
CenteredUtilities.getStartAndEndRadians = getStartAndEndRadians;
})(CenteredUtilities || (CenteredUtilities = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Series_CenteredUtilities = (CenteredUtilities);
/* *
*
* API Declarations
*
* */
/**
* @private
* @interface Highcharts.RadianAngles
*/ /**
* @name Highcharts.RadianAngles#end
* @type {number}
*/ /**
* @name Highcharts.RadianAngles#start
* @type {number}
*/
''; // Keeps doclets above in JS file
;// ./code/es5/es-modules/Series/Pie/PiePoint.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var PiePoint_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b,
p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var PiePoint_assign = (undefined && undefined.__assign) || function () {
PiePoint_assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return PiePoint_assign.apply(this, arguments);
};
var PiePoint_setAnimation = AnimationUtilities.setAnimation;
var PiePoint_addEvent = Core_Utilities.addEvent, PiePoint_defined = Core_Utilities.defined, PiePoint_extend = Core_Utilities.extend, PiePoint_isNumber = Core_Utilities.isNumber, PiePoint_pick = Core_Utilities.pick, PiePoint_relativeLength = Core_Utilities.relativeLength;
/* *
*
* Class
*
* */
var PiePoint = /** @class */ (function (_super) {
PiePoint_extends(PiePoint, _super);
/**
* Initialize the pie slice.
* @private
*/
function PiePoint(series, options, x) {
var _a;
var _this = _super.call(this,
series,
options,
x) || this;
_this.half = 0;
(_a = _this.name) !== null && _a !== void 0 ? _a : (_this.name = 'Slice');
// Add event listener for select
var toggleSlice = function (e) {
_this.slice(e.type === 'select');
};
PiePoint_addEvent(_this, 'select', toggleSlice);
PiePoint_addEvent(_this, 'unselect', toggleSlice);
return _this;
}
/* *
*
* Functions
*
* */
/* eslint-disable valid-jsdoc */
/**
* Extendable method for getting the path of the connector between the
* data label and the pie slice.
* @private
*/
PiePoint.prototype.getConnectorPath = function (dataLabel) {
var labelPosition = dataLabel.dataLabelPosition,
options = (dataLabel.options || {}),
connectorShape = options.connectorShape,
shapeFunc = (this.connectorShapes[connectorShape] || connectorShape);
return labelPosition && shapeFunc.call(this, PiePoint_assign(PiePoint_assign({}, labelPosition.computed), { alignment: labelPosition.alignment }), labelPosition.connectorPosition, options) || [];
};
/**
* @private
*/
PiePoint.prototype.getTranslate = function () {
return this.sliced && this.slicedTranslation || {
translateX: 0,
translateY: 0
};
};
/**
* @private
*/
PiePoint.prototype.haloPath = function (size) {
var shapeArgs = this.shapeArgs;
return this.sliced || !this.visible ?
[] :
this.series.chart.renderer.symbols.arc(shapeArgs.x, shapeArgs.y, shapeArgs.r + size, shapeArgs.r + size, {
// Substract 1px to ensure the background is not bleeding
// through between the halo and the slice (#7495).
innerR: shapeArgs.r - 1,
start: shapeArgs.start,
end: shapeArgs.end,
borderRadius: shapeArgs.borderRadius
});
};
/**
* Negative points are not valid (#1530, #3623, #5322)
* @private
*/
PiePoint.prototype.isValid = function () {
return PiePoint_isNumber(this.y) && this.y >= 0;
};
/**
* Toggle the visibility of a pie slice or other data point. Note that this
* method is available only for some series, like pie, treemap and sunburst.
*
* @function Highcharts.Point#setVisible
*
* @param {boolean} [vis]
* True to show the pie slice or other data point, false to hide. If
* undefined, the visibility is toggled.
*
* @param {boolean} [redraw] Whether to redraw the chart after the point is
* altered. If doing more operations on the chart, it is a good idea to set
* redraw to false and call {@link Chart#redraw|chart.redraw()} after.
*
*/
PiePoint.prototype.setVisible = function (vis, redraw) {
if (redraw === void 0) { redraw = true; }
if (vis !== this.visible) {
// If called without an argument, toggle visibility
this.update({
visible: vis !== null && vis !== void 0 ? vis : !this.visible
}, redraw, void 0, false);
}
};
/**
* Set or toggle whether the slice is cut out from the pie.
* @private
*
* @param {boolean} sliced
* When undefined, the slice state is toggled.
*
* @param {boolean} [redraw]
* Whether to redraw the chart. True by default.
*
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
* Animation options.
*/
PiePoint.prototype.slice = function (sliced, redraw, animation) {
var series = this.series,
chart = series.chart;
PiePoint_setAnimation(animation, chart);
// Redraw is true by default
redraw = PiePoint_pick(redraw, true);
/**
* Pie series only. Whether to display a slice offset from the
* center.
* @name Highcharts.Point#sliced
* @type {boolean|undefined}
*/
// if called without an argument, toggle
this.sliced = this.options.sliced = sliced =
PiePoint_defined(sliced) ? sliced : !this.sliced;
// Update userOptions.data
series.options.data[series.data.indexOf(this)] =
this.options;
if (this.graphic) {
this.graphic.animate(this.getTranslate());
}
};
return PiePoint;
}(Series_Point));
PiePoint_extend(PiePoint.prototype, {
connectorShapes: {
// Only one available before v7.0.0
fixedOffset: function (labelPosition, connectorPosition, options) {
var breakAt = connectorPosition.breakAt,
touchingSliceAt = connectorPosition.touchingSliceAt,
lineSegment = options.softConnector ? [
'C', // Soft break
// 1st control point (of the curve)
labelPosition.x +
// 5 gives the connector a little horizontal bend
(labelPosition.alignment === 'left' ? -5 : 5),
labelPosition.y, //
2 * breakAt.x - touchingSliceAt.x, // 2nd control point
2 * breakAt.y - touchingSliceAt.y, //
breakAt.x, // End of the curve
breakAt.y //
] : [
'L', // Pointy break
breakAt.x,
breakAt.y
];
// Assemble the path
return ([
['M', labelPosition.x, labelPosition.y],
lineSegment,
['L', touchingSliceAt.x, touchingSliceAt.y]
]);
},
straight: function (labelPosition, connectorPosition) {
var touchingSliceAt = connectorPosition.touchingSliceAt;
// Direct line to the slice
return [
['M', labelPosition.x, labelPosition.y],
['L', touchingSliceAt.x, touchingSliceAt.y]
];
},
crookedLine: function (labelPosition, connectorPosition, options) {
var _a = connectorPosition.angle,
angle = _a === void 0 ? this.angle || 0 : _a,
breakAt = connectorPosition.breakAt,
touchingSliceAt = connectorPosition.touchingSliceAt,
series = this.series,
_b = series.center,
cx = _b[0],
cy = _b[1],
diameter = _b[2],
r = diameter / 2,
_c = series.chart,
plotLeft = _c.plotLeft,
plotWidth = _c.plotWidth,
leftAligned = labelPosition.alignment === 'left',
x = labelPosition.x,
y = labelPosition.y;
var crookX = breakAt.x;
if (options.crookDistance) {
var crookDistance = PiePoint_relativeLength(// % to fraction
options.crookDistance, 1);
crookX = leftAligned ?
cx +
r +
(plotWidth + plotLeft - cx - r) * (1 - crookDistance) :
plotLeft + (cx - r) * crookDistance;
// When the crookDistance option is undefined, make the bend in the
// intersection between the radial line in the middle of the slice,
// and the extension of the label position.
}
else {
crookX = cx + (cy - y) * Math.tan(angle - Math.PI / 2);
}
var path = [['M',
x,
y]];
// The crookedLine formula doesn't make sense if the path overlaps
// the label - use straight line instead in that case
if (leftAligned ?
(crookX <= x && crookX >= breakAt.x) :
(crookX >= x && crookX <= breakAt.x)) {
path.push(['L', crookX, y]);
}
path.push(['L', breakAt.x, breakAt.y], ['L', touchingSliceAt.x, touchingSliceAt.y]);
return path;
}
}
});
/* *
*
* Default Export
*
* */
/* harmony default export */ var Pie_PiePoint = (PiePoint);
;// ./code/es5/es-modules/Series/Pie/PieSeriesDefaults.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* API Options
*
* */
/**
* A pie chart is a circular graphic which is divided into slices to
* illustrate numerical proportion.
*
* @sample highcharts/demo/pie-chart/
* Pie chart
*
* @extends plotOptions.line
* @excluding animationLimit, boostThreshold, connectEnds, connectNulls,
* cropThreshold, dashStyle, dataSorting, dragDrop,
* findNearestPointBy, getExtremesFromAll, label, lineWidth,
* linkedTo, marker, negativeColor, pointInterval,
* pointIntervalUnit, pointPlacement, pointStart,
* softThreshold, stacking, step, threshold, turboThreshold,
* zoneAxis, zones, dataSorting, boostBlending
* @product highcharts highmaps
* @optionparent plotOptions.pie
*
* @private
*/
var PieSeriesDefaults = {
/**
* The corner radius of the border surrounding each slice. A number
* signifies pixels. A percentage string, like for example `50%`, signifies
* a size relative to the radius and the inner radius.
*
* @sample highcharts/plotoptions/series-border-radius
* Column and pie with rounded border
*
* @since 11.0.0
*
* @type {number|string|Highcharts.BorderRadiusOptionsObject}
*/
borderRadius: 3,
/**
* @excluding legendItemClick
* @apioption plotOptions.pie.events
*/
/**
* Fires when the checkbox next to the point name in the legend is
* clicked. One parameter, event, is passed to the function. The state
* of the checkbox is found by event.checked. The checked item is found
* by event.item. Return false to prevent the default action which is to
* toggle the select state of the series.
*
* @sample {highcharts} highcharts/plotoptions/series-events-checkboxclick/
* Alert checkbox status
*
* @type {Function}
* @since 1.2.0
* @product highcharts highmaps
* @context Highcharts.Point
* @apioption plotOptions.pie.events.checkboxClick
*/
/**
* Fires when the legend item belonging to the pie point (slice) is
* clicked. The `this` keyword refers to the point itself. One
* parameter, `event`, is passed to the function, containing common
* event information. The default action is to toggle the visibility of
* the point. This can be prevented by calling `event.preventDefault()`.
*
* **Note:** This option is deprecated in favor of
* [legend.events.itemClick](#legend.events.itemClick).
*
* @deprecated 11.4.4
* @type {Highcharts.PointLegendItemClickCallbackFunction}
* @since 1.2.0
* @product highcharts highmaps
* @apioption plotOptions.pie.point.events.legendItemClick
*/
/**
* The center of the pie chart relative to the plot area. Can be
* percentages or pixel values. The default behaviour (as of 3.0) is to
* center the pie so that all slices and data labels are within the plot
* area. As a consequence, the pie may actually jump around in a chart
* with dynamic values, as the data labels move. In that case, the
* center should be explicitly set, for example to `["50%", "50%"]`.
*
* @sample {highcharts} highcharts/plotoptions/pie-center/
* Centered at 100, 100
*
* @type {Array<(number|string|null),(number|string|null)>}
* @default [null, null]
* @product highcharts highmaps
*
* @private
*/
center: [null, null],
/**
* The color of the pie series. A pie series is represented as an empty
* circle if the total sum of its values is 0. Use this property to
* define the color of its border.
*
* In styled mode, the color can be defined by the
* [colorIndex](#plotOptions.series.colorIndex) option. Also, the series
* color can be set with the `.highcharts-series`,
* `.highcharts-color-{n}`, `.highcharts-{type}-series` or
* `.highcharts-series-{n}` class, or individual classes given by the
* `className` option.
*
* @sample {highcharts} highcharts/plotoptions/pie-emptyseries/
* Empty pie series
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @default ${palette.neutralColor20}
* @apioption plotOptions.pie.color
*/
/**
* @product highcharts
*
* @private
*/
clip: false,
/**
* @ignore-option
*
* @private
*/
colorByPoint: true, // Always true for pies
/**
* A series specific or series type specific color set to use instead
* of the global [colors](#colors).
*
* @sample {highcharts} highcharts/demo/pie-monochrome/
* Set default colors for all pies
*
* @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
* @since 3.0
* @product highcharts highmaps
* @apioption plotOptions.pie.colors
*/
/**
* @declare Highcharts.SeriesPieDataLabelsOptionsObject
* @extends plotOptions.series.dataLabels
* @excluding align, allowOverlap, inside, staggerLines, step
* @private
*/
dataLabels: {
/**
* Alignment method for data labels. Possible values are:
*
* - `plotEdges`: Each label touches the nearest vertical edge of
* the plot area.
*
* - `connectors`: Connectors have the same x position and the
* widest label of each half (left & right) touches the nearest
* vertical edge of the plot area.
*
* @sample {highcharts} highcharts/plotoptions/pie-datalabels-alignto-connectors/
* alignTo: connectors
* @sample {highcharts} highcharts/plotoptions/pie-datalabels-alignto-plotedges/
* alignTo: plotEdges
*
* @type {string}
* @since 7.0.0
* @product highcharts highmaps
* @apioption plotOptions.pie.dataLabels.alignTo
*/
/**
* The color of the line connecting the data label to the pie slice.
* The default color is the same as the point's color.
*
* In styled mode, the connector stroke is given in the
* `.highcharts-data-label-connector` class.
*
* @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorcolor/
* Blue connectors
* @sample {highcharts} highcharts/css/pie-point/
* Styled connectors
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @since 2.1
* @product highcharts highmaps
* @apioption plotOptions.pie.dataLabels.connectorColor
*/
/**
* The distance from the data label to the connector. Note that
* data labels also have a default `padding`, so in order for the
* connector to touch the text, the `padding` must also be 0.
*
* @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorpadding/
* No padding
*
* @since 2.1
* @product highcharts highmaps
*/
connectorPadding: 5,
/**
* Specifies the method that is used to generate the connector path.
* Highcharts provides 3 built-in connector shapes: `'crookedLine'`
* (default since v11), `'fixedOffset'` and `'straight'`.
*
* Users can provide their own method by passing a function instead of a
* string. Three arguments are passed to the callback:
*
* - An object that holds the information about the coordinates of the
* label (`x` & `y` properties) and how the label is located in
* relation to the pie (`alignment` property). `alignment` can by one
* of the following: `'left'` (pie on the left side of the data
* label), `'right'` (pie on the right side of the data label) or
* `'center'` (data label overlaps the pie).
*
* - An object that holds the information about the position of the
* connector. Its `touchingSliceAt` porperty tells the position of
* the place where the connector touches the slice.
*
* - Data label options
*
* The function has to return an SVG path definition in array form (see
* the example).
*
* @sample {highcharts}
* highcharts/plotoptions/pie-datalabels-connectorshape-string/
* connectorShape is a String
* @sample {highcharts}
* highcharts/plotoptions/pie-datalabels-connectorshape-function/
* connectorShape is a function
*
* @type {string|Function}
* @since 7.0.0
* @product highcharts highmaps
*/
connectorShape: 'crookedLine',
/**
* The width of the line connecting the data label to the pie slice.
*
* In styled mode, the connector stroke width is given in the
* `.highcharts-data-label-connector` class.
*
* @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorwidth-disabled/
* Disable the connector
* @sample {highcharts} highcharts/css/pie-point/
* Styled connectors
*
* @type {number}
* @default 1
* @since 2.1
* @product highcharts highmaps
* @apioption plotOptions.pie.dataLabels.connectorWidth
*/
/**
* Works only if `connectorShape` is `'crookedLine'`. It defines how
* far from the vertical plot edge the coonnector path should be
* crooked. With the default, `undefined`, the crook is placed so that
* the horizontal line from the label intersects with the radial line
* extending through the center of the pie slice.
*
* @sample {highcharts} highcharts/plotoptions/pie-datalabels-crookdistance/
* crookDistance set to 90%
*
* @since 7.0.0
* @product highcharts highmaps
*/
crookDistance: void 0,
/**
* The distance of the data label from the pie's edge. Negative
* numbers put the data label on top of the pie slices. Can also be
* defined as a percentage of pie's radius. Connectors are only
* shown for data labels outside the pie.
*
* @sample {highcharts} highcharts/plotoptions/pie-datalabels-distance/
* Data labels on top of the pie
*
* @type {number|string}
* @since 2.1
* @product highcharts highmaps
*/
distance: 30,
enabled: true,
/**
* A
* [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
* for the data label. Available variables are the same as for
* `formatter`.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
* Add a unit
*
* @type {string}
* @default undefined
* @since 3.0
* @apioption plotOptions.pie.dataLabels.format
*/
// eslint-disable-next-line valid-jsdoc
/**
* Callback JavaScript function to format the data label. Note that
* if a `format` is defined, the format takes precedence and the
* formatter is ignored.
*
* @type {Highcharts.DataLabelsFormatterCallbackFunction}
* @default function () { return this.point.isNull ? void 0 : this.point.name; }
*/
formatter: function () {
return this.isNull ? void 0 : this.name;
},
/**
* Whether to render the connector as a soft arc or a line with a sharp
* break. Works only if `connectorShape` equals to `fixedOffset`.
*
* @sample {highcharts}
* highcharts/plotoptions/pie-datalabels-softconnector-true/
* Soft
* @sample {highcharts}
* highcharts/plotoptions/pie-datalabels-softconnector-false/
* Non soft
*
* @since 2.1.7
* @product highcharts highmaps
*/
softConnector: true,
/**
* @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow
* Long labels truncated with an ellipsis
* @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow-wrap
* Long labels are wrapped
*
* @type {Highcharts.CSSObject}
* @apioption plotOptions.pie.dataLabels.style
*/
x: 0
},
/**
* If the total sum of the pie's values is 0, the series is represented
* as an empty circle . The `fillColor` option defines the color of that
* circle. Use [pie.borderWidth](#plotOptions.pie.borderWidth) to set
* the border thickness.
*
* @sample {highcharts} highcharts/plotoptions/pie-emptyseries/
* Empty pie series
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @private
*/
fillColor: void 0,
/**
* The end angle of the pie in degrees where 0 is top and 90 is right.
* Defaults to `startAngle` plus 360.
*
* @sample {highcharts} highcharts/demo/pie-semi-circle/
* Semi-circle donut
*
* @type {number}
* @since 1.3.6
* @product highcharts highmaps
* @apioption plotOptions.pie.endAngle
*/
/**
* Thickness describing the ring size for a donut type chart,
* overriding [innerSize](#plotOptions.pie.innerSize).
*
* @type {number}
* @default undefined
* @product highcharts
* @since 10.1.0
* @apioption plotOptions.pie.thickness
* @private
*/
/**
* Equivalent to [chart.ignoreHiddenSeries](#chart.ignoreHiddenSeries),
* this option tells whether the series shall be redrawn as if the
* hidden point were `null`.
*
* The default value changed from `false` to `true` with Highcharts
* 3.0.
*
* @sample {highcharts} highcharts/plotoptions/pie-ignorehiddenpoint/
* True, the hiddden point is ignored
*
* @since 2.3.0
* @product highcharts highmaps
*
* @private
*/
ignoreHiddenPoint: true,
/**
* @default true
* @extends plotOptions.series.inactiveOtherPoints
* @private
*/
inactiveOtherPoints: true,
/**
* The size of the inner diameter for the pie. A size greater than 0
* renders a donut chart. Can be a percentage or pixel value.
* Percentages are relative to the pie size. Pixel values are given as
* integers. Setting overridden by thickness.
*
*
* Note: in Highcharts < 4.1.2, the percentage was relative to the plot
* area, not the pie size.
*
* @sample {highcharts} highcharts/plotoptions/pie-innersize-80px/
* 80px inner size
* @sample {highcharts} highcharts/plotoptions/pie-innersize-50percent/
* 50% of the plot area
* @sample {highcharts} highcharts/demo/3d-pie-donut/
* 3D donut
*
* @type {number|string}
* @default 0
* @since 2.0
* @product highcharts highmaps
* @apioption plotOptions.pie.innerSize
*/
/**
* @ignore-option
*
* @private
*/
legendType: 'point',
/**
* @ignore-option
*
* @private
*/
marker: null, // Point options are specified in the base options
/**
* The minimum size for a pie in response to auto margins. The pie will
* try to shrink to make room for data labels in side the plot area,
* but only to this size.
*
* @type {number|string}
* @default 80
* @since 3.0
* @product highcharts highmaps
* @apioption plotOptions.pie.minSize
*/
/**
* The diameter of the pie relative to the plot area. Can be a
* percentage or pixel value. Pixel values are given as integers. The
* default behaviour (as of 3.0) is to scale to the plot area and give
* room for data labels within the plot area.
* [slicedOffset](#plotOptions.pie.slicedOffset) is also included in the
* default size calculation. As a consequence, the size of the pie may
* vary when points are updated and data labels more around. In that
* case it is best to set a fixed value, for example `"75%"`.
*
* @sample {highcharts} highcharts/plotoptions/pie-size/
* Smaller pie
*
* @type {number|string|null}
* @product highcharts highmaps
*
* @private
*/
size: null,
/**
* Whether to display this particular series or series type in the
* legend. Since 2.1, pies are not shown in the legend by default.
*
* @sample {highcharts} highcharts/plotoptions/series-showinlegend/
* One series in the legend, one hidden
*
* @product highcharts highmaps
*
* @private
*/
showInLegend: false,
/**
* If a point is sliced, moved out from the center, how many pixels
* should it be moved?.
*
* @sample {highcharts} highcharts/plotoptions/pie-slicedoffset-20/
* 20px offset
*
* @product highcharts highmaps
*
* @private
*/
slicedOffset: 10,
/**
* The start angle of the pie slices in degrees where 0 is top and 90
* right.
*
* @sample {highcharts} highcharts/plotoptions/pie-startangle-90/
* Start from right
*
* @type {number}
* @default 0
* @since 2.3.4
* @product highcharts highmaps
* @apioption plotOptions.pie.startAngle
*/
/**
* Sticky tracking of mouse events. When true, the `mouseOut` event
* on a series isn't triggered until the mouse moves over another
* series, or out of the plot area. When false, the `mouseOut` event on
* a series is triggered when the mouse leaves the area around the
* series' graph or markers. This also implies the tooltip. When
* `stickyTracking` is false and `tooltip.shared` is false, the tooltip
* will be hidden when moving the mouse between series.
*
* @product highcharts highmaps
*
* @private
*/
stickyTracking: false,
tooltip: {
followPointer: true
},
/**
* The color of the border surrounding each slice. When `null`, the
* border takes the same color as the slice fill. This can be used
* together with a `borderWidth` to fill drawing gaps created by
* antialiazing artefacts in borderless pies.
*
* In styled mode, the border stroke is given in the `.highcharts-point`
* class.
*
* @sample {highcharts} highcharts/plotoptions/pie-bordercolor-black/
* Black border
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @default #ffffff
* @product highcharts highmaps
*
* @private
*/
borderColor: "#ffffff" /* Palette.backgroundColor */,
/**
* The width of the border surrounding each slice.
*
* When setting the border width to 0, there may be small gaps between
* the slices due to SVG antialiasing artefacts. To work around this,
* keep the border width at 0.5 or 1, but set the `borderColor` to
* `null` instead.
*
* In styled mode, the border stroke width is given in the
* `.highcharts-point` class.
*
* @sample {highcharts} highcharts/plotoptions/pie-borderwidth/
* 3px border
*
* @product highcharts highmaps
*
* @private
*/
borderWidth: 1,
/**
* @ignore-option
* @private
*/
lineWidth: void 0, // #12222
states: {
/**
* @extends plotOptions.series.states.hover
* @excluding marker, lineWidth, lineWidthPlus
* @product highcharts highmaps
*/
hover: {
/**
* How much to brighten the point on interaction. Requires the
* main color to be defined in hex or rgb(a) format.
*
* In styled mode, the hover brightness is by default replaced
* by a fill-opacity given in the `.highcharts-point-hover`
* class.
*
* @sample {highcharts} highcharts/plotoptions/pie-states-hover-brightness/
* Brightened by 0.5
*
* @product highcharts highmaps
*/
brightness: 0.1
}
}
};
/**
* A `pie` series. If the [type](#series.pie.type) option is not specified,
* it is inherited from [chart.type](#chart.type).
*
* @extends series,plotOptions.pie
* @excluding cropThreshold, dataParser, dataURL, linkedTo, stack, xAxis, yAxis,
* dataSorting, step, boostThreshold, boostBlending
* @product highcharts highmaps
* @apioption series.pie
*/
/**
* An array of data points for the series. For the `pie` series type,
* points can be given in the following ways:
*
* 1. An array of numerical values. In this case, the numerical values will be
* interpreted as `y` options. Example:
* ```js
* data: [0, 5, 3, 5]
* ```
*
* 2. An array of objects with named values. The following snippet shows only a
* few settings, see the complete options set below. If the total number of
* data points exceeds the series'
* [turboThreshold](#series.pie.turboThreshold),
* this option is not available.
* ```js
* data: [{
* y: 1,
* name: "Point2",
* color: "#00FF00"
* }, {
* y: 7,
* name: "Point1",
* color: "#FF00FF"
* }]
* ```
*
* @sample {highcharts} highcharts/chart/reflow-true/
* Numerical values
* @sample {highcharts} highcharts/series/data-array-of-arrays/
* Arrays of numeric x and y
* @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
* Arrays of datetime x and y
* @sample {highcharts} highcharts/series/data-array-of-name-value/
* Arrays of point.name and y
* @sample {highcharts} highcharts/series/data-array-of-objects/
* Config objects
*
* @type {Array<number|Array<string,(number|null)>|null|*>}
* @extends series.line.data
* @excluding marker, x
* @product highcharts highmaps
* @apioption series.pie.data
*/
/**
* @type {Highcharts.SeriesPieDataLabelsOptionsObject}
* @product highcharts highmaps
* @apioption series.pie.data.dataLabels
*/
/**
* The sequential index of the data point in the legend.
*
* @type {number}
* @product highcharts highmaps
* @apioption series.pie.data.legendIndex
*/
/**
* Whether to display a slice offset from the center.
*
* @sample {highcharts} highcharts/point/sliced/
* One sliced point
*
* @type {boolean}
* @product highcharts highmaps
* @apioption series.pie.data.sliced
*/
/**
* @extends plotOptions.pie.dataLabels
* @excluding align, allowOverlap, inside, staggerLines, step
* @product highcharts highmaps
* @apioption series.pie.dataLabels
*/
/**
* @excluding legendItemClick
* @product highcharts highmaps
* @apioption series.pie.events
*/
''; // Placeholder for transpiled doclets above
/* *
*
* Default Export
*
* */
/* harmony default export */ var Pie_PieSeriesDefaults = (PieSeriesDefaults);
;// ./code/es5/es-modules/Series/Pie/PieSeries.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var PieSeries_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b,
p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var getStartAndEndRadians = Series_CenteredUtilities.getStartAndEndRadians;
var PieSeries_noop = Core_Globals.noop;
var PieSeries_clamp = Core_Utilities.clamp, PieSeries_extend = Core_Utilities.extend, PieSeries_fireEvent = Core_Utilities.fireEvent, PieSeries_merge = Core_Utilities.merge, PieSeries_pick = Core_Utilities.pick;
/* *
*
* Class
*
* */
/**
* Pie series type.
*
* @private
* @class
* @name Highcharts.seriesTypes.pie
*
* @augments Highcharts.Series
*/
var PieSeries = /** @class */ (function (_super) {
PieSeries_extends(PieSeries, _super);
function PieSeries() {
return _super !== null && _super.apply(this, arguments) || this;
}
/* *
*
* Functions
*
* */
/* eslint-disable valid-jsdoc */
/**
* Animates the pies in.
* @private
*/
PieSeries.prototype.animate = function (init) {
var series = this,
points = series.points,
startAngleRad = series.startAngleRad;
if (!init) {
points.forEach(function (point) {
var graphic = point.graphic,
args = point.shapeArgs;
if (graphic && args) {
// Start values
graphic.attr({
// Animate from inner radius (#779)
r: PieSeries_pick(point.startR, (series.center && series.center[3] / 2)),
start: startAngleRad,
end: startAngleRad
});
// Animate
graphic.animate({
r: args.r,
start: args.start,
end: args.end
}, series.options.animation);
}
});
}
};
/**
* Called internally to draw auxiliary graph in pie-like series in
* situtation when the default graph is not sufficient enough to present
* the data well. Auxiliary graph is saved in the same object as
* regular graph.
* @private
*/
PieSeries.prototype.drawEmpty = function () {
var start = this.startAngleRad,
end = this.endAngleRad,
options = this.options;
var centerX,
centerY;
// Draw auxiliary graph if there're no visible points.
if (this.total === 0 && this.center) {
centerX = this.center[0];
centerY = this.center[1];
if (!this.graph) {
this.graph = this.chart.renderer
.arc(centerX, centerY, this.center[1] / 2, 0, start, end)
.addClass('highcharts-empty-series')
.add(this.group);
}
this.graph.attr({
d: SVG_Symbols.arc(centerX, centerY, this.center[2] / 2, 0, {
start: start,
end: end,
innerR: this.center[3] / 2
})
});
if (!this.chart.styledMode) {
this.graph.attr({
'stroke-width': options.borderWidth,
fill: options.fillColor || 'none',
stroke: options.color || "#cccccc" /* Palette.neutralColor20 */
});
}
}
else if (this.graph) { // Destroy the graph object.
this.graph = this.graph.destroy();
}
};
/**
* Slices in pie chart are initialized in DOM, but it's shapes and
* animations are normally run in `drawPoints()`.
* @private
*/
PieSeries.prototype.drawPoints = function () {
var renderer = this.chart.renderer;
this.points.forEach(function (point) {
// When updating a series between 2d and 3d or cartesian and
// polar, the shape type changes.
if (point.graphic && point.hasNewShapeType()) {
point.graphic = point.graphic.destroy();
}
if (!point.graphic) {
point.graphic = renderer[point.shapeType](point.shapeArgs)
.add(point.series.group);
point.delayedRendering = true;
}
});
};
/**
* Extend the generatePoints method by adding total and percentage
* properties to each point
* @private
*/
PieSeries.prototype.generatePoints = function () {
_super.prototype.generatePoints.call(this);
this.updateTotals();
};
/**
* Utility for getting the x value from a given y, used for anticollision
* logic in data labels.
* @private
*/
PieSeries.prototype.getX = function (y, left, point, dataLabel) {
var center = this.center,
// Variable pie has individual radius
radius = this.radii ?
this.radii[point.index] || 0 :
center[2] / 2,
labelPosition = dataLabel.dataLabelPosition,
distance = (labelPosition === null || labelPosition === void 0 ? void 0 : labelPosition.distance) || 0;
var angle = Math.asin(PieSeries_clamp((y - center[1]) / (radius + distance), -1, 1));
var x = center[0] +
(left ? -1 : 1) *
(Math.cos(angle) * (radius + distance)) +
(distance > 0 ?
(left ? -1 : 1) * (dataLabel.padding || 0) :
0);
return x;
};
/**
* Define hasData function for non-cartesian series. Returns true if the
* series has points at all.
* @private
*/
PieSeries.prototype.hasData = function () {
return !!this.dataTable.rowCount;
};
/**
* Draw the data points
* @private
*/
PieSeries.prototype.redrawPoints = function () {
var series = this,
chart = series.chart;
var groupTranslation,
graphic,
pointAttr,
shapeArgs;
this.drawEmpty();
// Apply the drop-shadow to the group because otherwise each element
// would cast a shadow on others
if (series.group && !chart.styledMode) {
series.group.shadow(series.options.shadow);
}
// Draw the slices
series.points.forEach(function (point) {
var animateTo = {};
graphic = point.graphic;
if (!point.isNull && graphic) {
shapeArgs = point.shapeArgs;
// If the point is sliced, use special translation, else use
// plot area translation
groupTranslation = point.getTranslate();
if (!chart.styledMode) {
pointAttr = series.pointAttribs(point, (point.selected && 'select'));
}
// Draw the slice
if (!point.delayedRendering) {
graphic
.setRadialReference(series.center);
if (!chart.styledMode) {
PieSeries_merge(true, animateTo, pointAttr);
}
PieSeries_merge(true, animateTo, shapeArgs, groupTranslation);
graphic.animate(animateTo);
}
else {
graphic
.setRadialReference(series.center)
.attr(shapeArgs)
.attr(groupTranslation);
if (!chart.styledMode) {
graphic
.attr(pointAttr)
.attr({ 'stroke-linejoin': 'round' });
}
point.delayedRendering = false;
}
graphic
.attr({
visibility: point.visible ? 'inherit' : 'hidden'
});
graphic.addClass(point.getClassName(), true);
}
else if (graphic) {
point.graphic = graphic.destroy();
}
});
};
/**
* Utility for sorting data labels.
* @private
*/
PieSeries.prototype.sortByAngle = function (points, sign) {
points.sort(function (a, b) {
return ((typeof a.angle !== 'undefined') &&
(b.angle - a.angle) * sign);
});
};
/**
* Do translation for pie slices
* @private
*/
PieSeries.prototype.translate = function (positions) {
PieSeries_fireEvent(this, 'translate');
this.generatePoints();
var series = this,
precision = 1000, // Issue #172
options = series.options,
slicedOffset = options.slicedOffset,
radians = getStartAndEndRadians(options.startAngle,
options.endAngle),
startAngleRad = series.startAngleRad = radians.start,
endAngleRad = series.endAngleRad = radians.end,
circ = endAngleRad - startAngleRad, // 2 * Math.PI,
points = series.points,
ignoreHiddenPoint = options.ignoreHiddenPoint,
len = points.length;
var start,
end,
angle,
// The x component of the radius vector for a given point
radiusX,
radiusY,
i,
point,
cumulative = 0;
// Get positions - either an integer or a percentage string must be
// given. If positions are passed as a parameter, we're in a
// recursive loop for adjusting space for data labels.
if (!positions) {
/**
* The series center position, read only. This applies only to
* circular chart types like pie and sunburst. It is an array of
* `[centerX, centerY, diameter, innerDiameter]`.
*
* @name Highcharts.Series#center
* @type {Array<number>}
*/
series.center = positions = series.getCenter();
}
// Calculate the geometry for each point
for (i = 0; i < len; i++) {
point = points[i];
// Set start and end angle
start = startAngleRad + (cumulative * circ);
if (point.isValid() &&
(!ignoreHiddenPoint || point.visible)) {
cumulative += point.percentage / 100;
}
end = startAngleRad + (cumulative * circ);
// Set the shape
var shapeArgs = {
x: positions[0],
y: positions[1],
r: positions[2] / 2,
innerR: positions[3] / 2,
start: Math.round(start * precision) / precision,
end: Math.round(end * precision) / precision
};
point.shapeType = 'arc';
point.shapeArgs = shapeArgs;
// The angle must stay within -90 and 270 (#2645)
angle = (end + start) / 2;
if (angle > 1.5 * Math.PI) {
angle -= 2 * Math.PI;
}
else if (angle < -Math.PI / 2) {
angle += 2 * Math.PI;
}
// Center for the sliced out slice
point.slicedTranslation = {
translateX: Math.round(Math.cos(angle) * slicedOffset),
translateY: Math.round(Math.sin(angle) * slicedOffset)
};
// Set the anchor point for tooltips
radiusX = Math.cos(angle) * positions[2] / 2;
radiusY = Math.sin(angle) * positions[2] / 2;
point.tooltipPos = [
positions[0] + radiusX * 0.7,
positions[1] + radiusY * 0.7
];
point.half = angle < -Math.PI / 2 || angle > Math.PI / 2 ?
1 :
0;
point.angle = angle;
}
PieSeries_fireEvent(series, 'afterTranslate');
};
/**
* Recompute total chart sum and update percentages of points.
* @private
*/
PieSeries.prototype.updateTotals = function () {
var points = this.points,
len = points.length,
ignoreHiddenPoint = this.options.ignoreHiddenPoint;
var i,
point,
total = 0;
// Get the total sum
for (i = 0; i < len; i++) {
point = points[i];
if (point.isValid() &&
(!ignoreHiddenPoint || point.visible)) {
total += point.y;
}
}
this.total = total;
// Set each point's properties
for (i = 0; i < len; i++) {
point = points[i];
point.percentage =
(total > 0 && (point.visible || !ignoreHiddenPoint)) ?
point.y / total * 100 :
0;
point.total = total;
}
};
/* *
*
* Static Properties
*
* */
PieSeries.defaultOptions = PieSeries_merge(Series_Series.defaultOptions, Pie_PieSeriesDefaults);
return PieSeries;
}(Series_Series));
PieSeries_extend(PieSeries.prototype, {
axisTypes: [],
directTouch: true,
drawGraph: void 0,
drawTracker: Column_ColumnSeries.prototype.drawTracker,
getCenter: Series_CenteredUtilities.getCenter,
getSymbol: PieSeries_noop,
invertible: false,
isCartesian: false,
noSharedTooltip: true,
pointAttribs: Column_ColumnSeries.prototype.pointAttribs,
pointClass: Pie_PiePoint,
requireSorting: false,
searchPoint: PieSeries_noop,
trackerGroups: ['group', 'dataLabelsGroup']
});
Series_SeriesRegistry.registerSeriesType('pie', PieSeries);
/* *
*
* Default Export
*
* */
/* harmony default export */ var Pie_PieSeries = ((/* unused pure expression or super */ null && (PieSeries)));
;// ./code/es5/es-modules/Series/Pie/PieDataLabel.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var PieDataLabel_composed = Core_Globals.composed, PieDataLabel_noop = Core_Globals.noop;
var PieDataLabel_distribute = Renderer_RendererUtilities.distribute;
var PieDataLabel_Series = Series_SeriesRegistry.series;
var PieDataLabel_arrayMax = Core_Utilities.arrayMax, PieDataLabel_clamp = Core_Utilities.clamp, PieDataLabel_defined = Core_Utilities.defined, PieDataLabel_pick = Core_Utilities.pick, PieDataLabel_pushUnique = Core_Utilities.pushUnique, PieDataLabel_relativeLength = Core_Utilities.relativeLength;
/* *
*
* Composition
*
* */
var PieDataLabel_ColumnDataLabel;
(function (ColumnDataLabel) {
/* *
*
* Constants
*
* */
var dataLabelPositioners = {
// Based on the value computed in Highcharts' distribute algorithm.
radialDistributionY: function (point,
dataLabel) {
var _a;
return (((_a = dataLabel.dataLabelPosition) === null || _a === void 0 ? void 0 : _a.top) || 0) +
point.distributeBox.pos;
},
// Get the x - use the natural x position for labels near the top and
// bottom, to prevent the top and botton slice connectors from touching
// each other on either side. Based on the value computed in Highcharts'
// distribute algorithm.
radialDistributionX: function (series, point, y, naturalY, dataLabel) {
var pos = dataLabel.dataLabelPosition;
return series.getX(y < ((pos === null || pos === void 0 ? void 0 : pos.top) || 0) + 2 || y > ((pos === null || pos === void 0 ? void 0 : pos.bottom) || 0) - 2 ?
naturalY :
y, point.half, point, dataLabel);
},
// The dataLabels.distance determines the x position of the label
justify: function (point, dataLabel, radius, seriesCenter) {
var _a;
return seriesCenter[0] + (point.half ? -1 : 1) *
(radius + (((_a = dataLabel.dataLabelPosition) === null || _a === void 0 ? void 0 : _a.distance) || 0));
},
// Left edges of the left-half labels touch the left edge of the plot
// area. Right edges of the right-half labels touch the right edge of
// the plot area.
alignToPlotEdges: function (dataLabel, half, plotWidth, plotLeft) {
var dataLabelWidth = dataLabel.getBBox().width;
return half ? dataLabelWidth + plotLeft :
plotWidth - dataLabelWidth - plotLeft;
},
// Connectors of each side end in the same x position. Labels are
// aligned to them. Left edge of the widest left-half label touches the
// left edge of the plot area. Right edge of the widest right-half label
// touches the right edge of the plot area.
alignToConnectors: function (points, half, plotWidth, plotLeft) {
var maxDataLabelWidth = 0,
dataLabelWidth;
// Find widest data label
points.forEach(function (point) {
dataLabelWidth = point.dataLabel.getBBox().width;
if (dataLabelWidth > maxDataLabelWidth) {
maxDataLabelWidth = dataLabelWidth;
}
});
return half ? maxDataLabelWidth + plotLeft :
plotWidth - maxDataLabelWidth - plotLeft;
}
};
/* *
*
* Functions
*
* */
/** @private */
function compose(PieSeriesClass) {
Series_DataLabel.compose(PieDataLabel_Series);
if (PieDataLabel_pushUnique(PieDataLabel_composed, 'PieDataLabel')) {
var pieProto = PieSeriesClass.prototype;
pieProto.dataLabelPositioners = dataLabelPositioners;
pieProto.alignDataLabel = PieDataLabel_noop;
pieProto.drawDataLabels = drawDataLabels;
pieProto.getDataLabelPosition = getDataLabelPosition;
pieProto.placeDataLabels = placeDataLabels;
pieProto.verifyDataLabelOverflow = verifyDataLabelOverflow;
}
}
ColumnDataLabel.compose = compose;
/** @private */
function getDataLabelPosition(point, distance) {
var halfPI = Math.PI / 2,
_a = point.shapeArgs || {},
_b = _a.start,
start = _b === void 0 ? 0 : _b,
_c = _a.end,
end = _c === void 0 ? 0 : _c;
var angle = point.angle || 0;
// If a large slice is crossing the lowest point, prefer rendering it 45
// degrees out at either lower right or lower left. That's where there's
// most likely to be space available and avoid text being truncated
// (#22100). Technically this logic should also apply to the top point,
// but that is more of an edge case since the default start angle is at
// the top.
if (distance > 0 &&
// Crossing the bottom
start < halfPI && end > halfPI &&
// Angle within the bottom quadrant
angle > halfPI / 2 && angle < halfPI * 1.5) {
angle = angle <= halfPI ?
Math.max(halfPI / 2, (start + halfPI) / 2) :
Math.min(halfPI * 1.5, (halfPI + end) / 2);
}
var _d = this, center = _d.center, options = _d.options, r = center[2] / 2, cosAngle = Math.cos(angle), sinAngle = Math.sin(angle), x = center[0] + cosAngle * r, y = center[1] + sinAngle * r, finalConnectorOffset = Math.min((options.slicedOffset || 0) + (options.borderWidth || 0), distance / 5); // #1678
return {
natural: {
// Initial position of the data label - it's utilized for
// finding the final position for the label
x: x + cosAngle * distance,
y: y + sinAngle * distance
},
computed: {
// Used for generating connector path - initialized later in
// drawDataLabels function x: undefined, y: undefined
},
// Left - pie on the left side of the data label
// Right - pie on the right side of the data label
// Center - data label overlaps the pie
alignment: distance < 0 ? 'center' : point.half ? 'right' : 'left',
connectorPosition: {
angle: angle,
breakAt: {
x: x + cosAngle * finalConnectorOffset,
y: y + sinAngle * finalConnectorOffset
},
touchingSliceAt: {
x: x,
y: y
}
},
distance: distance
};
}
/**
* Override the base drawDataLabels method by pie specific functionality
* @private
*/
function drawDataLabels() {
var _this = this;
var _a;
var series = this, points = series.points, chart = series.chart, plotWidth = chart.plotWidth, plotHeight = chart.plotHeight, plotLeft = chart.plotLeft, maxWidth = Math.round(chart.chartWidth / 3), seriesCenter = series.center, radius = seriesCenter[2] / 2, centerY = seriesCenter[1], halves = [
[], // Right
[] // Left
], overflow = [0, 0, 0, 0], // Top, right, bottom, left
dataLabelPositioners = series.dataLabelPositioners;
var connector,
dataLabelWidth,
labelHeight,
maxLabelDistance = 0;
// Get out if not enabled
if (!series.visible || !((_a = series.hasDataLabels) === null || _a === void 0 ? void 0 : _a.call(series))) {
return;
}
// Reset all labels that have been shortened
points.forEach(function (point) {
(point.dataLabels || []).forEach(function (dataLabel) {
if (dataLabel.shortened) {
dataLabel
.attr({
width: 'auto'
}).css({
width: 'auto',
textOverflow: 'clip'
});
dataLabel.shortened = false;
}
});
});
// Run parent method
PieDataLabel_Series.prototype.drawDataLabels.apply(series);
points.forEach(function (point) {
(point.dataLabels || []).forEach(function (dataLabel, i) {
var _a;
var r = seriesCenter[2] / 2,
dataLabelOptions = dataLabel.options,
distance = PieDataLabel_relativeLength((dataLabelOptions === null || dataLabelOptions === void 0 ? void 0 : dataLabelOptions.distance) || 0,
r);
// Arrange points for collision detection
if (i === 0) {
halves[point.half].push(point);
}
// Avoid long labels squeezing the pie size too far down
if (!PieDataLabel_defined((_a = dataLabelOptions === null || dataLabelOptions === void 0 ? void 0 : dataLabelOptions.style) === null || _a === void 0 ? void 0 : _a.width)) {
if (dataLabel.getBBox().width > maxWidth) {
dataLabel.css({
// Use a fraction of the maxWidth to avoid wrapping
// close to the end of the string.
width: Math.round(maxWidth * 0.7) + 'px'
});
dataLabel.shortened = true;
}
}
dataLabel.dataLabelPosition = _this.getDataLabelPosition(point, distance);
maxLabelDistance = Math.max(maxLabelDistance, distance);
});
});
/* Loop over the points in each half, starting from the top and bottom
* of the pie to detect overlapping labels.
*/
halves.forEach(function (points, halfIdx) {
var length = points.length,
positions = [];
var top,
bottom,
size = 0,
distributionLength;
if (!length) {
return;
}
// Sort by angle
series.sortByAngle(points, halfIdx - 0.5);
// Only do anti-collision when we have dataLabels outside the pie
// and have connectors. (#856)
if (maxLabelDistance > 0) {
top = Math.max(0, centerY - radius - maxLabelDistance);
bottom = Math.min(centerY + radius + maxLabelDistance, chart.plotHeight);
points.forEach(function (point) {
// Check if specific points' label is outside the pie
(point.dataLabels || []).forEach(function (dataLabel) {
var _a;
var labelPosition = dataLabel.dataLabelPosition;
if (labelPosition &&
labelPosition.distance > 0) {
// The point.top depends on point.labelDistance
// value. Used for calculation of y value in getX
// method
labelPosition.top = Math.max(0, centerY - radius - labelPosition.distance);
labelPosition.bottom = Math.min(centerY + radius + labelPosition.distance, chart.plotHeight);
size = dataLabel.getBBox().height || 21;
dataLabel.lineHeight = chart.renderer.fontMetrics(dataLabel.text || dataLabel).h + 2 * dataLabel.padding;
point.distributeBox = {
target: ((((_a = dataLabel.dataLabelPosition) === null || _a === void 0 ? void 0 : _a.natural.y) || 0) -
labelPosition.top +
dataLabel.lineHeight / 2),
size: size,
rank: point.y
};
positions.push(point.distributeBox);
}
});
});
distributionLength = bottom + size - top;
PieDataLabel_distribute(positions, distributionLength, distributionLength / 5);
// Uncomment this to visualize the boxes
/*
points.forEach((point): void => {
const box = point.distributeBox;
point.dlBox?.destroy();
if (box?.pos) {
point.dlBox = chart.renderer.rect(
chart.plotLeft + this.center[0] + (
halfIdx ?
-this.center[2] / 2 - 100 :
this.center[2] / 2
),
chart.plotTop + box.pos,
100,
box.size
)
.attr({
stroke: 'silver',
'stroke-width': 1
})
.add();
}
});
// */
}
// Now the used slots are sorted, fill them up sequentially
points.forEach(function (point) {
(point.dataLabels || []).forEach(function (dataLabel) {
var dataLabelOptions = (dataLabel.options || {}),
distributeBox = point.distributeBox,
labelPosition = dataLabel.dataLabelPosition,
naturalY = (labelPosition === null || labelPosition === void 0 ? void 0 : labelPosition.natural.y) || 0,
connectorPadding = dataLabelOptions
.connectorPadding || 0,
lineHeight = dataLabel.lineHeight || 21,
bBox = dataLabel.getBBox(),
topOffset = (lineHeight - bBox.height) / 2;
var x = 0,
y = naturalY,
visibility = 'inherit';
if (labelPosition) {
if (positions &&
PieDataLabel_defined(distributeBox) &&
labelPosition.distance > 0) {
if (typeof distributeBox.pos === 'undefined') {
visibility = 'hidden';
}
else {
labelHeight = distributeBox.size;
// Find label's y position
y = dataLabelPositioners
.radialDistributionY(point, dataLabel);
}
}
// Find label's x position. The justify option is
// undocumented in the API - preserve support for it
if (dataLabelOptions.justify) {
x = dataLabelPositioners.justify(point, dataLabel, radius, seriesCenter);
}
else {
switch (dataLabelOptions.alignTo) {
case 'connectors':
x = dataLabelPositioners.alignToConnectors(points, halfIdx, plotWidth, plotLeft);
break;
case 'plotEdges':
x = dataLabelPositioners.alignToPlotEdges(dataLabel, halfIdx, plotWidth, plotLeft);
break;
default:
x = dataLabelPositioners.radialDistributionX(series, point, y - topOffset, naturalY, dataLabel);
}
}
// Record the placement and visibility
labelPosition.attribs = {
visibility: visibility,
align: labelPosition.alignment
};
labelPosition.posAttribs = {
x: x +
(dataLabelOptions.x || 0) + // (#12985)
({
left: connectorPadding,
right: -connectorPadding
}[labelPosition.alignment] || 0),
y: y +
(dataLabelOptions.y || 0) - // (#12985)
// Vertically center
lineHeight / 2
};
labelPosition.computed.x = x;
labelPosition.computed.y = y - topOffset;
// Detect overflowing data labels
if (PieDataLabel_pick(dataLabelOptions.crop, true)) {
dataLabelWidth = dataLabel.getBBox().width;
var sideOverflow = void 0;
// Overflow left
if (x - dataLabelWidth < connectorPadding &&
halfIdx === 1 // Left half
) {
sideOverflow = Math.round(dataLabelWidth - x + connectorPadding);
overflow[3] = Math.max(sideOverflow, overflow[3]);
// Overflow right
}
else if (x + dataLabelWidth >
plotWidth - connectorPadding &&
halfIdx === 0 // Right half
) {
sideOverflow = Math.round(x +
dataLabelWidth -
plotWidth +
connectorPadding);
overflow[1] = Math.max(sideOverflow, overflow[1]);
}
// Overflow top
if (y - labelHeight / 2 < 0) {
overflow[0] = Math.max(Math.round(-y + labelHeight / 2), overflow[0]);
// Overflow left
}
else if (y + labelHeight / 2 > plotHeight) {
overflow[2] = Math.max(Math.round(y + labelHeight / 2 - plotHeight), overflow[2]);
}
labelPosition.sideOverflow = sideOverflow;
}
}
}); // For each data label of the point
}); // For each point
}); // For each half
// Do not apply the final placement and draw the connectors until we
// have verified that labels are not spilling over.
if (PieDataLabel_arrayMax(overflow) === 0 ||
this.verifyDataLabelOverflow(overflow)) {
// Place the labels in the final position
this.placeDataLabels();
this.points.forEach(function (point) {
(point.dataLabels || []).forEach(function (dataLabel) {
var _a;
// #8864: every connector can have individual options
var _b = (dataLabel.options || {}),
connectorColor = _b.connectorColor,
_c = _b.connectorWidth,
connectorWidth = _c === void 0 ? 1 : _c,
labelPosition = dataLabel.dataLabelPosition;
// Draw the connector
if (connectorWidth) {
var isNew = void 0;
connector = dataLabel.connector;
if (labelPosition && labelPosition.distance > 0) {
isNew = !connector;
if (!connector) {
dataLabel.connector = connector = chart.renderer
.path()
.addClass('highcharts-data-label-connector ' +
' highcharts-color-' +
point.colorIndex +
(point.className ?
' ' + point.className :
''))
.add(series.dataLabelsGroup);
}
if (!chart.styledMode) {
connector.attr({
'stroke-width': connectorWidth,
'stroke': (connectorColor ||
point.color ||
"#666666" /* Palette.neutralColor60 */)
});
}
connector[isNew ? 'attr' : 'animate']({
d: point.getConnectorPath(dataLabel)
});
connector.attr({
visibility: (_a = labelPosition.attribs) === null || _a === void 0 ? void 0 : _a.visibility
});
}
else if (connector) {
dataLabel.connector = connector.destroy();
}
}
});
});
}
}
/**
* Perform the final placement of the data labels after we have verified
* that they fall within the plot area.
* @private
*/
function placeDataLabels() {
this.points.forEach(function (point) {
(point.dataLabels || []).forEach(function (dataLabel) {
var _a;
var labelPosition = dataLabel.dataLabelPosition;
if (labelPosition) {
// Shorten data labels with ellipsis if they still overflow
// after the pie has reached minSize (#223).
if (labelPosition.sideOverflow) {
dataLabel.css({
width: (Math.max(dataLabel.getBBox().width -
labelPosition.sideOverflow, 0)) + 'px',
textOverflow: ((((_a = dataLabel.options) === null || _a === void 0 ? void 0 : _a.style) || {})
.textOverflow ||
'ellipsis')
});
dataLabel.shortened = true;
}
dataLabel.attr(labelPosition.attribs);
dataLabel[dataLabel.moved ? 'animate' : 'attr'](labelPosition.posAttribs);
dataLabel.moved = true;
}
else if (dataLabel) {
dataLabel.attr({ y: -9999 });
}
});
// Clear for update
delete point.distributeBox;
}, this);
}
/**
* Verify whether the data labels are allowed to draw, or we should run more
* translation and data label positioning to keep them inside the plot area.
* Returns true when data labels are ready to draw.
* @private
*/
function verifyDataLabelOverflow(overflow) {
var center = this.center,
options = this.options,
centerOption = options.center,
minSize = options.minSize || 80;
var newSize = minSize,
// If a size is set, return true and don't try to shrink the pie
// to fit the labels.
ret = options.size !== null;
if (!ret) {
// Handle horizontal size and center
if (centerOption[0] !== null) { // Fixed center
newSize = Math.max(center[2] -
Math.max(overflow[1], overflow[3]), minSize);
}
else { // Auto center
newSize = Math.max(
// Horizontal overflow
center[2] - overflow[1] - overflow[3], minSize);
// Horizontal center
center[0] += (overflow[3] - overflow[1]) / 2;
}
// Handle vertical size and center
if (centerOption[1] !== null) { // Fixed center
newSize = PieDataLabel_clamp(newSize, minSize, center[2] - Math.max(overflow[0], overflow[2]));
}
else { // Auto center
newSize = PieDataLabel_clamp(newSize, minSize,
// Vertical overflow
center[2] - overflow[0] - overflow[2]);
// Vertical center
center[1] += (overflow[0] - overflow[2]) / 2;
}
// If the size must be decreased, we need to run translate and
// drawDataLabels again
if (newSize < center[2]) {
center[2] = newSize;
center[3] = Math.min(// #3632
options.thickness ?
Math.max(0, newSize - options.thickness * 2) :
Math.max(0, PieDataLabel_relativeLength(options.innerSize || 0, newSize)), newSize); // #6647
this.translate(center);
if (this.drawDataLabels) {
this.drawDataLabels();
}
// Else, return true to indicate that the pie and its labels is
// within the plot area
}
else {
ret = true;
}
}
return ret;
}
})(PieDataLabel_ColumnDataLabel || (PieDataLabel_ColumnDataLabel = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var PieDataLabel = (PieDataLabel_ColumnDataLabel);
;// ./code/es5/es-modules/Core/Geometry/GeometryUtilities.js
/* *
*
* (c) 2010-2024 Highsoft AS
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* Namespace
*
* */
var GeometryUtilities;
(function (GeometryUtilities) {
/* *
*
* Functions
*
* */
/**
* Calculates the center between a list of points.
*
* @private
*
* @param {Array<Highcharts.PositionObject>} points
* A list of points to calculate the center of.
*
* @return {Highcharts.PositionObject}
* Calculated center
*/
function getCenterOfPoints(points) {
var sum = points.reduce(function (sum,
point) {
sum.x += point.x;
sum.y += point.y;
return sum;
}, { x: 0, y: 0 });
return {
x: sum.x / points.length,
y: sum.y / points.length
};
}
GeometryUtilities.getCenterOfPoints = getCenterOfPoints;
/**
* Calculates the distance between two points based on their x and y
* coordinates.
*
* @private
*
* @param {Highcharts.PositionObject} p1
* The x and y coordinates of the first point.
*
* @param {Highcharts.PositionObject} p2
* The x and y coordinates of the second point.
*
* @return {number}
* Returns the distance between the points.
*/
function getDistanceBetweenPoints(p1, p2) {
return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
}
GeometryUtilities.getDistanceBetweenPoints = getDistanceBetweenPoints;
/**
* Calculates the angle between two points.
* @todo add unit tests.
* @private
* @param {Highcharts.PositionObject} p1 The first point.
* @param {Highcharts.PositionObject} p2 The second point.
* @return {number} Returns the angle in radians.
*/
function getAngleBetweenPoints(p1, p2) {
return Math.atan2(p2.x - p1.x, p2.y - p1.y);
}
GeometryUtilities.getAngleBetweenPoints = getAngleBetweenPoints;
/**
* Test for point in polygon. Polygon defined as array of [x,y] points.
* @private
* @param {PositionObject} point The point potentially within a polygon.
* @param {Array<Array<number>>} polygon The polygon potentially containing the point.
*/
function pointInPolygon(_a, polygon) {
var x = _a.x,
y = _a.y;
var len = polygon.length;
var i,
j,
inside = false;
for (i = 0, j = len - 1; i < len; j = i++) {
var _b = polygon[i],
x1 = _b[0],
y1 = _b[1],
_c = polygon[j],
x2 = _c[0],
y2 = _c[1];
if (y1 > y !== y2 > y &&
(x < (x2 - x1) *
(y - y1) /
(y2 - y1) +
x1)) {
inside = !inside;
}
}
return inside;
}
GeometryUtilities.pointInPolygon = pointInPolygon;
})(GeometryUtilities || (GeometryUtilities = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Geometry_GeometryUtilities = (GeometryUtilities);
;// ./code/es5/es-modules/Extensions/OverlappingDataLabels.js
/* *
*
* Highcharts module to hide overlapping data labels.
* This module is included in Highcharts.
*
* (c) 2009-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var pointInPolygon = Geometry_GeometryUtilities.pointInPolygon;
var OverlappingDataLabels_addEvent = Core_Utilities.addEvent, OverlappingDataLabels_fireEvent = Core_Utilities.fireEvent, OverlappingDataLabels_objectEach = Core_Utilities.objectEach, OverlappingDataLabels_pick = Core_Utilities.pick;
/* *
*
* Functions
*
* */
/**
* Hide overlapping labels. Labels are moved and faded in and out on zoom to
* provide a smooth visual impression.
*
* @requires modules/overlapping-datalabels
*
* @private
* @function Highcharts.Chart#hideOverlappingLabels
* @param {Array<Highcharts.SVGElement>} labels
* Rendered data labels
*/
function chartHideOverlappingLabels(labels) {
var chart = this,
len = labels.length,
isIntersectRect = function (box1,
box2) { return !(box2.x >= box1.x + box1.width ||
box2.x + box2.width <= box1.x ||
box2.y >= box1.y + box1.height ||
box2.y + box2.height <= box1.y); },
isPolygonOverlap = function (box1Poly,
box2Poly) {
for (var _i = 0,
box1Poly_1 = box1Poly; _i < box1Poly_1.length; _i++) {
var p = box1Poly_1[_i];
if (pointInPolygon({ x: p[0], y: p[1] }, box2Poly)) {
return true;
}
}
return false;
};
/**
* Get the box with its position inside the chart, as opposed to getBBox
* that only reports the position relative to the parent.
*/
function getAbsoluteBox(label) {
var _a,
_b;
if (label && (!label.alignAttr || label.placed)) {
var padding = label.box ? 0 : (label.padding || 0),
pos = label.alignAttr || {
x: label.attr('x'),
y: label.attr('y')
},
bBox = label.getBBox();
label.width = bBox.width;
label.height = bBox.height;
return {
x: pos.x + (((_a = label.parentGroup) === null || _a === void 0 ? void 0 : _a.translateX) || 0) + padding,
y: pos.y + (((_b = label.parentGroup) === null || _b === void 0 ? void 0 : _b.translateY) || 0) + padding,
width: (label.width || 0) - 2 * padding,
height: (label.height || 0) - 2 * padding,
polygon: bBox === null || bBox === void 0 ? void 0 : bBox.polygon
};
}
}
var label,
label1,
label2,
box1,
box2,
isLabelAffected = false;
for (var i = 0; i < len; i++) {
label = labels[i];
if (label) {
// Mark with initial opacity
label.oldOpacity = label.opacity;
label.newOpacity = 1;
label.absoluteBox = getAbsoluteBox(label);
}
}
// Prevent a situation in a gradually rising slope, that each label will
// hide the previous one because the previous one always has lower rank.
labels.sort(function (a, b) { return (b.labelrank || 0) - (a.labelrank || 0); });
// Detect overlapping labels
for (var i = 0; i < len; ++i) {
label1 = labels[i];
box1 = label1 && label1.absoluteBox;
var box1Poly = box1 === null || box1 === void 0 ? void 0 : box1.polygon;
for (var j = i + 1; j < len; ++j) {
label2 = labels[j];
box2 = label2 && label2.absoluteBox;
var toHide = false;
if (box1 &&
box2 &&
label1 !== label2 && // #6465, polar chart with connectEnds
label1.newOpacity !== 0 &&
label2.newOpacity !== 0 &&
// #15863 dataLabels are no longer hidden by translation
label1.visibility !== 'hidden' &&
label2.visibility !== 'hidden') {
var box2Poly = box2.polygon;
// If labels have polygons, only evaluate
// based on polygons
if (box1Poly &&
box2Poly &&
box1Poly !== box2Poly) {
if (isPolygonOverlap(box1Poly, box2Poly)) {
toHide = true;
}
// If there are no polygons, evaluate rectangles coliding
}
else if (isIntersectRect(box1, box2)) {
toHide = true;
}
if (toHide) {
var overlappingLabel = (label1.labelrank < label2.labelrank ?
label1 :
label2),
labelText = overlappingLabel.text;
overlappingLabel.newOpacity = 0;
if (labelText === null || labelText === void 0 ? void 0 : labelText.element.querySelector('textPath')) {
labelText.hide();
}
}
}
}
}
// Hide or show
for (var _i = 0, labels_1 = labels; _i < labels_1.length; _i++) {
var label_1 = labels_1[_i];
if (hideOrShow(label_1, chart)) {
isLabelAffected = true;
}
}
if (isLabelAffected) {
OverlappingDataLabels_fireEvent(chart, 'afterHideAllOverlappingLabels');
}
}
/** @private */
function compose(ChartClass) {
var chartProto = ChartClass.prototype;
if (!chartProto.hideOverlappingLabels) {
chartProto.hideOverlappingLabels = chartHideOverlappingLabels;
OverlappingDataLabels_addEvent(ChartClass, 'render', OverlappingDataLabels_onChartRender);
}
}
/**
* Hide or show labels based on opacity.
*
* @private
* @function hideOrShow
* @param {Highcharts.SVGElement} label
* The label.
* @param {Highcharts.Chart} chart
* The chart that contains the label.
* @return {boolean}
* Whether label is affected
*/
function hideOrShow(label, chart) {
var complete,
newOpacity,
isLabelAffected = false;
if (label) {
newOpacity = label.newOpacity;
if (label.oldOpacity !== newOpacity) {
// Toggle data labels
if (label.hasClass('highcharts-data-label')) {
// Make sure the label is completely hidden to avoid catching
// clicks (#4362)
label[newOpacity ? 'removeClass' : 'addClass']('highcharts-data-label-hidden');
complete = function () {
if (!chart.styledMode) {
label.css({
pointerEvents: newOpacity ? 'auto' : 'none'
});
}
};
isLabelAffected = true;
// Animate or set the opacity
label[label.isOld ? 'animate' : 'attr']({ opacity: newOpacity }, void 0, complete);
OverlappingDataLabels_fireEvent(chart, 'afterHideOverlappingLabel');
// Toggle other labels, tick labels
}
else {
label.attr({
opacity: newOpacity
});
}
}
label.isOld = true;
}
return isLabelAffected;
}
/**
* Collect potential overlapping data labels. Stack labels probably don't need
* to be considered because they are usually accompanied by data labels that lie
* inside the columns.
* @private
*/
function OverlappingDataLabels_onChartRender() {
var _a;
var chart = this;
var labels = [];
// Consider external label collectors
for (var _i = 0, _b = (chart.labelCollectors || []); _i < _b.length; _i++) {
var collector = _b[_i];
labels = labels.concat(collector());
}
for (var _c = 0, _d = (chart.yAxis || []); _c < _d.length; _c++) {
var yAxis = _d[_c];
if (yAxis.stacking &&
yAxis.options.stackLabels &&
!yAxis.options.stackLabels.allowOverlap) {
OverlappingDataLabels_objectEach(yAxis.stacking.stacks, function (stack) {
OverlappingDataLabels_objectEach(stack, function (stackItem) {
if (stackItem.label) {
labels.push(stackItem.label);
}
});
});
}
}
for (var _e = 0, _f = (chart.series || []); _e < _f.length; _e++) {
var series = _f[_e];
if (series.visible && ((_a = series.hasDataLabels) === null || _a === void 0 ? void 0 : _a.call(series))) { // #3866
var push = function (points) {
var _loop_1 = function (point) {
if (point.visible) {
(point.dataLabels || []).forEach(function (label) {
var _a,
_b;
var options = label.options || {};
label.labelrank = OverlappingDataLabels_pick(options.labelrank, point.labelrank, (_a = point.shapeArgs) === null || _a === void 0 ? void 0 : _a.height); // #4118
// Allow overlap if the option is explicitly true
if (
// #13449
(_b = options.allowOverlap) !== null && _b !== void 0 ? _b :
// Pie labels outside have a separate placement
// logic, skip the overlap logic
Number(options.distance) > 0) {
label.oldOpacity = label.opacity;
label.newOpacity = 1;
hideOrShow(label, chart);
// Do not allow overlap
}
else {
labels.push(label);
}
});
}
};
for (var _i = 0, points_1 = points; _i < points_1.length; _i++) {
var point = points_1[_i];
_loop_1(point);
}
};
push(series.nodes || []);
push(series.points);
}
}
this.hideOverlappingLabels(labels);
}
/* *
*
* Default Export
*
* */
var OverlappingDataLabels = {
compose: compose
};
/* harmony default export */ var Extensions_OverlappingDataLabels = (OverlappingDataLabels);
;// ./code/es5/es-modules/Extensions/BorderRadius.js
/* *
*
* Highcharts Border Radius module
*
* Author: Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var BorderRadius_spreadArray = (undefined && undefined.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var BorderRadius_defaultOptions = Defaults.defaultOptions;
var BorderRadius_noop = Core_Globals.noop;
var BorderRadius_addEvent = Core_Utilities.addEvent, BorderRadius_extend = Core_Utilities.extend, BorderRadius_isObject = Core_Utilities.isObject, BorderRadius_merge = Core_Utilities.merge, BorderRadius_relativeLength = Core_Utilities.relativeLength;
/* *
*
* Constants
*
* */
var defaultBorderRadiusOptions = {
radius: 0,
scope: 'stack',
where: void 0
};
/* *
*
* Variables
*
* */
var oldArc = BorderRadius_noop;
var oldRoundedRect = BorderRadius_noop;
/* *
*
* Functions
*
* */
/**
* @private
*/
function applyBorderRadius(path, i, r) {
var a = path[i];
var b = path[i + 1];
if (b[0] === 'Z') {
b = path[0];
}
var line,
arc,
fromLineToArc;
// From straight line to arc
if ((a[0] === 'M' || a[0] === 'L') && b[0] === 'A') {
line = a;
arc = b;
fromLineToArc = true;
// From arc to straight line
}
else if (a[0] === 'A' && (b[0] === 'M' || b[0] === 'L')) {
line = b;
arc = a;
}
if (line && arc && arc.params) {
var bigR = arc[1],
// In our use cases, outer pie slice arcs are clockwise and inner
// arcs (donut/sunburst etc) are anti-clockwise
clockwise = arc[5], params = arc.params, start = params.start, end = params.end, cx = params.cx, cy = params.cy;
// Some geometric constants
var relativeR = clockwise ? (bigR - r) : (bigR + r),
// The angle, on the big arc, that the border radius arc takes up
angleOfBorderRadius = relativeR ? Math.asin(r / relativeR) : 0,
angleOffset = clockwise ?
angleOfBorderRadius :
-angleOfBorderRadius,
// The distance along the radius of the big arc to the starting
// point of the small border radius arc
distanceBigCenterToStartArc = (Math.cos(angleOfBorderRadius) *
relativeR);
// From line to arc
if (fromLineToArc) {
// Update the cache
params.start = start + angleOffset;
// First move to the start position at the radial line. We want to
// start one borderRadius closer to the center.
line[1] = cx + distanceBigCenterToStartArc * Math.cos(start);
line[2] = cy + distanceBigCenterToStartArc * Math.sin(start);
// Now draw an arc towards the point where the small circle touches
// the great circle.
path.splice(i + 1, 0, [
'A',
r,
r,
0, // Slanting,
0, // Long arc
1, // Clockwise
cx + bigR * Math.cos(params.start),
cy + bigR * Math.sin(params.start)
]);
// From arc to line
}
else {
// Update the cache
params.end = end - angleOffset;
// End the big arc a bit earlier
arc[6] = cx + bigR * Math.cos(params.end);
arc[7] = cy + bigR * Math.sin(params.end);
// Draw a small arc towards a point on the end angle, but one
// borderRadius closer to the center relative to the perimeter.
path.splice(i + 1, 0, [
'A',
r,
r,
0,
0,
1,
cx + distanceBigCenterToStartArc * Math.cos(end),
cy + distanceBigCenterToStartArc * Math.sin(end)
]);
}
// Long or short arc must be reconsidered because we have modified the
// start and end points
arc[4] = Math.abs(params.end - params.start) < Math.PI ? 0 : 1;
}
}
/**
* Extend arc with borderRadius.
* @private
*/
function BorderRadius_arc(x, y, w, h, options) {
if (options === void 0) { options = {}; }
var path = oldArc(x,
y,
w,
h,
options),
_a = options.innerR,
innerR = _a === void 0 ? 0 : _a,
_b = options.r,
r = _b === void 0 ? w : _b,
_c = options.start,
start = _c === void 0 ? 0 : _c,
_d = options.end,
end = _d === void 0 ? 0 : _d;
if (options.open || !options.borderRadius) {
return path;
}
var alpha = end - start,
sinHalfAlpha = Math.sin(alpha / 2),
borderRadius = Math.max(Math.min(BorderRadius_relativeLength(options.borderRadius || 0,
r - innerR),
// Cap to half the sector radius
(r - innerR) / 2,
// For smaller pie slices, cap to the largest small circle that
// can be fitted within the sector
(r * sinHalfAlpha) / (1 + sinHalfAlpha)), 0),
// For the inner radius, we need an extra cap because the inner arc
// is shorter than the outer arc
innerBorderRadius = Math.min(borderRadius, 2 * (alpha / Math.PI) * innerR);
// Apply turn-by-turn border radius. Start at the end since we're
// splicing in arc segments.
var i = path.length - 1;
while (i--) {
applyBorderRadius(path, i, i > 1 ? innerBorderRadius : borderRadius);
}
return path;
}
/** @private */
function seriesOnAfterColumnTranslate() {
var _a,
_b;
if (this.options.borderRadius &&
!(this.chart.is3d && this.chart.is3d())) {
var _c = this,
options = _c.options,
yAxis = _c.yAxis,
percent = options.stacking === 'percent',
seriesDefault = (_b = (_a = BorderRadius_defaultOptions.plotOptions) === null || _a === void 0 ? void 0 : _a[this.type]) === null || _b === void 0 ? void 0 : _b.borderRadius,
borderRadius = optionsToObject(options.borderRadius,
BorderRadius_isObject(seriesDefault) ? seriesDefault : {}),
reversed = yAxis.options.reversed;
for (var _i = 0, _d = this.points; _i < _d.length; _i++) {
var point = _d[_i];
var shapeArgs = point.shapeArgs;
if (point.shapeType === 'roundedRect' && shapeArgs) {
var _e = shapeArgs.width,
width = _e === void 0 ? 0 : _e,
_f = shapeArgs.height,
height = _f === void 0 ? 0 : _f,
_g = shapeArgs.y,
y = _g === void 0 ? 0 : _g;
var brBoxY = y,
brBoxHeight = height;
// It would be nice to refactor StackItem.getStackBox/
// setOffset so that we could get a reliable box out of
// it. Currently it is close if we remove the label
// offset, but we still need to run crispCol and also
// flip it if inverted, so atm it is simpler to do it
// like the below.
if (borderRadius.scope === 'stack' &&
point.stackTotal) {
var stackEnd = yAxis.translate(percent ? 100 : point.stackTotal,
false,
true,
false,
true),
stackThreshold = yAxis.translate(options.threshold || 0,
false,
true,
false,
true),
box = this.crispCol(0,
Math.min(stackEnd,
stackThreshold), 0,
Math.abs(stackEnd - stackThreshold));
brBoxY = box.y;
brBoxHeight = box.height;
}
var flip = (point.negative ? -1 : 1) *
(reversed ? -1 : 1) === -1;
// Handle the where option
var where = borderRadius.where;
// Waterfall, hanging columns should have rounding on
// all sides
if (!where &&
this.is('waterfall') &&
Math.abs((point.yBottom || 0) -
(this.translatedThreshold || 0)) > this.borderWidth) {
where = 'all';
}
if (!where) {
where = 'end';
}
// Get the radius
var r = Math.min(BorderRadius_relativeLength(borderRadius.radius,
width),
width / 2,
// Cap to the height, but not if where is `end`
where === 'all' ? height / 2 : Infinity) || 0;
// If the `where` option is 'end', cut off the
// rectangles by making the border-radius box one r
// greater, so that the imaginary radius falls outside
// the rectangle.
if (where === 'end') {
if (flip) {
brBoxY -= r;
brBoxHeight += r;
}
else {
brBoxHeight += r;
}
}
BorderRadius_extend(shapeArgs, { brBoxHeight: brBoxHeight, brBoxY: brBoxY, r: r });
}
}
}
}
/** @private */
function BorderRadius_compose(SeriesClass, SVGElementClass, SVGRendererClass) {
var PieSeriesClass = SeriesClass.types.pie;
if (!SVGElementClass.symbolCustomAttribs.includes('borderRadius')) {
var symbols = SVGRendererClass.prototype.symbols;
BorderRadius_addEvent(SeriesClass, 'afterColumnTranslate', seriesOnAfterColumnTranslate, {
// After columnrange and polar column modifications
order: 9
});
BorderRadius_addEvent(PieSeriesClass, 'afterTranslate', pieSeriesOnAfterTranslate);
SVGElementClass.symbolCustomAttribs.push('borderRadius', 'brBoxHeight', 'brBoxY');
oldArc = symbols.arc;
oldRoundedRect = symbols.roundedRect;
symbols.arc = BorderRadius_arc;
symbols.roundedRect = BorderRadius_roundedRect;
}
}
/** @private */
function optionsToObject(options, seriesBROptions) {
if (!BorderRadius_isObject(options)) {
options = { radius: options || 0 };
}
return BorderRadius_merge(defaultBorderRadiusOptions, seriesBROptions, options);
}
/** @private */
function pieSeriesOnAfterTranslate() {
var borderRadius = optionsToObject(this.options.borderRadius);
for (var _i = 0, _a = this.points; _i < _a.length; _i++) {
var point = _a[_i];
var shapeArgs = point.shapeArgs;
if (shapeArgs) {
shapeArgs.borderRadius = BorderRadius_relativeLength(borderRadius.radius, (shapeArgs.r || 0) - ((shapeArgs.innerR) || 0));
}
}
}
/**
* Extend roundedRect with individual cutting through rOffset.
* @private
*/
function BorderRadius_roundedRect(x, y, width, height, options) {
if (options === void 0) { options = {}; }
var path = oldRoundedRect(x,
y,
width,
height,
options),
_a = options.r,
r = _a === void 0 ? 0 : _a,
_b = options.brBoxHeight,
brBoxHeight = _b === void 0 ? height : _b,
_c = options.brBoxY,
brBoxY = _c === void 0 ? y : _c,
brOffsetTop = y - brBoxY,
brOffsetBtm = (brBoxY + brBoxHeight) - (y + height),
// When the distance to the border-radius box is greater than the r
// itself, it means no border radius. The -0.1 accounts for float
// rounding errors.
rTop = (brOffsetTop - r) > -0.1 ? 0 : r,
rBtm = (brOffsetBtm - r) > -0.1 ? 0 : r,
cutTop = Math.max(rTop && brOffsetTop, 0),
cutBtm = Math.max(rBtm && brOffsetBtm, 0);
/*
The naming of control points:
/ a -------- b \
/ \
h c
| |
| |
| |
g d
\ /
\ f -------- e /
*/
var a = [x + rTop,
y],
b = [x + width - rTop,
y],
c = [x + width,
y + rTop],
d = [
x + width,
y + height - rBtm
],
e = [
x + width - rBtm,
y + height
],
f = [x + rBtm,
y + height],
g = [x,
y + height - rBtm],
h = [x,
y + rTop];
var applyPythagoras = function (r,
altitude) { return Math.sqrt(Math.pow(r, 2) - Math.pow(altitude, 2)); };
// Inside stacks, cut off part of the top
if (cutTop) {
var base = applyPythagoras(rTop,
rTop - cutTop);
a[0] -= base;
b[0] += base;
c[1] = h[1] = y + rTop - cutTop;
}
// Column is lower than the radius. Cut off bottom inside the top
// radius.
if (height < rTop - cutTop) {
var base = applyPythagoras(rTop,
rTop - cutTop - height);
c[0] = d[0] = x + width - rTop + base;
e[0] = Math.min(c[0], e[0]);
f[0] = Math.max(d[0], f[0]);
g[0] = h[0] = x + rTop - base;
c[1] = h[1] = y + height;
}
// Inside stacks, cut off part of the bottom
if (cutBtm) {
var base = applyPythagoras(rBtm,
rBtm - cutBtm);
e[0] += base;
f[0] -= base;
d[1] = g[1] = y + height - rBtm + cutBtm;
}
// Cut off top inside the bottom radius
if (height < rBtm - cutBtm) {
var base = applyPythagoras(rBtm,
rBtm - cutBtm - height);
c[0] = d[0] = x + width - rBtm + base;
b[0] = Math.min(c[0], b[0]);
a[0] = Math.max(d[0], a[0]);
g[0] = h[0] = x + rBtm - base;
d[1] = g[1] = y;
}
// Preserve the box for data labels
path.length = 0;
path.push(BorderRadius_spreadArray(['M'], a, true), BorderRadius_spreadArray(['L'], b, true), BorderRadius_spreadArray(['A', rTop, rTop, 0, 0, 1], c, true), BorderRadius_spreadArray(['L'], d, true), BorderRadius_spreadArray(['A', rBtm, rBtm, 0, 0, 1], e, true), BorderRadius_spreadArray(['L'], f, true), BorderRadius_spreadArray(['A', rBtm, rBtm, 0, 0, 1], g, true), BorderRadius_spreadArray(['L'], h, true), BorderRadius_spreadArray(['A', rTop, rTop, 0, 0, 1], a, true), ['Z']);
return path;
}
/* *
*
* Default Export
*
* */
var BorderRadius = {
compose: BorderRadius_compose,
optionsToObject: optionsToObject
};
/* harmony default export */ var Extensions_BorderRadius = (BorderRadius);
/* *
*
* API Declarations
*
* */
/**
* Detailed options for border radius.
*
* @sample {highcharts} highcharts/plotoptions/column-borderradius/
* Rounded columns
* @sample highcharts/plotoptions/series-border-radius
* Column and pie with rounded border
*
* @interface Highcharts.BorderRadiusOptionsObject
*/ /**
* The border radius. A number signifies pixels. A percentage string, like for
* example `50%`, signifies a relative size. For columns this is relative to the
* column width, for pies it is relative to the radius and the inner radius.
*
* @name Highcharts.BorderRadiusOptionsObject#radius
* @type {string|number}
*/ /**
* The scope of the rounding for column charts. In a stacked column chart, the
* value `point` means each single point will get rounded corners. The value
* `stack` means the rounding will apply to the full stack, so that only points
* close to the top or bottom will receive rounding.
*
* @name Highcharts.BorderRadiusOptionsObject#scope
* @validvalue ["point", "stack"]
* @type {string}
*/ /**
* For column charts, where in the point or stack to apply rounding. The `end`
* value means only those corners at the point value will be rounded, leaving
* the corners at the base or threshold unrounded. This is the most intuitive
* behaviour. The `all` value means also the base will be rounded.
*
* @name Highcharts.BorderRadiusOptionsObject#where
* @validvalue ["all", "end"]
* @type {string}
* @default end
*/
(''); // Keeps doclets above in JS file
;// ./code/es5/es-modules/Core/Responsive.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var Responsive_diffObjects = Core_Utilities.diffObjects, Responsive_extend = Core_Utilities.extend, Responsive_find = Core_Utilities.find, Responsive_merge = Core_Utilities.merge, Responsive_pick = Core_Utilities.pick, Responsive_uniqueKey = Core_Utilities.uniqueKey;
/* *
*
* Composition
*
* */
var Responsive;
(function (Responsive) {
/* *
*
* Declarations
*
* */
/* *
*
* Functions
*
* */
/**
* @private
*/
function compose(ChartClass) {
var chartProto = ChartClass.prototype;
if (!chartProto.matchResponsiveRule) {
Responsive_extend(chartProto, {
matchResponsiveRule: matchResponsiveRule,
setResponsive: setResponsive
});
}
return ChartClass;
}
Responsive.compose = compose;
/**
* Handle a single responsiveness rule.
*
* @private
* @function Highcharts.Chart#matchResponsiveRule
* @param {Highcharts.ResponsiveRulesOptions} rule
* @param {Array<string>} matches
*/
function matchResponsiveRule(rule, matches) {
var condition = rule.condition,
fn = condition.callback || function () {
return (this.chartWidth <= Responsive_pick(condition.maxWidth,
Number.MAX_VALUE) &&
this.chartHeight <= Responsive_pick(condition.maxHeight,
Number.MAX_VALUE) &&
this.chartWidth >= Responsive_pick(condition.minWidth, 0) &&
this.chartHeight >= Responsive_pick(condition.minHeight, 0));
};
if (fn.call(this)) {
matches.push(rule._id);
}
}
/**
* Update the chart based on the current chart/document size and options
* for responsiveness.
*
* @private
* @function Highcharts.Chart#setResponsive
* @param {boolean} [redraw=true]
* @param {boolean} [reset=false]
* Reset by un-applying all rules. Chart.update resets all rules before
* applying updated options.
*/
function setResponsive(redraw, reset) {
var _this = this;
var options = this.options.responsive,
currentResponsive = this.currentResponsive;
var ruleIds = [],
undoOptions;
if (!reset && options && options.rules) {
options.rules.forEach(function (rule) {
if (typeof rule._id === 'undefined') {
rule._id = Responsive_uniqueKey();
}
_this.matchResponsiveRule(rule, ruleIds /* , redraw */);
}, this);
}
// Merge matching rules
var mergedOptions = Responsive_merge.apply(void 0,
ruleIds
.map(function (ruleId) { return Responsive_find((options || {}).rules || [],
function (rule) { return (rule._id === ruleId); }); })
.map(function (rule) { return (rule && rule.chartOptions); }));
mergedOptions.isResponsiveOptions = true;
// Stringified key for the rules that currently apply.
ruleIds = (ruleIds.toString() || void 0);
var currentRuleIds = (currentResponsive && currentResponsive.ruleIds);
// Changes in what rules apply
if (ruleIds !== currentRuleIds) {
// Undo previous rules. Before we apply a new set of rules, we
// need to roll back completely to base options (#6291).
if (currentResponsive) {
this.currentResponsive = void 0;
this.updatingResponsive = true;
this.update(currentResponsive.undoOptions, redraw, true);
this.updatingResponsive = false;
}
if (ruleIds) {
// Get undo-options for matching rules. The `undoOptions``
// hold the current values before they are changed by the
// `mergedOptions`.
undoOptions = Responsive_diffObjects(mergedOptions, this.options, true, this.collectionsWithUpdate);
undoOptions.isResponsiveOptions = true;
this.currentResponsive = {
ruleIds: ruleIds,
mergedOptions: mergedOptions,
undoOptions: undoOptions
};
if (!this.updatingResponsive) {
this.update(mergedOptions, redraw, true);
}
}
else {
this.currentResponsive = void 0;
}
}
}
})(Responsive || (Responsive = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Core_Responsive = (Responsive);
/* *
*
* API Declarations
*
* */
/**
* A callback function to gain complete control on when the responsive rule
* applies.
*
* @callback Highcharts.ResponsiveCallbackFunction
*
* @param {Highcharts.Chart} this
* Chart context.
*
* @return {boolean}
* Return `true` if it applies.
*/
(''); // Keeps doclets above in JS file
/* *
*
* API Options
*
* */
/**
* Allows setting a set of rules to apply for different screen or chart
* sizes. Each rule specifies additional chart options.
*
* @sample {highstock} stock/demo/responsive/
* Stock chart
* @sample highcharts/responsive/axis/
* Axis
* @sample highcharts/responsive/legend/
* Legend
* @sample highcharts/responsive/classname/
* Class name
*
* @since 5.0.0
* @apioption responsive
*/
/**
* A set of rules for responsive settings. The rules are executed from
* the top down.
*
* @sample {highcharts} highcharts/responsive/axis/
* Axis changes
* @sample {highstock} highcharts/responsive/axis/
* Axis changes
* @sample {highmaps} highcharts/responsive/axis/
* Axis changes
*
* @type {Array<*>}
* @since 5.0.0
* @apioption responsive.rules
*/
/**
* A full set of chart options to apply as overrides to the general
* chart options. The chart options are applied when the given rule
* is active.
*
* A special case is configuration objects that take arrays, for example
* [xAxis](#xAxis), [yAxis](#yAxis) or [series](#series). For these
* collections, an `id` option is used to map the new option set to
* an existing object. If an existing object of the same id is not found,
* the item of the same index updated. So for example, setting `chartOptions`
* with two series items without an `id`, will cause the existing chart's
* two series to be updated with respective options.
*
* @sample {highstock} stock/demo/responsive/
* Stock chart
* @sample highcharts/responsive/axis/
* Axis
* @sample highcharts/responsive/legend/
* Legend
* @sample highcharts/responsive/classname/
* Class name
*
* @type {Highcharts.Options}
* @since 5.0.0
* @apioption responsive.rules.chartOptions
*/
/**
* Under which conditions the rule applies.
*
* @since 5.0.0
* @apioption responsive.rules.condition
*/
/**
* A callback function to gain complete control on when the responsive
* rule applies. Return `true` if it applies. This opens for checking
* against other metrics than the chart size, for example the document
* size or other elements.
*
* @type {Highcharts.ResponsiveCallbackFunction}
* @since 5.0.0
* @context Highcharts.Chart
* @apioption responsive.rules.condition.callback
*/
/**
* The responsive rule applies if the chart height is less than this.
*
* @type {number}
* @since 5.0.0
* @apioption responsive.rules.condition.maxHeight
*/
/**
* The responsive rule applies if the chart width is less than this.
*
* @sample highcharts/responsive/axis/
* Max width is 500
*
* @type {number}
* @since 5.0.0
* @apioption responsive.rules.condition.maxWidth
*/
/**
* The responsive rule applies if the chart height is greater than this.
*
* @type {number}
* @default 0
* @since 5.0.0
* @apioption responsive.rules.condition.minHeight
*/
/**
* The responsive rule applies if the chart width is greater than this.
*
* @type {number}
* @default 0
* @since 5.0.0
* @apioption responsive.rules.condition.minWidth
*/
(''); // Keeps doclets above in JS file
;// ./code/es5/es-modules/masters/highcharts.src.js
var G = Core_Globals;
// Classes
G.AST = HTML_AST;
G.Axis = Axis_Axis;
G.Chart = Chart_Chart;
G.Color = Color_Color;
G.DataLabel = Series_DataLabel;
G.DataTableCore = Data_DataTableCore;
G.Fx = Animation_Fx;
G.HTMLElement = HTML_HTMLElement;
G.Legend = Legend_Legend;
G.LegendSymbol = Legend_LegendSymbol;
G.OverlappingDataLabels = G.OverlappingDataLabels || Extensions_OverlappingDataLabels;
G.PlotLineOrBand = PlotLineOrBand_PlotLineOrBand;
G.Point = Series_Point;
G.Pointer = Core_Pointer;
G.RendererRegistry = Renderer_RendererRegistry;
G.Series = Series_Series;
G.SeriesRegistry = Series_SeriesRegistry;
G.StackItem = Stacking_StackItem;
G.SVGElement = SVG_SVGElement;
G.SVGRenderer = SVG_SVGRenderer;
G.Templating = Core_Templating;
G.Tick = Axis_Tick;
G.Time = Core_Time;
G.Tooltip = Core_Tooltip;
// Utilities
G.animate = AnimationUtilities.animate;
G.animObject = AnimationUtilities.animObject;
G.chart = Chart_Chart.chart;
G.color = Color_Color.parse;
G.dateFormat = Core_Templating.dateFormat;
G.defaultOptions = Defaults.defaultOptions;
G.distribute = Renderer_RendererUtilities.distribute;
G.format = Core_Templating.format;
G.getDeferredAnimation = AnimationUtilities.getDeferredAnimation;
G.getOptions = Defaults.getOptions;
G.numberFormat = Core_Templating.numberFormat;
G.seriesType = Series_SeriesRegistry.seriesType;
G.setAnimation = AnimationUtilities.setAnimation;
G.setOptions = Defaults.setOptions;
G.stop = AnimationUtilities.stop;
G.time = Defaults.defaultTime;
G.timers = Animation_Fx.timers;
// Compositions
Extensions_BorderRadius.compose(G.Series, G.SVGElement, G.SVGRenderer);
Column_ColumnDataLabel.compose(G.Series.types.column);
Series_DataLabel.compose(G.Series);
Axis_DateTimeAxis.compose(G.Axis);
HTML_HTMLElement.compose(G.SVGRenderer);
Legend_Legend.compose(G.Chart);
Axis_LogarithmicAxis.compose(G.Axis);
Extensions_OverlappingDataLabels.compose(G.Chart);
PieDataLabel.compose(G.Series.types.pie);
PlotLineOrBand_PlotLineOrBand.compose(G.Chart, G.Axis);
Core_Pointer.compose(G.Chart);
Core_Responsive.compose(G.Chart);
Extensions_ScrollablePlotArea.compose(G.Axis, G.Chart, G.Series);
Stacking_StackingAxis.compose(G.Axis, G.Chart, G.Series);
Core_Tooltip.compose(G.Pointer);
Core_Utilities.extend(G, Core_Utilities);
// Default Export
/* harmony default export */ var highcharts_src = (G);
;// ./code/es5/es-modules/Series/DataModifyComposition.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var pointTooltipFormatter = Series_Point.prototype.tooltipFormatter;
var DataModifyComposition_addEvent = Core_Utilities.addEvent, DataModifyComposition_arrayMax = Core_Utilities.arrayMax, DataModifyComposition_arrayMin = Core_Utilities.arrayMin, DataModifyComposition_correctFloat = Core_Utilities.correctFloat, DataModifyComposition_defined = Core_Utilities.defined, DataModifyComposition_isArray = Core_Utilities.isArray, DataModifyComposition_isNumber = Core_Utilities.isNumber, DataModifyComposition_isString = Core_Utilities.isString, DataModifyComposition_pick = Core_Utilities.pick;
/* *
*
* Composition
*
* */
var DataModifyComposition;
(function (DataModifyComposition) {
/* *
*
* Declarations
*
* */
/* *
*
* Functions
*
* */
/**
* Extends the series, axis and point classes with
* compare and cumulative support.
*
* @private
*
* @param SeriesClass
* Series class to use.
*
* @param AxisClass
* Axis class to extend.
*
* @param PointClass
* Point class to use.
*/
function compose(SeriesClass, AxisClass, PointClass) {
var axisProto = AxisClass.prototype,
pointProto = PointClass.prototype,
seriesProto = SeriesClass.prototype;
if (!seriesProto.setCompare) {
seriesProto.setCompare = seriesSetCompare;
seriesProto.setCumulative = seriesSetCumulative;
DataModifyComposition_addEvent(SeriesClass, 'afterInit', afterInit);
DataModifyComposition_addEvent(SeriesClass, 'afterGetExtremes', afterGetExtremes);
DataModifyComposition_addEvent(SeriesClass, 'afterProcessData', afterProcessData);
}
if (!axisProto.setCompare) {
axisProto.setCompare = axisSetCompare;
axisProto.setModifier = setModifier;
axisProto.setCumulative = axisSetCumulative;
pointProto.tooltipFormatter = tooltipFormatter;
}
return SeriesClass;
}
DataModifyComposition.compose = compose;
/* ********************************************************************** *
* Start shared compare and cumulative logic *
* ********************************************************************** */
/**
* Shared code for the axis.setCompare() and the axis.setCumulative()
* methods. Inits the 'compare' or the 'cumulative' mode.
* @private
*/
function setModifier(mode, modeState, redraw) {
if (!this.isXAxis) {
this.series.forEach(function (series) {
if (mode === 'compare' &&
typeof modeState !== 'boolean') {
series.setCompare(modeState, false);
}
else if (mode === 'cumulative' &&
!DataModifyComposition_isString(modeState)) {
series.setCumulative(modeState, false);
}
});
if (DataModifyComposition_pick(redraw, true)) {
this.chart.redraw();
}
}
}
/**
* Extend the tooltip formatter by adding support for the point.change
* variable as well as the changeDecimals option.
*
* @ignore
* @function Highcharts.Point#tooltipFormatter
*
* @param {string} pointFormat
*/
function tooltipFormatter(pointFormat) {
var point = this, numberFormatter = point.series.chart.numberFormatter, replace = function (value) {
pointFormat = pointFormat.replace('{point.' + value + '}', (point[value] > 0 && value === 'change' ? '+' : '') +
numberFormatter(point[value], DataModifyComposition_pick(point.series.tooltipOptions.changeDecimals, 2)));
};
if (DataModifyComposition_defined(point.change)) {
replace('change');
}
if (DataModifyComposition_defined(point.cumulativeSum)) {
replace('cumulativeSum');
}
return pointTooltipFormatter.apply(this, [pointFormat]);
}
/**
* Extend series.init by adding a methods to modify the y values used
* for plotting on the y axis. For compare mode, this method is called both
* from the axis when finding dataMin and dataMax,
* and from the series.translate method.
*
* @ignore
* @function Highcharts.Series#init
*/
function afterInit() {
var compare = this.options.compare;
var dataModify;
if (compare === 'percent' ||
compare === 'value' ||
this.options.cumulative) {
dataModify = new Additions(this);
if (compare === 'percent' || compare === 'value') {
// Set comparison mode
dataModify.initCompare(compare);
}
else {
// Set Cumulative Sum mode
dataModify.initCumulative();
}
}
this.dataModify = dataModify;
}
/**
* Adjust the extremes (compare and cumulative modify the data).
* @private
*/
function afterGetExtremes(e) {
var dataExtremes = e.dataExtremes,
activeYData = dataExtremes.activeYData;
if (this.dataModify && dataExtremes) {
var extremes = void 0;
if (this.options.compare) {
extremes = [
this.dataModify.modifyValue(dataExtremes.dataMin),
this.dataModify.modifyValue(dataExtremes.dataMax)
];
}
else if (this.options.cumulative &&
DataModifyComposition_isArray(activeYData) &&
// If only one y visible, sum doesn't change
// so no need to change extremes
activeYData.length >= 2) {
extremes = Additions.getCumulativeExtremes(activeYData);
}
if (extremes) {
dataExtremes.dataMin = DataModifyComposition_arrayMin(extremes);
dataExtremes.dataMax = DataModifyComposition_arrayMax(extremes);
}
}
}
/* ********************************************************************** *
* End shared compare and cumulative logic *
* ********************************************************************** */
/* ********************************************************************** *
* Start value compare logic *
* ********************************************************************** */
/**
* Highcharts Stock only. Set the
* [compare](https://api.highcharts.com/highstock/plotOptions.series.compare)
* mode of the series after render time.
* In most cases it is more useful running
* {@link Axis#setCompare} on the X axis to update all its series.
*
* @function Highcharts.Series#setCompare
*
* @param {string|null} [compare]
* Can be one of `undefined` (default), `null`, `"percent"`
* or `"value"`.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart or to wait for a later call to
* {@link Chart#redraw}.
*/
function seriesSetCompare(compare, redraw) {
// Survive to export, #5485 (and for options generally)
this.options.compare = this.userOptions.compare = compare;
// Fire series.init() that will set or delete series.dataModify
this.update({}, DataModifyComposition_pick(redraw, true));
if (this.dataModify && (compare === 'value' || compare === 'percent')) {
this.dataModify.initCompare(compare);
}
else {
// When disabling, clear the points
this.points.forEach(function (point) {
delete point.change;
});
}
}
/**
* Extend series.processData by finding the first y value in the plot area,
* used for comparing the following values
*
* @ignore
* @function Highcharts.Series#processData
*/
function afterProcessData() {
var series = this,
// For series with more than one value (range, OHLC etc), compare
// against close or the pointValKey (#4922, #3112, #9854)
compareColumn = this.getColumn((series.pointArrayMap &&
(series.options.pointValKey || series.pointValKey)) || 'y',
true);
if (series.xAxis && // Not pies
compareColumn.length &&
series.dataModify) {
var processedXData = series.getColumn('x',
true),
length_1 = series.dataTable.rowCount,
compareStart = series.options.compareStart === true ? 0 : 1;
// Find the first value for comparison
for (var i = 0; i < length_1 - compareStart; i++) {
var compareValue = compareColumn[i];
if (DataModifyComposition_isNumber(compareValue) &&
compareValue !== 0 &&
processedXData[i + compareStart] >= (series.xAxis.min || 0)) {
series.dataModify.compareValue = compareValue;
break;
}
}
}
}
/**
* Highcharts Stock only. Set the compare mode on all series
* belonging to a Y axis.
*
* @see [plotOptions.series.compare](https://api.highcharts.com/highstock/plotOptions.series.compare)
*
* @sample stock/members/axis-setcompare/
* Set compare
*
* @function Highcharts.Axis#setCompare
*
* @param {string|null} [compare]
* The compare mode. Can be one of `undefined` (default), `null`,
* `"value"` or `"percent"`.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart or to wait for a later call to
* {@link Chart#redraw}.
*/
function axisSetCompare(compare, redraw) {
this.setModifier('compare', compare, redraw);
}
/* ********************************************************************** *
* End value compare logic *
* ********************************************************************** */
/* ********************************************************************** *
* Start Cumulative Sum logic, author: Rafal Sebestjanski *
* ********************************************************************** */
/**
* Highcharts Stock only. Set the
* [cumulative](https://api.highcharts.com/highstock/plotOptions.series.cumulative)
* mode of the series after render time.
* In most cases it is more useful running
* {@link Axis#setCumulative} on the Y axis to update all its series.
*
* @function Highcharts.Series#setCumulative
*
* @param {boolean} [cumulative=false]
* Either enable or disable Cumulative Sum mode.
* Can be one of `false` (default) or `true`.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart or to wait for a later call to
* {@link Chart#redraw}.
*/
function seriesSetCumulative(cumulative, redraw) {
// Set default value to false
cumulative = DataModifyComposition_pick(cumulative, false);
// Survive to export, #5485 (and for options generally)
this.options.cumulative = this.userOptions.cumulative = cumulative;
// Fire series.init() that will set or delete series.dataModify
this.update({}, DataModifyComposition_pick(redraw, true));
// If should, turn on the Cumulative Sum mode
if (this.dataModify) {
this.dataModify.initCumulative();
}
else {
// When disabling, clear the points
this.points.forEach(function (point) {
delete point.cumulativeSum;
});
}
}
/**
* Highcharts Stock only. Set the cumulative mode on all series
* belonging to a Y axis.
*
* @see [plotOptions.series.cumulative](https://api.highcharts.com/highstock/plotOptions.series.cumulative)
*
* @sample stock/members/axis-setcumulative/
* Set cumulative
*
* @function Highcharts.Axis#setCumulative
*
* @param {boolean} [cumulative]
* Whether to disable or enable the cumulative mode.
* Can be one of `undefined` (default, treated as `false`),
* `false` or `true`.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart or to wait for a later call to
* {@link Chart#redraw}.
*/
function axisSetCumulative(cumulative, redraw) {
this.setModifier('cumulative', cumulative, redraw);
}
/* *
*
* Classes
*
* */
/**
* @private
*/
var Additions = /** @class */ (function () {
/* *
*
* Constructors
*
* */
/**
* @private
*/
function Additions(series) {
this.series = series;
}
/* *
*
* Functions
*
* */
/**
* @private
*/
Additions.prototype.modifyValue = function () {
return 0;
};
/**
* @ignore
* @function Highcharts.Series#getCumulativeExtremes
*
* @param {Array} [activeYData]
* An array cointaining all the points' y values
* in a visible range.
*/
Additions.getCumulativeExtremes = function (activeYData) {
var cumulativeDataMin = Infinity,
cumulativeDataMax = -Infinity;
activeYData.reduce(function (prev, cur) {
var sum = prev + cur;
cumulativeDataMin = Math.min(cumulativeDataMin, sum, prev);
cumulativeDataMax = Math.max(cumulativeDataMax, sum, prev);
return sum;
});
return [cumulativeDataMin, cumulativeDataMax];
};
/**
* @ignore
* @function Highcharts.Series#initCompare
*
* @param {string} [compare]
* Can be one of `"percent"` or `"value"`.
*/
Additions.prototype.initCompare = function (compare) {
// Set the modifyValue method
this.modifyValue = function (value, index) {
if (value === null) {
value = 0;
}
var compareValue = this.compareValue;
if (typeof value !== 'undefined' &&
typeof compareValue !== 'undefined') { // #2601, #5814
// Get the modified value
if (compare === 'value') {
value -= compareValue;
// Compare percent
}
else {
var compareBase = this.series.options.compareBase;
value = 100 * (value / compareValue) -
(compareBase === 100 ? 0 : 100);
}
// Record for tooltip etc.
if (typeof index !== 'undefined') {
var point = this.series.points[index];
if (point) {
point.change = value;
}
}
return value;
}
return 0;
};
};
/**
* @ignore
* @function Highcharts.Series#initCumulative
*/
Additions.prototype.initCumulative = function () {
// Set the modifyValue method
this.modifyValue = function (value, index) {
if (value === null) {
value = 0;
}
if (value !== void 0 && index !== void 0) {
var prevPoint = index > 0 ?
this.series.points[index - 1] : null;
// Get the modified value
if (prevPoint && prevPoint.cumulativeSum) {
value = DataModifyComposition_correctFloat(prevPoint.cumulativeSum + value);
}
// Record for tooltip etc.
var point = this.series.points[index];
var cumulativeStart = point.series.options.cumulativeStart,
withinRange = point.x <= this.series.xAxis.max &&
point.x >= this.series.xAxis.min;
if (point) {
if (!cumulativeStart || withinRange) {
point.cumulativeSum = value;
}
else {
point.cumulativeSum = void 0;
}
}
return value;
}
return 0;
};
};
return Additions;
}());
DataModifyComposition.Additions = Additions;
})(DataModifyComposition || (DataModifyComposition = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Series_DataModifyComposition = (DataModifyComposition);
/* *
*
* API Options
*
* */
/**
* Compare the values of the series against the first non-null, non-
* zero value in the visible range. The y axis will show percentage
* or absolute change depending on whether `compare` is set to `"percent"`
* or `"value"`. When this is applied to multiple series, it allows
* comparing the development of the series against each other. Adds
* a `change` field to every point object.
*
* @see [compareBase](#plotOptions.series.compareBase)
* @see [Axis.setCompare()](/class-reference/Highcharts.Axis#setCompare)
* @see [Series.setCompare()](/class-reference/Highcharts.Series#setCompare)
*
* @sample {highstock} stock/plotoptions/series-compare-percent/
* Percent
* @sample {highstock} stock/plotoptions/series-compare-value/
* Value
*
* @type {string}
* @since 1.0.1
* @product highstock
* @validvalue ["percent", "value"]
* @apioption plotOptions.series.compare
*/
/**
* Defines if comparison should start from the first point within the visible
* range or should start from the last point **before** the range.
*
* In other words, this flag determines if first point within the visible range
* will have 0% (`compareStart=true`) or should have been already calculated
* according to the previous point (`compareStart=false`).
*
* @sample {highstock} stock/plotoptions/series-comparestart/
* Calculate compare within visible range
*
* @type {boolean}
* @default false
* @since 6.0.0
* @product highstock
* @apioption plotOptions.series.compareStart
*/
/**
* When [compare](#plotOptions.series.compare) is `percent`, this option
* dictates whether to use 0 or 100 as the base of comparison.
*
* @sample {highstock} stock/plotoptions/series-comparebase/
* Compare base is 100
*
* @type {number}
* @default 0
* @since 5.0.6
* @product highstock
* @validvalue [0, 100]
* @apioption plotOptions.series.compareBase
*/
/**
* Cumulative Sum feature replaces points' values with the following formula:
* `sum of all previous points' values + current point's value`.
* Works only for points in a visible range.
* Adds the `cumulativeSum` field to each point object that can be accessed
* e.g. in the [tooltip.pointFormat](https://api.highcharts.com/highstock/tooltip.pointFormat).
*
* With `dataGrouping` enabled, default grouping approximation is set to `sum`.
*
* @see [Axis.setCumulative()](/class-reference/Highcharts.Axis#setCumulative)
* @see [Series.setCumulative()](/class-reference/Highcharts.Series#setCumulative)
*
* @sample {highstock} stock/plotoptions/series-cumulative-sum/
* Cumulative Sum
*
* @type {boolean}
* @default false
* @since 9.3.0
* @product highstock
* @apioption plotOptions.series.cumulative
*/
/**
* Defines if cumulation should start from the first point within the visible
* range or should start from the last point **before** the range.
*
* In other words, this flag determines if first point within the visible range
* will start at 0 (`cumulativeStart=true`) or should have been already calculated
* according to the previous point (`cumulativeStart=false`).
*
* @sample {highstock} stock/plotoptions/series-cumulativestart/
* Cumulative Start
*
* @type {boolean}
* @default false
* @since 11.4.2
* @product highstock
* @apioption plotOptions.series.cumulativeStart
*/
''; // Keeps doclets above in transpiled file
;// ./code/es5/es-modules/Stock/Navigator/ChartNavigatorComposition.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var ChartNavigatorComposition_isTouchDevice = Core_Globals.isTouchDevice;
var ChartNavigatorComposition_addEvent = Core_Utilities.addEvent, ChartNavigatorComposition_merge = Core_Utilities.merge, ChartNavigatorComposition_pick = Core_Utilities.pick;
/* *
*
* Constants
*
* */
var composedMembers = [];
/* *
*
* Variables
*
* */
var NavigatorConstructor;
/* *
*
* Functions
*
* */
/**
* @private
*/
function ChartNavigatorComposition_compose(ChartClass, NavigatorClass) {
if (Core_Utilities.pushUnique(composedMembers, ChartClass)) {
var chartProto = ChartClass.prototype;
NavigatorConstructor = NavigatorClass;
chartProto.callbacks.push(onChartCallback);
ChartNavigatorComposition_addEvent(ChartClass, 'afterAddSeries', onChartAfterAddSeries);
ChartNavigatorComposition_addEvent(ChartClass, 'afterSetChartSize', onChartAfterSetChartSize);
ChartNavigatorComposition_addEvent(ChartClass, 'afterUpdate', onChartAfterUpdate);
ChartNavigatorComposition_addEvent(ChartClass, 'beforeRender', onChartBeforeRender);
ChartNavigatorComposition_addEvent(ChartClass, 'beforeShowResetZoom', onChartBeforeShowResetZoom);
ChartNavigatorComposition_addEvent(ChartClass, 'update', onChartUpdate);
}
}
/**
* Handle adding new series.
* @private
*/
function onChartAfterAddSeries() {
if (this.navigator) {
// Recompute which series should be shown in navigator, and add them
this.navigator.setBaseSeries(null, false);
}
}
/**
* For stock charts, extend the Chart.setChartSize method so that we can set the
* final top position of the navigator once the height of the chart, including
* the legend, is determined. #367. We can't use Chart.getMargins, because
* labels offsets are not calculated yet.
* @private
*/
function onChartAfterSetChartSize() {
var _a;
var legend = this.legend,
navigator = this.navigator;
var legendOptions,
xAxis,
yAxis;
if (navigator) {
legendOptions = legend && legend.options;
xAxis = navigator.xAxis;
yAxis = navigator.yAxis;
var scrollbarHeight = navigator.scrollbarHeight,
scrollButtonSize = navigator.scrollButtonSize;
// Compute the top position
if (this.inverted) {
navigator.left = navigator.opposite ?
this.chartWidth - scrollbarHeight -
navigator.height :
this.spacing[3] + scrollbarHeight;
navigator.top = this.plotTop + scrollButtonSize;
}
else {
navigator.left = ChartNavigatorComposition_pick(xAxis.left, this.plotLeft + scrollButtonSize);
navigator.top = navigator.navigatorOptions.top ||
this.chartHeight -
navigator.height -
scrollbarHeight -
(((_a = this.scrollbar) === null || _a === void 0 ? void 0 : _a.options.margin) || 0) -
this.spacing[2] -
(this.rangeSelector && this.extraBottomMargin ?
this.rangeSelector.getHeight() :
0) -
((legendOptions &&
legendOptions.verticalAlign === 'bottom' &&
legendOptions.layout !== 'proximate' && // #13392
legendOptions.enabled &&
!legendOptions.floating) ?
legend.legendHeight +
ChartNavigatorComposition_pick(legendOptions.margin, 10) :
0) -
(this.titleOffset ? this.titleOffset[2] : 0);
}
if (xAxis && yAxis) { // False if navigator is disabled (#904)
if (this.inverted) {
xAxis.options.left = yAxis.options.left = navigator.left;
}
else {
xAxis.options.top = yAxis.options.top = navigator.top;
}
xAxis.setAxisSize();
yAxis.setAxisSize();
}
}
}
/**
* Initialize navigator, if no scrolling exists yet.
* @private
*/
function onChartAfterUpdate(event) {
if (!this.navigator && !this.scroller &&
(this.options.navigator.enabled ||
this.options.scrollbar.enabled)) {
this.scroller = this.navigator = new NavigatorConstructor(this);
if (ChartNavigatorComposition_pick(event.redraw, true)) {
this.redraw(event.animation); // #7067
}
}
}
/**
* Initialize navigator for stock charts
* @private
*/
function onChartBeforeRender() {
var options = this.options;
if (options.navigator.enabled ||
options.scrollbar.enabled) {
this.scroller = this.navigator = new NavigatorConstructor(this);
}
}
/**
* For Stock charts. For x only zooming, do not to create the zoom button
* because X axis zooming is already allowed by the Navigator and Range
* selector. (#9285)
* @private
*/
function onChartBeforeShowResetZoom() {
var chartOptions = this.options,
navigator = chartOptions.navigator,
rangeSelector = chartOptions.rangeSelector;
if (((navigator && navigator.enabled) ||
(rangeSelector && rangeSelector.enabled)) &&
((!ChartNavigatorComposition_isTouchDevice &&
this.zooming.type === 'x') ||
(ChartNavigatorComposition_isTouchDevice && this.zooming.pinchType === 'x'))) {
return false;
}
}
/**
* @private
*/
function onChartCallback(chart) {
var navigator = chart.navigator;
// Initialize the navigator
if (navigator && chart.xAxis[0]) {
var extremes = chart.xAxis[0].getExtremes();
navigator.render(extremes.min, extremes.max);
}
}
/**
* Merge options, if no scrolling exists yet
* @private
*/
function onChartUpdate(e) {
var navigatorOptions = (e.options.navigator || {}),
scrollbarOptions = (e.options.scrollbar || {});
if (!this.navigator && !this.scroller &&
(navigatorOptions.enabled || scrollbarOptions.enabled)) {
ChartNavigatorComposition_merge(true, this.options.navigator, navigatorOptions);
ChartNavigatorComposition_merge(true, this.options.scrollbar, scrollbarOptions);
delete e.options.navigator;
delete e.options.scrollbar;
}
}
/* *
*
* Default Export
*
* */
var ChartNavigatorComposition = {
compose: ChartNavigatorComposition_compose
};
/* harmony default export */ var Navigator_ChartNavigatorComposition = (ChartNavigatorComposition);
;// ./code/es5/es-modules/Core/Axis/NavigatorAxisComposition.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var NavigatorAxisComposition_isTouchDevice = Core_Globals.isTouchDevice;
var NavigatorAxisComposition_addEvent = Core_Utilities.addEvent, NavigatorAxisComposition_correctFloat = Core_Utilities.correctFloat, NavigatorAxisComposition_defined = Core_Utilities.defined, NavigatorAxisComposition_isNumber = Core_Utilities.isNumber, NavigatorAxisComposition_pick = Core_Utilities.pick;
/* *
*
* Functions
*
* */
/**
* @private
*/
function NavigatorAxisComposition_onAxisInit() {
var axis = this;
if (!axis.navigatorAxis) {
axis.navigatorAxis = new NavigatorAxisAdditions(axis);
}
}
/**
* For Stock charts, override selection zooming with some special features
* because X axis zooming is already allowed by the Navigator and Range
* selector.
* @private
*/
function onAxisSetExtremes(e) {
var axis = this,
chart = axis.chart,
chartOptions = chart.options,
navigator = chartOptions.navigator,
navigatorAxis = axis.navigatorAxis,
pinchType = chart.zooming.pinchType,
rangeSelector = chartOptions.rangeSelector,
zoomType = chart.zooming.type;
var zoomed;
if (axis.isXAxis &&
((navigator === null || navigator === void 0 ? void 0 : navigator.enabled) || (rangeSelector === null || rangeSelector === void 0 ? void 0 : rangeSelector.enabled))) {
// For y only zooming, ignore the X axis completely
if (zoomType === 'y' && e.trigger === 'zoom') {
zoomed = false;
// For xy zooming, record the state of the zoom before zoom selection,
// then when the reset button is pressed, revert to this state. This
// should apply only if the chart is initialized with a range (#6612),
// otherwise zoom all the way out.
}
else if (((e.trigger === 'zoom' && zoomType === 'xy') ||
(NavigatorAxisComposition_isTouchDevice && pinchType === 'xy')) &&
axis.options.range) {
var previousZoom = navigatorAxis.previousZoom;
// Minimum defined, zooming in
if (NavigatorAxisComposition_defined(e.min)) {
navigatorAxis.previousZoom = [axis.min, axis.max];
// Minimum undefined, resetting zoom
}
else if (previousZoom) {
e.min = previousZoom[0];
e.max = previousZoom[1];
navigatorAxis.previousZoom = void 0;
}
}
}
if (typeof zoomed !== 'undefined') {
e.preventDefault();
}
}
/* *
*
* Class
*
* */
/**
* @private
* @class
*/
var NavigatorAxisAdditions = /** @class */ (function () {
/* *
*
* Constructors
*
* */
function NavigatorAxisAdditions(axis) {
this.axis = axis;
}
/* *
*
* Static Functions
*
* */
/**
* @private
*/
NavigatorAxisAdditions.compose = function (AxisClass) {
if (!AxisClass.keepProps.includes('navigatorAxis')) {
AxisClass.keepProps.push('navigatorAxis');
NavigatorAxisComposition_addEvent(AxisClass, 'init', NavigatorAxisComposition_onAxisInit);
NavigatorAxisComposition_addEvent(AxisClass, 'setExtremes', onAxisSetExtremes);
}
};
/* *
*
* Functions
*
* */
/**
* @private
*/
NavigatorAxisAdditions.prototype.destroy = function () {
this.axis = void 0;
};
/**
* Add logic to normalize the zoomed range in order to preserve the pressed
* state of range selector buttons
*
* @private
* @function Highcharts.Axis#toFixedRange
*/
NavigatorAxisAdditions.prototype.toFixedRange = function (pxMin, pxMax, fixedMin, fixedMax) {
var axis = this.axis,
halfPointRange = (axis.pointRange || 0) / 2;
var newMin = NavigatorAxisComposition_pick(fixedMin,
axis.translate(pxMin,
true, !axis.horiz)),
newMax = NavigatorAxisComposition_pick(fixedMax,
axis.translate(pxMax,
true, !axis.horiz));
// Add/remove half point range to/from the extremes (#1172)
if (!NavigatorAxisComposition_defined(fixedMin)) {
newMin = NavigatorAxisComposition_correctFloat(newMin + halfPointRange);
}
if (!NavigatorAxisComposition_defined(fixedMax)) {
newMax = NavigatorAxisComposition_correctFloat(newMax - halfPointRange);
}
if (!NavigatorAxisComposition_isNumber(newMin) || !NavigatorAxisComposition_isNumber(newMax)) { // #1195, #7411
newMin = newMax = void 0;
}
return {
min: newMin,
max: newMax
};
};
return NavigatorAxisAdditions;
}());
/* *
*
* Default Export
*
* */
/* harmony default export */ var NavigatorAxisComposition = (NavigatorAxisAdditions);
;// ./code/es5/es-modules/Stock/Navigator/NavigatorDefaults.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var NavigatorDefaults_color = Color_Color.parse;
var NavigatorDefaults_seriesTypes = Series_SeriesRegistry.seriesTypes;
/* *
*
* Constants
*
* */
/**
* The navigator is a small series below the main series, displaying
* a view of the entire data set. It provides tools to zoom in and
* out on parts of the data as well as panning across the dataset.
*
* @product highstock gantt
* @optionparent navigator
*/
var NavigatorDefaults = {
/**
* Whether the navigator and scrollbar should adapt to updated data
* in the base X axis. When loading data async, as in the demo below,
* this should be `false`. Otherwise new data will trigger navigator
* redraw, which will cause unwanted looping. In the demo below, the
* data in the navigator is set only once. On navigating, only the main
* chart content is updated.
*
* @sample {highstock} stock/demo/lazy-loading/
* Set to false with async data loading
*
* @type {boolean}
* @default true
* @apioption navigator.adaptToUpdatedData
*/
/**
* An integer identifying the index to use for the base series, or a
* string representing the id of the series.
*
* **Note**: As of Highcharts 5.0, this is now a deprecated option.
* Prefer [series.showInNavigator](#plotOptions.series.showInNavigator).
*
* @see [series.showInNavigator](#plotOptions.series.showInNavigator)
*
* @deprecated
* @type {number|string}
* @default 0
* @apioption navigator.baseSeries
*/
/**
* Enable or disable the navigator.
*
* @sample {highstock} stock/navigator/enabled/
* Disable the navigator
*
* @type {boolean}
* @default true
* @apioption navigator.enabled
*/
/**
* When the chart is inverted, whether to draw the navigator on the
* opposite side.
*
* @type {boolean}
* @default false
* @since 5.0.8
* @apioption navigator.opposite
*/
/**
* The height of the navigator.
*
* @sample {highstock} stock/navigator/height/
* A higher navigator
*/
height: 40,
/**
* The distance from the nearest element, the X axis or X axis labels.
*
* @sample {highstock} stock/navigator/margin/
* A margin of 2 draws the navigator closer to the X axis labels
*/
margin: 25,
/**
* Whether the mask should be inside the range marking the zoomed
* range, or outside. In Highcharts Stock 1.x it was always `false`.
*
* @sample {highstock} stock/demo/maskinside-false/
* False, mask outside
*
* @since 2.0
*/
maskInside: true,
/**
* Options for the handles for dragging the zoomed area.
*
* @sample {highstock} stock/navigator/handles/
* Colored handles
*/
handles: {
/**
* Width for handles.
*
* @sample {highstock} stock/navigator/styled-handles/
* Styled handles
*
* @since 6.0.0
*/
width: 7,
/**
* Border radius of the handles.
*
* @sample {highstock} stock/navigator/handles-border-radius/
* Border radius on the navigator handles.
*
* @since 11.4.2
*/
borderRadius: 0,
/**
* Height for handles.
*
* @sample {highstock} stock/navigator/styled-handles/
* Styled handles
*
* @since 6.0.0
*/
height: 15,
/**
* Array to define shapes of handles. 0-index for left, 1-index for
* right.
*
* Additionally, the URL to a graphic can be given on this form:
* `url(graphic.png)`. Note that for the image to be applied to
* exported charts, its URL needs to be accessible by the export
* server.
*
* Custom callbacks for symbol path generation can also be added to
* `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
* used by its method name, as shown in the demo.
*
* @sample {highstock} stock/navigator/styled-handles/
* Styled handles
*
* @type {Array<string>}
* @default ["navigator-handle", "navigator-handle"]
* @since 6.0.0
*/
symbols: ['navigator-handle', 'navigator-handle'],
/**
* Allows to enable/disable handles.
*
* @since 6.0.0
*/
enabled: true,
/**
* The width for the handle border and the stripes inside.
*
* @sample {highstock} stock/navigator/styled-handles/
* Styled handles
*
* @since 6.0.0
* @apioption navigator.handles.lineWidth
*/
lineWidth: 1,
/**
* The fill for the handle.
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
backgroundColor: "#f2f2f2" /* Palette.neutralColor5 */,
/**
* The stroke for the handle border and the stripes inside.
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
borderColor: "#999999" /* Palette.neutralColor40 */
},
/**
* The color of the mask covering the areas of the navigator series
* that are currently not visible in the main series. The default
* color is bluish with an opacity of 0.3 to see the series below.
*
* @see In styled mode, the mask is styled with the
* `.highcharts-navigator-mask` and
* `.highcharts-navigator-mask-inside` classes.
*
* @sample {highstock} stock/navigator/maskfill/
* Blue, semi transparent mask
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @default rgba(102,133,194,0.3)
*/
maskFill: NavigatorDefaults_color("#667aff" /* Palette.highlightColor60 */).setOpacity(0.3).get(),
/**
* The color of the line marking the currently zoomed area in the
* navigator.
*
* @sample {highstock} stock/navigator/outline/
* 2px blue outline
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @default #cccccc
*/
outlineColor: "#999999" /* Palette.neutralColor40 */,
/**
* The width of the line marking the currently zoomed area in the
* navigator.
*
* @see In styled mode, the outline stroke width is set with the
* `.highcharts-navigator-outline` class.
*
* @sample {highstock} stock/navigator/outline/
* 2px blue outline
*
* @type {number}
*/
outlineWidth: 1,
/**
* Options for the navigator series. Available options are the same
* as any series, documented at [plotOptions](#plotOptions.series)
* and [series](#series).
*
* Unless data is explicitly defined on navigator.series, the data
* is borrowed from the first series in the chart.
*
* Default series options for the navigator series are:
* ```js
* series: {
* type: 'areaspline',
* fillOpacity: 0.05,
* dataGrouping: {
* smoothed: true
* },
* lineWidth: 1,
* marker: {
* enabled: false
* }
* }
* ```
*
* @see In styled mode, the navigator series is styled with the
* `.highcharts-navigator-series` class.
*
* @sample {highstock} stock/navigator/series-data/
* Using a separate data set for the navigator
* @sample {highstock} stock/navigator/series/
* A green navigator series
*
* @type {*|Array<*>|Highcharts.SeriesOptionsType|Array<Highcharts.SeriesOptionsType>}
*/
series: {
/**
* The type of the navigator series.
*
* Heads up:
* In column-type navigator, zooming is limited to at least one
* point with its `pointRange`.
*
* @sample {highstock} stock/navigator/column/
* Column type navigator
*
* @type {string}
* @default {highstock} `areaspline` if defined, otherwise `line`
* @default {gantt} gantt
*/
type: (typeof NavigatorDefaults_seriesTypes.areaspline === 'undefined' ?
'line' :
'areaspline'),
/**
* The fill opacity of the navigator series.
*/
fillOpacity: 0.05,
/**
* The pixel line width of the navigator series.
*/
lineWidth: 1,
/**
* @ignore-option
*/
compare: null,
/**
* @ignore-option
*/
sonification: {
enabled: false
},
/**
* Unless data is explicitly defined, the data is borrowed from the
* first series in the chart.
*
* @type {Array<number|Array<number|string|null>|object|null>}
* @product highstock
* @apioption navigator.series.data
*/
/**
* Data grouping options for the navigator series.
*
* @extends plotOptions.series.dataGrouping
*/
dataGrouping: {
approximation: 'average',
enabled: true,
groupPixelWidth: 2,
// Replace smoothed property by anchors, #12455.
firstAnchor: 'firstPoint',
anchor: 'middle',
lastAnchor: 'lastPoint',
// Day and week differs from plotOptions.series.dataGrouping
units: [
['millisecond', [1, 2, 5, 10, 20, 25, 50, 100, 200, 500]],
['second', [1, 2, 5, 10, 15, 30]],
['minute', [1, 2, 5, 10, 15, 30]],
['hour', [1, 2, 3, 4, 6, 8, 12]],
['day', [1, 2, 3, 4]],
['week', [1, 2, 3]],
['month', [1, 3, 6]],
['year', null]
]
},
/**
* Data label options for the navigator series. Data labels are
* disabled by default on the navigator series.
*
* @extends plotOptions.series.dataLabels
*/
dataLabels: {
enabled: false,
zIndex: 2 // #1839
},
id: 'highcharts-navigator-series',
className: 'highcharts-navigator-series',
/**
* Sets the fill color of the navigator series.
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @apioption navigator.series.color
*/
/**
* Line color for the navigator series. Allows setting the color
* while disallowing the default candlestick setting.
*
* @type {Highcharts.ColorString|null}
*/
lineColor: null, // #4602
marker: {
enabled: false
},
/**
* Since Highcharts Stock v8, default value is the same as default
* `pointRange` defined for a specific type (e.g. `null` for
* column type).
*
* In Highcharts Stock version < 8, defaults to 0.
*
* @extends plotOptions.series.pointRange
* @type {number|null}
* @apioption navigator.series.pointRange
*/
/**
* The threshold option. Setting it to 0 will make the default
* navigator area series draw its area from the 0 value and up.
*
* @type {number|null}
*/
threshold: null
},
/**
* Enable or disable navigator sticking to right, while adding new
* points. If `undefined`, the navigator sticks to the axis maximum only
* if it was already at the maximum prior to adding points.
*
* @type {boolean}
* @default undefined
* @since 10.2.1
* @sample {highstock} stock/navigator/sticktomax-false/
* stickToMax set to false
* @apioption navigator.stickToMax
*/
/**
* Options for the navigator X axis. Default series options for the
* navigator xAxis are:
* ```js
* xAxis: {
* tickWidth: 0,
* lineWidth: 0,
* gridLineWidth: 1,
* tickPixelInterval: 200,
* labels: {
* align: 'left',
* style: {
* color: '#888'
* },
* x: 3,
* y: -4
* }
* }
* ```
*
* @extends xAxis
* @excluding linkedTo, maxZoom, minRange, opposite, range, scrollbar,
* showEmpty, maxRange
*/
xAxis: {
/**
* Additional range on the right side of the xAxis. Works similar to
* `xAxis.maxPadding`, but the value is set in terms of axis values,
* percentage or pixels.
*
* If it's a number, it is interpreted as axis values, which in a
* datetime axis equals milliseconds.
*
* If it's a percentage string, is interpreted as percentages of the
* axis length. An overscroll of 50% will make a 100px axis 50px longer.
*
* If it's a pixel string, it is interpreted as a fixed pixel value, but
* limited to 90% of the axis length.
*
* If it's undefined, the value is inherited from `xAxis.overscroll`.
*
* Can be set for both, main xAxis and navigator's xAxis.
*
* @type {number | string | undefined}
* @since 6.0.0
* @apioption navigator.xAxis.overscroll
*/
className: 'highcharts-navigator-xaxis',
tickLength: 0,
lineWidth: 0,
gridLineColor: "#e6e6e6" /* Palette.neutralColor10 */,
id: 'navigator-x-axis',
gridLineWidth: 1,
tickPixelInterval: 200,
labels: {
align: 'left',
/**
* @type {Highcharts.CSSObject}
*/
style: {
/** @ignore */
color: "#000000" /* Palette.neutralColor100 */,
/** @ignore */
fontSize: '0.7em',
/** @ignore */
opacity: 0.6,
/** @ignore */
textOutline: '2px contrast'
},
x: 3,
y: -4
},
crosshair: false
},
/**
* Options for the navigator Y axis. Default series options for the
* navigator yAxis are:
* ```js
* yAxis: {
* gridLineWidth: 0,
* startOnTick: false,
* endOnTick: false,
* minPadding: 0.1,
* maxPadding: 0.1,
* labels: {
* enabled: false
* },
* title: {
* text: null
* },
* tickWidth: 0
* }
* ```
*
* @extends yAxis
* @excluding height, linkedTo, maxZoom, minRange, ordinal, range,
* showEmpty, scrollbar, top, units, maxRange, minLength,
* maxLength, resize
*/
yAxis: {
className: 'highcharts-navigator-yaxis',
gridLineWidth: 0,
startOnTick: false,
endOnTick: false,
minPadding: 0.1,
id: 'navigator-y-axis',
maxPadding: 0.1,
labels: {
enabled: false
},
crosshair: false,
title: {
text: null
},
tickLength: 0,
tickWidth: 0
}
};
/* *
*
* Default Export
*
* */
/* harmony default export */ var Navigator_NavigatorDefaults = (NavigatorDefaults);
/* *
*
* API Options
*
* */
/**
* Maximum range which can be set using the navigator's handles.
* Opposite of [xAxis.minRange](#xAxis.minRange).
*
* @sample {highstock} stock/navigator/maxrange/
* Defined max and min range
*
* @type {number}
* @since 6.0.0
* @product highstock gantt
* @apioption xAxis.maxRange
*/
(''); // Keeps doclets above in JS file
;// ./code/es5/es-modules/Stock/Navigator/NavigatorSymbols.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var NavigatorSymbols_spreadArray = (undefined && undefined.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var NavigatorSymbols_relativeLength = Core_Utilities.relativeLength;
/* *
*
* Constants
*
* */
/**
* Draw one of the handles on the side of the zoomed range in the navigator.
* @private
*/
function navigatorHandle(_x, _y, width, height, options) {
if (options === void 0) { options = {}; }
var halfWidth = options.width ? options.width / 2 : width,
markerPosition = 1.5,
r = NavigatorSymbols_relativeLength(options.borderRadius || 0,
Math.min(halfWidth * 2,
height));
height = options.height || height;
return NavigatorSymbols_spreadArray([
['M', -markerPosition, height / 2 - 3.5],
['L', -markerPosition, height / 2 + 4.5],
['M', markerPosition - 1, height / 2 - 3.5],
['L', markerPosition - 1, height / 2 + 4.5]
], SVG_Symbols.rect(-halfWidth - 1, 0.5, halfWidth * 2 + 1, height, { r: r }), true);
}
/* *
*
* Default Export
*
* */
var NavigatorSymbols = {
'navigator-handle': navigatorHandle
};
/* harmony default export */ var Navigator_NavigatorSymbols = (NavigatorSymbols);
;// ./code/es5/es-modules/Stock/Utilities/StockUtilities.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var StockUtilities_defined = Core_Utilities.defined;
/* *
*
* Functions
*
* */
/**
* Sets the chart.fixedRange to the specified value. If the value is larger
* than actual range, sets it to the maximum possible range. (#20327)
*
* @private
* @function Highcharts.StockChart#setFixedRange
* @param {number|undefined} range
* Range to set in axis units.
*/
function setFixedRange(range) {
var xAxis = this.xAxis[0];
if (StockUtilities_defined(xAxis.dataMax) &&
StockUtilities_defined(xAxis.dataMin) &&
range) {
this.fixedRange = Math.min(range, xAxis.dataMax - xAxis.dataMin);
}
else {
this.fixedRange = range;
}
}
var StockUtilities = {
setFixedRange: setFixedRange
};
/* harmony default export */ var Utilities_StockUtilities = (StockUtilities);
;// ./code/es5/es-modules/Stock/Navigator/NavigatorComposition.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var NavigatorComposition_setOptions = Defaults.setOptions;
var NavigatorComposition_composed = Core_Globals.composed;
var getRendererType = Renderer_RendererRegistry.getRendererType;
var NavigatorComposition_setFixedRange = Utilities_StockUtilities.setFixedRange;
var NavigatorComposition_addEvent = Core_Utilities.addEvent, NavigatorComposition_extend = Core_Utilities.extend, NavigatorComposition_pushUnique = Core_Utilities.pushUnique;
/* *
*
* Variables
*
* */
/* *
*
* Functions
*
* */
/**
* @private
*/
function NavigatorComposition_compose(ChartClass, AxisClass, SeriesClass) {
NavigatorAxisComposition.compose(AxisClass);
if (NavigatorComposition_pushUnique(NavigatorComposition_composed, 'Navigator')) {
ChartClass.prototype.setFixedRange = NavigatorComposition_setFixedRange;
NavigatorComposition_extend(getRendererType().prototype.symbols, Navigator_NavigatorSymbols);
NavigatorComposition_addEvent(SeriesClass, 'afterUpdate', onSeriesAfterUpdate);
NavigatorComposition_setOptions({ navigator: Navigator_NavigatorDefaults });
}
}
/**
* Handle updating series
* @private
*/
function onSeriesAfterUpdate() {
if (this.chart.navigator && !this.options.isInternal) {
this.chart.navigator.setBaseSeries(null, false);
}
}
/* *
*
* Default Export
*
* */
var NavigatorComposition = {
compose: NavigatorComposition_compose
};
/* harmony default export */ var Navigator_NavigatorComposition = (NavigatorComposition);
;// ./code/es5/es-modules/Core/Axis/ScrollbarAxis.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var ScrollbarAxis_composed = Core_Globals.composed;
var ScrollbarAxis_addEvent = Core_Utilities.addEvent, ScrollbarAxis_defined = Core_Utilities.defined, ScrollbarAxis_pick = Core_Utilities.pick, ScrollbarAxis_pushUnique = Core_Utilities.pushUnique;
/* *
*
* Composition
*
* */
var ScrollbarAxis;
(function (ScrollbarAxis) {
/* *
*
* Variables
*
* */
var Scrollbar;
/* *
*
* Functions
*
* */
/**
* Attaches to axis events to create scrollbars if enabled.
*
* @private
*
* @param {Highcharts.Axis} AxisClass
* Axis class to extend.
*
* @param {Highcharts.Scrollbar} ScrollbarClass
* Scrollbar class to use.
*/
function compose(AxisClass, ScrollbarClass) {
if (ScrollbarAxis_pushUnique(ScrollbarAxis_composed, 'Axis.Scrollbar')) {
Scrollbar = ScrollbarClass;
ScrollbarAxis_addEvent(AxisClass, 'afterGetOffset', onAxisAfterGetOffset);
ScrollbarAxis_addEvent(AxisClass, 'afterInit', onAxisAfterInit);
ScrollbarAxis_addEvent(AxisClass, 'afterRender', onAxisAfterRender);
}
}
ScrollbarAxis.compose = compose;
/** @private */
function getExtremes(axis) {
var axisMin = ScrollbarAxis_pick(axis.options && axis.options.min,
axis.min);
var axisMax = ScrollbarAxis_pick(axis.options && axis.options.max,
axis.max);
return {
axisMin: axisMin,
axisMax: axisMax,
scrollMin: ScrollbarAxis_defined(axis.dataMin) ?
Math.min(axisMin, axis.min, axis.dataMin, ScrollbarAxis_pick(axis.threshold, Infinity)) : axisMin,
scrollMax: ScrollbarAxis_defined(axis.dataMax) ?
Math.max(axisMax, axis.max, axis.dataMax, ScrollbarAxis_pick(axis.threshold, -Infinity)) : axisMax
};
}
/**
* Make space for a scrollbar.
* @private
*/
function onAxisAfterGetOffset() {
var axis = this,
scrollbar = axis.scrollbar,
opposite = scrollbar && !scrollbar.options.opposite,
index = axis.horiz ? 2 : opposite ? 3 : 1;
if (scrollbar) {
// Reset scrollbars offsets
axis.chart.scrollbarsOffsets = [0, 0];
axis.chart.axisOffset[index] +=
scrollbar.size + (scrollbar.options.margin || 0);
}
}
/**
* Wrap axis initialization and create scrollbar if enabled.
* @private
*/
function onAxisAfterInit() {
var axis = this;
if (axis.options &&
axis.options.scrollbar &&
axis.options.scrollbar.enabled) {
// Predefined options:
axis.options.scrollbar.vertical = !axis.horiz;
axis.options.startOnTick = axis.options.endOnTick = false;
axis.scrollbar = new Scrollbar(axis.chart.renderer, axis.options.scrollbar, axis.chart);
ScrollbarAxis_addEvent(axis.scrollbar, 'changed', function (e) {
var _a = getExtremes(axis),
axisMin = _a.axisMin,
axisMax = _a.axisMax,
unitedMin = _a.scrollMin,
unitedMax = _a.scrollMax,
range = unitedMax - unitedMin;
var to,
from;
// #12834, scroll when show/hide series, wrong extremes
if (!ScrollbarAxis_defined(axisMin) || !ScrollbarAxis_defined(axisMax)) {
return;
}
if ((axis.horiz && !axis.reversed) ||
(!axis.horiz && axis.reversed)) {
to = unitedMin + range * this.to;
from = unitedMin + range * this.from;
}
else {
// Y-values in browser are reversed, but this also
// applies for reversed horizontal axis:
to = unitedMin + range * (1 - this.from);
from = unitedMin + range * (1 - this.to);
}
if (this.shouldUpdateExtremes(e.DOMType)) {
// #17977, set animation to undefined instead of true
var animate = e.DOMType === 'mousemove' ||
e.DOMType === 'touchmove' ? false : void 0;
axis.setExtremes(from, to, true, animate, e);
}
else {
// When live redraw is disabled, don't change extremes
// Only change the position of the scrollbar thumb
this.setRange(this.from, this.to);
}
});
}
}
/**
* Wrap rendering axis, and update scrollbar if one is created.
* @private
*/
function onAxisAfterRender() {
var axis = this,
_a = getExtremes(axis),
scrollMin = _a.scrollMin,
scrollMax = _a.scrollMax,
scrollbar = axis.scrollbar,
offset = (axis.axisTitleMargin + (axis.titleOffset || 0)),
scrollbarsOffsets = axis.chart.scrollbarsOffsets,
axisMargin = axis.options.margin || 0;
var offsetsIndex,
from,
to;
if (scrollbar && scrollbarsOffsets) {
if (axis.horiz) {
// Reserve space for labels/title
if (!axis.opposite) {
scrollbarsOffsets[1] += offset;
}
scrollbar.position(axis.left, (axis.top +
axis.height +
2 +
scrollbarsOffsets[1] -
(axis.opposite ? axisMargin : 0)), axis.width, axis.height);
// Next scrollbar should reserve space for margin (if set)
if (!axis.opposite) {
scrollbarsOffsets[1] += axisMargin;
}
offsetsIndex = 1;
}
else {
// Reserve space for labels/title
if (axis.opposite) {
scrollbarsOffsets[0] += offset;
}
var xPosition = void 0;
if (!scrollbar.options.opposite) {
xPosition = axis.opposite ? 0 : axisMargin;
}
else {
xPosition = axis.left +
axis.width +
2 +
scrollbarsOffsets[0] -
(axis.opposite ? 0 : axisMargin);
}
scrollbar.position(xPosition, axis.top, axis.width, axis.height);
// Next scrollbar should reserve space for margin (if set)
if (axis.opposite) {
scrollbarsOffsets[0] += axisMargin;
}
offsetsIndex = 0;
}
scrollbarsOffsets[offsetsIndex] += scrollbar.size +
(scrollbar.options.margin || 0);
if (isNaN(scrollMin) ||
isNaN(scrollMax) ||
!ScrollbarAxis_defined(axis.min) ||
!ScrollbarAxis_defined(axis.max) ||
axis.dataMin === axis.dataMax // #10733
) {
// Default action: when data extremes are the same or there is
// not extremes on the axis, but scrollbar exists, make it
// full size
scrollbar.setRange(0, 1);
}
else if (axis.min === axis.max) { // #20359
// When the extremes are the same, set the scrollbar to a point
// within the extremes range. Utilize pointRange to perform the
// calculations. (#20359)
var interval = axis.pointRange / (axis.dataMax +
1);
from = interval * axis.min;
to = interval * (axis.max + 1);
scrollbar.setRange(from, to);
}
else {
from = ((axis.min - scrollMin) /
(scrollMax - scrollMin));
to = ((axis.max - scrollMin) /
(scrollMax - scrollMin));
if ((axis.horiz && !axis.reversed) ||
(!axis.horiz && axis.reversed)) {
scrollbar.setRange(from, to);
}
else {
// Inverse vertical axis
scrollbar.setRange(1 - to, 1 - from);
}
}
}
}
})(ScrollbarAxis || (ScrollbarAxis = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Axis_ScrollbarAxis = (ScrollbarAxis);
;// ./code/es5/es-modules/Stock/Scrollbar/ScrollbarDefaults.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* Constant
*
* */
/**
*
* The scrollbar is a means of panning over the X axis of a stock chart.
* Scrollbars can also be applied to other types of axes.
*
* Another approach to scrollable charts is the [chart.scrollablePlotArea](
* https://api.highcharts.com/highcharts/chart.scrollablePlotArea) option that
* is especially suitable for simpler cartesian charts on mobile.
*
* In styled mode, all the presentational options for the
* scrollbar are replaced by the classes `.highcharts-scrollbar-thumb`,
* `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
* `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
*
* @sample stock/yaxis/inverted-bar-scrollbar/
* A scrollbar on a simple bar chart
*
* @product highstock gantt
* @optionparent scrollbar
*
* @private
*/
var ScrollbarDefaults = {
/**
* The height of the scrollbar. If `buttonsEnabled` is true , the height
* also applies to the width of the scroll arrows so that they are always
* squares.
*
* @sample stock/scrollbar/style/
* Non-default height
*
* @type {number}
*/
height: 10,
/**
* The border rounding radius of the bar.
*
* @sample stock/scrollbar/style/
* Scrollbar styling
*/
barBorderRadius: 5,
/**
* The corner radius of the scrollbar buttons.
*
* @sample stock/scrollbar/style/
* Scrollbar styling
*/
buttonBorderRadius: 0,
/**
* Enable or disable the buttons at the end of the scrollbar.
*
* @since 11.0.0
*/
buttonsEnabled: false,
/**
* Enable or disable the scrollbar.
*
* @sample stock/scrollbar/enabled/
* Disable the scrollbar, only use navigator
*
* @type {boolean}
* @default true
* @apioption scrollbar.enabled
*/
/**
* Whether to redraw the main chart as the scrollbar or the navigator
* zoomed window is moved. Defaults to `true` for modern browsers and
* `false` for legacy IE browsers as well as mobile devices.
*
* @sample stock/scrollbar/liveredraw
* Setting live redraw to false
*
* @type {boolean}
* @since 1.3
*/
liveRedraw: void 0,
/**
* The margin between the scrollbar and its axis when the scrollbar is
* applied directly to an axis, or the navigator in case that is enabled.
* Defaults to 10 for axis, 0 for navigator.
*
* @type {number|undefined}
*/
margin: void 0,
/**
* The minimum width of the scrollbar.
*
* @since 1.2.5
*/
minWidth: 6,
/** @ignore-option */
opposite: true,
/**
* Whether to show or hide the scrollbar when the scrolled content is
* zoomed out to it full extent.
*
* @type {boolean}
* @default true
* @apioption scrollbar.showFull
*/
step: 0.2,
/**
* The z index of the scrollbar group.
*/
zIndex: 3,
/**
* The background color of the scrollbar itself.
*
* @sample stock/scrollbar/style/
* Scrollbar styling
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
barBackgroundColor: "#cccccc" /* Palette.neutralColor20 */,
/**
* The width of the bar's border.
*
* @sample stock/scrollbar/style/
* Scrollbar styling
*/
barBorderWidth: 0,
/**
* The color of the scrollbar's border.
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
barBorderColor: "#cccccc" /* Palette.neutralColor20 */,
/**
* The color of the small arrow inside the scrollbar buttons.
*
* @sample stock/scrollbar/style/
* Scrollbar styling
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
buttonArrowColor: "#333333" /* Palette.neutralColor80 */,
/**
* The color of scrollbar buttons.
*
* @sample stock/scrollbar/style/
* Scrollbar styling
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
buttonBackgroundColor: "#e6e6e6" /* Palette.neutralColor10 */,
/**
* The color of the border of the scrollbar buttons.
*
* @sample stock/scrollbar/style/
* Scrollbar styling
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
buttonBorderColor: "#cccccc" /* Palette.neutralColor20 */,
/**
* The border width of the scrollbar buttons.
*
* @sample stock/scrollbar/style/
* Scrollbar styling
*/
buttonBorderWidth: 1,
/**
* The color of the small rifles in the middle of the scrollbar.
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
rifleColor: 'none',
/**
* The color of the track background.
*
* @sample stock/scrollbar/style/
* Scrollbar styling
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
trackBackgroundColor: 'rgba(255, 255, 255, 0.001)', // #18922
/**
* The color of the border of the scrollbar track.
*
* @sample stock/scrollbar/style/
* Scrollbar styling
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
trackBorderColor: "#cccccc" /* Palette.neutralColor20 */,
/**
* The corner radius of the border of the scrollbar track.
*
* @sample stock/scrollbar/style/
* Scrollbar styling
*/
trackBorderRadius: 5,
/**
* The width of the border of the scrollbar track.
*
* @sample stock/scrollbar/style/
* Scrollbar styling
*/
trackBorderWidth: 1
};
/* *
*
* Default Export
*
* */
/* harmony default export */ var Scrollbar_ScrollbarDefaults = (ScrollbarDefaults);
;// ./code/es5/es-modules/Stock/Scrollbar/Scrollbar.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var Scrollbar_defaultOptions = Defaults.defaultOptions;
var Scrollbar_addEvent = Core_Utilities.addEvent, Scrollbar_correctFloat = Core_Utilities.correctFloat, Scrollbar_crisp = Core_Utilities.crisp, Scrollbar_defined = Core_Utilities.defined, Scrollbar_destroyObjectProperties = Core_Utilities.destroyObjectProperties, Scrollbar_fireEvent = Core_Utilities.fireEvent, Scrollbar_merge = Core_Utilities.merge, Scrollbar_pick = Core_Utilities.pick, Scrollbar_removeEvent = Core_Utilities.removeEvent;
/* *
*
* Constants
*
* */
/* eslint-disable no-invalid-this, valid-jsdoc */
/**
* A reusable scrollbar, internally used in Highcharts Stock's
* navigator and optionally on individual axes.
*
* @private
* @class
* @name Highcharts.Scrollbar
* @param {Highcharts.SVGRenderer} renderer
* @param {Highcharts.ScrollbarOptions} options
* @param {Highcharts.Chart} chart
*/
var Scrollbar = /** @class */ (function () {
/* *
*
* Constructors
*
* */
function Scrollbar(renderer, options, chart) {
/* *
*
* Properties
*
* */
this._events = [];
this.chartX = 0;
this.chartY = 0;
this.from = 0;
this.scrollbarButtons = [];
this.scrollbarLeft = 0;
this.scrollbarStrokeWidth = 1;
this.scrollbarTop = 0;
this.size = 0;
this.to = 0;
this.trackBorderWidth = 1;
this.x = 0;
this.y = 0;
this.init(renderer, options, chart);
}
/* *
*
* Static Functions
*
* */
Scrollbar.compose = function (AxisClass) {
Axis_ScrollbarAxis.compose(AxisClass, Scrollbar);
};
/**
* When we have vertical scrollbar, rifles and arrow in buttons should be
* rotated. The same method is used in Navigator's handles, to rotate them.
*
* @function Highcharts.swapXY
*
* @param {Highcharts.SVGPathArray} path
* Path to be rotated.
*
* @param {boolean} [vertical]
* If vertical scrollbar, swap x-y values.
*
* @return {Highcharts.SVGPathArray}
* Rotated path.
*
* @requires modules/stock
*/
Scrollbar.swapXY = function (path, vertical) {
if (vertical) {
path.forEach(function (seg) {
var len = seg.length;
var temp;
for (var i = 0; i < len; i += 2) {
temp = seg[i + 1];
if (typeof temp === 'number') {
seg[i + 1] = seg[i + 2];
seg[i + 2] = temp;
}
}
});
}
return path;
};
/* *
*
* Functions
*
* */
/**
* Set up the mouse and touch events for the Scrollbar
*
* @private
* @function Highcharts.Scrollbar#addEvents
*/
Scrollbar.prototype.addEvents = function () {
var buttonsOrder = this.options.inverted ? [1, 0] : [0, 1],
buttons = this.scrollbarButtons,
bar = this.scrollbarGroup.element,
track = this.track.element,
mouseDownHandler = this.mouseDownHandler.bind(this),
mouseMoveHandler = this.mouseMoveHandler.bind(this),
mouseUpHandler = this.mouseUpHandler.bind(this);
var _events = [
// Mouse events
[
buttons[buttonsOrder[0]].element,
'click',
this.buttonToMinClick.bind(this)
],
[
buttons[buttonsOrder[1]].element,
'click',
this.buttonToMaxClick.bind(this)
],
[track, 'click',
this.trackClick.bind(this)],
[bar, 'mousedown',
mouseDownHandler],
[bar.ownerDocument, 'mousemove',
mouseMoveHandler],
[bar.ownerDocument, 'mouseup',
mouseUpHandler],
// Touch events
[bar, 'touchstart',
mouseDownHandler],
[bar.ownerDocument, 'touchmove',
mouseMoveHandler],
[bar.ownerDocument, 'touchend',
mouseUpHandler]
];
// Add them all
_events.forEach(function (args) {
Scrollbar_addEvent.apply(null, args);
});
this._events = _events;
};
Scrollbar.prototype.buttonToMaxClick = function (e) {
var scroller = this;
var range = ((scroller.to - scroller.from) *
Scrollbar_pick(scroller.options.step, 0.2));
scroller.updatePosition(scroller.from + range, scroller.to + range);
Scrollbar_fireEvent(scroller, 'changed', {
from: scroller.from,
to: scroller.to,
trigger: 'scrollbar',
DOMEvent: e
});
};
Scrollbar.prototype.buttonToMinClick = function (e) {
var scroller = this;
var range = Scrollbar_correctFloat(scroller.to - scroller.from) *
Scrollbar_pick(scroller.options.step, 0.2);
scroller.updatePosition(Scrollbar_correctFloat(scroller.from - range), Scrollbar_correctFloat(scroller.to - range));
Scrollbar_fireEvent(scroller, 'changed', {
from: scroller.from,
to: scroller.to,
trigger: 'scrollbar',
DOMEvent: e
});
};
/**
* Get normalized (0-1) cursor position over the scrollbar
*
* @private
* @function Highcharts.Scrollbar#cursorToScrollbarPosition
*
* @param {*} normalizedEvent
* normalized event, with chartX and chartY values
*
* @return {Highcharts.Dictionary<number>}
* Local position {chartX, chartY}
*/
Scrollbar.prototype.cursorToScrollbarPosition = function (normalizedEvent) {
var scroller = this,
options = scroller.options,
minWidthDifference = options.minWidth > scroller.calculatedWidth ?
options.minWidth :
0; // `minWidth` distorts translation
return {
chartX: (normalizedEvent.chartX - scroller.x -
scroller.xOffset) /
(scroller.barWidth - minWidthDifference),
chartY: (normalizedEvent.chartY - scroller.y -
scroller.yOffset) /
(scroller.barWidth - minWidthDifference)
};
};
/**
* Destroys allocated elements.
*
* @private
* @function Highcharts.Scrollbar#destroy
*/
Scrollbar.prototype.destroy = function () {
var scroller = this,
navigator = scroller.chart.scroller;
// Disconnect events added in addEvents
scroller.removeEvents();
// Destroy properties
[
'track',
'scrollbarRifles',
'scrollbar',
'scrollbarGroup',
'group'
].forEach(function (prop) {
if (scroller[prop] && scroller[prop].destroy) {
scroller[prop] = scroller[prop].destroy();
}
});
// #6421, chart may have more scrollbars
if (navigator && scroller === navigator.scrollbar) {
navigator.scrollbar = null;
// Destroy elements in collection
Scrollbar_destroyObjectProperties(navigator.scrollbarButtons);
}
};
/**
* Draw the scrollbar buttons with arrows
*
* @private
* @function Highcharts.Scrollbar#drawScrollbarButton
* @param {number} index
* 0 is left, 1 is right
*/
Scrollbar.prototype.drawScrollbarButton = function (index) {
var scroller = this,
renderer = scroller.renderer,
scrollbarButtons = scroller.scrollbarButtons,
options = scroller.options,
size = scroller.size,
group = renderer.g().add(scroller.group);
scrollbarButtons.push(group);
if (options.buttonsEnabled) {
// Create a rectangle for the scrollbar button
var rect = renderer.rect()
.addClass('highcharts-scrollbar-button')
.add(group);
// Presentational attributes
if (!scroller.chart.styledMode) {
rect.attr({
stroke: options.buttonBorderColor,
'stroke-width': options.buttonBorderWidth,
fill: options.buttonBackgroundColor
});
}
// Place the rectangle based on the rendered stroke width
rect.attr(rect.crisp({
x: -0.5,
y: -0.5,
width: size,
height: size,
r: options.buttonBorderRadius
}, rect.strokeWidth()));
// Button arrow
var arrow = renderer
.path(Scrollbar.swapXY([[
'M',
size / 2 + (index ? -1 : 1),
size / 2 - 3
],
[
'L',
size / 2 + (index ? -1 : 1),
size / 2 + 3
],
[
'L',
size / 2 + (index ? 2 : -2),
size / 2
]],
options.vertical))
.addClass('highcharts-scrollbar-arrow')
.add(scrollbarButtons[index]);
if (!scroller.chart.styledMode) {
arrow.attr({
fill: options.buttonArrowColor
});
}
}
};
/**
* @private
* @function Highcharts.Scrollbar#init
* @param {Highcharts.SVGRenderer} renderer
* @param {Highcharts.ScrollbarOptions} options
* @param {Highcharts.Chart} chart
*/
Scrollbar.prototype.init = function (renderer, options, chart) {
var scroller = this;
scroller.scrollbarButtons = [];
scroller.renderer = renderer;
scroller.userOptions = options;
scroller.options = Scrollbar_merge(Scrollbar_ScrollbarDefaults, Scrollbar_defaultOptions.scrollbar, options);
scroller.options.margin = Scrollbar_pick(scroller.options.margin, 10);
scroller.chart = chart;
// Backward compatibility
scroller.size = Scrollbar_pick(scroller.options.size, scroller.options.height);
// Init
if (options.enabled) {
scroller.render();
scroller.addEvents();
}
};
Scrollbar.prototype.mouseDownHandler = function (e) {
var _a;
var scroller = this,
normalizedEvent = ((_a = scroller.chart.pointer) === null || _a === void 0 ? void 0 : _a.normalize(e)) || e,
mousePosition = scroller.cursorToScrollbarPosition(normalizedEvent);
scroller.chartX = mousePosition.chartX;
scroller.chartY = mousePosition.chartY;
scroller.initPositions = [scroller.from, scroller.to];
scroller.grabbedCenter = true;
};
/**
* Event handler for the mouse move event.
* @private
*/
Scrollbar.prototype.mouseMoveHandler = function (e) {
var _a;
var scroller = this,
normalizedEvent = ((_a = scroller.chart.pointer) === null || _a === void 0 ? void 0 : _a.normalize(e)) || e,
options = scroller.options,
direction = options.vertical ?
'chartY' : 'chartX',
initPositions = scroller.initPositions || [];
var scrollPosition,
chartPosition,
change;
// In iOS, a mousemove event with e.pageX === 0 is fired when
// holding the finger down in the center of the scrollbar. This
// should be ignored.
if (scroller.grabbedCenter &&
// #4696, scrollbar failed on Android
(!e.touches || e.touches[0][direction] !== 0)) {
chartPosition = scroller.cursorToScrollbarPosition(normalizedEvent)[direction];
scrollPosition = scroller[direction];
change = chartPosition - scrollPosition;
scroller.hasDragged = true;
scroller.updatePosition(initPositions[0] + change, initPositions[1] + change);
if (scroller.hasDragged) {
Scrollbar_fireEvent(scroller, 'changed', {
from: scroller.from,
to: scroller.to,
trigger: 'scrollbar',
DOMType: e.type,
DOMEvent: e
});
}
}
};
/**
* Event handler for the mouse up event.
* @private
*/
Scrollbar.prototype.mouseUpHandler = function (e) {
var scroller = this;
if (scroller.hasDragged) {
Scrollbar_fireEvent(scroller, 'changed', {
from: scroller.from,
to: scroller.to,
trigger: 'scrollbar',
DOMType: e.type,
DOMEvent: e
});
}
scroller.grabbedCenter =
scroller.hasDragged =
scroller.chartX =
scroller.chartY = null;
};
/**
* Position the scrollbar, method called from a parent with defined
* dimensions.
*
* @private
* @function Highcharts.Scrollbar#position
* @param {number} x
* x-position on the chart
* @param {number} y
* y-position on the chart
* @param {number} width
* width of the scrollbar
* @param {number} height
* height of the scrollbar
*/
Scrollbar.prototype.position = function (x, y, width, height) {
var scroller = this,
options = scroller.options,
buttonsEnabled = options.buttonsEnabled,
_a = options.margin,
margin = _a === void 0 ? 0 : _a,
vertical = options.vertical,
method = scroller.rendered ? 'animate' : 'attr';
var xOffset = height,
yOffset = 0;
// Make the scrollbar visible when it is repositioned, #15763.
scroller.group.show();
scroller.x = x;
scroller.y = y + this.trackBorderWidth;
scroller.width = width; // Width with buttons
scroller.height = height;
scroller.xOffset = xOffset;
scroller.yOffset = yOffset;
// If Scrollbar is a vertical type, swap options:
if (vertical) {
scroller.width = scroller.yOffset = width = yOffset = scroller.size;
scroller.xOffset = xOffset = 0;
scroller.yOffset = yOffset = buttonsEnabled ? scroller.size : 0;
// Width without buttons
scroller.barWidth = height - (buttonsEnabled ? width * 2 : 0);
scroller.x = x = x + margin;
}
else {
scroller.height = height = scroller.size;
scroller.xOffset = xOffset = buttonsEnabled ? scroller.size : 0;
// Width without buttons
scroller.barWidth = width - (buttonsEnabled ? height * 2 : 0);
scroller.y = scroller.y + margin;
}
// Set general position for a group:
scroller.group[method]({
translateX: x,
translateY: scroller.y
});
// Resize background/track:
scroller.track[method]({
width: width,
height: height
});
// Move right/bottom button to its place:
scroller.scrollbarButtons[1][method]({
translateX: vertical ? 0 : width - xOffset,
translateY: vertical ? height - yOffset : 0
});
};
/**
* Removes the event handlers attached previously with addEvents.
*
* @private
* @function Highcharts.Scrollbar#removeEvents
*/
Scrollbar.prototype.removeEvents = function () {
this._events.forEach(function (args) {
Scrollbar_removeEvent.apply(null, args);
});
this._events.length = 0;
};
/**
* Render scrollbar with all required items.
*
* @private
* @function Highcharts.Scrollbar#render
*/
Scrollbar.prototype.render = function () {
var scroller = this,
renderer = scroller.renderer,
options = scroller.options,
size = scroller.size,
styledMode = scroller.chart.styledMode,
group = renderer.g('scrollbar')
.attr({
zIndex: options.zIndex
})
.hide() // Initially hide the scrollbar #15863
.add();
// Draw the scrollbar group
scroller.group = group;
// Draw the scrollbar track:
scroller.track = renderer.rect()
.addClass('highcharts-scrollbar-track')
.attr({
r: options.trackBorderRadius || 0,
height: size,
width: size
}).add(group);
if (!styledMode) {
scroller.track.attr({
fill: options.trackBackgroundColor,
stroke: options.trackBorderColor,
'stroke-width': options.trackBorderWidth
});
}
var trackBorderWidth = scroller.trackBorderWidth =
scroller.track.strokeWidth();
scroller.track.attr({
x: -Scrollbar_crisp(0, trackBorderWidth),
y: -Scrollbar_crisp(0, trackBorderWidth)
});
// Draw the scrollbar itself
scroller.scrollbarGroup = renderer.g().add(group);
scroller.scrollbar = renderer.rect()
.addClass('highcharts-scrollbar-thumb')
.attr({
height: size - trackBorderWidth,
width: size - trackBorderWidth,
r: options.barBorderRadius || 0
}).add(scroller.scrollbarGroup);
scroller.scrollbarRifles = renderer
.path(Scrollbar.swapXY([
['M', -3, size / 4],
['L', -3, 2 * size / 3],
['M', 0, size / 4],
['L', 0, 2 * size / 3],
['M', 3, size / 4],
['L', 3, 2 * size / 3]
], options.vertical))
.addClass('highcharts-scrollbar-rifles')
.add(scroller.scrollbarGroup);
if (!styledMode) {
scroller.scrollbar.attr({
fill: options.barBackgroundColor,
stroke: options.barBorderColor,
'stroke-width': options.barBorderWidth
});
scroller.scrollbarRifles.attr({
stroke: options.rifleColor,
'stroke-width': 1
});
}
scroller.scrollbarStrokeWidth = scroller.scrollbar.strokeWidth();
scroller.scrollbarGroup.translate(-Scrollbar_crisp(0, scroller.scrollbarStrokeWidth), -Scrollbar_crisp(0, scroller.scrollbarStrokeWidth));
// Draw the buttons:
scroller.drawScrollbarButton(0);
scroller.drawScrollbarButton(1);
};
/**
* Set scrollbar size, with a given scale.
*
* @private
* @function Highcharts.Scrollbar#setRange
* @param {number} from
* scale (0-1) where bar should start
* @param {number} to
* scale (0-1) where bar should end
*/
Scrollbar.prototype.setRange = function (from, to) {
var scroller = this,
options = scroller.options,
vertical = options.vertical,
minWidth = options.minWidth,
fullWidth = scroller.barWidth,
method = (this.rendered &&
!this.hasDragged &&
!(this.chart.navigator && this.chart.navigator.hasDragged)) ? 'animate' : 'attr';
if (!Scrollbar_defined(fullWidth)) {
return;
}
var toPX = fullWidth * Math.min(to, 1);
var fromPX,
newSize;
from = Math.max(from, 0);
fromPX = Math.ceil(fullWidth * from);
scroller.calculatedWidth = newSize = Scrollbar_correctFloat(toPX - fromPX);
// We need to recalculate position, if minWidth is used
if (newSize < minWidth) {
fromPX = (fullWidth - minWidth + newSize) * from;
newSize = minWidth;
}
var newPos = Math.floor(fromPX + scroller.xOffset + scroller.yOffset);
var newRiflesPos = newSize / 2 - 0.5; // -0.5 -> rifle line width / 2
// Store current position:
scroller.from = from;
scroller.to = to;
if (!vertical) {
scroller.scrollbarGroup[method]({
translateX: newPos
});
scroller.scrollbar[method]({
width: newSize
});
scroller.scrollbarRifles[method]({
translateX: newRiflesPos
});
scroller.scrollbarLeft = newPos;
scroller.scrollbarTop = 0;
}
else {
scroller.scrollbarGroup[method]({
translateY: newPos
});
scroller.scrollbar[method]({
height: newSize
});
scroller.scrollbarRifles[method]({
translateY: newRiflesPos
});
scroller.scrollbarTop = newPos;
scroller.scrollbarLeft = 0;
}
if (newSize <= 12) {
scroller.scrollbarRifles.hide();
}
else {
scroller.scrollbarRifles.show();
}
// Show or hide the scrollbar based on the showFull setting
if (options.showFull === false) {
if (from <= 0 && to >= 1) {
scroller.group.hide();
}
else {
scroller.group.show();
}
}
scroller.rendered = true;
};
/**
* Checks if the extremes should be updated in response to a scrollbar
* change event.
*
* @private
* @function Highcharts.Scrollbar#shouldUpdateExtremes
*/
Scrollbar.prototype.shouldUpdateExtremes = function (eventType) {
return (Scrollbar_pick(this.options.liveRedraw, Core_Globals.svg &&
!Core_Globals.isTouchDevice &&
!this.chart.boosted) ||
// Mouseup always should change extremes
eventType === 'mouseup' ||
eventType === 'touchend' ||
// Internal events
!Scrollbar_defined(eventType));
};
Scrollbar.prototype.trackClick = function (e) {
var _a;
var scroller = this;
var normalizedEvent = ((_a = scroller.chart.pointer) === null || _a === void 0 ? void 0 : _a.normalize(e)) || e,
range = scroller.to - scroller.from,
top = scroller.y + scroller.scrollbarTop,
left = scroller.x + scroller.scrollbarLeft;
if ((scroller.options.vertical && normalizedEvent.chartY > top) ||
(!scroller.options.vertical && normalizedEvent.chartX > left)) {
// On the top or on the left side of the track:
scroller.updatePosition(scroller.from + range, scroller.to + range);
}
else {
// On the bottom or the right side of the track:
scroller.updatePosition(scroller.from - range, scroller.to - range);
}
Scrollbar_fireEvent(scroller, 'changed', {
from: scroller.from,
to: scroller.to,
trigger: 'scrollbar',
DOMEvent: e
});
};
/**
* Update the scrollbar with new options
*
* @private
* @function Highcharts.Scrollbar#update
* @param {Highcharts.ScrollbarOptions} options
*/
Scrollbar.prototype.update = function (options) {
this.destroy();
this.init(this.chart.renderer, Scrollbar_merge(true, this.options, options), this.chart);
};
/**
* Update position option in the Scrollbar, with normalized 0-1 scale
*
* @private
* @function Highcharts.Scrollbar#updatePosition
* @param {number} from
* @param {number} to
*/
Scrollbar.prototype.updatePosition = function (from, to) {
if (to > 1) {
from = Scrollbar_correctFloat(1 - Scrollbar_correctFloat(to - from));
to = 1;
}
if (from < 0) {
to = Scrollbar_correctFloat(to - from);
from = 0;
}
this.from = from;
this.to = to;
};
/* *
*
* Static Properties
*
* */
Scrollbar.defaultOptions = Scrollbar_ScrollbarDefaults;
return Scrollbar;
}());
/* *
*
* Registry
*
* */
Scrollbar_defaultOptions.scrollbar = Scrollbar_merge(true, Scrollbar.defaultOptions, Scrollbar_defaultOptions.scrollbar);
/* *
*
* Default Export
*
* */
/* harmony default export */ var Scrollbar_Scrollbar = (Scrollbar);
;// ./code/es5/es-modules/Stock/Navigator/Navigator.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var Navigator_assign = (undefined && undefined.__assign) || function () {
Navigator_assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return Navigator_assign.apply(this, arguments);
};
var Navigator_defaultOptions = Defaults.defaultOptions;
var Navigator_isTouchDevice = Core_Globals.isTouchDevice;
var symbols = SVG_SVGRenderer.prototype.symbols;
var Navigator_addEvent = Core_Utilities.addEvent, Navigator_clamp = Core_Utilities.clamp, Navigator_correctFloat = Core_Utilities.correctFloat, Navigator_defined = Core_Utilities.defined, Navigator_destroyObjectProperties = Core_Utilities.destroyObjectProperties, Navigator_erase = Core_Utilities.erase, Navigator_extend = Core_Utilities.extend, Navigator_find = Core_Utilities.find, Navigator_fireEvent = Core_Utilities.fireEvent, Navigator_isArray = Core_Utilities.isArray, Navigator_isNumber = Core_Utilities.isNumber, Navigator_merge = Core_Utilities.merge, Navigator_pick = Core_Utilities.pick, Navigator_removeEvent = Core_Utilities.removeEvent, Navigator_splat = Core_Utilities.splat;
/* *
*
* Functions
*
* */
/**
* Finding the min or max of a set of variables where we don't know if they are
* defined, is a pattern that is repeated several places in Highcharts. Consider
* making this a global utility method.
* @private
*/
function numExt(extreme) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
var numbers = [].filter.call(args,
Navigator_isNumber);
if (numbers.length) {
return Math[extreme].apply(0, numbers);
}
}
/* *
*
* Class
*
* */
/**
* The Navigator class
*
* @private
* @class
* @name Highcharts.Navigator
*
* @param {Highcharts.Chart} chart
* Chart object
*/
var Navigator = /** @class */ (function () {
/* *
*
* Constructor
*
* */
function Navigator(chart) {
this.isDirty = false;
this.scrollbarHeight = 0;
this.init(chart);
}
/* *
*
* Static Properties
*
* */
Navigator.compose = function (ChartClass, AxisClass, SeriesClass) {
Navigator_ChartNavigatorComposition.compose(ChartClass, Navigator);
Navigator_NavigatorComposition.compose(ChartClass, AxisClass, SeriesClass);
};
/* *
*
* Functions
*
* */
/**
* Draw one of the handles on the side of the zoomed range in the navigator.
*
* @private
* @function Highcharts.Navigator#drawHandle
*
* @param {number} x
* The x center for the handle
*
* @param {number} index
* 0 for left and 1 for right
*
* @param {boolean|undefined} inverted
* Flag for chart.inverted
*
* @param {string} verb
* Use 'animate' or 'attr'
*/
Navigator.prototype.drawHandle = function (x, index, inverted, verb) {
var navigator = this,
height = navigator.navigatorOptions.handles.height;
// Place it
navigator.handles[index][verb](inverted ? {
translateX: Math.round(navigator.left + navigator.height / 2),
translateY: Math.round(navigator.top + parseInt(x, 10) + 0.5 - height)
} : {
translateX: Math.round(navigator.left + parseInt(x, 10)),
translateY: Math.round(navigator.top + navigator.height / 2 - height / 2 - 1)
});
};
/**
* Render outline around the zoomed range
*
* @private
* @function Highcharts.Navigator#drawOutline
*
* @param {number} zoomedMin
* in pixels position where zoomed range starts
*
* @param {number} zoomedMax
* in pixels position where zoomed range ends
*
* @param {boolean|undefined} inverted
* flag if chart is inverted
*
* @param {string} verb
* use 'animate' or 'attr'
*/
Navigator.prototype.drawOutline = function (zoomedMin, zoomedMax, inverted, verb) {
var navigator = this, maskInside = navigator.navigatorOptions.maskInside, outlineWidth = navigator.outline.strokeWidth(), halfOutline = outlineWidth / 2, outlineCorrection = (outlineWidth % 2) / 2, // #5800
scrollButtonSize = navigator.scrollButtonSize, navigatorSize = navigator.size, navigatorTop = navigator.top, height = navigator.height, lineTop = navigatorTop - halfOutline, lineBtm = navigatorTop + height;
var left = navigator.left,
verticalMin,
path;
if (inverted) {
verticalMin = navigatorTop + zoomedMax + outlineCorrection;
zoomedMax = navigatorTop + zoomedMin + outlineCorrection;
path = [
[
'M',
left + height,
navigatorTop - scrollButtonSize - outlineCorrection
],
// Top right of zoomed range
['L', left + height, verticalMin],
['L', left, verticalMin], // Top left of z.r.
['M', left, zoomedMax], // Bottom left of z.r.
['L', left + height, zoomedMax], // Bottom right of z.r.
[
'L',
left + height,
navigatorTop + navigatorSize + scrollButtonSize
]
];
if (maskInside) {
path.push(
// Upper left of zoomed range
['M', left + height, verticalMin - halfOutline],
// Upper right of z.r.
[
'L',
left + height,
zoomedMax + halfOutline
]);
}
}
else {
left -= scrollButtonSize;
zoomedMin += left + scrollButtonSize - outlineCorrection;
zoomedMax += left + scrollButtonSize - outlineCorrection;
path = [
// Left
['M', left, lineTop],
// Upper left of zoomed range
['L', zoomedMin, lineTop],
// Lower left of z.r.
['L', zoomedMin, lineBtm],
// Lower right of z.r.
['M', zoomedMax, lineBtm],
// Upper right of z.r.
['L', zoomedMax, lineTop],
// Right
[
'L',
left + navigatorSize + scrollButtonSize * 2,
lineTop
]
];
if (maskInside) {
path.push(
// Upper left of zoomed range
['M', zoomedMin - halfOutline, lineTop],
// Upper right of z.r.
['L', zoomedMax + halfOutline, lineTop]);
}
}
navigator.outline[verb]({
d: path
});
};
/**
* Render outline around the zoomed range
*
* @private
* @function Highcharts.Navigator#drawMasks
*
* @param {number} zoomedMin
* in pixels position where zoomed range starts
*
* @param {number} zoomedMax
* in pixels position where zoomed range ends
*
* @param {boolean|undefined} inverted
* flag if chart is inverted
*
* @param {string} verb
* use 'animate' or 'attr'
*/
Navigator.prototype.drawMasks = function (zoomedMin, zoomedMax, inverted, verb) {
var navigator = this,
left = navigator.left,
top = navigator.top,
navigatorHeight = navigator.height;
var height,
width,
x,
y;
// Determine rectangle position & size
// According to (non)inverted position:
if (inverted) {
x = [left, left, left];
y = [top, top + zoomedMin, top + zoomedMax];
width = [navigatorHeight, navigatorHeight, navigatorHeight];
height = [
zoomedMin,
zoomedMax - zoomedMin,
navigator.size - zoomedMax
];
}
else {
x = [left, left + zoomedMin, left + zoomedMax];
y = [top, top, top];
width = [
zoomedMin,
zoomedMax - zoomedMin,
navigator.size - zoomedMax
];
height = [navigatorHeight, navigatorHeight, navigatorHeight];
}
navigator.shades.forEach(function (shade, i) {
shade[verb]({
x: x[i],
y: y[i],
width: width[i],
height: height[i]
});
});
};
/**
* Generate and update DOM elements for a navigator:
*
* - main navigator group
*
* - all shades
*
* - outline
*
* - handles
*
* @private
* @function Highcharts.Navigator#renderElements
*/
Navigator.prototype.renderElements = function () {
var _a,
_b;
var navigator = this,
navigatorOptions = navigator.navigatorOptions,
maskInside = navigatorOptions.maskInside,
chart = navigator.chart,
inverted = chart.inverted,
renderer = chart.renderer,
mouseCursor = {
cursor: inverted ? 'ns-resize' : 'ew-resize'
},
// Create the main navigator group
navigatorGroup = (_a = navigator.navigatorGroup) !== null && _a !== void 0 ? _a : (navigator.navigatorGroup = renderer
.g('navigator')
.attr({
zIndex: 8,
visibility: 'hidden'
})
.add());
// Create masks, each mask will get events and fill:
[
!maskInside,
maskInside,
!maskInside
].forEach(function (hasMask, index) {
var _a;
var shade = (_a = navigator.shades[index]) !== null && _a !== void 0 ? _a : (navigator.shades[index] = renderer.rect()
.addClass('highcharts-navigator-mask' +
(index === 1 ? '-inside' : '-outside'))
.add(navigatorGroup));
if (!chart.styledMode) {
shade.attr({
fill: hasMask ? navigatorOptions.maskFill : 'rgba(0,0,0,0)'
});
if (index === 1) {
shade.css(mouseCursor);
}
}
});
// Create the outline:
if (!navigator.outline) {
navigator.outline = renderer.path()
.addClass('highcharts-navigator-outline')
.add(navigatorGroup);
}
if (!chart.styledMode) {
navigator.outline.attr({
'stroke-width': navigatorOptions.outlineWidth,
stroke: navigatorOptions.outlineColor
});
}
// Create the handlers:
if ((_b = navigatorOptions.handles) === null || _b === void 0 ? void 0 : _b.enabled) {
var handlesOptions_1 = navigatorOptions.handles,
height_1 = handlesOptions_1.height,
width_1 = handlesOptions_1.width;
[0, 1].forEach(function (index) {
var _a;
var symbolName = handlesOptions_1.symbols[index];
if (!navigator.handles[index] ||
navigator.handles[index].symbolUrl !== symbolName) {
// Generate symbol from scratch if we're dealing with an URL
(_a = navigator.handles[index]) === null || _a === void 0 ? void 0 : _a.destroy();
navigator.handles[index] = renderer.symbol(symbolName, -width_1 / 2 - 1, 0, width_1, height_1, handlesOptions_1);
// Z index is 6 for right handle, 7 for left. Can't be 10,
// because of the tooltip in inverted chart (#2908).
navigator.handles[index].attr({ zIndex: 7 - index })
.addClass('highcharts-navigator-handle ' +
'highcharts-navigator-handle-' +
['left', 'right'][index]).add(navigatorGroup);
navigator.addMouseEvents();
// If the navigator symbol changed, update its path and name
}
else if (!navigator.handles[index].isImg &&
navigator.handles[index].symbolName !== symbolName) {
var symbolFn = symbols[symbolName],
path = symbolFn.call(symbols, -width_1 / 2 - 1, 0,
width_1,
height_1);
navigator.handles[index].attr({
d: path
});
navigator.handles[index].symbolName = symbolName;
}
if (chart.inverted) {
navigator.handles[index].attr({
rotation: 90,
rotationOriginX: Math.floor(-width_1 / 2),
rotationOriginY: (height_1 + width_1) / 2
});
}
if (!chart.styledMode) {
navigator.handles[index]
.attr({
fill: handlesOptions_1.backgroundColor,
stroke: handlesOptions_1.borderColor,
'stroke-width': handlesOptions_1.lineWidth,
width: handlesOptions_1.width,
height: handlesOptions_1.height,
x: -width_1 / 2 - 1,
y: 0
})
.css(mouseCursor);
}
});
}
};
/**
* Update navigator
*
* @private
* @function Highcharts.Navigator#update
*
* @param {Highcharts.NavigatorOptions} options
* Options to merge in when updating navigator
*/
Navigator.prototype.update = function (options, redraw) {
var _a,
_b;
var _this = this;
var _c,
_d;
if (redraw === void 0) { redraw = false; }
var chart = this.chart,
invertedUpdate = chart.options.chart.inverted !==
((_c = chart.scrollbar) === null || _c === void 0 ? void 0 : _c.options.vertical);
Navigator_merge(true, chart.options.navigator, options);
this.navigatorOptions = chart.options.navigator || {};
this.setOpposite();
// Revert to destroy/init for navigator/scrollbar enabled toggle
if (Navigator_defined(options.enabled) || invertedUpdate) {
this.destroy();
this.navigatorEnabled = options.enabled || this.navigatorEnabled;
return this.init(chart);
}
if (this.navigatorEnabled) {
this.isDirty = true;
if (options.adaptToUpdatedData === false) {
this.baseSeries.forEach(function (series) {
Navigator_removeEvent(series, 'updatedData', _this.updatedDataHandler);
}, this);
}
if (options.adaptToUpdatedData) {
this.baseSeries.forEach(function (series) {
series.eventsToUnbind.push(Navigator_addEvent(series, 'updatedData', _this.updatedDataHandler));
}, this);
}
// Update navigator series
if (options.series || options.baseSeries) {
this.setBaseSeries(void 0, false);
}
// Update navigator axis
if (options.height || options.xAxis || options.yAxis) {
this.height = (_d = options.height) !== null && _d !== void 0 ? _d : this.height;
var offsets = this.getXAxisOffsets();
this.xAxis.update(Navigator_assign(Navigator_assign({}, options.xAxis), (_a = { offsets: offsets }, _a[chart.inverted ? 'width' : 'height'] = this.height, _a[chart.inverted ? 'height' : 'width'] = void 0, _a)), false);
this.yAxis.update(Navigator_assign(Navigator_assign({}, options.yAxis), (_b = {}, _b[chart.inverted ? 'width' : 'height'] = this.height, _b)), false);
}
}
if (redraw) {
chart.redraw();
}
};
/**
* Render the navigator
*
* @private
* @function Highcharts.Navigator#render
* @param {number} min
* X axis value minimum
* @param {number} max
* X axis value maximum
* @param {number} [pxMin]
* Pixel value minimum
* @param {number} [pxMax]
* Pixel value maximum
*/
Navigator.prototype.render = function (min, max, pxMin, pxMax) {
var navigator = this,
chart = navigator.chart,
xAxis = navigator.xAxis,
pointRange = xAxis.pointRange || 0,
scrollbarXAxis = xAxis.navigatorAxis.fake ? chart.xAxis[0] : xAxis,
navigatorEnabled = navigator.navigatorEnabled,
rendered = navigator.rendered,
inverted = chart.inverted,
minRange = chart.xAxis[0].minRange,
maxRange = chart.xAxis[0].options.maxRange,
scrollButtonSize = navigator.scrollButtonSize;
var navigatorWidth,
scrollbarLeft,
scrollbarTop,
scrollbarHeight = navigator.scrollbarHeight,
navigatorSize,
verb;
// Don't redraw while moving the handles (#4703).
if (this.hasDragged && !Navigator_defined(pxMin)) {
return;
}
if (this.isDirty) {
// Update DOM navigator elements
this.renderElements();
}
min = Navigator_correctFloat(min - pointRange / 2);
max = Navigator_correctFloat(max + pointRange / 2);
// Don't render the navigator until we have data (#486, #4202, #5172).
if (!Navigator_isNumber(min) || !Navigator_isNumber(max)) {
// However, if navigator was already rendered, we may need to resize
// it. For example hidden series, but visible navigator (#6022).
if (rendered) {
pxMin = 0;
pxMax = Navigator_pick(xAxis.width, scrollbarXAxis.width);
}
else {
return;
}
}
navigator.left = Navigator_pick(xAxis.left,
// In case of scrollbar only, without navigator
chart.plotLeft + scrollButtonSize +
(inverted ? chart.plotWidth : 0));
var zoomedMax = navigator.size = navigatorSize = Navigator_pick(xAxis.len, (inverted ? chart.plotHeight : chart.plotWidth) -
2 * scrollButtonSize);
if (inverted) {
navigatorWidth = scrollbarHeight;
}
else {
navigatorWidth = navigatorSize + 2 * scrollButtonSize;
}
// Get the pixel position of the handles
pxMin = Navigator_pick(pxMin, xAxis.toPixels(min, true));
pxMax = Navigator_pick(pxMax, xAxis.toPixels(max, true));
// Verify (#1851, #2238)
if (!Navigator_isNumber(pxMin) || Math.abs(pxMin) === Infinity) {
pxMin = 0;
pxMax = navigatorWidth;
}
// Are we below the minRange? (#2618, #6191)
var newMin = xAxis.toValue(pxMin,
true),
newMax = xAxis.toValue(pxMax,
true),
currentRange = Math.abs(Navigator_correctFloat(newMax - newMin));
if (currentRange < minRange) {
if (this.grabbedLeft) {
pxMin = xAxis.toPixels(newMax - minRange - pointRange, true);
}
else if (this.grabbedRight) {
pxMax = xAxis.toPixels(newMin + minRange + pointRange, true);
}
}
else if (Navigator_defined(maxRange) &&
Navigator_correctFloat(currentRange - pointRange) > maxRange) {
if (this.grabbedLeft) {
pxMin = xAxis.toPixels(newMax - maxRange - pointRange, true);
}
else if (this.grabbedRight) {
pxMax = xAxis.toPixels(newMin + maxRange + pointRange, true);
}
}
// Handles are allowed to cross, but never exceed the plot area
navigator.zoomedMax = Navigator_clamp(Math.max(pxMin, pxMax), 0, zoomedMax);
navigator.zoomedMin = Navigator_clamp(navigator.fixedWidth ?
navigator.zoomedMax - navigator.fixedWidth :
Math.min(pxMin, pxMax), 0, zoomedMax);
navigator.range = navigator.zoomedMax - navigator.zoomedMin;
zoomedMax = Math.round(navigator.zoomedMax);
var zoomedMin = Math.round(navigator.zoomedMin);
if (navigatorEnabled) {
navigator.navigatorGroup.attr({
visibility: 'inherit'
});
// Place elements
verb = rendered && !navigator.hasDragged ? 'animate' : 'attr';
navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
if (navigator.navigatorOptions.handles.enabled) {
navigator.drawHandle(zoomedMin, 0, inverted, verb);
navigator.drawHandle(zoomedMax, 1, inverted, verb);
}
}
if (navigator.scrollbar) {
if (inverted) {
scrollbarTop = navigator.top - scrollButtonSize;
scrollbarLeft = navigator.left - scrollbarHeight +
(navigatorEnabled || !scrollbarXAxis.opposite ? 0 :
// Multiple axes has offsets:
(scrollbarXAxis.titleOffset || 0) +
// Self margin from the axis.title
scrollbarXAxis.axisTitleMargin);
scrollbarHeight = navigatorSize + 2 * scrollButtonSize;
}
else {
scrollbarTop = navigator.top + (navigatorEnabled ?
navigator.height :
-scrollbarHeight);
scrollbarLeft = navigator.left - scrollButtonSize;
}
// Reposition scrollbar
navigator.scrollbar.position(scrollbarLeft, scrollbarTop, navigatorWidth, scrollbarHeight);
// Keep scale 0-1
navigator.scrollbar.setRange(
// Use real value, not rounded because range can be very small
// (#1716)
navigator.zoomedMin / (navigatorSize || 1), navigator.zoomedMax / (navigatorSize || 1));
}
navigator.rendered = true;
this.isDirty = false;
Navigator_fireEvent(this, 'afterRender');
};
/**
* Set up the mouse and touch events for the navigator
*
* @private
* @function Highcharts.Navigator#addMouseEvents
*/
Navigator.prototype.addMouseEvents = function () {
var navigator = this,
chart = navigator.chart,
container = chart.container;
var eventsToUnbind = [],
mouseMoveHandler,
mouseUpHandler;
/**
* Create mouse events' handlers.
* Make them as separate functions to enable wrapping them:
*/
navigator.mouseMoveHandler = mouseMoveHandler = function (e) {
navigator.onMouseMove(e);
};
navigator.mouseUpHandler = mouseUpHandler = function (e) {
navigator.onMouseUp(e);
};
// Add shades and handles mousedown events
eventsToUnbind = navigator.getPartsEvents('mousedown');
eventsToUnbind.push(
// Add mouse move and mouseup events. These are bind to doc/div,
// because Navigator.grabbedSomething flags are stored in mousedown
// events
Navigator_addEvent(chart.renderTo, 'mousemove', mouseMoveHandler), Navigator_addEvent(container.ownerDocument, 'mouseup', mouseUpHandler),
// Touch events
Navigator_addEvent(chart.renderTo, 'touchmove', mouseMoveHandler), Navigator_addEvent(container.ownerDocument, 'touchend', mouseUpHandler));
eventsToUnbind.concat(navigator.getPartsEvents('touchstart'));
navigator.eventsToUnbind = eventsToUnbind;
// Data events
if (navigator.series && navigator.series[0]) {
eventsToUnbind.push(Navigator_addEvent(navigator.series[0].xAxis, 'foundExtremes', function () {
chart.navigator.modifyNavigatorAxisExtremes();
}));
}
};
/**
* Generate events for handles and masks
*
* @private
* @function Highcharts.Navigator#getPartsEvents
*
* @param {string} eventName
* Event name handler, 'mousedown' or 'touchstart'
*
* @return {Array<Function>}
* An array of functions to remove navigator functions from the
* events again.
*/
Navigator.prototype.getPartsEvents = function (eventName) {
var navigator = this,
events = [];
['shades', 'handles'].forEach(function (name) {
navigator[name].forEach(function (navigatorItem, index) {
events.push(Navigator_addEvent(navigatorItem.element, eventName, function (e) {
navigator[name + 'Mousedown'](e, index);
}));
});
});
return events;
};
/**
* Mousedown on a shaded mask, either:
*
* - will be stored for future drag&drop
*
* - will directly shift to a new range
*
* @private
* @function Highcharts.Navigator#shadesMousedown
*
* @param {Highcharts.PointerEventObject} e
* Mouse event
*
* @param {number} index
* Index of a mask in Navigator.shades array
*/
Navigator.prototype.shadesMousedown = function (e, index) {
var _a;
e = ((_a = this.chart.pointer) === null || _a === void 0 ? void 0 : _a.normalize(e)) || e;
var navigator = this,
chart = navigator.chart,
xAxis = navigator.xAxis,
zoomedMin = navigator.zoomedMin,
navigatorSize = navigator.size,
range = navigator.range;
var navigatorPosition = navigator.left,
chartX = e.chartX,
fixedMax,
fixedMin,
ext,
left;
// For inverted chart, swap some options:
if (chart.inverted) {
chartX = e.chartY;
navigatorPosition = navigator.top;
}
if (index === 1) {
// Store information for drag&drop
navigator.grabbedCenter = chartX;
navigator.fixedWidth = range;
navigator.dragOffset = chartX - zoomedMin;
}
else {
// Shift the range by clicking on shaded areas
left = chartX - navigatorPosition - range / 2;
if (index === 0) {
left = Math.max(0, left);
}
else if (index === 2 && left + range >= navigatorSize) {
left = navigatorSize - range;
if (navigator.reversedExtremes) {
// #7713
left -= range;
fixedMin = navigator.getUnionExtremes().dataMin;
}
else {
// #2293, #3543
fixedMax = navigator.getUnionExtremes().dataMax;
}
}
if (left !== zoomedMin) { // It has actually moved
navigator.fixedWidth = range; // #1370
ext = xAxis.navigatorAxis.toFixedRange(left, left + range, fixedMin, fixedMax);
if (Navigator_defined(ext.min)) { // #7411
Navigator_fireEvent(this, 'setRange', {
min: Math.min(ext.min, ext.max),
max: Math.max(ext.min, ext.max),
redraw: true,
eventArguments: {
trigger: 'navigator'
}
});
}
}
}
};
/**
* Mousedown on a handle mask.
* Will store necessary information for drag&drop.
*
* @private
* @function Highcharts.Navigator#handlesMousedown
* @param {Highcharts.PointerEventObject} e
* Mouse event
* @param {number} index
* Index of a handle in Navigator.handles array
*/
Navigator.prototype.handlesMousedown = function (e, index) {
var _a;
e = ((_a = this.chart.pointer) === null || _a === void 0 ? void 0 : _a.normalize(e)) || e;
var navigator = this,
chart = navigator.chart,
baseXAxis = chart.xAxis[0],
// For reversed axes, min and max are changed,
// so the other extreme should be stored
reverse = navigator.reversedExtremes;
if (index === 0) {
// Grab the left handle
navigator.grabbedLeft = true;
navigator.otherHandlePos = navigator.zoomedMax;
navigator.fixedExtreme = reverse ? baseXAxis.min : baseXAxis.max;
}
else {
// Grab the right handle
navigator.grabbedRight = true;
navigator.otherHandlePos = navigator.zoomedMin;
navigator.fixedExtreme = reverse ? baseXAxis.max : baseXAxis.min;
}
chart.setFixedRange(void 0);
};
/**
* Mouse move event based on x/y mouse position.
*
* @private
* @function Highcharts.Navigator#onMouseMove
*
* @param {Highcharts.PointerEventObject} e
* Mouse event
*/
Navigator.prototype.onMouseMove = function (e) {
var _a;
var navigator = this,
chart = navigator.chart,
navigatorSize = navigator.navigatorSize,
range = navigator.range,
dragOffset = navigator.dragOffset,
inverted = chart.inverted;
var left = navigator.left,
chartX;
// In iOS, a mousemove event with e.pageX === 0 is fired when holding
// the finger down in the center of the scrollbar. This should be
// ignored.
if (!e.touches || e.touches[0].pageX !== 0) { // #4696
e = ((_a = chart.pointer) === null || _a === void 0 ? void 0 : _a.normalize(e)) || e;
chartX = e.chartX;
// Swap some options for inverted chart
if (inverted) {
left = navigator.top;
chartX = e.chartY;
}
// Drag left handle or top handle
if (navigator.grabbedLeft) {
navigator.hasDragged = true;
navigator.render(0, 0, chartX - left, navigator.otherHandlePos);
// Drag right handle or bottom handle
}
else if (navigator.grabbedRight) {
navigator.hasDragged = true;
navigator.render(0, 0, navigator.otherHandlePos, chartX - left);
// Drag scrollbar or open area in navigator
}
else if (navigator.grabbedCenter) {
navigator.hasDragged = true;
if (chartX < dragOffset) { // Outside left
chartX = dragOffset;
// Outside right
}
else if (chartX >
navigatorSize + dragOffset - range) {
chartX = navigatorSize + dragOffset - range;
}
navigator.render(0, 0, chartX - dragOffset, chartX - dragOffset + range);
}
if (navigator.hasDragged &&
navigator.scrollbar &&
Navigator_pick(navigator.scrollbar.options.liveRedraw,
// By default, don't run live redraw on touch
// devices or if the chart is in boost.
!Navigator_isTouchDevice &&
!this.chart.boosted)) {
e.DOMType = e.type;
setTimeout(function () {
navigator.onMouseUp(e);
}, 0);
}
}
};
/**
* Mouse up event based on x/y mouse position.
*
* @private
* @function Highcharts.Navigator#onMouseUp
* @param {Highcharts.PointerEventObject} e
* Mouse event
*/
Navigator.prototype.onMouseUp = function (e) {
var navigator = this,
chart = navigator.chart,
xAxis = navigator.xAxis,
scrollbar = navigator.scrollbar,
DOMEvent = e.DOMEvent || e,
inverted = chart.inverted,
verb = navigator.rendered && !navigator.hasDragged ?
'animate' : 'attr';
var zoomedMax,
zoomedMin,
unionExtremes,
fixedMin,
fixedMax,
ext;
if (
// MouseUp is called for both, navigator and scrollbar (that order),
// which causes calling afterSetExtremes twice. Prevent first call
// by checking if scrollbar is going to set new extremes (#6334)
(navigator.hasDragged && (!scrollbar || !scrollbar.hasDragged)) ||
e.trigger === 'scrollbar') {
unionExtremes = navigator.getUnionExtremes();
// When dragging one handle, make sure the other one doesn't change
if (navigator.zoomedMin === navigator.otherHandlePos) {
fixedMin = navigator.fixedExtreme;
}
else if (navigator.zoomedMax === navigator.otherHandlePos) {
fixedMax = navigator.fixedExtreme;
}
// Snap to right edge (#4076)
if (navigator.zoomedMax === navigator.size) {
fixedMax = navigator.reversedExtremes ?
unionExtremes.dataMin :
unionExtremes.dataMax;
}
// Snap to left edge (#7576)
if (navigator.zoomedMin === 0) {
fixedMin = navigator.reversedExtremes ?
unionExtremes.dataMax :
unionExtremes.dataMin;
}
ext = xAxis.navigatorAxis.toFixedRange(navigator.zoomedMin, navigator.zoomedMax, fixedMin, fixedMax);
if (Navigator_defined(ext.min)) {
Navigator_fireEvent(this, 'setRange', {
min: Math.min(ext.min, ext.max),
max: Math.max(ext.min, ext.max),
redraw: true,
animation: navigator.hasDragged ? false : null,
eventArguments: {
trigger: 'navigator',
triggerOp: 'navigator-drag',
DOMEvent: DOMEvent // #1838
}
});
}
}
if (e.DOMType !== 'mousemove' &&
e.DOMType !== 'touchmove') {
navigator.grabbedLeft = navigator.grabbedRight =
navigator.grabbedCenter = navigator.fixedWidth =
navigator.fixedExtreme = navigator.otherHandlePos =
navigator.hasDragged = navigator.dragOffset = null;
}
// Update position of navigator shades, outline and handles (#12573)
if (navigator.navigatorEnabled &&
Navigator_isNumber(navigator.zoomedMin) &&
Navigator_isNumber(navigator.zoomedMax)) {
zoomedMin = Math.round(navigator.zoomedMin);
zoomedMax = Math.round(navigator.zoomedMax);
if (navigator.shades) {
navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
}
if (navigator.outline) {
navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
}
if (navigator.navigatorOptions.handles.enabled &&
Object.keys(navigator.handles).length ===
navigator.handles.length) {
navigator.drawHandle(zoomedMin, 0, inverted, verb);
navigator.drawHandle(zoomedMax, 1, inverted, verb);
}
}
};
/**
* Removes the event handlers attached previously with addEvents.
*
* @private
* @function Highcharts.Navigator#removeEvents
*/
Navigator.prototype.removeEvents = function () {
if (this.eventsToUnbind) {
this.eventsToUnbind.forEach(function (unbind) {
unbind();
});
this.eventsToUnbind = void 0;
}
this.removeBaseSeriesEvents();
};
/**
* Remove data events.
*
* @private
* @function Highcharts.Navigator#removeBaseSeriesEvents
*/
Navigator.prototype.removeBaseSeriesEvents = function () {
var baseSeries = this.baseSeries || [];
if (this.navigatorEnabled && baseSeries[0]) {
if (this.navigatorOptions.adaptToUpdatedData !== false) {
baseSeries.forEach(function (series) {
Navigator_removeEvent(series, 'updatedData', this.updatedDataHandler);
}, this);
}
// We only listen for extremes-events on the first baseSeries
if (baseSeries[0].xAxis) {
Navigator_removeEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes);
}
}
};
/**
* Calculate the navigator xAxis offsets
*
* @private
*/
Navigator.prototype.getXAxisOffsets = function () {
return (this.chart.inverted ?
[this.scrollButtonSize, 0, -this.scrollButtonSize, 0] :
[0, -this.scrollButtonSize, 0, this.scrollButtonSize]);
};
/**
* Initialize the Navigator object
*
* @private
* @function Highcharts.Navigator#init
*/
Navigator.prototype.init = function (chart) {
var _a;
var chartOptions = chart.options,
navigatorOptions = chartOptions.navigator || {},
navigatorEnabled = navigatorOptions.enabled,
scrollbarOptions = chartOptions.scrollbar || {},
scrollbarEnabled = scrollbarOptions.enabled,
height = navigatorEnabled && navigatorOptions.height || 0,
scrollbarHeight = scrollbarEnabled && scrollbarOptions.height || 0,
scrollButtonSize = scrollbarOptions.buttonsEnabled && scrollbarHeight || 0;
this.handles = [];
this.shades = [];
this.chart = chart;
this.setBaseSeries();
this.height = height;
this.scrollbarHeight = scrollbarHeight;
this.scrollButtonSize = scrollButtonSize;
this.scrollbarEnabled = scrollbarEnabled;
this.navigatorEnabled = navigatorEnabled;
this.navigatorOptions = navigatorOptions;
this.scrollbarOptions = scrollbarOptions;
this.setOpposite();
var navigator = this,
baseSeries = navigator.baseSeries,
xAxisIndex = chart.xAxis.length,
yAxisIndex = chart.yAxis.length,
baseXaxis = baseSeries && baseSeries[0] && baseSeries[0].xAxis ||
chart.xAxis[0] || { options: {} };
chart.isDirtyBox = true;
if (navigator.navigatorEnabled) {
var offsets = this.getXAxisOffsets();
// An x axis is required for scrollbar also
navigator.xAxis = new Axis_Axis(chart, Navigator_merge({
// Inherit base xAxis' break, ordinal options and overscroll
breaks: baseXaxis.options.breaks,
ordinal: baseXaxis.options.ordinal,
overscroll: baseXaxis.options.overscroll
}, navigatorOptions.xAxis, {
type: 'datetime',
yAxis: (_a = navigatorOptions.yAxis) === null || _a === void 0 ? void 0 : _a.id,
index: xAxisIndex,
isInternal: true,
offset: 0,
keepOrdinalPadding: true, // #2436
startOnTick: false,
endOnTick: false,
// Inherit base xAxis' padding when ordinal is false (#16915).
minPadding: baseXaxis.options.ordinal ? 0 :
baseXaxis.options.minPadding,
maxPadding: baseXaxis.options.ordinal ? 0 :
baseXaxis.options.maxPadding,
zoomEnabled: false
}, chart.inverted ? {
offsets: offsets,
width: height
} : {
offsets: offsets,
height: height
}), 'xAxis');
navigator.yAxis = new Axis_Axis(chart, Navigator_merge(navigatorOptions.yAxis, {
alignTicks: false,
offset: 0,
index: yAxisIndex,
isInternal: true,
reversed: Navigator_pick((navigatorOptions.yAxis &&
navigatorOptions.yAxis.reversed), (chart.yAxis[0] && chart.yAxis[0].reversed), false), // #14060
zoomEnabled: false
}, chart.inverted ? {
width: height
} : {
height: height
}), 'yAxis');
// If we have a base series, initialize the navigator series
if (baseSeries || navigatorOptions.series.data) {
navigator.updateNavigatorSeries(false);
// If not, set up an event to listen for added series
}
else if (chart.series.length === 0) {
navigator.unbindRedraw = Navigator_addEvent(chart, 'beforeRedraw', function () {
// We've got one, now add it as base
if (chart.series.length > 0 && !navigator.series) {
navigator.setBaseSeries();
navigator.unbindRedraw(); // Reset
}
});
}
navigator.reversedExtremes = (chart.inverted && !navigator.xAxis.reversed) || (!chart.inverted && navigator.xAxis.reversed);
// Render items, so we can bind events to them:
navigator.renderElements();
// Add mouse events
navigator.addMouseEvents();
// In case of scrollbar only, fake an x axis to get translation
}
else {
navigator.xAxis = {
chart: chart,
navigatorAxis: {
fake: true
},
translate: function (value, reverse) {
var axis = chart.xAxis[0], ext = axis.getExtremes(), scrollTrackWidth = axis.len - 2 * scrollButtonSize, min = numExt('min', axis.options.min, ext.dataMin), valueRange = numExt('max', axis.options.max, ext.dataMax) - min;
return reverse ?
// From pixel to value
(value * valueRange / scrollTrackWidth) + min :
// From value to pixel
scrollTrackWidth * (value - min) / valueRange;
},
toPixels: function (value) {
return this.translate(value);
},
toValue: function (value) {
return this.translate(value, true);
}
};
navigator.xAxis.navigatorAxis.axis = navigator.xAxis;
navigator.xAxis.navigatorAxis.toFixedRange = (NavigatorAxisComposition.prototype.toFixedRange.bind(navigator.xAxis.navigatorAxis));
}
// Initialize the scrollbar
if (chart.options.scrollbar.enabled) {
var options = Navigator_merge(chart.options.scrollbar, { vertical: chart.inverted });
if (!Navigator_isNumber(options.margin) && navigator.navigatorEnabled) {
options.margin = chart.inverted ? -3 : 3;
}
chart.scrollbar = navigator.scrollbar = new Scrollbar_Scrollbar(chart.renderer, options, chart);
Navigator_addEvent(navigator.scrollbar, 'changed', function (e) {
var range = navigator.size,
to = range * this.to,
from = range * this.from;
navigator.hasDragged = navigator.scrollbar.hasDragged;
navigator.render(0, 0, from, to);
if (this.shouldUpdateExtremes(e.DOMType)) {
setTimeout(function () {
navigator.onMouseUp(e);
});
}
});
}
// Add data events
navigator.addBaseSeriesEvents();
// Add redraw events
navigator.addChartEvents();
};
/**
* Set the opposite property on navigator
*
* @private
*/
Navigator.prototype.setOpposite = function () {
var navigatorOptions = this.navigatorOptions,
navigatorEnabled = this.navigatorEnabled,
chart = this.chart;
this.opposite = Navigator_pick(navigatorOptions.opposite, Boolean(!navigatorEnabled && chart.inverted)); // #6262
};
/**
* Get the union data extremes of the chart - the outer data extremes of the
* base X axis and the navigator axis.
*
* @private
* @function Highcharts.Navigator#getUnionExtremes
*/
Navigator.prototype.getUnionExtremes = function (returnFalseOnNoBaseSeries) {
var baseAxis = this.chart.xAxis[0],
time = this.chart.time,
navAxis = this.xAxis,
navAxisOptions = navAxis.options,
baseAxisOptions = baseAxis.options;
var ret;
if (!returnFalseOnNoBaseSeries || baseAxis.dataMin !== null) {
ret = {
dataMin: Navigator_pick(// #4053
time.parse(navAxisOptions === null || navAxisOptions === void 0 ? void 0 : navAxisOptions.min), numExt('min', time.parse(baseAxisOptions.min), baseAxis.dataMin, navAxis.dataMin, navAxis.min)),
dataMax: Navigator_pick(time.parse(navAxisOptions === null || navAxisOptions === void 0 ? void 0 : navAxisOptions.max), numExt('max', time.parse(baseAxisOptions.max), baseAxis.dataMax, navAxis.dataMax, navAxis.max))
};
}
return ret;
};
/**
* Set the base series and update the navigator series from this. With a bit
* of modification we should be able to make this an API method to be called
* from the outside
*
* @private
* @function Highcharts.Navigator#setBaseSeries
* @param {Highcharts.SeriesOptionsType} [baseSeriesOptions]
* Additional series options for a navigator
* @param {boolean} [redraw]
* Whether to redraw after update.
*/
Navigator.prototype.setBaseSeries = function (baseSeriesOptions, redraw) {
var chart = this.chart,
baseSeries = this.baseSeries = [];
baseSeriesOptions = (baseSeriesOptions ||
chart.options && chart.options.navigator.baseSeries ||
(chart.series.length ?
// Find the first non-navigator series (#8430)
Navigator_find(chart.series, function (s) { return (!s.options.isInternal); }).index :
0));
// Iterate through series and add the ones that should be shown in
// navigator.
(chart.series || []).forEach(function (series, i) {
if (
// Don't include existing nav series
!series.options.isInternal &&
(series.options.showInNavigator ||
(i === baseSeriesOptions ||
series.options.id === baseSeriesOptions) &&
series.options.showInNavigator !== false)) {
baseSeries.push(series);
}
});
// When run after render, this.xAxis already exists
if (this.xAxis && !this.xAxis.navigatorAxis.fake) {
this.updateNavigatorSeries(true, redraw);
}
};
/**
* Update series in the navigator from baseSeries, adding new if does not
* exist.
*
* @private
* @function Highcharts.Navigator.updateNavigatorSeries
*/
Navigator.prototype.updateNavigatorSeries = function (addEvents, redraw) {
var _a,
_b;
var navigator = this,
chart = navigator.chart,
baseSeries = navigator.baseSeries,
navSeriesMixin = {
enableMouseTracking: false,
index: null, // #6162
linkedTo: null, // #6734
group: 'nav', // For columns
padXAxis: false,
xAxis: (_a = this.navigatorOptions.xAxis) === null || _a === void 0 ? void 0 : _a.id,
yAxis: (_b = this.navigatorOptions.yAxis) === null || _b === void 0 ? void 0 : _b.id,
showInLegend: false,
stacking: void 0, // #4823
isInternal: true,
states: {
inactive: {
opacity: 1
}
}
},
// Remove navigator series that are no longer in the baseSeries
navigatorSeries = navigator.series =
(navigator.series || []).filter(function (navSeries) {
var base = navSeries.baseSeries;
if (baseSeries.indexOf(base) < 0) { // Not in array
// If there is still a base series connected to this
// series, remove event handler and reference.
if (base) {
Navigator_removeEvent(base, 'updatedData', navigator.updatedDataHandler);
delete base.navigatorSeries;
}
// Kill the nav series. It may already have been
// destroyed (#8715).
if (navSeries.chart) {
navSeries.destroy();
}
return false;
}
return true;
});
var baseOptions,
mergedNavSeriesOptions,
chartNavigatorSeriesOptions = navigator.navigatorOptions.series,
baseNavigatorOptions;
// Go through each base series and merge the options to create new
// series
if (baseSeries && baseSeries.length) {
baseSeries.forEach(function (base) {
var _a;
var linkedNavSeries = base.navigatorSeries,
userNavOptions = Navigator_extend(
// Grab color and visibility from base as default
{
color: base.color,
visible: base.visible
}, !Navigator_isArray(chartNavigatorSeriesOptions) ?
chartNavigatorSeriesOptions :
Navigator_defaultOptions.navigator.series);
// Don't update if the series exists in nav and we have disabled
// adaptToUpdatedData.
if (linkedNavSeries &&
navigator.navigatorOptions.adaptToUpdatedData === false) {
return;
}
navSeriesMixin.name = 'Navigator ' + baseSeries.length;
baseOptions = base.options || {};
baseNavigatorOptions = baseOptions.navigatorOptions || {};
// The dataLabels options are not merged correctly
// if the settings are an array, #13847.
userNavOptions.dataLabels = Navigator_splat(userNavOptions.dataLabels);
mergedNavSeriesOptions = Navigator_merge(baseOptions, navSeriesMixin, userNavOptions, baseNavigatorOptions);
// Once nav series type is resolved, pick correct pointRange
mergedNavSeriesOptions.pointRange = Navigator_pick(
// Stricte set pointRange in options
userNavOptions.pointRange, baseNavigatorOptions.pointRange,
// Fallback to default values, e.g. `null` for column
Navigator_defaultOptions.plotOptions[mergedNavSeriesOptions.type || 'line'].pointRange);
// Merge data separately. Do a slice to avoid mutating the
// navigator options from base series (#4923).
var navigatorSeriesData = baseNavigatorOptions.data || userNavOptions.data;
navigator.hasNavigatorData =
navigator.hasNavigatorData || !!navigatorSeriesData;
mergedNavSeriesOptions.data = (navigatorSeriesData ||
((_a = baseOptions.data) === null || _a === void 0 ? void 0 : _a.slice(0)));
// Update or add the series
if (linkedNavSeries && linkedNavSeries.options) {
linkedNavSeries.update(mergedNavSeriesOptions, redraw);
}
else {
base.navigatorSeries = chart.initSeries(mergedNavSeriesOptions);
// Set data on initial run with dataSorting enabled (#20318)
chart.setSortedData();
base.navigatorSeries.baseSeries = base; // Store ref
navigatorSeries.push(base.navigatorSeries);
}
});
}
// If user has defined data (and no base series) or explicitly defined
// navigator.series as an array, we create these series on top of any
// base series.
if (chartNavigatorSeriesOptions.data &&
!(baseSeries && baseSeries.length) ||
Navigator_isArray(chartNavigatorSeriesOptions)) {
navigator.hasNavigatorData = false;
// Allow navigator.series to be an array
chartNavigatorSeriesOptions =
Navigator_splat(chartNavigatorSeriesOptions);
chartNavigatorSeriesOptions.forEach(function (userSeriesOptions, i) {
navSeriesMixin.name =
'Navigator ' + (navigatorSeries.length + 1);
mergedNavSeriesOptions = Navigator_merge(Navigator_defaultOptions.navigator.series, {
// Since we don't have a base series to pull color from,
// try to fake it by using color from series with same
// index. Otherwise pull from the colors array. We need
// an explicit color as otherwise updates will increment
// color counter and we'll get a new color for each
// update of the nav series.
color: chart.series[i] &&
!chart.series[i].options.isInternal &&
chart.series[i].color ||
chart.options.colors[i] ||
chart.options.colors[0]
}, navSeriesMixin, userSeriesOptions);
mergedNavSeriesOptions.data = userSeriesOptions.data;
if (mergedNavSeriesOptions.data) {
navigator.hasNavigatorData = true;
navigatorSeries.push(chart.initSeries(mergedNavSeriesOptions));
}
});
}
if (addEvents) {
this.addBaseSeriesEvents();
}
};
/**
* Add data events.
* For example when main series is updated we need to recalculate extremes
*
* @private
* @function Highcharts.Navigator#addBaseSeriesEvent
*/
Navigator.prototype.addBaseSeriesEvents = function () {
var _this = this;
var navigator = this,
baseSeries = navigator.baseSeries || [];
// Bind modified extremes event to first base's xAxis only.
// In event of > 1 base-xAxes, the navigator will ignore those.
// Adding this multiple times to the same axis is no problem, as
// duplicates should be discarded by the browser.
if (baseSeries[0] && baseSeries[0].xAxis) {
baseSeries[0].eventsToUnbind.push(Navigator_addEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes));
}
baseSeries.forEach(function (base) {
// Link base series show/hide to navigator series visibility
base.eventsToUnbind.push(Navigator_addEvent(base, 'show', function () {
if (this.navigatorSeries) {
this.navigatorSeries.setVisible(true, false);
}
}));
base.eventsToUnbind.push(Navigator_addEvent(base, 'hide', function () {
if (this.navigatorSeries) {
this.navigatorSeries.setVisible(false, false);
}
}));
// Respond to updated data in the base series, unless explicitly
// not adapting to data changes.
if (_this.navigatorOptions.adaptToUpdatedData !== false) {
if (base.xAxis) {
base.eventsToUnbind.push(Navigator_addEvent(base, 'updatedData', _this.updatedDataHandler));
}
}
// Handle series removal
base.eventsToUnbind.push(Navigator_addEvent(base, 'remove', function () {
if (baseSeries) {
Navigator_erase(baseSeries, base); // #21043
}
if (this.navigatorSeries && navigator.series) {
Navigator_erase(navigator.series, this.navigatorSeries);
if (Navigator_defined(this.navigatorSeries.options)) {
this.navigatorSeries.remove(false);
}
delete this.navigatorSeries;
}
}));
});
};
/**
* Get minimum from all base series connected to the navigator
* @private
* @param {number} currentSeriesMin
* Minium from the current series
* @return {number}
* Minimum from all series
*/
Navigator.prototype.getBaseSeriesMin = function (currentSeriesMin) {
return this.baseSeries.reduce(function (min, series) {
var _a;
// #10193
return Math.min(min, (_a = series.getColumn('x')[0]) !== null && _a !== void 0 ? _a : min);
}, currentSeriesMin);
};
/**
* Set the navigator x axis extremes to reflect the total. The navigator
* extremes should always be the extremes of the union of all series in the
* chart as well as the navigator series.
*
* @private
* @function Highcharts.Navigator#modifyNavigatorAxisExtremes
*/
Navigator.prototype.modifyNavigatorAxisExtremes = function () {
var xAxis = this.xAxis;
if (typeof xAxis.getExtremes !== 'undefined') {
var unionExtremes = this.getUnionExtremes(true);
if (unionExtremes &&
(unionExtremes.dataMin !== xAxis.min ||
unionExtremes.dataMax !== xAxis.max)) {
xAxis.min = unionExtremes.dataMin;
xAxis.max = unionExtremes.dataMax;
}
}
};
/**
* Hook to modify the base axis extremes with information from the Navigator
*
* @private
* @function Highcharts.Navigator#modifyBaseAxisExtremes
*/
Navigator.prototype.modifyBaseAxisExtremes = function () {
var _a;
var baseXAxis = this,
navigator = baseXAxis.chart.navigator,
baseExtremes = baseXAxis.getExtremes(),
baseMin = baseExtremes.min,
baseMax = baseExtremes.max,
baseDataMin = baseExtremes.dataMin,
baseDataMax = baseExtremes.dataMax,
range = baseMax - baseMin,
stickToMin = navigator.stickToMin,
stickToMax = navigator.stickToMax,
overscroll = Navigator_pick((_a = baseXAxis.ordinal) === null || _a === void 0 ? void 0 : _a.convertOverscroll(baseXAxis.options.overscroll), 0),
navigatorSeries = navigator.series && navigator.series[0],
hasSetExtremes = !!baseXAxis.setExtremes,
// When the extremes have been set by range selector button, don't
// stick to min or max. The range selector buttons will handle the
// extremes. (#5489)
unmutable = baseXAxis.eventArgs &&
baseXAxis.eventArgs.trigger === 'rangeSelectorButton';
var newMax,
newMin;
if (!unmutable) {
// If the zoomed range is already at the min, move it to the right
// as new data comes in
if (stickToMin) {
newMin = baseDataMin;
newMax = newMin + range;
}
// If the zoomed range is already at the max, move it to the right
// as new data comes in
if (stickToMax) {
newMax = baseDataMax + overscroll;
// If stickToMin is true, the new min value is set above
if (!stickToMin) {
newMin = Math.max(baseDataMin, // Don't go below data extremes (#13184)
newMax - range, navigator.getBaseSeriesMin(navigatorSeries && navigatorSeries.xData ?
navigatorSeries.xData[0] :
-Number.MAX_VALUE));
}
}
// Update the extremes
if (hasSetExtremes && (stickToMin || stickToMax)) {
if (Navigator_isNumber(newMin)) {
baseXAxis.min = baseXAxis.userMin = newMin;
baseXAxis.max = baseXAxis.userMax = newMax;
}
}
}
// Reset
navigator.stickToMin =
navigator.stickToMax = null;
};
/**
* Handler for updated data on the base series. When data is modified, the
* navigator series must reflect it. This is called from the Chart.redraw
* function before axis and series extremes are computed.
*
* @private
* @function Highcharts.Navigator#updateDataHandler
*/
Navigator.prototype.updatedDataHandler = function () {
var navigator = this.chart.navigator,
baseSeries = this,
navigatorSeries = this.navigatorSeries,
shouldStickToMax = navigator.reversedExtremes ?
Math.round(navigator.zoomedMin) === 0 :
Math.round(navigator.zoomedMax) >= Math.round(navigator.size);
// If the scrollbar is scrolled all the way to the right, keep right as
// new data comes in, unless user set navigator.stickToMax to false.
navigator.stickToMax = Navigator_pick(this.chart.options.navigator &&
this.chart.options.navigator.stickToMax, shouldStickToMax);
navigator.stickToMin = navigator.shouldStickToMin(baseSeries, navigator);
// Set the navigator series data to the new data of the base series
if (navigatorSeries && !navigator.hasNavigatorData) {
navigatorSeries.options.pointStart = baseSeries.getColumn('x')[0];
navigatorSeries.setData(baseSeries.options.data, false, null, false); // #5414
}
};
/**
* Detect if the zoomed area should stick to the minimum, #14742.
*
* @private
* @function Highcharts.Navigator#shouldStickToMin
*/
Navigator.prototype.shouldStickToMin = function (baseSeries, navigator) {
var xDataMin = navigator.getBaseSeriesMin(baseSeries.getColumn('x')[0]),
xAxis = baseSeries.xAxis,
max = xAxis.max,
min = xAxis.min,
range = xAxis.options.range;
var stickToMin = true;
if (Navigator_isNumber(max) && Navigator_isNumber(min)) {
// If range declared, stick to the minimum only if the range
// is smaller than the data set range.
if (range && max - xDataMin > 0) {
stickToMin = max - xDataMin < range;
}
else {
// If the current axis minimum falls outside the new
// updated dataset, we must adjust.
stickToMin = min <= xDataMin;
}
}
else {
stickToMin = false; // #15864
}
return stickToMin;
};
/**
* Add chart events, like redrawing navigator, when chart requires that.
*
* @private
* @function Highcharts.Navigator#addChartEvents
*/
Navigator.prototype.addChartEvents = function () {
if (!this.eventsToUnbind) {
this.eventsToUnbind = [];
}
this.eventsToUnbind.push(
// Move the scrollbar after redraw, like after data updata even if
// axes don't redraw
Navigator_addEvent(this.chart, 'redraw', function () {
var navigator = this.navigator,
xAxis = navigator && (navigator.baseSeries &&
navigator.baseSeries[0] &&
navigator.baseSeries[0].xAxis ||
this.xAxis[0]); // #5709, #13114
if (xAxis) {
navigator.render(xAxis.min,
xAxis.max);
}
}),
// Make room for the navigator, can be placed around the chart:
Navigator_addEvent(this.chart, 'getMargins', function () {
var chart = this,
navigator = chart.navigator;
var marginName = navigator.opposite ?
'plotTop' : 'marginBottom';
if (chart.inverted) {
marginName = navigator.opposite ?
'marginRight' : 'plotLeft';
}
chart[marginName] =
(chart[marginName] || 0) + (navigator.navigatorEnabled || !chart.inverted ?
navigator.height + navigator.scrollbarHeight :
0) + navigator.navigatorOptions.margin;
}), Navigator_addEvent(Navigator, 'setRange', function (e) {
this.chart.xAxis[0].setExtremes(e.min, e.max, e.redraw, e.animation, e.eventArguments);
}));
};
/**
* Destroys allocated elements.
*
* @private
* @function Highcharts.Navigator#destroy
*/
Navigator.prototype.destroy = function () {
var _this = this;
// Disconnect events added in addEvents
this.removeEvents();
if (this.xAxis) {
Navigator_erase(this.chart.xAxis, this.xAxis);
Navigator_erase(this.chart.axes, this.xAxis);
}
if (this.yAxis) {
Navigator_erase(this.chart.yAxis, this.yAxis);
Navigator_erase(this.chart.axes, this.yAxis);
}
// Destroy series
(this.series || []).forEach(function (s) {
if (s.destroy) {
s.destroy();
}
});
// Destroy properties
[
'series', 'xAxis', 'yAxis', 'shades', 'outline', 'scrollbarTrack',
'scrollbarRifles', 'scrollbarGroup', 'scrollbar', 'navigatorGroup',
'rendered'
].forEach(function (prop) {
if (_this[prop] && _this[prop].destroy) {
_this[prop].destroy();
}
_this[prop] = null;
});
// Destroy elements in collection
[this.handles].forEach(function (coll) {
Navigator_destroyObjectProperties(coll);
});
this.navigatorEnabled = false;
};
return Navigator;
}());
/* *
*
* Default Export
*
* */
/* harmony default export */ var Navigator_Navigator = (Navigator);
;// ./code/es5/es-modules/Core/Axis/OrdinalAxis.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var OrdinalAxis_assign = (undefined && undefined.__assign) || function () {
OrdinalAxis_assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return OrdinalAxis_assign.apply(this, arguments);
};
var OrdinalAxis_addEvent = Core_Utilities.addEvent, OrdinalAxis_correctFloat = Core_Utilities.correctFloat, OrdinalAxis_css = Core_Utilities.css, OrdinalAxis_defined = Core_Utilities.defined, OrdinalAxis_error = Core_Utilities.error, OrdinalAxis_isNumber = Core_Utilities.isNumber, OrdinalAxis_pick = Core_Utilities.pick, OrdinalAxis_timeUnits = Core_Utilities.timeUnits, OrdinalAxis_isString = Core_Utilities.isString;
/* *
*
* Composition
*
* */
/**
* Extends the axis with ordinal support.
* @private
*/
var OrdinalAxis;
(function (OrdinalAxis) {
/* *
*
* Declarations
*
* */
/* *
*
* Functions
*
* */
/**
* Extends the axis with ordinal support.
*
* @private
*
* @param AxisClass
* Axis class to extend.
*
* @param ChartClass
* Chart class to use.
*
* @param SeriesClass
* Series class to use.
*/
function compose(AxisClass, SeriesClass, ChartClass) {
var axisProto = AxisClass.prototype;
if (!axisProto.ordinal2lin) {
axisProto.getTimeTicks = getTimeTicks;
axisProto.index2val = index2val;
axisProto.lin2val = lin2val;
axisProto.val2lin = val2lin;
// Record this to prevent overwriting by broken-axis module (#5979)
axisProto.ordinal2lin = axisProto.val2lin;
OrdinalAxis_addEvent(AxisClass, 'afterInit', onAxisAfterInit);
OrdinalAxis_addEvent(AxisClass, 'foundExtremes', onAxisFoundExtremes);
OrdinalAxis_addEvent(AxisClass, 'afterSetScale', onAxisAfterSetScale);
OrdinalAxis_addEvent(AxisClass, 'initialAxisTranslation', onAxisInitialAxisTranslation);
OrdinalAxis_addEvent(ChartClass, 'pan', onChartPan);
OrdinalAxis_addEvent(ChartClass, 'touchpan', onChartPan);
OrdinalAxis_addEvent(SeriesClass, 'updatedData', onSeriesUpdatedData);
}
return AxisClass;
}
OrdinalAxis.compose = compose;
/**
* In an ordinal axis, there might be areas with dense concentrations of
* points, then large gaps between some. Creating equally distributed
* ticks over this entire range may lead to a huge number of ticks that
* will later be removed. So instead, break the positions up in
* segments, find the tick positions for each segment then concatenize
* them. This method is used from both data grouping logic and X axis
* tick position logic.
* @private
*/
function getTimeTicks(normalizedInterval, min, max, startOfWeek, positions, closestDistance, findHigherRanks) {
if (positions === void 0) { positions = []; }
if (closestDistance === void 0) { closestDistance = 0; }
var higherRanks = {},
tickPixelIntervalOption = this.options.tickPixelInterval,
time = this.chart.time,
// Record all the start positions of a segment, to use when
// deciding what's a gap in the data.
segmentStarts = [];
var end,
segmentPositions,
hasCrossedHigherRank,
info,
outsideMax,
start = 0,
groupPositions = [],
lastGroupPosition = -Number.MAX_VALUE;
// The positions are not always defined, for example for ordinal
// positions when data has regular interval (#1557, #2090)
if ((!this.options.ordinal && !this.options.breaks) ||
!positions ||
positions.length < 3 ||
typeof min === 'undefined') {
return time.getTimeTicks.apply(time, arguments);
}
// Analyze the positions array to split it into segments on gaps
// larger than 5 times the closest distance. The closest distance is
// already found at this point, so we reuse that instead of
// computing it again.
var posLength = positions.length;
for (end = 0; end < posLength; end++) {
outsideMax = end && positions[end - 1] > max;
if (positions[end] < min) { // Set the last position before min
start = end;
}
if (end === posLength - 1 ||
positions[end + 1] - positions[end] > closestDistance * 5 ||
outsideMax) {
// For each segment, calculate the tick positions from the
// getTimeTicks utility function. The interval will be the
// same regardless of how long the segment is.
if (positions[end] > lastGroupPosition) { // #1475
segmentPositions = time.getTimeTicks(normalizedInterval, positions[start], positions[end], startOfWeek);
// Prevent duplicate groups, for example for multiple
// segments within one larger time frame (#1475)
while (segmentPositions.length &&
segmentPositions[0] <= lastGroupPosition) {
segmentPositions.shift();
}
if (segmentPositions.length) {
lastGroupPosition =
segmentPositions[segmentPositions.length - 1];
}
segmentStarts.push(groupPositions.length);
groupPositions = groupPositions.concat(segmentPositions);
}
// Set start of next segment
start = end + 1;
}
if (outsideMax) {
break;
}
}
// Get the grouping info from the last of the segments. The info is
// the same for all segments.
if (segmentPositions) {
info = segmentPositions.info;
// Optionally identify ticks with higher rank, for example
// when the ticks have crossed midnight.
if (findHigherRanks && info.unitRange <= OrdinalAxis_timeUnits.hour) {
end = groupPositions.length - 1;
// Compare points two by two
for (start = 1; start < end; start++) {
if (time.dateFormat('%d', groupPositions[start]) !==
time.dateFormat('%d', groupPositions[start - 1])) {
higherRanks[groupPositions[start]] = 'day';
hasCrossedHigherRank = true;
}
}
// If the complete array has crossed midnight, we want
// to mark the first positions also as higher rank
if (hasCrossedHigherRank) {
higherRanks[groupPositions[0]] = 'day';
}
info.higherRanks = higherRanks;
}
// Save the info
info.segmentStarts = segmentStarts;
groupPositions.info = info;
}
else {
OrdinalAxis_error(12, false, this.chart);
}
// Don't show ticks within a gap in the ordinal axis, where the
// space between two points is greater than a portion of the tick
// pixel interval
if (findHigherRanks && OrdinalAxis_defined(tickPixelIntervalOption)) {
var length_1 = groupPositions.length,
translatedArr = [],
distances = [];
var itemToRemove = void 0,
translated = void 0,
lastTranslated = void 0,
medianDistance = void 0,
distance = void 0,
i = length_1;
// Find median pixel distance in order to keep a reasonably even
// distance between ticks (#748)
while (i--) {
translated = this.translate(groupPositions[i]);
if (lastTranslated) {
distances[i] = lastTranslated - translated;
}
translatedArr[i] = lastTranslated = translated;
}
distances.sort(function (a, b) { return a - b; });
medianDistance = distances[Math.floor(distances.length / 2)];
if (medianDistance < tickPixelIntervalOption * 0.6) {
medianDistance = null;
}
// Now loop over again and remove ticks where needed
i = groupPositions[length_1 - 1] > max ? length_1 - 1 : length_1; // #817
lastTranslated = void 0;
while (i--) {
translated = translatedArr[i];
distance = Math.abs(lastTranslated - translated);
// #4175 - when axis is reversed, the distance, is negative but
// tickPixelIntervalOption positive, so we need to compare the
// same values
// Remove ticks that are closer than 0.6 times the pixel
// interval from the one to the right, but not if it is close to
// the median distance (#748).
if (lastTranslated &&
distance < tickPixelIntervalOption * 0.8 &&
(medianDistance === null || distance < medianDistance * 0.8)) {
// Is this a higher ranked position with a normal
// position to the right?
if (higherRanks[groupPositions[i]] &&
!higherRanks[groupPositions[i + 1]]) {
// Yes: remove the lower ranked neighbour to the
// right
itemToRemove = i + 1;
lastTranslated = translated; // #709
}
else {
// No: remove this one
itemToRemove = i;
}
groupPositions.splice(itemToRemove, 1);
}
else {
lastTranslated = translated;
}
}
}
return groupPositions;
}
/**
* Get axis position of given index of the extended ordinal positions.
* Used only when panning an ordinal axis.
*
* @private
* @function Highcharts.Axis#index2val
* @param {number} index
* The index value of searched point
*/
function index2val(index) {
var axis = this,
ordinal = axis.ordinal,
// Context could be changed to extendedOrdinalPositions.
ordinalPositions = ordinal.positions;
// The visible range contains only equally spaced values.
if (!ordinalPositions) {
return index;
}
var i = ordinalPositions.length - 1,
distance;
if (index < 0) { // Out of range, in effect panning to the left
index = ordinalPositions[0];
}
else if (index > i) { // Out of range, panning to the right
index = ordinalPositions[i];
}
else { // Split it up
i = Math.floor(index);
distance = index - i; // The decimal
}
if (typeof distance !== 'undefined' &&
typeof ordinalPositions[i] !== 'undefined') {
return ordinalPositions[i] + (distance ?
distance *
(ordinalPositions[i + 1] - ordinalPositions[i]) :
0);
}
return index;
}
/**
* Translate from linear (internal) to axis value.
*
* @private
* @function Highcharts.Axis#lin2val
* @param {number} val
* The linear abstracted value.
*/
function lin2val(val) {
var axis = this,
ordinal = axis.ordinal,
localMin = axis.old ? axis.old.min : axis.min,
localA = axis.old ? axis.old.transA : axis.transA;
// Always use extendedPositions (#19816)
var positions = ordinal.getExtendedPositions();
// In some cases (especially in early stages of the chart creation) the
// getExtendedPositions might return undefined.
if (positions === null || positions === void 0 ? void 0 : positions.length) {
// Convert back from modivied value to pixels. // #15970
var pixelVal = OrdinalAxis_correctFloat((val - localMin) * localA +
axis.minPixelPadding),
index = OrdinalAxis_correctFloat(ordinal.getIndexOfPoint(pixelVal,
positions)),
mantissa = OrdinalAxis_correctFloat(index % 1);
// Check if the index is inside position array. If true,
// read/approximate value for that exact index.
if (index >= 0 && index <= positions.length - 1) {
var leftNeighbour = positions[Math.floor(index)],
rightNeighbour = positions[Math.ceil(index)],
distance = rightNeighbour - leftNeighbour;
return positions[Math.floor(index)] + mantissa * distance;
}
}
// If the value is outside positions array, return initial value
return val; // #16784
}
/**
* Internal function to calculate the precise index in ordinalPositions
* array.
* @private
*/
function getIndexInArray(ordinalPositions, val) {
var index = OrdinalAxis.Additions.findIndexOf(ordinalPositions,
val,
true);
if (ordinalPositions[index] === val) {
return index;
}
var percent = (val - ordinalPositions[index]) /
(ordinalPositions[index + 1] - ordinalPositions[index]);
return index + percent;
}
/**
* @private
*/
function onAxisAfterInit() {
var axis = this;
if (!axis.ordinal) {
axis.ordinal = new OrdinalAxis.Additions(axis);
}
}
/**
* @private
*/
function onAxisFoundExtremes() {
var axis = this,
eventArgs = axis.eventArgs,
options = axis.options;
if (axis.isXAxis &&
OrdinalAxis_defined(options.overscroll) &&
options.overscroll !== 0 &&
OrdinalAxis_isNumber(axis.max) &&
OrdinalAxis_isNumber(axis.min)) {
if (axis.options.ordinal && !axis.ordinal.originalOrdinalRange) {
// Calculate the original ordinal range
axis.ordinal.getExtendedPositions(false);
}
if (axis.max === axis.dataMax &&
(
// Panning is an exception. We don't want to apply
// overscroll when panning over the dataMax
(eventArgs === null || eventArgs === void 0 ? void 0 : eventArgs.trigger) !== 'pan' ||
axis.isInternal) &&
// Scrollbar buttons are the other execption
(eventArgs === null || eventArgs === void 0 ? void 0 : eventArgs.trigger) !== 'navigator') {
var overscroll = axis.ordinal.convertOverscroll(options.overscroll);
axis.max += overscroll;
// Live data and buttons require translation for the min:
if (!axis.isInternal &&
OrdinalAxis_defined(axis.userMin) &&
(eventArgs === null || eventArgs === void 0 ? void 0 : eventArgs.trigger) !== 'mousewheel') {
axis.min += overscroll;
}
}
}
}
/**
* For ordinal axis, that loads data async, redraw axis after data is
* loaded. If we don't do that, axis will have the same extremes as
* previously, but ordinal positions won't be calculated. See #10290
* @private
*/
function onAxisAfterSetScale() {
var axis = this;
if (axis.horiz && !axis.isDirty) {
axis.isDirty = axis.isOrdinal &&
axis.chart.navigator &&
!axis.chart.navigator.adaptToUpdatedData;
}
}
/**
* @private
*/
function onAxisInitialAxisTranslation() {
var axis = this;
if (axis.ordinal) {
axis.ordinal.beforeSetTickPositions();
axis.tickInterval = axis.ordinal.postProcessTickInterval(axis.tickInterval);
}
}
/**
* Extending the Chart.pan method for ordinal axes
* @private
*/
function onChartPan(e) {
var chart = this,
xAxis = chart.xAxis[0],
overscroll = xAxis.ordinal.convertOverscroll(xAxis.options.overscroll),
chartX = e.originalEvent.chartX,
panning = chart.options.chart.panning;
var runBase = false;
if (panning &&
panning.type !== 'y' &&
xAxis.options.ordinal &&
xAxis.series.length &&
// On touch devices, let default function handle the pinching
(!e.touches || e.touches.length <= 1)) {
var mouseDownX = chart.mouseDownX,
extremes = xAxis.getExtremes(),
dataMin = extremes.dataMin,
dataMax = extremes.dataMax,
min = extremes.min,
max = extremes.max,
hoverPoints = chart.hoverPoints,
closestPointRange = (xAxis.closestPointRange ||
(xAxis.ordinal && xAxis.ordinal.overscrollPointsRange)),
pointPixelWidth = (xAxis.translationSlope *
(xAxis.ordinal.slope || closestPointRange)),
// How many ordinal units did we move?
movedUnits = Math.round((mouseDownX - chartX) / pointPixelWidth),
// Get index of all the chart's points
extendedOrdinalPositions = xAxis.ordinal.getExtendedPositions(),
extendedAxis = {
ordinal: {
positions: extendedOrdinalPositions,
extendedOrdinalPositions: extendedOrdinalPositions
}
},
index2val_1 = xAxis.index2val,
val2lin_1 = xAxis.val2lin;
var trimmedRange = void 0,
ordinalPositions = void 0;
// Make sure panning to the edges does not decrease the zoomed range
if ((min <= dataMin && movedUnits < 0) ||
(max + overscroll >= dataMax && movedUnits > 0)) {
return;
}
// We have an ordinal axis, but the data is equally spaced
if (!extendedAxis.ordinal.positions) {
runBase = true;
}
else if (Math.abs(movedUnits) > 1) {
// Remove active points for shared tooltip
if (hoverPoints) {
hoverPoints.forEach(function (point) {
point.setState();
});
}
// In grouped data series, the last ordinal position represents
// the grouped data, which is to the left of the real data max.
// If we don't compensate for this, we will be allowed to pan
// grouped data series passed the right of the plot area.
ordinalPositions = extendedAxis.ordinal.positions;
if (dataMax >
ordinalPositions[ordinalPositions.length - 1]) {
ordinalPositions.push(dataMax);
}
// Get the new min and max values by getting the ordinal index
// for the current extreme, then add the moved units and
// translate back to values. This happens on the extended
// ordinal positions if the new position is out of range, else
// it happens on the current x axis which is smaller and faster.
chart.setFixedRange(max - min);
trimmedRange = xAxis.navigatorAxis
.toFixedRange(void 0, void 0, index2val_1.apply(extendedAxis, [
val2lin_1.apply(extendedAxis, [min, true]) +
movedUnits
]), index2val_1.apply(extendedAxis, [
val2lin_1.apply(extendedAxis, [max, true]) +
movedUnits
]));
// Apply it if it is within the available data range
if (trimmedRange.min >= Math.min(ordinalPositions[0], min) &&
trimmedRange.max <= Math.max(ordinalPositions[ordinalPositions.length - 1], max) + overscroll) {
xAxis.setExtremes(trimmedRange.min, trimmedRange.max, true, false, { trigger: 'pan' });
}
chart.mouseDownX = chartX; // Set new reference for next run
OrdinalAxis_css(chart.container, { cursor: 'move' });
}
}
else {
runBase = true;
}
// Revert to the linear chart.pan version
if (runBase || (panning && /y/.test(panning.type))) {
if (overscroll) {
xAxis.max = xAxis.dataMax + overscroll;
}
}
else {
e.preventDefault();
}
}
/**
* @private
*/
function onSeriesUpdatedData() {
var xAxis = this.xAxis;
// Destroy the extended ordinal index on updated data
// and destroy extendedOrdinalPositions, #16055.
if (xAxis && xAxis.options.ordinal) {
delete xAxis.ordinal.index;
delete xAxis.ordinal.originalOrdinalRange;
}
}
/**
* Translate from a linear axis value to the corresponding ordinal axis
* position. If there are no gaps in the ordinal axis this will be the
* same. The translated value is the value that the point would have if
* the axis was linear, using the same min and max.
*
* @private
* @function Highcharts.Axis#val2lin
* @param {number} val
* The axis value.
* @param {boolean} [toIndex]
* Whether to return the index in the ordinalPositions or the new value.
*/
function val2lin(val, toIndex) {
var axis = this,
ordinal = axis.ordinal,
ordinalPositions = ordinal.positions;
var slope = ordinal.slope,
extendedOrdinalPositions;
if (!ordinalPositions) {
return val;
}
var ordinalLength = ordinalPositions.length;
var ordinalIndex;
// If the searched value is inside visible plotArea, ivastigate the
// value basing on ordinalPositions.
if (ordinalPositions[0] <= val &&
ordinalPositions[ordinalLength - 1] >= val) {
ordinalIndex = getIndexInArray(ordinalPositions, val);
// Final return value is based on ordinalIndex
}
else {
extendedOrdinalPositions =
ordinal.getExtendedPositions &&
ordinal.getExtendedPositions();
if (!(extendedOrdinalPositions && extendedOrdinalPositions.length)) {
return val;
}
var length_2 = extendedOrdinalPositions.length;
if (!slope) {
slope =
(extendedOrdinalPositions[length_2 - 1] -
extendedOrdinalPositions[0]) /
length_2;
}
// `originalPointReference` is equal to the index of first point of
// ordinalPositions in extendedOrdinalPositions.
var originalPositionsReference = getIndexInArray(extendedOrdinalPositions,
ordinalPositions[0]);
// If the searched value is outside the visiblePlotArea,
// check if it is inside extendedOrdinalPositions.
if (val >= extendedOrdinalPositions[0] &&
val <=
extendedOrdinalPositions[length_2 - 1]) {
// Return Value
ordinalIndex = getIndexInArray(extendedOrdinalPositions, val) -
originalPositionsReference;
}
else {
if (!toIndex) {
// If the value is outside positions array,
// return initial value, #16784
return val;
}
// Since ordinal.slope is the average distance between 2
// points on visible plotArea, this can be used to calculate
// the approximate position of the point, which is outside
// the extendedOrdinalPositions.
if (val < extendedOrdinalPositions[0]) {
var diff = extendedOrdinalPositions[0] - val,
approximateIndexOffset = diff / slope;
ordinalIndex =
-originalPositionsReference -
approximateIndexOffset;
}
else {
var diff = val -
extendedOrdinalPositions[length_2 - 1],
approximateIndexOffset = diff / slope;
ordinalIndex =
approximateIndexOffset +
length_2 -
originalPositionsReference;
}
}
}
return toIndex ? ordinalIndex : slope * (ordinalIndex || 0) +
ordinal.offset;
}
/* *
*
* Classes
*
* */
/**
* @private
*/
var Additions = /** @class */ (function () {
/* *
*
* Constructors
*
* */
/**
* @private
*/
function Additions(axis) {
this.index = {};
this.axis = axis;
}
/* *
*
* Functions
*
* */
/**
* Calculate the ordinal positions before tick positions are calculated.
* @private
*/
Additions.prototype.beforeSetTickPositions = function () {
var _a;
var axis = this.axis,
ordinal = axis.ordinal,
extremes = axis.getExtremes(),
min = extremes.min,
max = extremes.max,
hasBreaks = (_a = axis.brokenAxis) === null || _a === void 0 ? void 0 : _a.hasBreaks,
isOrdinal = axis.options.ordinal;
var len,
uniqueOrdinalPositions,
dist,
minIndex,
maxIndex,
slope,
i,
ordinalPositions = [],
overscrollPointsRange = Number.MAX_VALUE,
useOrdinal = false,
adjustOrdinalExtremesPoints = false,
isBoosted = false;
// Apply the ordinal logic
if (isOrdinal || hasBreaks) { // #4167 YAxis is never ordinal ?
var distanceBetweenPoint_1 = 0;
axis.series.forEach(function (series, i) {
var xData = series.getColumn('x',
true);
uniqueOrdinalPositions = [];
// For an axis with multiple series, check if the distance
// between points is identical throughout all series.
if (i > 0 &&
series.options.id !== 'highcharts-navigator-series' &&
xData.length > 1) {
adjustOrdinalExtremesPoints = (distanceBetweenPoint_1 !== xData[1] - xData[0]);
}
distanceBetweenPoint_1 = xData[1] - xData[0];
if (series.boosted) {
isBoosted = series.boosted;
}
if (series.reserveSpace() &&
(series
.takeOrdinalPosition !== false || hasBreaks)) {
// Concatenate the processed X data into the existing
// positions, or the empty array
ordinalPositions = ordinalPositions.concat(xData);
len = ordinalPositions.length;
// Remove duplicates (#1588)
ordinalPositions.sort(function (a, b) {
// Without a custom function it is sorted as strings
return a - b;
});
overscrollPointsRange = Math.min(overscrollPointsRange, OrdinalAxis_pick(
// Check for a single-point series:
series.closestPointRange, overscrollPointsRange));
if (len) {
i = 0;
while (i < len - 1) {
if (ordinalPositions[i] !==
ordinalPositions[i + 1]) {
uniqueOrdinalPositions.push(ordinalPositions[i + 1]);
}
i++;
}
// Check first item:
if (uniqueOrdinalPositions[0] !==
ordinalPositions[0]) {
uniqueOrdinalPositions.unshift(ordinalPositions[0]);
}
ordinalPositions = uniqueOrdinalPositions;
}
}
});
if (!axis.ordinal.originalOrdinalRange) {
// Calculate current originalOrdinalRange
axis.ordinal.originalOrdinalRange =
(ordinalPositions.length - 1) * overscrollPointsRange;
}
// If the distance between points is not identical throughout
// all series, remove the first and last ordinal position to
// avoid enabling ordinal logic when it is not needed, #17405.
// Only for boosted series because changes are negligible.
if (adjustOrdinalExtremesPoints && isBoosted) {
ordinalPositions.pop();
ordinalPositions.shift();
}
// Cache the length
len = ordinalPositions.length;
// Check if we really need the overhead of mapping axis data
// against the ordinal positions. If the series consist of
// evenly spaced data any way, we don't need any ordinal logic.
if (len > 2) { // Two points have equal distance by default
dist = ordinalPositions[1] - ordinalPositions[0];
i = len - 1;
while (i-- && !useOrdinal) {
if (ordinalPositions[i + 1] - ordinalPositions[i] !==
dist) {
useOrdinal = true;
}
}
// When zooming in on a week, prevent axis padding for
// weekends even though the data within the week is evenly
// spaced.
if (!axis.options.keepOrdinalPadding &&
(ordinalPositions[0] - min > dist ||
(max -
ordinalPositions[ordinalPositions.length - 1]) > dist)) {
useOrdinal = true;
}
}
else if (axis.options.overscroll) {
if (len === 2) {
// Exactly two points, distance for overscroll is fixed:
overscrollPointsRange =
ordinalPositions[1] - ordinalPositions[0];
}
else if (len === 1) {
// We have just one point, closest distance is unknown.
// Assume then it is last point and overscrolled range:
overscrollPointsRange = axis.ordinal.convertOverscroll(axis.options.overscroll);
ordinalPositions = [
ordinalPositions[0],
ordinalPositions[0] + overscrollPointsRange
];
}
else {
// In case of zooming in on overscrolled range, stick to
// the old range:
overscrollPointsRange = ordinal.overscrollPointsRange;
}
}
// Record the slope and offset to compute the linear values from
// the array index. Since the ordinal positions may exceed the
// current range, get the start and end positions within it
// (#719, #665b)
if (useOrdinal || axis.forceOrdinal) {
if (axis.options.overscroll) {
ordinal.overscrollPointsRange = overscrollPointsRange;
ordinalPositions = ordinalPositions.concat(ordinal.getOverscrollPositions());
}
// Register
ordinal.positions = ordinalPositions;
// This relies on the ordinalPositions being set. Use
// Math.max and Math.min to prevent padding on either sides
// of the data.
minIndex = axis.ordinal2lin(// #5979
Math.max(min, ordinalPositions[0]), true);
maxIndex = Math.max(axis.ordinal2lin(Math.min(max, ordinalPositions[ordinalPositions.length - 1]), true), 1); // #3339
// Set the slope and offset of the values compared to the
// indices in the ordinal positions.
ordinal.slope = slope =
(max - min) / (maxIndex - minIndex);
ordinal.offset = min - (minIndex * slope);
}
else {
ordinal.overscrollPointsRange = OrdinalAxis_pick(axis.closestPointRange, ordinal.overscrollPointsRange);
ordinal.positions = axis.ordinal.slope = ordinal.offset =
void 0;
}
}
axis.isOrdinal = isOrdinal && useOrdinal; // #3818, #4196, #4926
ordinal.groupIntervalFactor = null; // Reset for next run
};
/**
* Faster way of using the Array.indexOf method.
* Works for sorted arrays only with unique values.
*
* @param {Array} sortedArray
* The sorted array inside which we are looking for.
* @param {number} key
* The key to being found.
* @param {boolean} indirectSearch
* In case of lack of the point in the array, should return
* value be equal to -1 or the closest smaller index.
* @private
*/
Additions.findIndexOf = function (sortedArray, key, indirectSearch) {
var start = 0,
end = sortedArray.length - 1,
middle;
while (start < end) {
middle = Math.ceil((start + end) / 2);
// Key found as the middle element.
if (sortedArray[middle] <= key) {
// Continue searching to the right.
start = middle;
}
else {
// Continue searching to the left.
end = middle - 1;
}
}
if (sortedArray[start] === key) {
return start;
}
// Key could not be found.
return !indirectSearch ? -1 : start;
};
/**
* Get the ordinal positions for the entire data set. This is necessary
* in chart panning because we need to find out what points or data
* groups are available outside the visible range. When a panning
* operation starts, if an index for the given grouping does not exists,
* it is created and cached. This index is deleted on updated data, so
* it will be regenerated the next time a panning operation starts.
* @private
*/
Additions.prototype.getExtendedPositions = function (withOverscroll) {
if (withOverscroll === void 0) { withOverscroll = true; }
var ordinal = this,
axis = ordinal.axis,
axisProto = axis.constructor.prototype,
chart = axis.chart,
key = axis.series.reduce(function (k,
series) {
var grouping = series.currentDataGrouping;
return (k +
(grouping ? grouping.count + grouping.unitName : 'raw'));
}, ''), overscroll = withOverscroll ?
axis.ordinal.convertOverscroll(axis.options.overscroll) : 0, extremes = axis.getExtremes();
var fakeAxis,
fakeSeries = void 0,
ordinalIndex = ordinal.index;
// If this is the first time, or the ordinal index is deleted by
// updatedData,
// create it.
if (!ordinalIndex) {
ordinalIndex = ordinal.index = {};
}
if (!ordinalIndex[key]) {
// Create a fake axis object where the extended ordinal
// positions are emulated
fakeAxis = {
series: [],
chart: chart,
forceOrdinal: false,
getExtremes: function () {
return {
min: extremes.dataMin,
max: extremes.dataMax + overscroll
};
},
applyGrouping: axisProto.applyGrouping,
getGroupPixelWidth: axisProto.getGroupPixelWidth,
getTimeTicks: axisProto.getTimeTicks,
options: {
ordinal: true
},
ordinal: {
getGroupIntervalFactor: this.getGroupIntervalFactor
},
ordinal2lin: axisProto.ordinal2lin, // #6276
getIndexOfPoint: axisProto.getIndexOfPoint,
val2lin: axisProto.val2lin // #2590
};
fakeAxis.ordinal.axis = fakeAxis;
// Add the fake series to hold the full data, then apply
// processData to it
axis.series.forEach(function (series) {
var _a,
_b,
_c;
fakeSeries = {
xAxis: fakeAxis,
chart: chart,
groupPixelWidth: series.groupPixelWidth,
destroyGroupedData: Core_Globals.noop,
getColumn: series.getColumn,
applyGrouping: series.applyGrouping,
getProcessedData: series.getProcessedData,
reserveSpace: series.reserveSpace,
visible: series.visible
};
var xData = series.getColumn('x').concat(withOverscroll ?
ordinal.getOverscrollPositions() :
[]);
fakeSeries.dataTable = new Data_DataTableCore({
columns: {
x: xData
}
});
fakeSeries.options = OrdinalAxis_assign(OrdinalAxis_assign({}, series.options), { dataGrouping: series.currentDataGrouping ? {
firstAnchor: (_a = series.options.dataGrouping) === null || _a === void 0 ? void 0 : _a.firstAnchor,
anchor: (_b = series.options.dataGrouping) === null || _b === void 0 ? void 0 : _b.anchor,
lastAnchor: (_c = series.options.dataGrouping) === null || _c === void 0 ? void 0 : _c.firstAnchor,
enabled: true,
forced: true,
approximation: 'open',
units: [[
series.currentDataGrouping.unitName,
[series.currentDataGrouping.count]
]]
} : {
enabled: false
} });
fakeAxis.series.push(fakeSeries);
series.processData.apply(fakeSeries);
});
fakeAxis.applyGrouping({ hasExtremesChanged: true });
// Force to use the ordinal when points are evenly spaced (e.g.
// weeks), #3825.
if (((fakeSeries === null || fakeSeries === void 0 ? void 0 : fakeSeries.closestPointRange) !==
(fakeSeries === null || fakeSeries === void 0 ? void 0 : fakeSeries.basePointRange)) &&
fakeSeries.currentDataGrouping) {
fakeAxis.forceOrdinal = true;
}
// Run beforeSetTickPositions to compute the ordinalPositions
axis.ordinal.beforeSetTickPositions.apply({ axis: fakeAxis });
if (!axis.ordinal.originalOrdinalRange &&
fakeAxis.ordinal.originalOrdinalRange) {
axis.ordinal.originalOrdinalRange =
fakeAxis.ordinal.originalOrdinalRange;
}
// Cache it
if (fakeAxis.ordinal.positions) {
ordinalIndex[key] = fakeAxis.ordinal.positions;
}
}
return ordinalIndex[key];
};
/**
* Find the factor to estimate how wide the plot area would have been if
* ordinal gaps were included. This value is used to compute an imagined
* plot width in order to establish the data grouping interval.
*
* A real world case is the intraday-candlestick example. Without this
* logic, it would show the correct data grouping when viewing a range
* within each day, but once moving the range to include the gap between
* two days, the interval would include the cut-away night hours and the
* data grouping would be wrong. So the below method tries to compensate
* by identifying the most common point interval, in this case days.
*
* An opposite case is presented in issue #718. We have a long array of
* daily data, then one point is appended one hour after the last point.
* We expect the data grouping not to change.
*
* In the future, if we find cases where this estimation doesn't work
* optimally, we might need to add a second pass to the data grouping
* logic, where we do another run with a greater interval if the number
* of data groups is more than a certain fraction of the desired group
* count.
* @private
*/
Additions.prototype.getGroupIntervalFactor = function (xMin, xMax, series) {
var ordinal = this,
processedXData = series.getColumn('x',
true),
len = processedXData.length,
distances = [];
var median,
i,
groupIntervalFactor = ordinal.groupIntervalFactor;
// Only do this computation for the first series, let the other
// inherit it (#2416)
if (!groupIntervalFactor) {
// Register all the distances in an array
for (i = 0; i < len - 1; i++) {
distances[i] = (processedXData[i + 1] -
processedXData[i]);
}
// Sort them and find the median
distances.sort(function (a, b) {
return a - b;
});
median = distances[Math.floor(len / 2)];
// Compensate for series that don't extend through the entire
// axis extent. #1675.
xMin = Math.max(xMin, processedXData[0]);
xMax = Math.min(xMax, processedXData[len - 1]);
ordinal.groupIntervalFactor = groupIntervalFactor =
(len * median) / (xMax - xMin);
}
// Return the factor needed for data grouping
return groupIntervalFactor;
};
/**
* Get index of point inside the ordinal positions array.
*
* @private
* @param {number} pixelVal
* The pixel value of a point.
*
* @param {Array<number>} [ordinalArray]
* An array of all points available on the axis for the given data set.
* Either ordinalPositions if the value is inside the plotArea or
* extendedOrdinalPositions if not.
*/
Additions.prototype.getIndexOfPoint = function (pixelVal, ordinalArray) {
var ordinal = this,
axis = ordinal.axis,
min = axis.min,
minX = axis.minPixelPadding,
indexOfMin = getIndexInArray(ordinalArray,
min);
var ordinalPointPixelInterval = axis.translationSlope *
(ordinal.slope ||
axis.closestPointRange ||
ordinal.overscrollPointsRange);
var shiftIndex = OrdinalAxis_correctFloat((pixelVal - minX) / ordinalPointPixelInterval);
return indexOfMin + shiftIndex;
};
/**
* Get ticks for an ordinal axis within a range where points don't
* exist. It is required when overscroll is enabled. We can't base on
* points, because we may not have any, so we use approximated
* pointRange and generate these ticks between Axis.dataMax,
* Axis.dataMax + Axis.overscroll evenly spaced. Used in panning and
* navigator scrolling.
* @private
*/
Additions.prototype.getOverscrollPositions = function () {
var ordinal = this,
axis = ordinal.axis,
extraRange = ordinal.convertOverscroll(axis.options.overscroll),
distance = ordinal.overscrollPointsRange,
positions = [];
var max = axis.dataMax;
if (OrdinalAxis_defined(distance)) {
// Max + pointRange because we need to scroll to the last
while (max < axis.dataMax + extraRange) {
max += distance;
positions.push(max);
}
}
return positions;
};
/**
* Make the tick intervals closer because the ordinal gaps make the
* ticks spread out or cluster.
* @private
*/
Additions.prototype.postProcessTickInterval = function (tickInterval) {
// Problem: https://jsfiddle.net/highcharts/FQm4E/1/. This is a case
// where this algorithm doesn't work optimally. In this case, the
// tick labels are spread out per week, but all the gaps reside
// within weeks. So we have a situation where the labels are courser
// than the ordinal gaps, and thus the tick interval should not be
// altered.
var ordinal = this,
axis = ordinal.axis,
ordinalSlope = ordinal.slope,
closestPointRange = axis.closestPointRange;
var ret;
if (ordinalSlope && closestPointRange) {
if (!axis.options.breaks) {
ret = (tickInterval /
(ordinalSlope / closestPointRange));
}
else {
ret = closestPointRange || tickInterval; // #7275
}
}
else {
ret = tickInterval;
}
return ret;
};
/**
* If overscroll is pixel or pecentage value, convert it to axis range.
*
* @private
* @param {number | string} overscroll
* Overscroll value in axis range, pixels or percentage value.
* @return {number}
* Overscroll value in axis range.
*/
Additions.prototype.convertOverscroll = function (overscroll) {
if (overscroll === void 0) { overscroll = 0; }
var ordinal = this,
axis = ordinal.axis,
calculateOverscroll = function (overscrollPercentage) {
return OrdinalAxis_pick(ordinal.originalOrdinalRange,
OrdinalAxis_defined(axis.dataMax) && OrdinalAxis_defined(axis.dataMin) ?
axis.dataMax - axis.dataMin : 0) * overscrollPercentage;
};
if (OrdinalAxis_isString(overscroll)) {
var overscrollValue = parseInt(overscroll, 10);
if (/%$/.test(overscroll)) {
// If overscroll is percentage
return calculateOverscroll(overscrollValue / 100);
}
if (/px/.test(overscroll)) {
// If overscroll is pixels, it is limited to 90% of the axis
// length to prevent division by zero
var limitedOverscrollValue = Math.min(overscrollValue,
axis.len * 0.9),
pixelToPercent = limitedOverscrollValue / axis.len;
return calculateOverscroll(pixelToPercent / (1 - pixelToPercent));
}
// If overscroll is a string but not pixels or percentage,
// return 0 as no overscroll
return 0;
}
return overscroll;
};
return Additions;
}());
OrdinalAxis.Additions = Additions;
})(OrdinalAxis || (OrdinalAxis = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Axis_OrdinalAxis = (OrdinalAxis);
;// ./code/es5/es-modules/Stock/RangeSelector/RangeSelectorDefaults.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* Declarations
*
* */
/**
* 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.
*
* ```js
* Highcharts.setOptions({
* lang: {
* locale: 'fr'
* }
* });
* ```
*
* @optionparent lang
*/
var lang = {
/**
* The text for the label for the range selector buttons.
*
* @product highstock gantt
*/
rangeSelectorZoom: 'Zoom',
/**
* The text for the label for the "from" input box in the range
* selector. Since v9.0, this string is empty as the label is not
* rendered by default.
*
* @product highstock gantt
*/
rangeSelectorFrom: '',
/**
* The text for the label for the "to" input box in the range selector.
*
* @product highstock gantt
*/
rangeSelectorTo: '→'
};
/**
* The range selector is a tool for selecting ranges to display within
* the chart. It provides buttons to select preconfigured ranges in
* the chart, like 1 day, 1 week, 1 month etc. It also provides input
* boxes where min and max dates can be manually input.
*
* @product highstock gantt
* @optionparent rangeSelector
*/
var rangeSelector = {
/**
* Whether to enable all buttons from the start. By default buttons are
* only enabled if the corresponding time range exists on the X axis,
* but enabling all buttons allows for dynamically loading different
* time ranges.
*
* @sample {highstock} stock/rangeselector/allbuttonsenabled-true/
* All buttons enabled
*
* @since 2.0.3
*/
allButtonsEnabled: false,
/**
* An array of configuration objects for the buttons.
*
* Defaults to:
* ```js
* buttons: [{
* type: 'month',
* count: 1,
* text: '1m',
* title: 'View 1 month'
* }, {
* type: 'month',
* count: 3,
* text: '3m',
* title: 'View 3 months'
* }, {
* type: 'month',
* count: 6,
* text: '6m',
* title: 'View 6 months'
* }, {
* type: 'ytd',
* text: 'YTD',
* title: 'View year to date'
* }, {
* type: 'year',
* count: 1,
* text: '1y',
* title: 'View 1 year'
* }, {
* type: 'all',
* text: 'All',
* title: 'View all'
* }]
* ```
*
* @sample {highstock} stock/demo/rangeselector-datagrouping/
* Data grouping by buttons
*
* @type {Array<*>}
*/
buttons: [{
type: 'month',
count: 1,
text: '1m',
title: 'View 1 month'
}, {
type: 'month',
count: 3,
text: '3m',
title: 'View 3 months'
}, {
type: 'month',
count: 6,
text: '6m',
title: 'View 6 months'
}, {
type: 'ytd',
text: 'YTD',
title: 'View year to date'
}, {
type: 'year',
count: 1,
text: '1y',
title: 'View 1 year'
}, {
type: 'all',
text: 'All',
title: 'View all'
}],
/**
* How many units of the defined type the button should span. If `type`
* is "month" and `count` is 3, the button spans three months.
*
* @type {number}
* @default 1
* @apioption rangeSelector.buttons.count
*/
/**
* Fires when clicking on the rangeSelector button. One parameter,
* event, is passed to the function, containing common event
* information.
*
* ```js
* click: function(e) {
* console.log(this);
* }
* ```
*
* Return false to stop default button's click action.
*
* @sample {highstock} stock/rangeselector/button-click/
* Click event on the button
*
* @type {Highcharts.RangeSelectorClickCallbackFunction}
* @apioption rangeSelector.buttons.events.click
*/
/**
* Additional range (in milliseconds) added to the end of the calculated
* time span.
*
* @sample {highstock} stock/rangeselector/min-max-offsets/
* Button offsets
*
* @type {number}
* @default 0
* @since 6.0.0
* @apioption rangeSelector.buttons.offsetMax
*/
/**
* Additional range (in milliseconds) added to the start of the
* calculated time span.
*
* @sample {highstock} stock/rangeselector/min-max-offsets/
* Button offsets
*
* @type {number}
* @default 0
* @since 6.0.0
* @apioption rangeSelector.buttons.offsetMin
*/
/**
* When buttons apply dataGrouping on a series, by default zooming
* in/out will deselect buttons and unset dataGrouping. Enable this
* option to keep buttons selected when extremes change.
*
* @sample {highstock} stock/rangeselector/preserve-datagrouping/
* Different preserveDataGrouping settings
*
* @type {boolean}
* @default false
* @since 6.1.2
* @apioption rangeSelector.buttons.preserveDataGrouping
*/
/**
* A custom data grouping object for each button.
*
* @see [series.dataGrouping](#plotOptions.series.dataGrouping)
*
* @sample {highstock} stock/demo/rangeselector-datagrouping/
* Data grouping by range selector buttons
*
* @type {*}
* @extends plotOptions.series.dataGrouping
* @apioption rangeSelector.buttons.dataGrouping
*/
/**
* The text for the button itself.
*
* @type {string}
* @apioption rangeSelector.buttons.text
*/
/**
* Explanation for the button, shown as a tooltip on hover, and used by
* assistive technology.
*
* @type {string}
* @apioption rangeSelector.buttons.title
*/
/**
* Defined the time span for the button. Can be one of `millisecond`,
* `second`, `minute`, `hour`, `day`, `week`, `month`, `year`, `ytd`,
* and `all`.
*
* @type {Highcharts.RangeSelectorButtonTypeValue}
* @apioption rangeSelector.buttons.type
*/
/**
* The space in pixels between the buttons in the range selector.
*/
buttonSpacing: 5,
/**
* Whether to collapse the range selector buttons into a dropdown when
* there is not enough room to show everything in a single row, instead
* of dividing the range selector into multiple rows.
* Can be one of the following:
* - `always`: Always collapse
* - `responsive`: Only collapse when there is not enough room
* - `never`: Never collapse
*
* @sample {highstock} stock/rangeselector/dropdown/
* Dropdown option
*
* @validvalue ["always", "responsive", "never"]
* @since 9.0.0
*/
dropdown: 'responsive',
/**
* Enable or disable the range selector. Default to `true` for stock
* charts, using the `stockChart` factory.
*
* @sample {highstock} stock/rangeselector/enabled/
* Disable the range selector
*
* @type {boolean|undefined}
* @default {highstock} true
*/
enabled: void 0,
/**
* The vertical alignment of the rangeselector box. Allowed properties
* are `top`, `middle`, `bottom`.
*
* @sample {highstock} stock/rangeselector/vertical-align-middle/
* Middle
* @sample {highstock} stock/rangeselector/vertical-align-bottom/
* Bottom
*
* @type {Highcharts.VerticalAlignValue}
* @since 6.0.0
*/
verticalAlign: 'top',
/**
* A collection of attributes for the buttons. The object takes SVG
* attributes like `fill`, `stroke`, `stroke-width`, as well as `style`,
* a collection of CSS properties for the text.
*
* The object can also be extended with states, so you can set
* presentational options for `hover`, `select` or `disabled` button
* states.
*
* CSS styles for the text label.
*
* In styled mode, the buttons are styled by the
* `.highcharts-range-selector-buttons .highcharts-button` rule with its
* different states.
*
* @sample {highstock} stock/rangeselector/styling/
* Styling the buttons and inputs
*
* @type {Highcharts.SVGAttributes}
*/
buttonTheme: {
/** @ignore */
width: 28,
/** @ignore */
height: 18,
/** @ignore */
padding: 2,
/** @ignore */
zIndex: 7 // #484, #852
},
/**
* When the rangeselector is floating, the plot area does not reserve
* space for it. This opens for positioning anywhere on the chart.
*
* @sample {highstock} stock/rangeselector/floating/
* Placing the range selector between the plot area and the
* navigator
*
* @since 6.0.0
*/
floating: false,
/**
* The x offset of the range selector relative to its horizontal
* alignment within `chart.spacingLeft` and `chart.spacingRight`.
*
* @since 6.0.0
*/
x: 0,
/**
* The y offset of the range selector relative to its horizontal
* alignment within `chart.spacingLeft` and `chart.spacingRight`.
*
* @since 6.0.0
*/
y: 0,
/**
* Deprecated. The height of the range selector. Currently it is
* calculated dynamically.
*
* @deprecated
* @type {number|undefined}
* @since 2.1.9
*/
height: void 0, // Reserved space for buttons and input
/**
* The border color of the date input boxes.
*
* @sample {highstock} stock/rangeselector/styling/
* Styling the buttons and inputs
*
* @type {Highcharts.ColorString}
* @since 1.3.7
*/
inputBoxBorderColor: 'none',
/**
* The pixel height of the date input boxes.
*
* @sample {highstock} stock/rangeselector/styling/
* Styling the buttons and inputs
*
* @since 1.3.7
*/
inputBoxHeight: 17,
/**
* The pixel width of the date input boxes. When `undefined`, the width
* is fitted to the rendered content.
*
* @sample {highstock} stock/rangeselector/styling/
* Styling the buttons and inputs
*
* @type {number|undefined}
* @since 1.3.7
*/
inputBoxWidth: void 0,
/**
* The date format in the input boxes when not selected for editing.
* Defaults to `%e %b %Y`.
*
* This is used to determine which type of input to show,
* `datetime-local`, `date` or `time` and falling back to `text` when
* the browser does not support the input type or the format contains
* milliseconds.
*
* @sample {highstock} stock/rangeselector/input-type/
* Input types
* @sample {highstock} stock/rangeselector/input-format/
* Milliseconds in the range selector
*
*/
inputDateFormat: '%[ebY]',
/**
* A custom callback function to parse values entered in the input boxes and
* return a valid JavaScript time as milliseconds since 1970. The first
* argument passed is the value to parse, second is a boolean indicating use
* of UTC time. The third is a reference to the `time` object. Time zone can
* be read from `time.timezone`.
*
* This will only get called for inputs of type `text`. Since v8.2.3, the
* input type is dynamically determined based on the granularity of the
* `inputDateFormat` and the browser support.
*
* @sample {highstock} stock/rangeselector/input-format/
* Milliseconds in the range selector
*
* @type {Highcharts.RangeSelectorParseCallbackFunction}
* @since 1.3.3
*/
inputDateParser: void 0,
/**
* The date format in the input boxes when they are selected for
* editing. This must be a format that is recognized by JavaScript
* Date.parse.
*
* This will only be used for inputs of type `text`. Since v8.2.3,
* the input type is dynamically determined based on the granularity
* of the `inputDateFormat` and the browser support.
*
* @sample {highstock} stock/rangeselector/input-format/
* Milliseconds in the range selector
*
*/
inputEditDateFormat: '%Y-%m-%d',
/**
* Enable or disable the date input boxes.
*/
inputEnabled: true,
/**
* Positioning for the input boxes. Allowed properties are `align`,
* `x` and `y`.
*
* @since 1.2.4
*/
inputPosition: {
/**
* The alignment of the input box. Allowed properties are `left`,
* `center`, `right`.
*
* @sample {highstock} stock/rangeselector/input-button-opposite-alignment/
* Opposite alignment
*
* @sample {highstock} stock/rangeselector/input-button-same-alignment/
* Same alignment for buttons and input
*
* @type {Highcharts.AlignValue}
* @since 6.0.0
*/
align: 'right',
/**
* X offset of the input row.
*/
x: 0,
/**
* Y offset of the input row.
*/
y: 0
},
/**
* The space in pixels between the labels and the date input boxes in
* the range selector.
*
* @since 9.0.0
*/
inputSpacing: 5,
/**
* The index of the button to appear pre-selected. If the selected range
* exceeds the total data range and the 'all' option is available,
* the 'all' option, showing the full range, is automatically selected.
*
* @type {number}
*/
selected: void 0,
/**
* Positioning for the button row.
*
* @since 1.2.4
*/
buttonPosition: {
/**
* The alignment of the input box. Allowed properties are `left`,
* `center`, `right`.
*
* @sample {highstock} stock/rangeselector/input-button-opposite-alignment/
* Opposite alignment
*
* @sample {highstock} stock/rangeselector/input-button-same-alignment/
* Same alignment for buttons and input
*
* @type {Highcharts.AlignValue}
* @since 6.0.0
*/
align: 'left',
/**
* X offset of the button row.
*/
x: 0,
/**
* Y offset of the button row.
*/
y: 0
},
/**
* CSS for the HTML inputs in the range selector.
*
* In styled mode, the inputs are styled by the
* `.highcharts-range-input text` rule in SVG mode, and
* `input.highcharts-range-selector` when active.
*
* @sample {highstock} stock/rangeselector/styling/
* Styling the buttons and inputs
*
* @type {Highcharts.CSSObject}
* @apioption rangeSelector.inputStyle
*/
inputStyle: {
/** @ignore */
color: "#334eff" /* Palette.highlightColor80 */,
/** @ignore */
cursor: 'pointer',
/** @ignore */
fontSize: '0.8em'
},
/**
* CSS styles for the labels - the Zoom, From and To texts.
*
* In styled mode, the labels are styled by the
* `.highcharts-range-label` class.
*
* @sample {highstock} stock/rangeselector/styling/
* Styling the buttons and inputs
*
* @type {Highcharts.CSSObject}
*/
labelStyle: {
/** @ignore */
color: "#666666" /* Palette.neutralColor60 */,
/** @ignore */
fontSize: '0.8em'
}
};
/* *
*
* Default Export
*
* */
var RangeSelectorDefaults = {
lang: lang,
rangeSelector: rangeSelector
};
/* harmony default export */ var RangeSelector_RangeSelectorDefaults = (RangeSelectorDefaults);
;// ./code/es5/es-modules/Stock/RangeSelector/RangeSelectorComposition.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var RangeSelectorComposition_defaultOptions = Defaults.defaultOptions;
var RangeSelectorComposition_composed = Core_Globals.composed;
var RangeSelectorComposition_addEvent = Core_Utilities.addEvent, RangeSelectorComposition_defined = Core_Utilities.defined, RangeSelectorComposition_extend = Core_Utilities.extend, RangeSelectorComposition_isNumber = Core_Utilities.isNumber, RangeSelectorComposition_merge = Core_Utilities.merge, RangeSelectorComposition_pick = Core_Utilities.pick, RangeSelectorComposition_pushUnique = Core_Utilities.pushUnique;
/* *
*
* Constants
*
* */
var chartDestroyEvents = [];
/* *
*
* Variables
*
* */
var RangeSelectorConstructor;
/* *
*
* Functions
*
* */
/**
* Get the axis min value based on the range option and the current max. For
* stock charts this is extended via the {@link RangeSelector} so that if the
* selected range is a multiple of months or years, it is compensated for
* various month lengths.
*
* @private
* @function Highcharts.Axis#minFromRange
* @return {number|undefined}
* The new minimum value.
*/
function axisMinFromRange() {
var rangeOptions = this.range,
type = rangeOptions.type,
max = this.max,
time = this.chart.time,
// Get the true range from a start date
getTrueRange = function (base,
count) {
var original = time.toParts(base),
modified = original.slice();
if (type === 'year') {
modified[0] += count;
}
else {
modified[1] += count;
}
var d = time.makeTime.apply(time,
modified);
var numbers = time.toParts(d);
// When subtracting a month still places us in the same month, like
// subtracting one month from March 31 places us on February 31,
// which translates to March 3 (#6537)
if (type === 'month' &&
original[1] === numbers[1] &&
Math.abs(count) === 1) {
modified[0] = original[0];
modified[1] = original[1];
// 0 is the last day of the previous month
modified[2] = 0;
}
d = time.makeTime.apply(time, modified);
return d - base;
};
var min,
range;
if (RangeSelectorComposition_isNumber(rangeOptions)) {
min = max - rangeOptions;
range = rangeOptions;
}
else if (rangeOptions) {
min = max + getTrueRange(max, -(rangeOptions.count || 1));
// Let the fixedRange reflect initial settings (#5930)
if (this.chart) {
this.chart.setFixedRange(max - min);
}
}
var dataMin = RangeSelectorComposition_pick(this.dataMin,
Number.MIN_VALUE);
if (!RangeSelectorComposition_isNumber(min)) {
min = dataMin;
}
if (min <= dataMin) {
min = dataMin;
if (typeof range === 'undefined') { // #4501
range = getTrueRange(min, rangeOptions.count);
}
this.newMax = Math.min(min + range, RangeSelectorComposition_pick(this.dataMax, Number.MAX_VALUE));
}
if (!RangeSelectorComposition_isNumber(max)) {
min = void 0;
}
else if (!RangeSelectorComposition_isNumber(rangeOptions) &&
rangeOptions &&
rangeOptions._offsetMin) {
min += rangeOptions._offsetMin;
}
return min;
}
/**
* @private
*/
function updateRangeSelectorButtons() {
var _a;
(_a = this.rangeSelector) === null || _a === void 0 ? void 0 : _a.redrawElements();
}
/**
* @private
*/
function RangeSelectorComposition_compose(AxisClass, ChartClass, RangeSelectorClass) {
RangeSelectorConstructor = RangeSelectorClass;
if (RangeSelectorComposition_pushUnique(RangeSelectorComposition_composed, 'RangeSelector')) {
var chartProto = ChartClass.prototype;
AxisClass.prototype.minFromRange = axisMinFromRange;
RangeSelectorComposition_addEvent(ChartClass, 'afterGetContainer', createRangeSelector);
RangeSelectorComposition_addEvent(ChartClass, 'beforeRender', RangeSelectorComposition_onChartBeforeRender);
RangeSelectorComposition_addEvent(ChartClass, 'destroy', onChartDestroy);
RangeSelectorComposition_addEvent(ChartClass, 'getMargins', onChartGetMargins);
RangeSelectorComposition_addEvent(ChartClass, 'redraw', redrawRangeSelector);
RangeSelectorComposition_addEvent(ChartClass, 'update', RangeSelectorComposition_onChartUpdate);
RangeSelectorComposition_addEvent(ChartClass, 'beforeRedraw', updateRangeSelectorButtons);
chartProto.callbacks.push(redrawRangeSelector);
RangeSelectorComposition_extend(RangeSelectorComposition_defaultOptions, { rangeSelector: RangeSelector_RangeSelectorDefaults.rangeSelector });
RangeSelectorComposition_extend(RangeSelectorComposition_defaultOptions.lang, RangeSelector_RangeSelectorDefaults.lang);
}
}
/**
* Initialize rangeselector for stock charts
* @private
*/
function createRangeSelector() {
if (this.options.rangeSelector &&
this.options.rangeSelector.enabled) {
this.rangeSelector = new RangeSelectorConstructor(this);
}
}
/**
* @private
*/
function RangeSelectorComposition_onChartBeforeRender() {
var chart = this,
rangeSelector = chart.rangeSelector;
if (rangeSelector) {
if (RangeSelectorComposition_isNumber(rangeSelector.deferredYTDClick)) {
rangeSelector.clickButton(rangeSelector.deferredYTDClick);
delete rangeSelector.deferredYTDClick;
}
var verticalAlign = rangeSelector.options.verticalAlign;
if (!rangeSelector.options.floating) {
if (verticalAlign === 'bottom') {
this.extraBottomMargin = true;
}
else if (verticalAlign === 'top') {
this.extraTopMargin = true;
}
}
}
}
function redrawRangeSelector() {
var chart = this;
var rangeSelector = this.rangeSelector;
if (!rangeSelector) {
return;
}
var alignTo;
var extremes = chart.xAxis[0].getExtremes();
var legend = chart.legend;
var verticalAlign = (rangeSelector &&
rangeSelector.options.verticalAlign);
if (RangeSelectorComposition_isNumber(extremes.min)) {
rangeSelector.render(extremes.min, extremes.max);
}
// Re-align the legend so that it's below the rangeselector
if (legend.display &&
verticalAlign === 'top' &&
verticalAlign === legend.options.verticalAlign) {
// Create a new alignment box for the legend.
alignTo = RangeSelectorComposition_merge(chart.spacingBox);
if (legend.options.layout === 'vertical') {
alignTo.y = chart.plotTop;
}
else {
alignTo.y += rangeSelector.getHeight();
}
legend.group.placed = false; // Don't animate the alignment.
legend.align(alignTo);
}
}
/**
* Remove resize/afterSetExtremes at chart destroy.
* @private
*/
function onChartDestroy() {
for (var i = 0, iEnd = chartDestroyEvents.length; i < iEnd; ++i) {
var events = chartDestroyEvents[i];
if (events[0] === this) {
events[1].forEach(function (unbind) { return unbind(); });
chartDestroyEvents.splice(i, 1);
return;
}
}
}
/**
*
*/
function onChartGetMargins() {
var _a;
var rangeSelector = this.rangeSelector;
if ((_a = rangeSelector === null || rangeSelector === void 0 ? void 0 : rangeSelector.options) === null || _a === void 0 ? void 0 : _a.enabled) {
var rangeSelectorHeight = rangeSelector.getHeight();
var verticalAlign = rangeSelector.options.verticalAlign;
if (!rangeSelector.options.floating) {
if (verticalAlign === 'bottom') {
this.marginBottom += rangeSelectorHeight;
}
else if (verticalAlign !== 'middle') {
this.plotTop += rangeSelectorHeight;
}
}
}
}
/**
* @private
*/
function RangeSelectorComposition_onChartUpdate(e) {
var chart = this,
options = e.options,
optionsRangeSelector = options.rangeSelector,
extraBottomMarginWas = this.extraBottomMargin,
extraTopMarginWas = this.extraTopMargin;
var rangeSelector = chart.rangeSelector;
if (optionsRangeSelector &&
optionsRangeSelector.enabled &&
!RangeSelectorComposition_defined(rangeSelector) &&
this.options.rangeSelector) {
this.options.rangeSelector.enabled = true;
this.rangeSelector = rangeSelector = new RangeSelectorConstructor(this);
}
this.extraBottomMargin = false;
this.extraTopMargin = false;
if (rangeSelector) {
var verticalAlign = (optionsRangeSelector &&
optionsRangeSelector.verticalAlign) || (rangeSelector.options && rangeSelector.options.verticalAlign);
if (!rangeSelector.options.floating) {
if (verticalAlign === 'bottom') {
this.extraBottomMargin = true;
}
else if (verticalAlign !== 'middle') {
this.extraTopMargin = true;
}
}
if (this.extraBottomMargin !== extraBottomMarginWas ||
this.extraTopMargin !== extraTopMarginWas) {
this.isDirtyBox = true;
}
}
}
/* *
*
* Default Export
*
* */
var RangeSelectorComposition = {
compose: RangeSelectorComposition_compose
};
/* harmony default export */ var RangeSelector_RangeSelectorComposition = (RangeSelectorComposition);
;// ./code/es5/es-modules/Stock/RangeSelector/RangeSelector.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var RangeSelector_defaultOptions = Defaults.defaultOptions;
var RangeSelector_addEvent = Core_Utilities.addEvent, RangeSelector_createElement = Core_Utilities.createElement, RangeSelector_css = Core_Utilities.css, RangeSelector_defined = Core_Utilities.defined, RangeSelector_destroyObjectProperties = Core_Utilities.destroyObjectProperties, RangeSelector_diffObjects = Core_Utilities.diffObjects, RangeSelector_discardElement = Core_Utilities.discardElement, RangeSelector_extend = Core_Utilities.extend, RangeSelector_fireEvent = Core_Utilities.fireEvent, RangeSelector_isNumber = Core_Utilities.isNumber, RangeSelector_isString = Core_Utilities.isString, RangeSelector_merge = Core_Utilities.merge, RangeSelector_objectEach = Core_Utilities.objectEach, RangeSelector_pick = Core_Utilities.pick, RangeSelector_splat = Core_Utilities.splat;
/* *
*
* Functions
*
* */
/**
* Get the preferred input type based on a date format string.
*
* @private
* @function preferredInputType
*/
function preferredInputType(format) {
var hasTimeKey = function (char) {
return new RegExp("%[[a-zA-Z]*".concat(char)).test(format);
};
var ms = RangeSelector_isString(format) ?
format.indexOf('%L') !== -1 :
// Implemented but not typed as of 2024
format.fractionalSecondDigits;
if (ms) {
return 'text';
}
var date = RangeSelector_isString(format) ?
['a', 'A', 'd', 'e', 'w', 'b', 'B', 'm', 'o', 'y', 'Y']
.some(hasTimeKey) :
format.dateStyle || format.day || format.month || format.year;
var time = RangeSelector_isString(format) ?
['H', 'k', 'I', 'l', 'M', 'S'].some(hasTimeKey) :
format.timeStyle || format.hour || format.minute || format.second;
if (date && time) {
return 'datetime-local';
}
if (date) {
return 'date';
}
if (time) {
return 'time';
}
return 'text';
}
/* *
*
* Class
*
* */
/**
* The range selector.
*
* @private
* @class
* @name Highcharts.RangeSelector
* @param {Highcharts.Chart} chart
*/
var RangeSelector = /** @class */ (function () {
/* *
*
* Constructor
*
* */
function RangeSelector(chart) {
var _this = this;
this.isDirty = false;
this.buttonOptions = RangeSelector.prototype.defaultButtons;
this.initialButtonGroupWidth = 0;
this.maxButtonWidth = function () {
var buttonWidth = 0;
_this.buttons.forEach(function (button) {
var bBox = button.getBBox();
if (bBox.width > buttonWidth) {
buttonWidth = bBox.width;
}
});
return buttonWidth;
};
this.init(chart);
}
/* *
*
* Static Functions
*
* */
/**
* @private
*/
RangeSelector.compose = function (AxisClass, ChartClass) {
RangeSelector_RangeSelectorComposition.compose(AxisClass, ChartClass, RangeSelector);
};
/* *
*
* Functions
*
* */
/**
* The method to run when one of the buttons in the range selectors is
* clicked
*
* @private
* @function Highcharts.RangeSelector#clickButton
* @param {number} i
* The index of the button
* @param {boolean} [redraw]
*/
RangeSelector.prototype.clickButton = function (i, redraw) {
var rangeSelector = this,
chart = rangeSelector.chart,
rangeOptions = rangeSelector.buttonOptions[i],
baseAxis = chart.xAxis[0],
unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || baseAxis || {},
type = rangeOptions.type,
dataGrouping = rangeOptions.dataGrouping;
var dataMin = unionExtremes.dataMin,
dataMax = unionExtremes.dataMax,
newMin,
newMax = RangeSelector_isNumber(baseAxis === null || baseAxis === void 0 ? void 0 : baseAxis.max) ? Math.round(Math.min(baseAxis.max,
dataMax !== null && dataMax !== void 0 ? dataMax : baseAxis.max)) : void 0, // #1568
baseXAxisOptions,
range = rangeOptions._range,
rangeMin,
ctx,
ytdExtremes,
addOffsetMin = true;
// Chart has no data, base series is removed
if (dataMin === null || dataMax === null) {
return;
}
rangeSelector.setSelected(i);
// Apply dataGrouping associated to button
if (dataGrouping) {
this.forcedDataGrouping = true;
Axis_Axis.prototype.setDataGrouping.call(baseAxis || { chart: this.chart }, dataGrouping, false);
this.frozenStates = rangeOptions.preserveDataGrouping;
}
// Apply range
if (type === 'month' || type === 'year') {
if (!baseAxis) {
// This is set to the user options and picked up later when the
// axis is instantiated so that we know the min and max.
range = rangeOptions;
}
else {
ctx = {
range: rangeOptions,
max: newMax,
chart: chart,
dataMin: dataMin,
dataMax: dataMax
};
newMin = baseAxis.minFromRange.call(ctx);
if (RangeSelector_isNumber(ctx.newMax)) {
newMax = ctx.newMax;
}
// #15799: offsetMin is added in minFromRange so that it works
// with pre-selected buttons as well
addOffsetMin = false;
}
// Fixed times like minutes, hours, days
}
else if (range) {
if (RangeSelector_isNumber(newMax)) {
newMin = Math.max(newMax - range, dataMin);
newMax = Math.min(newMin + range, dataMax);
addOffsetMin = false;
}
}
else if (type === 'ytd') {
// On user clicks on the buttons, or a delayed action running from
// the beforeRender event (below), the baseAxis is defined.
if (baseAxis) {
// When "ytd" is the pre-selected button for the initial view,
// its calculation is delayed and rerun in the beforeRender
// event (below). When the series are initialized, but before
// the chart is rendered, we have access to the xData array
// (#942).
if (baseAxis.hasData() && (!RangeSelector_isNumber(dataMax) ||
!RangeSelector_isNumber(dataMin))) {
dataMin = Number.MAX_VALUE;
dataMax = -Number.MAX_VALUE;
chart.series.forEach(function (series) {
// Reassign it to the last item
var xData = series.getColumn('x');
if (xData.length) {
dataMin = Math.min(xData[0], dataMin);
dataMax = Math.max(xData[xData.length - 1], dataMax);
}
});
redraw = false;
}
if (RangeSelector_isNumber(dataMax) && RangeSelector_isNumber(dataMin)) {
ytdExtremes = rangeSelector.getYTDExtremes(dataMax, dataMin);
newMin = rangeMin = ytdExtremes.min;
newMax = ytdExtremes.max;
}
// "ytd" is pre-selected. We don't yet have access to processed
// point and extremes data (things like pointStart and pointInterval
// are missing), so we delay the process (#942)
}
else {
rangeSelector.deferredYTDClick = i;
return;
}
}
else if (type === 'all' && baseAxis) {
// If the navigator exist and the axis range is declared reset that
// range and from now on only use the range set by a user, #14742.
if (chart.navigator && chart.navigator.baseSeries[0]) {
chart.navigator.baseSeries[0].xAxis.options.range = void 0;
}
newMin = dataMin;
newMax = dataMax;
}
if (addOffsetMin && rangeOptions._offsetMin && RangeSelector_defined(newMin)) {
newMin += rangeOptions._offsetMin;
}
if (rangeOptions._offsetMax && RangeSelector_defined(newMax)) {
newMax += rangeOptions._offsetMax;
}
if (this.dropdown) {
this.dropdown.selectedIndex = i + 1;
}
// Update the chart
if (!baseAxis) {
// Axis not yet instantiated. Temporarily set min and range
// options and axes once defined and remove them on
// chart load (#4317 & #20529).
baseXAxisOptions = RangeSelector_splat(chart.options.xAxis || {})[0];
var axisRangeUpdateEvent_1 = RangeSelector_addEvent(chart, 'afterCreateAxes',
function () {
var xAxis = chart.xAxis[0];
xAxis.range = xAxis.options.range = range;
xAxis.min = xAxis.options.min = rangeMin;
});
RangeSelector_addEvent(chart, 'load', function resetMinAndRange() {
var xAxis = chart.xAxis[0];
chart.setFixedRange(rangeOptions._range);
xAxis.options.range = baseXAxisOptions.range;
xAxis.options.min = baseXAxisOptions.min;
axisRangeUpdateEvent_1(); // Remove event
});
}
else if (RangeSelector_isNumber(newMin) && RangeSelector_isNumber(newMax)) {
// Existing axis object. Set extremes after render time.
baseAxis.setExtremes(newMin, newMax, RangeSelector_pick(redraw, true), void 0, // Auto animation
{
trigger: 'rangeSelectorButton',
rangeSelectorButton: rangeOptions
});
chart.setFixedRange(rangeOptions._range);
}
RangeSelector_fireEvent(this, 'afterBtnClick');
};
/**
* Set the selected option. This method only sets the internal flag, it
* doesn't update the buttons or the actual zoomed range.
*
* @private
* @function Highcharts.RangeSelector#setSelected
* @param {number} [selected]
*/
RangeSelector.prototype.setSelected = function (selected) {
this.selected = this.options.selected = selected;
};
/**
* Initialize the range selector
*
* @private
* @function Highcharts.RangeSelector#init
* @param {Highcharts.Chart} chart
*/
RangeSelector.prototype.init = function (chart) {
var rangeSelector = this,
options = chart.options.rangeSelector,
buttonOptions = options.buttons,
selectedOption = options.selected,
blurInputs = function () {
var minInput = rangeSelector.minInput,
maxInput = rangeSelector.maxInput;
// #3274 in some case blur is not defined
if (minInput && !!minInput.blur) {
RangeSelector_fireEvent(minInput, 'blur');
}
if (maxInput && !!maxInput.blur) {
RangeSelector_fireEvent(maxInput, 'blur');
}
};
rangeSelector.chart = chart;
rangeSelector.options = options;
rangeSelector.buttons = [];
rangeSelector.buttonOptions = buttonOptions;
this.eventsToUnbind = [];
this.eventsToUnbind.push(RangeSelector_addEvent(chart.container, 'mousedown', blurInputs));
this.eventsToUnbind.push(RangeSelector_addEvent(chart, 'resize', blurInputs));
// Extend the buttonOptions with actual range
buttonOptions.forEach(rangeSelector.computeButtonRange);
// Zoomed range based on a pre-selected button index
if (typeof selectedOption !== 'undefined' &&
buttonOptions[selectedOption]) {
this.clickButton(selectedOption, false);
}
this.eventsToUnbind.push(RangeSelector_addEvent(chart, 'load', function () {
// If a data grouping is applied to the current button, release it
// when extremes change
if (chart.xAxis && chart.xAxis[0]) {
RangeSelector_addEvent(chart.xAxis[0], 'setExtremes', function (e) {
if (RangeSelector_isNumber(this.max) &&
RangeSelector_isNumber(this.min) &&
this.max - this.min !== chart.fixedRange &&
e.trigger !== 'rangeSelectorButton' &&
e.trigger !== 'updatedData' &&
rangeSelector.forcedDataGrouping &&
!rangeSelector.frozenStates) {
this.setDataGrouping(false, false);
}
});
}
}));
this.createElements();
};
/**
* Dynamically update the range selector buttons after a new range has been
* set
*
* @private
* @function Highcharts.RangeSelector#updateButtonStates
*/
RangeSelector.prototype.updateButtonStates = function () {
var rangeSelector = this,
chart = this.chart,
dropdown = this.dropdown,
dropdownLabel = this.dropdownLabel,
baseAxis = chart.xAxis[0],
actualRange = Math.round(baseAxis.max - baseAxis.min),
hasNoData = !baseAxis.hasVisibleSeries,
day = 24 * 36e5, // A single day in milliseconds
unionExtremes = (chart.scroller &&
chart.scroller.getUnionExtremes()) || baseAxis,
dataMin = unionExtremes.dataMin,
dataMax = unionExtremes.dataMax,
ytdExtremes = rangeSelector.getYTDExtremes(dataMax,
dataMin),
ytdMin = ytdExtremes.min,
ytdMax = ytdExtremes.max,
selected = rangeSelector.selected,
allButtonsEnabled = rangeSelector.options.allButtonsEnabled,
buttonStates = new Array(rangeSelector.buttonOptions.length)
.fill(0),
selectedExists = RangeSelector_isNumber(selected),
buttons = rangeSelector.buttons;
var isSelectedTooGreat = false,
selectedIndex = null;
rangeSelector.buttonOptions.forEach(function (rangeOptions, i) {
var _a;
var range = rangeOptions._range,
type = rangeOptions.type,
count = rangeOptions.count || 1,
offsetRange = rangeOptions._offsetMax -
rangeOptions._offsetMin,
isSelected = i === selected,
// Disable buttons where the range exceeds what is allowed i;
// the current view
isTooGreatRange = range >
dataMax - dataMin,
// Disable buttons where the range is smaller than the minimum
// range
isTooSmallRange = range < baseAxis.minRange;
// Do not select the YTD button if not explicitly told so
var isYTDButNotSelected = false,
// Disable the All button if we're already showing all
isSameRange = range === actualRange;
if (isSelected && isTooGreatRange) {
isSelectedTooGreat = true;
}
if (baseAxis.isOrdinal &&
((_a = baseAxis.ordinal) === null || _a === void 0 ? void 0 : _a.positions) &&
range &&
actualRange < range) {
// Handle ordinal ranges
var positions = baseAxis.ordinal.positions,
prevOrdinalPosition = Axis_OrdinalAxis.Additions.findIndexOf(positions,
baseAxis.min,
true),
nextOrdinalPosition = Math.min(Axis_OrdinalAxis.Additions.findIndexOf(positions,
baseAxis.max,
true) + 1,
positions.length - 1);
if (positions[nextOrdinalPosition] -
positions[prevOrdinalPosition] > range) {
isSameRange = true;
}
}
else if (
// Months and years have variable range so we check the extremes
(type === 'month' || type === 'year') &&
(actualRange + 36e5 >=
{ month: 28, year: 365 }[type] * day * count - offsetRange) &&
(actualRange - 36e5 <=
{ month: 31, year: 366 }[type] * day * count + offsetRange)) {
isSameRange = true;
}
else if (type === 'ytd') {
isSameRange = (ytdMax - ytdMin + offsetRange) === actualRange;
isYTDButNotSelected = !isSelected;
}
else if (type === 'all') {
isSameRange = (baseAxis.max - baseAxis.min >=
dataMax - dataMin);
}
// The new zoom area happens to match the range for a button - mark
// it selected. This happens when scrolling across an ordinal gap.
// It can be seen in the intraday demos when selecting 1h and scroll
// across the night gap.
var disable = (!allButtonsEnabled &&
!(isSelectedTooGreat && type === 'all') &&
(isTooGreatRange ||
isTooSmallRange ||
hasNoData));
var select = ((isSelectedTooGreat && type === 'all') ||
(isYTDButNotSelected ? false : isSameRange) ||
(isSelected && rangeSelector.frozenStates));
if (disable) {
buttonStates[i] = 3;
}
else if (select) {
if (!selectedExists || i === selected) {
selectedIndex = i;
}
}
});
if (selectedIndex !== null) {
buttonStates[selectedIndex] = 2;
rangeSelector.setSelected(selectedIndex);
if (this.dropdown) {
this.dropdown.selectedIndex = selectedIndex + 1;
}
}
else {
rangeSelector.setSelected();
if (this.dropdown) {
this.dropdown.selectedIndex = -1;
}
if (dropdownLabel) {
dropdownLabel.setState(0);
dropdownLabel.attr({
text: (RangeSelector_defaultOptions.lang.rangeSelectorZoom || '') + ' ▾'
});
}
}
for (var i = 0; i < buttonStates.length; i++) {
var state = buttonStates[i];
var button = buttons[i];
if (button.state !== state) {
button.setState(state);
if (dropdown) {
dropdown.options[i + 1].disabled = (state === 3);
if (state === 2) {
if (dropdownLabel) {
dropdownLabel.setState(2);
dropdownLabel.attr({
text: rangeSelector.buttonOptions[i].text + ' ▾'
});
}
dropdown.selectedIndex = i + 1;
}
var bbox = dropdownLabel.getBBox();
RangeSelector_css(dropdown, {
width: "" + bbox.width + "px",
height: "" + bbox.height + "px"
});
}
}
}
};
/**
* Compute and cache the range for an individual button
*
* @private
* @function Highcharts.RangeSelector#computeButtonRange
* @param {Highcharts.RangeSelectorButtonsOptions} rangeOptions
*/
RangeSelector.prototype.computeButtonRange = function (rangeOptions) {
var type = rangeOptions.type,
count = rangeOptions.count || 1,
// These time intervals have a fixed number of milliseconds, as
// opposed to month, ytd and year
fixedTimes = {
millisecond: 1,
second: 1000,
minute: 60 * 1000,
hour: 3600 * 1000,
day: 24 * 3600 * 1000,
week: 7 * 24 * 3600 * 1000
};
// Store the range on the button object
if (fixedTimes[type]) {
rangeOptions._range = fixedTimes[type] * count;
}
else if (type === 'month' || type === 'year') {
rangeOptions._range = {
month: 30,
year: 365
}[type] * 24 * 36e5 * count;
}
rangeOptions._offsetMin = RangeSelector_pick(rangeOptions.offsetMin, 0);
rangeOptions._offsetMax = RangeSelector_pick(rangeOptions.offsetMax, 0);
rangeOptions._range +=
rangeOptions._offsetMax - rangeOptions._offsetMin;
};
/**
* Get the unix timestamp of a HTML input for the dates
*
* @private
* @function Highcharts.RangeSelector#getInputValue
*/
RangeSelector.prototype.getInputValue = function (name) {
var input = name === 'min' ? this.minInput : this.maxInput;
var options = this.chart.options
.rangeSelector;
var time = this.chart.time;
if (input) {
return ((input.type === 'text' && options.inputDateParser) ||
this.defaultInputDateParser)(input.value, time.timezone === 'UTC', time);
}
return 0;
};
/**
* Set the internal and displayed value of a HTML input for the dates
*
* @private
* @function Highcharts.RangeSelector#setInputValue
*/
RangeSelector.prototype.setInputValue = function (name, inputTime) {
var options = this.options, time = this.chart.time, input = name === 'min' ? this.minInput : this.maxInput, dateBox = name === 'min' ? this.minDateBox : this.maxDateBox;
if (input) {
input.setAttribute('type', preferredInputType(options.inputDateFormat || '%e %b %Y'));
var hcTimeAttr = input.getAttribute('data-hc-time');
var updatedTime = RangeSelector_defined(hcTimeAttr) ? Number(hcTimeAttr) : void 0;
if (RangeSelector_defined(inputTime)) {
var previousTime = updatedTime;
if (RangeSelector_defined(previousTime)) {
input.setAttribute('data-hc-time-previous', previousTime);
}
input.setAttribute('data-hc-time', inputTime);
updatedTime = inputTime;
}
input.value = time.dateFormat((this.inputTypeFormats[input.type] ||
options.inputEditDateFormat), updatedTime);
if (dateBox) {
dateBox.attr({
text: time.dateFormat(options.inputDateFormat, updatedTime)
});
}
}
};
/**
* Set the min and max value of a HTML input for the dates
*
* @private
* @function Highcharts.RangeSelector#setInputExtremes
*/
RangeSelector.prototype.setInputExtremes = function (name, min, max) {
var input = name === 'min' ? this.minInput : this.maxInput;
if (input) {
var format = this.inputTypeFormats[input.type];
var time = this.chart.time;
if (format) {
var newMin = time.dateFormat(format,
min);
if (input.min !== newMin) {
input.min = newMin;
}
var newMax = time.dateFormat(format,
max);
if (input.max !== newMax) {
input.max = newMax;
}
}
}
};
/**
* @private
* @function Highcharts.RangeSelector#showInput
* @param {string} name
*/
RangeSelector.prototype.showInput = function (name) {
var dateBox = name === 'min' ? this.minDateBox : this.maxDateBox, input = name === 'min' ? this.minInput : this.maxInput;
if (input && dateBox && this.inputGroup) {
var isTextInput = input.type === 'text',
_a = this.inputGroup,
_b = _a.translateX,
translateX = _b === void 0 ? 0 : _b,
_c = _a.translateY,
translateY = _c === void 0 ? 0 : _c,
_d = dateBox.x,
x = _d === void 0 ? 0 : _d,
_e = dateBox.width,
width = _e === void 0 ? 0 : _e,
_f = dateBox.height,
height = _f === void 0 ? 0 : _f,
inputBoxWidth = this.options.inputBoxWidth;
RangeSelector_css(input, {
width: isTextInput ?
((width + (inputBoxWidth ? -2 : 20)) + 'px') :
'auto',
height: (height - 2) + 'px',
border: '2px solid silver'
});
if (isTextInput && inputBoxWidth) {
RangeSelector_css(input, {
left: (translateX + x) + 'px',
top: translateY + 'px'
});
// Inputs of types date, time or datetime-local should be centered
// on top of the dateBox
}
else {
RangeSelector_css(input, {
left: Math.min(Math.round(x +
translateX -
(input.offsetWidth - width) / 2), this.chart.chartWidth - input.offsetWidth) + 'px',
top: (translateY - (input.offsetHeight - height) / 2) + 'px'
});
}
}
};
/**
* @private
* @function Highcharts.RangeSelector#hideInput
* @param {string} name
*/
RangeSelector.prototype.hideInput = function (name) {
var input = name === 'min' ? this.minInput : this.maxInput;
if (input) {
RangeSelector_css(input, {
top: '-9999em',
border: 0,
width: '1px',
height: '1px'
});
}
};
/**
* @private
* @function Highcharts.RangeSelector#defaultInputDateParser
*/
RangeSelector.prototype.defaultInputDateParser = function (inputDate, useUTC, time) {
return (time === null || time === void 0 ? void 0 : time.parse(inputDate)) || 0;
};
/**
* Draw either the 'from' or the 'to' HTML input box of the range selector
*
* @private
* @function Highcharts.RangeSelector#drawInput
*/
RangeSelector.prototype.drawInput = function (name) {
var _a = this,
chart = _a.chart,
div = _a.div,
inputGroup = _a.inputGroup;
var rangeSelector = this,
chartStyle = chart.renderer.style || {},
renderer = chart.renderer,
options = chart.options.rangeSelector,
lang = RangeSelector_defaultOptions.lang,
isMin = name === 'min';
/**
* @private
*/
function updateExtremes(name) {
var _a;
var maxInput = rangeSelector.maxInput,
minInput = rangeSelector.minInput,
chartAxis = chart.xAxis[0],
unionExtremes = ((_a = chart.scroller) === null || _a === void 0 ? void 0 : _a.getUnionExtremes()) || chartAxis,
dataMin = unionExtremes.dataMin,
dataMax = unionExtremes.dataMax,
currentExtreme = chart.xAxis[0].getExtremes()[name];
var value = rangeSelector.getInputValue(name);
if (RangeSelector_isNumber(value) && value !== currentExtreme) {
// Validate the extremes. If it goes beyond the data min or
// max, use the actual data extreme (#2438).
if (isMin && maxInput && RangeSelector_isNumber(dataMin)) {
if (value > Number(maxInput.getAttribute('data-hc-time'))) {
value = void 0;
}
else if (value < dataMin) {
value = dataMin;
}
}
else if (minInput && RangeSelector_isNumber(dataMax)) {
if (value < Number(minInput.getAttribute('data-hc-time'))) {
value = void 0;
}
else if (value > dataMax) {
value = dataMax;
}
}
// Set the extremes
if (typeof value !== 'undefined') { // @todo typeof undefined
chartAxis.setExtremes(isMin ? value : chartAxis.min, isMin ? chartAxis.max : value, void 0, void 0, { trigger: 'rangeSelectorInput' });
}
}
}
// Create the text label
var text = lang[isMin ? 'rangeSelectorFrom' : 'rangeSelectorTo'] || '';
var label = renderer
.label(text, 0)
.addClass('highcharts-range-label')
.attr({
padding: text ? 2 : 0,
height: text ? options.inputBoxHeight : 0
})
.add(inputGroup);
// Create an SVG label that shows updated date ranges and records click
// events that bring in the HTML input.
var dateBox = renderer
.label('', 0)
.addClass('highcharts-range-input')
.attr({
padding: 2,
width: options.inputBoxWidth,
height: options.inputBoxHeight,
'text-align': 'center'
})
.on('click',
function () {
// If it is already focused, the onfocus event doesn't fire
// (#3713)
rangeSelector.showInput(name);
rangeSelector[name + 'Input'].focus();
});
if (!chart.styledMode) {
dateBox.attr({
stroke: options.inputBoxBorderColor,
'stroke-width': 1
});
}
dateBox.add(inputGroup);
// Create the HTML input element. This is rendered as 1x1 pixel then set
// to the right size when focused.
var input = RangeSelector_createElement('input', {
name: name,
className: 'highcharts-range-selector'
},
void 0,
div);
// #14788: Setting input.type to an unsupported type throws in IE, so
// we need to use setAttribute instead
input.setAttribute('type', preferredInputType(options.inputDateFormat || '%e %b %Y'));
if (!chart.styledMode) {
// Styles
label.css(RangeSelector_merge(chartStyle, options.labelStyle));
dateBox.css(RangeSelector_merge({
color: "#333333" /* Palette.neutralColor80 */
}, chartStyle, options.inputStyle));
RangeSelector_css(input, RangeSelector_extend({
position: 'absolute',
border: 0,
boxShadow: '0 0 15px rgba(0,0,0,0.3)',
width: '1px', // Chrome needs a pixel to see it
height: '1px',
padding: 0,
textAlign: 'center',
fontSize: chartStyle.fontSize,
fontFamily: chartStyle.fontFamily,
top: '-9999em' // #4798
}, options.inputStyle));
}
// Blow up the input box
input.onfocus = function () {
rangeSelector.showInput(name);
};
// Hide away the input box
input.onblur = function () {
// Update extremes only when inputs are active
if (input === Core_Globals.doc.activeElement) { // Only when focused
// Update also when no `change` event is triggered, like when
// clicking inside the SVG (#4710)
updateExtremes(name);
}
// #10404 - move hide and blur outside focus
rangeSelector.hideInput(name);
rangeSelector.setInputValue(name);
input.blur(); // #4606
};
var keyDown = false;
// Handle changes in the input boxes
input.onchange = function () {
// Update extremes and blur input when clicking date input calendar
if (!keyDown) {
updateExtremes(name);
rangeSelector.hideInput(name);
input.blur();
}
};
input.onkeypress = function (event) {
// IE does not fire onchange on enter
if (event.keyCode === 13) {
updateExtremes(name);
}
};
input.onkeydown = function (event) {
keyDown = true;
// Arrow keys
if (event.key === 'ArrowUp' ||
event.key === 'ArrowDown' ||
event.key === 'Tab') {
updateExtremes(name);
}
};
input.onkeyup = function () {
keyDown = false;
};
return { dateBox: dateBox, input: input, label: label };
};
/**
* Get the position of the range selector buttons and inputs. This can be
* overridden from outside for custom positioning.
*
* @private
* @function Highcharts.RangeSelector#getPosition
*/
RangeSelector.prototype.getPosition = function () {
var chart = this.chart,
options = chart.options.rangeSelector,
top = options.verticalAlign === 'top' ?
chart.plotTop - chart.axisOffset[0] :
0; // Set offset only for verticalAlign top
return {
buttonTop: top + options.buttonPosition.y,
inputTop: top + options.inputPosition.y - 10
};
};
/**
* Get the extremes of YTD. Will choose dataMax if its value is lower than
* the current timestamp. Will choose dataMin if its value is higher than
* the timestamp for the start of current year.
*
* @private
* @function Highcharts.RangeSelector#getYTDExtremes
* @return {*}
* Returns min and max for the YTD
*/
RangeSelector.prototype.getYTDExtremes = function (dataMax, dataMin) {
var time = this.chart.time,
year = time.toParts(dataMax)[0],
startOfYear = time.makeTime(year, 0);
return {
max: dataMax,
min: Math.max(dataMin, startOfYear)
};
};
RangeSelector.prototype.createElements = function () {
var _a;
var chart = this.chart,
renderer = chart.renderer,
container = chart.container,
chartOptions = chart.options,
options = chartOptions.rangeSelector,
inputEnabled = options.inputEnabled,
inputsZIndex = RangeSelector_pick((_a = chartOptions.chart.style) === null || _a === void 0 ? void 0 : _a.zIndex, 0) + 1;
if (options.enabled === false) {
return;
}
this.group = renderer.g('range-selector-group')
.attr({
zIndex: 7
})
.add();
this.div = RangeSelector_createElement('div', void 0, {
position: 'relative',
height: 0,
zIndex: inputsZIndex
});
if (this.buttonOptions.length) {
this.renderButtons();
}
// First create a wrapper outside the container in order to make
// the inputs work and make export correct
if (container.parentNode) {
container.parentNode.insertBefore(this.div, container);
}
if (inputEnabled) {
this.createInputs();
}
};
/**
* Create the input elements and its group.
*
*/
RangeSelector.prototype.createInputs = function () {
this.inputGroup = this.chart.renderer.g('input-group').add(this.group);
var minElems = this.drawInput('min');
this.minDateBox = minElems.dateBox;
this.minLabel = minElems.label;
this.minInput = minElems.input;
var maxElems = this.drawInput('max');
this.maxDateBox = maxElems.dateBox;
this.maxLabel = maxElems.label;
this.maxInput = maxElems.input;
};
/**
* Render the range selector including the buttons and the inputs. The first
* time render is called, the elements are created and positioned. On
* subsequent calls, they are moved and updated.
*
* @private
* @function Highcharts.RangeSelector#render
* @param {number} [min]
* X axis minimum
* @param {number} [max]
* X axis maximum
*/
RangeSelector.prototype.render = function (min, max) {
var _a,
_b;
if (this.options.enabled === false) {
return;
}
var chart = this.chart,
chartOptions = chart.options,
options = chartOptions.rangeSelector,
// Place inputs above the container
inputEnabled = options.inputEnabled;
if (inputEnabled) {
if (!this.inputGroup) {
this.createInputs();
}
// Set or reset the input values
this.setInputValue('min', min);
this.setInputValue('max', max);
if (!this.chart.styledMode) {
(_a = this.maxLabel) === null || _a === void 0 ? void 0 : _a.css(options.labelStyle);
(_b = this.minLabel) === null || _b === void 0 ? void 0 : _b.css(options.labelStyle);
}
var unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || chart.xAxis[0] || {};
if (RangeSelector_defined(unionExtremes.dataMin) &&
RangeSelector_defined(unionExtremes.dataMax)) {
var minRange = chart.xAxis[0].minRange || 0;
this.setInputExtremes('min', unionExtremes.dataMin, Math.min(unionExtremes.dataMax, this.getInputValue('max')) - minRange);
this.setInputExtremes('max', Math.max(unionExtremes.dataMin, this.getInputValue('min')) + minRange, unionExtremes.dataMax);
}
// Reflow
if (this.inputGroup) {
var x_1 = 0;
[
this.minLabel,
this.minDateBox,
this.maxLabel,
this.maxDateBox
].forEach(function (label) {
if (label) {
var width = label.getBBox().width;
if (width) {
label.attr({ x: x_1 });
x_1 += width + options.inputSpacing;
}
}
});
}
}
else {
if (this.inputGroup) {
this.inputGroup.destroy();
delete this.inputGroup;
}
}
if (!this.chart.styledMode) {
if (this.zoomText) {
this.zoomText.css(options.labelStyle);
}
}
this.alignElements();
this.updateButtonStates();
};
/**
* Render the range buttons. This only runs the first time, later the
* positioning is laid out in alignElements.
*
* @private
* @function Highcharts.RangeSelector#renderButtons
*/
RangeSelector.prototype.renderButtons = function () {
var _this = this;
var _a,
_b;
var _c;
var _d = this,
chart = _d.chart,
options = _d.options;
var lang = RangeSelector_defaultOptions.lang;
var renderer = chart.renderer;
var buttonTheme = RangeSelector_merge(options.buttonTheme);
var states = buttonTheme && buttonTheme.states;
// Prevent the button from resetting the width when the button state
// changes since we need more control over the width when collapsing
// the buttons
delete buttonTheme.width;
delete buttonTheme.states;
this.buttonGroup = renderer.g('range-selector-buttons').add(this.group);
var dropdown = this.dropdown = RangeSelector_createElement('select',
void 0, {
position: 'absolute',
padding: 0,
border: 0,
cursor: 'pointer',
opacity: 0.0001
},
this.div);
// Create a label for dropdown select element
var userButtonTheme = (_a = chart.userOptions.rangeSelector) === null || _a === void 0 ? void 0 : _a.buttonTheme;
this.dropdownLabel = renderer.button('', 0, 0, function () { }, RangeSelector_merge(buttonTheme, {
'stroke-width': RangeSelector_pick(buttonTheme['stroke-width'], 0),
width: 'auto',
paddingLeft: RangeSelector_pick(options.buttonTheme.paddingLeft, userButtonTheme === null || userButtonTheme === void 0 ? void 0 : userButtonTheme.padding, 8),
paddingRight: RangeSelector_pick(options.buttonTheme.paddingRight, userButtonTheme === null || userButtonTheme === void 0 ? void 0 : userButtonTheme.padding, 8)
}), states && states.hover, states && states.select, states && states.disabled)
.hide()
.add(this.group);
// Prevent page zoom on iPhone
RangeSelector_addEvent(dropdown, 'touchstart', function () {
dropdown.style.fontSize = '16px';
});
// Forward events from select to button
var mouseOver = Core_Globals.isMS ? 'mouseover' : 'mouseenter', mouseOut = Core_Globals.isMS ? 'mouseout' : 'mouseleave';
RangeSelector_addEvent(dropdown, mouseOver, function () {
RangeSelector_fireEvent(_this.dropdownLabel.element, mouseOver);
});
RangeSelector_addEvent(dropdown, mouseOut, function () {
RangeSelector_fireEvent(_this.dropdownLabel.element, mouseOut);
});
RangeSelector_addEvent(dropdown, 'change', function () {
var button = _this.buttons[dropdown.selectedIndex - 1];
RangeSelector_fireEvent(button.element, 'click');
});
this.zoomText = renderer
.label(lang.rangeSelectorZoom || '', 0)
.attr({
padding: options.buttonTheme.padding,
height: options.buttonTheme.height,
paddingLeft: 0,
paddingRight: 0
})
.add(this.buttonGroup);
if (!this.chart.styledMode) {
this.zoomText.css(options.labelStyle);
(_b = (_c = options.buttonTheme)['stroke-width']) !== null && _b !== void 0 ? _b : (_c['stroke-width'] = 0);
}
RangeSelector_createElement('option', {
textContent: this.zoomText.textStr,
disabled: true
}, void 0, dropdown);
this.createButtons();
};
RangeSelector.prototype.createButtons = function () {
var _this = this;
var options = this.options;
var buttonTheme = RangeSelector_merge(options.buttonTheme);
var states = buttonTheme && buttonTheme.states;
// Prevent the button from resetting the width when the button state
// changes since we need more control over the width when collapsing
// the buttons
var width = buttonTheme.width || 28;
delete buttonTheme.width;
delete buttonTheme.states;
this.buttonOptions.forEach(function (rangeOptions, i) {
_this.createButton(rangeOptions, i, width, states);
});
};
RangeSelector.prototype.createButton = function (rangeOptions, i, width, states) {
var _this = this;
var _a = this,
dropdown = _a.dropdown,
buttons = _a.buttons,
chart = _a.chart,
options = _a.options;
var renderer = chart.renderer;
var buttonTheme = RangeSelector_merge(options.buttonTheme);
dropdown === null || dropdown === void 0 ? void 0 : dropdown.add(RangeSelector_createElement('option', {
textContent: rangeOptions.title || rangeOptions.text
}), i + 2);
buttons[i] = renderer
.button(rangeOptions.text, 0, 0, function (e) {
// Extract events from button object and call
var buttonEvents = (rangeOptions.events && rangeOptions.events.click);
var callDefaultEvent;
if (buttonEvents) {
callDefaultEvent =
buttonEvents.call(rangeOptions, e);
}
if (callDefaultEvent !== false) {
_this.clickButton(i);
}
_this.isActive = true;
}, buttonTheme, states && states.hover, states && states.select, states && states.disabled)
.attr({
'text-align': 'center',
width: width
})
.add(this.buttonGroup);
if (rangeOptions.title) {
buttons[i].attr('title', rangeOptions.title);
}
};
/**
* Align the elements horizontally and vertically.
*
* @private
* @function Highcharts.RangeSelector#alignElements
*/
RangeSelector.prototype.alignElements = function () {
var _this = this;
var _a;
var _b = this,
buttonGroup = _b.buttonGroup,
buttons = _b.buttons,
chart = _b.chart,
group = _b.group,
inputGroup = _b.inputGroup,
options = _b.options,
zoomText = _b.zoomText;
var chartOptions = chart.options;
var navButtonOptions = (chartOptions.exporting &&
chartOptions.exporting.enabled !== false &&
chartOptions.navigation &&
chartOptions.navigation.buttonOptions);
var buttonPosition = options.buttonPosition,
inputPosition = options.inputPosition,
verticalAlign = options.verticalAlign;
// Get the X offset required to avoid overlapping with the exporting
// button. This is used both by the buttonGroup and the inputGroup.
var getXOffsetForExportButton = function (group,
position,
rightAligned) {
if (navButtonOptions &&
_this.titleCollision(chart) &&
verticalAlign === 'top' &&
rightAligned && ((position.y -
group.getBBox().height - 12) <
((navButtonOptions.y || 0) +
(navButtonOptions.height || 0) +
chart.spacing[0]))) {
return -40;
}
return 0;
};
var plotLeft = chart.plotLeft;
if (group && buttonPosition && inputPosition) {
var translateX = buttonPosition.x - chart.spacing[3];
if (buttonGroup) {
this.positionButtons();
if (!this.initialButtonGroupWidth) {
var width_1 = 0;
if (zoomText) {
width_1 += zoomText.getBBox().width + 5;
}
buttons.forEach(function (button, i) {
width_1 += button.width || 0;
if (i !== buttons.length - 1) {
width_1 += options.buttonSpacing;
}
});
this.initialButtonGroupWidth = width_1;
}
plotLeft -= chart.spacing[3];
// Detect collision between button group and exporting
var xOffsetForExportButton_1 = getXOffsetForExportButton(buttonGroup,
buttonPosition,
buttonPosition.align === 'right' ||
inputPosition.align === 'right');
this.alignButtonGroup(xOffsetForExportButton_1);
if ((_a = this.buttonGroup) === null || _a === void 0 ? void 0 : _a.translateY) {
this.dropdownLabel
.attr({ y: this.buttonGroup.translateY });
}
// Skip animation
group.placed = buttonGroup.placed = chart.hasLoaded;
}
var xOffsetForExportButton = 0;
if (options.inputEnabled && inputGroup) {
// Detect collision between the input group and exporting button
xOffsetForExportButton = getXOffsetForExportButton(inputGroup, inputPosition, buttonPosition.align === 'right' ||
inputPosition.align === 'right');
if (inputPosition.align === 'left') {
translateX = plotLeft;
}
else if (inputPosition.align === 'right') {
translateX = -Math.max(chart.axisOffset[1], -xOffsetForExportButton);
}
// Update the alignment to the updated spacing box
inputGroup.align({
y: inputPosition.y,
width: inputGroup.getBBox().width,
align: inputPosition.align,
// Fix wrong getBBox() value on right align
x: inputPosition.x + translateX - 2
}, true, chart.spacingBox);
// Skip animation
inputGroup.placed = chart.hasLoaded;
}
this.handleCollision(xOffsetForExportButton);
// Vertical align
group.align({
verticalAlign: verticalAlign
}, true, chart.spacingBox);
var alignTranslateY = group.alignAttr.translateY;
// Set position
var groupHeight = group.getBBox().height + 20; // # 20 padding
var translateY = 0;
// Calculate bottom position
if (verticalAlign === 'bottom') {
var legendOptions = chart.legend && chart.legend.options;
var legendHeight = (legendOptions &&
legendOptions.verticalAlign === 'bottom' &&
legendOptions.enabled &&
!legendOptions.floating ?
(chart.legend.legendHeight +
RangeSelector_pick(legendOptions.margin, 10)) :
0);
groupHeight = groupHeight + legendHeight - 20;
translateY = (alignTranslateY -
groupHeight -
(options.floating ? 0 : options.y) -
(chart.titleOffset ? chart.titleOffset[2] : 0) -
10 // 10 spacing
);
}
if (verticalAlign === 'top') {
if (options.floating) {
translateY = 0;
}
if (chart.titleOffset && chart.titleOffset[0]) {
translateY = chart.titleOffset[0];
}
translateY += ((chart.margin[0] - chart.spacing[0]) || 0);
}
else if (verticalAlign === 'middle') {
if (inputPosition.y === buttonPosition.y) {
translateY = alignTranslateY;
}
else if (inputPosition.y || buttonPosition.y) {
if (inputPosition.y < 0 ||
buttonPosition.y < 0) {
translateY -= Math.min(inputPosition.y, buttonPosition.y);
}
else {
translateY = alignTranslateY - groupHeight;
}
}
}
group.translate(options.x, options.y + Math.floor(translateY));
// Translate HTML inputs
var _c = this,
minInput = _c.minInput,
maxInput = _c.maxInput,
dropdown = _c.dropdown;
if (options.inputEnabled && minInput && maxInput) {
minInput.style.marginTop = group.translateY + 'px';
maxInput.style.marginTop = group.translateY + 'px';
}
if (dropdown) {
dropdown.style.marginTop = group.translateY + 'px';
}
}
};
/**
* @private
*/
RangeSelector.prototype.redrawElements = function () {
var _a,
_b,
_c,
_d,
_e,
_f,
_g;
var chart = this.chart,
_h = this.options,
inputBoxHeight = _h.inputBoxHeight,
inputBoxBorderColor = _h.inputBoxBorderColor;
(_a = this.maxDateBox) === null || _a === void 0 ? void 0 : _a.attr({
height: inputBoxHeight
});
(_b = this.minDateBox) === null || _b === void 0 ? void 0 : _b.attr({
height: inputBoxHeight
});
if (!chart.styledMode) {
(_c = this.maxDateBox) === null || _c === void 0 ? void 0 : _c.attr({
stroke: inputBoxBorderColor
});
(_d = this.minDateBox) === null || _d === void 0 ? void 0 : _d.attr({
stroke: inputBoxBorderColor
});
}
if (this.isDirty) {
this.isDirty = false;
// Reset this prop to force redrawing collapse of buttons
this.isCollapsed = void 0;
var newButtonsOptions = (_e = this.options.buttons) !== null && _e !== void 0 ? _e : [];
var btnLength = Math.min(newButtonsOptions.length,
this.buttonOptions.length);
var _j = this,
dropdown = _j.dropdown,
options = _j.options;
var buttonTheme = RangeSelector_merge(options.buttonTheme);
var states = buttonTheme && buttonTheme.states;
// Prevent the button from resetting the width when the button state
// changes since we need more control over the width when collapsing
// the buttons
var width = buttonTheme.width || 28;
// Destroy additional buttons
if (newButtonsOptions.length < this.buttonOptions.length) {
for (var i = this.buttonOptions.length - 1; i >= newButtonsOptions.length; i--) {
var btn = this.buttons.pop();
btn === null || btn === void 0 ? void 0 : btn.destroy();
(_f = this.dropdown) === null || _f === void 0 ? void 0 : _f.options.remove(i + 1);
}
}
// Update current buttons
for (var i = btnLength - 1; i >= 0; i--) {
var diff = RangeSelector_diffObjects(newButtonsOptions[i],
this.buttonOptions[i]);
if (Object.keys(diff).length !== 0) {
var rangeOptions = newButtonsOptions[i];
this.buttons[i].destroy();
dropdown === null || dropdown === void 0 ? void 0 : dropdown.options.remove(i + 1);
this.createButton(rangeOptions, i, width, states);
this.computeButtonRange(rangeOptions);
}
}
// Create missing buttons
if (newButtonsOptions.length > this.buttonOptions.length) {
for (var i = this.buttonOptions.length; i < newButtonsOptions.length; i++) {
this.createButton(newButtonsOptions[i], i, width, states);
this.computeButtonRange(newButtonsOptions[i]);
}
}
this.buttonOptions = (_g = this.options.buttons) !== null && _g !== void 0 ? _g : [];
if (RangeSelector_defined(this.options.selected) && this.buttons.length) {
this.clickButton(this.options.selected, false);
}
}
};
/**
* Align the button group horizontally and vertically.
*
* @private
* @function Highcharts.RangeSelector#alignButtonGroup
* @param {number} xOffsetForExportButton
* @param {number} [width]
*/
RangeSelector.prototype.alignButtonGroup = function (xOffsetForExportButton, width) {
var _a = this,
chart = _a.chart,
options = _a.options,
buttonGroup = _a.buttonGroup,
dropdown = _a.dropdown,
dropdownLabel = _a.dropdownLabel;
var buttonPosition = options.buttonPosition;
var plotLeft = chart.plotLeft - chart.spacing[3];
var translateX = buttonPosition.x - chart.spacing[3];
var dropdownTranslateX = chart.plotLeft;
if (buttonPosition.align === 'right') {
translateX += xOffsetForExportButton - plotLeft; // #13014
if (this.hasVisibleDropdown) {
dropdownTranslateX = chart.chartWidth +
xOffsetForExportButton -
this.maxButtonWidth() - 20;
}
}
else if (buttonPosition.align === 'center') {
translateX -= plotLeft / 2;
if (this.hasVisibleDropdown) {
dropdownTranslateX = chart.chartWidth / 2 -
this.maxButtonWidth();
}
}
if (dropdown) {
RangeSelector_css(dropdown, {
left: dropdownTranslateX + 'px',
top: (buttonGroup === null || buttonGroup === void 0 ? void 0 : buttonGroup.translateY) + 'px'
});
}
dropdownLabel === null || dropdownLabel === void 0 ? void 0 : dropdownLabel.attr({
x: dropdownTranslateX
});
if (buttonGroup) {
// Align button group
buttonGroup.align({
y: buttonPosition.y,
width: RangeSelector_pick(width, this.initialButtonGroupWidth),
align: buttonPosition.align,
x: translateX
}, true, chart.spacingBox);
}
};
/**
* @private
* @function Highcharts.RangeSelector#positionButtons
*/
RangeSelector.prototype.positionButtons = function () {
var _a = this,
buttons = _a.buttons,
chart = _a.chart,
options = _a.options,
zoomText = _a.zoomText;
var verb = chart.hasLoaded ? 'animate' : 'attr';
var buttonPosition = options.buttonPosition;
var plotLeft = chart.plotLeft;
var buttonLeft = plotLeft;
if (zoomText && zoomText.visibility !== 'hidden') {
// #8769, allow dynamically updating margins
zoomText[verb]({
x: RangeSelector_pick(plotLeft + buttonPosition.x, plotLeft)
});
// Button start position
buttonLeft += buttonPosition.x +
zoomText.getBBox().width + 5;
}
for (var i = 0, iEnd = this.buttonOptions.length; i < iEnd; ++i) {
if (buttons[i].visibility !== 'hidden') {
buttons[i][verb]({ x: buttonLeft });
// Increase the button position for the next button
buttonLeft += (buttons[i].width || 0) + options.buttonSpacing;
}
else {
buttons[i][verb]({ x: plotLeft });
}
}
};
/**
* Handle collision between the button group and the input group
*
* @private
* @function Highcharts.RangeSelector#handleCollision
*
* @param {number} xOffsetForExportButton
* The X offset of the group required to make room for the
* exporting button
*/
RangeSelector.prototype.handleCollision = function (xOffsetForExportButton) {
var _a = this,
chart = _a.chart,
buttonGroup = _a.buttonGroup,
inputGroup = _a.inputGroup,
initialButtonGroupWidth = _a.initialButtonGroupWidth;
var _b = this.options,
buttonPosition = _b.buttonPosition,
dropdown = _b.dropdown,
inputPosition = _b.inputPosition;
var moveInputsDown = function () {
if (inputGroup && buttonGroup) {
inputGroup.attr({
translateX: inputGroup.alignAttr.translateX + (chart.axisOffset[1] >= -xOffsetForExportButton ?
0 :
-xOffsetForExportButton),
translateY: inputGroup.alignAttr.translateY +
buttonGroup.getBBox().height + 10
});
}
};
// Detect collision
if (inputGroup && buttonGroup) {
if (inputPosition.align === buttonPosition.align) {
moveInputsDown();
if (initialButtonGroupWidth >
chart.plotWidth + xOffsetForExportButton - 20) {
this.collapseButtons();
}
else {
this.expandButtons();
}
}
else if (initialButtonGroupWidth -
xOffsetForExportButton +
inputGroup.getBBox().width >
chart.plotWidth) {
if (dropdown === 'responsive') {
this.collapseButtons();
}
else {
moveInputsDown();
}
}
else {
this.expandButtons();
}
}
else if (buttonGroup && dropdown === 'responsive') {
if (initialButtonGroupWidth > chart.plotWidth) {
this.collapseButtons();
}
else {
this.expandButtons();
}
}
// Forced states
if (buttonGroup) {
if (dropdown === 'always') {
this.collapseButtons();
}
if (dropdown === 'never') {
this.expandButtons();
}
}
this.alignButtonGroup(xOffsetForExportButton);
};
/**
* Collapse the buttons and show the select element.
*
* @private
* @function Highcharts.RangeSelector#collapseButtons
* @param {number} xOffsetForExportButton
*/
RangeSelector.prototype.collapseButtons = function () {
var _a = this,
buttons = _a.buttons,
zoomText = _a.zoomText;
if (this.isCollapsed === true) {
return;
}
this.isCollapsed = true;
zoomText.hide();
buttons.forEach(function (button) { return void button.hide(); });
this.showDropdown();
};
/**
* Show all the buttons and hide the select element.
*
* @private
* @function Highcharts.RangeSelector#expandButtons
*/
RangeSelector.prototype.expandButtons = function () {
var _a = this,
buttons = _a.buttons,
zoomText = _a.zoomText;
if (this.isCollapsed === false) {
return;
}
this.isCollapsed = false;
this.hideDropdown();
zoomText.show();
buttons.forEach(function (button) { return void button.show(); });
this.positionButtons();
};
/**
* Position the select element on top of the button.
*
* @private
* @function Highcharts.RangeSelector#showDropdown
*/
RangeSelector.prototype.showDropdown = function () {
var _a = this,
buttonGroup = _a.buttonGroup,
dropdownLabel = _a.dropdownLabel,
dropdown = _a.dropdown;
if (buttonGroup && dropdown) {
dropdownLabel.show();
RangeSelector_css(dropdown, { visibility: 'inherit' });
this.hasVisibleDropdown = true;
}
};
/**
* @private
* @function Highcharts.RangeSelector#hideDropdown
*/
RangeSelector.prototype.hideDropdown = function () {
var dropdown = this.dropdown;
if (dropdown) {
this.dropdownLabel.hide();
RangeSelector_css(dropdown, {
visibility: 'hidden',
width: '1px',
height: '1px'
});
this.hasVisibleDropdown = false;
}
};
/**
* Extracts height of range selector
*
* @private
* @function Highcharts.RangeSelector#getHeight
* @return {number}
* Returns rangeSelector height
*/
RangeSelector.prototype.getHeight = function () {
var rangeSelector = this,
options = rangeSelector.options,
rangeSelectorGroup = rangeSelector.group,
inputPosition = options.inputPosition,
buttonPosition = options.buttonPosition,
yPosition = options.y,
buttonPositionY = buttonPosition.y,
inputPositionY = inputPosition.y;
var rangeSelectorHeight = 0;
if (options.height) {
return options.height;
}
// Align the elements before we read the height in case we're switching
// between wrapped and non-wrapped layout
this.alignElements();
rangeSelectorHeight = rangeSelectorGroup ?
// 13px to keep back compatibility
(rangeSelectorGroup.getBBox(true).height) + 13 +
yPosition :
0;
var minPosition = Math.min(inputPositionY,
buttonPositionY);
if ((inputPositionY < 0 && buttonPositionY < 0) ||
(inputPositionY > 0 && buttonPositionY > 0)) {
rangeSelectorHeight += Math.abs(minPosition);
}
return rangeSelectorHeight;
};
/**
* Detect collision with title or subtitle
*
* @private
* @function Highcharts.RangeSelector#titleCollision
* @return {boolean}
* Returns collision status
*/
RangeSelector.prototype.titleCollision = function (chart) {
return !(chart.options.title.text ||
chart.options.subtitle.text);
};
/**
* Update the range selector with new options
*
* @private
* @function Highcharts.RangeSelector#update
* @param {Highcharts.RangeSelectorOptions} options
*/
RangeSelector.prototype.update = function (options, redraw) {
if (redraw === void 0) { redraw = true; }
var chart = this.chart;
RangeSelector_merge(true, this.options, options);
if (this.options.selected &&
this.options.selected >= this.options.buttons.length) {
this.options.selected = void 0;
chart.options.rangeSelector.selected = void 0;
}
if (RangeSelector_defined(options.enabled)) {
this.destroy();
return this.init(chart);
}
this.isDirty = !!options.buttons;
if (redraw) {
this.render();
}
};
/**
* Destroys allocated elements.
*
* @private
* @function Highcharts.RangeSelector#destroy
*/
RangeSelector.prototype.destroy = function () {
var rSelector = this,
minInput = rSelector.minInput,
maxInput = rSelector.maxInput;
if (rSelector.eventsToUnbind) {
rSelector.eventsToUnbind.forEach(function (unbind) { return unbind(); });
rSelector.eventsToUnbind = void 0;
}
// Destroy elements in collections
RangeSelector_destroyObjectProperties(rSelector.buttons);
// Clear input element events
if (minInput) {
minInput.onfocus = minInput.onblur = minInput.onchange = null;
}
if (maxInput) {
maxInput.onfocus = maxInput.onblur = maxInput.onchange = null;
}
// Destroy HTML and SVG elements
RangeSelector_objectEach(rSelector, function (val, key) {
if (val && key !== 'chart') {
if (val instanceof SVG_SVGElement) {
// SVGElement
val.destroy();
}
else if (val instanceof window.HTMLElement) {
// HTML element
RangeSelector_discardElement(val);
}
delete rSelector[key];
}
if (val !== RangeSelector.prototype[key]) {
rSelector[key] = null;
}
}, this);
this.buttons = [];
};
return RangeSelector;
}());
RangeSelector_extend(RangeSelector.prototype, {
/**
* The date formats to use when setting min, max and value on date inputs.
* @private
*/
inputTypeFormats: {
'datetime-local': '%Y-%m-%dT%H:%M:%S',
'date': '%Y-%m-%d',
'time': '%H:%M:%S'
}
});
/* *
*
* Default Export
*
* */
/* harmony default export */ var RangeSelector_RangeSelector = (RangeSelector);
/* *
*
* API Options
*
* */
/**
* Define the time span for the button
*
* @typedef {"all"|"day"|"hour"|"millisecond"|"minute"|"month"|"second"|"week"|"year"|"ytd"} Highcharts.RangeSelectorButtonTypeValue
*/
/**
* Callback function to react on button clicks.
*
* @callback Highcharts.RangeSelectorClickCallbackFunction
*
* @param {global.Event} e
* Event arguments.
*
* @param {boolean|undefined}
* Return false to cancel the default button event.
*/
/**
* Callback function to parse values entered in the input boxes and return a
* valid JavaScript time as milliseconds since 1970.
*
* @callback Highcharts.RangeSelectorParseCallbackFunction
*
* @param {string} value
* Input value to parse.
*
* @return {number}
* Parsed JavaScript time value.
*/
(''); // Keeps doclets above in JS file
;// ./code/es5/es-modules/Core/Chart/StockChart.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var StockChart_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b,
p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var StockChart_format = Core_Templating.format;
var StockChart_getOptions = Defaults.getOptions;
var StockChart_setFixedRange = Utilities_StockUtilities.setFixedRange;
var StockChart_addEvent = Core_Utilities.addEvent, StockChart_clamp = Core_Utilities.clamp, StockChart_crisp = Core_Utilities.crisp, StockChart_defined = Core_Utilities.defined, StockChart_extend = Core_Utilities.extend, StockChart_find = Core_Utilities.find, StockChart_isNumber = Core_Utilities.isNumber, StockChart_isString = Core_Utilities.isString, StockChart_merge = Core_Utilities.merge, StockChart_pick = Core_Utilities.pick, StockChart_splat = Core_Utilities.splat;
/* *
*
* Functions
*
* */
/**
* Get stock-specific default axis options.
*
* @private
* @function getDefaultAxisOptions
*/
function getDefaultAxisOptions(coll, options, defaultOptions) {
var _a,
_b,
_c,
_d;
if (coll === 'xAxis') {
return {
minPadding: 0,
maxPadding: 0,
overscroll: 0,
ordinal: true
};
}
if (coll === 'yAxis') {
return {
labels: {
y: -2
},
opposite: (_b = (_a = defaultOptions.opposite) !== null && _a !== void 0 ? _a : options.opposite) !== null && _b !== void 0 ? _b : true,
showLastLabel: !!(
// #6104, show last label by default for category axes
options.categories ||
options.type === 'category'),
title: {
text: ((_c = defaultOptions.title) === null || _c === void 0 ? void 0 : _c.text) !== 'Values' ?
(_d = defaultOptions.title) === null || _d === void 0 ? void 0 : _d.text :
null
}
};
}
return {};
}
/**
* Get stock-specific forced axis options.
*
* @private
* @function getForcedAxisOptions
*/
function getForcedAxisOptions(type, chartOptions) {
if (type === 'xAxis') {
// Always disable startOnTick:true on the main axis when the navigator
// is enabled (#1090)
var navigatorEnabled = StockChart_pick(chartOptions.navigator && chartOptions.navigator.enabled,
Navigator_NavigatorDefaults.enabled,
true);
var axisOptions = {
type: 'datetime',
categories: void 0
};
if (navigatorEnabled) {
axisOptions.startOnTick = false;
axisOptions.endOnTick = false;
}
return axisOptions;
}
return {};
}
/* *
*
* Class
*
* */
/**
* Stock-optimized chart. Use {@link Highcharts.Chart|Chart} for common charts.
*
* @requires modules/stock
*
* @class
* @name Highcharts.StockChart
* @extends Highcharts.Chart
*/
var StockChart = /** @class */ (function (_super) {
StockChart_extends(StockChart, _super);
function StockChart() {
return _super !== null && _super.apply(this, arguments) || this;
}
/* *
*
* Functions
*
* */
/**
* Initializes the chart. The constructor's arguments are passed on
* directly.
*
* @function Highcharts.StockChart#init
*
* @param {Highcharts.Options} userOptions
* Custom options.
*
* @param {Function} [callback]
* Function to run when the chart has loaded and all external
* images are loaded.
*
*
* @emits Highcharts.StockChart#event:init
* @emits Highcharts.StockChart#event:afterInit
*/
StockChart.prototype.init = function (userOptions, callback) {
var defaultOptions = StockChart_getOptions(),
xAxisOptions = userOptions.xAxis,
yAxisOptions = userOptions.yAxis,
// Always disable startOnTick:true on the main axis when the
// navigator is enabled (#1090)
navigatorEnabled = StockChart_pick(userOptions.navigator && userOptions.navigator.enabled,
Navigator_NavigatorDefaults.enabled,
true);
// Avoid doing these twice
userOptions.xAxis = userOptions.yAxis = void 0;
var options = StockChart_merge({
chart: {
panning: {
enabled: true,
type: 'x'
},
zooming: {
pinchType: 'x',
mouseWheel: {
type: 'x'
}
}
},
navigator: {
enabled: navigatorEnabled
},
scrollbar: {
// #4988 - check if setOptions was called
enabled: StockChart_pick(Scrollbar_ScrollbarDefaults.enabled,
true)
},
rangeSelector: {
// #4988 - check if setOptions was called
enabled: StockChart_pick(RangeSelector_RangeSelectorDefaults.rangeSelector.enabled,
true)
},
title: {
text: null
},
tooltip: {
split: StockChart_pick(defaultOptions.tooltip && defaultOptions.tooltip.split,
true),
crosshairs: true
},
legend: {
enabled: false
}
},
userOptions, // User's options
{
isStock: true // Internal flag
});
userOptions.xAxis = xAxisOptions;
userOptions.yAxis = yAxisOptions;
// Apply X axis options to both single and multi y axes
options.xAxis = StockChart_splat(userOptions.xAxis || {}).map(function (xAxisOptions) { return StockChart_merge(getDefaultAxisOptions('xAxis', xAxisOptions, defaultOptions.xAxis),
// #7690
xAxisOptions, // User options
getForcedAxisOptions('xAxis', userOptions)); });
// Apply Y axis options to both single and multi y axes
options.yAxis = StockChart_splat(userOptions.yAxis || {}).map(function (yAxisOptions) { return StockChart_merge(getDefaultAxisOptions('yAxis', yAxisOptions, defaultOptions.yAxis),
// #7690
yAxisOptions // User options
); });
_super.prototype.init.call(this, options, callback);
};
/**
* Factory for creating different axis types.
* Extended to add stock defaults.
*
* @private
* @function Highcharts.StockChart#createAxis
* @param {string} coll
* An axis type.
* @param {Chart.CreateAxisOptionsObject} options
* The axis creation options.
*/
StockChart.prototype.createAxis = function (coll, options) {
options.axis = StockChart_merge(getDefaultAxisOptions(coll, options.axis, StockChart_getOptions()[coll]), options.axis, getForcedAxisOptions(coll, this.userOptions));
return _super.prototype.createAxis.call(this, coll, options);
};
return StockChart;
}(Chart_Chart));
StockChart_addEvent(Chart_Chart, 'update', function (e) {
var chart = this,
options = e.options;
// Use case: enabling scrollbar from a disabled state.
// Scrollbar needs to be initialized from a controller, Navigator in this
// case (#6615)
if ('scrollbar' in options && chart.navigator) {
StockChart_merge(true, chart.options.scrollbar, options.scrollbar);
chart.navigator.update({ enabled: !!chart.navigator.navigatorEnabled });
delete options.scrollbar;
}
});
/* *
*
* Composition
*
* */
(function (StockChart) {
/* *
*
* Functions
*
* */
/** @private */
function compose(ChartClass, AxisClass, SeriesClass, SVGRendererClass) {
var seriesProto = SeriesClass.prototype;
if (!seriesProto.forceCropping) {
StockChart_addEvent(AxisClass, 'afterDrawCrosshair', onAxisAfterDrawCrosshair);
StockChart_addEvent(AxisClass, 'afterHideCrosshair', onAxisAfterHideCrosshair);
StockChart_addEvent(AxisClass, 'autoLabelAlign', onAxisAutoLabelAlign);
StockChart_addEvent(AxisClass, 'destroy', onAxisDestroy);
StockChart_addEvent(AxisClass, 'getPlotLinePath', onAxisGetPlotLinePath);
ChartClass.prototype.setFixedRange = StockChart_setFixedRange;
seriesProto.forceCropping = seriesForceCropping;
StockChart_addEvent(SeriesClass, 'setOptions', onSeriesSetOptions);
SVGRendererClass.prototype.crispPolyLine = svgRendererCrispPolyLine;
}
}
StockChart.compose = compose;
/**
* Extend crosshairs to also draw the label.
* @private
*/
function onAxisAfterDrawCrosshair(event) {
var _a,
_b,
_c;
var axis = this;
// Check if the label has to be drawn
if (!(((_b = (_a = axis.crosshair) === null || _a === void 0 ? void 0 : _a.label) === null || _b === void 0 ? void 0 : _b.enabled) &&
axis.cross &&
StockChart_isNumber(axis.min) &&
StockChart_isNumber(axis.max))) {
return;
}
var chart = axis.chart,
log = axis.logarithmic,
options = axis.crosshair.label, // The label's options
horiz = axis.horiz, // Axis orientation
opposite = axis.opposite, // Axis position
left = axis.left, // Left position
top = axis.top, // Top position
width = axis.width,
tickInside = axis.options.tickPosition === 'inside',
snap = axis.crosshair.snap !== false,
e = event.e || ((_c = axis.cross) === null || _c === void 0 ? void 0 : _c.e),
point = event.point;
var crossLabel = axis.crossLabel, // The svgElement
posx,
posy,
formatOption = options.format,
formatFormat = '',
limit,
offset = 0,
// Use last available event (#5287)
min = axis.min,
max = axis.max;
if (log) {
min = log.lin2log(axis.min);
max = log.lin2log(axis.max);
}
var align = (horiz ? 'center' : opposite ?
(axis.labelAlign === 'right' ? 'right' : 'left') :
(axis.labelAlign === 'left' ? 'left' : 'center'));
// If the label does not exist yet, create it.
if (!crossLabel) {
crossLabel = axis.crossLabel = chart.renderer
.label('', 0, void 0, options.shape || 'callout')
.addClass('highcharts-crosshair-label highcharts-color-' + (point && point.series ?
point.series.colorIndex :
axis.series[0] && this.series[0].colorIndex))
.attr({
align: options.align || align,
padding: StockChart_pick(options.padding, 8),
r: StockChart_pick(options.borderRadius, 3),
zIndex: 2
})
.add(axis.labelGroup);
// Presentational
if (!chart.styledMode) {
crossLabel
.attr({
fill: options.backgroundColor ||
( // #14888
point && point.series &&
point.series.color) ||
"#666666" /* Palette.neutralColor60 */,
stroke: options.borderColor || '',
'stroke-width': options.borderWidth || 0
})
.css(StockChart_extend({
color: "#ffffff" /* Palette.backgroundColor */,
fontWeight: 'normal',
fontSize: '0.7em',
textAlign: 'center'
}, options.style || {}));
}
}
if (horiz) {
posx = snap ? (point.plotX || 0) + left : e.chartX;
posy = top + (opposite ? 0 : axis.height);
}
else {
posx = left + axis.offset + (opposite ? width : 0);
posy = snap ? (point.plotY || 0) + top : e.chartY;
}
if (!formatOption && !options.formatter) {
if (axis.dateTime) {
formatFormat = '%b %d, %Y';
}
formatOption =
'{value' + (formatFormat ? ':' + formatFormat : '') + '}';
}
// Show the label
var value = snap ?
(axis.isXAxis ? point.x : point.y) :
axis.toValue(horiz ? e.chartX : e.chartY);
// Crosshair should be rendered within Axis range (#7219) and the point
// of currentPriceIndicator should be inside the plot area (#14879).
var isInside = point && point.series ?
point.series.isPointInside(point) :
(StockChart_isNumber(value) && value > min && value < max);
var text = '';
if (formatOption) {
text = StockChart_format(formatOption, { value: value }, chart);
}
else if (options.formatter && StockChart_isNumber(value)) {
text = options.formatter.call(axis, value);
}
crossLabel.attr({
text: text,
x: posx,
y: posy,
visibility: isInside ? 'inherit' : 'hidden'
});
var crossBox = crossLabel.getBBox();
// Now it is placed we can correct its position
if (StockChart_isNumber(crossLabel.x) && !horiz && !opposite) {
posx = crossLabel.x - (crossBox.width / 2);
}
if (StockChart_isNumber(crossLabel.y)) {
if (horiz) {
if ((tickInside && !opposite) || (!tickInside && opposite)) {
posy = crossLabel.y - crossBox.height;
}
}
else {
posy = crossLabel.y - (crossBox.height / 2);
}
}
// Check the edges
if (horiz) {
limit = {
left: left,
right: left + axis.width
};
}
else {
limit = {
left: axis.labelAlign === 'left' ? left : 0,
right: axis.labelAlign === 'right' ?
left + axis.width :
chart.chartWidth
};
}
var translateX = crossLabel.translateX || 0;
// Left edge
if (translateX < limit.left) {
offset = limit.left - translateX;
}
// Right edge
if (translateX + crossBox.width >= limit.right) {
offset = -(translateX + crossBox.width - limit.right);
}
// Show the crosslabel
crossLabel.attr({
x: Math.max(0, posx + offset),
y: Math.max(0, posy),
// First set x and y, then anchorX and anchorY, when box is actually
// calculated, #5702
anchorX: horiz ?
posx :
(axis.opposite ? 0 : chart.chartWidth),
anchorY: horiz ?
(axis.opposite ? chart.chartHeight : 0) :
posy + crossBox.height / 2
});
}
/**
* Wrapper to hide the label.
* @private
*/
function onAxisAfterHideCrosshair() {
var axis = this;
if (axis.crossLabel) {
axis.crossLabel = axis.crossLabel.hide();
}
}
/**
* Override the automatic label alignment so that the first Y axis' labels
* are drawn on top of the grid line, and subsequent axes are drawn outside.
* @private
*/
function onAxisAutoLabelAlign(e) {
var axis = this,
chart = axis.chart,
options = axis.options,
panes = chart._labelPanes = chart._labelPanes || {},
labelOptions = options.labels;
if (chart.options.isStock && axis.coll === 'yAxis') {
var key = options.top + ',' + options.height;
// Do it only for the first Y axis of each pane
if (!panes[key] && labelOptions.enabled) {
if (labelOptions.distance === 15 && // Default
axis.side === 1) {
labelOptions.distance = 0;
}
if (typeof labelOptions.align === 'undefined') {
labelOptions.align = 'right';
}
panes[key] = axis;
e.align = 'right';
e.preventDefault();
}
}
}
/**
* Clear axis from label panes. (#6071)
* @private
*/
function onAxisDestroy() {
var axis = this, chart = axis.chart, key = (axis.options &&
(axis.options.top + ',' + axis.options.height));
if (key && chart._labelPanes && chart._labelPanes[key] === axis) {
delete chart._labelPanes[key];
}
}
/**
* Override getPlotLinePath to allow for multipane charts.
* @private
*/
function onAxisGetPlotLinePath(e) {
var axis = this,
series = (axis.isLinked && !axis.series && axis.linkedParent ?
axis.linkedParent.series :
axis.series),
chart = axis.chart,
renderer = chart.renderer,
axisLeft = axis.left,
axisTop = axis.top,
result = [],
translatedValue = e.translatedValue,
value = e.value,
force = e.force,
/**
* Return the other axis based on either the axis option or on
* related series.
* @private
*/
getAxis = function (coll) {
var otherColl = coll === 'xAxis' ? 'yAxis' : 'xAxis',
opt = axis.options[otherColl];
// Other axis indexed by number
if (StockChart_isNumber(opt)) {
return [chart[otherColl][opt]];
}
// Other axis indexed by id (like navigator)
if (StockChart_isString(opt)) {
return [chart.get(opt)];
}
// Auto detect based on existing series
return series.map(function (s) { return s[otherColl]; });
};
var x1,
y1,
x2,
y2,
axes = [], // #3416 need a default array
axes2,
uniqueAxes,
transVal;
if ( // For stock chart, by default render paths across the panes
// except the case when `acrossPanes` is disabled by user (#6644)
(chart.options.isStock && e.acrossPanes !== false) &&
// Ignore in case of colorAxis or zAxis. #3360, #3524, #6720
axis.coll === 'xAxis' || axis.coll === 'yAxis') {
e.preventDefault();
// Get the related axes based on series
axes = getAxis(axis.coll);
// Get the related axes based options.*Axis setting #2810
axes2 = (axis.isXAxis ? chart.yAxis : chart.xAxis);
for (var _i = 0, axes2_1 = axes2; _i < axes2_1.length; _i++) {
var A = axes2_1[_i];
if (!A.options.isInternal) {
var a = (A.isXAxis ? 'yAxis' : 'xAxis'),
relatedAxis = (StockChart_defined(A.options[a]) ?
chart[a][A.options[a]] :
chart[a][0]);
if (axis === relatedAxis) {
axes.push(A);
}
}
}
// Remove duplicates in the axes array. If there are no axes in the
// axes array, we are adding an axis without data, so we need to
// populate this with grid lines (#2796).
uniqueAxes = axes.length ?
[] :
[axis.isXAxis ? chart.yAxis[0] : chart.xAxis[0]]; // #3742
var _loop_1 = function (axis2) {
if (uniqueAxes.indexOf(axis2) === -1 &&
// Do not draw on axis which overlap completely. #5424
!StockChart_find(uniqueAxes,
function (unique) { return (unique.pos === axis2.pos &&
unique.len === axis2.len); })) {
uniqueAxes.push(axis2);
}
};
for (var _a = 0, axes_1 = axes; _a < axes_1.length; _a++) {
var axis2 = axes_1[_a];
_loop_1(axis2);
}
transVal = StockChart_pick(translatedValue, axis.translate(value || 0, void 0, void 0, e.old));
if (StockChart_isNumber(transVal)) {
if (axis.horiz) {
for (var _b = 0, uniqueAxes_1 = uniqueAxes; _b < uniqueAxes_1.length; _b++) {
var axis2 = uniqueAxes_1[_b];
var skip = void 0;
y1 = axis2.pos;
y2 = y1 + axis2.len;
x1 = x2 = Math.round(transVal + axis.transB);
// Outside plot area
if (force !== 'pass' &&
(x1 < axisLeft || x1 > axisLeft + axis.width)) {
if (force) {
x1 = x2 = StockChart_clamp(x1, axisLeft, axisLeft + axis.width);
}
else {
skip = true;
}
}
if (!skip) {
result.push(['M', x1, y1], ['L', x2, y2]);
}
}
}
else {
for (var _c = 0, uniqueAxes_2 = uniqueAxes; _c < uniqueAxes_2.length; _c++) {
var axis2 = uniqueAxes_2[_c];
var skip = void 0;
x1 = axis2.pos;
x2 = x1 + axis2.len;
y1 = y2 = Math.round(axisTop + axis.height - transVal);
// Outside plot area
if (force !== 'pass' &&
(y1 < axisTop || y1 > axisTop + axis.height)) {
if (force) {
y1 = y2 = StockChart_clamp(y1, axisTop, axisTop + axis.height);
}
else {
skip = true;
}
}
if (!skip) {
result.push(['M', x1, y1], ['L', x2, y2]);
}
}
}
}
e.path = result.length > 0 ?
renderer.crispPolyLine(result, e.lineWidth || 1) :
// #3557 getPlotLinePath in regular Highcharts also returns null
void 0;
}
}
/**
* Handle som Stock-specific series defaults, override the plotOptions
* before series options are handled.
* @private
*/
function onSeriesSetOptions(e) {
var series = this;
if (series.chart.options.isStock) {
var overrides = void 0;
if (series.is('column') || series.is('columnrange')) {
overrides = {
borderWidth: 0,
shadow: false
};
}
else if (!series.is('scatter') && !series.is('sma')) {
overrides = {
marker: {
enabled: false,
radius: 2
}
};
}
if (overrides) {
e.plotOptions[series.type] = StockChart_merge(e.plotOptions[series.type], overrides);
}
}
}
/**
* Based on the data grouping options decides whether
* the data should be cropped while processing.
*
* @ignore
* @function Highcharts.Series#forceCropping
*/
function seriesForceCropping() {
var series = this,
chart = series.chart,
options = series.options,
dataGroupingOptions = options.dataGrouping,
groupingEnabled = (series.allowDG !== false &&
dataGroupingOptions &&
StockChart_pick(dataGroupingOptions.enabled,
chart.options.isStock));
return groupingEnabled;
}
/* eslint-disable jsdoc/check-param-names */
/**
* Factory function for creating new stock charts. Creates a new
* {@link Highcharts.StockChart|StockChart} object with different default
* options than the basic Chart.
*
* @example
* let chart = Highcharts.stockChart('container', {
* series: [{
* data: [1, 2, 3, 4, 5, 6, 7, 8, 9],
* pointInterval: 24 * 60 * 60 * 1000
* }]
* });
*
* @function Highcharts.stockChart
*
* @param {string|Highcharts.HTMLDOMElement} [renderTo]
* The DOM element to render to, or its id.
*
* @param {Highcharts.Options} options
* The chart options structure as described in the
* [options reference](https://api.highcharts.com/highstock).
*
* @param {Highcharts.ChartCallbackFunction} [callback]
* A function to execute when the chart object is finished
* rendering and all external image files (`chart.backgroundImage`,
* `chart.plotBackgroundImage` etc) are loaded. Defining a
* [chart.events.load](https://api.highcharts.com/highstock/chart.events.load)
* handler is equivalent.
*
* @return {Highcharts.StockChart}
* The chart object.
*/
function stockChart(a, b, c) {
return new StockChart(a, b, c);
}
StockChart.stockChart = stockChart;
/* eslint-enable jsdoc/check-param-names */
/**
* Function to crisp a line with multiple segments
*
* @private
* @function Highcharts.SVGRenderer#crispPolyLine
*/
function svgRendererCrispPolyLine(points, width) {
// Points format: [['M', 0, 0], ['L', 100, 0]]
// normalize to a crisp line
for (var i = 0; i < points.length; i = i + 2) {
var start = points[i],
end = points[i + 1];
if (StockChart_defined(start[1]) && start[1] === end[1]) {
start[1] = end[1] = StockChart_crisp(start[1], width);
}
if (StockChart_defined(start[2]) && start[2] === end[2]) {
start[2] = end[2] = StockChart_crisp(start[2], width);
}
}
return points;
}
})(StockChart || (StockChart = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Chart_StockChart = (StockChart);
;// ./code/es5/es-modules/Series/HLC/HLCPoint.js
/* *
*
* (c) 2010-2024 Pawel Lysy
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var HLCPoint_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b,
p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var ColumnPoint = Series_SeriesRegistry.seriesTypes.column.prototype.pointClass;
/* *
*
* Class
*
* */
var HLCPoint = /** @class */ (function (_super) {
HLCPoint_extends(HLCPoint, _super);
function HLCPoint() {
return _super !== null && _super.apply(this, arguments) || this;
}
return HLCPoint;
}(ColumnPoint));
/* *
*
* Default Export
*
* */
/* harmony default export */ var HLC_HLCPoint = (HLCPoint);
;// ./code/es5/es-modules/Series/HLC/HLCSeriesDefaults.js
/* *
*
* (c) 2010-2024 Pawel Lysy
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* API Options
*
* */
/**
* An HLC chart is a style of financial chart used to describe price
* movements over time. It displays high, low and close values per
* data point.
*
* @sample stock/demo/hlc/
* HLC chart
*
* @extends plotOptions.column
* @excluding borderColor, borderRadius, borderWidth, crisp, stacking,
* stack
* @product highstock
* @optionparent plotOptions.hlc
*/
var HLCSeriesDefaults = {
/**
* The approximate pixel width of each group. If for example a series
* with 30 points is displayed over a 600 pixel wide plot area, no
* grouping is performed. If however the series contains so many points
* that the spacing is less than the groupPixelWidth, Highcharts will
* try to group it into appropriate groups so that each is more or less
* two pixels wide. Defaults to `5`.
*
* @type {number}
* @default 5
* @product highstock
* @apioption plotOptions.hlc.dataGrouping.groupPixelWidth
*/
/**
* @type {Highcharts.DataGroupingApproximationValue|Function}
* @default hlc
* @product highstock
* @apioption plotOptions.hlc.dataGrouping.approximation
*/
/**
* @default close
* @apioption plotOptions.hlc.colorKey
*/
/**
* The pixel width of the line/border. Defaults to `1`.
*
* @sample {highstock} stock/plotoptions/hlc-linewidth/
* A greater line width
*
* @type {number}
* @default 1
* @product highstock
*
* @public
*/
lineWidth: 1,
tooltip: {
pointFormat: '<span style="color:{point.color}">\u25CF</span> ' +
'<b> {series.name}</b><br/>' +
'High: {point.high}<br/>' +
'Low: {point.low}<br/>' +
'Close: {point.close}<br/>'
},
/**
* @type {number|null}
*/
threshold: null,
states: {
/**
* @extends plotOptions.column.states.hover
* @product highstock
*/
hover: {
/**
* The pixel width of the line representing the HLC point.
*
* @type {number}
* @default 3
* @product highstock
*/
lineWidth: 3
}
},
/**
* Determines which one of `high`, `low`, `close` values should
* be represented as `point.y`, which is later used to set dataLabel
* position and [compare](#plotOptions.series.compare).
*
* @sample {highstock} stock/plotoptions/hlc-pointvalkey/
* Possible values
*
* @declare Highcharts.OptionsHLCPointValKeyValue
* @type {string}
* @default close
* @validvalue ["high", "low", "close"]
* @product highstock
* @apioption plotOptions.hlc.pointValKey
*/
/**
* @default close
* @apioption plotOptions.hlc.colorKey
*/
stickyTracking: true
};
/**
* A `hlc` series. If the [type](#series.hlc.type) option is not
* specified, it is inherited from [chart.type](#chart.type).
*
* @extends series,plotOptions.hlc
* @excluding dataParser, dataURL
* @product highstock
* @apioption series.hlc
*/
/**
* An array of data points for the series. For the `hlc` series type,
* points can be given in the following ways:
*
* 1. An array of arrays with 4 or 3 values. In this case, the values correspond
* to `x,high,low,close`. If the first value is a string, it is applied
* as the name of the point, and the `x` value is inferred. The `x` value can
* also be omitted, in which case the inner arrays should be of length of 3\.
* Then the `x` value is automatically calculated, either starting at 0 and
* incremented by 1, or from `pointStart` and `pointInterval` given in the
* series options.
* ```js
* data: [
* [0, 5, 6, 7],
* [1, 4, 8, 2],
* [2, 3, 4, 10]
* ]
* ```
*
* 2. An array of objects with named values. The following snippet shows only a
* few settings, see the complete options set below. If the total number of
* data points exceeds the series'
* [turboThreshold](#series.hlc.turboThreshold), this option is not
* available.
* ```js
* data: [{
* x: 1,
* high: 4,
* low: 5,
* close: 2,
* name: "Point2",
* color: "#00FF00"
* }, {
* x: 1,
* high: 3,
* low: 6,
* close: 7,
* name: "Point1",
* color: "#FF00FF"
* }]
* ```
*
* @type {Array<Array<(number|string),number,number>|Array<(number|string),number,number,number>|*>}
* @extends series.arearange.data
* @excluding y, marker
* @product highstock
* @apioption series.hlc.data
*/
/**
* The closing value of each data point.
*
* @type {number}
* @product highstock
* @apioption series.hlc.data.close
*/
(''); // Keeps doclets above in JS file
/* *
*
* Default Export
*
* */
/* harmony default export */ var HLC_HLCSeriesDefaults = (HLCSeriesDefaults);
;// ./code/es5/es-modules/Series/HLC/HLCSeries.js
/* *
*
* (c) 2010-2024 Pawel Lysy
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var HLCSeries_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b,
p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var HLCSeries_ColumnSeries = Series_SeriesRegistry.seriesTypes.column;
var HLCSeries_crisp = Core_Utilities.crisp, HLCSeries_extend = Core_Utilities.extend, HLCSeries_merge = Core_Utilities.merge;
/* *
*
* Class
*
* */
/**
* The hlc series type.
*
* @private
* @class
* @name Highcharts.seriesTypes.hlc
*
* @augments Highcharts.Series
*/
var HLCSeries = /** @class */ (function (_super) {
HLCSeries_extends(HLCSeries, _super);
function HLCSeries() {
return _super !== null && _super.apply(this, arguments) || this;
}
/* *
*
* Functions
*
* */
/**
* Extend the path if close is not between high and low.
*
* @param {SVGPath} path the path array of the point
* @param {number} halfStrokeWidth
* @param {number} value value of the point to which the stem should be extended
*/
HLCSeries.prototype.extendStem = function (path, halfStrokeWidth, value) {
var start = path[0];
var end = path[1];
// We don't need to worry about crisp - close value
// is already crisped and halfStrokeWidth should remove it.
if (typeof start[2] === 'number') {
start[2] = Math.max(value + halfStrokeWidth, start[2]);
}
if (typeof end[2] === 'number') {
end[2] = Math.min(value - halfStrokeWidth, end[2]);
}
};
/**
* Function to create SVGPath of the point based on the
* plot positions of this point.
* @private
*/
HLCSeries.prototype.getPointPath = function (point, graphic) {
// Crisp vector coordinates
var strokeWidth = graphic.strokeWidth(),
series = point.series,
// #2596:
crispX = HLCSeries_crisp(point.plotX || 0,
strokeWidth),
halfWidth = Math.round(point.shapeArgs.width / 2);
// The vertical stem
var path = [
['M',
crispX,
Math.round(point.yBottom)],
['L',
crispX,
Math.round(point.plotHigh)]
];
// Close
if (point.close !== null) {
var plotClose = HLCSeries_crisp(point.plotClose,
strokeWidth);
path.push(['M', crispX, plotClose], ['L', crispX + halfWidth, plotClose]);
series.extendStem(path, strokeWidth / 2, plotClose);
}
return path;
};
/**
* Draw single point
* @private
*/
HLCSeries.prototype.drawSinglePoint = function (point) {
var series = point.series,
chart = series.chart;
var path,
graphic = point.graphic;
if (typeof point.plotY !== 'undefined') {
// Create and/or update the graphic
if (!graphic) {
point.graphic = graphic = chart.renderer.path()
.add(series.group);
}
if (!chart.styledMode) {
graphic.attr(series.pointAttribs(point, (point.selected && 'select'))); // #3897
}
// Crisp vector coordinates
path = series.getPointPath(point, graphic);
graphic[!graphic ? 'attr' : 'animate']({ d: path })
.addClass(point.getClassName(), true);
}
};
/**
* Draw the data points
* @private
*/
HLCSeries.prototype.drawPoints = function () {
this.points.forEach(this.drawSinglePoint);
};
/**
* @private
* @function Highcharts.seriesTypes.hlc#init
*/
HLCSeries.prototype.init = function () {
_super.prototype.init.apply(this, arguments);
this.options.stacking = void 0; // #8817
};
/**
* Postprocess mapping between options and SVG attributes
* @private
*/
HLCSeries.prototype.pointAttribs = function (point, state) {
var attribs = _super.prototype.pointAttribs.call(this,
point,
state);
delete attribs.fill;
return attribs;
};
HLCSeries.prototype.toYData = function (point) {
// Return a plain array for speedy calculation
return [point.high, point.low, point.close];
};
/**
* Translate data points from raw values x and y to plotX and plotY
*
* @private
* @function Highcharts.seriesTypes.hlc#translate
*/
HLCSeries.prototype.translate = function () {
var series = this,
yAxis = series.yAxis,
names = (this.pointArrayMap && this.pointArrayMap.slice()) || [],
translated = names.map(function (name) {
return "plot".concat(name.charAt(0).toUpperCase() + name.slice(1));
});
translated.push('yBottom');
names.push('low');
_super.prototype.translate.apply(series);
// Do the translation
series.points.forEach(function (point) {
names.forEach(function (name, i) {
var value = point[name];
if (value !== null) {
if (series.dataModify) {
value = series.dataModify.modifyValue(value);
}
point[translated[i]] =
yAxis.toPixels(value, true);
}
});
// Align the tooltip to the high value to avoid covering the
// point
point.tooltipPos[1] =
point.plotHigh + yAxis.pos - series.chart.plotTop;
});
};
/* *
*
* Static Properties
*
* */
HLCSeries.defaultOptions = HLCSeries_merge(HLCSeries_ColumnSeries.defaultOptions, HLC_HLCSeriesDefaults);
return HLCSeries;
}(HLCSeries_ColumnSeries));
HLCSeries_extend(HLCSeries.prototype, {
pointClass: HLC_HLCPoint,
animate: null, // Disable animation
directTouch: false,
keysAffectYAxis: ['low', 'high'],
pointArrayMap: ['high', 'low', 'close'],
pointAttrToOptions: {
stroke: 'color',
'stroke-width': 'lineWidth'
},
pointValKey: 'close'
});
Series_SeriesRegistry.registerSeriesType('hlc', HLCSeries);
/* *
*
* Default Export
*
* */
/* harmony default export */ var HLC_HLCSeries = ((/* unused pure expression or super */ null && (HLCSeries)));
;// ./code/es5/es-modules/Series/OHLC/OHLCPoint.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var OHLCPoint_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b,
p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var OHLCPoint_HLCSeries = Series_SeriesRegistry.seriesTypes.hlc;
/* *
*
* Class
*
* */
var OHLCPoint = /** @class */ (function (_super) {
OHLCPoint_extends(OHLCPoint, _super);
function OHLCPoint() {
return _super !== null && _super.apply(this, arguments) || this;
}
/* *
*
* Functions
*
* */
/**
* Extend the parent method by adding up or down to the class name.
* @private
* @function Highcharts.seriesTypes.ohlc#getClassName
*/
OHLCPoint.prototype.getClassName = function () {
return _super.prototype.getClassName.call(this) +
(this.open < this.close ?
' highcharts-point-up' :
' highcharts-point-down');
};
/**
* Save upColor as point color (#14826).
* @private
* @function Highcharts.seriesTypes.ohlc#resolveUpColor
*/
OHLCPoint.prototype.resolveUpColor = function () {
if (this.open < this.close &&
!this.options.color &&
this.series.options.upColor) {
this.color = this.series.options.upColor;
}
};
/**
* Extend the parent method by saving upColor.
* @private
* @function Highcharts.seriesTypes.ohlc#resolveColor
*/
OHLCPoint.prototype.resolveColor = function () {
_super.prototype.resolveColor.call(this);
if (!this.series.is('heikinashi')) {
this.resolveUpColor();
}
};
/**
* Extend the parent method by saving upColor.
* @private
* @function Highcharts.seriesTypes.ohlc#getZone
*
* @return {Highcharts.SeriesZonesOptionsObject}
* The zone item.
*/
OHLCPoint.prototype.getZone = function () {
var zone = _super.prototype.getZone.call(this);
this.resolveUpColor();
return zone;
};
/**
* Extend the parent method by resolving up/down colors (#15849)
* @private
**/
OHLCPoint.prototype.applyOptions = function () {
_super.prototype.applyOptions.apply(this, arguments);
if (this.resolveColor) {
this.resolveColor();
}
return this;
};
return OHLCPoint;
}(OHLCPoint_HLCSeries.prototype.pointClass));
/* *
*
* Default Export
*
* */
/* harmony default export */ var OHLC_OHLCPoint = (OHLCPoint);
;// ./code/es5/es-modules/Series/OHLC/OHLCSeriesDefaults.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* API Options
*
* */
/**
* An OHLC chart is a style of financial chart used to describe price
* movements over time. It displays open, high, low and close values per
* data point.
*
* @sample stock/demo/ohlc
* OHLC chart
*
* @extends plotOptions.hlc
* @product highstock
* @optionparent plotOptions.ohlc
*/
var OHLCSeriesDefaults = {
/**
* @type {Highcharts.DataGroupingApproximationValue|Function}
* @default ohlc
* @product highstock
* @apioption plotOptions.ohlc.dataGrouping.approximation
*/
/**
* Determines which one of `open`, `high`, `low`, `close` values should
* be represented as `point.y`, which is later used to set dataLabel
* position and [compare](#plotOptions.series.compare).
*
* @declare Highcharts.OptionsPointValKeyValue
* @default close
* @validvalue ["open", "high", "low", "close"]
* @product highstock
* @apioption plotOptions.ohlc.pointValKey
*/
/**
* Line color for up points.
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @product highstock
* @apioption plotOptions.ohlc.upColor
*/
tooltip: {
pointFormat: '<span style="color:{point.color}">\u25CF</span> ' +
'<b> {series.name}</b><br/>' +
'Open: {point.open}<br/>' +
'High: {point.high}<br/>' +
'Low: {point.low}<br/>' +
'Close: {point.close}<br/>'
}
};
/**
* The parameter allows setting line series type and use OHLC indicators.
* Data in OHLC format is required.
*
* @sample {highstock} stock/indicators/use-ohlc-data
* Use OHLC data format to plot line chart
*
* @type {boolean}
* @product highstock
* @apioption plotOptions.line.useOhlcData
*/
/**
* A `ohlc` series. If the [type](#series.ohlc.type) option is not
* specified, it is inherited from [chart.type](#chart.type).
*
* @extends series,plotOptions.ohlc
* @excluding dataParser, dataURL
* @product highstock
* @apioption series.ohlc
*/
/**
* An array of data points for the series. For the `ohlc` series type,
* points can be given in the following ways:
*
* 1. An array of arrays with 5 or 4 values. In this case, the values correspond
* to `x,open,high,low,close`. If the first value is a string, it is applied
* as the name of the point, and the `x` value is inferred. The `x` value can
* also be omitted, in which case the inner arrays should be of length 4\.
* Then the `x` value is automatically calculated, either starting at 0 and
* incremented by 1, or from `pointStart` and `pointInterval` given in the
* series options.
* ```js
* data: [
* [0, 6, 5, 6, 7],
* [1, 9, 4, 8, 2],
* [2, 6, 3, 4, 10]
* ]
* ```
*
* 2. An array of objects with named values. The following snippet shows only a
* few settings, see the complete options set below. If the total number of
* data points exceeds the series'
* [turboThreshold](#series.ohlc.turboThreshold), this option is not
* available.
* ```js
* data: [{
* x: 1,
* open: 3,
* high: 4,
* low: 5,
* close: 2,
* name: "Point2",
* color: "#00FF00"
* }, {
* x: 1,
* open: 4,
* high: 3,
* low: 6,
* close: 7,
* name: "Point1",
* color: "#FF00FF"
* }]
* ```
*
* @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
* @extends series.arearange.data
* @excluding y, marker
* @product highstock
* @apioption series.ohlc.data
*/
/**
* The closing value of each data point.
*
* @type {number}
* @product highstock
* @apioption series.ohlc.data.close
*/
/**
* The opening value of each data point.
*
* @type {number}
* @product highstock
* @apioption series.ohlc.data.open
*/
''; // Adds doclets above to transpiled
/* *
*
* Default Export
*
* */
/* harmony default export */ var OHLC_OHLCSeriesDefaults = (OHLCSeriesDefaults);
;// ./code/es5/es-modules/Series/OHLC/OHLCSeries.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var OHLCSeries_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b,
p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var OHLCSeries_composed = Core_Globals.composed;
var OHLCSeries_HLCSeries = Series_SeriesRegistry.seriesTypes.hlc;
var OHLCSeries_addEvent = Core_Utilities.addEvent, OHLCSeries_crisp = Core_Utilities.crisp, OHLCSeries_extend = Core_Utilities.extend, OHLCSeries_merge = Core_Utilities.merge, OHLCSeries_pushUnique = Core_Utilities.pushUnique;
/* *
*
* Functions
*
* */
/**
* @private
*/
function onSeriesAfterSetOptions(e) {
var options = e.options,
dataGrouping = options.dataGrouping;
if (dataGrouping &&
options.useOhlcData &&
options.id !== 'highcharts-navigator-series') {
dataGrouping.approximation = 'ohlc';
}
}
/**
* Add useOhlcData option
* @private
*/
function onSeriesInit(eventOptions) {
// eslint-disable-next-line no-invalid-this
var series = this,
options = eventOptions.options;
if (options.useOhlcData &&
options.id !== 'highcharts-navigator-series') {
OHLCSeries_extend(series, {
pointValKey: OHLCSeries.prototype.pointValKey,
// Keys: ohlcProto.keys, // @todo potentially nonsense
pointArrayMap: OHLCSeries.prototype.pointArrayMap,
toYData: OHLCSeries.prototype.toYData
});
}
}
/* *
*
* Class
*
* */
/**
* The ohlc series type.
*
* @private
* @class
* @name Highcharts.seriesTypes.ohlc
*
* @augments Highcharts.Series
*/
var OHLCSeries = /** @class */ (function (_super) {
OHLCSeries_extends(OHLCSeries, _super);
function OHLCSeries() {
return _super !== null && _super.apply(this, arguments) || this;
}
/* *
*
* Static Functions
*
* */
OHLCSeries.compose = function (SeriesClass) {
var _args = [];
for (var _i = 1; _i < arguments.length; _i++) {
_args[_i - 1] = arguments[_i];
}
if (OHLCSeries_pushUnique(OHLCSeries_composed, 'OHLCSeries')) {
OHLCSeries_addEvent(SeriesClass, 'afterSetOptions', onSeriesAfterSetOptions);
OHLCSeries_addEvent(SeriesClass, 'init', onSeriesInit);
}
};
/* *
*
* Functions
*
* */
OHLCSeries.prototype.getPointPath = function (point, graphic) {
var path = _super.prototype.getPointPath.call(this,
point,
graphic),
strokeWidth = graphic.strokeWidth(),
crispX = OHLCSeries_crisp(point.plotX || 0,
strokeWidth),
halfWidth = Math.round(point.shapeArgs.width / 2);
if (point.open !== null) {
var plotOpen = OHLCSeries_crisp(point.plotOpen,
strokeWidth);
path.push(['M', crispX, plotOpen], ['L', crispX - halfWidth, plotOpen]);
_super.prototype.extendStem.call(this, path, strokeWidth / 2, plotOpen);
}
return path;
};
/**
* Postprocess mapping between options and SVG attributes
* @private
*/
OHLCSeries.prototype.pointAttribs = function (point, state) {
var attribs = _super.prototype.pointAttribs.call(this,
point,
state),
options = this.options;
delete attribs.fill;
if (!point.options.color &&
options.upColor &&
point.open < point.close) {
attribs.stroke = options.upColor;
}
return attribs;
};
OHLCSeries.prototype.toYData = function (point) {
// Return a plain array for speedy calculation
return [point.open, point.high, point.low, point.close];
};
/* *
*
* Static Properties
*
* */
OHLCSeries.defaultOptions = OHLCSeries_merge(OHLCSeries_HLCSeries.defaultOptions, OHLC_OHLCSeriesDefaults);
return OHLCSeries;
}(OHLCSeries_HLCSeries));
OHLCSeries_extend(OHLCSeries.prototype, {
pointClass: OHLC_OHLCPoint,
pointArrayMap: ['open', 'high', 'low', 'close']
});
Series_SeriesRegistry.registerSeriesType('ohlc', OHLCSeries);
/* *
*
* Default Export
*
* */
/* harmony default export */ var OHLC_OHLCSeries = (OHLCSeries);
;// ./code/es5/es-modules/Series/Candlestick/CandlestickSeriesDefaults.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* API Options
*
* */
/**
* A candlestick chart is a style of financial chart used to describe
* price movements over time.
*
* @sample stock/demo/candlestick/
* Candlestick chart
*
* @extends plotOptions.ohlc
* @excluding borderColor,borderRadius,borderWidth
* @product highstock
* @optionparent plotOptions.candlestick
*/
var CandlestickSeriesDefaults = {
/**
* The specific line color for up candle sticks. The default is to
* inherit the general `lineColor` setting.
*
* @sample {highstock} stock/plotoptions/candlestick-linecolor/
* Candlestick line colors
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @since 1.3.6
* @product highstock
* @apioption plotOptions.candlestick.upLineColor
*/
states: {
/**
* @extends plotOptions.column.states.hover
* @product highstock
*/
hover: {
/**
* The pixel width of the line/border around the
* candlestick.
*
* @product highstock
*/
lineWidth: 2
}
},
/**
* @type {number|null}
* @product highstock
*/
threshold: null,
/**
* The color of the line/border of the candlestick.
*
* In styled mode, the line stroke can be set with the
* `.highcharts-candlestick-series .highcahrts-point` rule.
*
* @see [upLineColor](#plotOptions.candlestick.upLineColor)
*
* @sample {highstock} stock/plotoptions/candlestick-linecolor/
* Candlestick line colors
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @default #000000
* @product highstock
*/
lineColor: "#000000" /* Palette.neutralColor100 */,
/**
* The pixel width of the candlestick line/border. Defaults to `1`.
*
*
* In styled mode, the line stroke width can be set with the
* `.highcharts-candlestick-series .highcahrts-point` rule.
*
* @product highstock
*/
lineWidth: 1,
/**
* The fill color of the candlestick when values are rising.
*
* In styled mode, the up color can be set with the
* `.highcharts-candlestick-series .highcharts-point-up` rule.
*
* @sample {highstock} stock/plotoptions/candlestick-color/
* Custom colors
* @sample {highstock} highcharts/css/candlestick/
* Colors in styled mode
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @default #ffffff
* @product highstock
*/
upColor: "#ffffff" /* Palette.backgroundColor */,
/**
* @product highstock
*/
stickyTracking: true
};
/**
* A `candlestick` series. If the [type](#series.candlestick.type)
* option is not specified, it is inherited from [chart.type](
* #chart.type).
*
* @type {*}
* @extends series,plotOptions.candlestick
* @excluding dataParser, dataURL, marker
* @product highstock
* @apioption series.candlestick
*/
/**
* An array of data points for the series. For the `candlestick` series
* type, points can be given in the following ways:
*
* 1. An array of arrays with 5 or 4 values. In this case, the values correspond
* to `x,open,high,low,close`. If the first value is a string, it is applied
* as the name of the point, and the `x` value is inferred. The `x` value can
* also be omitted, in which case the inner arrays should be of length 4.
* Then the `x` value is automatically calculated, either starting at 0 and
* incremented by 1, or from `pointStart` and `pointInterval` given in the
* series options.
* ```js
* data: [
* [0, 7, 2, 0, 4],
* [1, 1, 4, 2, 8],
* [2, 3, 3, 9, 3]
* ]
* ```
*
* 2. An array of objects with named values. The following snippet shows only a
* few settings, see the complete options set below. If the total number of
* data points exceeds the series'
* [turboThreshold](#series.candlestick.turboThreshold), this option is not
* available.
* ```js
* data: [{
* x: 1,
* open: 9,
* high: 2,
* low: 4,
* close: 6,
* name: "Point2",
* color: "#00FF00"
* }, {
* x: 1,
* open: 1,
* high: 4,
* low: 7,
* close: 7,
* name: "Point1",
* color: "#FF00FF"
* }]
* ```
*
* @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
* @extends series.ohlc.data
* @excluding y
* @product highstock
* @apioption series.candlestick.data
*/
''; // Adds doclets above to transpiled
/* *
*
* Default Export
*
* */
/* harmony default export */ var Candlestick_CandlestickSeriesDefaults = (CandlestickSeriesDefaults);
;// ./code/es5/es-modules/Series/Candlestick/CandlestickSeries.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var CandlestickSeries_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b,
p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var CandlestickSeries_a = Series_SeriesRegistry.seriesTypes, CandlestickSeries_ColumnSeries = CandlestickSeries_a.column, CandlestickSeries_OHLCSeries = CandlestickSeries_a.ohlc;
var CandlestickSeries_crisp = Core_Utilities.crisp, CandlestickSeries_merge = Core_Utilities.merge;
/* *
*
* Class
*
* */
/**
* The candlestick series type.
*
* @private
* @class
* @name Highcharts.seriesTypes.candlestick
*
* @augments Highcharts.seriesTypes.ohlc
*/
var CandlestickSeries = /** @class */ (function (_super) {
CandlestickSeries_extends(CandlestickSeries, _super);
function CandlestickSeries() {
return _super !== null && _super.apply(this, arguments) || this;
}
/* *
*
* Functions
*
* */
/**
* Postprocess mapping between options and SVG attributes
*
* @private
* @function Highcharts.seriesTypes.candlestick#pointAttribs
*/
CandlestickSeries.prototype.pointAttribs = function (point, state) {
var attribs = CandlestickSeries_ColumnSeries.prototype.pointAttribs.call(this,
point,
state),
options = this.options,
isUp = point.open < point.close,
stroke = options.lineColor || this.color,
color = point.color || this.color; // (#14826)
attribs['stroke-width'] = options.lineWidth;
attribs.fill = point.options.color ||
(isUp ? (options.upColor || color) : color);
attribs.stroke = point.options.lineColor ||
(isUp ? (options.upLineColor || stroke) : stroke);
// Select or hover states
if (state) {
var stateOptions = options.states[state];
attribs.fill = stateOptions.color || attribs.fill;
attribs.stroke = stateOptions.lineColor || attribs.stroke;
attribs['stroke-width'] =
stateOptions.lineWidth || attribs['stroke-width'];
}
return attribs;
};
/**
* Draw the data points.
*
* @private
* @function Highcharts.seriesTypes.candlestick#drawPoints
*/
CandlestickSeries.prototype.drawPoints = function () {
var series = this,
points = series.points,
chart = series.chart,
reversedYAxis = series.yAxis.reversed;
for (var _i = 0, points_1 = points; _i < points_1.length; _i++) {
var point = points_1[_i];
var graphic = point.graphic,
plotOpen = void 0,
plotClose = void 0,
topBox = void 0,
bottomBox = void 0,
hasTopWhisker = void 0,
hasBottomWhisker = void 0,
crispX = void 0,
path = void 0,
halfWidth = void 0;
var isNew = !graphic;
if (typeof point.plotY !== 'undefined') {
if (!graphic) {
point.graphic = graphic = chart.renderer.path()
.add(series.group);
}
if (!series.chart.styledMode) {
graphic
.attr(series.pointAttribs(point, (point.selected && 'select'))) // #3897
.shadow(series.options.shadow);
}
// Crisp vector coordinates
var strokeWidth = graphic.strokeWidth();
// #2596:
crispX = CandlestickSeries_crisp(point.plotX || 0, strokeWidth);
plotOpen = point.plotOpen;
plotClose = point.plotClose;
topBox = Math.min(plotOpen, plotClose);
bottomBox = Math.max(plotOpen, plotClose);
halfWidth = Math.round(point.shapeArgs.width / 2);
hasTopWhisker = reversedYAxis ?
bottomBox !== point.yBottom :
Math.round(topBox) !==
Math.round(point.plotHigh || 0);
hasBottomWhisker = reversedYAxis ?
Math.round(topBox) !==
Math.round(point.plotHigh || 0) :
bottomBox !== point.yBottom;
topBox = CandlestickSeries_crisp(topBox, strokeWidth);
bottomBox = CandlestickSeries_crisp(bottomBox, strokeWidth);
// Create the path. Due to a bug in Chrome 49, the path is
// first instantiated with no values, then the values
// pushed. For unknown reasons, instantiating the path array
// with all the values would lead to a crash when updating
// frequently (#5193).
path = [];
path.push(['M', crispX - halfWidth, bottomBox], ['L', crispX - halfWidth, topBox], ['L', crispX + halfWidth, topBox], ['L', crispX + halfWidth, bottomBox], ['Z'], // Ensure a nice rectangle #2602
['M', crispX, topBox], [
'L',
// #460, #2094
crispX,
hasTopWhisker ?
Math.round(reversedYAxis ?
point.yBottom :
point.plotHigh) :
topBox
], ['M', crispX, bottomBox], [
'L',
// #460, #2094
crispX,
hasBottomWhisker ?
Math.round(reversedYAxis ?
point.plotHigh :
point.yBottom) :
bottomBox
]);
graphic[isNew ? 'attr' : 'animate']({ d: path })
.addClass(point.getClassName(), true);
}
}
};
/* *
*
* Static Properties
*
* */
CandlestickSeries.defaultOptions = CandlestickSeries_merge(CandlestickSeries_OHLCSeries.defaultOptions, { tooltip: CandlestickSeries_OHLCSeries.defaultOptions.tooltip }, Candlestick_CandlestickSeriesDefaults);
return CandlestickSeries;
}(CandlestickSeries_OHLCSeries));
Series_SeriesRegistry.registerSeriesType('candlestick', CandlestickSeries);
/* *
*
* Default Export
*
* */
/* harmony default export */ var Candlestick_CandlestickSeries = ((/* unused pure expression or super */ null && (CandlestickSeries)));
;// ./code/es5/es-modules/Series/Flags/FlagsPoint.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var FlagsPoint_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b,
p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var FlagsPoint_ColumnPoint = Series_SeriesRegistry.seriesTypes.column.prototype.pointClass;
var FlagsPoint_isNumber = Core_Utilities.isNumber;
/* *
*
* Class
*
* */
var FlagsPoint = /** @class */ (function (_super) {
FlagsPoint_extends(FlagsPoint, _super);
function FlagsPoint() {
/* *
*
* Properties
*
* */
var _this = _super !== null && _super.apply(this,
arguments) || this;
_this.ttBelow = false;
return _this;
}
/* *
*
* Functions
*
* */
/**
* @private
*/
FlagsPoint.prototype.isValid = function () {
// #9233 - Prevent from treating flags as null points (even if
// they have no y values defined).
return FlagsPoint_isNumber(this.y) || typeof this.y === 'undefined';
};
/**
* @private
*/
FlagsPoint.prototype.hasNewShapeType = function () {
var shape = this.options.shape || this.series.options.shape;
return this.graphic && shape && shape !== this.graphic.symbolKey;
};
return FlagsPoint;
}(FlagsPoint_ColumnPoint));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Flags_FlagsPoint = (FlagsPoint);
;// ./code/es5/es-modules/Series/Flags/FlagsSeriesDefaults.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* API Options
*
* */
/**
* Flags are used to mark events in stock charts. They can be added on the
* timeline, or attached to a specific series.
*
* @sample stock/demo/flags-general/
* Flags on a line series
*
* @extends plotOptions.column
* @excluding animation, borderColor, borderWidth,
* colorByPoint, cropThreshold, dataGrouping, pointPadding,
* pointWidth, turboThreshold
* @product highstock
* @optionparent plotOptions.flags
*/
var FlagsSeriesDefaults = {
/**
* The corner radius of the border surrounding each flag. For `squarepin`
* shaped flags only. A number signifies pixels. A percentage string, like
* for example 50%, signifies a relative size.
*/
borderRadius: 0,
/**
* In case the flag is placed on a series, on what point key to place
* it. Line and columns have one key, `y`. In range or OHLC-type series,
* however, the flag can optionally be placed on the `open`, `high`,
* `low` or `close` key.
*
* @sample {highstock} stock/plotoptions/flags-onkey/
* Range series, flag on high
*
* @type {string}
* @default y
* @since 4.2.2
* @product highstock
* @validvalue ["y", "open", "high", "low", "close"]
* @apioption plotOptions.flags.onKey
*/
/**
* The id of the series that the flags should be drawn on. If no id
* is given, the flags are drawn on the x axis.
*
* @sample {highstock} stock/plotoptions/flags/
* Flags on series and on x axis
*
* @type {string}
* @product highstock
* @apioption plotOptions.flags.onSeries
*/
pointRange: 0, // #673
/**
* Whether the flags are allowed to overlap sideways. If `false`, the
* flags are moved sideways using an algorithm that seeks to place every
* flag as close as possible to its original position.
*
* @sample {highstock} stock/plotoptions/flags-allowoverlapx
* Allow sideways overlap
*
* @since 6.0.4
*/
allowOverlapX: false,
/**
* The shape of the marker. Can be one of "flag", "circlepin",
* "squarepin", or an image of the format `url(/path-to-image.jpg)`.
* Individual shapes can also be set for each point.
*
* @sample {highstock} stock/plotoptions/flags/
* Different shapes
*
* @type {Highcharts.FlagsShapeValue}
* @product highstock
*/
shape: 'flag',
/**
* When multiple flags in the same series fall on the same value, this
* number determines the vertical offset between them.
*
* @sample {highstock} stock/plotoptions/flags-stackdistance/
* A greater stack distance
*
* @product highstock
*/
stackDistance: 12,
/**
* Text alignment for the text inside the flag.
*
* @since 5.0.0
* @product highstock
* @validvalue ["left", "center", "right"]
*/
textAlign: 'center',
/**
* Specific tooltip options for flag series. Flag series tooltips are
* different from most other types in that a flag doesn't have a data
* value, so the tooltip rather displays the `text` option for each
* point.
*
* @extends plotOptions.series.tooltip
* @excluding changeDecimals, valueDecimals, valuePrefix, valueSuffix
* @product highstock
*/
tooltip: {
pointFormat: '{point.text}'
},
/**
* @type {number|null}
*/
threshold: null,
/**
* The text to display on each flag. This can be defined on series
* level, or individually for each point. Defaults to `"A"`.
*
* @type {string}
* @default A
* @product highstock
* @apioption plotOptions.flags.title
*/
/**
* The y position of the top left corner of the flag relative to either
* the series (if onSeries is defined), or the x axis. Defaults to
* `-30`.
*
* @product highstock
*/
y: -30,
/**
* Whether to use HTML to render the flag texts. Using HTML allows for
* advanced formatting, images and reliable bi-directional text
* rendering. Note that exported images won't respect the HTML, and that
* HTML won't respect Z-index settings.
*
* @type {boolean}
* @default false
* @since 1.3
* @product highstock
* @apioption plotOptions.flags.useHTML
*/
/**
* Fixed width of the flag's shape. By default, width is autocalculated
* according to the flag's title.
*
* @sample {highstock} stock/demo/flags-shapes/
* Flags with fixed width
*
* @type {number}
* @product highstock
* @apioption plotOptions.flags.width
*/
/**
* Fixed height of the flag's shape. By default, height is
* autocalculated according to the flag's title.
*
* @type {number}
* @product highstock
* @apioption plotOptions.flags.height
*/
/**
* The fill color for the flags.
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @product highstock
*/
fillColor: "#ffffff" /* Palette.backgroundColor */,
/**
* The color of the line/border of the flag.
*
* In styled mode, the stroke is set in the
* `.highcharts-flag-series.highcharts-point` rule.
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @default #000000
* @product highstock
* @apioption plotOptions.flags.lineColor
*/
/**
* The pixel width of the flag's line/border.
*
* @product highstock
*/
lineWidth: 1,
states: {
/**
* @extends plotOptions.column.states.hover
* @product highstock
*/
hover: {
/**
* The color of the line/border of the flag.
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @product highstock
*/
lineColor: "#000000" /* Palette.neutralColor100 */,
/**
* The fill or background color of the flag.
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @product highstock
*/
fillColor: "#ccd3ff" /* Palette.highlightColor20 */
}
},
/**
* The text styles of the flag.
*
* In styled mode, the styles are set in the
* `.highcharts-flag-series .highcharts-point` rule.
*
* @type {Highcharts.CSSObject}
* @default {"fontSize": "11px", "fontWeight": "bold"}
* @product highstock
*/
style: {
/** @ignore-option */
fontSize: '0.7em',
/** @ignore-option */
fontWeight: 'bold'
}
};
/**
* A `flags` series. If the [type](#series.flags.type) option is not
* specified, it is inherited from [chart.type](#chart.type).
*
* @extends series,plotOptions.flags
* @excluding animation, borderColor, borderRadius, borderWidth, colorByPoint,
* connectNulls, cropThreshold, dashStyle, dataGrouping, dataParser,
* dataURL, gapSize, gapUnit, linecap, lineWidth, marker,
* pointPadding, pointWidth, step, turboThreshold, useOhlcData
* @product highstock
* @apioption series.flags
*/
/**
* An array of data points for the series. For the `flags` series type,
* points can be given in the following ways:
*
* 1. An array of objects with named values. The following snippet shows only a
* few settings, see the complete options set below. If the total number of
* data points exceeds the series'
* [turboThreshold](#series.flags.turboThreshold), this option is not
* available.
* ```js
* data: [{
* x: 1,
* title: "A",
* text: "First event"
* }, {
* x: 1,
* title: "B",
* text: "Second event"
* }]
* ```
*
* @type {Array<*>}
* @extends series.line.data
* @excluding dataLabels, marker, name, y
* @product highstock
* @apioption series.flags.data
*/
/**
* The fill color of an individual flag. By default it inherits from
* the series color.
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @product highstock
* @apioption series.flags.data.fillColor
*/
/**
* The longer text to be shown in the flag's tooltip.
*
* @type {string}
* @product highstock
* @apioption series.flags.data.text
*/
/**
* The short text to be shown on the flag.
*
* @type {string}
* @product highstock
* @apioption series.flags.data.title
*/
''; // Keeps doclets above in transpiled file
/* *
*
* Default Export
*
* */
/* harmony default export */ var Flags_FlagsSeriesDefaults = (FlagsSeriesDefaults);
;// ./code/es5/es-modules/Series/Flags/FlagsSymbols.js
/* *
*
* Imports
*
* */
/* *
*
* Composition
*
* */
var FlagsSymbols;
(function (FlagsSymbols) {
/* *
*
* Constants
*
* */
var modifiedMembers = [];
/* *
*
* Functions
*
* */
/* eslint-disable valid-jsdoc */
/**
* @private
*/
function compose(SVGRendererClass) {
if (modifiedMembers.indexOf(SVGRendererClass) === -1) {
modifiedMembers.push(SVGRendererClass);
var symbols = SVGRendererClass.prototype.symbols;
symbols.flag = flag;
createPinSymbol(symbols, 'circle');
createPinSymbol(symbols, 'square');
}
var RendererClass = Renderer_RendererRegistry.getRendererType();
// The symbol callbacks are generated on the SVGRenderer object in all
// browsers.
if (modifiedMembers.indexOf(RendererClass)) {
modifiedMembers.push(RendererClass);
}
}
FlagsSymbols.compose = compose;
/**
* Create the flag icon with anchor.
* @private
*/
function flag(x, y, w, h, options) {
var anchorX = (options && options.anchorX) || x,
anchorY = (options && options.anchorY) || y;
// To do: unwanted any cast because symbols.circle has wrong type, it
// actually returns an SVGPathArray
var path = this.circle(anchorX - 1,
anchorY - 1, 2, 2);
path.push(['M', anchorX, anchorY], ['L', x, y + h], ['L', x, y], ['L', x + w, y], ['L', x + w, y + h], ['L', x, y + h], ['Z']);
return path;
}
/**
* Create the circlepin and squarepin icons with anchor.
* @private
*/
function createPinSymbol(symbols, shape) {
symbols[(shape + 'pin')] = function (x, y, w, h, options) {
var anchorX = options && options.anchorX,
anchorY = options && options.anchorY;
var path;
// For single-letter flags, make sure circular flags are not taller
// than their width
if (shape === 'circle' && h > w) {
x -= Math.round((h - w) / 2);
w = h;
}
path = (symbols[shape])(x, y, w, h, options);
if (anchorX && anchorY) {
/**
* If the label is below the anchor, draw the connecting line
* from the top edge of the label, otherwise start drawing from
* the bottom edge
*/
var labelX = anchorX;
if (shape === 'circle') {
labelX = x + w / 2;
}
else {
var startSeg = path[0];
var endSeg = path[1];
if (startSeg[0] === 'M' && endSeg[0] === 'L') {
labelX = (startSeg[1] + endSeg[1]) / 2;
}
}
var labelY = (y > anchorY) ? y : y + h;
path.push([
'M',
labelX,
labelY
], [
'L',
anchorX,
anchorY
]);
path = path.concat(symbols.circle(anchorX - 1, anchorY - 1, 2, 2));
}
return path;
};
}
})(FlagsSymbols || (FlagsSymbols = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Flags_FlagsSymbols = (FlagsSymbols);
;// ./code/es5/es-modules/Series/OnSeriesComposition.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var OnSeriesComposition_composed = Core_Globals.composed;
var columnProto = Column_ColumnSeries.prototype;
var OnSeriesComposition_seriesProto = Series_Series.prototype;
var OnSeriesComposition_defined = Core_Utilities.defined, OnSeriesComposition_pushUnique = Core_Utilities.pushUnique, OnSeriesComposition_stableSort = Core_Utilities.stableSort;
/* *
*
* Composition
*
* */
var OnSeriesComposition;
(function (OnSeriesComposition) {
/* *
*
* Declarations
*
* */
/* *
*
* Functions
*
* */
/**
* @private
*/
function compose(SeriesClass) {
if (OnSeriesComposition_pushUnique(OnSeriesComposition_composed, 'OnSeries')) {
var seriesProto_1 = SeriesClass.prototype;
seriesProto_1.getPlotBox = getPlotBox;
seriesProto_1.translate = translate;
}
return SeriesClass;
}
OnSeriesComposition.compose = compose;
/**
* Override getPlotBox. If the onSeries option is valid, return the plot box
* of the onSeries, otherwise proceed as usual.
*
* @private
*/
function getPlotBox(name) {
return OnSeriesComposition_seriesProto.getPlotBox.call((this.options.onSeries &&
this.chart.get(this.options.onSeries)) || this, name);
}
OnSeriesComposition.getPlotBox = getPlotBox;
/**
* Extend the translate method by placing the point on the related series
*
* @private
*/
function translate() {
var _a,
_b;
columnProto.translate.apply(this);
var series = this,
options = series.options,
chart = series.chart,
points = series.points,
optionsOnSeries = options.onSeries,
onSeries = (optionsOnSeries &&
chart.get(optionsOnSeries)),
step = onSeries && onSeries.options.step,
onData = (onSeries && onSeries.points),
inverted = chart.inverted,
xAxis = series.xAxis,
yAxis = series.yAxis;
var cursor = points.length - 1,
point,
lastPoint,
onKey = options.onKey || 'y',
i = onData && onData.length,
xOffset = 0,
leftPoint,
lastX,
rightPoint,
currentDataGrouping,
distanceRatio;
// Relate to a master series
if (onSeries && onSeries.visible && i) {
xOffset = (onSeries.pointXOffset || 0) + (onSeries.barW || 0) / 2;
currentDataGrouping = onSeries.currentDataGrouping;
lastX = (onData[i - 1].x +
(currentDataGrouping ? currentDataGrouping.totalRange : 0)); // #2374
// sort the data points
OnSeriesComposition_stableSort(points, function (a, b) { return (a.x - b.x); });
onKey = 'plot' + onKey[0].toUpperCase() + onKey.substr(1);
var _loop_1 = function () {
leftPoint = onData[i];
point = points[cursor];
point.y = leftPoint.y;
if (leftPoint.x <= point.x &&
typeof leftPoint[onKey] !== 'undefined') {
if (point.x <= lastX) { // #803
point.plotY = leftPoint[onKey];
// Interpolate between points, #666
if (leftPoint.x < point.x &&
!step) {
rightPoint = onData[i + 1];
if (rightPoint &&
typeof rightPoint[onKey] !== 'undefined') {
// If the series is spline, calculate Y of the
// point on the bezier line. #19264
if (OnSeriesComposition_defined(point.plotX) &&
onSeries.is('spline')) {
leftPoint = leftPoint;
rightPoint = rightPoint;
var p0_1 = [
leftPoint.plotX || 0,
leftPoint.plotY || 0
],
p3_1 = [
rightPoint.plotX || 0,
rightPoint.plotY || 0
],
p1_1 = (((_a = leftPoint.controlPoints) === null || _a === void 0 ? void 0 : _a.high) ||
p0_1),
p2_1 = (((_b = rightPoint.controlPoints) === null || _b === void 0 ? void 0 : _b.low) ||
p3_1),
pixelThreshold = 0.25,
maxIterations = 100,
calculateCoord = function (t,
key) { return (
// The parametric formula for the
// cubic Bezier curve.
Math.pow(1 - t, 3) * p0_1[key] +
3 * (1 - t) * (1 - t) * t *
p1_1[key] + 3 * (1 - t) * t * t *
p2_1[key] + t * t * t * p3_1[key]); };
var tMin = 0,
tMax = 1,
t = void 0;
// Find `t` of the parametric function of
// the bezier curve for the given `plotX`.
for (var i_1 = 0; i_1 < maxIterations; i_1++) {
var tMid = (tMin + tMax) / 2;
var xMid = calculateCoord(tMid, 0);
if (xMid === null) {
break;
}
if (Math.abs(xMid - point.plotX) < pixelThreshold) {
t = tMid;
break;
}
if (xMid < point.plotX) {
tMin = tMid;
}
else {
tMax = tMid;
}
}
if (OnSeriesComposition_defined(t)) {
point.plotY =
calculateCoord(t, 1);
point.y =
yAxis.toValue(point.plotY, true);
}
}
else {
// The distance ratio, between 0 and 1
distanceRatio =
(point.x - leftPoint.x) /
(rightPoint.x - leftPoint.x);
point.plotY +=
distanceRatio *
// The plotY distance
(rightPoint[onKey] - leftPoint[onKey]);
point.y +=
distanceRatio *
(rightPoint.y - leftPoint.y);
}
}
}
}
cursor--;
i++; // Check again for points in the same x position
if (cursor < 0) {
return "break";
}
}
};
while (i-- && points[cursor]) {
var state_1 = _loop_1();
if (state_1 === "break")
break;
}
}
// Add plotY position and handle stacking
points.forEach(function (point, i) {
var stackIndex;
point.plotX += xOffset; // #2049
// Undefined plotY means the point is either on axis, outside series
// range or hidden series. If the series is outside the range of the
// x axis it should fall through with an undefined plotY, but then
// we must remove the shapeArgs (#847). For inverted charts, we need
// to calculate position anyway, because series.invertGroups is not
// defined
if (typeof point.plotY === 'undefined' || inverted) {
if (point.plotX >= 0 &&
point.plotX <= xAxis.len) {
// We're inside xAxis range
if (inverted) {
point.plotY = xAxis.translate(point.x, 0, 1, 0, 1);
point.plotX = OnSeriesComposition_defined(point.y) ?
yAxis.translate(point.y, 0, 0, 0, 1) :
0;
}
else {
point.plotY = (xAxis.opposite ? 0 : series.yAxis.len) +
xAxis.offset; // For the windbarb demo
}
}
else {
point.shapeArgs = {}; // 847
}
}
// If multiple flags appear at the same x, order them into a stack
lastPoint = points[i - 1];
if (lastPoint && lastPoint.plotX === point.plotX) {
if (typeof lastPoint.stackIndex === 'undefined') {
lastPoint.stackIndex = 0;
}
stackIndex = lastPoint.stackIndex + 1;
}
point.stackIndex = stackIndex; // #3639
});
this.onSeries = onSeries;
}
OnSeriesComposition.translate = translate;
})(OnSeriesComposition || (OnSeriesComposition = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Series_OnSeriesComposition = (OnSeriesComposition);
;// ./code/es5/es-modules/Series/Flags/FlagsSeries.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var FlagsSeries_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d,
b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d,
b) { d.__proto__ = b; }) ||
function (d,
b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b,
p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var FlagsSeries_noop = Core_Globals.noop;
var FlagsSeries_distribute = Renderer_RendererUtilities.distribute;
var FlagsSeries_Series = Series_SeriesRegistry.series, FlagsSeries_ColumnSeries = Series_SeriesRegistry.seriesTypes.column;
var FlagsSeries_addEvent = Core_Utilities.addEvent, FlagsSeries_defined = Core_Utilities.defined, FlagsSeries_extend = Core_Utilities.extend, FlagsSeries_isNumber = Core_Utilities.isNumber, FlagsSeries_merge = Core_Utilities.merge, FlagsSeries_objectEach = Core_Utilities.objectEach, FlagsSeries_wrap = Core_Utilities.wrap;
/* *
*
* Classes
*
* */
/**
* The Flags series.
*
* @private
* @class
* @name Highcharts.seriesTypes.flags
*
* @augments Highcharts.Series
*/
var FlagsSeries = /** @class */ (function (_super) {
FlagsSeries_extends(FlagsSeries, _super);
function FlagsSeries() {
return _super !== null && _super.apply(this, arguments) || this;
}
/* *
*
* Functions
*
* */
/**
* Disable animation, but keep clipping (#8546).
* @private
*/
FlagsSeries.prototype.animate = function (init) {
if (init) {
this.setClip();
}
};
/**
* Draw the markers.
* @private
*/
FlagsSeries.prototype.drawPoints = function () {
var _a,
_b;
var series = this,
points = series.points,
chart = series.chart,
renderer = chart.renderer,
inverted = chart.inverted,
options = series.options,
optionsY = options.y,
yAxis = series.yAxis,
boxesMap = {},
boxes = [],
borderRadius = FlagsSeries_isNumber(options.borderRadius) ?
options.borderRadius : 0;
var plotX,
plotY,
shape,
i,
point,
graphic,
stackIndex,
anchorY,
attribs,
outsideRight,
centered;
i = points.length;
while (i--) {
point = points[i];
outsideRight =
(inverted ? point.plotY : point.plotX) >
series.xAxis.len;
plotX = point.plotX;
stackIndex = point.stackIndex;
shape = point.options.shape || options.shape;
plotY = point.plotY;
if (typeof plotY !== 'undefined') {
plotY = point.plotY + optionsY -
(typeof stackIndex !== 'undefined' &&
(stackIndex * options.stackDistance));
}
// Skip connectors for higher level stacked points
point.anchorX = stackIndex ? void 0 : point.plotX;
anchorY = stackIndex ? void 0 : point.plotY;
centered = shape !== 'flag';
graphic = point.graphic;
// Only draw the point if y is defined and the flag is within
// the visible area
if (typeof plotY !== 'undefined' &&
plotX >= 0 &&
!outsideRight) {
// #15384
if (graphic && point.hasNewShapeType()) {
graphic = graphic.destroy();
}
// Create the flag
if (!graphic) {
graphic = point.graphic = renderer.label('', 0, void 0, shape, void 0, void 0, options.useHTML)
.addClass('highcharts-point')
.add(series.markerGroup);
// Add reference to the point for tracker (#6303)
if (point.graphic.div) {
point.graphic.div.point = point;
}
graphic.isNew = true;
}
graphic.attr({
align: centered ? 'center' : 'left',
width: options.width,
height: options.height,
'text-align': options.textAlign,
r: borderRadius
});
if (!chart.styledMode) {
graphic
.attr(series.pointAttribs(point))
.css(FlagsSeries_merge(options.style, point.style))
.shadow(options.shadow);
}
if (plotX > 0) { // #3119
plotX -= graphic.strokeWidth() % 2; // #4285
}
// Plant the flag
attribs = {
y: plotY,
anchorY: anchorY
};
if (options.allowOverlapX) {
attribs.x = plotX;
attribs.anchorX = point.anchorX;
}
graphic.attr({
// Allow empty string as a flag title (#20549)
text: (_b = (_a = point.options.title) !== null && _a !== void 0 ? _a : options.title) !== null && _b !== void 0 ? _b : 'A'
})[graphic.isNew ? 'attr' : 'animate'](attribs);
// Rig for the distribute function
if (!options.allowOverlapX) {
if (!boxesMap[point.plotX]) {
boxesMap[point.plotX] = {
align: centered ? 0.5 : 0,
size: graphic.width || 0,
target: plotX,
anchorX: plotX
};
}
else {
boxesMap[point.plotX].size = Math.max(boxesMap[point.plotX].size, graphic.width || 0);
}
}
// Set the tooltip anchor position
point.tooltipPos = [
plotX,
plotY + yAxis.pos - chart.plotTop
]; // #6327
}
else if (graphic) {
point.graphic = graphic.destroy();
}
}
// Handle X-dimension overlapping
if (!options.allowOverlapX) {
var maxDistance_1 = 100;
FlagsSeries_objectEach(boxesMap, function (box) {
box.plotX = box.anchorX;
boxes.push(box);
maxDistance_1 = Math.max(box.size, maxDistance_1);
});
// If necessary (for overlapping or long labels) distribute it
// depending on the label width or a hardcoded value, #16041.
FlagsSeries_distribute(boxes, inverted ? yAxis.len : this.xAxis.len, maxDistance_1);
for (var _i = 0, points_1 = points; _i < points_1.length; _i++) {
var point_1 = points_1[_i];
var plotX_1 = point_1.plotX,
graphic_1 = point_1.graphic,
box = graphic_1 && boxesMap[plotX_1];
if (box && graphic_1) {
// Hide flag when its box position is not specified
// (#8573, #9299)
if (!FlagsSeries_defined(box.pos)) {
graphic_1.hide().isNew = true;
}
else {
graphic_1[graphic_1.isNew ? 'attr' : 'animate']({
x: box.pos + (box.align || 0) * box.size,
anchorX: point_1.anchorX
}).show().isNew = false;
}
}
}
}
// Can be a mix of SVG and HTML and we need events for both (#6303)
if (options.useHTML && series.markerGroup) {
FlagsSeries_wrap(series.markerGroup, 'on', function (proceed) {
return SVG_SVGElement.prototype.on.apply(
// For HTML
// eslint-disable-next-line no-invalid-this
proceed.apply(this, [].slice.call(arguments, 1)),
// And for SVG
[].slice.call(arguments, 1));
});
}
};
/**
* Extend the column trackers with listeners to expand and contract
* stacks.
* @private
*/
FlagsSeries.prototype.drawTracker = function () {
var series = this,
points = series.points;
_super.prototype.drawTracker.call(this);
var _loop_1 = function (point) {
var graphic = point.graphic;
if (graphic) {
if (point.unbindMouseOver) {
point.unbindMouseOver();
}
point.unbindMouseOver = FlagsSeries_addEvent(graphic.element, 'mouseover', function () {
// Raise this point
if (point.stackIndex > 0 &&
!point.raised) {
point._y = graphic.y;
graphic.attr({
y: point._y - 8
});
point.raised = true;
}
// Revert other raised points
for (var _i = 0, points_3 = points; _i < points_3.length; _i++) {
var otherPoint = points_3[_i];
if (otherPoint !== point &&
otherPoint.raised &&
otherPoint.graphic) {
otherPoint.graphic.attr({
y: otherPoint._y
});
otherPoint.raised = false;
}
}
});
}
};
/* *
* Bring each stacked flag up on mouse over, this allows readability
* of vertically stacked elements as well as tight points on the x
* axis. #1924.
*/
for (var _i = 0, points_2 = points; _i < points_2.length; _i++) {
var point = points_2[_i];
_loop_1(point);
}
};
/**
* Get presentational attributes
* @private
*/
FlagsSeries.prototype.pointAttribs = function (point, state) {
var options = this.options,
color = (point && point.color) || this.color;
var lineColor = options.lineColor,
lineWidth = (point && point.lineWidth),
fill = (point && point.fillColor) || options.fillColor;
if (state) {
fill = options.states[state].fillColor;
lineColor = options.states[state].lineColor;
lineWidth = options.states[state].lineWidth;
}
return {
fill: fill || color,
stroke: lineColor || color,
'stroke-width': lineWidth || options.lineWidth || 0
};
};
/**
* @private
*/
FlagsSeries.prototype.setClip = function () {
FlagsSeries_Series.prototype.setClip.apply(this, arguments);
if (this.options.clip !== false &&
this.sharedClipKey &&
this.markerGroup) {
this.markerGroup.clip(this.chart.sharedClips[this.sharedClipKey]);
}
};
/* *
*
* Static Properties
*
* */
FlagsSeries.compose = Flags_FlagsSymbols.compose;
FlagsSeries.defaultOptions = FlagsSeries_merge(FlagsSeries_ColumnSeries.defaultOptions, Flags_FlagsSeriesDefaults);
return FlagsSeries;
}(FlagsSeries_ColumnSeries));
Series_OnSeriesComposition.compose(FlagsSeries);
FlagsSeries_extend(FlagsSeries.prototype, {
allowDG: false,
forceCrop: true,
invertible: false, // Flags series group should not be invertible (#14063).
noSharedTooltip: true,
pointClass: Flags_FlagsPoint,
sorted: false,
takeOrdinalPosition: false, // #1074
trackerGroups: ['markerGroup'],
buildKDTree: FlagsSeries_noop,
/**
* Inherit the initialization from base Series.
* @private
*/
init: FlagsSeries_Series.prototype.init
});
Series_SeriesRegistry.registerSeriesType('flags', FlagsSeries);
/* *
*
* Default Export
*
* */
/* harmony default export */ var Flags_FlagsSeries = (FlagsSeries);
/* *
*
* API Declarations
*
* */
/**
* @typedef {"circlepin"|"flag"|"squarepin"} Highcharts.FlagsShapeValue
*/
''; // Detach doclets above
;// ./code/es5/es-modules/Core/Axis/BrokenAxis.js
/* *
*
* (c) 2009-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var BrokenAxis_addEvent = Core_Utilities.addEvent, BrokenAxis_find = Core_Utilities.find, BrokenAxis_fireEvent = Core_Utilities.fireEvent, BrokenAxis_isArray = Core_Utilities.isArray, BrokenAxis_isNumber = Core_Utilities.isNumber, BrokenAxis_pick = Core_Utilities.pick;
/* *
*
* Composition
*
* */
/**
* Axis with support of broken data rows.
* @private
*/
var BrokenAxis;
(function (BrokenAxis) {
/* *
*
* Declarations
*
* */
/* *
*
* Functions
*
* */
/**
* Adds support for broken axes.
* @private
*/
function compose(AxisClass, SeriesClass) {
if (!AxisClass.keepProps.includes('brokenAxis')) {
AxisClass.keepProps.push('brokenAxis');
BrokenAxis_addEvent(AxisClass, 'init', onAxisInit);
BrokenAxis_addEvent(AxisClass, 'afterInit', onAxisAfterInit);
BrokenAxis_addEvent(AxisClass, 'afterSetTickPositions', onAxisAfterSetTickPositions);
BrokenAxis_addEvent(AxisClass, 'afterSetOptions', onAxisAfterSetOptions);
var seriesProto = SeriesClass.prototype;
seriesProto.drawBreaks = seriesDrawBreaks;
seriesProto.gappedPath = seriesGappedPath;
BrokenAxis_addEvent(SeriesClass, 'afterGeneratePoints', onSeriesAfterGeneratePoints);
BrokenAxis_addEvent(SeriesClass, 'afterRender', onSeriesAfterRender);
}
return AxisClass;
}
BrokenAxis.compose = compose;
/**
* @private
*/
function onAxisAfterInit() {
if (typeof this.brokenAxis !== 'undefined') {
this.brokenAxis.setBreaks(this.options.breaks, false);
}
}
/**
* Force Axis to be not-ordinal when breaks are defined.
* @private
*/
function onAxisAfterSetOptions() {
var _a;
var axis = this;
if ((_a = axis.brokenAxis) === null || _a === void 0 ? void 0 : _a.hasBreaks) {
axis.options.ordinal = false;
}
}
/**
* @private
*/
function onAxisAfterSetTickPositions() {
var axis = this,
brokenAxis = axis.brokenAxis;
if (brokenAxis === null || brokenAxis === void 0 ? void 0 : brokenAxis.hasBreaks) {
var tickPositions = axis.tickPositions,
info = axis.tickPositions.info,
newPositions = [];
for (var i = 0; i < tickPositions.length; i++) {
if (!brokenAxis.isInAnyBreak(tickPositions[i])) {
newPositions.push(tickPositions[i]);
}
}
axis.tickPositions = newPositions;
axis.tickPositions.info = info;
}
}
/**
* @private
*/
function onAxisInit() {
var axis = this;
if (!axis.brokenAxis) {
axis.brokenAxis = new Additions(axis);
}
}
/**
* @private
*/
function onSeriesAfterGeneratePoints() {
var _a,
_b;
var _c = this,
isDirty = _c.isDirty,
connectNulls = _c.options.connectNulls,
points = _c.points,
xAxis = _c.xAxis,
yAxis = _c.yAxis;
// Set, or reset visibility of the points. Axis.setBreaks marks
// the series as isDirty
if (isDirty) {
var i = points.length;
while (i--) {
var point = points[i];
// Respect nulls inside the break (#4275)
var nullGap = point.y === null && connectNulls === false;
var isPointInBreak = (!nullGap && (((_a = xAxis === null || xAxis === void 0 ? void 0 : xAxis.brokenAxis) === null || _a === void 0 ? void 0 : _a.isInAnyBreak(point.x,
true)) ||
((_b = yAxis === null || yAxis === void 0 ? void 0 : yAxis.brokenAxis) === null || _b === void 0 ? void 0 : _b.isInAnyBreak(point.y,
true))));
// Set point.visible if in any break.
// If not in break, reset visible to original value.
point.visible = isPointInBreak ?
false :
point.options.visible !== false;
}
}
}
/**
* @private
*/
function onSeriesAfterRender() {
this.drawBreaks(this.xAxis, ['x']);
this.drawBreaks(this.yAxis, BrokenAxis_pick(this.pointArrayMap, ['y']));
}
/**
* @private
*/
function seriesDrawBreaks(axis, keys) {
var _a;
var series = this,
points = series.points;
var breaks,
threshold,
y;
if ((_a = axis === null || axis === void 0 ? void 0 : axis.brokenAxis) === null || _a === void 0 ? void 0 : _a.hasBreaks) {
var brokenAxis_1 = axis.brokenAxis;
keys.forEach(function (key) {
var _a,
_b;
breaks = (brokenAxis_1 === null || brokenAxis_1 === void 0 ? void 0 : brokenAxis_1.breakArray) || [];
threshold = axis.isXAxis ?
axis.min :
BrokenAxis_pick(series.options.threshold, axis.min);
// Array of breaks that have been "zoomed-out" which means that
// they were shown previously, but now after zoom, they are not
// (#19885).
var breaksOutOfRange = (_b = (_a = axis === null || axis === void 0 ? void 0 : axis.options) === null || _a === void 0 ? void 0 : _a.breaks) === null || _b === void 0 ? void 0 : _b.filter(function (brk) {
var isOut = true;
// Iterate to see if "brk" is in axis range
for (var i = 0; i < breaks.length; i++) {
var otherBreak = breaks[i];
if (otherBreak.from === brk.from &&
otherBreak.to === brk.to) {
isOut = false;
break;
}
}
return isOut;
});
points.forEach(function (point) {
y = BrokenAxis_pick(point['stack' + key.toUpperCase()], point[key]);
breaks.forEach(function (brk) {
if (BrokenAxis_isNumber(threshold) && BrokenAxis_isNumber(y)) {
var eventName = '';
if ((threshold < brk.from && y > brk.to) ||
(threshold > brk.from && y < brk.from)) {
eventName = 'pointBreak';
}
else if ((threshold < brk.from &&
y > brk.from &&
y < brk.to) || (threshold > brk.from &&
y > brk.to &&
y < brk.from)) {
eventName = 'pointInBreak';
}
if (eventName) {
BrokenAxis_fireEvent(axis, eventName, { point: point, brk: brk });
}
}
});
breaksOutOfRange === null || breaksOutOfRange === void 0 ? void 0 : breaksOutOfRange.forEach(function (brk) {
BrokenAxis_fireEvent(axis, 'pointOutsideOfBreak', { point: point, brk: brk });
});
});
});
}
}
/**
* Extend getGraphPath by identifying gaps in the data so that we
* can draw a gap in the line or area. This was moved from ordinal
* axis module to broken axis module as of #5045.
*
* @private
* @function Highcharts.Series#gappedPath
*
* @return {Highcharts.SVGPathArray}
* Gapped path
*/
function seriesGappedPath() {
var currentDataGrouping = this.currentDataGrouping,
groupingSize = currentDataGrouping === null || currentDataGrouping === void 0 ? void 0 : currentDataGrouping.gapSize,
points = this.points.slice(),
yAxis = this.yAxis;
var gapSize = this.options.gapSize,
i = points.length - 1,
stack;
/**
* Defines when to display a gap in the graph, together with the
* [gapUnit](plotOptions.series.gapUnit) option.
*
* In case when `dataGrouping` is enabled, points can be grouped
* into a larger time span. This can make the grouped points to
* have a greater distance than the absolute value of `gapSize`
* property, which will result in disappearing graph completely.
* To prevent this situation the mentioned distance between
* grouped points is used instead of previously defined
* `gapSize`.
*
* In practice, this option is most often used to visualize gaps
* in time series. In a stock chart, intraday data is available
* for daytime hours, while gaps will appear in nights and
* weekends.
*
* @see [gapUnit](plotOptions.series.gapUnit)
* @see [xAxis.breaks](#xAxis.breaks)
*
* @sample {highstock} stock/plotoptions/series-gapsize/
* Setting the gap size to 2 introduces gaps for weekends in
* daily datasets.
*
* @type {number}
* @default 0
* @product highstock
* @requires modules/broken-axis
* @apioption plotOptions.series.gapSize
*/
/**
* Together with [gapSize](plotOptions.series.gapSize), this
* option defines where to draw gaps in the graph.
*
* When the `gapUnit` is `"relative"` (default), a gap size of 5
* means that if the distance between two points is greater than
* 5 times that of the two closest points, the graph will be
* broken.
*
* When the `gapUnit` is `"value"`, the gap is based on absolute
* axis values, which on a datetime axis is milliseconds. This
* also applies to the navigator series that inherits gap
* options from the base series.
*
* @see [gapSize](plotOptions.series.gapSize)
*
* @type {string}
* @default relative
* @since 5.0.13
* @product highstock
* @validvalue ["relative", "value"]
* @requires modules/broken-axis
* @apioption plotOptions.series.gapUnit
*/
if (gapSize && i > 0) { // #5008
// Gap unit is relative
if (this.options.gapUnit !== 'value') {
gapSize *= this.basePointRange;
}
// Setting a new gapSize in case dataGrouping is enabled
// (#7686)
if (groupingSize &&
groupingSize > gapSize &&
// Except when DG is forced (e.g. from other series)
// and has lower granularity than actual points (#11351)
groupingSize >= this.basePointRange) {
gapSize = groupingSize;
}
// Extension for ordinal breaks
var current = void 0,
next = void 0;
while (i--) {
// Reassign next if it is not visible
if (!(next && next.visible !== false)) {
next = points[i + 1];
}
current = points[i];
// Skip iteration if one of the points is not visible
if (next.visible === false || current.visible === false) {
continue;
}
if (next.x - current.x > gapSize) {
var xRange = (current.x + next.x) / 2;
points.splice(// Insert after this one
i + 1, 0, {
isNull: true,
x: xRange
});
// For stacked chart generate empty stack items, #6546
if (yAxis.stacking && this.options.stacking) {
stack = yAxis.stacking.stacks[this.stackKey][xRange] = new Stacking_StackItem(yAxis, yAxis.options.stackLabels, false, xRange, this.stack);
stack.total = 0;
}
}
// Assign current to next for the upcoming iteration
next = current;
}
}
// Call base method
return this.getGraphPath(points);
}
/* *
*
* Class
*
* */
/**
* Provides support for broken axes.
* @private
* @class
*/
var Additions = /** @class */ (function () {
/* *
*
* Constructors
*
* */
function Additions(axis) {
this.hasBreaks = false;
this.axis = axis;
}
/* *
*
* Static Functions
*
* */
/**
* @private
*/
Additions.isInBreak = function (brk, val) {
var repeat = brk.repeat || Infinity,
from = brk.from,
length = brk.to - brk.from,
test = (val >= from ?
(val - from) % repeat :
repeat - ((from - val) % repeat));
var ret;
if (!brk.inclusive) {
ret = test < length && test !== 0;
}
else {
ret = test <= length;
}
return ret;
};
/**
* @private
*/
Additions.lin2Val = function (val) {
var axis = this;
var brokenAxis = axis.brokenAxis;
var breakArray = brokenAxis && brokenAxis.breakArray;
if (!breakArray || !BrokenAxis_isNumber(val)) {
return val;
}
var nval = val,
brk,
i;
for (i = 0; i < breakArray.length; i++) {
brk = breakArray[i];
if (brk.from >= nval) {
break;
}
else if (brk.to < nval) {
nval += brk.len;
}
else if (Additions.isInBreak(brk, nval)) {
nval += brk.len;
}
}
return nval;
};
/**
* @private
*/
Additions.val2Lin = function (val) {
var axis = this;
var brokenAxis = axis.brokenAxis;
var breakArray = brokenAxis && brokenAxis.breakArray;
if (!breakArray || !BrokenAxis_isNumber(val)) {
return val;
}
var nval = val,
brk,
i;
for (i = 0; i < breakArray.length; i++) {
brk = breakArray[i];
if (brk.to <= val) {
nval -= brk.len;
}
else if (brk.from >= val) {
break;
}
else if (Additions.isInBreak(brk, val)) {
nval -= (val - brk.from);
break;
}
}
return nval;
};
/* *
*
* Functions
*
* */
/**
* Returns the first break found where the x is larger then break.from
* and smaller then break.to.
*
* @param {number} x
* The number which should be within a break.
*
* @param {Array<Highcharts.XAxisBreaksOptions>} breaks
* The array of breaks to search within.
*
* @return {Highcharts.XAxisBreaksOptions|undefined}
* Returns the first break found that matches, returns false if no break
* is found.
*/
Additions.prototype.findBreakAt = function (x, breaks) {
return BrokenAxis_find(breaks, function (b) {
return b.from < x && x < b.to;
});
};
/**
* @private
*/
Additions.prototype.isInAnyBreak = function (val, testKeep) {
var brokenAxis = this,
axis = brokenAxis.axis,
breaks = axis.options.breaks || [];
var i = breaks.length,
inbrk,
keep,
ret;
if (i && BrokenAxis_isNumber(val)) {
while (i--) {
if (Additions.isInBreak(breaks[i], val)) {
inbrk = true;
if (!keep) {
keep = BrokenAxis_pick(breaks[i].showPoints, !axis.isXAxis);
}
}
}
if (inbrk && testKeep) {
ret = inbrk && !keep;
}
else {
ret = inbrk;
}
}
return ret;
};
/**
* Dynamically set or unset breaks in an axis. This function in lighter
* than using Axis.update, and it also preserves animation.
*
* @private
* @function Highcharts.Axis#setBreaks
*
* @param {Array<Highcharts.XAxisBreaksOptions>} [breaks]
* The breaks to add. When `undefined` it removes existing breaks.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart immediately.
*/
Additions.prototype.setBreaks = function (breaks, redraw) {
var brokenAxis = this,
axis = brokenAxis.axis,
time = axis.chart.time,
hasBreaks = BrokenAxis_isArray(breaks) &&
!!breaks.length &&
!!Object.keys(breaks[0]).length; // Check for [{}], #16368.
axis.isDirty = brokenAxis.hasBreaks !== hasBreaks;
brokenAxis.hasBreaks = hasBreaks;
// Compile string dates
breaks === null || breaks === void 0 ? void 0 : breaks.forEach(function (brk) {
brk.from = time.parse(brk.from) || 0;
brk.to = time.parse(brk.to) || 0;
});
if (breaks !== axis.options.breaks) {
axis.options.breaks = axis.userOptions.breaks = breaks;
}
axis.forceRedraw = true; // Force recalculation in setScale
// Recalculate series related to the axis.
axis.series.forEach(function (series) {
series.isDirty = true;
});
if (!hasBreaks && axis.val2lin === Additions.val2Lin) {
// Revert to prototype functions
delete axis.val2lin;
delete axis.lin2val;
}
if (hasBreaks) {
axis.userOptions.ordinal = false;
axis.lin2val = Additions.lin2Val;
axis.val2lin = Additions.val2Lin;
axis.setExtremes = function (newMin, newMax, redraw, animation, eventArguments) {
// If trying to set extremes inside a break, extend min to
// after, and max to before the break ( #3857 )
if (brokenAxis.hasBreaks) {
var breaks_1 = (this.options.breaks || []);
var axisBreak = void 0;
while ((axisBreak = brokenAxis.findBreakAt(newMin, breaks_1))) {
newMin = axisBreak.to;
}
while ((axisBreak = brokenAxis.findBreakAt(newMax, breaks_1))) {
newMax = axisBreak.from;
}
// If both min and max is within the same break.
if (newMax < newMin) {
newMax = newMin;
}
}
axis.constructor.prototype.setExtremes.call(this, newMin, newMax, redraw, animation, eventArguments);
};
axis.setAxisTranslation = function () {
axis.constructor.prototype.setAxisTranslation.call(this);
brokenAxis.unitLength = void 0;
if (brokenAxis.hasBreaks) {
var breaks_2 = axis.options.breaks || [],
// Temporary one:
breakArrayT_1 = [],
breakArray_1 = [],
pointRangePadding = BrokenAxis_pick(axis.pointRangePadding, 0);
var length_1 = 0,
inBrk_1,
repeat_1,
min_1 = axis.userMin || axis.min,
max_1 = axis.userMax || axis.max,
start_1,
i_1;
// Min & max check (#4247)
breaks_2.forEach(function (brk) {
repeat_1 = brk.repeat || Infinity;
if (BrokenAxis_isNumber(min_1) && BrokenAxis_isNumber(max_1)) {
if (Additions.isInBreak(brk, min_1)) {
min_1 += ((brk.to % repeat_1) -
(min_1 % repeat_1));
}
if (Additions.isInBreak(brk, max_1)) {
max_1 -= ((max_1 % repeat_1) -
(brk.from % repeat_1));
}
}
});
// Construct an array holding all breaks in the axis
breaks_2.forEach(function (brk) {
start_1 = brk.from;
repeat_1 = brk.repeat || Infinity;
if (BrokenAxis_isNumber(min_1) && BrokenAxis_isNumber(max_1)) {
while (start_1 - repeat_1 > min_1) {
start_1 -= repeat_1;
}
while (start_1 < min_1) {
start_1 += repeat_1;
}
for (i_1 = start_1; i_1 < max_1; i_1 += repeat_1) {
breakArrayT_1.push({
value: i_1,
move: 'in'
});
breakArrayT_1.push({
value: i_1 + brk.to - brk.from,
move: 'out',
size: brk.breakSize
});
}
}
});
breakArrayT_1.sort(function (a, b) {
return ((a.value === b.value) ?
((a.move === 'in' ? 0 : 1) -
(b.move === 'in' ? 0 : 1)) :
a.value - b.value);
});
// Simplify the breaks
inBrk_1 = 0;
start_1 = min_1;
breakArrayT_1.forEach(function (brk) {
inBrk_1 += (brk.move === 'in' ? 1 : -1);
if (inBrk_1 === 1 && brk.move === 'in') {
start_1 = brk.value;
}
if (inBrk_1 === 0 && BrokenAxis_isNumber(start_1)) {
breakArray_1.push({
from: start_1,
to: brk.value,
len: brk.value - start_1 - (brk.size || 0)
});
length_1 += (brk.value -
start_1 -
(brk.size || 0));
}
});
brokenAxis.breakArray = breakArray_1;
// Used with staticScale, and below the actual axis
// length, when breaks are subtracted.
if (BrokenAxis_isNumber(min_1) &&
BrokenAxis_isNumber(max_1) &&
BrokenAxis_isNumber(axis.min)) {
brokenAxis.unitLength = max_1 - min_1 - length_1 +
pointRangePadding;
BrokenAxis_fireEvent(axis, 'afterBreaks');
if (axis.staticScale) {
axis.transA = axis.staticScale;
}
else if (brokenAxis.unitLength) {
axis.transA *=
(max_1 - axis.min + pointRangePadding) /
brokenAxis.unitLength;
}
if (pointRangePadding) {
axis.minPixelPadding =
axis.transA * (axis.minPointOffset || 0);
}
axis.min = min_1;
axis.max = max_1;
}
}
};
}
if (BrokenAxis_pick(redraw, true)) {
axis.chart.redraw();
}
};
return Additions;
}());
BrokenAxis.Additions = Additions;
})(BrokenAxis || (BrokenAxis = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ var Axis_BrokenAxis = (BrokenAxis);
;// ./code/es5/es-modules/masters/modules/broken-axis.src.js
/**
* @license Highcharts JS v12.1.2 (2025-01-09)
* @module highcharts/modules/broken-axis
* @requires highcharts
*
* (c) 2009-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*/
var broken_axis_src_G = Core_Globals;
broken_axis_src_G.BrokenAxis = broken_axis_src_G.BrokenAxis || Axis_BrokenAxis;
broken_axis_src_G.BrokenAxis.compose(broken_axis_src_G.Axis, broken_axis_src_G.Series);
/* harmony default export */ var broken_axis_src = ((/* unused pure expression or super */ null && (Highcharts)));
;// ./code/es5/es-modules/Extensions/DataGrouping/ApproximationRegistry.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* Constants
*
* */
/**
* Define the available approximation types. The data grouping
* approximations takes an array or numbers as the first parameter. In case
* of ohlc, four arrays are sent in as four parameters. Each array consists
* only of numbers. In case null values belong to the group, the property
* .hasNulls will be set to true on the array.
*
* @product highstock
*
* @private
*/
var ApproximationRegistry = {
// Approximations added programmatically
};
/* *
*
* Default Export
*
* */
/* harmony default export */ var DataGrouping_ApproximationRegistry = (ApproximationRegistry);
;// ./code/es5/es-modules/Extensions/DataGrouping/ApproximationDefaults.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var ApproximationDefaults_arrayMax = Core_Utilities.arrayMax, ApproximationDefaults_arrayMin = Core_Utilities.arrayMin, ApproximationDefaults_correctFloat = Core_Utilities.correctFloat, ApproximationDefaults_extend = Core_Utilities.extend, ApproximationDefaults_isNumber = Core_Utilities.isNumber;
/* *
*
* Functions
*
* */
/**
* @private
*/
function average(arr) {
var len = arr.length;
var ret = sum(arr);
// If we have a number, return it divided by the length. If not,
// return null or undefined based on what the sum method finds.
if (ApproximationDefaults_isNumber(ret) && len) {
ret = ApproximationDefaults_correctFloat(ret / len);
}
return ret;
}
/**
* The same as average, but for series with multiple values, like area ranges.
* @private
*/
function averages() {
var ret = [];
[].forEach.call(arguments, function (arr) {
ret.push(average(arr));
});
// Return undefined when first elem. is undefined and let
// sum method handle null (#7377)
return typeof ret[0] === 'undefined' ? void 0 : ret;
}
/**
* @private
*/
function ApproximationDefaults_close(arr) {
return arr.length ?
arr[arr.length - 1] :
(arr.hasNulls ? null : void 0);
}
/**
* @private
*/
function high(arr) {
return arr.length ?
ApproximationDefaults_arrayMax(arr) :
(arr.hasNulls ? null : void 0);
}
/**
* HLC, OHLC and range are special cases where a multidimensional array is input
* and an array is output.
* @private
*/
function hlc(high, low, close) {
high = DataGrouping_ApproximationRegistry.high(high);
low = DataGrouping_ApproximationRegistry.low(low);
close = DataGrouping_ApproximationRegistry.close(close);
if (ApproximationDefaults_isNumber(high) ||
ApproximationDefaults_isNumber(low) ||
ApproximationDefaults_isNumber(close)) {
return [high, low, close];
}
}
/**
* @private
*/
function low(arr) {
return arr.length ?
ApproximationDefaults_arrayMin(arr) :
(arr.hasNulls ? null : void 0);
}
/**
* @private
*/
function ohlc(open, high, low, close) {
open = DataGrouping_ApproximationRegistry.open(open);
high = DataGrouping_ApproximationRegistry.high(high);
low = DataGrouping_ApproximationRegistry.low(low);
close = DataGrouping_ApproximationRegistry.close(close);
if (ApproximationDefaults_isNumber(open) ||
ApproximationDefaults_isNumber(high) ||
ApproximationDefaults_isNumber(low) ||
ApproximationDefaults_isNumber(close)) {
return [open, high, low, close];
}
}
/**
* @private
*/
function ApproximationDefaults_open(arr) {
return arr.length ? arr[0] : (arr.hasNulls ? null : void 0);
}
/**
* @private
*/
function range(low, high) {
low = DataGrouping_ApproximationRegistry.low(low);
high = DataGrouping_ApproximationRegistry.high(high);
if (ApproximationDefaults_isNumber(low) || ApproximationDefaults_isNumber(high)) {
return [low, high];
}
if (low === null && high === null) {
return null;
}
// Else, return is undefined
}
/**
* @private
*/
function sum(arr) {
var len = arr.length,
ret;
// 1. it consists of nulls exclusive
if (!len && arr.hasNulls) {
ret = null;
// 2. it has a length and real values
}
else if (len) {
ret = 0;
while (len--) {
ret += arr[len];
}
}
// 3. it has zero length, so just return undefined
// => doNothing()
return ret;
}
/* *
*
* Default Export
*
* */
var ApproximationDefaults = {
average: average,
averages: averages,
close: ApproximationDefaults_close,
high: high,
hlc: hlc,
low: low,
ohlc: ohlc,
open: ApproximationDefaults_open,
range: range,
sum: sum
};
ApproximationDefaults_extend(DataGrouping_ApproximationRegistry, ApproximationDefaults);
/* harmony default export */ var DataGrouping_ApproximationDefaults = (ApproximationDefaults);
;// ./code/es5/es-modules/Extensions/DataGrouping/DataGroupingDefaults.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* Constants
*
* */
/**
* Common options
* @private
*/
var common = {
/// enabled: null, // (true for stock charts, false for basic),
// forced: undefined,
groupPixelWidth: 2,
// The first one is the point or start value, the second is the start
// value if we're dealing with range, the third one is the end value if
// dealing with a range
dateTimeLabelFormats: {
millisecond: [
'%[AebHMSL]',
'%[AebHMSL]',
'-%[HMSL]'
],
second: [
'%[AebHMS]',
'%[AebHMS]',
'-%[HMS]'
],
minute: [
'%[AebHM]',
'%[AebHM]',
'-%[HM]'
],
hour: [
'%[AebHM]',
'%[AebHM]',
'-%[HM]'
],
day: [
'%[AebY]',
'%[Aeb]',
'-%[AebY]'
],
week: [
'week from %[AebY]',
'%[Aeb]',
'-%[AebY]'
],
month: [
'%[BY]',
'%[B]',
'-%[BY]'
],
year: [
'%Y',
'%Y',
'-%Y'
]
}
/// smoothed = false, // enable this for navigator series only
};
/**
* Extends common options
* @private
*/
var seriesSpecific = {
line: {},
spline: {},
area: {},
areaspline: {},
arearange: {},
column: {
groupPixelWidth: 10
},
columnrange: {
groupPixelWidth: 10
},
candlestick: {
groupPixelWidth: 10
},
ohlc: {
groupPixelWidth: 5
},
hlc: {
groupPixelWidth: 5
// Move to HeikinAshiSeries.ts after refactoring data grouping.
},
heikinashi: {
groupPixelWidth: 10
}
};
/**
* Units are defined in a separate array to allow complete overriding in
* case of a user option.
* @private
*/
var units = [
[
'millisecond', // Unit name
[1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // Allowed multiples
], [
'second',
[1, 2, 5, 10, 15, 30]
], [
'minute',
[1, 2, 5, 10, 15, 30]
], [
'hour',
[1, 2, 3, 4, 6, 8, 12]
], [
'day',
[1]
], [
'week',
[1]
], [
'month',
[1, 3, 6]
], [
'year',
null
]
];
/* *
*
* Default Export
*
* */
var DataGroupingDefaults = {
common: common,
seriesSpecific: seriesSpecific,
units: units
};
/* harmony default export */ var DataGrouping_DataGroupingDefaults = (DataGroupingDefaults);
;// ./code/es5/es-modules/Extensions/DataGrouping/DataGroupingAxisComposition.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var DataGroupingAxisComposition_addEvent = Core_Utilities.addEvent, DataGroupingAxisComposition_extend = Core_Utilities.extend, DataGroupingAxisComposition_merge = Core_Utilities.merge, DataGroupingAxisComposition_pick = Core_Utilities.pick;
/* *
*
* Variables
*
* */
var AxisConstructor;
/* *
*
* Functions
*
* */
/**
* Check the groupPixelWidth and apply the grouping if needed.
* Fired only after processing the data.
*
* @product highstock
*
* @function Highcharts.Axis#applyGrouping
*/
function applyGrouping(e) {
var axis = this,
series = axis.series;
// Reset the groupPixelWidth for all series, #17141.
series.forEach(function (series) {
series.groupPixelWidth = void 0; // #2110
});
series.forEach(function (series) {
series.groupPixelWidth = (axis.getGroupPixelWidth &&
axis.getGroupPixelWidth());
if (series.groupPixelWidth) {
series.hasProcessed = true; // #2692
}
// Fire independing on series.groupPixelWidth to always set a proper
// dataGrouping state, (#16238)
series.applyGrouping(!!e.hasExtremesChanged);
});
}
/**
* @private
*/
function DataGroupingAxisComposition_compose(AxisClass) {
AxisConstructor = AxisClass;
var axisProto = AxisClass.prototype;
if (!axisProto.applyGrouping) {
DataGroupingAxisComposition_addEvent(AxisClass, 'afterSetScale', onAfterSetScale);
// When all series are processed, calculate the group pixel width and
// then if this value is different than zero apply groupings.
DataGroupingAxisComposition_addEvent(AxisClass, 'postProcessData', applyGrouping);
DataGroupingAxisComposition_extend(axisProto, {
applyGrouping: applyGrouping,
getGroupPixelWidth: getGroupPixelWidth,
setDataGrouping: setDataGrouping
});
}
}
/**
* Get the data grouping pixel width based on the greatest defined individual
* width of the axis' series, and if whether one of the axes need grouping.
* @private
*/
function getGroupPixelWidth() {
var series = this.series;
var i = series.length,
groupPixelWidth = 0,
doGrouping = false,
dataLength,
dgOptions;
// If one of the series needs grouping, apply it to all (#1634)
while (i--) {
dgOptions = series[i].options.dataGrouping;
if (dgOptions) { // #2692
// If multiple series are compared on the same x axis, give them the
// same group pixel width (#334)
groupPixelWidth = Math.max(groupPixelWidth,
// Fallback to commonOptions (#9693)
DataGroupingAxisComposition_pick(dgOptions.groupPixelWidth, DataGrouping_DataGroupingDefaults.common.groupPixelWidth));
dataLength = (series[i].dataTable.modified ||
series[i].dataTable).rowCount;
// Execute grouping if the amount of points is greater than the
// limit defined in groupPixelWidth
if (series[i].groupPixelWidth ||
(dataLength >
(this.chart.plotSizeX / groupPixelWidth)) ||
(dataLength && dgOptions.forced)) {
doGrouping = true;
}
}
}
return doGrouping ? groupPixelWidth : 0;
}
/**
* When resetting the scale reset the hasProcessed flag to avoid taking
* previous data grouping of neighbour series into account when determining
* group pixel width (#2692).
* @private
*/
function onAfterSetScale() {
this.series.forEach(function (series) {
series.hasProcessed = false;
});
}
/**
* Highcharts Stock only. Force data grouping on all the axis' series.
*
* @product highstock
*
* @function Highcharts.Axis#setDataGrouping
*
* @param {boolean|Highcharts.DataGroupingOptionsObject} [dataGrouping]
* A `dataGrouping` configuration. Use `false` to disable data grouping
* dynamically.
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart or wait for a later call to
* {@link Chart#redraw}.
*/
function setDataGrouping(dataGrouping, redraw) {
var axis = this;
var i;
redraw = DataGroupingAxisComposition_pick(redraw, true);
if (!dataGrouping) {
dataGrouping = {
forced: false,
units: null
};
}
// Axis is instantiated, update all series
if (this instanceof AxisConstructor) {
i = this.series.length;
while (i--) {
this.series[i].update({
dataGrouping: dataGrouping
}, false);
}
// Axis not yet instantiated, alter series options
}
else {
this.chart.options.series.forEach(function (seriesOptions) {
// Merging dataGrouping options with already defined options #16759
seriesOptions.dataGrouping = typeof dataGrouping === 'boolean' ?
dataGrouping :
DataGroupingAxisComposition_merge(dataGrouping, seriesOptions.dataGrouping);
});
}
// Clear ordinal slope, so we won't accidentally use the old one (#7827)
if (axis.ordinal) {
axis.ordinal.slope = void 0;
}
if (redraw) {
this.chart.redraw();
}
}
/* *
*
* Default Export
*
* */
var DataGroupingAxisComposition = {
compose: DataGroupingAxisComposition_compose
};
/* harmony default export */ var DataGrouping_DataGroupingAxisComposition = (DataGroupingAxisComposition);
;// ./code/es5/es-modules/Extensions/DataGrouping/DataGroupingSeriesComposition.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var DataGroupingSeriesComposition_seriesProto = Series_SeriesRegistry.series.prototype;
var DataGroupingSeriesComposition_addEvent = Core_Utilities.addEvent, DataGroupingSeriesComposition_defined = Core_Utilities.defined, DataGroupingSeriesComposition_error = Core_Utilities.error, DataGroupingSeriesComposition_extend = Core_Utilities.extend, DataGroupingSeriesComposition_isNumber = Core_Utilities.isNumber, DataGroupingSeriesComposition_merge = Core_Utilities.merge, DataGroupingSeriesComposition_pick = Core_Utilities.pick, DataGroupingSeriesComposition_splat = Core_Utilities.splat;
/* *
*
* Constants
*
* */
var baseGeneratePoints = DataGroupingSeriesComposition_seriesProto.generatePoints;
/* *
*
* Functions
*
* */
/**
* @private
*/
function adjustExtremes(xAxis, groupedXData) {
// Make sure the X axis extends to show the first group (#2533)
// But only for visible series (#5493, #6393)
if (DataGroupingSeriesComposition_defined(groupedXData[0]) &&
DataGroupingSeriesComposition_isNumber(xAxis.min) &&
DataGroupingSeriesComposition_isNumber(xAxis.dataMin) &&
groupedXData[0] < xAxis.min) {
if ((!DataGroupingSeriesComposition_defined(xAxis.options.min) &&
xAxis.min <= xAxis.dataMin) ||
xAxis.min === xAxis.dataMin) {
xAxis.min = Math.min(groupedXData[0], xAxis.min);
}
xAxis.dataMin = Math.min(groupedXData[0], xAxis.dataMin);
}
// When the last anchor set, change the extremes that
// the last point is visible (#12455).
if (DataGroupingSeriesComposition_defined(groupedXData[groupedXData.length - 1]) &&
DataGroupingSeriesComposition_isNumber(xAxis.max) &&
DataGroupingSeriesComposition_isNumber(xAxis.dataMax) &&
groupedXData[groupedXData.length - 1] > xAxis.max) {
if ((!DataGroupingSeriesComposition_defined(xAxis.options.max) &&
DataGroupingSeriesComposition_isNumber(xAxis.dataMax) &&
xAxis.max >= xAxis.dataMax) || xAxis.max === xAxis.dataMax) {
xAxis.max = Math.max(groupedXData[groupedXData.length - 1], xAxis.max);
}
xAxis.dataMax = Math.max(groupedXData[groupedXData.length - 1], xAxis.dataMax);
}
}
/**
* @private
*/
function anchorPoints(series, groupedXData, xMax) {
var options = series.options,
dataGroupingOptions = options.dataGrouping,
totalRange = (series.currentDataGrouping && series.currentDataGrouping.gapSize),
xData = series.getColumn('x');
if (!(dataGroupingOptions &&
xData.length &&
totalRange &&
series.groupMap)) {
return;
}
var groupedDataLastIndex = groupedXData.length - 1,
anchor = dataGroupingOptions.anchor,
firstAnchor = dataGroupingOptions.firstAnchor,
lastAnchor = dataGroupingOptions.lastAnchor;
var anchorIndexIterator = groupedXData.length - 1,
anchorFirstIndex = 0;
// Change the first point position, but only when it is
// the first point in the data set not in the current zoom.
if (firstAnchor && xData[0] >= groupedXData[0]) {
anchorFirstIndex++;
var groupStart = series.groupMap[0].start,
groupLength = series.groupMap[0].length;
var firstGroupEnd = void 0;
if (DataGroupingSeriesComposition_isNumber(groupStart) && DataGroupingSeriesComposition_isNumber(groupLength)) {
firstGroupEnd = groupStart + (groupLength - 1);
}
groupedXData[0] = {
start: groupedXData[0],
middle: groupedXData[0] + 0.5 * totalRange,
end: groupedXData[0] + totalRange,
firstPoint: xData[0],
lastPoint: firstGroupEnd && xData[firstGroupEnd]
}[firstAnchor];
}
// Change the last point position but only when it is
// the last point in the data set not in the current zoom,
// or if it is not the 1st point simultaneously.
if (groupedDataLastIndex > 0 &&
lastAnchor &&
totalRange &&
groupedXData[groupedDataLastIndex] >= xMax - totalRange) {
anchorIndexIterator--;
var lastGroupStart = series.groupMap[series.groupMap.length - 1].start;
groupedXData[groupedDataLastIndex] = {
start: groupedXData[groupedDataLastIndex],
middle: groupedXData[groupedDataLastIndex] + 0.5 * totalRange,
end: groupedXData[groupedDataLastIndex] + totalRange,
firstPoint: lastGroupStart && xData[lastGroupStart],
lastPoint: xData[xData.length - 1]
}[lastAnchor];
}
if (anchor && anchor !== 'start') {
var shiftInterval = (totalRange *
{ middle: 0.5,
end: 1 }[anchor]);
// Anchor the rest of the points apart from the ones, that were
// previously moved.
while (anchorIndexIterator >= anchorFirstIndex) {
groupedXData[anchorIndexIterator] += shiftInterval;
anchorIndexIterator--;
}
}
}
/**
* For the processed data, calculate the grouped data if needed.
*
* @private
* @function Highcharts.Series#applyGrouping
*/
function DataGroupingSeriesComposition_applyGrouping(hasExtremesChanged) {
var series = this,
chart = series.chart,
options = series.options,
dataGroupingOptions = options.dataGrouping,
groupingEnabled = series.allowDG !== false && dataGroupingOptions &&
DataGroupingSeriesComposition_pick(dataGroupingOptions.enabled,
chart.options.isStock),
reserveSpace = series.reserveSpace(),
lastDataGrouping = this.currentDataGrouping;
var currentDataGrouping,
croppedData,
revertRequireSorting = false;
// Data needs to be sorted for dataGrouping
if (groupingEnabled && !series.requireSorting) {
series.requireSorting = revertRequireSorting = true;
}
// Skip if skipDataGrouping method returns false or if grouping is disabled
// (in that order).
var skip = skipDataGrouping(series,
hasExtremesChanged) === false || !groupingEnabled;
// Revert original requireSorting value if changed
if (revertRequireSorting) {
series.requireSorting = false;
}
if (skip) {
return;
}
series.destroyGroupedData();
var table = dataGroupingOptions.groupAll ?
series.dataTable :
series.dataTable.modified || series.dataTable,
processedXData = series.getColumn('x', !dataGroupingOptions.groupAll),
xData = processedXData,
plotSizeX = chart.plotSizeX,
xAxis = series.xAxis,
extremes = xAxis.getExtremes(),
ordinal = xAxis.options.ordinal,
groupPixelWidth = series.groupPixelWidth;
var i,
hasGroupedData;
// Execute grouping if the amount of points is greater than the limit
// defined in groupPixelWidth
if (groupPixelWidth &&
xData &&
table.rowCount &&
plotSizeX &&
DataGroupingSeriesComposition_isNumber(extremes.min)) {
hasGroupedData = true;
// Force recreation of point instances in series.translate, #5699
series.isDirty = true;
series.points = null; // #6709
var xMin = extremes.min,
xMax = extremes.max,
groupIntervalFactor = (ordinal &&
xAxis.ordinal &&
xAxis.ordinal.getGroupIntervalFactor(xMin,
xMax,
series)) || 1,
interval = (groupPixelWidth * (xMax - xMin) / plotSizeX) *
groupIntervalFactor,
groupPositions = xAxis.getTimeTicks(Axis_DateTimeAxis.Additions.prototype.normalizeTimeTickInterval(interval,
dataGroupingOptions.units ||
DataGrouping_DataGroupingDefaults.units),
// Processed data may extend beyond axis (#4907)
Math.min(xMin,
xData[0]),
Math.max(xMax,
xData[xData.length - 1]),
xAxis.options.startOfWeek,
processedXData,
series.closestPointRange),
groupedData = DataGroupingSeriesComposition_seriesProto.groupData.apply(series,
[
table,
groupPositions,
dataGroupingOptions.approximation
]);
var modified = groupedData.modified,
groupedXData = modified.getColumn('x',
true),
gapSize = 0;
// The smoothed option is deprecated, instead, there is a fallback
// to the new anchoring mechanism. #12455.
if ((dataGroupingOptions === null || dataGroupingOptions === void 0 ? void 0 : dataGroupingOptions.smoothed) &&
modified.rowCount) {
dataGroupingOptions.firstAnchor = 'firstPoint';
dataGroupingOptions.anchor = 'middle';
dataGroupingOptions.lastAnchor = 'lastPoint';
DataGroupingSeriesComposition_error(32, false, chart, {
'dataGrouping.smoothed': 'use dataGrouping.anchor'
});
}
// Record what data grouping values were used
for (i = 1; i < groupPositions.length; i++) {
// The grouped gapSize needs to be the largest distance between
// the group to capture varying group sizes like months or DST
// crossing (#10000). Also check that the gap is not at the
// start of a segment.
if (!groupPositions.info.segmentStarts ||
groupPositions.info.segmentStarts.indexOf(i) === -1) {
gapSize = Math.max(groupPositions[i] - groupPositions[i - 1], gapSize);
}
}
currentDataGrouping = groupPositions.info;
currentDataGrouping.gapSize = gapSize;
series.closestPointRange = groupPositions.info.totalRange;
series.groupMap = groupedData.groupMap;
series.currentDataGrouping = currentDataGrouping;
anchorPoints(series, groupedXData || [], xMax);
if (reserveSpace && groupedXData) {
adjustExtremes(xAxis, groupedXData);
}
// We calculated all group positions but we should render only the ones
// within the visible range
if (dataGroupingOptions.groupAll) {
// Keep the reference to all grouped points for further calculation,
// used in Heikin-Ashi and hollow candlestick series.
series.allGroupedTable = modified;
croppedData = series.cropData(modified, xAxis.min || 0, xAxis.max || 0);
modified = croppedData.modified;
groupedXData = modified.getColumn('x');
series.cropStart = croppedData.start; // #15005
}
// Set the modified table
series.dataTable.modified = modified;
}
else {
series.groupMap = void 0;
series.currentDataGrouping = void 0;
}
series.hasGroupedData = hasGroupedData;
series.preventGraphAnimation =
(lastDataGrouping && lastDataGrouping.totalRange) !==
(currentDataGrouping && currentDataGrouping.totalRange);
}
/**
* @private
*/
function DataGroupingSeriesComposition_compose(SeriesClass) {
var seriesProto = SeriesClass.prototype;
if (!seriesProto.applyGrouping) {
var PointClass = SeriesClass.prototype.pointClass;
// Override point prototype to throw a warning when trying to update
// grouped points.
DataGroupingSeriesComposition_addEvent(PointClass, 'update', function () {
if (this.dataGroup) {
DataGroupingSeriesComposition_error(24, false, this.series.chart);
return false;
}
});
DataGroupingSeriesComposition_addEvent(SeriesClass, 'afterSetOptions', onAfterSetOptions);
DataGroupingSeriesComposition_addEvent(SeriesClass, 'destroy', destroyGroupedData);
DataGroupingSeriesComposition_extend(seriesProto, {
applyGrouping: DataGroupingSeriesComposition_applyGrouping,
destroyGroupedData: destroyGroupedData,
generatePoints: generatePoints,
getDGApproximation: getDGApproximation,
groupData: groupData
});
}
}
/**
* Destroy the grouped data points. #622, #740
* @private
*/
function destroyGroupedData() {
// Clear previous groups
if (this.groupedData) {
this.groupedData.forEach(function (point, i) {
if (point) {
this.groupedData[i] = point.destroy ?
point.destroy() : null;
}
}, this);
// Clears all:
// - `this.groupedData`
// - `this.points`
// - `preserve` object in series.update()
this.groupedData.length = 0;
delete this.allGroupedTable;
}
}
/**
* Override the generatePoints method by adding a reference to grouped data
* @private
*/
function generatePoints() {
baseGeneratePoints.apply(this);
// Record grouped data in order to let it be destroyed the next time
// processData runs
this.destroyGroupedData(); // #622
this.groupedData = this.hasGroupedData ? this.points : null;
}
/**
* Set default approximations to the prototypes if present. Properties are
* inherited down. Can be overridden for individual series types.
* @private
*/
function getDGApproximation() {
if (this.is('arearange')) {
return 'range';
}
if (this.is('ohlc')) {
return 'ohlc';
}
if (this.is('hlc')) {
return 'hlc';
}
if (
// #18974, default approximation for cumulative
// should be `sum` when `dataGrouping` is enabled
this.is('column') ||
this.options.cumulative) {
return 'sum';
}
return 'average';
}
/**
* Highcharts Stock only. Takes parallel arrays of x and y data and groups the
* data into intervals defined by groupPositions, a collection of starting x
* values for each group.
*
* @product highstock
*
* @function Highcharts.Series#groupData
* @param {Highcharts.DataTable} table
* The series data table.
* @param {Array<number>} groupPositions
* Group positions.
* @param {string|Function} [approximation]
* Approximation to use.
* @return {Highcharts.DataGroupingResultObject}
* Mapped groups.
*/
function groupData(table, groupPositions, approximation) {
var xData = table.getColumn('x', true) || [], yData = table.getColumn('y', true), series = this, data = series.data, dataOptions = series.options && series.options.data, groupedXData = [], modified = new Data_DataTableCore(), groupMap = [], dataLength = table.rowCount,
// When grouping the fake extended axis for panning, we don't need to
// consider y
handleYData = !!yData, values = [], pointArrayMap = series.pointArrayMap, pointArrayMapLength = pointArrayMap && pointArrayMap.length, extendedPointArrayMap = ['x'].concat(pointArrayMap || ['y']),
// Data columns to be applied to the modified data table at the end
valueColumns = (pointArrayMap || ['y']).map(function () { return []; }), groupAll = (this.options.dataGrouping &&
this.options.dataGrouping.groupAll);
var pointX,
pointY,
groupedY,
pos = 0,
start = 0;
var approximationFn = (typeof approximation === 'function' ?
approximation :
approximation && DataGrouping_ApproximationRegistry[approximation] ?
DataGrouping_ApproximationRegistry[approximation] :
DataGrouping_ApproximationRegistry[(series.getDGApproximation && series.getDGApproximation() ||
'average')]);
// Calculate values array size from pointArrayMap length
if (pointArrayMapLength) {
var len = pointArrayMap.length;
while (len--) {
values.push([]);
}
}
else {
values.push([]);
}
var valuesLen = pointArrayMapLength || 1;
for (var i = 0; i <= dataLength; i++) {
// Start with the first point within the X axis range (#2696)
if (xData[i] < groupPositions[0]) {
continue; // With next point
}
// When a new group is entered, summarize and initialize
// the previous group
while ((typeof groupPositions[pos + 1] !== 'undefined' &&
xData[i] >= groupPositions[pos + 1]) ||
i === dataLength) { // Get the last group
// get group x and y
pointX = groupPositions[pos];
series.dataGroupInfo = {
start: groupAll ? start : (series.cropStart + start),
length: values[0].length,
groupStart: pointX
};
groupedY = approximationFn.apply(series, values);
// By default, let options of the first grouped point be passed over
// to the grouped point. This allows preserving properties like
// `name` and `color` or custom properties. Implementers can
// override this from the approximation function, where they can
// write custom options to `this.dataGroupInfo.options`.
if (series.pointClass && !DataGroupingSeriesComposition_defined(series.dataGroupInfo.options)) {
// Convert numbers and arrays into objects
series.dataGroupInfo.options = DataGroupingSeriesComposition_merge(series.pointClass.prototype
.optionsToObject.call({ series: series }, series.options.data[series.cropStart + start]));
// Make sure the raw data (x, y, open, high etc) is not copied
// over and overwriting approximated data.
extendedPointArrayMap.forEach(function (key) {
delete series.dataGroupInfo.options[key];
});
}
// Push the grouped data
if (typeof groupedY !== 'undefined') {
groupedXData.push(pointX);
// Push the grouped values to the parallel columns
var groupedValuesArr = DataGroupingSeriesComposition_splat(groupedY);
for (var j = 0; j < groupedValuesArr.length; j++) {
valueColumns[j].push(groupedValuesArr[j]);
}
groupMap.push(series.dataGroupInfo);
}
// Reset the aggregate arrays
start = i;
for (var j = 0; j < valuesLen; j++) {
values[j].length = 0; // Faster than values[j] = []
values[j].hasNulls = false;
}
// Advance on the group positions
pos += 1;
// Don't loop beyond the last group
if (i === dataLength) {
break;
}
}
// Break out
if (i === dataLength) {
break;
}
// For each raw data point, push it to an array that contains all values
// for this specific group
if (pointArrayMap) {
var index = groupAll ? i : series.cropStart + i,
point = (data && data[index]) ||
series.pointClass.prototype.applyOptions.apply({
series: series
},
[dataOptions[index]]);
var val = void 0;
for (var j = 0; j < pointArrayMapLength; j++) {
val = point[pointArrayMap[j]];
if (DataGroupingSeriesComposition_isNumber(val)) {
values[j].push(val);
}
else if (val === null) {
values[j].hasNulls = true;
}
}
}
else {
pointY = handleYData ? yData[i] : null;
if (DataGroupingSeriesComposition_isNumber(pointY)) {
values[0].push(pointY);
}
else if (pointY === null) {
values[0].hasNulls = true;
}
}
}
var columns = {
x: groupedXData
};
(pointArrayMap || ['y']).forEach(function (key, i) {
columns[key] = valueColumns[i];
});
modified.setColumns(columns);
return {
groupMap: groupMap,
modified: modified
};
}
/**
* Handle default options for data grouping. This must be set at runtime because
* some series types are defined after this.
* @private
*/
function onAfterSetOptions(e) {
var options = e.options,
type = this.type,
plotOptions = this.chart.options.plotOptions,
// External series, for example technical indicators should also inherit
// commonOptions which are not available outside this module
baseOptions = (this.useCommonDataGrouping &&
DataGrouping_DataGroupingDefaults.common),
seriesSpecific = DataGrouping_DataGroupingDefaults.seriesSpecific;
var defaultOptions = Defaults.defaultOptions.plotOptions[type].dataGrouping;
if (plotOptions && (seriesSpecific[type] || baseOptions)) { // #1284
var rangeSelector = this.chart.rangeSelector;
if (!defaultOptions) {
defaultOptions = DataGroupingSeriesComposition_merge(DataGrouping_DataGroupingDefaults.common, seriesSpecific[type]);
}
options.dataGrouping = DataGroupingSeriesComposition_merge(baseOptions, defaultOptions, plotOptions.series && plotOptions.series.dataGrouping, // #1228
// Set by the StockChart constructor:
plotOptions[type].dataGrouping, this.userOptions.dataGrouping, !options.isInternal &&
rangeSelector &&
DataGroupingSeriesComposition_isNumber(rangeSelector.selected) &&
rangeSelector.buttonOptions[rangeSelector.selected].dataGrouping);
}
}
/**
* @private
*/
function skipDataGrouping(series, force) {
return !(series.isCartesian &&
!series.isDirty &&
!series.xAxis.isDirty &&
!series.yAxis.isDirty &&
!force);
}
/* *
*
* Default Export
*
* */
var DataGroupingSeriesComposition = {
compose: DataGroupingSeriesComposition_compose,
groupData: groupData
};
/* harmony default export */ var DataGrouping_DataGroupingSeriesComposition = (DataGroupingSeriesComposition);
;// ./code/es5/es-modules/Extensions/DataGrouping/DataGrouping.js
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var DataGrouping_format = Core_Templating.format;
var DataGrouping_composed = Core_Globals.composed;
var DataGrouping_addEvent = Core_Utilities.addEvent, DataGrouping_extend = Core_Utilities.extend, DataGrouping_isNumber = Core_Utilities.isNumber, DataGrouping_pick = Core_Utilities.pick, DataGrouping_pushUnique = Core_Utilities.pushUnique;
/* *
*
* Functions
*
* */
/**
* @private
*/
function DataGrouping_compose(AxisClass, SeriesClass, TooltipClass) {
DataGrouping_DataGroupingAxisComposition.compose(AxisClass);
DataGrouping_DataGroupingSeriesComposition.compose(SeriesClass);
if (TooltipClass &&
DataGrouping_pushUnique(DataGrouping_composed, 'DataGrouping')) {
DataGrouping_addEvent(TooltipClass, 'headerFormatter', onTooltipHeaderFormatter);
}
}
/**
* Extend the original method, make the tooltip's header reflect the grouped
* range.
* @private
*/
function onTooltipHeaderFormatter(e) {
var _a;
var chart = this.chart,
time = chart.time,
point = e.point,
series = point.series,
options = series.options,
tooltipOptions = series.tooltipOptions,
dataGroupingOptions = options.dataGrouping,
xAxis = series.xAxis;
var xDateFormat = tooltipOptions.xDateFormat || '', xDateFormatEnd, currentDataGrouping, dateTimeLabelFormats, labelFormats, formattedKey, formatString = tooltipOptions[e.isFooter ? 'footerFormat' : 'headerFormat'];
// Apply only to grouped series
if (xAxis &&
xAxis.options.type === 'datetime' &&
dataGroupingOptions &&
DataGrouping_isNumber(point.key)) {
// Set variables
currentDataGrouping = series.currentDataGrouping;
dateTimeLabelFormats = dataGroupingOptions.dateTimeLabelFormats ||
// Fallback to commonOptions (#9693)
DataGrouping_DataGroupingDefaults.common.dateTimeLabelFormats;
// If we have grouped data, use the grouping information to get the
// right format
if (currentDataGrouping) {
labelFormats = dateTimeLabelFormats[currentDataGrouping.unitName];
if (currentDataGrouping.count === 1) {
xDateFormat = labelFormats[0];
}
else {
xDateFormat = labelFormats[1];
xDateFormatEnd = labelFormats[2];
}
// If not grouped, and we don't have set the xDateFormat option, get the
// best fit, so if the least distance between points is one minute, show
// it, but if the least distance is one day, skip hours and minutes etc.
}
else if (!xDateFormat && dateTimeLabelFormats && xAxis.dateTime) {
xDateFormat = xAxis.dateTime.getXDateFormat(point.x, tooltipOptions.dateTimeLabelFormats);
}
var groupStart = DataGrouping_pick((_a = series.groupMap) === null || _a === void 0 ? void 0 : _a[point.index].groupStart,
point.key),
groupEnd = groupStart + ((currentDataGrouping === null || currentDataGrouping === void 0 ? void 0 : currentDataGrouping.totalRange) || 0) - 1;
formattedKey = time.dateFormat(xDateFormat, groupStart);
if (xDateFormatEnd) {
formattedKey += time.dateFormat(xDateFormatEnd, groupEnd);
}
// Replace default header style with class name
if (series.chart.styledMode) {
formatString = this.styledModeFormat(formatString);
}
// Return the replaced format
e.text = DataGrouping_format(formatString, {
point: DataGrouping_extend(point, { key: formattedKey }),
series: series
}, chart);
e.preventDefault();
}
}
/* *
*
* Default Export
*
* */
var DataGroupingComposition = {
compose: DataGrouping_compose,
groupData: DataGrouping_DataGroupingSeriesComposition.groupData
};
/* harmony default export */ var DataGrouping = (DataGroupingComposition);
/* *
*
* API Declarations
*
* */
/**
* @typedef {"average"|"averages"|"open"|"high"|"low"|"close"|"sum"} Highcharts.DataGroupingApproximationValue
*/
/**
* The position of the point inside the group.
*
* @typedef {"start"|"middle"|"end"} Highcharts.DataGroupingAnchor
*/
/**
* The position of the first or last point in the series inside the group.
*
* @typedef {"start"|"middle"|"end"|"firstPoint"|"lastPoint"} Highcharts.DataGroupingAnchorExtremes
*/
/**
* Highcharts Stock only.
*
* @product highstock
* @interface Highcharts.DataGroupingInfoObject
*/ /**
* @name Highcharts.DataGroupingInfoObject#length
* @type {number}
*/ /**
* @name Highcharts.DataGroupingInfoObject#options
* @type {Highcharts.SeriesOptionsType|undefined}
*/ /**
* @name Highcharts.DataGroupingInfoObject#start
* @type {number}
*/
/**
* Highcharts Stock only.
*
* @product highstock
* @interface Highcharts.DataGroupingResultObject
*/ /**
* @name Highcharts.DataGroupingResultObject#groupedXData
* @type {Array<number>}
*/ /**
* @name Highcharts.DataGroupingResultObject#groupedYData
* @type {Array<(number|null|undefined)>|Array<Array<(number|null|undefined)>>}
*/ /**
* @name Highcharts.DataGroupingResultObject#groupMap
* @type {Array<DataGroupingInfoObject>}
*/
/**
* Highcharts Stock only. If a point object is created by data
* grouping, it doesn't reflect actual points in the raw
* data. In this case, the `dataGroup` property holds
* information that points back to the raw data.
*
* - `dataGroup.start` is the index of the first raw data
* point in the group.
*
* - `dataGroup.length` is the amount of points in the
* group.
*
* @sample stock/members/point-datagroup
* Click to inspect raw data points
*
* @product highstock
*
* @name Highcharts.Point#dataGroup
* @type {Highcharts.DataGroupingInfoObject|undefined}
*/
(''); // Detach doclets above
/* *
*
* API Options
*
* */
/**
* Data grouping is the concept of sampling the data values into larger
* blocks in order to ease readability and increase performance of the
* JavaScript charts. Highcharts Stock by default applies data grouping when
* the points become closer than a certain pixel value, determined by
* the `groupPixelWidth` option.
*
* If data grouping is applied, the grouping information of grouped
* points can be read from the [Point.dataGroup](
* /class-reference/Highcharts.Point#dataGroup). If point options other than
* the data itself are set, for example `name` or `color` or custom properties,
* the grouping logic doesn't know how to group it. In this case the options of
* the first point instance are copied over to the group point. This can be
* altered through a custom `approximation` callback function.
*
* @declare Highcharts.DataGroupingOptionsObject
* @product highstock
* @requires modules/stock
* @apioption plotOptions.series.dataGrouping
*/
/**
* Specifies how the points should be located on the X axis inside the group.
* Points that are extremes can be set separately. Available options:
*
* - `start` places the point at the beginning of the group
* (e.g. range 00:00:00 - 23:59:59 -> 00:00:00)
*
* - `middle` places the point in the middle of the group
* (e.g. range 00:00:00 - 23:59:59 -> 12:00:00)
*
* - `end` places the point at the end of the group
* (e.g. range 00:00:00 - 23:59:59 -> 23:59:59)
*
* @sample {highstock} stock/plotoptions/series-datagrouping-anchor
* Changing the point x-coordinate inside the group.
*
* @see [dataGrouping.firstAnchor](#plotOptions.series.dataGrouping.firstAnchor)
* @see [dataGrouping.lastAnchor](#plotOptions.series.dataGrouping.lastAnchor)
*
* @type {Highcharts.DataGroupingAnchor}
* @since 9.1.0
* @default start
* @apioption plotOptions.series.dataGrouping.anchor
*/
/**
* The method of approximation inside a group. When for example 30 days
* are grouped into one month, this determines what value should represent
* the group. Possible values are "average", "averages", "open", "high",
* "low", "close" and "sum". For OHLC and candlestick series the approximation
* is "ohlc" by default, which finds the open, high, low and close values
* within all the grouped data. For ranges, the approximation is "range",
* which finds the low and high values. For multi-dimensional data,
* like ranges and OHLC, "averages" will compute the average for each
* dimension.
*
* Custom aggregate methods can be added by assigning a callback function
* as the approximation. This function takes a numeric array as the
* argument and should return a single numeric value or `null`. Note
* that the numeric array will never contain null values, only true
* numbers. Instead, if null values are present in the raw data, the
* numeric array will have an `.hasNulls` property set to `true`. For
* single-value data sets the data is available in the first argument
* of the callback function. For OHLC data sets, all the open values
* are in the first argument, all high values in the second etc.
*
* Since v4.2.7, grouping meta data is available in the approximation
* callback from `this.dataGroupInfo`. It can be used to extract information
* from the raw data.
*
* Defaults to `average` for line-type series, `sum` for columns, `range`
* for range series, `hlc` for HLC, and `ohlc` for OHLC and candlestick.
*
* @sample {highstock} stock/plotoptions/series-datagrouping-approximation
* Approximation callback with custom data
* @sample {highstock} stock/plotoptions/series-datagrouping-simple-approximation
* Simple approximation demo
*
* @type {Highcharts.DataGroupingApproximationValue|Function}
* @apioption plotOptions.series.dataGrouping.approximation
*/
/**
* Datetime formats for the header of the tooltip in a stock chart.
* The format can vary within a chart depending on the currently selected
* time range and the current data grouping.
*
* The default formats are:
* ```js
* {
* millisecond: [
* '%A, %e %b, %H:%M:%S.%L', '%A, %e %b, %H:%M:%S.%L', '-%H:%M:%S.%L'
* ],
* second: ['%A, %e %b, %H:%M:%S', '%A, %e %b, %H:%M:%S', '-%H:%M:%S'],
* minute: ['%A, %e %b, %H:%M', '%A, %e %b, %H:%M', '-%H:%M'],
* hour: ['%A, %e %b, %H:%M', '%A, %e %b, %H:%M', '-%H:%M'],
* day: ['%A, %e %b %Y', '%A, %e %b', '-%A, %e %b %Y'],
* week: ['Week from %A, %e %b %Y', '%A, %e %b', '-%A, %e %b %Y'],
* month: ['%B %Y', '%B', '-%B %Y'],
* year: ['%Y', '%Y', '-%Y']
* }
* ```
*
* For each of these array definitions, the first item is the format
* used when the active time span is one unit. For instance, if the
* current data applies to one week, the first item of the week array
* is used. The second and third items are used when the active time
* span is more than two units. For instance, if the current data applies
* to two weeks, the second and third item of the week array are used,
* and applied to the start and end date of the time span.
*
* @type {Object}
* @apioption plotOptions.series.dataGrouping.dateTimeLabelFormats
*/
/**
* Enable or disable data grouping.
*
* @type {boolean}
* @default true
* @apioption plotOptions.series.dataGrouping.enabled
*/
/**
* Specifies how the first grouped point is positioned on the xAxis.
* If firstAnchor and/or lastAnchor are defined, then those options take
* precedence over anchor for the first and/or last grouped points.
* Available options:
*
* -`start` places the point at the beginning of the group
* (e.g. range 00:00:00 - 23:59:59 -> 00:00:00)
*
* -`middle` places the point in the middle of the group
* (e.g. range 00:00:00 - 23:59:59 -> 12:00:00)
*
* -`end` places the point at the end of the group
* (e.g. range 00:00:00 - 23:59:59 -> 23:59:59)
*
* -`firstPoint` the first point in the group
* (e.g. points at 00:13, 00:35, 00:59 -> 00:13)
*
* -`lastPoint` the last point in the group
* (e.g. points at 00:13, 00:35, 00:59 -> 00:59)
*
* @sample {highstock} stock/plotoptions/series-datagrouping-first-anchor
* Applying first and last anchor.
*
* @see [dataGrouping.anchor](#plotOptions.series.dataGrouping.anchor)
*
* @type {Highcharts.DataGroupingAnchorExtremes}
* @since 9.1.0
* @default start
* @apioption plotOptions.series.dataGrouping.firstAnchor
*/
/**
* When data grouping is forced, it runs no matter how small the intervals
* are. This can be handy for example when the sum should be calculated
* for values appearing at random times within each hour.
*
* @type {boolean}
* @default false
* @apioption plotOptions.series.dataGrouping.forced
*/
/**
* The approximate pixel width of each group. If for example a series
* with 30 points is displayed over a 600 pixel wide plot area, no grouping
* is performed. If however the series contains so many points that
* the spacing is less than the groupPixelWidth, Highcharts will try
* to group it into appropriate groups so that each is more or less
* two pixels wide. If multiple series with different group pixel widths
* are drawn on the same x axis, all series will take the greatest width.
* For example, line series have 2px default group width, while column
* series have 10px. If combined, both the line and the column will
* have 10px by default.
*
* @type {number}
* @default 2
* @apioption plotOptions.series.dataGrouping.groupPixelWidth
*/
/**
* By default only points within the visible range are grouped. Enabling this
* option will force data grouping to calculate all grouped points for a given
* dataset. That option prevents for example a column series from calculating
* a grouped point partially. The effect is similar to
* [Series.getExtremesFromAll](#plotOptions.series.getExtremesFromAll) but does
* not affect yAxis extremes.
*
* @sample {highstock} stock/plotoptions/series-datagrouping-groupall/
* Two series with the same data but different groupAll setting
*
* @type {boolean}
* @default false
* @since 6.1.0
* @apioption plotOptions.series.dataGrouping.groupAll
*/
/**
* Specifies how the last grouped point is positioned on the xAxis.
* If firstAnchor and/or lastAnchor are defined, then those options take
* precedence over anchor for the first and/or last grouped points.
* Available options:
*
* -`start` places the point at the beginning of the group
* (e.g. range 00:00:00 - 23:59:59 -> 00:00:00)
*
* -`middle` places the point in the middle of the group
* (e.g. range 00:00:00 - 23:59:59 -> 12:00:00)
*
* -`end` places the point at the end of the group
* (e.g. range 00:00:00 - 23:59:59 -> 23:59:59)
*
* -`firstPoint` the first point in the group
* (e.g. points at 00:13, 00:35, 00:59 -> 00:13)
*
* -`lastPoint` the last point in the group
* (e.g. points at 00:13, 00:35, 00:59 -> 00:59)
*
* @sample {highstock} stock/plotoptions/series-datagrouping-first-anchor
* Applying first and last anchor.
*
* @sample {highstock} stock/plotoptions/series-datagrouping-last-anchor
* Applying the last anchor in the chart with live data.
*
* @see [dataGrouping.anchor](#plotOptions.series.dataGrouping.anchor)
*
* @type {Highcharts.DataGroupingAnchorExtremes}
* @since 9.1.0
* @default start
* @apioption plotOptions.series.dataGrouping.lastAnchor
*/
/**
* Normally, a group is indexed by the start of that group, so for example
* when 30 daily values are grouped into one month, that month's x value
* will be the 1st of the month. This apparently shifts the data to
* the left. When the smoothed option is true, this is compensated for.
* The data is shifted to the middle of the group, and min and max
* values are preserved. Internally, this is used in the Navigator series.
*
* @type {boolean}
* @default false
* @deprecated
* @apioption plotOptions.series.dataGrouping.smoothed
*/
/**
* An array determining what time intervals the data is allowed to be
* grouped to. Each array item is an array where the first value is
* the time unit and the second value another array of allowed multiples.
*
* Defaults to:
* ```js
* units: [[
* 'millisecond', // unit name
* [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
* ], [
* 'second',
* [1, 2, 5, 10, 15, 30]
* ], [
* 'minute',
* [1, 2, 5, 10, 15, 30]
* ], [
* 'hour',
* [1, 2, 3, 4, 6, 8, 12]
* ], [
* 'day',
* [1]
* ], [
* 'week',
* [1]
* ], [
* 'month',
* [1, 3, 6]
* ], [
* 'year',
* null
* ]]
* ```
*
* @type {Array<Array<string,(Array<number>|null)>>}
* @apioption plotOptions.series.dataGrouping.units
*/
/**
* The approximate pixel width of each group. If for example a series
* with 30 points is displayed over a 600 pixel wide plot area, no grouping
* is performed. If however the series contains so many points that
* the spacing is less than the groupPixelWidth, Highcharts will try
* to group it into appropriate groups so that each is more or less
* two pixels wide. Defaults to `10`.
*
* @sample {highstock} stock/plotoptions/series-datagrouping-grouppixelwidth/
* Two series with the same data density but different groupPixelWidth
*
* @type {number}
* @default 10
* @apioption plotOptions.column.dataGrouping.groupPixelWidth
*/
''; // Required by JSDoc parsing
;// ./code/es5/es-modules/masters/modules/datagrouping.src.js
/**
* @license Highstock JS v12.1.2 (2025-01-09)
* @module highcharts/modules/datagrouping
* @requires highcharts
*
* Data grouping module
*
* (c) 2010-2024 Torstein Hønsi
*
* License: www.highcharts.com/license
*/
var datagrouping_src_G = Core_Globals;
datagrouping_src_G.dataGrouping = datagrouping_src_G.dataGrouping || {};
datagrouping_src_G.dataGrouping.approximationDefaults = (datagrouping_src_G.dataGrouping.approximationDefaults ||
DataGrouping_ApproximationDefaults);
datagrouping_src_G.dataGrouping.approximations = (datagrouping_src_G.dataGrouping.approximations ||
DataGrouping_ApproximationRegistry);
DataGrouping.compose(datagrouping_src_G.Axis, datagrouping_src_G.Series, datagrouping_src_G.Tooltip);
/* harmony default export */ var datagrouping_src = ((/* unused pure expression or super */ null && (Highcharts)));
;// ./code/es5/es-modules/Extensions/Annotations/NavigationBindingsUtilities.js
/* *
*
* (c) 2009-2024 Highsoft, Black Label
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var NavigationBindingsUtilities_defined = Core_Utilities.defined, NavigationBindingsUtilities_isNumber = Core_Utilities.isNumber, NavigationBindingsUtilities_pick = Core_Utilities.pick;
/* *
*
* Constants
*
* */
/**
* Define types for editable fields per annotation. There is no need to define
* numbers, because they won't change their type to string.
* @private
*/
var annotationsFieldsTypes = {
backgroundColor: 'string',
borderColor: 'string',
borderRadius: 'string',
color: 'string',
fill: 'string',
fontSize: 'string',
labels: 'string',
name: 'string',
stroke: 'string',
title: 'string'
};
/* *
*
* Functions
*
* */
/**
* Returns the first xAxis or yAxis that was clicked with its value.
*
* @private
*
* @param {Array<Highcharts.PointerAxisCoordinateObject>} coords
* All the chart's x or y axes with a current pointer's axis value.
*
* @return {Highcharts.PointerAxisCoordinateObject}
* Object with a first found axis and its value that pointer
* is currently pointing.
*/
function getAssignedAxis(coords) {
return coords.filter(function (coord) {
var extremes = coord.axis.getExtremes(),
axisMin = extremes.min,
axisMax = extremes.max,
// Correct axis edges when axis has series
// with pointRange (like column)
minPointOffset = NavigationBindingsUtilities_pick(coord.axis.minPointOffset, 0);
return NavigationBindingsUtilities_isNumber(axisMin) && NavigationBindingsUtilities_isNumber(axisMax) &&
coord.value >= (axisMin - minPointOffset) &&
coord.value <= (axisMax + minPointOffset) &&
// Don't count navigator axis
!coord.axis.options.isInternal;
})[0]; // If the axes overlap, return the first axis that was found.
}
/**
* Get field type according to value
*
* @private
*
* @param {'boolean'|'number'|'string'} value
* Atomic type (one of: string, number, boolean)
*
* @return {'checkbox'|'number'|'text'}
* Field type (one of: text, number, checkbox)
*/
function getFieldType(key, value) {
var predefinedType = annotationsFieldsTypes[key];
var fieldType = typeof value;
if (NavigationBindingsUtilities_defined(predefinedType)) {
fieldType = predefinedType;
}
return {
'string': 'text',
'number': 'number',
'boolean': 'checkbox'
}[fieldType];
}
/* *
*
* Default Export
*
* */
var NavigationBindingUtilities = {
annotationsFieldsTypes: annotationsFieldsTypes,
getAssignedAxis: getAssignedAxis,
getFieldType: getFieldType
};
/* harmony default export */ var NavigationBindingsUtilities = (NavigationBindingUtilities);
;// ./code/es5/es-modules/Extensions/MouseWheelZoom/MouseWheelZoom.js
/* *
*
* (c) 2023 Torstein Honsi, Askel Eirik Johansson
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var MouseWheelZoom_addEvent = Core_Utilities.addEvent, MouseWheelZoom_isObject = Core_Utilities.isObject, MouseWheelZoom_pick = Core_Utilities.pick, MouseWheelZoom_defined = Core_Utilities.defined, MouseWheelZoom_merge = Core_Utilities.merge;
var MouseWheelZoom_getAssignedAxis = NavigationBindingsUtilities.getAssignedAxis;
/* *
*
* Constants
*
* */
var composedClasses = [], MouseWheelZoom_defaultOptions = {
enabled: true,
sensitivity: 1.1
};
var wheelTimer;
/* *
*
* Functions
*
* */
/**
* @private
*/
var MouseWheelZoom_optionsToObject = function (options) {
if (!MouseWheelZoom_isObject(options)) {
options = {
enabled: options !== null && options !== void 0 ? options : true
};
}
return MouseWheelZoom_merge(MouseWheelZoom_defaultOptions, options);
};
/**
* @private
*/
var zoomBy = function (chart, howMuch, xAxis, yAxis, mouseX, mouseY, options) {
var type = MouseWheelZoom_pick(options.type,
chart.zooming.type, '');
var axes = [];
if (type === 'x') {
axes = xAxis;
}
else if (type === 'y') {
axes = yAxis;
}
else if (type === 'xy') {
axes = chart.axes;
}
var hasZoomed = chart.transform({
axes: axes,
// Create imaginary reference and target rectangles around the mouse
// point that scales up or down with `howMuch`;
to: {
x: mouseX - 5,
y: mouseY - 5,
// Must use 10 to get passed the limit for too small reference.
// Below this, the transform will default to a pan.
width: 10,
height: 10
},
from: {
x: mouseX - 5 * howMuch,
y: mouseY - 5 * howMuch,
width: 10 * howMuch,
height: 10 * howMuch
},
trigger: 'mousewheel'
});
if (hasZoomed) {
if (MouseWheelZoom_defined(wheelTimer)) {
clearTimeout(wheelTimer);
}
// Some time after the last mousewheel event, run drop. In case any of
// the affected axes had `startOnTick` or `endOnTick`, they will be
// re-adjusted now.
wheelTimer = setTimeout(function () {
var _a;
(_a = chart.pointer) === null || _a === void 0 ? void 0 : _a.drop();
}, 400);
}
return hasZoomed;
};
/**
* @private
*/
function onAfterGetContainer() {
var _this = this;
var wheelZoomOptions = MouseWheelZoom_optionsToObject(this.zooming.mouseWheel);
if (wheelZoomOptions.enabled) {
MouseWheelZoom_addEvent(this.container, 'wheel', function (e) {
var _a,
_b;
e = ((_a = _this.pointer) === null || _a === void 0 ? void 0 : _a.normalize(e)) || e;
var pointer = _this.pointer,
allowZoom = pointer && !pointer.inClass(e.target, 'highcharts-no-mousewheel');
// Firefox uses e.detail, WebKit and IE uses deltaX, deltaY, deltaZ.
if (_this.isInsidePlot(e.chartX - _this.plotLeft, e.chartY - _this.plotTop) && allowZoom) {
var wheelSensitivity = wheelZoomOptions.sensitivity || 1.1,
delta = e.detail || ((e.deltaY || 0) / 120),
xAxisCoords = MouseWheelZoom_getAssignedAxis(pointer.getCoordinates(e).xAxis),
yAxisCoords = MouseWheelZoom_getAssignedAxis(pointer.getCoordinates(e).yAxis);
var hasZoomed = zoomBy(_this,
Math.pow(wheelSensitivity,
delta),
xAxisCoords ? [xAxisCoords.axis] : _this.xAxis,
yAxisCoords ? [yAxisCoords.axis] : _this.yAxis,
e.chartX,
e.chartY,
wheelZoomOptions);
// Prevent page scroll
if (hasZoomed) {
(_b = e.preventDefault) === null || _b === void 0 ? void 0 : _b.call(e);
}
}
});
}
}
/**
* @private
*/
function MouseWheelZoom_compose(ChartClass) {
if (composedClasses.indexOf(ChartClass) === -1) {
composedClasses.push(ChartClass);
MouseWheelZoom_addEvent(ChartClass, 'afterGetContainer', onAfterGetContainer);
}
}
/* *
*
* Default Export
*
* */
var MouseWheelZoomComposition = {
compose: MouseWheelZoom_compose
};
/* harmony default export */ var MouseWheelZoom = (MouseWheelZoomComposition);
/* *
*
* API Options
*
* */
/**
* The mouse wheel zoom is a feature included in Highcharts Stock, but is also
* available for Highcharts Core as a module. Zooming with the mouse wheel is
* enabled by default in Highcharts Stock. In Highcharts Core it is enabled if
* [chart.zooming.type](chart.zooming.type) is set. It can be disabled by
* setting this option to `false`.
*
* @type {boolean|object}
* @since 11.1.0
* @requires modules/mouse-wheel-zoom
* @sample {highcharts} highcharts/mouse-wheel-zoom/enabled
* Enable or disable
* @sample {highstock} stock/mouse-wheel-zoom/enabled
* Enable or disable
* @apioption chart.zooming.mouseWheel
*/
/**
* Zooming with the mouse wheel can be disabled by setting this option to
* `false`.
*
* @type {boolean}
* @default true
* @since 11.1.0
* @requires modules/mouse-wheel-zoom
* @apioption chart.zooming.mouseWheel.enabled
*/
/**
* Adjust the sensitivity of the zoom. Sensitivity of mouse wheel or trackpad
* scrolling. `1` is no sensitivity, while with `2`, one mouse wheel delta will
* zoom in `50%`.
*
* @type {number}
* @default 1.1
* @since 11.1.0
* @requires modules/mouse-wheel-zoom
* @sample {highcharts} highcharts/mouse-wheel-zoom/sensitivity
* Change mouse wheel zoom sensitivity
* @sample {highstock} stock/mouse-wheel-zoom/sensitivity
* Change mouse wheel zoom sensitivity
* @apioption chart.zooming.mouseWheel.sensitivity
*/
/**
* Decides in what dimensions the user can zoom scrolling the wheel. Can be one
* of `x`, `y` or `xy`. In Highcharts Core, if not specified here, it will
* inherit the type from [chart.zooming.type](chart.zooming.type). In Highcharts
* Stock, it defaults to `x`.
*
* Note that particularly with mouse wheel in the y direction, the zoom is
* affected by the default [yAxis.startOnTick](#yAxis.startOnTick) and
* [endOnTick]((#yAxis.endOnTick)) settings. In order to respect these settings,
* the zoom level will adjust after the user has stopped zooming. To prevent
* this, consider setting `startOnTick` and `endOnTick` to `false`.
*
* @type {string}
* @default {highcharts} undefined
* @default {highstock} x
* @validvalue ["x", "y", "xy"]
* @since 11.1.0
* @requires modules/mouse-wheel-zoom
* @apioption chart.zooming.mouseWheel.type
*/
(''); // Keeps doclets above in JS file
;// ./code/es5/es-modules/masters/modules/mouse-wheel-zoom.src.js
/**
* @license Highcharts JS v12.1.2 (2025-01-09)
* @module highcharts/modules/mouse-wheel-zoom
* @requires highcharts
*
* Mousewheel zoom module
*
* (c) 2023 Askel Eirik Johansson
*
* License: www.highcharts.com/license
*/
var mouse_wheel_zoom_src_G = Core_Globals;
mouse_wheel_zoom_src_G.MouseWheelZoom = mouse_wheel_zoom_src_G.MouseWheelZoom || MouseWheelZoom;
mouse_wheel_zoom_src_G.MouseWheelZoom.compose(mouse_wheel_zoom_src_G.Chart);
/* harmony default export */ var mouse_wheel_zoom_src = ((/* unused pure expression or super */ null && (Highcharts)));
;// ./code/es5/es-modules/masters/modules/stock.src.js
/**
* @license Highstock JS v12.1.2 (2025-01-09)
* @module highcharts/modules/stock
* @requires highcharts
*
* Highcharts Stock as a plugin for Highcharts
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*/
var stock_src_G = Core_Globals;
// Classes
stock_src_G.Navigator = stock_src_G.Navigator || Navigator_Navigator;
stock_src_G.OrdinalAxis = stock_src_G.OrdinalAxis || Axis_OrdinalAxis;
stock_src_G.RangeSelector = stock_src_G.RangeSelector || RangeSelector_RangeSelector;
stock_src_G.Scrollbar = stock_src_G.Scrollbar || Scrollbar_Scrollbar;
// Functions
stock_src_G.stockChart = stock_src_G.stockChart || Chart_StockChart.stockChart;
stock_src_G.StockChart = stock_src_G.StockChart || stock_src_G.stockChart;
stock_src_G.extend(stock_src_G.StockChart, Chart_StockChart);
// Compositions
Series_DataModifyComposition.compose(stock_src_G.Series, stock_src_G.Axis, stock_src_G.Point);
Flags_FlagsSeries.compose(stock_src_G.Renderer);
OHLC_OHLCSeries.compose(stock_src_G.Series);
stock_src_G.Navigator.compose(stock_src_G.Chart, stock_src_G.Axis, stock_src_G.Series);
stock_src_G.OrdinalAxis.compose(stock_src_G.Axis, stock_src_G.Series, stock_src_G.Chart);
stock_src_G.RangeSelector.compose(stock_src_G.Axis, stock_src_G.Chart);
stock_src_G.Scrollbar.compose(stock_src_G.Axis);
stock_src_G.StockChart.compose(stock_src_G.Chart, stock_src_G.Axis, stock_src_G.Series, stock_src_G.SVGRenderer);
/* harmony default export */ var stock_src = ((/* unused pure expression or super */ null && (Highcharts)));
;// ./code/es5/es-modules/masters/highstock.src.js
/**
* @license Highstock JS v12.1.2 (2025-01-09)
* @module highcharts/highstock
*
* (c) 2009-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*/
highcharts_src.product = 'Highstock';
/* harmony default export */ var highstock_src = (highcharts_src);
__webpack_exports__ = __webpack_exports__["default"];
/******/ return __webpack_exports__;
/******/ })()
;
});
Hacked By AnonymousFox1.0, Coded By AnonymousFox