UNPKG

highcharts

Version:
1,312 lines (1,310 loc) 2.34 MB
/** * @license Highstock JS v8.0.0 (2019-12-10) * * (c) 2009-2018 Torstein Honsi * * License: www.highcharts.com/license */ 'use strict'; (function (root, factory) { if (typeof module === 'object' && module.exports) { factory['default'] = factory; module.exports = root.document ? factory(root) : factory; } else if (typeof define === 'function' && define.amd) { define('highcharts/highstock', function () { return factory(root); }); } else { if (root.Highcharts) { root.Highcharts.error(16, true); } root.Highcharts = factory(root); } }(typeof window !== 'undefined' ? window : this, function (win) { var _modules = {}; function _registerModule(obj, path, args, fn) { if (!obj.hasOwnProperty(path)) { obj[path] = fn.apply(null, args); } } _registerModule(_modules, 'parts/Globals.js', [], function () { /* * * * (c) 2010-2019 Torstein Honsi * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ /* globals Image, window */ /** * 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 */ // glob is a temporary fix to allow our es-modules to work. var glob = ( // @todo UMD variable named `window`, and glob named `win` typeof win !== 'undefined' ? win : typeof window !== 'undefined' ? window : {}), doc = glob.document, SVG_NS = 'http://www.w3.org/2000/svg', userAgent = (glob.navigator && glob.navigator.userAgent) || '', svg = (doc && doc.createElementNS && !!doc.createElementNS(SVG_NS, 'svg').createSVGRect), isMS = /(edge|msie|trident)/i.test(userAgent) && !glob.opera, isFirefox = userAgent.indexOf('Firefox') !== -1, isChrome = userAgent.indexOf('Chrome') !== -1, hasBidiBug = (isFirefox && parseInt(userAgent.split('Firefox/')[1], 10) < 4 // issue #38 ); var H = { product: 'Highcharts', version: '8.0.0', deg2rad: Math.PI * 2 / 360, doc: doc, hasBidiBug: hasBidiBug, hasTouch: !!glob.TouchEvent, isMS: isMS, isWebKit: userAgent.indexOf('AppleWebKit') !== -1, isFirefox: isFirefox, isChrome: isChrome, isSafari: !isChrome && userAgent.indexOf('Safari') !== -1, isTouchDevice: /(Mobile|Android|Windows Phone)/.test(userAgent), SVG_NS: SVG_NS, chartCount: 0, seriesTypes: {}, symbolSizes: {}, svg: svg, win: glob, marginNames: ['plotTop', 'marginRight', 'marginBottom', 'plotLeft'], noop: function () { }, /** * 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>} */ charts: [], /** * 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. * * @sample highcharts/global/dateformats/ * Adding support for week number * * @name Highcharts.dateFormats * @type {Highcharts.Dictionary<Highcharts.TimeFormatCallbackFunction>} */ dateFormats: {} }; return H; }); _registerModule(_modules, 'parts/Utilities.js', [_modules['parts/Globals.js']], function (H) { /* * * * (c) 2010-2019 Torstein Honsi * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ /** * An animation configuration. Animation configurations can also be defined as * booleans, where `false` turns off animation and `true` defaults to a duration * of 500ms. * * @interface Highcharts.AnimationOptionsObject */ /** * A callback function to exectute when the animation finishes. * @name Highcharts.AnimationOptionsObject#complete * @type {Function|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 costructor. * @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} */ /** * 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. * * @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} */ /** * Formats data as a string. Usually the data is accessible throught 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 * var chart = Highcharts.chart('container', { ... }); * * @namespace Highcharts */ H.timers = []; var charts = H.charts, doc = H.doc, win = H.win; /** * 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. * * @param {Highcharts.Dictionary<string>} [params] * Additional parameters for the generated message. * * @return {void} */ H.error = function (code, stop, chart, params) { var isCode = isNumber(code), message = isCode ? "Highcharts error #" + code + ": www.highcharts.com/errors/" + code + "/" : code.toString(), defaultHandler = function () { if (stop) { throw new Error(message); } // else ... if (win.console) { console.log(message); // eslint-disable-line no-console } }; if (typeof params !== 'undefined') { var additionalMessages_1 = ''; if (isCode) { message += '?'; } H.objectEach(params, function (value, key) { additionalMessages_1 += ('\n' + key + ': ' + value); if (isCode) { message += encodeURI(key) + '=' + encodeURI(value); } }); message += additionalMessages_1; } if (chart) { H.fireEvent(chart, 'displayError', { code: code, message: message, params: params }, defaultHandler); } else { defaultHandler(); } }; /* eslint-disable no-invalid-this, valid-jsdoc */ /** * 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 * var 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 {Highcharts.AnimationOptionsObject} options * Animation options. * * @param {string} prop * The single attribute or CSS property to animate. */ H.Fx = function (elem, options, prop) { this.options = options; this.elem = elem; this.prop = prop; /* eslint-enable no-invalid-this, valid-jsdoc */ }; H.Fx.prototype = { /** * Set the current step of a path definition on SVGElement. * * @function Highcharts.Fx#dSetter * * @return {void} */ dSetter: function () { var start = this.paths[0], end = this.paths[1], ret = [], now = this.now, i = start.length, startVal; // Land on the final path without adjustment points appended in the ends if (now === 1) { ret = this.toD; } else if (i === end.length && now < 1) { while (i--) { startVal = parseFloat(start[i]); ret[i] = ( // A letter instruction like M or L isNaN(startVal) || // Arc boolean flags: end[i - 4] === 'A' || // large-arc-flag end[i - 5] === 'A' // sweep-flag ) ? end[i] : (now * parseFloat('' + (end[i] - startVal)) + startVal); } // If animation is finished or length not matching, land on right value } else { ret = end; } this.elem.attr('d', ret, null, true); }, /** * Update the element with the current animation step. * * @function Highcharts.Fx#update * * @return {void} */ 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`. * * @return {void} */ run: function (from, to, unit) { var self = this, options = self.options, timer = function (gotoEnd) { return timer.stopped ? false : self.step(gotoEnd); }, requestAnimationFrame = win.requestAnimationFrame || function (step) { setTimeout(step, 13); }, step = function () { for (var i = 0; i < H.timers.length; i++) { if (!H.timers[i]()) { H.timers.splice(i--, 1); } } if (H.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() && H.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. */ step: function (gotoEnd) { var t = +new Date(), ret, done, options = this.options, elem = this.elem, complete = options.complete, duration = options.duration, curAnim = options.curAnim; 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; 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 {string} 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. */ initPath: function (elem, fromD, toD) { fromD = fromD || ''; var shift, startX = elem.startX, endX = elem.endX, bezier = fromD.indexOf('C') > -1, numParams = bezier ? 7 : 3, fullLength, slice, i, start = fromD.split(' '), end = toD.slice(), // copy isArea = elem.isArea, positionFactor = isArea ? 2 : 1, reverse; /** * In splines make moveTo and lineTo points have six parameters like * bezier curves, to allow animation one-to-one. * @private * @param {Highcharts.SVGPathArray} arr - array * @return {void} */ function sixify(arr) { var isOperator, nextIsOperator; i = arr.length; while (i--) { // Fill in dummy coordinates only if the next operator comes // three places behind (#5788) isOperator = arr[i] === 'M' || arr[i] === 'L'; nextIsOperator = /[a-zA-Z]/.test(arr[i + 3]); if (isOperator && nextIsOperator) { arr.splice(i + 1, 0, arr[i + 1], arr[i + 2], arr[i + 1], arr[i + 2]); } } } /** * Insert an array at the given position of another array * @private * @param {Array<*>} arr - array * @param {Array<*>} subArr - array * @param {number} index - number * @return {void} */ function insertSlice(arr, subArr, index) { [].splice.apply(arr, [index, 0].concat(subArr)); } /** * If shifting points, prepend a dummy point to the end path. * @private * @param {Highcharts.SVGPathArray} arr - array * @param {Highcharts.SVGPathArray} other - array * @return {void} */ function prepend(arr, other) { while (arr.length < fullLength) { // Move to, line to or curve to? arr[0] = other[fullLength - arr.length]; // Prepend a copy of the first point insertSlice(arr, arr.slice(0, numParams), 0); // For areas, the bottom path goes back again to the left, so we // need to append a copy of the last point. if (isArea) { insertSlice(arr, arr.slice(arr.length - numParams), arr.length); i--; } } arr[0] = 'M'; } /** * Copy and append last point until the length matches the end length. * @private * @param {Highcharts.SVGPathArray} arr - array * @param {Highcharts.SVGPathArray} other - array * @return {void} */ function append(arr, other) { var i = (fullLength - arr.length) / numParams; while (i > 0 && i--) { // 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. slice = arr.slice().splice((arr.length / positionFactor) - numParams, numParams * positionFactor); // Move to, line to or curve to? slice[0] = other[fullLength - numParams - (i * numParams)]; // Disable first control point if (bezier) { slice[numParams - 6] = slice[numParams - 2]; slice[numParams - 5] = slice[numParams - 1]; } // Now insert the slice, either in the middle (for areas) or at // the end (for lines) insertSlice(arr, slice, arr.length / positionFactor); if (isArea) { i--; } } } if (bezier) { sixify(start); sixify(end); } // 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) { 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 && 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 * numParams); if (!reverse) { prepend(end, start); append(start, end); } else { prepend(start, end); append(end, start); } } return [start, end]; }, /** * Handle animation of the color attributes directly. * * @function Highcharts.Fx#fillSetter * * @return {void} */ fillSetter: function () { H.Fx.prototype.strokeSetter.apply(this, arguments); }, /** * Handle animation of the color attributes directly. * * @function Highcharts.Fx#strokeSetter * * @return {void} */ strokeSetter: function () { this.elem.attr(this.prop, H.color(this.start).tweenTo(H.color(this.end), this.pos), null, true); } }; // End of Fx prototype /* eslint-disable valid-jsdoc */ /** * 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 {boolean} extend * Whether to extend the left-side object (a) or return a whole new * object. * * @param {T|undefined} a * The first object to extend. When only this is given, the function * returns a deep copy. * * @param {...Array<object|undefined>} [n] * An object 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. */ /** * Utility function to deep merge two or more objects and return a third 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 {T|undefined} a * The first object to extend. When only this is given, the function * returns a deep copy. * * @param {...Array<object|undefined>} [n] * An object 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. */ H.merge = function () { /* eslint-enable valid-jsdoc */ var i, args = arguments, len, ret = {}, doCopy = function (copy, original) { // An object is replacing a primitive if (typeof copy !== 'object') { copy = {}; } objectEach(original, function (value, key) { // 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 (args[0] === true) { ret = args[1]; args = Array.prototype.slice.call(args, 2); } // For each argument, extend the return 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; } /** * 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; } } } /** * 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. 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} [prop] * 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, prop, value) { var ret; // if the prop is a string if (isString(prop)) { // set the value if (defined(value)) { elem.setAttribute(prop, value); // get the value } else if (elem && elem.getAttribute) { ret = elem.getAttribute(prop); // IE7 and below cannot get class through getAttribute (#7850) if (!ret && prop === 'class') { ret = elem.getAttribute(prop + 'Name'); } } // else if prop is defined, it is a hash of key/value pairs } else { objectEach(prop, function (val, key) { elem.setAttribute(key, val); }); } 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} id * Id of a timeout. * * @return {void} */ H.clearTimeout = function (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 {object} 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} */ H.css = function (el, styles) { if (H.isMS && !H.svg) { // #2686 if (styles && typeof styles.opacity !== 'undefined') { styles.filter = 'alpha(opacity=' + (styles.opacity * 100) + ')'; } } 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. */ H.createElement = function (tag, attribs, styles, parent, nopad) { var el = doc.createElement(tag), css = H.css; if (attribs) { extend(el, attribs); }