highcharts
Version:
JavaScript charting framework
1,309 lines (1,308 loc) • 2.39 MB
JavaScript
/**
* @license Highcharts Gantt JS v8.0.0 (2019-12-10)
*
* (c) 2017-2018 Lars Cabrera, Torstein Honsi, Jon Arild Nygard & Oystein Moseng
*
* 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/highcharts-gantt', 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) {