corrade-nucleus-nucleons

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 10  →  ?path2? @ 11
/base/000_base/bower_components/highcharts/highstock.src.js
@@ -1,5 +1,5 @@
/**
* @license Highstock JS v5.0.10 (2017-03-31)
* @license Highstock JS v5.0.12 (2017-05-24)
*
* (c) 2009-2016 Torstein Honsi
*
@@ -35,7 +35,7 @@
 
var Highcharts = win.Highcharts ? win.Highcharts.error(16, true) : {
product: 'Highstock',
version: '5.0.10',
version: '5.0.12',
deg2rad: Math.PI * 2 / 360,
doc: doc,
hasBidiBug: hasBidiBug,
@@ -51,11 +51,18 @@
svg: svg,
vml: vml,
win: win,
charts: [],
marginNames: ['plotTop', 'marginRight', 'marginBottom', 'plotLeft'],
noop: function() {
return undefined;
}
},
/**
* 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`.
* @type {Array.<Highcharts.Chart>}
* @memberOf Highcharts
*/
charts: []
};
return Highcharts;
}());
@@ -69,7 +76,12 @@
 
/**
* The Highcharts object is the placeholder for all other members, and various
* utility functions.
* utility functions. The most important member of the namespace would be the
* chart constructor.
*
* @example
* var chart = Highcharts.chart('container', { ... });
*
* @namespace Highcharts
*/
 
@@ -91,6 +103,8 @@
* directly in the console.
* @param {Boolean} [stop=false] - Whether to throw an error or just log a
* warning in the console.
*
* @sample highcharts/chart/highcharts-error/ Custom error handler
*/
H.error = function(code, stop) {
var msg = H.isNumber(code) ?
@@ -106,8 +120,9 @@
};
 
/**
* An animator object. One instance applies to one property (attribute or style
* prop) on one element.
* 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}.
*
* @constructor Fx
* @memberOf Highcharts
@@ -114,6 +129,11 @@
* @param {HTMLDOMElement|SVGElement} elem - The element to animate.
* @param {AnimationOptions} options - Animation options.
* @param {string} prop - The single attribute or CSS property to animate.
* @private
*
* @example
* var rect = renderer.rect(0, 0, 10, 10).add();
* rect.animate({ width: 100 });
*/
H.Fx = function(elem, options, prop) {
this.options = options;
@@ -249,8 +269,7 @@
elem = this.elem,
complete = options.complete,
duration = options.duration,
curAnim = options.curAnim,
i;
curAnim = options.curAnim;
 
if (elem.attr && !elem.element) { // #2616, element is destroyed
ret = false;
@@ -263,11 +282,12 @@
curAnim[this.prop] = true;
 
done = true;
for (i in curAnim) {
if (curAnim[i] !== true) {
 
H.objectEach(curAnim, function(val) {
if (val !== true) {
done = false;
}
}
});
 
if (done && complete) {
complete.call(elem);
@@ -450,7 +470,20 @@
}
}; // End of Fx prototype
 
/**
* Handle animation of the color attributes directly.
*/
H.Fx.prototype.fillSetter =
H.Fx.prototype.strokeSetter = function() {
this.elem.attr(
this.prop,
H.color(this.start).tweenTo(H.color(this.end), this.pos),
null,
true
);
};
 
 
/**
* Utility function to extend an object with the members of another.
*
@@ -493,29 +526,26 @@
len,
ret = {},
doCopy = function(copy, original) {
var value, key;
 
// An object is replacing a primitive
if (typeof copy !== 'object') {
copy = {};
}
 
for (key in original) {
if (original.hasOwnProperty(key)) {
value = original[key];
H.objectEach(original, function(value, key) {
 
// Copy the contents of objects, but not arrays or DOM nodes
if (H.isObject(value, true) &&
key !== 'renderTo' &&
typeof value.nodeType !== 'number') {
copy[key] = doCopy(copy[key] || {}, value);
// Copy the contents of objects, but not arrays or DOM nodes
if (
H.isObject(value, true) &&
!H.isClass(value) &&
!H.isDOMElement(value)
) {
copy[key] = doCopy(copy[key] || {}, value);
 
// Primitives and arrays are copied over directly
} else {
copy[key] = original[key];
}
// Primitives and arrays are copied over directly
} else {
copy[key] = original[key];
}
}
});
return copy;
};
 
@@ -581,10 +611,39 @@
* @returns {Boolean} - True if the argument is an object.
*/
H.isObject = function(obj, strict) {
return obj && typeof obj === 'object' && (!strict || !H.isArray(obj));
return !!obj && typeof obj === 'object' && (!strict || !H.isArray(obj));
};
 
/**
* Utility function to check if an Object is a HTML Element.
*
* @function #isDOMElement
* @memberOf Highcharts
* @param {Object} obj - The item to check.
* @returns {Boolean} - True if the argument is a HTML Element.
*/
H.isDOMElement = function(obj) {
return H.isObject(obj) && typeof obj.nodeType === 'number';
};
 
/**
* Utility function to check if an Object is an class.
*
* @function #isClass
* @memberOf Highcharts
* @param {Object} obj - The item to check.
* @returns {Boolean} - True if the argument is an class.
*/
H.isClass = function(obj) {
var c = obj && obj.constructor;
return !!(
H.isObject(obj, true) &&
!H.isDOMElement(obj) &&
(c && c.name && c.name !== 'Object')
);
};
 
/**
* Utility function to check if an item is of type number.
*
* @function #isNumber
@@ -640,8 +699,7 @@
* @returns {*} When used as a getter, return the value.
*/
H.attr = function(elem, prop, value) {
var key,
ret;
var ret;
 
// if the prop is a string
if (H.isString(prop)) {
@@ -656,9 +714,9 @@
 
// else if prop is defined, it is a hash of key/value pairs
} else if (H.defined(prop) && H.isObject(prop)) {
for (key in prop) {
elem.setAttribute(key, prop[key]);
}
H.objectEach(prop, function(val, key) {
elem.setAttribute(key, val);
});
}
return ret;
};
@@ -883,8 +941,11 @@
};
 
/**
* Format a date, based on the syntax for PHP's [strftime]{@link
* http://www.php.net/manual/en/function.strftime.php} function.
* 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]{@link
* http://www.php.net/manual/en/function.strftime.php} function. Additional
* formats can be given in the {@link Highcharts.dateFormats} hook.
*
* @function #dateFormat
* @memberOf Highcharts
@@ -902,7 +963,6 @@
 
var D = H.Date,
date = new D(timestamp - H.getTZOffset(timestamp)),
key, // used in for constuct below
// get the basic time values
hours = date[D.hcGetHours](),
day = date[D.hcGetDay](),
@@ -917,70 +977,84 @@
// List all format keys. Custom formats can be added from the outside.
replacements = H.extend({
 
//-- Day
// Short weekday, like 'Mon'
'a': shortWeekdays ?
shortWeekdays[day] : langWeekdays[day].substr(0, 3),
// Long weekday, like 'Monday'
'A': langWeekdays[day],
// Two digit day of the month, 01 to 31
'd': pad(dayOfMonth),
// Day of the month, 1 through 31
'e': pad(dayOfMonth, 2, ' '),
'w': day,
//-- Day
// Short weekday, like 'Mon'
'a': shortWeekdays ?
shortWeekdays[day] : langWeekdays[day].substr(0, 3),
// Long weekday, like 'Monday'
'A': langWeekdays[day],
// Two digit day of the month, 01 to 31
'd': pad(dayOfMonth),
// Day of the month, 1 through 31
'e': pad(dayOfMonth, 2, ' '),
'w': day,
 
// Week (none implemented)
//'W': weekNumber(),
// Week (none implemented)
//'W': weekNumber(),
 
//-- Month
// Short month, like 'Jan'
'b': lang.shortMonths[month],
// Long month, like 'January'
'B': lang.months[month],
// Two digit month number, 01 through 12
'm': pad(month + 1),
//-- Month
// Short month, like 'Jan'
'b': lang.shortMonths[month],
// Long month, like 'January'
'B': lang.months[month],
// Two digit month number, 01 through 12
'm': pad(month + 1),
 
//-- Year
// Two digits year, like 09 for 2009
'y': fullYear.toString().substr(2, 2),
// Four digits year, like 2009
'Y': fullYear,
//-- Year
// Two digits year, like 09 for 2009
'y': fullYear.toString().substr(2, 2),
// Four digits year, like 2009
'Y': fullYear,
 
//-- Time
// Two digits hours in 24h format, 00 through 23
'H': pad(hours),
// Hours in 24h format, 0 through 23
'k': hours,
// Two digits hours in 12h format, 00 through 11
'I': pad((hours % 12) || 12),
// Hours in 12h format, 1 through 12
'l': (hours % 12) || 12,
// Two digits minutes, 00 through 59
'M': pad(date[D.hcGetMinutes]()),
// Upper case AM or PM
'p': hours < 12 ? 'AM' : 'PM',
// Lower case AM or PM
'P': hours < 12 ? 'am' : 'pm',
// Two digits seconds, 00 through 59
'S': pad(date.getSeconds()),
// Milliseconds (naming from Ruby)
'L': pad(Math.round(timestamp % 1000), 3)
}, H.dateFormats);
//-- Time
// Two digits hours in 24h format, 00 through 23
'H': pad(hours),
// Hours in 24h format, 0 through 23
'k': hours,
// Two digits hours in 12h format, 00 through 11
'I': pad((hours % 12) || 12),
// Hours in 12h format, 1 through 12
'l': (hours % 12) || 12,
// Two digits minutes, 00 through 59
'M': pad(date[D.hcGetMinutes]()),
// Upper case AM or PM
'p': hours < 12 ? 'AM' : 'PM',
// Lower case AM or PM
'P': hours < 12 ? 'am' : 'pm',
// Two digits seconds, 00 through 59
'S': pad(date.getSeconds()),
// Milliseconds (naming from Ruby)
'L': pad(Math.round(timestamp % 1000), 3)
},
 
/**
* 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.
*
* @type {Object}
* @name dateFormats
* @memberOf Highcharts
* @sample highcharts/global/dateformats/ Adding support for week
* number
*/
H.dateFormats
);
 
 
// Do the replaces
for (key in replacements) {
H.objectEach(replacements, function(val, key) {
// Regex would do it in one line, but this is faster
while (format.indexOf('%' + key) !== -1) {
format = format.replace(
'%' + key,
typeof replacements[key] === 'function' ?
replacements[key](timestamp) :
replacements[key]
typeof val === 'function' ? val(timestamp) : val
);
}
}
 
});
 
// Optionally capitalize the string and return
return capitalize ?
format.substr(0, 1).toUpperCase() + format.substr(1) :
@@ -1262,17 +1336,16 @@
* @returns {void}
*/
H.destroyObjectProperties = function(obj, except) {
var n;
for (n in obj) {
H.objectEach(obj, function(val, n) {
// If the object is non-null and destroy is defined
if (obj[n] && obj[n] !== except && obj[n].destroy) {
if (val && val !== except && val.destroy) {
// Invoke the destroy
obj[n].destroy();
val.destroy();
}
 
// Delete the property from the object.
delete obj[n];
}
});
};
 
 
@@ -1374,10 +1447,12 @@
* @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.
* in the lang options, or a dot.
* @param {String} [thousandsSep] - The thousands separator, defaults to the one
* given in the lang options.
* given in the lang options, or a space character.
* @returns {String} The formatted number.
*
* @sample members/highcharts-numberformat/ Custom number format
*/
H.numberFormat = function(number, decimals, decimalPoint, thousandsSep) {
number = +number || 0;
@@ -1452,9 +1527,10 @@
* @memberOf Highcharts
* @param {HTMLDOMElement} el - A HTML element.
* @param {String} prop - The property name.
* @param {Boolean} [toInt=true] - Parse to integer.
* @returns {Number} - The numeric value.
*/
H.getStyle = function(el, prop) {
H.getStyle = function(el, prop, toInt) {
 
var style;
 
@@ -1471,7 +1547,13 @@
 
// Otherwise, get the computed style
style = win.getComputedStyle(el, undefined);
return style && H.pInt(style.getPropertyValue(prop));
if (style) {
style = style.getPropertyValue(prop);
if (H.pick(toInt, true)) {
style = H.pInt(style);
}
}
return style;
};
 
/**
@@ -1606,6 +1688,26 @@
};
 
/**
* Iterate over object key pairs in an object.
*
* @function #objectEach
* @memberOf Highcharts
* @param {Object} obj - The object to iterate over.
* @param {Function} 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 {Object} ctx The context
*/
H.objectEach = function(obj, fn, ctx) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
fn.call(ctx, obj[key], key, obj);
}
}
};
 
/**
* Add an event listener.
*
* @function #addEvent
@@ -1684,8 +1786,7 @@
 
function removeAllEvents() {
var types,
len,
n;
len;
 
if (!el.nodeName) {
return; // break on non-DOM events
@@ -1698,7 +1799,7 @@
types = hcEvents;
}
 
for (n in types) {
H.objectEach(types, function(val, n) {
if (hcEvents[n]) {
len = hcEvents[n].length;
while (len--) {
@@ -1705,7 +1806,7 @@
removeOneEvent(n, hcEvents[n][len]);
}
}
}
});
}
 
if (hcEvents) {
@@ -1841,8 +1942,7 @@
unit = '',
end,
fx,
args,
prop;
args;
 
if (!H.isObject(opt)) { // Number or undefined/null
args = arguments;
@@ -1860,8 +1960,7 @@
(Math[opt.easing] || Math.easeInOutSine);
opt.curAnim = H.merge(params);
 
for (prop in params) {
 
H.objectEach(params, function(val, prop) {
// Stop current running animation of this property
H.stop(el, prop);
 
@@ -1887,13 +1986,13 @@
}
 
if (!end) {
end = params[prop];
end = val;
}
if (end && end.match && end.match('px')) {
end = end.replace(/px/g, ''); // #4351
}
fx.run(start, end, unit);
}
});
};
 
/**
@@ -1919,6 +2018,10 @@
var defaultOptions = H.getOptions(),
seriesTypes = H.seriesTypes;
 
if (seriesTypes[type]) {
return H.error(27); // Series type already defined
}
 
// Merge the options
defaultOptions.plotOptions[type] = H.merge(
defaultOptions.plotOptions[parent],
@@ -2142,6 +2245,7 @@
// Collection of named colors. Can be extended from the outside by adding
// colors to Highcharts.Color.prototype.names.
names: {
none: 'rgba(255,255,255,0)',
white: '#ffffff',
black: '#000000'
},
@@ -2283,6 +2387,45 @@
setOpacity: function(alpha) {
this.rgba[3] = alpha;
return this;
},
 
/*
* Return an intermediate color between two colors.
*
* @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 {String}
* The intermediate color in rgba notation.
*/
tweenTo: function(to, pos) {
// Check for has alpha, because rgba colors perform worse due to lack of
// support in WebKit.
var from = this,
hasAlpha,
ret;
 
// Unsupported color, return to-color (#3920)
if (!to.rgba.length) {
ret = to.input || 'none';
 
// Interpolate
} else {
from = from.rgba;
to = to.rgba;
hasAlpha = (to[3] !== 1 || from[3] !== 1);
ret = (hasAlpha ? 'rgba(' : 'rgb(') +
Math.round(to[0] + (from[0] - to[0]) * (1 - pos)) + ',' +
Math.round(to[1] + (from[1] - to[1]) * (1 - pos)) + ',' +
Math.round(to[2] + (from[2] - to[2]) * (1 - pos)) +
(hasAlpha ?
(',' + (to[3] + (from[3] - to[3]) * (1 - pos))) :
'') + ')';
}
return ret;
}
};
H.color = function(input) {
@@ -2324,6 +2467,7 @@
isWebKit = H.isWebKit,
merge = H.merge,
noop = H.noop,
objectEach = H.objectEach,
pick = H.pick,
pInt = H.pInt,
removeEvent = H.removeEvent,
@@ -2339,23 +2483,25 @@
*/
/**
* The SVGElement prototype is a JavaScript wrapper for SVG elements used in the
* rendering layer of Highcharts. Combined with the {@link 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.
* 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 SVGRenderer} object, like [rect]{@link SVGRenderer#rect},
* [path]{@link SVGRenderer#path}, [text]{@link SVGRenderer#text}, [label]{@link
* SVGRenderer#label}, [g]{@link SVGRenderer#g} and more.
* {@link Highcharts.SVGRenderer} object, like
* [rect]{@link Highcharts.SVGRenderer#rect}, [path]{@link
* Highcharts.SVGRenderer#path}, [text]{@link Highcharts.SVGRenderer#text},
* [label]{@link Highcharts.SVGRenderer#label}, [g]{@link
* Highcharts.SVGRenderer#g} and more.
*
* @class
* @class Highcharts.SVGElement
*/
SVGElement = H.SVGElement = function() {
return this;
};
SVGElement.prototype = {
extend(SVGElement.prototype, /** @lends Highcharts.SVGElement.prototype */ {
 
// Default base for animation
opacity: 1,
@@ -2374,8 +2520,10 @@
* Initialize the SVG renderer. This function only exists to make the
* initiation process overridable. It should not be called directly.
*
* @param {SVGRenderer} renderer The SVGRenderer instance to initialize to.
* @param {String} nodeName The SVG node name.
* @param {HighchartsSVGRenderer} renderer
* The SVGRenderer instance to initialize to.
* @param {String} nodeName
* The SVG node name.
* @returns {void}
*/
init: function(renderer, nodeName) {
@@ -2391,7 +2539,7 @@
 
/**
* The renderer that the SVGElement belongs to.
* @type {SVGRenderer}
* @type {Highcharts.SVGRenderer}
*/
this.renderer = renderer;
},
@@ -2402,7 +2550,11 @@
* @param {SVGAttributes} params SVG attributes or CSS to animate.
* @param {AnimationOptions} [options] Animation options.
* @param {Function} [complete] Function to perform at the end of animation.
* @returns {SVGElement} Returns the SVGElement for chaining.
*
* @sample highcharts/members/element-on/
* Setting some attributes by animation
*
* @returns {Highcharts.SVGElement} Returns the SVGElement for chaining.
*/
animate: function(params, options, complete) {
var animOptions = H.animObject(
@@ -2482,7 +2634,6 @@
stopColor,
stopOpacity,
radialReference,
n,
id,
key = [],
value;
@@ -2527,14 +2678,14 @@
}
 
// Build the unique key to detect whether we need to create a new element (#1282)
for (n in gradAttr) {
objectEach(gradAttr, function(val, n) {
if (n !== 'id') {
key.push(n, gradAttr[n]);
key.push(n, val);
}
}
for (n in stops) {
key.push(stops[n]);
}
});
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
@@ -2723,10 +2874,10 @@
* 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
* @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 - Used internally when `.attr` is
* @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.
*
@@ -2733,6 +2884,9 @@
* @returns {SVGElement|string|number} If used as a setter, it returns the
* current {@link SVGElement} so the calls can be chained. If used as a
* getter, the current value of the attribute is returned.
*
* @sample highcharts/members/renderer-rect/
* Setting some attributes
*
* @example
* // Set multiple attributes
@@ -2752,7 +2906,6 @@
*/
attr: function(hash, val, complete, continueAnimation) {
var key,
value,
element = this.element,
hasSetSymbolSize,
ret = this,
@@ -2773,8 +2926,7 @@
// setter
} else {
 
for (key in hash) {
value = hash[key];
objectEach(hash, function(val, key) {
skipAttr = false;
 
// Unless .attr is from the animator update, stop current
@@ -2802,24 +2954,18 @@
 
if (!skipAttr) {
setter = this[key + 'Setter'] || this._defaultSetter;
setter.call(this, value, key, element);
setter.call(this, val, key, element);
 
 
// Let the shadow follow the main element
if (this.shadows && /^(width|height|visibility|x|y|d|transform|cx|cy|r)$/.test(key)) {
this.updateShadows(key, value, setter);
this.updateShadows(key, val, setter);
}
 
}
}
}, this);
 
// Update transform. Do this outside the loop to prevent redundant updating for batch setting
// of attributes.
if (this.doTransform) {
this.updateTransform();
this.doTransform = false;
}
 
this.afterSetters();
}
 
// In accordance with animate, run a complete callback
@@ -2830,7 +2976,21 @@
return ret;
},
 
/**
* 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.
*/
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;
}
},
 
 
/**
* Update the shadow elements with new attributes.
*
@@ -2865,7 +3025,7 @@
* @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.
* @returns {SVGElement} Return the SVG element for chainability.
* @returns {Highcharts.SVGElement} Return the SVG element for chainability.
*/
addClass: function(className, replace) {
var currentClassName = this.attr('class') || '';
@@ -2893,7 +3053,7 @@
/**
* Remove a class name from the element.
* @param {string} className The class name to remove.
* @return {SVGElement} Returns the SVG element for chainability.
* @return {Highcharts.SVGElement} Returns the SVG element for chainability.
*/
removeClass: function(className) {
attr(this.element, 'class', (attr(this.element, 'class') || '').replace(className, ''));
@@ -2930,7 +3090,7 @@
*
* @param {ClipRect} [clipRect] - The clipping rectangle. If skipped, the
* current clip is removed.
* @returns {SVGElement} Returns the SVG element to allow chaining.
* @returns {Highcharts.SVGElement} Returns the SVG element to allow chaining.
*/
clip: function(clipRect) {
return this.attr(
@@ -2960,7 +3120,6 @@
crisp: function(rect, strokeWidth) {
 
var wrapper = this,
key,
attribs = {},
normalizer;
 
@@ -2976,11 +3135,11 @@
rect.strokeWidth = strokeWidth;
}
 
for (key in rect) {
if (wrapper[key] !== rect[key]) { // only set attribute if changed
wrapper[key] = attribs[key] = rect[key];
objectEach(rect, function(val, key) {
if (wrapper[key] !== val) { // only set attribute if changed
wrapper[key] = attribs[key] = val;
}
}
});
 
return attribs;
},
@@ -2991,7 +3150,10 @@
* Highcharts, like `width`, `ellipsis` and `textOverflow` for SVG text
* elements.
* @param {CSSObject} styles The new CSS styles.
* @returns {SVGElement} Return the SVG element for chaining.
* @returns {Highcharts.SVGElement} Return the SVG element for chaining.
*
* @sample highcharts/members/renderer-text-on-chart/
* Styled text
*/
css: function(styles) {
var oldStyles = this.styles,
@@ -2998,7 +3160,6 @@
newStyles = {},
elem = this.element,
textWidth,
n,
serializedCss = '',
hyphenate,
hasNew = !oldStyles,
@@ -3015,12 +3176,12 @@
 
// Filter out existing styles to increase performance (#2640)
if (oldStyles) {
for (n in styles) {
if (styles[n] !== oldStyles[n]) {
newStyles[n] = styles[n];
objectEach(styles, function(style, n) {
if (style !== oldStyles[n]) {
newStyles[n] = style;
hasNew = true;
}
}
});
}
if (hasNew) {
 
@@ -3055,13 +3216,13 @@
hyphenate = function(a, b) {
return '-' + b.toLowerCase();
};
for (n in styles) {
objectEach(styles, function(style, n) {
if (inArray(n, svgPseudoProps) === -1) {
serializedCss +=
n.replace(/([A-Z])/g, hyphenate) + ':' +
styles[n] + ';';
style + ';';
}
}
});
if (serializedCss) {
attr(elem, 'style', serializedCss); // #1881
}
@@ -3107,7 +3268,10 @@
* touch devices, to prevent the browser from waiting for a click event
* from firing.
* @param {Function} handler - The handler callback.
* @returns {SVGElement} The SVGElement for chaining.
* @returns {Highcharts.SVGElement} The SVGElement for chaining.
*
* @sample highcharts/members/element-on/
* A clickable rectangle
*/
on: function(eventType, handler) {
var svgElement = this,
@@ -3140,7 +3304,7 @@
*
* @param {Array} coordinates The center reference. The format is
* `[centerX, centerY, diameter]` in pixels.
* @returns {SVGElement} Returns the SVGElement for chaining.
* @returns {Highcharts.SVGElement} Returns the SVGElement for chaining.
*/
setRadialReference: function(coordinates) {
var existingGradient = this.renderer.gradients[this.element.gradient];
@@ -3181,7 +3345,7 @@
*
* @param {boolean} inverted - Whether to invert or not. An inverted shape
* can be un-inverted by setting it to false.
* @returns {SVGElement} Return the SVGElement for chaining.
* @returns {Highcharts.SVGElement} Return the SVGElement for chaining.
*/
invert: function(inverted) {
var wrapper = this;
@@ -3239,9 +3403,12 @@
},
 
/**
* Bring the element to the front.
* Bring the element to the front. Alternatively, a new zIndex can be set.
*
* @returns {SVGElement} Returns the SVGElement for chaining.
* @returns {Highcharts.SVGElement} Returns the SVGElement for chaining.
*
* @sample highcharts/members/element-tofront/
* Click an element to bring it to front
*/
toFront: function() {
var element = this.element;
@@ -3252,7 +3419,7 @@
 
/**
* Align the element relative to the chart or another box.
* ß
*
* @param {Object} [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.
@@ -3270,7 +3437,7 @@
* 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.
* @returns {SVGElement} Returns the SVGElement for chaining.
* @returns {Highcharts.SVGElement} Returns the SVGElement for chaining.
*/
align: function(alignOptions, alignByTranslate, box) {
var align,
@@ -3356,6 +3523,9 @@
* would be have been if it were not rotated.
* @returns {Object} The bounding box with `x`, `y`, `width` and `height`
* properties.
*
* @sample highcharts/members/renderer-on-chart/
* Draw a rectangle based on a text's bounding box
*/
getBBox: function(reload, rot) {
var wrapper = this,
@@ -3516,7 +3686,7 @@
* `inherit` rather than `visible`. The difference is that an element with
* `visibility="visible"` will be visible even if the parent is hidden.
*
* @returns {SVGElement} Returns the SVGElement for chaining.
* @returns {Highcharts.SVGElement} Returns the SVGElement for chaining.
*/
show: function(inherit) {
return this.attr({
@@ -3528,7 +3698,7 @@
* Hide the element, equivalent to setting the `visibility` attribute to
* `hidden`.
*
* @returns {SVGElement} Returns the SVGElement for chaining.
* @returns {Highcharts.SVGElement} Returns the SVGElement for chaining.
*/
hide: function() {
return this.attr({
@@ -3559,10 +3729,11 @@
/**
* Add the element to the DOM. All elements must be added this way.
*
* @param {SVGElement|SVGDOMElement} [parent] The parent item to add it to.
* If undefined, the element is added to the {@link SVGRenderer.box}.
* @param {Highcharts.SVGElement|SVGDOMElement} [parent] The parent item to add it to.
* If undefined, the element is added to the {@link
* Highcharts.SVGRenderer.box}.
*
* @returns {SVGElement} Returns the SVGElement for chaining.
* @returns {Highcharts.SVGElement} Returns the SVGElement for chaining.
*
* @sample highcharts/members/renderer-g - Elements added to a group
*/
@@ -3628,23 +3799,29 @@
destroy: function() {
var wrapper = this,
element = wrapper.element || {},
parentToClean = wrapper.renderer.isSVG && element.nodeName === 'SPAN' && wrapper.parentGroup,
parentToClean =
wrapper.renderer.isSVG &&
element.nodeName === 'SPAN' &&
wrapper.parentGroup,
grandParent,
key,
ownerSVGElement = element.ownerSVGElement,
i;
 
// remove events
element.onclick = element.onmouseout = element.onmouseover = element.onmousemove = element.point = null;
element.onclick = element.onmouseout = element.onmouseover =
element.onmousemove = element.point = null;
stop(wrapper); // stop running animations
 
if (wrapper.clipPath) {
if (wrapper.clipPath && ownerSVGElement) {
// Look for existing references to this clipPath and remove them
// before destroying the element (#6196).
each(
wrapper.element.ownerSVGElement.querySelectorAll('[clip-path]'),
ownerSVGElement.querySelectorAll('[clip-path]'),
function(el) {
// Include the closing paranthesis in the test to rule out
// id's from 10 and above (#6550)
if (el.getAttribute('clip-path')
.indexOf(wrapper.clipPath.element.id) > -1) {
.indexOf(wrapper.clipPath.element.id + ')') > -1) {
el.removeAttribute('clip-path');
}
}
@@ -3680,9 +3857,9 @@
erase(wrapper.renderer.alignedObjects, wrapper);
}
 
for (key in wrapper) {
objectEach(wrapper, function(val, key) {
delete wrapper[key];
}
});
 
return null;
},
@@ -3705,13 +3882,13 @@
* @param {boolean|ShadowOptions} shadowOptions The shadow options. If
* `true`, the default options are applied. If `false`, the current
* shadow will be removed.
* @param {SVGElement} [group] The SVG group element where the shadows will
* @param {Highcharts.SVGElement} [group] The SVG group element where the shadows will
* be applied. The default is to add it to the same parent as the current
* element. Internally, this is ised for pie slices, where all the
* shadows are added to an element behind all the slices.
* @param {boolean} [cutOff] Used internally for column shadows.
*
* @returns {SVGElement} Returns the SVGElement for chaining.
* @returns {Highcharts.SVGElement} Returns the SVGElement for chaining.
*
* @example
* renderer.rect(10, 100, 100, 100)
@@ -3796,7 +3973,8 @@
 
/**
* Get the current value of an attribute or pseudo attribute, used mainly
* for animation. Called internally from the {@link SVGRenderer#attr}
* for animation. Called internally from the {@link
* Highcharts.SVGRenderer#attr}
* function.
*
* @private
@@ -3971,7 +4149,7 @@
_defaultSetter: function(value, key, element) {
element.setAttribute(key, value);
}
};
});
 
// Some shared setters and getters
SVGElement.prototype.yGetter = SVGElement.prototype.xGetter;
@@ -4023,16 +4201,16 @@
* @sample highcharts/members/renderer-on-chart - Annotating a chart programmatically.
* @sample highcharts/members/renderer-basic - Independedt SVG drawing.
*
* @class
* @class Highcharts.SVGRenderer
*/
SVGRenderer = H.SVGRenderer = function() {
this.init.apply(this, arguments);
};
SVGRenderer.prototype = {
extend(SVGRenderer.prototype, /** @lends Highcharts.SVGRenderer.prototype */ {
/**
* A pointer to the renderer's associated Element class. The VMLRenderer
* will have a pointer to VMLElement here.
* @type {SVGElement}
* @type {Highcharts.SVGElement}
*/
Element: SVGElement,
SVG_NS: SVG_NS,
@@ -4071,7 +4249,7 @@
this.box = element;
/**
* The wrapper for the root `svg` node of the renderer.
* @type {SVGElement}
* @type {Highcharts.SVGElement}
*/
this.boxWrapper = boxWrapper;
renderer.alignedObjects = [];
@@ -4091,7 +4269,7 @@
 
// Add description
desc = this.createElement('desc').add();
desc.element.appendChild(doc.createTextNode('Created with Highstock 5.0.10'));
desc.element.appendChild(doc.createTextNode('Created with Highstock 5.0.12'));
 
 
renderer.defs = this.createElement('defs').add();
@@ -4208,7 +4386,7 @@
* SVGRenderer#rect} or {@link SVGRenderer#text}.
*
* @param {string} nodeName - The node name, for example `rect`, `g` etc.
* @returns {SVGElement} The generated SVGElement.
* @returns {Highcharts.SVGElement} The generated SVGElement.
*/
createElement: function(nodeName) {
var wrapper = new this.Element();
@@ -4296,7 +4474,7 @@
* CSS text features like `width`, `text-overflow`, `white-space`, and
* also attributes like `href` and `style`.
* @private
* @param {SVGElement} wrapper The parent SVGElement.
* @param {Highcharts.SVGElement} wrapper The parent SVGElement.
*/
buildText: function(wrapper) {
var textNode = wrapper.element,
@@ -4373,7 +4551,7 @@
 
clsRegex = /<.*class="([^"]+)".*>/;
styleRegex = /<.*style="([^"]+)".*>/;
hrefRegex = /<.*href="(http[^"]+)".*>/;
hrefRegex = /<.*href="([^"]+)".*>/;
 
if (tempParent) {
tempParent.appendChild(textNode); // attach it to the DOM to read offset width
@@ -4760,13 +4938,19 @@
* var path = renderer.path(['M', 10, 10, 'L', 30, 30, 'z'])
* .attr({ stroke: '#ff00ff' })
* .add();
* @returns {SVGElement} The generated wrapper element.
* @returns {Highcharts.SVGElement} The generated wrapper 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
*
*/
/**
* Draw a path, wraps the SVG `path` element.
*
* @param {SVGAttributes} [attribs] The initial attributes.
* @returns {SVGElement} The generated wrapper element.
* @returns {Highcharts.SVGElement} The generated wrapper element.
*/
path: function(path) {
var attribs = {
@@ -4788,13 +4972,15 @@
* @param {number} [x] The center x position.
* @param {number} [y] The center y position.
* @param {number} [r] The radius.
* @returns {SVGElement} The generated wrapper element.
* @returns {Highcharts.SVGElement} The generated wrapper element.
*
* @sample highcharts/members/renderer-circle/ Drawing a circle
*/
/**
* Draw a circle, wraps the SVG `circle` element.
*
* @param {SVGAttributes} [attribs] The initial attributes.
* @returns {SVGElement} The generated wrapper element.
* @returns {Highcharts.SVGElement} The generated wrapper element.
*/
circle: function(x, y, r) {
var attribs = isObject(x) ? x : {
@@ -4822,12 +5008,15 @@
* 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.
* @returns {SVGElement} The generated wrapper element.
* @returns {Highcharts.SVGElement} The generated wrapper element.
*
* @sample highcharts/members/renderer-arc/
* Drawing an arc
*/
/**
* Draw and return an arc. Overloaded function that takes arguments object.
* @param {SVGAttributes} attribs Initial SVG attributes.
* @returns {SVGElement} The generated wrapper element.
* @returns {Highcharts.SVGElement} The generated wrapper element.
*/
arc: function(x, y, r, innerR, start, end) {
var arc,
@@ -4865,13 +5054,19 @@
* @param {number} [r] Border corner radius.
* @param {number} [strokeWidth] A stroke width can be supplied to allow
* crisp drawing.
* @returns {SVGElement} The generated wrapper element.
* @returns {Highcharts.SVGElement} The generated wrapper element.
*/
/**
* Draw and return a rectangle.
* @param {SVGAttributes} [attributes] General SVG attributes for the
* rectangle.
* @returns {SVGElement} The generated wrapper element.
* @param {SVGAttributes} [attributes]
* General SVG attributes for the rectangle.
* @return {Highcharts.SVGElement}
* The generated wrapper element.
*
* @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
*/
rect: function(x, y, width, height, r, strokeWidth) {
 
@@ -4940,11 +5135,16 @@
},
 
/**
* Create and return an svg group element.
* 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()}.
*
* @param {string} [name] The group will be given a class name of
* `highcharts-{name}`. This can be used for styling and scripting.
* @returns {SVGElement} The generated wrapper element.
* @returns {Highcharts.SVGElement} The generated wrapper element.
*
* @sample highcharts/members/renderer-g/
* Show and hide grouped objects
*/
g: function(name) {
var elem = this.createElement('g');
@@ -4962,7 +5162,12 @@
* image file width.
* @param {number} [height] The image height. If omitted it defaults to the
* image file height.
* @returns {SVGElement} The generated wrapper element.
* @returns {Highcharts.SVGElement} The generated wrapper element.
*
* @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
*/
image: function(src, x, y, width, height) {
var attribs = {
@@ -5021,12 +5226,17 @@
 
var ren = this,
obj,
imageRegex = /^url\((.*?)\)$/,
isImage = imageRegex.test(symbol),
sym = !isImage && (this.symbols[symbol] ? symbol : 'circle'),
 
 
// get the symbol definition function
symbolFn = this.symbols[symbol],
symbolFn = sym && this.symbols[sym],
 
// check if there's a path defined for this symbol
path = defined(x) && symbolFn && this.symbols[symbol](
path = defined(x) && symbolFn && symbolFn.call(
this.symbols,
Math.round(x),
Math.round(y),
width,
@@ -5033,7 +5243,6 @@
height,
options
),
imageRegex = /^url\((.*?)\)$/,
imageSrc,
centerImage;
 
@@ -5046,7 +5255,7 @@
 
// expando properties for use in animate and attr
extend(obj, {
symbolName: symbol,
symbolName: sym,
x: x,
y: y,
width: width,
@@ -5057,8 +5266,8 @@
}
 
 
// image symbols
} else if (imageRegex.test(symbol)) {
// Image symbols
} else if (isImage) {
 
 
imageSrc = symbol.match(imageRegex)[1];
@@ -5371,7 +5580,7 @@
},
 
/**
* @typedef {SVGElement} ClipRect - A clipping rectangle that can be applied
* @typedef {Highcharts.SVGElement} ClipRect - A clipping rectangle that can be applied
* to one or more {@link SVGElement} instances. It is instanciated with the
* {@link SVGRenderer#clipRect} function and applied with the {@link
* SVGElement#clip} function.
@@ -5415,11 +5624,27 @@
 
 
/**
* Add text to the SVG object
* @param {String} str
* @param {number} x Left position
* @param {number} y Top position
* @param {Boolean} useHTML Use HTML to render the text
* 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' })`.
* @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.
*
* @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
*/
text: function(str, x, y, useHTML) {
 
@@ -5534,20 +5759,42 @@
},
 
/**
* Add a label, a text item that can hold a colored or gradient background
* as well as a border and shadow. Supported custom attributes include
* `padding`.
* 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' })`.
*
* @param {string} str
* @param {number} x
* @param {number} y
* @param {String} shape
* @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
* @param {Boolean} baseline Whether to position the label relative to the text baseline,
* like renderer.text, or to the upper border of the rectangle.
* @param {String} className Class name for the group
* @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
* 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} baseline
* 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.
*
* @sample highcharts/members/renderer-label-on-chart/
* A label on the chart
*/
label: function(str, x, y, shape, anchorX, anchorY, useHTML, baseline, className) {
 
@@ -5772,11 +6019,11 @@
};
 
wrapper.anchorXSetter = function(value, key) {
anchorX = value;
anchorX = wrapper.anchorX = value;
boxAttr(key, Math.round(value) - getCrispAdjust() - wrapperX);
};
wrapper.anchorYSetter = function(value, key) {
anchorY = value;
anchorY = wrapper.anchorY = value;
boxAttr(key, value - wrapperY);
};
 
@@ -5867,7 +6114,7 @@
}
});
}
}; // end SVGRenderer
}); // end SVGRenderer
 
 
// general renderer
@@ -7435,7 +7682,7 @@
useUTC: true,
//timezoneOffset: 0,
 
VMLRadialGradientURL: 'http://code.highcharts.com/5.0.10/gfx/vml-radial-gradient.png'
VMLRadialGradientURL: 'http://code.highcharts.com/5.0.12/gfx/vml-radial-gradient.png'
 
},
chart: {
@@ -7551,7 +7798,8 @@
itemStyle: {
color: '#333333',
fontSize: '12px',
fontWeight: 'bold'
fontWeight: 'bold',
textOverflow: 'ellipsis'
},
itemHoverStyle: {
//cursor: 'pointer', removed as of #601
@@ -7745,8 +7993,15 @@
}
 
/**
* Merge the default options with custom options and return the new options structure
* @param {Object} options The new custom options
* Merge the default options with custom options and return the new options
* structure. Commonly used for defining reusable templates.
*
* @function #setOptions
* @memberOf Highcharts
* @sample highcharts/global/useutc-false Setting a global option
* @sample highcharts/members/setoptions Applying a global theme
* @param {Object} options The new custom chart options.
* @returns {Object} Updated options.
*/
H.setOptions = function(options) {
 
@@ -7781,339 +8036,6 @@
*
* License: www.highcharts.com/license
*/
var arrayMax = H.arrayMax,
arrayMin = H.arrayMin,
defined = H.defined,
destroyObjectProperties = H.destroyObjectProperties,
each = H.each,
erase = H.erase,
merge = H.merge,
pick = H.pick;
/*
* The object wrapper for plot lines and plot bands
* @param {Object} options
*/
H.PlotLineOrBand = function(axis, options) {
this.axis = axis;
 
if (options) {
this.options = options;
this.id = options.id;
}
};
 
H.PlotLineOrBand.prototype = {
 
/**
* Render the plot line or plot band. If it is already existing,
* move it.
*/
render: function() {
var plotLine = this,
axis = plotLine.axis,
horiz = axis.horiz,
options = plotLine.options,
optionsLabel = options.label,
label = plotLine.label,
to = options.to,
from = options.from,
value = options.value,
isBand = defined(from) && defined(to),
isLine = defined(value),
svgElem = plotLine.svgElem,
isNew = !svgElem,
path = [],
addEvent,
eventType,
color = options.color,
zIndex = pick(options.zIndex, 0),
events = options.events,
attribs = {
'class': 'highcharts-plot-' + (isBand ? 'band ' : 'line ') + (options.className || '')
},
groupAttribs = {},
renderer = axis.chart.renderer,
groupName = isBand ? 'bands' : 'lines',
group,
log2lin = axis.log2lin;
 
// logarithmic conversion
if (axis.isLog) {
from = log2lin(from);
to = log2lin(to);
value = log2lin(value);
}
 
 
// Set the presentational attributes
if (isLine) {
attribs = {
stroke: color,
'stroke-width': options.width
};
if (options.dashStyle) {
attribs.dashstyle = options.dashStyle;
}
 
} else if (isBand) { // plot band
if (color) {
attribs.fill = color;
}
if (options.borderWidth) {
attribs.stroke = options.borderColor;
attribs['stroke-width'] = options.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 (isNew) {
plotLine.svgElem = svgElem =
renderer
.path()
.attr(attribs).add(group);
}
 
 
// Set the path or return
if (isLine) {
path = axis.getPlotLinePath(value, svgElem.strokeWidth());
} else if (isBand) { // plot band
path = axis.getPlotBandPath(from, to, options);
} else {
return;
}
 
 
// common for lines and bands
if (isNew && path && path.length) {
svgElem.attr({
d: path
});
 
// events
if (events) {
addEvent = function(eventType) {
svgElem.on(eventType, function(e) {
events[eventType].apply(plotLine, [e]);
});
};
for (eventType in events) {
addEvent(eventType);
}
}
} else if (svgElem) {
if (path) {
svgElem.show();
svgElem.animate({
d: path
});
} else {
svgElem.hide();
if (label) {
plotLine.label = label = label.destroy();
}
}
}
 
// the plot band/line label
if (optionsLabel && defined(optionsLabel.text) && path && path.length &&
axis.width > 0 && axis.height > 0 && !path.flat) {
// apply defaults
optionsLabel = merge({
align: horiz && isBand && 'center',
x: horiz ? !isBand && 4 : 10,
verticalAlign: !horiz && isBand && 'middle',
y: horiz ? isBand ? 16 : 10 : isBand ? 6 : -4,
rotation: horiz && !isBand && 90
}, optionsLabel);
 
this.renderLabel(optionsLabel, path, isBand, zIndex);
 
} else if (label) { // move out of sight
label.hide();
}
 
// chainable
return plotLine;
},
 
/**
* Render and align label for plot line or band.
*/
renderLabel: function(optionsLabel, path, isBand, zIndex) {
var plotLine = this,
label = plotLine.label,
renderer = plotLine.axis.chart.renderer,
attribs,
xs,
ys,
x,
y;
 
// add the SVG element
if (!label) {
attribs = {
align: optionsLabel.textAlign || optionsLabel.align,
rotation: optionsLabel.rotation,
'class': 'highcharts-plot-' + (isBand ? 'band' : 'line') + '-label ' + (optionsLabel.className || '')
};
 
attribs.zIndex = zIndex;
 
plotLine.label = label = renderer.text(
optionsLabel.text,
0,
0,
optionsLabel.useHTML
)
.attr(attribs)
.add();
 
 
label.css(optionsLabel.style);
 
}
 
// get the bounding box and align the label
// #3000 changed to better handle choice between plotband or plotline
xs = [path[1], path[4], (isBand ? path[6] : path[1])];
ys = [path[2], path[5], (isBand ? path[7] : path[2])];
x = arrayMin(xs);
y = arrayMin(ys);
 
label.align(optionsLabel, false, {
x: x,
y: y,
width: arrayMax(xs) - x,
height: arrayMax(ys) - y
});
label.show();
},
 
/**
* Remove the plot line or band
*/
destroy: function() {
// remove it from the lookup
erase(this.axis.plotLinesAndBands, this);
 
delete this.axis;
destroyObjectProperties(this);
}
};
 
/**
* Object with members for extending the Axis prototype
* @todo Extend directly instead of adding object to Highcharts first
*/
 
H.AxisPlotLineOrBandExtension = {
 
/**
* Create the path for a plot band
*/
getPlotBandPath: function(from, to) {
var toPath = this.getPlotLinePath(to, null, null, true),
path = this.getPlotLinePath(from, null, null, true),
// #4964 check if chart is inverted or plotband is on yAxis
horiz = this.horiz,
plus = 1,
outside =
(from < this.min && to < this.min) ||
(from > this.max && to > this.max);
 
if (path && toPath) {
 
// Flat paths don't need labels (#3836)
if (outside) {
path.flat = path.toString() === toPath.toString();
plus = 0;
}
 
// Add 1 pixel, when coordinates are the same
path.push(
horiz && toPath[4] === path[4] ? toPath[4] + plus : toPath[4], !horiz && toPath[5] === path[5] ? toPath[5] + plus : toPath[5],
horiz && toPath[1] === path[1] ? toPath[1] + plus : toPath[1], !horiz && toPath[2] === path[2] ? toPath[2] + plus : toPath[2]
);
} else { // outside the axis area
path = null;
}
 
return path;
},
 
addPlotBand: function(options) {
return this.addPlotBandOrLine(options, 'plotBands');
},
 
addPlotLine: function(options) {
return this.addPlotBandOrLine(options, 'plotLines');
},
 
/**
* Add a plot band or plot line after render time
*
* @param options {Object} The plotBand or plotLine configuration object
*/
addPlotBandOrLine: function(options, coll) {
var obj = new H.PlotLineOrBand(this, options).render(),
userOptions = this.userOptions;
 
if (obj) { // #2189
// Add it to the user options for exporting and Axis.update
if (coll) {
userOptions[coll] = userOptions[coll] || [];
userOptions[coll].push(options);
}
this.plotLinesAndBands.push(obj);
}
 
return obj;
},
 
/**
* Remove a plot band or plot line from the chart by id
* @param {Object} id
*/
removePlotBandOrLine: function(id) {
var plotLinesAndBands = this.plotLinesAndBands,
options = this.options,
userOptions = this.userOptions,
i = plotLinesAndBands.length;
while (i--) {
if (plotLinesAndBands[i].id === id) {
plotLinesAndBands[i].destroy();
}
}
each([options.plotLines || [], userOptions.plotLines || [], options.plotBands || [], userOptions.plotBands || []], function(arr) {
i = arr.length;
while (i--) {
if (arr[i].id === id) {
erase(arr, arr[i]);
}
}
});
}
};
 
}(Highcharts));
(function(H) {
/**
* (c) 2010-2017 Torstein Honsi
*
* License: www.highcharts.com/license
*/
var correctFloat = H.correctFloat,
defined = H.defined,
destroyObjectProperties = H.destroyObjectProperties,
@@ -8130,6 +8052,7 @@
this.pos = pos;
this.type = type || '';
this.isNew = true;
this.isNewLabel = true;
 
if (!type && !noLabel) {
this.addLabel();
@@ -8569,9 +8492,11 @@
// Set the new position, and show or hide
if (show && isNumber(xy.y)) {
xy.opacity = opacity;
label[tick.isNew ? 'attr' : 'animate'](xy);
label[tick.isNewLabel ? 'attr' : 'animate'](xy);
tick.isNewLabel = false;
} else {
label.attr('y', -9999); // #1338
tick.isNewLabel = true;
}
tick.isNew = false;
}
@@ -8618,7 +8543,7 @@
};
 
}(Highcharts));
(function(H) {
var Axis = (function(H) {
/**
* (c) 2010-2017 Torstein Honsi
*
@@ -8629,7 +8554,6 @@
animObject = H.animObject,
arrayMax = H.arrayMax,
arrayMin = H.arrayMin,
AxisPlotLineOrBandExtension = H.AxisPlotLineOrBandExtension,
color = H.color,
correctFloat = H.correctFloat,
defaultOptions = H.defaultOptions,
@@ -8648,8 +8572,8 @@
isString = H.isString,
merge = H.merge,
normalizeTickInterval = H.normalizeTickInterval,
objectEach = H.objectEach,
pick = H.pick,
PlotLineOrBand = H.PlotLineOrBand,
removeEvent = H.removeEvent,
splat = H.splat,
syncTimeout = H.syncTimeout,
@@ -8656,19 +8580,44 @@
Tick = H.Tick;
 
/**
* Create a new axis object.
* @constructor Axis
* @param {Object} chart
* @param {Object} options
* Create a new axis object. Called internally when instanciating 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 Highcharts.Axis
* @memberOf Highcharts
* @param {Highcharts.Chart} chart - The Chart instance to apply the axis on.
* @param {Object} options - Axis options
*/
H.Axis = function() {
var Axis = function() {
this.init.apply(this, arguments);
};
 
H.Axis.prototype = {
H.extend(Axis.prototype, /** @lends Highcharts.Axis.prototype */ {
 
/**
* Default options for the X axis - the Y axis has extended defaults
* Default options for the X axis - the Y axis has extended defaults.
*
* @private
* @type {Object}
*/
defaultOptions: {
// allowDecimals: null,
@@ -8741,6 +8690,7 @@
//text: null,
align: 'middle', // low, middle or high
//margin: 0 for horizontal, 10 for vertical axes,
// reserveSpace: true,
//rotation: 0,
//side: 'outside',
 
@@ -8770,7 +8720,10 @@
},
 
/**
* This options set extends the defaultOptions for Y axes
* This options set extends the defaultOptions for Y axes.
*
* @private
* @type {Object}
*/
defaultYAxisOptions: {
endOnTick: true,
@@ -8814,7 +8767,10 @@
},
 
/**
* These options extend the defaultOptions for left axes
* These options extend the defaultOptions for left axes.
*
* @private
* @type {Object}
*/
defaultLeftAxisOptions: {
labels: {
@@ -8826,7 +8782,10 @@
},
 
/**
* These options extend the defaultOptions for right axes
* These options extend the defaultOptions for right axes.
*
* @private
* @type {Object}
*/
defaultRightAxisOptions: {
labels: {
@@ -8838,7 +8797,10 @@
},
 
/**
* These options extend the defaultOptions for bottom axes
* These options extend the defaultOptions for bottom axes.
*
* @private
* @type {Object}
*/
defaultBottomAxisOptions: {
labels: {
@@ -8852,7 +8814,10 @@
}
},
/**
* These options extend the defaultOptions for top axes
* These options extend the defaultOptions for top axes.
*
* @private
* @type {Object}
*/
defaultTopAxisOptions: {
labels: {
@@ -8878,7 +8843,7 @@
axis.chart = chart;
 
// Flag, is the axis horizontal
axis.horiz = chart.inverted ? !isXAxis : isXAxis;
axis.horiz = chart.inverted && !axis.isZAxis ? !isXAxis : isXAxis;
 
// Flag, isXAxis
axis.isXAxis = isXAxis;
@@ -8896,7 +8861,8 @@
type = options.type,
isDatetimeAxis = type === 'datetime';
 
axis.labelFormatter = options.labels.formatter || axis.defaultLabelFormatter; // can be overwritten by dynamic format
axis.labelFormatter = options.labels.formatter ||
axis.defaultLabelFormatter; // can be overwritten by dynamic format
 
 
// Flag, stagger lines or not
@@ -8933,13 +8899,6 @@
// Linked axis.
//axis.linkedParent = undefined;
 
// Tick positions
//axis.tickPositions = undefined; // array containing predefined positions
// Tick intervals
//axis.tickInterval = undefined;
//axis.minorTickInterval = undefined;
 
 
// Major ticks
axis.ticks = {};
axis.labelEdge = [];
@@ -8991,14 +8950,16 @@
//axis.userMax = undefined,
 
// Crosshair options
axis.crosshair = pick(options.crosshair, splat(chart.options.tooltip.crosshairs)[isXAxis ? 0 : 1], false);
// Run Axis
axis.crosshair = pick(
options.crosshair,
splat(chart.options.tooltip.crosshairs)[isXAxis ? 0 : 1],
false
);
 
var eventType,
events = axis.options.events;
var events = axis.options.events;
 
// Register
if (inArray(axis, chart.axes) === -1) { // don't add it again on Axis.update()
// Register. Don't add it again on Axis.update().
if (inArray(axis, chart.axes) === -1) { //
if (isXAxis) { // #2713
chart.axes.splice(chart.xAxis.length, 0, axis);
} else {
@@ -9011,18 +8972,14 @@
axis.series = axis.series || []; // populated by Series
 
// inverted charts have reversed xAxes as default
if (chart.inverted && isXAxis && axis.reversed === undefined) {
if (chart.inverted && !axis.isZAxis && isXAxis && axis.reversed === undefined) {
axis.reversed = true;
}
 
axis.removePlotBand = axis.removePlotBandOrLine;
axis.removePlotLine = axis.removePlotBandOrLine;
 
 
// register event listeners
for (eventType in events) {
addEvent(axis, eventType, events[eventType]);
}
objectEach(events, function(event, eventType) {
addEvent(axis, eventType, event);
});
 
// extend logarithmic axis
axis.lin2log = options.linearToLogConverter || axis.lin2log;
@@ -9038,8 +8995,11 @@
setOptions: function(userOptions) {
this.options = merge(
this.defaultOptions,
this.coll === 'yAxis' && this.defaultYAxisOptions, [this.defaultTopAxisOptions, this.defaultRightAxisOptions,
this.defaultBottomAxisOptions, this.defaultLeftAxisOptions
this.coll === 'yAxis' && this.defaultYAxisOptions, [
this.defaultTopAxisOptions,
this.defaultRightAxisOptions,
this.defaultBottomAxisOptions,
this.defaultLeftAxisOptions
][this.side],
merge(
defaultOptions[this.coll], // if set in setOptions (#1053)
@@ -9049,7 +9009,12 @@
},
 
/**
* The default label formatter. The context is a special config object for the label.
* The default label formatter. The context is a special config object for
* the label. In apps, use the {@link
* https://api.highcharts.com/highcharts/xAxis.labels.formatter|
* labels.formatter} instead except when a modification is needed.
*
* @private
*/
defaultLabelFormatter: function() {
var axis = this.axis,
@@ -9064,8 +9029,11 @@
ret,
formatOption = axis.options.labels.format,
 
// make sure the same symbol is added for all labels on a linear axis
numericSymbolDetector = axis.isLog ? Math.abs(value) : axis.tickInterval;
// make sure the same symbol is added for all labels on a linear
// axis
numericSymbolDetector = axis.isLog ?
Math.abs(value) :
axis.tickInterval;
 
if (formatOption) {
ret = format(formatOption, this);
@@ -9077,12 +9045,18 @@
ret = H.dateFormat(dateTimeLabelFormat, value);
 
} else if (i && 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.
// 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-- && ret === undefined) {
multi = Math.pow(numSymMagnitude, i + 1);
if (numericSymbolDetector >= multi && (value * 10) % multi === 0 && numericSymbols[i] !== null && value !== 0) { // #5480
if (
numericSymbolDetector >= multi &&
(value * 10) % multi === 0 &&
numericSymbols[i] !== null &&
value !== 0
) { // #5480
ret = H.numberFormat(value / multi, -1) + numericSymbols[i];
}
}
@@ -9137,11 +9111,14 @@
if (axis.isXAxis) {
xData = series.xData;
if (xData.length) {
// 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.
// 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 = arrayMin(xData);
if (!isNumber(seriesDataMin) && !(seriesDataMin instanceof Date)) { // Date for #5010
if (!isNumber(seriesDataMin) &&
!(seriesDataMin instanceof Date) // #5010
) {
xData = grep(xData, function(x) {
return isNumber(x);
});
@@ -9148,12 +9125,19 @@
seriesDataMin = arrayMin(xData); // Do it again with valid data
}
 
axis.dataMin = Math.min(pick(axis.dataMin, xData[0]), seriesDataMin);
axis.dataMax = Math.max(pick(axis.dataMax, xData[0]), arrayMax(xData));
axis.dataMin = Math.min(
pick(axis.dataMin, xData[0]),
seriesDataMin
);
axis.dataMax = Math.max(
pick(axis.dataMax, xData[0]),
arrayMax(xData)
);
 
}
 
// Get dataMin and dataMax for Y axes, as well as handle stacking and processed data
// Get dataMin and dataMax for Y axes, as well as handle
// stacking and processed data
} else {
 
// Get this particular series extremes
@@ -9161,12 +9145,19 @@
seriesDataMax = series.dataMax;
seriesDataMin = series.dataMin;
 
// 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
// 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 (defined(seriesDataMin) && defined(seriesDataMax)) {
axis.dataMin = Math.min(pick(axis.dataMin, seriesDataMin), seriesDataMin);
axis.dataMax = Math.max(pick(axis.dataMax, seriesDataMax), seriesDataMax);
axis.dataMin = Math.min(
pick(axis.dataMin, seriesDataMin),
seriesDataMin
);
axis.dataMax = Math.max(
pick(axis.dataMax, seriesDataMax),
seriesDataMax
);
}
 
// Adjust to threshold
@@ -9174,7 +9165,9 @@
axis.threshold = threshold;
}
// If any series has a hard threshold, it takes precedence
if (!seriesOptions.softThreshold || axis.positiveValuesOnly) {
if (!seriesOptions.softThreshold ||
axis.positiveValuesOnly
) {
axis.softThreshold = false;
}
}
@@ -9237,23 +9230,37 @@
},
 
/**
* Utility method to translate an axis value to pixel position.
* @param {Number} value A value in terms of axis units
* @param {Boolean} paneCoordinates Whether to return the pixel coordinate relative to the chart
* or just the axis/pane itself.
* Translate a value in terms of axis units into pixels within the chart.
*
* @param {Number} value
* A value in terms of axis units.
* @param {Boolean} paneCoordinates
* 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.
*/
toPixels: function(value, paneCoordinates) {
return this.translate(value, false, !this.horiz, null, true) + (paneCoordinates ? 0 : this.pos);
return this.translate(value, false, !this.horiz, null, true) +
(paneCoordinates ? 0 : this.pos);
},
 
/**
* Utility method to translate a pixel position in to an axis value.
* @param {Number} pixel The pixel value coordinate
* @param {Boolean} paneCoordiantes Whether the input pixel is relative to the chart or just the
* axis/pane itself.
* Translate a pixel position along the axis to a value in terms of axis
* units.
* @param {Number} pixel
* The pixel value coordinate.
* @param {Boolean} paneCoordiantes
* Whether the input pixel is relative to the chart or just the
* axis/pane itself.
* @return {Number} The axis value.
*/
toValue: function(pixel, paneCoordinates) {
return this.translate(pixel - (paneCoordinates ? 0 : this.pos), true, !this.horiz, null, true);
return this.translate(
pixel - (paneCoordinates ? 0 : this.pos),
true, !this.horiz,
null,
true
);
},
 
/**
@@ -9312,7 +9319,18 @@
},
 
/**
* Set the tick positions of a linear axis to round values like whole tens or every five.
* Internal function to et the tick positions of a linear axis to round
* values like whole tens or every five.
*
* @param {Number} tickInterval
* The normalized tick interval
* @param {Number} min
* Axis minimum.
* @param {Number} max
* Axis maximum.
*
* @return {Array.<Number>}
* An array of numbers where ticks should be placed.
*/
getLinearTickPositions: function(tickInterval, min, max) {
var pos,
@@ -9418,6 +9436,8 @@
* 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
*/
adjustForMinRange: function() {
var axis = this,
@@ -9425,7 +9445,7 @@
min = axis.min,
max = axis.max,
zoomOffset,
spaceAvailable = axis.dataMax - axis.dataMin >= axis.minRange,
spaceAvailable,
closestDataRange,
i,
distance,
@@ -9461,6 +9481,8 @@
 
// if minRange is exceeded, adjust
if (max - min < axis.minRange) {
 
spaceAvailable = axis.dataMax - axis.dataMin >= axis.minRange;
minRange = axis.minRange;
zoomOffset = (minRange - max + min) / 2;
 
@@ -9492,7 +9514,9 @@
},
 
/**
* Find the closestPointRange across all series
* Find the closestPointRange across all series.
*
* @private
*/
getClosest: function() {
var ret;
@@ -9559,7 +9583,7 @@
 
if (this.names.length > 0) {
this.names.length = 0;
this.minRange = undefined;
this.minRange = this.userMinRange; // Reset
each(this.series || [], function(series) {
 
// Reset incrementer (#5928)
@@ -9908,9 +9932,18 @@
// 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 && defined(this.min) &&
!this.tickAmount && options.allowDecimals !== false;
this.single =
this.min === this.max &&
defined(this.min) &&
!this.tickAmount &&
(
// Data is on integer (#6563)
parseInt(this.min, 10) === this.min ||
 
// Between integers and decimals are not allowed (#6274)
options.allowDecimals !== false
);
 
// Find the tick positions
this.tickPositions = tickPositions = tickPositionsOption && tickPositionsOption.slice(); // Work on a copy (#1565)
if (!tickPositions) {
@@ -9917,7 +9950,10 @@
 
if (this.isDatetimeAxis) {
tickPositions = this.getTimeTicks(
this.normalizeTimeTickInterval(this.tickInterval, options.units),
this.normalizeTimeTickInterval(
this.tickInterval,
options.units
),
this.min,
this.max,
options.startOfWeek,
@@ -9926,9 +9962,17 @@
true
);
} else if (this.isLog) {
tickPositions = this.getLogTickPositions(this.tickInterval, this.min, this.max);
tickPositions = this.getLogTickPositions(
this.tickInterval,
this.min,
this.max
);
} else {
tickPositions = this.getLinearTickPositions(this.tickInterval, this.min, this.max);
tickPositions = this.getLinearTickPositions(
this.tickInterval,
this.min,
this.max
);
}
 
// Too dense ticks, keep only the first and last (#4477)
@@ -9997,8 +10041,11 @@
},
 
/**
* Check if there are multiple axes in the same pane
* @returns {Boolean} There are other axes
* Check if there are multiple axes in the same pane.
*
* @private
* @return {Boolean}
* True if there are other axes.
*/
alignToOthers: function() {
var others = {}, // Whether there is another axis to pair with this one
@@ -10067,7 +10114,9 @@
 
/**
* When using multiple axes, adjust the number of ticks to match the highest
* number of ticks in that group
* number of ticks in that group.
*
* @private
*/
adjustTickAmount: function() {
var tickInterval = this.tickInterval,
@@ -10164,14 +10213,35 @@
},
 
/**
* Set the extremes and optionally redraw
* @param {Number} newMin
* @param {Number} newMax
* @param {Boolean} redraw
* @param {Boolean|Object} animation Whether to apply animation, and optionally animation
* configuration
* @param {Object} eventArguments
* 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.
*
* @param {Number} [newMin]
* The new minimum value.
* @param {Number} [newMax]
* The new maximum value.
* @param {Boolean} [redraw=true]
* Whether to redraw the chart or wait for an explicit call to
* {@link Highcharts.Chart#redraw}
* @param {AnimationOptions} [animation=true]
* Enable or modify animations.
* @param {Object} [eventArguments]
* Arguments to be accessed in event handler.
*
* @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 Highstock
* @sample maps/members/axis-setextremes/
* Set extremes in Highmaps
*/
setExtremes: function(newMin, newMax, redraw, animation, eventArguments) {
var axis = this,
@@ -10290,8 +10360,33 @@
},
 
/**
* Get the actual axis extremes
* The returned object literal from the {@link Highcharts.Axis#getExtremes}
* function.
* @typedef {Object} Extremes
* @property {Number} dataMax
* The maximum value of the axis' associated series.
* @property {Number} dataMin
* The minimum value of the axis' associated series.
* @property {Number} max
* 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`.
* @property {Number} min
* 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`.
*/
/**
* Get the current extremes for the axis.
*
* @returns {Extremes}
* An object containing extremes information.
*
* @sample members/axis-getextremes/
* Report extremes by click on a button
* @sample maps/members/axis-getextremes/
* Get extremes in Highmaps
*/
getExtremes: function() {
var axis = this,
isLog = axis.isLog,
@@ -10330,8 +10425,13 @@
},
 
/**
* Compute auto alignment for the axis label based on which side the axis is on
* and the given rotation for the label
* Compute auto alignment for the axis label based on which side the axis is
* on and the given rotation for the label.
*
* @param {Number} rotation
* The rotation in degrees as set by either the `rotation` or
* `autoRotation` options.
* @private
*/
autoLabelAlign: function(rotation) {
var ret,
@@ -10371,9 +10471,10 @@
* Return the size of the labels
*/
labelMetrics: function() {
var index = this.tickPositions && this.tickPositions[0] || 0;
return this.chart.renderer.fontMetrics(
this.options.labels.style && this.options.labels.style.fontSize,
this.ticks[0] && this.ticks[0].label
this.ticks[index] && this.ticks[index].label
);
},
 
@@ -10641,8 +10742,12 @@
 
/**
* Generates a tick for initial positioning.
* @param {number} pos - The tick position in axis values.
* @param {number} i - The index of the tick in axis.tickPositions.
*
* @private
* @param {number} pos
* The tick position in axis values.
* @param {number} i
* The index of the tick in {@link Axis.tickPositions}.
*/
generateTick: function(pos) {
var ticks = this.ticks;
@@ -10666,7 +10771,7 @@
ticks = axis.ticks,
horiz = axis.horiz,
side = axis.side,
invertedSide = chart.inverted ? [1, 0, 3, 2][side] : side,
invertedSide = chart.inverted && !axis.isZAxis ? [1, 0, 3, 2][side] : side,
hasData,
showAxis,
titleOffset = 0,
@@ -10680,7 +10785,6 @@
clipOffset = chart.clipOffset,
clip,
directionFactor = [-1, 1, 1, -1][side],
n,
className = options.className,
axisParent = axis.axisParent, // Used in color axis
lineHeightCorrection,
@@ -10748,17 +10852,18 @@
 
 
} else { // doesn't have data
for (n in ticks) {
ticks[n].destroy();
objectEach(ticks, function(tick, n) {
tick.destroy();
delete ticks[n];
}
});
}
 
if (axisTitleOptions && axisTitleOptions.text && axisTitleOptions.enabled !== false) {
axis.addTitle(showAxis);
 
if (showAxis) {
titleOffset = axis.axisTitle.getBBox()[horiz ? 'height' : 'width'];
if (showAxis && axisTitleOptions.reserveSpace !== false) {
axis.titleOffset = titleOffset =
axis.axisTitle.getBBox()[horiz ? 'height' : 'width'];
titleOffsetOption = axisTitleOptions.offset;
titleMargin = defined(titleOffsetOption) ? 0 : pick(axisTitleOptions.margin, horiz ? 5 : 10);
}
@@ -10799,13 +10904,26 @@
0 // #4866
);
 
// Decide the clipping needed to keep the graph inside the plot area and axis lines
clip = options.offset ? 0 : Math.floor(axis.axisLine.strokeWidth() / 2) * 2; // #4308, #4371
clipOffset[invertedSide] = Math.max(clipOffset[invertedSide], clip);
// Decide the clipping needed to keep the graph inside the plot area and
// axis lines
clip = Math.floor(axis.axisLine.strokeWidth() / 2) * 2; // #4308, #4371
if (options.offset > 0) {
clip -= options.offset * 2;
}
clipOffset[invertedSide] = Math.max(
clipOffset[invertedSide] || clip,
clip
);
},
 
/**
* Get the path for the axis line
* Internal function to get the path for the axis line. Extended for polar
* charts.
*
* @param {Number} lineWidth
* The line width in pixels.
* @return {Array}
* The SVG path definition in array form.
*/
getLinePath: function(lineWidth) {
var chart = this.chart,
@@ -10813,7 +10931,8 @@
offset = this.offset,
horiz = this.horiz,
lineLeft = this.left + (opposite ? this.width : 0) + offset,
lineTop = chart.chartHeight - this.bottom - (opposite ? this.height : 0) + offset;
lineTop = chart.chartHeight - this.bottom -
(opposite ? this.height : 0) + offset;
 
if (opposite) {
lineWidth *= -1; // crispify the other way - #1480, #1687
@@ -10976,10 +11095,9 @@
 
// Mark all elements inActive before we go over and mark the active ones
each([ticks, minorTicks, alternateBands], function(coll) {
var pos;
for (pos in coll) {
coll[pos].isActive = false;
}
objectEach(coll, function(tick) {
tick.isActive = false;
});
});
 
// If the series has data draw the ticks. Else only the line and title
@@ -11015,7 +11133,7 @@
to = 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]) {
alternateBands[pos] = new PlotLineOrBand(axis);
alternateBands[pos] = new H.PlotLineOrBand(axis);
}
from = pos + tickmarkOffset; // #949
alternateBands[pos].options = {
@@ -11041,8 +11159,7 @@
 
// Remove inactive ticks
each([ticks, minorTicks, alternateBands], function(coll) {
var pos,
i,
var i,
forDestruction = [],
delay = animation.duration,
destroyInactiveItems = function() {
@@ -11058,15 +11175,14 @@
 
};
 
for (pos in coll) {
 
if (!coll[pos].isActive) {
objectEach(coll, function(tick, pos) {
if (!tick.isActive) {
// Render to zero opacity
coll[pos].render(pos, false, 0);
coll[pos].isActive = false;
tick.render(pos, false, 0);
tick.isActive = false;
forDestruction.push(pos);
}
}
});
 
// When the objects are finished fading out, destroy them
syncTimeout(
@@ -11087,11 +11203,14 @@
}
 
if (axisTitle && showAxis) {
 
axisTitle[axisTitle.isNew ? 'attr' : 'animate'](
axis.getTitlePosition()
);
axisTitle.isNew = false;
var titleXy = axis.getTitlePosition();
if (isNumber(titleXy.y)) {
axisTitle[axisTitle.isNew ? 'attr' : 'animate'](titleXy);
axisTitle.isNew = false;
} else {
axisTitle.attr('y', -9999);
axisTitle.isNew = true;
}
}
 
// Stacked totals:
@@ -11130,16 +11249,19 @@
keepProps: ['extKey', 'hcEvents', 'names', 'series', 'userMax', 'userMin'],
 
/**
* Destroys an Axis instance.
* Destroys an Axis instance. See {@link Axis#remove} for the API endpoint
* to fully remove the axis.
*
* @private
* @param {Boolean} keepEvents
* Whether to preserve events, used internally in Axis.update.
*/
destroy: function(keepEvents) {
var axis = this,
stacks = axis.stacks,
stackKey,
plotLinesAndBands = axis.plotLinesAndBands,
plotGroup,
i,
n;
i;
 
// Remove the events
if (!keepEvents) {
@@ -11147,11 +11269,11 @@
}
 
// Destroy each stack total
for (stackKey in stacks) {
destroyObjectProperties(stacks[stackKey]);
objectEach(stacks, function(stack, stackKey) {
destroyObjectProperties(stack);
 
stacks[stackKey] = null;
}
});
 
// Destroy collections
each([axis.ticks, axis.minorTicks, axis.alternateBands], function(coll) {
@@ -11177,18 +11299,21 @@
}
 
// Delete all properties and fall back to the prototype.
for (n in axis) {
if (axis.hasOwnProperty(n) && inArray(n, axis.keepProps) === -1) {
delete axis[n];
objectEach(axis, function(val, key) {
if (inArray(key, axis.keepProps) === -1) {
delete axis[key];
}
}
});
},
 
/**
* Draw the crosshair
*
* @param {Object} e The event arguments from the modified pointer event
* @param {Object} point The Point object
* Internal function to draw a crosshair.
*
* @param {PointerEvent} [e]
* The event arguments from the modified pointer event, extended
* with `chartX` and `chartY`
* @param {Point} [point]
* The Point object if the crosshair snaps to points.
*/
drawCrosshair: function(e, point) {
 
@@ -11286,10 +11411,10 @@
this.cross.hide();
}
}
}; // end Axis
}); // end Axis
 
extend(H.Axis.prototype, AxisPlotLineOrBandExtension);
 
H.Axis = Axis;
return Axis;
}(Highcharts));
(function(H) {
/**
@@ -11326,7 +11451,8 @@
higherRanks = {},
useUTC = defaultOptions.global.useUTC,
minYear, // used in months and years as a basis for Date.UTC()
minDate = new Date(min - Math.abs(getTZOffset(min))), // #6278
// When crossing DST, use the max. Resolves #6278.
minDate = new Date(min - Math.max(getTZOffset(min), getTZOffset(max))),
makeTime = Date.hcMakeTime,
interval = normalizedInterval.unitRange,
count = normalizedInterval.count,
@@ -11674,6 +11800,398 @@
};
 
}(Highcharts));
(function(H, Axis) {
/**
* (c) 2010-2017 Torstein Honsi
*
* License: www.highcharts.com/license
*/
var arrayMax = H.arrayMax,
arrayMin = H.arrayMin,
defined = H.defined,
destroyObjectProperties = H.destroyObjectProperties,
each = H.each,
erase = H.erase,
merge = H.merge,
pick = H.pick;
/*
* The object wrapper for plot lines and plot bands
* @param {Object} options
*/
H.PlotLineOrBand = function(axis, options) {
this.axis = axis;
 
if (options) {
this.options = options;
this.id = options.id;
}
};
 
H.PlotLineOrBand.prototype = {
 
/**
* Render the plot line or plot band. If it is already existing,
* move it.
*/
render: function() {
var plotLine = this,
axis = plotLine.axis,
horiz = axis.horiz,
options = plotLine.options,
optionsLabel = options.label,
label = plotLine.label,
to = options.to,
from = options.from,
value = options.value,
isBand = defined(from) && defined(to),
isLine = defined(value),
svgElem = plotLine.svgElem,
isNew = !svgElem,
path = [],
color = options.color,
zIndex = pick(options.zIndex, 0),
events = options.events,
attribs = {
'class': 'highcharts-plot-' + (isBand ? 'band ' : 'line ') + (options.className || '')
},
groupAttribs = {},
renderer = axis.chart.renderer,
groupName = isBand ? 'bands' : 'lines',
group,
log2lin = axis.log2lin;
 
// logarithmic conversion
if (axis.isLog) {
from = log2lin(from);
to = log2lin(to);
value = log2lin(value);
}
 
 
// Set the presentational attributes
if (isLine) {
attribs = {
stroke: color,
'stroke-width': options.width
};
if (options.dashStyle) {
attribs.dashstyle = options.dashStyle;
}
 
} else if (isBand) { // plot band
if (color) {
attribs.fill = color;
}
if (options.borderWidth) {
attribs.stroke = options.borderColor;
attribs['stroke-width'] = options.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 (isNew) {
plotLine.svgElem = svgElem =
renderer
.path()
.attr(attribs).add(group);
}
 
 
// Set the path or return
if (isLine) {
path = axis.getPlotLinePath(value, svgElem.strokeWidth());
} else if (isBand) { // plot band
path = axis.getPlotBandPath(from, to, options);
} else {
return;
}
 
 
// common for lines and bands
if (isNew && path && path.length) {
svgElem.attr({
d: path
});
 
// events
if (events) {
H.objectEach(events, function(event, eventType) {
svgElem.on(eventType, function(e) {
events[eventType].apply(plotLine, [e]);
});
});
}
} else if (svgElem) {
if (path) {
svgElem.show();
svgElem.animate({
d: path
});
} else {
svgElem.hide();
if (label) {
plotLine.label = label = label.destroy();
}
}
}
 
// the plot band/line label
if (optionsLabel && defined(optionsLabel.text) && path && path.length &&
axis.width > 0 && axis.height > 0 && !path.flat) {
// apply defaults
optionsLabel = merge({
align: horiz && isBand && 'center',
x: horiz ? !isBand && 4 : 10,
verticalAlign: !horiz && isBand && 'middle',
y: horiz ? isBand ? 16 : 10 : isBand ? 6 : -4,
rotation: horiz && !isBand && 90
}, optionsLabel);
 
this.renderLabel(optionsLabel, path, isBand, zIndex);
 
} else if (label) { // move out of sight
label.hide();
}
 
// chainable
return plotLine;
},
 
/**
* Render and align label for plot line or band.
*/
renderLabel: function(optionsLabel, path, isBand, zIndex) {
var plotLine = this,
label = plotLine.label,
renderer = plotLine.axis.chart.renderer,
attribs,
xs,
ys,
x,
y;
 
// add the SVG element
if (!label) {
attribs = {
align: optionsLabel.textAlign || optionsLabel.align,
rotation: optionsLabel.rotation,
'class': 'highcharts-plot-' + (isBand ? 'band' : 'line') + '-label ' + (optionsLabel.className || '')
};
 
attribs.zIndex = zIndex;
 
plotLine.label = label = renderer.text(
optionsLabel.text,
0,
0,
optionsLabel.useHTML
)
.attr(attribs)
.add();
 
 
label.css(optionsLabel.style);
 
}
 
// get the bounding box and align the label
// #3000 changed to better handle choice between plotband or plotline
xs = [path[1], path[4], (isBand ? path[6] : path[1])];
ys = [path[2], path[5], (isBand ? path[7] : path[2])];
x = arrayMin(xs);
y = arrayMin(ys);
 
label.align(optionsLabel, false, {
x: x,
y: y,
width: arrayMax(xs) - x,
height: arrayMax(ys) - y
});
label.show();
},
 
/**
* Remove the plot line or band
*/
destroy: function() {
// remove it from the lookup
erase(this.axis.plotLinesAndBands, this);
 
delete this.axis;
destroyObjectProperties(this);
}
};
 
/**
* Object with members for extending the Axis prototype
* @todo Extend directly instead of adding object to Highcharts first
*/
 
H.extend(Axis.prototype, /** @lends Highcharts.Axis.prototype */ {
 
/**
* Create the path for a plot band
*/
getPlotBandPath: function(from, to) {
var toPath = this.getPlotLinePath(to, null, null, true),
path = this.getPlotLinePath(from, null, null, true),
// #4964 check if chart is inverted or plotband is on yAxis
horiz = this.horiz,
plus = 1,
outside =
(from < this.min && to < this.min) ||
(from > this.max && to > this.max);
 
if (path && toPath) {
 
// Flat paths don't need labels (#3836)
if (outside) {
path.flat = path.toString() === toPath.toString();
plus = 0;
}
 
// Add 1 pixel, when coordinates are the same
path.push(
horiz && toPath[4] === path[4] ? toPath[4] + plus : toPath[4], !horiz && toPath[5] === path[5] ? toPath[5] + plus : toPath[5],
horiz && toPath[1] === path[1] ? toPath[1] + plus : toPath[1], !horiz && toPath[2] === path[2] ? toPath[2] + plus : toPath[2]
);
} else { // outside the axis area
path = null;
}
 
return path;
},
 
/**
* Add a plot band after render time.
*
* @param {AxisPlotBandsOptions} options
* A configuration object for the plot band, as defined in {@link
* https://api.highcharts.com/highcharts/xAxis.plotBands|
* xAxis.plotBands}.
* @return {Object}
* The added plot band.
* @sample highcharts/members/axis-addplotband/
* Toggle the plot band from a button
*/
addPlotBand: function(options) {
return this.addPlotBandOrLine(options, 'plotBands');
},
 
/**
* Add a plot line after render time.
*
* @param {AxisPlotLinesOptions} options
* A configuration object for the plot line, as defined in {@link
* https://api.highcharts.com/highcharts/xAxis.plotLines|
* xAxis.plotLines}.
* @return {Object}
* The added plot line.
* @sample highcharts/members/axis-addplotline/
* Toggle the plot line from a button
*/
addPlotLine: function(options) {
return this.addPlotBandOrLine(options, 'plotLines');
},
 
/**
* Add a plot band or plot line after render time. Called from addPlotBand
* and addPlotLine internally.
*
* @private
* @param options {AxisPlotLinesOptions|AxisPlotBandsOptions}
* The plotBand or plotLine configuration object.
*/
addPlotBandOrLine: function(options, coll) {
var obj = new H.PlotLineOrBand(this, options).render(),
userOptions = this.userOptions;
 
if (obj) { // #2189
// Add it to the user options for exporting and Axis.update
if (coll) {
userOptions[coll] = userOptions[coll] || [];
userOptions[coll].push(options);
}
this.plotLinesAndBands.push(obj);
}
 
return obj;
},
 
/**
* Remove a plot band or plot line from the chart by id. Called internally
* from `removePlotBand` and `removePlotLine`.
*
* @private
* @param {String} id
*/
removePlotBandOrLine: function(id) {
var plotLinesAndBands = this.plotLinesAndBands,
options = this.options,
userOptions = this.userOptions,
i = plotLinesAndBands.length;
while (i--) {
if (plotLinesAndBands[i].id === id) {
plotLinesAndBands[i].destroy();
}
}
each([
options.plotLines || [],
userOptions.plotLines || [],
options.plotBands || [],
userOptions.plotBands || []
], function(arr) {
i = arr.length;
while (i--) {
if (arr[i].id === id) {
erase(arr, arr[i]);
}
}
});
},
 
/**
* Remove a plot band by its id.
*
* @param {String} id
* The plot band's `id` as given in the original configuration
* object or in the `addPlotBand` option.
* @sample highcharts/members/axis-removeplotband/
* Remove plot band by id
* @sample highcharts/members/axis-addplotband/
* Toggle the plot band from a button
*/
removePlotBand: function(id) {
this.removePlotBandOrLine(id);
},
 
/**
* Remove a plot line by its id.
* @param {String} id
* The plot line's `id` as given in the original configuration
* object or in the `addPlotLine` option.
* @sample highcharts/xaxis/plotlines-id/
* Remove plot line by id
* @sample highcharts/members/axis-addplotline/
* Toggle the plot line from a button
*/
removePlotLine: function(id) {
this.removePlotBandOrLine(id);
}
});
 
}(Highcharts, Axis));
(function(H) {
/**
* (c) 2010-2017 Torstein Honsi
@@ -11808,6 +12326,8 @@
 
update: function(options) {
this.destroy();
// Update user options (#6218)
merge(true, this.chart.options.tooltip.userOptions, options);
this.init(this.chart, merge(true, this.options, options));
},
 
@@ -12139,6 +12659,18 @@
if (tooltip.split) {
this.renderSplit(text, pointOrPoints);
} else {
 
// Prevent the tooltip from flowing over the chart box (#6659)
 
if (!options.style.width) {
 
label.css({
width: this.chart.spacingBox.width
});
 
}
 
 
label.attr({
text: text && text.join ? text.join('') : text
});
@@ -12511,9 +13043,26 @@
},
 
/**
* Add crossbrowser support for chartX and chartY
* @param {Object} e The event object in standard browsers
* @typedef {Object} PointerEvent
* A native browser mouse or touch event, extended with position
* information relative to the {@link Chart.container}.
* @property {Number} chartX
* The X coordinate of the pointer interaction relative to the
* chart.
* @property {Number} chartY
* The Y coordinate of the pointer interaction relative to the
* chart.
*
*/
/**
* Add crossbrowser support for chartX and chartY.
*
* @param {Object} e
* The event object in standard browsers.
*
* @return {PointerEvent}
* A browser event with extended properties `chartX` and `chartY`
*/
normalize: function(e, chartPosition) {
var chartX,
chartY,
@@ -12645,64 +13194,93 @@
return point;
},
 
getHoverData: function(existingHoverPoint, existingHoverSeries, series, isDirectTouch, shared, e) {
getChartCoordinatesFromPoint: function(point, inverted) {
var series = point.series,
xAxis = series.xAxis,
yAxis = series.yAxis;
 
if (xAxis && yAxis) {
return inverted ? {
chartX: xAxis.len + xAxis.pos - point.clientX,
chartY: yAxis.len + yAxis.pos - point.plotY
} : {
chartX: point.clientX + xAxis.pos,
chartY: point.plotY + yAxis.pos
};
}
},
 
/**
* Calculates what is the current hovered point/points and series.
*
* @private
*
* @param {undefined|Point} existingHoverPoint
* The point currrently beeing hovered.
* @param {undefined|Series} existingHoverSeries
* The series currently beeing hovered.
* @param {Array<.Series>} series
* All the series in the chart.
* @param {boolean} isDirectTouch
* Is the pointer directly hovering the point.
* @param {boolean} shared
* Whether it is a shared tooltip or not.
* @param {object} coordinates
* Chart coordinates of the pointer.
* @param {number} coordinates.chartX
* @param {number} coordinates.chartY
*
* @return {object}
* Object containing resulting hover data.
*/
getHoverData: function(
existingHoverPoint,
existingHoverSeries,
series,
isDirectTouch,
shared,
coordinates
) {
var hoverPoint = existingHoverPoint,
hoverSeries = existingHoverSeries,
searchSeries,
searchSeries = shared ? series : [hoverSeries],
useExisting = !!(isDirectTouch && existingHoverPoint),
notSticky = hoverSeries && !hoverSeries.stickyTracking,
isHoverPoint = function(point, i) {
return i === 0;
},
hoverPoints;
 
// If it has a hoverPoint and that series requires direct touch (like columns, #3899), or we're on
// a noSharedTooltip series among shared tooltip series (#4546), use the hoverPoint . Otherwise,
// search the k-d tree.
// Handle shared tooltip or cases where a series is not yet hovered
if (isDirectTouch) {
if (shared) {
hoverPoints = [];
each(series, function(s) {
// Skip hidden series
var noSharedTooltip = s.noSharedTooltip && shared,
directTouch = !shared && s.directTouch,
kdpointT;
if (s.visible && !noSharedTooltip && !directTouch && pick(s.options.enableMouseTracking, true)) { // #3821
kdpointT = s.searchKDTree({
clientX: hoverPoint.clientX,
plotY: hoverPoint.plotY
}, !noSharedTooltip && s.kdDimensions === 1);
if (kdpointT && kdpointT.series) { // Point.series becomes null when reset and before redraw (#5197)
hoverPoints.push(kdpointT);
}
}
});
// If kdTree is not built
if (hoverPoints.length === 0) {
hoverPoints = [hoverPoint];
}
} else {
hoverPoints = [hoverPoint];
}
// When the hovered series has stickyTracking false.
} else if (hoverSeries && !hoverSeries.stickyTracking) {
if (!shared) {
series = [hoverSeries];
}
hoverPoints = this.getKDPoints(series, shared, e);
hoverPoint = H.find(hoverPoints, function(p) {
// If there is a hoverPoint and its series requires direct touch (like
// columns, #3899), or we're on a noSharedTooltip series among shared
// tooltip series (#4546), use the existing hoverPoint.
if (useExisting) {
isHoverPoint = function(p) {
return p === existingHoverPoint;
};
} else if (notSticky) {
isHoverPoint = function(p) {
return p.series === hoverSeries;
});
// When the hoverSeries has stickyTracking or there is no series hovered.
};
} else {
// Avoid series with stickyTracking
// Avoid series with stickyTracking false
searchSeries = H.grep(series, function(s) {
return s.stickyTracking;
});
hoverPoints = this.getKDPoints(searchSeries, shared, e);
hoverPoint = hoverPoints[0];
hoverSeries = hoverPoint && hoverPoint.series;
// If
if (shared) {
hoverPoints = this.getKDPoints(series, shared, e);
}
}
hoverPoints = (useExisting && !shared) ?
// Non-shared tooltips with directTouch don't use the k-d-tree
[existingHoverPoint] :
this.getKDPoints(searchSeries, shared, coordinates);
hoverPoint = H.find(hoverPoints, isHoverPoint);
hoverSeries = hoverPoint && hoverPoint.series;
 
// In this case we could only look for the hoverPoint in series with
// stickyTracking, but we should still include all series in the shared
// tooltip.
if (!useExisting && !notSticky && shared) {
hoverPoints = this.getKDPoints(series, shared, coordinates);
}
// Keep the order of series in tooltip
// Must be done after assigning of hoverPoint
hoverPoints.sort(function(p1, p2) {
@@ -12728,24 +13306,37 @@
hoverPoint = p || chart.hoverPoint,
hoverSeries = hoverPoint && hoverPoint.series || chart.hoverSeries,
// onMouseOver or already hovering a series with directTouch
isDirectTouch = !!p || (!shared && hoverSeries && hoverSeries.directTouch),
hoverData = this.getHoverData(hoverPoint, hoverSeries, series, isDirectTouch, shared, e),
isDirectTouch = !!p || (
(hoverSeries && hoverSeries.directTouch) &&
pointer.isDirectTouch
),
hoverData = this.getHoverData(
hoverPoint,
hoverSeries,
series,
isDirectTouch,
shared,
e
),
useSharedTooltip,
followPointer,
anchor,
points;
 
// Update variables from hoverData.
hoverPoint = hoverData.hoverPoint;
hoverSeries = hoverData.hoverSeries;
followPointer = hoverSeries && hoverSeries.tooltipOptions.followPointer;
useSharedTooltip = shared && hoverPoint && !hoverPoint.series.noSharedTooltip;
useSharedTooltip = (
shared &&
hoverPoint &&
!hoverPoint.series.noSharedTooltip
);
points = (useSharedTooltip ?
hoverData.hoverPoints :
(hoverPoint ? [hoverPoint] : [])
);
 
// Refresh tooltip for kdpoint if new hover point or tooltip was hidden // #3926, #4200
// Refresh tooltip for kdpoint if new hover point or tooltip was hidden
// #3926, #4200
if (
hoverPoint &&
// !(hoverSeries && hoverSeries.directTouch) &&
@@ -12766,13 +13357,11 @@
}
 
// If tracking is on series in stead of on each point,
// fire mouseOver on hover point.
if (hoverSeries && !hoverSeries.directTouch) { // #4448
if (chart.hoverPoint) {
chart.hoverPoint.firePointEvent('mouseOut');
}
hoverPoint.firePointEvent('mouseOver');
// fire mouseOver on hover point. // #4448
if (chart.hoverPoint) {
chart.hoverPoint.firePointEvent('mouseOut');
}
hoverPoint.firePointEvent('mouseOver');
chart.hoverPoints = points;
chart.hoverPoint = hoverPoint;
// Draw tooltip if necessary
@@ -12798,27 +13387,32 @@
});
}
 
// Draw crosshairs (#4927, #5269 #5066, #5658)
// Issues related to crosshair #4927, #5269 #5066, #5658
each(chart.axes, function drawAxisCrosshair(axis) {
// Snap is true. For each hover point, loop over the axes and draw a
// crosshair if that point belongs to the axis.
// @todo Consider only one crosshair per axis.
if (pick(axis.crosshair.snap, true)) {
each(points, function(p) {
if (p.series[axis.coll] === axis) {
axis.drawCrosshair(e, p);
}
});
var snap = pick(axis.crosshair.snap, true);
if (!snap) {
axis.drawCrosshair(e);
 
// Axis has snapping crosshairs, and one of the hover points belongs
// to axis
} else if (H.find(points, function(p) {
return p.series[axis.coll] === axis;
})) {
axis.drawCrosshair(e, hoverPoint);
// Axis has snapping crosshairs, but no hover point belongs to axis
} else {
axis.drawCrosshair(e);
axis.hideCrosshair();
}
});
},
 
/**
* Reset the tracking by hiding the tooltip, the hover series state and the hover point
* Reset the tracking by hiding the tooltip, the hover series state and the
* hover point
*
* @param allowMove {Boolean} Instead of destroying the tooltip altogether, allow moving it if possible
* @param allowMove {Boolean}
* Instead of destroying the tooltip altogether, allow moving it if
* possible
*/
reset: function(allowMove, delay) {
var pointer = this,
@@ -13190,7 +13784,7 @@
onTrackerMouseOut: function(e) {
var series = this.chart.hoverSeries,
relatedTarget = e.relatedTarget || e.toElement;
 
this.isDirectTouch = false;
if (series && relatedTarget && !series.stickyTracking &&
!this.inClass(relatedTarget, 'highcharts-tooltip') &&
(!this.inClass(relatedTarget, 'highcharts-series-' + series.index) || // #2499, #4465
@@ -13279,28 +13873,28 @@
* Destroys the Pointer object and disconnects DOM events.
*/
destroy: function() {
var prop;
var pointer = this;
 
if (this.unDocMouseMove) {
this.unDocMouseMove();
if (pointer.unDocMouseMove) {
pointer.unDocMouseMove();
}
 
removeEvent(
this.chart.container,
pointer.chart.container,
'mouseleave',
this.onContainerMouseLeave
pointer.onContainerMouseLeave
);
if (!H.chartCount) {
removeEvent(doc, 'mouseup', this.onDocumentMouseUp);
removeEvent(doc, 'touchend', this.onDocumentTouchEnd);
removeEvent(doc, 'mouseup', pointer.onDocumentMouseUp);
removeEvent(doc, 'touchend', pointer.onDocumentTouchEnd);
}
 
// memory and CPU leak
clearInterval(this.tooltipTimeout);
clearInterval(pointer.tooltipTimeout);
 
for (prop in this) {
this[prop] = null;
}
H.objectEach(pointer, function(val, prop) {
pointer[prop] = null;
});
}
};
 
@@ -13593,6 +14187,7 @@
css = H.css,
doc = H.doc,
extend = H.extend,
hasTouch = H.hasTouch,
noop = H.noop,
Pointer = H.Pointer,
removeEvent = H.removeEvent,
@@ -13599,26 +14194,23 @@
win = H.win,
wrap = H.wrap;
 
if (win.PointerEvent || win.MSPointerEvent) {
if (!hasTouch && (win.PointerEvent || win.MSPointerEvent)) {
 
// The touches object keeps track of the points being touched at all times
var touches = {},
hasPointerEvent = !!win.PointerEvent,
getWebkitTouches = function() {
var key,
fake = [];
var fake = [];
fake.item = function(i) {
return this[i];
};
for (key in touches) {
if (touches.hasOwnProperty(key)) {
fake.push({
pageX: touches[key].pageX,
pageY: touches[key].pageY,
target: touches[key].target
});
}
}
H.objectEach(touches, function(touch) {
fake.push({
pageX: touch.pageX,
pageY: touch.pageY,
target: touch.target
});
});
return fake;
},
translateMSPointer = function(e, method, wktype, func) {
@@ -13701,13 +14293,13 @@
}
 
}(Highcharts));
(function(H) {
(function(Highcharts) {
/**
* (c) 2010-2017 Torstein Honsi
*
* License: www.highcharts.com/license
*/
var Legend,
var H = Highcharts,
 
addEvent = H.addEvent,
css = H.css,
@@ -13722,15 +14314,19 @@
stableSort = H.stableSort,
win = H.win,
wrap = H.wrap;
 
/**
* The overview of the chart's series.
* The overview of the chart's series. The legend object is instanciated
* internally in the chart constructor, and available from `chart.legend`. Each
* chart has only one legend.
*
* @class
*/
Legend = H.Legend = function(chart, options) {
Highcharts.Legend = function(chart, options) {
this.init(chart, options);
};
 
Legend.prototype = {
Highcharts.Legend.prototype = {
 
/**
* Initialize the legend
@@ -13774,10 +14370,15 @@
},
 
/**
* Update the legend with new options. Equivalent to running chart.update
* Update the legend with new options. Equivalent to running `chart.update`
* with a legend configuration option.
* @param {Object} options Legend options
* @param {Boolean} redraw Whether to redraw the chart, defaults to true.
* @param {LegendOptions} options
* Legend options.
* @param {Boolean} [redraw=true]
* Whether to redraw the chart.
*
* @sample highcharts/legend/legend-update/
* Legend update
*/
update: function(options, redraw) {
var chart = this.chart;
@@ -13812,8 +14413,7 @@
markerOptions = item.options && item.options.marker,
symbolAttr = {
fill: symbolColor
},
key;
};
 
if (legendItem) {
legendItem.css({
@@ -13834,9 +14434,9 @@
//symbolAttr.stroke = symbolColor;
symbolAttr = item.pointAttribs();
if (!visible) {
for (key in symbolAttr) {
H.objectEach(symbolAttr, function(val, key) {
symbolAttr[key] = hiddenColor;
}
});
}
}
 
@@ -13975,8 +14575,8 @@
null,
null,
null,
options.useHTML,
null,
null,
'legend-title'
)
.attr({
@@ -14042,6 +14642,9 @@
showCheckbox = legend.createCheckboxForItem &&
seriesOptions &&
seriesOptions.showCheckbox,
// full width minus text width
itemExtraWidth = symbolWidth + symbolPadding + itemDistance +
(showCheckbox ? 20 : 0),
useHTML = options.useHTML,
fontSize = 12,
itemClassName = item.options.className;
@@ -14110,6 +14713,18 @@
// Colorize the items
legend.colorizeItem(item, item.visible);
 
// Take care of max width and text overflow (#6659)
 
if (!itemStyle.width) {
 
li.css({
width: (options.itemWidth || chart.spacingBox.width) -
itemExtraWidth
});
 
}
 
 
// Always update the text
legend.setText(item);
 
@@ -14119,8 +14734,7 @@
itemWidth = item.checkboxOffset =
options.itemWidth ||
item.legendItemWidth ||
symbolWidth + symbolPadding + bBox.width + itemDistance +
(showCheckbox ? 20 : 0);
bBox.width + itemExtraWidth;
legend.itemHeight = itemHeight = Math.round(
item.legendItemHeight || bBox.height || legend.symbolHeight
);
@@ -14416,7 +15030,7 @@
lastY,
allItems = this.allItems,
clipToHeight = function(height) {
if (height) {
if (typeof height === 'number') {
clipRect.attr({
height: height
});
@@ -14740,7 +15354,7 @@
// to nested group elements, as the legend item texts are within 4 group
// elements.
if (/Trident\/7\.0/.test(win.navigator.userAgent) || isFirefox) {
wrap(Legend.prototype, 'positionItem', function(proceed, item) {
wrap(Highcharts.Legend.prototype, 'positionItem', function(proceed, item) {
var legend = this,
// If chart destroyed in sync, this is undefined (#2030)
runPositionItem = function() {
@@ -14788,6 +15402,7 @@
Legend = H.Legend, // @todo add as requirement
marginNames = H.marginNames,
merge = H.merge,
objectEach = H.objectEach,
Pointer = H.Pointer, // @todo add as requirement
pick = H.pick,
pInt = H.pInt,
@@ -14799,23 +15414,62 @@
win = H.win,
Renderer = H.Renderer;
/**
* The Chart class.
* The Chart class. The recommended constructor is {@link Highcharts#chart}.
* @class Highcharts.Chart
* @memberOf Highcharts
* @param {String|HTMLDOMElement} renderTo - The DOM element to render to, or its
* id.
* @param {ChartOptions} options - The chart options structure.
* @param {Function} callback - Function to run when the chart has loaded.
* @param {String|HTMLDOMElement} renderTo
* The DOM element to render to, or its id.
* @param {Options} options
* The chart options structure.
* @param {Function} [callback]
* Function to run when the chart has loaded and and all external images
* are loaded. Defining a {@link
* https://api.highcharts.com/highcharts/chart.events.load|chart.event.load}
* handler is equivalent.
*
* @example
* var chart = new Highcharts.Chart('container', {
* title: {
* text: 'My chart'
* },
* series: [{
* data: [1, 3, 2, 4]
* }]
* })
*/
var Chart = H.Chart = function() {
this.getArgs.apply(this, arguments);
};
 
/**
* Factory function for basic charts.
*
* @function #chart
* @memberOf Highcharts
* @param {String|HTMLDOMElement} renderTo - The DOM element to render to, or
* its id.
* @param {Options} options - The chart options structure.
* @param {Function} [callback] - Function to run when the chart has loaded and
* and all external images are loaded. Defining a {@link
* https://api.highcharts.com/highcharts/chart.events.load|chart.event.load}
* handler is equivalent.
* @return {Highcharts.Chart} - Returns the Chart object.
*
* @example
* // Render a chart in to div#container
* var chart = Highcharts.chart('container', {
* title: {
* text: 'My chart'
* },
* series: [{
* data: [1, 3, 2, 4]
* }]
* });
*/
H.chart = function(a, b, c) {
return new Chart(a, b, c);
};
 
Chart.prototype = {
extend(Chart.prototype, /** @lends Highcharts.Chart.prototype */ {
 
/**
* Hook for modules
@@ -14844,10 +15498,27 @@
 
// Handle regular options
var options,
seriesOptions = userOptions.series; // skip merging data points to increase performance
type,
seriesOptions = userOptions.series, // skip merging data points to increase performance
userPlotOptions = userOptions.plotOptions || {};
 
userOptions.series = null;
options = merge(defaultOptions, userOptions); // do the merge
 
// Override (by copy of user options) or clear tooltip options
// in chart.options.plotOptions (#6218)
for (type in options.plotOptions) {
options.plotOptions[type].tooltip = (
userPlotOptions[type] &&
merge(userPlotOptions[type].tooltip) // override by copy
) || undefined; // or clear
}
// User options have higher priority than default options (#6218).
// In case of exporting: path is changed
options.tooltip.userOptions = (userOptions.chart &&
userOptions.chart.forExport && userOptions.tooltip.userOptions) ||
userOptions.tooltip;
 
options.series = userOptions.series = seriesOptions; // set back the series data
this.userOptions = userOptions;
 
@@ -14866,12 +15537,59 @@
 
this.callback = callback;
this.isResizing = 0;
 
/**
* The options structure for the chart. It contains members for the sub
* elements like series, legend, tooltip etc.
*
* @memberof Highcharts.Chart
* @name options
* @type {Options}
*/
this.options = options;
//chartTitleOptions = undefined;
//chartSubtitleOptions = undefined;
/**
* All the axes in the chart.
*
* @memberof Highcharts.Chart
* @name axes
* @see Highcharts.Chart.xAxis
* @see Highcharts.Chart.yAxis
* @type {Array.<Highcharts.Axis>}
*/
this.axes = [];
 
this.axes = [];
/**
* All the current series in the chart.
*
* @memberof Highcharts.Chart
* @name series
* @type {Array.<Highcharts.Series>}
*/
this.series = [];
 
/**
* The chart title. The title has an `update` method that allows
* modifying the options directly or indirectly via `chart.update`.
*
* @memberof Highcharts.Chart
* @name title
* @type Object
*
* @sample highcharts/members/title-update/
* Updating titles
*/
 
/**
* The chart subtitle. The subtitle has an `update` method that allows
* modifying the options directly or indirectly via `chart.update`.
*
* @memberof Highcharts.Chart
* @name subtitle
* @type Object
*/
 
 
 
this.hasCartesianSeries = optionsChart.showAxes;
//this.axisOffset = undefined;
//this.inverted = undefined;
@@ -14887,7 +15605,6 @@
//this.oldChartHeight = undefined;
 
//this.renderTo = undefined;
//this.renderToClone = undefined;
 
//this.spacingBox = undefined
 
@@ -14901,22 +15618,34 @@
//this.loadingDiv = undefined;
//this.loadingSpan = undefined;
 
var chart = this,
eventType;
var chart = this;
 
// Add the chart to the global lookup
chart.index = charts.length;
 
charts.push(chart);
H.chartCount++;
 
// Chart event handlers
if (chartEvents) {
for (eventType in chartEvents) {
addEvent(chart, eventType, chartEvents[eventType]);
}
objectEach(chartEvents, function(event, eventType) {
addEvent(chart, eventType, event);
});
}
 
/**
* A collection of the X axes in the chart.
* @type {Array.<Highcharts.Axis>}
* @name xAxis
* @memberOf Highcharts.Chart
*/
chart.xAxis = [];
/**
* A collection of the Y axes in the chart.
* @type {Array.<Highcharts.Axis>}
* @name yAxis
* @memberOf Highcharts.Chart
*/
chart.yAxis = [];
 
chart.pointCount = chart.colorCounter = chart.symbolCounter = 0;
@@ -14950,7 +15679,7 @@
* #6112). This function is called on series initialization and destroy.
*
* @param {number} fromIndex - If this is given, only the series above this
* index are handled.
* index are handled.
*/
orderSeries: function(fromIndex) {
var series = this.series,
@@ -14982,10 +15711,16 @@
},
 
/**
* Redraw legend, axes or series based on updated data
* 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.
*
* @param {Boolean|Object} animation Whether to apply animation, and optionally animation
* configuration
* @param {AnimationOptions} animation
* If or how to apply animation to the redraw.
*/
redraw: function(animation) {
var chart = this,
@@ -15012,7 +15747,7 @@
H.setAnimation(animation, chart);
 
if (isHiddenChart) {
chart.cloneRenderTo();
chart.temporaryDisplay();
}
 
// Adjust title layout (reflow multiline text)
@@ -15139,7 +15874,7 @@
fireEvent(chart, 'render');
 
if (isHiddenChart) {
chart.cloneRenderTo(true);
chart.temporaryDisplay(true);
}
 
// Fire callbacks that are put on hold until after the redraw
@@ -15149,8 +15884,13 @@
},
 
/**
* Get an axis, series or point object by id.
* @param id {String} The id as given in the configuration options
* Get an axis, series or point object by `id` as given in the configuration
* options. Returns `undefined` if no item is found.
* @param id {String} The id as given in the configuration options.
* @return {Highcharts.Axis|Highcharts.Series|Highcharts.Point|undefined}
* The retrieved item.
* @sample highcharts/plotoptions/series-id/
* Get series by id
*/
get: function(id) {
 
@@ -15207,12 +15947,21 @@
 
 
/**
* Get the currently selected points from all series
* 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.
*
* @return {Array.<Highcharts.Point>}
* The currently selected points.
*
* @sample highcharts/plotoptions/series-allowpointselect-line/
* Get selected points
*/
getSelectedPoints: function() {
var points = [];
each(this.series, function(serie) {
points = points.concat(grep(serie.points || [], function(point) {
// series.data - for points outside of viewed range (#6445)
points = points.concat(grep(serie.data || [], function(point) {
return point.selected;
}));
});
@@ -15220,7 +15969,18 @@
},
 
/**
* Get the currently selected series
* 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 {@link
* https://api.highcharts.com/highcharts/plotOptions.series.showCheckbox|
* series.showCheckBox} is true.
*
* @return {Array.<Highcharts.Series>}
* The currently selected series.
*
* @sample highcharts/members/chart-getselectedseries/
* Get selected series
*/
getSelectedSeries: function() {
return grep(this.series, function(serie) {
@@ -15229,11 +15989,18 @@
},
 
/**
* Show the title and subtitle of the chart
* Set a new title or subtitle for the chart.
*
* @param titleOptions {Object} New title options
* @param subtitleOptions {Object} New subtitle options
* @param titleOptions {TitleOptions}
* New title options.
* @param subtitleOptions {SubtitleOptions}
* New subtitle options.
* @param redraw {Boolean}
* Whether to redraw the chart or wait for a later call to
* `chart.redraw()`.
*
* @sample highcharts/members/chart-settitle/ Set title text and styles
*
*/
setTitle: function(titleOptions, subtitleOptions, redraw) {
var chart = this,
@@ -15324,6 +16091,9 @@
each(['title', 'subtitle'], function(key) {
var title = this[key],
titleOptions = this.options[key],
offset = key === 'title' ? -3 :
// Floating subtitle (#6574)
titleOptions.verticalAlign ? 0 : titleOffset + 2,
titleSize;
 
if (title) {
@@ -15338,7 +16108,7 @@
spacingBox.width + titleOptions.widthAdjust) + 'px'
})
.align(extend({
y: titleOffset + titleSize + (key === 'title' ? -3 : 2)
y: offset + titleSize
}, titleOptions), false, 'spacingBox');
 
if (!titleOptions.floating && !titleOptions.verticalAlign) {
@@ -15371,7 +16141,7 @@
optionsChart = chart.options.chart,
widthOption = optionsChart.width,
heightOption = optionsChart.height,
renderTo = chart.renderToClone || chart.renderTo;
renderTo = chart.renderTo;
 
// Get inner width and height
if (!defined(widthOption)) {
@@ -15395,41 +16165,47 @@
},
 
/**
* Create a clone of the chart's renderTo div and place it outside the viewport to allow
* size computation on chart.render and chart.redraw
* 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.
*
* @param {Boolean} revert - Revert to the saved original styles.
*/
cloneRenderTo: function(revert) {
var clone = this.renderToClone,
container = this.container;
temporaryDisplay: function(revert) {
var node = this.renderTo,
tempStyle;
if (!revert) {
while (node && node.style) {
if (getStyle(node, 'display', false) === 'none') {
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;
}
 
// Destroy the clone and bring the container back to the real renderTo div
if (revert) {
if (clone) {
while (clone.childNodes.length) { // #5231
this.renderTo.appendChild(clone.firstChild);
H.css(node, tempStyle);
if (node.style.setProperty) { // #2631
node.style.setProperty('display', 'block', 'important');
}
}
discardElement(clone);
delete this.renderToClone;
node = node.parentNode;
}
 
// Set up the clone
} else {
if (container && container.parentNode === this.renderTo) {
this.renderTo.removeChild(container); // do not clone this
while (node && node.style) {
if (node.hcOrigStyle) {
H.css(node, node.hcOrigStyle);
delete node.hcOrigStyle;
}
node = node.parentNode;
}
this.renderToClone = clone = this.renderTo.cloneNode(0);
css(clone, {
position: 'absolute',
top: '-9999px',
display: 'block' // #833
});
if (clone.style.setProperty) { // #2631
clone.style.setProperty('display', 'block', 'important');
}
doc.body.appendChild(clone);
if (container) {
clone.appendChild(container);
}
}
},
 
@@ -15441,8 +16217,8 @@
},
 
/**
* Get the containing element, determine the size and create the inner container
* div to hold the chart
* Get the containing element, determine the size and create the inner
* container div to hold the chart
*/
getContainer: function() {
var chart = this,
@@ -15472,12 +16248,17 @@
H.error(13, true);
}
 
// 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,
// 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).
oldChartIndex = pInt(attr(renderTo, indexAttrName));
if (isNumber(oldChartIndex) && charts[oldChartIndex] && charts[oldChartIndex].hasRendered) {
if (
isNumber(oldChartIndex) &&
charts[oldChartIndex] &&
charts[oldChartIndex].hasRendered
) {
charts[oldChartIndex].destroy();
}
 
@@ -15493,7 +16274,7 @@
// 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.cloneRenderTo();
chart.temporaryDisplay();
}
 
// get the width and height
@@ -15505,8 +16286,8 @@
 
containerStyle = extend({
position: 'relative',
overflow: 'hidden', // needed for context menu (avoid scrollbars) and
// content overflow in IE
overflow: 'hidden', // needed for context menu (avoid scrollbars)
// and content overflow in IE
width: chartWidth + 'px',
height: chartHeight + 'px',
textAlign: 'left',
@@ -15515,13 +16296,23 @@
'-webkit-tap-highlight-color': 'rgba(0,0,0,0)'
}, optionsChart.style);
 
chart.container = container = createElement(
 
/**
* The containing HTML element of the chart. The container is
* dynamically inserted into the element given as the `renderTo`
* parameterin the {@link Highcharts#chart} constructor.
*
* @memberOf Highcharts.Chart
* @type {HTMLDOMElement}
*/
container = createElement(
'div', {
id: containerId
},
containerStyle,
chart.renderToClone || renderTo
renderTo
);
chart.container = container;
 
// cache the cursor (#1650)
chart._cursor = container.style.cursor;
@@ -15548,9 +16339,9 @@
},
 
/**
* 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
* 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
*/
getMargins: function(skipAxes) {
var chart = this,
@@ -15562,7 +16353,10 @@
 
// Adjust for title and subtitle
if (titleOffset && !defined(margin[0])) {
chart.plotTop = Math.max(chart.plotTop, titleOffset + chart.options.title.margin + spacing[0]);
chart.plotTop = Math.max(
chart.plotTop,
titleOffset + chart.options.title.margin + spacing[0]
);
}
 
// Adjust for legend
@@ -15572,7 +16366,8 @@
 
// adjust for scroller
if (chart.extraMargin) {
chart[chart.extraMargin.type] = (chart[chart.extraMargin.type] || 0) + chart.extraMargin.value;
chart[chart.extraMargin.type] =
(chart[chart.extraMargin.type] || 0) + chart.extraMargin.value;
}
if (chart.extraTopMargin) {
chart.plotTop += chart.extraTopMargin;
@@ -15585,7 +16380,8 @@
getAxisMargins: function() {
 
var chart = this,
axisOffset = chart.axisOffset = [0, 0, 0, 0], // top, right, bottom, left
// [top, right, bottom, left]
axisOffset = chart.axisOffset = [0, 0, 0, 0],
margin = chart.margin;
 
// pre-render axes to get labels offset width
@@ -15609,7 +16405,21 @@
},
 
/**
* Resize the chart to its container if size is not explicitly set
* Reflows the chart to its container. By default, the chart reflows
* automatically to its container following a `window.resize` event, as per
* the {@link https://api.highcharts/highcharts/chart.reflow|chart.reflow}
* option. However, there are no reliable events for div resize, so if the
* container is resized without a window resize event, this must be called
* explicitly.
*
* @param {Object} e
* Event arguments. Used primarily when the function is called
* internally as a response to window resize.
*
* @sample highcharts/members/chart-reflow/
* Resize div and reflow
* @sample highcharts/chart/events-container/
* Pop up and reflow
*/
reflow: function(e) {
var chart = this,
@@ -15620,14 +16430,25 @@
height = optionsChart.height || getStyle(renderTo, 'height'),
target = e ? e.target : win;
 
// Width and height checks for display:none. Target is doc in IE8 and Opera,
// win in Firefox, Chrome and IE9.
if (!hasUserWidth && !chart.isPrinting && width && height && (target === win || target === doc)) { // #1093
if (width !== chart.containerWidth || height !== chart.containerHeight) {
// Width and height checks for display:none. Target is doc in IE8 and
// Opera, win in Firefox, Chrome and IE9.
if (!hasUserWidth &&
!chart.isPrinting &&
width &&
height &&
(target === win || target === doc)
) {
if (
width !== chart.containerWidth ||
height !== chart.containerHeight
) {
clearTimeout(chart.reflowTimeout);
// When called from window.resize, e is set, else it's called directly (#2224)
// When called from window.resize, e is set, else it's called
// directly (#2224)
chart.reflowTimeout = syncTimeout(function() {
if (chart.container) { // It may have been destroyed in the meantime (#1257)
// Set size, it may have been destroyed in the meantime
// (#1257)
if (chart.container) {
chart.setSize(undefined, undefined, false);
}
}, e ? 100 : 0);
@@ -15662,10 +16483,27 @@
},
 
/**
* Resize the chart to a given width and height
* @param {Number} width
* @param {Number} height
* @param {Object|Boolean} animation
* 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.
* @param {Number|undefined|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|undefined|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 {AnimationOptions} [animation=true]
* Whether and how to apply animation.
*
* @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/
* Highstock with UI resizable
*/
setSize: function(width, height, animation) {
var chart = this,
@@ -15688,7 +16526,8 @@
}
chart.getChartSize();
 
// Resize the container with the global animation applied if enabled (#2503)
// Resize the container with the global animation applied if enabled
// (#2503)
 
globalAnimation = renderer.globalAnimation;
(globalAnimation ? animate : css)(chart.container, {
@@ -15718,7 +16557,8 @@
chart.oldChartHeight = null;
fireEvent(chart, 'resize');
 
// Fire endResize and set isResizing back. If animation is disabled, fire without delay
// Fire endResize and set isResizing back. If animation is disabled,
// fire without delay
syncTimeout(function() {
if (chart) {
fireEvent(chart, 'endResize', null, function() {
@@ -15729,8 +16569,8 @@
},
 
/**
* Set the public chart properties. This is done before and after the pre-render
* to determine margin sizes
* Set the public chart properties. This is done before and after the
* pre-render to determine margin sizes
*/
setChartSize: function(skipAxes) {
var chart = this,
@@ -15749,10 +16589,21 @@
plotHeight,
plotBorderWidth;
 
function clipOffsetSide(side) {
var offset = clipOffset[side] || 0;
return Math.max(plotBorderWidth || offset, offset) / 2;
}
 
chart.plotLeft = plotLeft = Math.round(chart.plotLeft);
chart.plotTop = plotTop = Math.round(chart.plotTop);
chart.plotWidth = plotWidth = Math.max(0, Math.round(chartWidth - plotLeft - chart.marginRight));
chart.plotHeight = plotHeight = Math.max(0, Math.round(chartHeight - plotTop - chart.marginBottom));
chart.plotWidth = plotWidth = Math.max(
0,
Math.round(chartWidth - plotLeft - chart.marginRight)
);
chart.plotHeight = plotHeight = Math.max(
0,
Math.round(chartHeight - plotTop - chart.marginBottom)
);
 
chart.plotSizeX = inverted ? plotHeight : plotWidth;
chart.plotSizeY = inverted ? plotWidth : plotHeight;
@@ -15774,13 +16625,24 @@
};
 
plotBorderWidth = 2 * Math.floor(chart.plotBorderWidth / 2);
clipX = Math.ceil(Math.max(plotBorderWidth, clipOffset[3]) / 2);
clipY = Math.ceil(Math.max(plotBorderWidth, clipOffset[0]) / 2);
clipX = Math.ceil(clipOffsetSide(3));
clipY = Math.ceil(clipOffsetSide(0));
chart.clipBox = {
x: clipX,
y: clipY,
width: Math.floor(chart.plotSizeX - Math.max(plotBorderWidth, clipOffset[1]) / 2 - clipX),
height: Math.max(0, Math.floor(chart.plotSizeY - Math.max(plotBorderWidth, clipOffset[2]) / 2 - clipY))
width: Math.floor(
chart.plotSizeX -
clipOffsetSide(1) -
clipX
),
height: Math.max(
0,
Math.floor(
chart.plotSizeY -
clipOffsetSide(2) -
clipY
)
)
};
 
if (!skipAxes) {
@@ -15804,16 +16666,20 @@
values = isObject(value) ? value : [value, value, value, value];
 
each(['Top', 'Right', 'Bottom', 'Left'], function(sideName, side) {
chart[target][side] = pick(chartOptions[target + sideName], values[side]);
chart[target][side] = pick(
chartOptions[target + sideName],
values[side]
);
});
});
 
// Set margin names like chart.plotTop, chart.plotLeft, chart.marginRight, chart.marginBottom.
// Set margin names like chart.plotTop, chart.plotLeft,
// chart.marginRight, chart.marginBottom.
each(marginNames, function(m, side) {
chart[m] = pick(chart.margin[side], chart.spacing[side]);
});
chart.axisOffset = [0, 0, 0, 0]; // top, right, bottom, left
chart.clipOffset = [0, 0, 0, 0];
chart.clipOffset = [];
},
 
/**
@@ -15900,8 +16766,13 @@
// Create the background image
if (plotBackgroundImage) {
if (!plotBGImage) {
chart.plotBGImage = renderer.image(plotBackgroundImage, plotLeft, plotTop, plotWidth, plotHeight)
.add();
chart.plotBGImage = renderer.image(
plotBackgroundImage,
plotLeft,
plotTop,
plotWidth,
plotHeight
).add();
} else {
plotBGImage.animate(plotBox);
}
@@ -15951,9 +16822,9 @@
},
 
/**
* 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.
* 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.
*/
propFromSeries: function() {
var chart = this,
@@ -15967,12 +16838,14 @@
each(['inverted', 'angular', 'polar'], function(key) {
 
// The default series type's class
klass = seriesTypes[optionsChart.type || optionsChart.defaultSeriesType];
klass = seriesTypes[optionsChart.type ||
optionsChart.defaultSeriesType];
 
// Get the value from available chart-wide properties
value =
optionsChart[key] || // It is set in the options
(klass && klass.prototype[key]); // The default series class requires it
(klass && klass.prototype[key]); // The default series class
// requires it
 
// 4. Check if any the chart's series require it
i = seriesOptions && seriesOptions.length;
@@ -15990,8 +16863,8 @@
},
 
/**
* Link two or more series together. This is done initially from Chart.render,
* and after Chart.addSeries and Series.remove.
* Link two or more series together. This is done initially from
* Chart.render, and after Chart.addSeries and Series.remove.
*/
linkSeries: function() {
var chart = this,
@@ -16011,10 +16884,15 @@
} else {
linkedTo = chart.get(linkedTo);
}
if (linkedTo && linkedTo.linkedParent !== series) { // #3341 avoid mutual linking
// #3341 avoid mutual linking
if (linkedTo && linkedTo.linkedParent !== series) {
linkedTo.linkedSeries.push(series);
series.linkedParent = linkedTo;
series.visible = pick(series.options.visible, linkedTo.options.visible, series.visible); // #3879
series.visible = pick(
series.options.visible,
linkedTo.options.visible,
series.visible
); // #3879
}
}
});
@@ -16154,7 +17032,11 @@
},
 
/**
* Show chart credits based on config options
* Set a new credits label for the chart.
*
* @param {CreditOptions} options
* A configuration object for the new credits.
* @sample highcharts/credits/credits-update/ Add and update credits
*/
addCredits: function(credits) {
var chart = this;
@@ -16161,6 +17043,17 @@
 
credits = merge(true, this.options.credits, credits);
if (credits.enabled && !this.credits) {
 
/**
* The chart's credits label. The label has an `update` method that
* allows setting new options as per the {@link
* https://api.highcharts.com/highcharts/credits|
* credits options set}.
*
* @memberof Highcharts.Chart
* @name credits
* @type {Highcharts.SVGElement}
*/
this.credits = this.renderer.text(
credits.text + (this.mapCredits || ''),
0,
@@ -16191,7 +17084,14 @@
},
 
/**
* Clean up memory usage
* 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 Highstock
*/
destroy: function() {
var chart = this,
@@ -16205,7 +17105,11 @@
fireEvent(chart, 'destroy');
 
// Delete the chart from charts lookup array
charts[chart.index] = undefined;
if (chart.renderer.forExport) {
H.erase(charts, chart); // #6569
} else {
charts[chart.index] = undefined;
}
H.chartCount--;
chart.renderTo.removeAttribute('data-highcharts-chart');
 
@@ -16255,9 +17159,9 @@
}
 
// clean it all up
for (i in chart) {
delete chart[i];
}
objectEach(chart, function(val, key) {
delete chart[key];
});
 
},
 
@@ -16335,7 +17239,7 @@
}
 
// If the chart was rendered outside the top container, put it back in (#3679)
chart.cloneRenderTo(true);
chart.temporaryDisplay(true);
 
},
 
@@ -16364,10 +17268,10 @@
this.onload = null;
}
 
}; // end Chart
}); // end Chart
 
}(Highcharts));
(function(H) {
(function(Highcharts) {
/**
* (c) 2010-2017 Torstein Honsi
*
@@ -16374,6 +17278,7 @@
* License: www.highcharts.com/license
*/
var Point,
H = Highcharts,
 
each = H.each,
extend = H.extend,
@@ -16386,18 +17291,19 @@
removeEvent = H.removeEvent;
 
/**
* The Point object. The point objects are generated from the series.data
* 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.
* @constructor Point
* `Series.points` array. Other ways to instaniate points are through {@link
* Highcharts.Series#addPoint} or {@link Highcharts.Series#setData}.
*
* @class
*/
Point = H.Point = function() {};
Point.prototype = {
 
Highcharts.Point = Point = function() {};
Highcharts.Point.prototype = {
 
/**
* Initialize the point. Called internally based on the series.data option.
* @function #init
* @memberOf Point
* @param {Object} series The series object containing this point.
* @param {Object} options The data in either number, array or object
* format.
@@ -16411,8 +17317,22 @@
colorCount = series.chart.options.chart.colorCount,
colorIndex;
 
/**
* The series object associated with the point.
*
* @name series
* @memberof Highcharts.Point
* @type Highcharts.Series
*/
point.series = series;
 
 
/**
* The point's current color.
* @name color
* @memberof Highcharts.Point
* @type {Color}
*/
point.color = series.color; // #3445
 
point.applyOptions(options, x);
@@ -16441,8 +17361,6 @@
* Apply the options containing the x and y data and possible some extra
* properties. Called on point init or from point.update.
*
* @function #applyOptions
* @memberOf Point
* @param {Object} options The point options as defined in series.data.
* @param {Number} x Optionally, the X value.
* @returns {Object} The Point instance.
@@ -16713,9 +17631,60 @@
 
fireEvent(this, eventType, eventArgs, defaultFunction);
},
 
/**
* For certain series types, like pie charts, where individual points can
* be shown or hidden.
*
* @name visible
* @memberOf Highcharts.Point
* @type {Boolean}
*/
visible: true
};
 
/**
* For categorized axes this property holds the category name for the
* point. For other axes it holds the X value.
*
* @name category
* @memberOf Highcharts.Point
* @type {String|Number}
*/
 
/**
* The percentage for points in a stacked series or pies.
*
* @name percentage
* @memberOf Highcharts.Point
* @type {Number}
*/
 
/**
* The total of values in either a stack for stacked series, or a pie in a pie
* series.
*
* @name total
* @memberOf Highcharts.Point
* @type {Number}
*/
 
/**
* The x value of the point.
*
* @name x
* @memberOf Highcharts.Point
* @type {Number}
*/
 
/**
* The y value of the point.
*
* @name y
* @memberOf Highcharts.Point
* @type {Number}
*/
 
}(Highcharts));
(function(H) {
/**
@@ -16742,6 +17711,7 @@
isString = H.isString,
LegendSymbolMixin = H.LegendSymbolMixin, // @todo add as a requirement
merge = H.merge,
objectEach = H.objectEach,
pick = H.pick,
Point = H.Point, // @todo add as a requirement
removeEvent = H.removeEvent,
@@ -16751,24 +17721,51 @@
win = H.win;
 
/**
* The base function which all other series types inherit from. The data in the series is stored
* in various arrays.
* This is the base series prototype that all other series types inherit from.
* A new series is initiated either through the {@link https://api.highcharts.com/highcharts/series|
* series} option structure, or after the chart is initiated, through {@link
* Highcharts.Chart#addSeries}.
*
* - First, series.options.data contains all the original config options for
* each point whether added by options or methods like series.addPoint.
* - 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.
* - series.xData and series.processedXData contain clean x values, equivalent to series.data and series.points.
* - series.yData and series.processedYData contain clean y values, equivalent to series.data and series.points.
* 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}.
*
* @constructor Series
* @param {Object} chart - The chart instance.
* @param {Object} options - The series options.
* Configuration options for the series are given in three levels. Options for
* all series in a chart are given in the {@link https://api.highcharts.com/highcharts/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, or as
* arguements 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`.
* - 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.
* - `series.xData` and `series.processedXData` contain clean x values, equivalent
* to `series.data` and `series.points`.
* - `series.yData` and `series.processedYData` contain clean y values, equivalent
* to `series.data` and `series.points`.
*
* @class Highcharts.Series
* @param {Highcharts.Chart} chart
* The chart instance.
* @param {Object} options
* The series options.
*/
H.Series = H.seriesType('line', null, { // base series options
 
@@ -16846,11 +17843,13 @@
// borderRadius: undefined,
padding: 5
},
cropThreshold: 300, // draw points outside the plot area when the number of points is less than this
// draw points outside the plot area when the number of points is less than
// this
cropThreshold: 300,
pointRange: 0,
//pointStart: 0,
//pointInterval: 1,
//showInLegend: null, // auto: true for standalone series, false for linked series
//showInLegend: null, // auto = false for linked series
softThreshold: true,
states: { // states for the entire series
hover: {
@@ -16876,7 +17875,8 @@
},
stickyTracking: true,
//tooltip: {
//pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.y}</b>'
//pointFormat: '<span style="color:{point.color}">\u25CF</span>' +
// '{series.name}: <b>{point.y}</b>'
//valueDecimals: null,
//xDateFormat: '%A, %b %e, %Y',
//valuePrefix: '',
@@ -16886,7 +17886,7 @@
// zIndex: null
findNearestPointBy: 'x'
 
}, /** @lends Series.prototype */ {
}, /** @lends Highcharts.Series.prototype */ {
isCartesian: true,
pointClass: Point,
sorted: true, // requires the data to be sorted
@@ -16894,17 +17894,43 @@
directTouch: false,
axisTypes: ['xAxis', 'yAxis'],
colorCounter: 0,
parallelArrays: ['x', 'y'], // each point's x and y values are stored in this.xData and this.yData
// each point's x and y values are stored in this.xData and this.yData
parallelArrays: ['x', 'y'],
coll: 'series',
init: function(chart, options) {
var series = this,
eventType,
events,
chartSeries = chart.series,
lastSeries;
 
/**
* Read only. The chart that the series belongs to.
*
* @name chart
* @memberOf Series
* @type {Chart}
*/
series.chart = chart;
series.options = options = series.setOptions(options); // merge with plotOptions
 
/**
* 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 type
* @memberOf Series
* @type String
*/
 
/**
* Read only. The series' current options. To update, use {@link
* Series#update}.
*
* @name options
* @memberOf Series
* @type SeriesOptions
*/
series.options = options = series.setOptions(options);
series.linkedSeries = [];
 
// bind the axes
@@ -16912,20 +17938,50 @@
 
// set some variables
extend(series, {
/**
* The series name as given in the options. Defaults to
* "Series {n}".
*
* @name name
* @memberOf Series
* @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 visible
* @memberOf Series
* @type {Boolean}
*/
visible: options.visible !== false, // true by default
/**
* Read only. The series' selected state as set by {@link
* Highcharts.Series#select}.
*
* @name selected
* @memberOf Series
* @type {Boolean}
*/
selected: options.selected === true // false by default
});
 
// register event listeners
events = options.events;
for (eventType in events) {
addEvent(series, eventType, events[eventType]);
}
 
objectEach(events, function(event, eventType) {
addEvent(series, eventType, event);
});
if (
(events && events.click) ||
(options.point && options.point.events && options.point.events.click) ||
(
options.point &&
options.point.events &&
options.point.events.click
) ||
options.allowPointSelect
) {
chart.runTrackerClick = true;
@@ -17004,21 +18060,47 @@
chart = series.chart,
axisOptions;
 
each(series.axisTypes || [], function(AXIS) { // repeat for xAxis and yAxis
// repeat for xAxis and yAxis
each(series.axisTypes || [], function(AXIS) {
 
each(chart[AXIS], function(axis) { // loop through the chart's axis objects
// loop through the chart's axis objects
each(chart[AXIS], function(axis) {
axisOptions = axis.options;
 
// apply if the series xAxis or yAxis option mathches the number of the
// axis, or if undefined, use the first axis
if ((seriesOptions[AXIS] === axisOptions.index) ||
(seriesOptions[AXIS] !== undefined && seriesOptions[AXIS] === axisOptions.id) ||
(seriesOptions[AXIS] === undefined && axisOptions.index === 0)) {
// apply if the series xAxis or yAxis option mathches the number
// of the axis, or if undefined, use the first axis
if (
seriesOptions[AXIS] === axisOptions.index ||
(
seriesOptions[AXIS] !== undefined &&
seriesOptions[AXIS] === axisOptions.id
) ||
(
seriesOptions[AXIS] === undefined &&
axisOptions.index === 0
)
) {
 
// register this series in the axis.series lookup
series.insert(axis.series);
 
// set this series.xAxis or series.yAxis reference
/**
* Read only. The unique xAxis object associated with the
* series.
*
* @name xAxis
* @memberOf Series
* @type Axis
*/
/**
* Read only. The unique yAxis object associated with the
* series.
*
* @name yAxis
* @memberOf Series
* @type Axis
*/
series[AXIS] = axis;
 
// mark dirty for redraw
@@ -17035,10 +18117,11 @@
},
 
/**
* For simple series types like line and column, the data values are held in arrays like
* xData and yData for quick lookup to find extremes and more. For multidimensional series
* like bubble and map, this can be extended with arrays like zData and valueData by
* adding to the series.parallelArrays array.
* For simple series types like line and column, the data values are held in
* arrays like xData and yData for quick lookup to find extremes and more.
* For multidimensional series like bubble and map, this can be extended
* with arrays like zData and valueData by adding to the
* series.parallelArrays array.
*/
updateParallelArrays: function(point, i) {
var series = point.series,
@@ -17046,12 +18129,18 @@
fn = isNumber(i) ?
// Insert the value in the given position
function(key) {
var val = key === 'y' && series.toYData ? series.toYData(point) : point[key];
var val = key === 'y' && series.toYData ?
series.toYData(point) :
point[key];
series[key + 'Data'][i] = val;
} :
// Apply the method specified in i with the following arguments as arguments
// Apply the method specified in i with the following arguments
// as arguments
function(key) {
Array.prototype[i].apply(series[key + 'Data'], Array.prototype.slice.call(args, 2));
Array.prototype[i].apply(
series[key + 'Data'],
Array.prototype.slice.call(args, 2)
);
};
 
each(series.parallelArrays, fn);
@@ -17058,8 +18147,9 @@
},
 
/**
* 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.
* 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.
*/
autoIncrement: function() {
 
@@ -17071,7 +18161,11 @@
 
xIncrement = pick(xIncrement, options.pointStart, 0);
 
this.pointInterval = pointInterval = pick(this.pointInterval, options.pointInterval, 1);
this.pointInterval = pointInterval = pick(
this.pointInterval,
options.pointInterval,
1
);
 
// Added code for pointInterval strings
if (pointIntervalUnit) {
@@ -17078,11 +18172,17 @@
date = new Date(xIncrement);
 
if (pointIntervalUnit === 'day') {
date = +date[Date.hcSetDate](date[Date.hcGetDate]() + pointInterval);
date = +date[Date.hcSetDate](
date[Date.hcGetDate]() + pointInterval
);
} else if (pointIntervalUnit === 'month') {
date = +date[Date.hcSetMonth](date[Date.hcGetMonth]() + pointInterval);
date = +date[Date.hcSetMonth](
date[Date.hcGetMonth]() + pointInterval
);
} else if (pointIntervalUnit === 'year') {
date = +date[Date.hcSetFullYear](date[Date.hcGetFullYear]() + pointInterval);
date = +date[Date.hcSetFullYear](
date[Date.hcGetFullYear]() + pointInterval
);
}
pointInterval = date - xIncrement;
 
@@ -17108,10 +18208,11 @@
 
this.userOptions = itemOptions;
 
// General series options take precedence over type options because otherwise, default
// type options like column.animation would be overwritten by the general option.
// But issues have been raised here (#3881), and the solution may be to distinguish
// between default option and userOptions like in the tooltip below.
// General series options take precedence over type options because
// otherwise, default type options like column.animation would be
// overwritten by the general option. But issues have been raised here
// (#3881), and the solution may be to distinguish between default
// option and userOptions like in the tooltip below.
options = merge(
typeOptions,
plotOptions.series,
@@ -17118,14 +18219,20 @@
itemOptions
);
 
// The tooltip options are merged between global and series specific options
// 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 = merge(
defaultOptions.tooltip,
defaultOptions.plotOptions[this.type].tooltip,
userOptions.tooltip,
userPlotOptions.series && userPlotOptions.series.tooltip,
userPlotOptions[this.type] && userPlotOptions[this.type].tooltip,
itemOptions.tooltip
defaultOptions.tooltip, // 1
defaultOptions.plotOptions.series &&
defaultOptions.plotOptions.series.tooltip, // 2
defaultOptions.plotOptions[this.type].tooltip, // 3
chartOptions.tooltip.userOptions, // 4
plotOptions.series && plotOptions.series.tooltip, // 5
plotOptions[this.type].tooltip, // 6
itemOptions.tooltip // 7
);
 
// When shared tooltip, stickyTracking is true by default,
@@ -17132,7 +18239,8 @@
// unless user says otherwise.
this.stickyTracking = pick(
itemOptions.stickyTracking,
userPlotOptions[this.type] && userPlotOptions[this.type].stickyTracking,
userPlotOptions[this.type] &&
userPlotOptions[this.type].stickyTracking,
userPlotOptions.series && userPlotOptions.series.stickyTracking,
(
this.tooltipOptions.shared && !this.noSharedTooltip ?
@@ -17149,9 +18257,14 @@
// Handle color zones
this.zoneAxis = options.zoneAxis;
zones = this.zones = (options.zones || []).slice();
if ((options.negativeColor || options.negativeFillColor) && !options.zones) {
if (
(options.negativeColor || options.negativeFillColor) &&
!options.zones
) {
zones.push({
value: options[this.zoneAxis + 'Threshold'] || options.threshold || 0,
value: options[this.zoneAxis + 'Threshold'] ||
options.threshold ||
0,
className: 'highcharts-negative',
 
color: options.negativeColor,
@@ -17185,8 +18298,12 @@
setting;
 
if (!value) {
// Pick up either the colorIndex option, or the _colorIndex after Series.update()
setting = pick(userOptions[indexName], userOptions['_' + indexName]);
// Pick up either the colorIndex option, or the _colorIndex after
// Series.update()
setting = pick(
userOptions[indexName],
userOptions['_' + indexName]
);
if (defined(setting)) { // after Series.update()
i = setting;
} else {
@@ -17214,9 +18331,15 @@
 
getColor: function() {
if (this.options.colorByPoint) {
this.options.color = null; // #4359, selected slice got series.color even when colorByPoint was set.
// #4359, selected slice got series.color even when colorByPoint was
// set.
this.options.color = null;
} else {
this.getCyclic('color', this.options.color || defaultPlotOptions[this.type].color, this.chart.options.colors);
this.getCyclic(
'color',
this.options.color || defaultPlotOptions[this.type].color,
this.chart.options.colors
);
}
},
 
@@ -17226,15 +18349,50 @@
getSymbol: function() {
var seriesMarkerOption = this.options.marker;
 
this.getCyclic('symbol', seriesMarkerOption.symbol, this.chart.options.symbols);
this.getCyclic(
'symbol',
seriesMarkerOption.symbol,
this.chart.options.symbols
);
},
 
drawLegendSymbol: LegendSymbolMixin.drawLineMarker,
 
/**
* Replace the series data with a new set of data
* @param {Object} data
* @param {Object} redraw
* 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.
*
* @param {SeriesDataOptions} data
* Takes an array of data in the same format as described under
* `series<type>data` for the given series type.
* @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 {AnimationOptions} [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 the updated data is the same length as the existing data,
* points will be updated instead of replaced. This allows updating
* with animation and performs better. In this case, the original
* array is not passed by reference. Set false to prevent.
*
* @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 Highstock
* @sample maps/members/series-setdata/
* Set new data in Highmaps
*/
setData: function(data, redraw, animation, updatePoints) {
var series = this,
@@ -17257,9 +18415,16 @@
dataLength = data.length;
redraw = pick(redraw, true);
 
// If the point count is the same as is was, just run Point.update which is
// cheaper, allows animation, and keeps references to points.
if (updatePoints !== false && dataLength && oldDataLength === dataLength && !series.cropped && !series.hasGroupedData && series.visible) {
// If the point count is the same as is was, just run Point.update which
// is cheaper, allows animation, and keeps references to points.
if (
updatePoints !== false &&
dataLength &&
oldDataLength === dataLength &&
!series.cropped &&
!series.hasGroupedData &&
series.visible
) {
each(data, function(point, i) {
// .update doesn't exist on a linked, hidden series (#3709)
if (oldData[i].update && point !== options.data[i]) {
@@ -17279,10 +18444,11 @@
series[key + 'Data'].length = 0;
});
 
// In turbo mode, only one- or twodimensional arrays of numbers are allowed. The
// first value is 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.
// In turbo mode, only one- or twodimensional arrays of numbers are
// allowed. The first value is 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.
if (turboThreshold && dataLength > turboThreshold) {
 
// find the first non-null point
@@ -17298,7 +18464,9 @@
xData[i] = this.autoIncrement();
yData[i] = data[i];
}
} else if (isArray(firstPoint)) { // assume all points are arrays
 
// Assume all points are arrays when first point is
} else if (isArray(firstPoint)) {
if (valueCount) { // [x, low, high] or [x, o, h, l, c]
for (i = 0; i < dataLength; i++) {
pt = data[i];
@@ -17313,7 +18481,9 @@
}
}
} else {
H.error(12); // Highcharts expects configs to be numbers or arrays in turbo mode
// Highcharts expects configs to be numbers or arrays in
// turbo mode
H.error(12);
}
} else {
for (i = 0; i < dataLength; i++) {
@@ -17321,17 +18491,29 @@
pt = {
series: series
};
series.pointClass.prototype.applyOptions.apply(pt, [data[i]]);
series.pointClass.prototype.applyOptions.apply(
pt, [data[i]]
);
series.updateParallelArrays(pt, i);
}
}
}
 
// Forgetting to cast strings to numbers is a common caveat when handling CSV or JSON
// Forgetting to cast strings to numbers is a common caveat when
// handling CSV or JSON
if (isString(yData[0])) {
H.error(14, true);
}
 
/**
* Read only. An array containing the series' data point objects. To
* modify the data, use {@link Highcharts.Series#setData} or {@link
* Highcharts.Point#update}.
*
* @name data
* @memberOf Highcharts.Series
* @type {Array.<Highcharts.Point>}
*/
series.data = [];
series.options.data = series.userOptions.data = data;
 
@@ -17367,12 +18549,13 @@
},
 
/**
* 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.
* 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.
*/
processData: function(force) {
var series = this,
processedXData = series.xData, // copied during slice operation below
processedXData = series.xData, // copied during slice operation
processedYData = series.yData,
dataLength = processedXData.length,
croppedData,
@@ -17384,7 +18567,9 @@
i, // loop variable
options = series.options,
cropThreshold = options.cropThreshold,
getExtremesFromAll = series.getExtremesFromAll || options.getExtremesFromAll, // #4599
getExtremesFromAll =
series.getExtremesFromAll ||
options.getExtremesFromAll, // #4599
isCartesian = series.isCartesian,
xExtremes,
val2lin = xAxis && xAxis.val2lin,
@@ -17392,9 +18577,16 @@
min,
max;
 
// 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 (isCartesian && !series.isDirty && !xAxis.isDirty && !series.yAxis.isDirty && !force) {
// 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 (
isCartesian &&
!series.isDirty &&
!xAxis.isDirty &&
!series.yAxis.isDirty &&
!force
) {
return false;
}
 
@@ -17405,16 +18597,32 @@
}
 
// optionally filter out points outside the plot area
if (isCartesian && series.sorted && !getExtremesFromAll && (!cropThreshold || dataLength > cropThreshold || series.forceCrop)) {
if (
isCartesian &&
series.sorted &&
!getExtremesFromAll &&
(!cropThreshold || dataLength > cropThreshold || series.forceCrop)
) {
 
// it's outside current extremes
if (processedXData[dataLength - 1] < min || processedXData[0] > max) {
if (
processedXData[dataLength - 1] < min ||
processedXData[0] > max
) {
processedXData = [];
processedYData = [];
 
// only crop if it's actually spilling out
} else if (processedXData[0] < min || processedXData[dataLength - 1] > max) {
croppedData = this.cropData(series.xData, series.yData, min, max);
} else if (
processedXData[0] < min ||
processedXData[dataLength - 1] > max
) {
croppedData = this.cropData(
series.xData,
series.yData,
min,
max
);
processedXData = croppedData.xData;
processedYData = croppedData.yData;
cropStart = croppedData.start;
@@ -17430,11 +18638,18 @@
val2lin(processedXData[i]) - val2lin(processedXData[i - 1]) :
processedXData[i] - processedXData[i - 1];
 
if (distance > 0 && (closestPointRange === undefined || distance < closestPointRange)) {
if (
distance > 0 &&
(
closestPointRange === undefined ||
distance < closestPointRange
)
) {
closestPointRange = distance;
 
// 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)
// 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)
} else if (distance < 0 && series.requireSorting) {
H.error(15);
}
@@ -17451,14 +18666,16 @@
},
 
/**
* 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
* 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
*/
cropData: function(xData, yData, min, max) {
var dataLength = xData.length,
cropStart = 0,
cropEnd = dataLength,
cropShoulder = pick(this.cropShoulder, 1), // line-type series need one point outside
// line-type series need one point outside
cropShoulder = pick(this.cropShoulder, 1),
i,
j;
 
@@ -17488,8 +18705,8 @@
 
 
/**
* Generate the data point after the data has been processed by cropping away
* unused points and optionally grouped in Highcharts Stock.
* Generate the data point after the data has been processed by cropping
* away unused points and optionally grouped in Highcharts Stock.
*/
generatePoints: function() {
var series = this,
@@ -17504,6 +18721,7 @@
cropStart = series.cropStart || 0,
cursor,
hasGroupedData = series.hasGroupedData,
keys = options.keys,
point,
points = [],
i;
@@ -17514,16 +18732,43 @@
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];
if (!point && dataOptions[cursor] !== undefined) { // #970
data[cursor] = point = (new PointClass()).init(series, dataOptions[cursor], processedXData[i]);
data[cursor] = point = (new PointClass()).init(
series,
dataOptions[cursor],
processedXData[i]
);
}
} else {
// splat the y data in case of ohlc data array
point = (new PointClass()).init(series, [processedXData[i]].concat(splat(processedYData[i])));
point = (new PointClass()).init(
series, [processedXData[i]].concat(splat(processedYData[i]))
);
 
/**
* Highstock 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.
*
* @name dataGroup
* @memberOf Point
* @type {Object}
*
*/
point.dataGroup = series.groupMap[i];
}
if (point) { // #6279
@@ -17532,11 +18777,22 @@
}
}
 
// Hide cropped-away points - this only runs when the number of points is above cropThreshold, or when
// swithching view from non-grouped data to grouped data (#637)
if (data && (processedDataLength !== (dataLength = data.length) || hasGroupedData)) {
// 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 swithching view from non-grouped
// data to grouped data (#637)
if (
data &&
(
processedDataLength !== (dataLength = data.length) ||
hasGroupedData
)
) {
for (i = 0; i < dataLength; i++) {
if (i === cropStart && !hasGroupedData) { // when has grouped data, clear all points
// when has grouped data, clear all points
if (i === cropStart && !hasGroupedData) {
i += processedDataLength;
}
if (data[i]) {
@@ -17560,7 +18816,8 @@
yDataLength,
activeYData = [],
activeCounter = 0,
xExtremes = xAxis.getExtremes(), // #2117, need to compensate for log X axis
// #2117, need to compensate for log X axis
xExtremes = xAxis.getExtremes(),
xMin = xExtremes.min,
xMax = xExtremes.max,
validValue,
@@ -17578,10 +18835,15 @@
x = xData[i];
y = yData[i];
 
// For points within the visible range, including the first point outside the
// visible range, consider y extremes
validValue = (isNumber(y, true) || isArray(y)) && (!yAxis.positiveValuesOnly || (y.length || y > 0));
withinRange = this.getExtremesFromAll || this.options.getExtremesFromAll || this.cropped ||
// For points within the visible range, including the first point
// outside the visible range, consider y extremes
validValue =
(isNumber(y, true) || isArray(y)) &&
(!yAxis.positiveValuesOnly || (y.length || y > 0));
withinRange =
this.getExtremesFromAll ||
this.options.getExtremesFromAll ||
this.cropped ||
((xData[i] || x) >= xMin && (xData[i] || x) <= xMax);
 
if (validValue && withinRange) {
@@ -17627,7 +18889,9 @@
hasModifyValue = !!series.modifyValue,
i,
pointPlacement = options.pointPlacement,
dynamicallyPlaced = pointPlacement === 'between' || isNumber(pointPlacement),
dynamicallyPlaced =
pointPlacement === 'between' ||
isNumber(pointPlacement),
threshold = options.threshold,
stackThreshold = options.startFromThreshold ? threshold : 0,
plotX,
@@ -17650,7 +18914,10 @@
xValue = point.x,
yValue = point.y,
yBottom = point.low,
stack = stacking && yAxis.stacks[(series.negStacks && yValue < (stackThreshold ? 0 : threshold) ? '-' : '') + series.stackKey],
stack = stacking && yAxis.stacks[(
series.negStacks &&
yValue < (stackThreshold ? 0 : threshold) ? '-' : ''
) + series.stackKey],
pointStack,
stackValues;
 
@@ -17673,14 +18940,27 @@
);
 
// Calculate the bottom y value for stacked series
if (stacking && series.visible && !point.isNull && stack && stack[xValue]) {
stackIndicator = series.getStackIndicator(stackIndicator, xValue, series.index);
if (
stacking &&
series.visible &&
!point.isNull &&
stack &&
stack[xValue]
) {
stackIndicator = series.getStackIndicator(
stackIndicator,
xValue,
series.index
);
pointStack = stack[xValue];
stackValues = pointStack.points[stackIndicator.key];
yBottom = stackValues[0];
yValue = stackValues[1];
 
if (yBottom === stackThreshold && stackIndicator.key === stack[xValue].base) {
if (
yBottom === stackThreshold &&
stackIndicator.key === stack[xValue].base
) {
yBottom = pick(threshold, yAxis.min);
}
if (yAxis.positiveValuesOnly && yBottom <= 0) { // #1200, #1232
@@ -17688,11 +18968,16 @@
}
 
point.total = point.stackTotal = pointStack.total;
point.percentage = pointStack.total && (point.y / pointStack.total * 100);
point.percentage =
pointStack.total &&
(point.y / pointStack.total * 100);
point.stackY = yValue;
 
// Place the stack label
pointStack.setOffset(series.pointXOffset || 0, series.barW || 0);
pointStack.setOffset(
series.pointXOffset || 0,
series.barW || 0
);
 
}
 
@@ -17707,16 +18992,26 @@
}
 
// Set the the plotY value, reset it for redraws
point.plotY = plotY = (typeof yValue === 'number' && yValue !== Infinity) ?
Math.min(Math.max(-1e5, yAxis.translate(yValue, 0, 1, 0, 1)), 1e5) : // #3201
point.plotY = plotY =
(typeof yValue === 'number' && yValue !== Infinity) ?
Math.min(Math.max(-1e5,
yAxis.translate(yValue, 0, 1, 0, 1)), 1e5) : // #3201
undefined;
 
point.isInside = plotY !== undefined && plotY >= 0 && plotY <= yAxis.len && // #3519
plotX >= 0 && plotX <= xAxis.len;
point.isInside =
plotY !== undefined &&
plotY >= 0 &&
plotY <= yAxis.len && // #3519
plotX >= 0 &&
plotX <= xAxis.len;
 
 
// Set client related positions for mouse tracking
point.clientX = dynamicallyPlaced ? correctFloat(xAxis.translate(xValue, 0, 0, 0, 1, pointPlacement)) : plotX; // #1514, #5383, #5518
point.clientX = dynamicallyPlaced ?
correctFloat(
xAxis.translate(xValue, 0, 0, 0, 1, pointPlacement)
) :
plotX; // #1514, #5383, #5518
 
point.negative = point.y < (threshold || 0);
 
@@ -17727,7 +19022,10 @@
// Determine auto enabling of markers (#3635, #5099)
if (!point.isNull) {
if (lastPlotX !== undefined) {
closestPointRangePx = Math.min(closestPointRangePx, Math.abs(plotX - lastPlotX));
closestPointRangePx = Math.min(
closestPointRangePx,
Math.abs(plotX - lastPlotX)
);
}
lastPlotX = plotX;
}
@@ -17743,8 +19041,13 @@
*/
getValidPoints: function(points, insideOnly) {
var chart = this.chart;
return grep(points || this.points || [], function isValidPoint(point) { // #3916, #5029
if (insideOnly && !chart.isInsidePlot(point.plotX, point.plotY, chart.inverted)) { // #5085
// #3916, #5029, #5085
return grep(points || this.points || [], function isValidPoint(point) {
if (insideOnly && !chart.isInsidePlot(
point.plotX,
point.plotY,
chart.inverted
)) {
return false;
}
return !point.isNull;
@@ -17752,8 +19055,9 @@
},
 
/**
* Set the clipping for the series. For animated series it is called twice, first to initiate
* animating the clip then the second time without the animation to set the final clip.
* Set the clipping for the series. For animated series it is called twice,
* first to initiate animating the clip then the second time without the
* animation to set the final clip.
*/
setClip: function(animation) {
var chart = this.chart,
@@ -17762,11 +19066,20 @@
inverted = chart.inverted,
seriesClipBox = this.clipBox,
clipBox = seriesClipBox || chart.clipBox,
sharedClipKey = this.sharedClipKey || ['_sharedClip', animation && animation.duration, animation && animation.easing, clipBox.height, options.xAxis, options.yAxis].join(','), // #4526
sharedClipKey =
this.sharedClipKey || [
'_sharedClip',
animation && animation.duration,
animation && animation.easing,
clipBox.height,
options.xAxis,
options.yAxis
].join(','), // #4526
clipRect = chart[sharedClipKey],
markerClipRect = chart[sharedClipKey + 'm'];
 
// If a clipping rectangle with the same properties is currently present in the chart, use that.
// If a clipping rectangle with the same properties is currently present
// in the chart, use that.
if (!clipRect) {
 
// When animation is set, prepare the initial positions
@@ -17883,7 +19196,7 @@
hasPointMarker,
enabled,
isInside,
markerGroup = series.markerGroup,
markerGroup = series[series.specialGroup] || series.markerGroup,
xAxis = series.xAxis,
markerAttribs,
globallyEnabled = pick(
@@ -18054,7 +19367,6 @@
i,
data = series.data || [],
point,
prop,
axis;
 
// add event hook
@@ -18091,8 +19403,8 @@
clearTimeout(series.animationTimeout);
 
// Destroy all SVGElements associated to the series
for (prop in series) {
if (series[prop] instanceof SVGElement && !series[prop].survive) { // Survive provides a hook for not destroying
objectEach(series, function(val, prop) {
if (val instanceof SVGElement && !val.survive) { // Survive provides a hook for not destroying
 
// issue 134 workaround
destroy = issue134 && prop === 'group' ?
@@ -18099,9 +19411,9 @@
'hide' :
'destroy';
 
series[prop][destroy]();
val[destroy]();
}
}
});
 
// remove from hoverSeries
if (chart.hoverSeries === series) {
@@ -18111,9 +19423,9 @@
chart.orderSeries();
 
// clear all members
for (prop in series) {
objectEach(series, function(val, prop) {
delete series[prop];
}
});
},
 
/**
@@ -18474,8 +19786,9 @@
},
 
/**
* 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.
* 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.
*/
plotGroup: function(prop, name, visibility, zIndex, parent) {
var group = this[prop],
@@ -18483,20 +19796,33 @@
 
// Generate it on first call
if (isNew) {
this[prop] = group = this.chart.renderer.g(name)
this[prop] = group = this.chart.renderer.g()
.attr({
zIndex: zIndex || 0.1 // IE8 and pointer logic use this
})
.add(parent);
 
group.addClass('highcharts-series-' + this.index + ' highcharts-' + this.type + '-series highcharts-color-' + this.colorIndex +
' ' + (this.options.className || ''));
}
 
// 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 ' +
'highcharts-color-' + this.colorIndex + ' ' +
(this.options.className || '')
),
true
);
 
// Place it on first and subsequent (redraw) calls
group.attr({
visibility: visibility
})[isNew ? 'attr' : 'animate'](this.getPlotBox());
})[isNew ? 'attr' : 'animate'](
this.getPlotBox()
);
return group;
},
 
@@ -18529,9 +19855,12 @@
chart = series.chart,
group,
options = series.options,
// Animation doesn't work in IE8 quirks when the group div is hidden,
// and looks bad in other oldIE
animDuration = !!series.animate && chart.renderer.isSVG && animObject(options.animation).duration,
// Animation doesn't work in IE8 quirks when the group div is
// hidden, and looks bad in other oldIE
animDuration = (!!series.animate &&
chart.renderer.isSVG &&
animObject(options.animation).duration
),
visibility = series.visible ? 'inherit' : 'hidden', // #2597
zIndex = options.zIndex,
hasRendered = series.hasRendered,
@@ -18587,7 +19916,10 @@
 
 
// draw the mouse tracking area
if (series.drawTracker && series.options.enableMouseTracking !== false) {
if (
series.drawTracker &&
series.options.enableMouseTracking !== false
) {
series.drawTracker();
}
 
@@ -18594,7 +19926,8 @@
// Handle inverted series and tracker groups
series.invertGroups(inverted);
 
// Initial clipping, must be defined after inverting groups for VML. Applies to columns etc. (#3839).
// Initial clipping, must be defined after inverting groups for VML.
// Applies to columns etc. (#3839).
if (options.clip !== false && !series.sharedClipKey && !hasRendered) {
group.clip(chart.clipRect);
}
@@ -18604,8 +19937,9 @@
series.animate();
}
 
// Call the afterAnimate function on animation complete (but don't overwrite the animation.complete option
// which should be available to the user).
// Call the afterAnimate function on animation complete (but don't
// overwrite the animation.complete option which should be available to
// the user).
if (!hasRendered) {
series.animationTimeout = syncTimeout(function() {
series.afterAnimate();
@@ -18613,7 +19947,8 @@
}
 
series.isDirty = false; // means data is in accordance with what you see
// (See #322) series.isDirty = series.isDirtyData = false; // means data is in accordance with what you see
// (See #322) series.isDirty = series.isDirtyData = false; // means
// data is in accordance with what you see
series.hasRendered = true;
},
 
@@ -18623,7 +19958,8 @@
redraw: function() {
var series = this,
chart = series.chart,
wasDirty = series.isDirty || series.isDirtyData, // cache it here as it is set to false in render, but used after
// cache it here as it is set to false in render, but used after
wasDirty = series.isDirty || series.isDirtyData,
group = series.group,
xAxis = series.xAxis,
yAxis = series.yAxis;
@@ -18663,8 +19999,10 @@
inverted = series.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
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);
},
 
@@ -18704,18 +20042,26 @@
// build and return nod
return {
point: points[median],
left: _kdtree(points.slice(0, median), depth + 1, dimensions),
right: _kdtree(points.slice(median + 1), depth + 1, dimensions)
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)
// Start the recursive build process with a clone of the points array
// and null points filtered out (#3873)
function startRecursive() {
series.kdTree = _kdtree(
series.getValidPoints(
null, !series.directTouch // For line-type series restrict to plot area, but column-type series not (#3916, #4511)
null,
// For line-type series restrict to plot area, but
// column-type series not (#3916, #4511)
!series.directTouch
),
dimensions,
dimensions
@@ -18738,8 +20084,12 @@
 
// Set the one and two dimensional distance on the point object
function setDistance(p1, p2) {
var x = (defined(p1[kdX]) && defined(p2[kdX])) ? Math.pow(p1[kdX] - p2[kdX], 2) : null,
y = (defined(p1[kdY]) && defined(p2[kdY])) ? Math.pow(p1[kdY] - p2[kdY], 2) : null,
var x = (defined(p1[kdX]) && defined(p2[kdX])) ?
Math.pow(p1[kdX] - p2[kdX], 2) :
null,
y = (defined(p1[kdY]) && defined(p2[kdY])) ?
Math.pow(p1[kdY] - p2[kdY], 2) :
null,
r = (x || 0) + (y || 0);
 
p2.dist = defined(r) ? Math.sqrt(r) : Number.MAX_VALUE;
@@ -18770,10 +20120,18 @@
ret = (nPoint1[kdComparer] < ret[kdComparer] ? nPoint1 : point);
}
if (tree[sideB]) {
// compare distance to current best to splitting point to decide wether to check side B or not
// compare distance to current best to splitting point to decide
// wether to check side B or not
if (Math.sqrt(tdist * tdist) < ret[kdComparer]) {
nPoint2 = _search(search, tree[sideB], depth + 1, dimensions);
ret = (nPoint2[kdComparer] < ret[kdComparer] ? nPoint2 : ret);
nPoint2 = _search(
search,
tree[sideB],
depth + 1,
dimensions
);
ret = nPoint2[kdComparer] < ret[kdComparer] ?
nPoint2 :
ret;
}
}
 
@@ -18805,6 +20163,7 @@
destroyObjectProperties = H.destroyObjectProperties,
each = H.each,
format = H.format,
objectEach = H.objectEach,
pick = H.pick,
Series = H.Series;
 
@@ -19005,9 +20364,6 @@
chart = axis.chart,
renderer = chart.renderer,
stacks = axis.stacks,
stackKey,
oneStack,
stackCategory,
stackTotalGroup = axis.stackTotalGroup;
 
// Create a separate group for the stack total labels
@@ -19026,12 +20382,11 @@
stackTotalGroup.translate(chart.plotLeft, chart.plotTop);
 
// Render each stack total
for (stackKey in stacks) {
oneStack = stacks[stackKey];
for (stackCategory in oneStack) {
oneStack[stackCategory].render(stackTotalGroup);
}
}
objectEach(stacks, function(type) {
objectEach(type, function(stack) {
stack.render(stackTotalGroup);
});
});
};
 
/**
@@ -19038,30 +20393,28 @@
* Set all the stacks to initial states and destroy unused ones.
*/
Axis.prototype.resetStacks = function() {
var stacks = this.stacks,
type,
i;
if (!this.isXAxis) {
for (type in stacks) {
for (i in stacks[type]) {
 
var axis = this,
stacks = axis.stacks;
if (!axis.isXAxis) {
objectEach(stacks, function(type) {
objectEach(type, function(stack, key) {
// Clean up memory after point deletion (#1044, #4320)
if (stacks[type][i].touched < this.stacksTouched) {
stacks[type][i].destroy();
delete stacks[type][i];
if (stack.touched < axis.stacksTouched) {
stack.destroy();
delete type[key];
 
// Reset stacks
} else {
stacks[type][i].total = null;
stacks[type][i].cum = null;
stack.total = null;
stack.cum = null;
}
}
}
});
});
}
};
 
Axis.prototype.cleanStacks = function() {
var stacks, type, i;
var stacks;
 
if (!this.isXAxis) {
if (this.oldStacks) {
@@ -19069,11 +20422,11 @@
}
 
// reset stacks
for (type in stacks) {
for (i in stacks[type]) {
stacks[type][i].cum = stacks[type][i].total;
}
}
objectEach(stacks, function(type) {
objectEach(type, function(stack) {
stack.cum = stack.total;
});
});
}
};
 
@@ -19297,7 +20650,9 @@
inArray = H.inArray,
isNumber = H.isNumber,
isObject = H.isObject,
isArray = H.isArray,
merge = H.merge,
objectEach = H.objectEach,
pick = H.pick,
Point = H.Point,
Series = H.Series,
@@ -19309,14 +20664,27 @@
extend(Chart.prototype, /** @lends Highcharts.Chart.prototype */ {
 
/**
* Add a series dynamically after time
* 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 initiated, add the series as a configuration
* option instead. With multiple axes, the `offset` is dynamically adjusted.
*
* @param {Object} options The config options
* @param {Boolean} redraw Whether to redraw the chart after adding. Defaults to true.
* @param {Boolean|Object} animation Whether to apply animation, and optionally animation
* configuration
* @param {SeriesOptions} options
* The config options for the series.
* @param {Boolean} [redraw=true]
* Whether to redraw the chart after adding.
* @param {AnimationOptions} animation
* Whether to apply animation, and optionally animation
* configuration.
*
* @return {Object} series The newly created series object
* @return {Highcharts.Series}
* The newly created series object.
*
* @sample highcharts/members/chart-addseries/
* Add a series from a button
* @sample stock/members/chart-addseries/
* Add a series in Highstock
*/
addSeries: function(options, redraw, animation) {
var series,
@@ -19342,9 +20710,21 @@
},
 
/**
* Add an axis to the chart
* @param {Object} options The axis option
* @param {Boolean} isX Whether it is an X axis or a value axis
* 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 initiated, add the axis as a configuration
* option instead.
* @param {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 {AnimationOptions} [animation=true]
* Whether and how to apply animation in the redraw.
*
* @sample highcharts/members/chart-addaxis/ Add and remove axes
*/
addAxis: function(options, isX, redraw, animation) {
var key = isX ? 'xAxis' : 'yAxis',
@@ -19366,8 +20746,21 @@
},
 
/**
* Dim the chart and show a loading text or symbol
* @param {String} str An optional text to show in the loading label instead of the default one
* 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}.
*
* @param {String} str
* An optional text to show in the loading label instead of the
* default one. The default text is set in {@link
* http://api.highcharts.com/highcharts/lang.loading|lang.loading}.
*
* @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 Highstock
*/
showLoading: function(str) {
var chart = this,
@@ -19432,7 +20825,13 @@
},
 
/**
* Hide the loading layer
* 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 Highstock
*/
hideLoading: function() {
var options = this.options,
@@ -19470,14 +20869,38 @@
* extended from plugins.
*/
propsRequireUpdateSeries: ['chart.inverted', 'chart.polar',
'chart.ignoreHiddenSeries', 'chart.type', 'colors', 'plotOptions'
'chart.ignoreHiddenSeries', 'chart.type', 'colors', 'plotOptions',
'tooltip'
],
 
/**
* Chart.update function that takes the whole options stucture.
* 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
* {@link https://api.highcharts.com/highcharts/xAxis|xAxis},
* {@link https://api.highcharts.com/highcharts/yAxis|yAxis} or
* {@link https://api.highcharts.com/highcharts/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
* 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.
*
* See also the {@link https://api.highcharts.com/highcharts/responsive|
* responsive option set}. Switching between `responsive.rules` basically
* runs `chart.update` under the hood.
*
* @param {Options} options
* A configuration object for the new chart options.
* @param {Boolean} [redraw=true]
* Whether to redraw the chart.
*
* @sample highcharts/members/chart-update/
* Update chart geometry
*/
update: function(options, redraw) {
var key,
var chart = this,
adders = {
credits: 'addCredits',
title: 'setTitle',
@@ -19491,17 +20914,17 @@
 
// If the top-level chart option is present, some special updates are required
if (optionsChart) {
merge(true, this.options.chart, optionsChart);
merge(true, chart.options.chart, optionsChart);
 
// Setter function
if ('className' in optionsChart) {
this.setClassName(optionsChart.className);
chart.setClassName(optionsChart.className);
}
 
if ('inverted' in optionsChart || 'polar' in optionsChart) {
// Parse options.chart.inverted and options.chart.polar together
// with the available series.
this.propFromSeries();
chart.propFromSeries();
updateAllAxes = true;
}
 
@@ -19509,28 +20932,36 @@
updateAllAxes = true;
}
 
for (key in optionsChart) {
if (optionsChart.hasOwnProperty(key)) {
if (inArray('chart.' + key, this.propsRequireUpdateSeries) !== -1) {
updateAllSeries = true;
}
// Only dirty box
if (inArray(key, this.propsRequireDirtyBox) !== -1) {
this.isDirtyBox = true;
}
 
objectEach(optionsChart, function(val, key) {
if (inArray('chart.' + key, chart.propsRequireUpdateSeries) !== -1) {
updateAllSeries = true;
}
}
// Only dirty box
if (inArray(key, chart.propsRequireDirtyBox) !== -1) {
chart.isDirtyBox = true;
}
});
 
 
if ('style' in optionsChart) {
this.renderer.setStyle(optionsChart.style);
chart.renderer.setStyle(optionsChart.style);
}
 
}
 
// Some option stuctures correspond one-to-one to chart objects that have
// update methods, for example
// Moved up, because tooltip needs updated plotOptions (#6218)
 
if (options.colors) {
this.options.colors = options.colors;
}
 
 
if (options.plotOptions) {
merge(true, this.options.plotOptions, options.plotOptions);
}
 
// Some option stuctures 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
@@ -19539,30 +20970,23 @@
// options.mapNavigation => chart.mapNavigation
// options.navigator => chart.navigator
// options.scrollbar => chart.scrollbar
for (key in options) {
if (this[key] && typeof this[key].update === 'function') {
this[key].update(options[key], false);
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 this[adders[key]] === 'function') {
this[adders[key]](options[key]);
} else if (typeof chart[adders[key]] === 'function') {
chart[adders[key]](val);
}
 
if (key !== 'chart' && inArray(key, this.propsRequireUpdateSeries) !== -1) {
if (
key !== 'chart' &&
inArray(key, chart.propsRequireUpdateSeries) !== -1
) {
updateAllSeries = true;
}
}
});
 
 
if (options.colors) {
this.options.colors = options.colors;
}
 
 
if (options.plotOptions) {
merge(true, this.options.plotOptions, options.plotOptions);
}
 
// 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
@@ -19569,22 +20993,29 @@
// 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.
each(['xAxis', 'yAxis', 'series', 'colorAxis', 'pane'], function(coll) {
each([
'xAxis',
'yAxis',
'zAxis',
'series',
'colorAxis',
'pane'
], function(coll) {
if (options[coll]) {
each(splat(options[coll]), function(newOptions, i) {
var item = (
defined(newOptions.id) &&
this.get(newOptions.id)
) || this[coll][i];
chart.get(newOptions.id)
) || chart[coll][i];
if (item && item.coll === coll) {
item.update(newOptions, false);
}
}, this);
});
}
}, this);
});
 
if (updateAllAxes) {
each(this.axes, function(axis) {
each(chart.axes, function(axis) {
axis.update({}, false);
});
}
@@ -19592,7 +21023,7 @@
// Certain options require the whole series structure to be thrown away
// and rebuilt
if (updateAllSeries) {
each(this.series, function(series) {
each(chart.series, function(series) {
series.update({}, false);
});
}
@@ -19599,17 +21030,17 @@
 
// For loading, just update the options, do not redraw
if (options.loading) {
merge(true, this.options.loading, options.loading);
merge(true, chart.options.loading, options.loading);
}
 
// Update size. Redraw is forced.
newWidth = optionsChart && optionsChart.width;
newHeight = optionsChart && optionsChart.height;
if ((isNumber(newWidth) && newWidth !== this.chartWidth) ||
(isNumber(newHeight) && newHeight !== this.chartHeight)) {
this.setSize(newWidth, newHeight);
if ((isNumber(newWidth) && newWidth !== chart.chartWidth) ||
(isNumber(newHeight) && newHeight !== chart.chartHeight)) {
chart.setSize(newWidth, newHeight);
} else if (pick(redraw, true)) {
this.redraw();
chart.redraw();
}
},
 
@@ -19624,14 +21055,32 @@
});
 
// extend the Point prototype for dynamic methods
extend(Point.prototype, /** @lends Point.prototype */ {
extend(Point.prototype, /** @lends Highcharts.Point.prototype */ {
/**
* Point.update with new options (typically x/y data) and optionally redraw the series.
* Update point with new options (typically x/y data) and optionally redraw
* the series.
*
* @param {Object} options Point options as defined in the series.data array
* @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call
* @param {Boolean|Object} animation Whether to apply animation, and optionally animation
* configuration
* @param {Object} 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 main 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 {AnimationOptions} [animation=true]
* Whether to apply animation, and optionally animation
* configuration.
*
* @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
*/
update: function(options, redraw, animation, runEvent) {
var point = this,
@@ -19703,9 +21152,21 @@
 
/**
* Remove a point and optionally redraw the series and if necessary the axes
* @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call
* @param {Boolean|Object} animation Whether to apply animation, and optionally animation
* configuration
* @param {Boolean} redraw
* 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 {AnimationOptions} [animation=false]
* Whether to apply animation, and optionally animation
* configuration.
*
* @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
*/
remove: function(redraw, animation) {
this.series.removePoint(inArray(this, this.series.data), redraw, animation);
@@ -19715,13 +21176,43 @@
// Extend the series prototype for dynamic methods
extend(Series.prototype, /** @lends Series.prototype */ {
/**
* Add a point dynamically after chart load time
* @param {Object} options Point options as given in series.data
* @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call
* @param {Boolean} shift If shift is true, a point is shifted off the start
* of the series as one is appended to the end.
* @param {Boolean|AnimationOptions} animation Whether to apply animation, and optionally animation
* configuration
* 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.
*
* @param {Number|Array|Object} 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 {AnimationOptions} [animation]
* Whether to apply animation, and optionally animation
* configuration.
*
* @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 Highstock
* @sample stock/members/series-addpoint-shift/
* Append and shift in Highstock
* @sample maps/members/series-addpoint/
* Add a point in Highmaps
*/
addPoint: function(options, redraw, shift, animation) {
var series = this,
@@ -19797,7 +21288,24 @@
},
 
/**
* Remove a point (rendered or not), by index
* Remove a point from the series. Unlike the {@link Highcharts.Point#remove}
* method, this can also be done on a point that is not instanciated because
* it is outside the view or subject to Highstock data grouping.
*
* @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 {AnimationOptions} [animation]
* Whether and optionally how the series should be animated.
*
* @sample highcharts/members/series-removepoint/
* Remove cropped point
*/
removePoint: function(i, redraw, animation) {
 
@@ -19841,11 +21349,19 @@
},
 
/**
* Remove a series and optionally redraw the chart
* Remove a series and optionally redraw the chart.
*
* @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call
* @param {Boolean|Object} animation Whether to apply animation, and optionally animation
* configuration
* @param {Boolean} [redraw=true]
* Whether to redraw the chart or wait for an explicit call to
* {@link Highcharts.Chart#redraw}.
* @param {AnimationOptions} [animation]
* Whether to apply animation, and optionally animation
* configuration
* @param {Boolean} [withEvent=true]
* Used internally, whether to fire the series `remove` event.
*
* @sample highcharts/members/series-remove/
* Remove first series from a button
*/
remove: function(redraw, animation, withEvent) {
var series = this,
@@ -19874,20 +21390,45 @@
},
 
/**
* Series.update with a new set of options
* 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 initiated from scratch. Therefore, this method is more
* performance expensive than some other utility methods like {@link
* Series#setData} or {@link Series#setVisible}.
*
* @param {SeriesOptions} 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.
*
* @sample highcharts/members/series-update/
* Updating series options
* @sample maps/members/series-update/
* Update series options in Highmaps
*/
update: function(newOptions, redraw) {
var series = this,
chart = this.chart,
// must use user options when changing type because this.options is merged
// in with type specific plotOptions
oldOptions = this.userOptions,
oldType = this.oldType || this.type,
chart = series.chart,
// must use user options when changing type because series.options
// is merged in with type specific plotOptions
oldOptions = series.userOptions,
oldType = series.oldType || series.type,
newType = newOptions.type || oldOptions.type || chart.options.chart.type,
proto = seriesTypes[oldType].prototype,
preserve = ['group', 'markerGroup', 'dataLabelsGroup'],
n;
 
// Running Series.update to update the data only is an intuitive usage,
// so we want to make sure that when used like this, we run the
// cheaper setData function and allow animation instead of completely
// recreating the series instance.
if (Object.keys && Object.keys(newOptions).toString() === 'data') {
return this.setData(newOptions.data, redraw);
}
 
// If we're changing type or zIndex, create new groups (#3380, #3404)
if ((newType && newType !== oldType) || newOptions.zIndex !== undefined) {
preserve.length = 0;
@@ -19902,19 +21443,19 @@
// Do the merge, with some forced options
newOptions = merge(oldOptions, {
animation: false,
index: this.index,
pointStart: this.xData[0] // when updating after addPoint
index: series.index,
pointStart: series.xData[0] // when updating after addPoint
}, {
data: this.options.data
data: series.options.data
}, newOptions);
 
// Destroy the series and delete all properties. Reinsert all methods
// and properties from the new type prototype (#2270, #3719)
this.remove(false, null, false);
series.remove(false, null, false);
for (n in proto) {
this[n] = undefined;
series[n] = undefined;
}
extend(this, seriesTypes[newType || oldType].prototype);
extend(series, seriesTypes[newType || oldType].prototype);
 
// Re-register groups (#3094)
each(preserve, function(prop) {
@@ -19921,9 +21462,9 @@
series[prop] = preserve[prop];
});
 
this.init(chart, newOptions);
this.oldType = oldType;
chart.linkSeries(); // Links are lost in this.remove (#3028)
series.init(chart, newOptions);
series.oldType = oldType;
chart.linkSeries(); // Links are lost in series.remove (#3028)
if (pick(redraw, true)) {
chart.redraw(false);
}
@@ -19931,19 +21472,27 @@
});
 
// Extend the Axis.prototype for dynamic methods
extend(Axis.prototype, /** @lends Axis.prototype */ {
extend(Axis.prototype, /** @lends Highcharts.Axis.prototype */ {
 
/**
* Axis.update with a new options structure
* 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.
*
* @param {Object} options
* The new options that will be merged in with existing options on
* the axis.
* @sample highcharts/members/axis-update/ Axis update demo
*/
update: function(newOptions, redraw) {
update: function(options, redraw) {
var chart = this.chart;
 
newOptions = chart.options[this.coll][this.options.index] = merge(this.userOptions, newOptions);
options = chart.options[this.coll][this.options.index] =
merge(this.userOptions, options);
 
this.destroy(true);
 
this.init(chart, extend(newOptions, {
this.init(chart, extend(options, {
events: undefined
}));
 
@@ -19954,7 +21503,12 @@
},
 
/**
* Remove the axis from the chart
* Remove the axis from the chart.
*
* @param {Boolean} [redraw=true] Whether to redraw the chart following the
* remove.
*
* @sample highcharts/members/chart-addaxis/ Add and remove axes
*/
remove: function(redraw) {
var chart = this.chart,
@@ -19972,7 +21526,13 @@
// Remove the axis
erase(chart.axes, this);
erase(chart[key], this);
chart.options[key].splice(this.options.index, 1);
 
if (isArray(chart.options[key])) {
chart.options[key].splice(this.options.index, 1);
} else { // color axis, #6488
delete chart.options[key];
}
 
each(chart[key], function(axis, i) { // Re-index, #1706
axis.options.index = i;
});
@@ -19985,18 +21545,26 @@
},
 
/**
* Update the axis title by options
* Update the axis title by options after render time.
*
* @param {TitleOptions} titleOptions
* The additional title options.
* @param {Boolean} [redraw=true]
* Whether to redraw the chart after setting the title.
* @sample highcharts/members/axis-settitle/ Set a new Y axis title
*/
setTitle: function(newTitleOptions, redraw) {
setTitle: function(titleOptions, redraw) {
this.update({
title: newTitleOptions
title: titleOptions
}, redraw);
},
 
/**
* Set new axis categories and optionally redraw
* @param {Array} categories
* @param {Boolean} redraw
* Set new axis categories and optionally redraw.
* @param {Array.<String>} categories - The new categories.
* @param {Boolean} [redraw=true] - Whether to redraw the chart.
* @sample highcharts/members/axis-setcategories/ Set categories by click on
* a button
*/
setCategories: function(categories, redraw) {
this.update({
@@ -20053,8 +21621,7 @@
seriesLength = yAxisSeries.length,
visibleSeries,
upOrDown = pick(yAxis.options.reversedStacks, true) ? 1 : -1,
i,
x;
i;
 
if (this.options.stacking) {
// Create a map where we can quickly look up the points by their X value.
@@ -20063,11 +21630,11 @@
}
 
// Sort the keys (#1651)
for (x in stack) {
if (stack[x].total !== null) { // nulled after switching between grouping and not (#1651, #2336)
H.objectEach(stack, function(stackX, x) {
if (stackX.total !== null) { // nulled after switching between grouping and not (#1651, #2336)
keys.push(x);
}
}
});
keys.sort(function(a, b) {
return a - b;
});
@@ -20624,8 +22191,15 @@
var otherOptions = otherSeries.options,
otherYAxis = otherSeries.yAxis,
columnIndex;
if (otherSeries.type === series.type && otherSeries.visible &&
yAxis.len === otherYAxis.len && yAxis.pos === otherYAxis.pos) { // #642, #2086
if (
otherSeries.type === series.type &&
(
otherSeries.visible ||
!series.chart.options.chart.ignoreHiddenSeries
) &&
yAxis.len === otherYAxis.len &&
yAxis.pos === otherYAxis.pos
) { // #642, #2086
if (otherOptions.stacking) {
stackKey = otherSeries.stackKey;
if (stackGroups[stackKey] === undefined) {
@@ -20777,8 +22351,10 @@
point.shapeType = 'rect';
point.shapeArgs = series.crispCol.apply(
series,
point.isNull ? [point.plotX, yAxis.len / 2, 0, 0] : // #3169, drilldown from null must have a position to work from
[barX, barY, barW, barH]
point.isNull ?
// #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
[barX, translatedThreshold, barW, 0] : [barX, barY, barW, barH]
);
});
 
@@ -20822,7 +22398,7 @@
// Handle zone colors
if (point && this.zones.length) {
zone = point.getZone();
fill = (zone && zone.color) || point.options.color || this.color; // When zones are present, don't use point.color (#4267)
fill = point.options.color || (zone && zone.color) || this.color; // When zones are present, don't use point.color (#4267). Changed order (#6527)
}
 
// Select or hover states
@@ -21112,7 +22688,7 @@
distance: 30,
enabled: true,
formatter: function() { // #2945
return this.y === null ? undefined : this.point.name;
return this.point.isNull ? undefined : this.point.name;
},
// softConnector: true,
x: 0
@@ -21197,11 +22773,9 @@
// Get the total sum
for (i = 0; i < len; i++) {
point = points[i];
// Disallow negative values (#1530, #3623, #5322)
if (point.y < 0) {
point.y = null;
}
total += (ignoreHiddenPoint && !point.visible) ? 0 : point.y;
total += (ignoreHiddenPoint && !point.visible) ?
0 :
point.isNull ? 0 : point.y;
}
this.total = total;
 
@@ -21233,6 +22807,7 @@
options = series.options,
slicedOffset = options.slicedOffset,
connectorOffset = slicedOffset + (options.borderWidth || 0),
finalConnectorOffset,
start,
end,
angle,
@@ -21256,14 +22831,14 @@
series.center = positions = series.getCenter();
}
 
// utility for getting the x value from a given y, used for anticollision logic in data labels
series.getX = function(y, left) {
 
angle = Math.asin(Math.min((y - positions[1]) / (positions[2] / 2 + labelDistance), 1));
 
// Utility for getting the x value from a given y, used for anticollision
// logic in data labels.
// Added point for using specific points' label distance.
series.getX = function(y, left, point) {
angle = Math.asin(Math.min((y - positions[1]) / (positions[2] / 2 + point.labelDistance), 1));
return positions[0] +
(left ? -1 : 1) *
(Math.cos(angle) * (positions[2] / 2 + labelDistance));
(Math.cos(angle) * (positions[2] / 2 + point.labelDistance));
};
 
// Calculate the geometry for each point
@@ -21271,6 +22846,15 @@
 
point = points[i];
 
// Used for distance calculation for specific point.
point.labelDistance = pick(
point.options.dataLabels && point.options.dataLabels.distance,
labelDistance
);
 
// Saved for later dataLabels distance calculation.
series.maxLabelDistance = Math.max(series.maxLabelDistance || 0, point.labelDistance);
 
// set start and end angle
start = startAngleRad + (cumulative * circ);
if (!ignoreHiddenPoint || point.visible) {
@@ -21314,16 +22898,18 @@
point.half = angle < -Math.PI / 2 || angle > Math.PI / 2 ? 1 : 0;
point.angle = angle;
 
// set the anchor point for data labels
connectorOffset = Math.min(connectorOffset, labelDistance / 5); // #1678
// Set the anchor point for data labels. Use point.labelDistance
// instead of labelDistance // #1174
// finalConnectorOffset - not override connectorOffset value.
finalConnectorOffset = Math.min(connectorOffset, point.labelDistance / 5); // #1678
point.labelPos = [
positions[0] + radiusX + Math.cos(angle) * labelDistance, // first break of connector
positions[1] + radiusY + Math.sin(angle) * labelDistance, // a/a
positions[0] + radiusX + Math.cos(angle) * connectorOffset, // second break, right outside pie
positions[1] + radiusY + Math.sin(angle) * connectorOffset, // a/a
positions[0] + radiusX + Math.cos(angle) * point.labelDistance, // first break of connector
positions[1] + radiusY + Math.sin(angle) * point.labelDistance, // a/a
positions[0] + radiusX + Math.cos(angle) * finalConnectorOffset, // second break, right outside pie
positions[1] + radiusY + Math.sin(angle) * finalConnectorOffset, // a/a
positions[0] + radiusX, // landing point for connector
positions[1] + radiusY, // a/a
labelDistance < 0 ? // alignment
point.labelDistance < 0 ? // alignment
'center' :
point.half ? 'right' : 'left', // alignment
angle // center angle
@@ -21358,7 +22944,7 @@
 
// draw the slices
each(series.points, function(point) {
if (point.y !== null) {
if (!point.isNull) {
graphic = point.graphic;
shapeArgs = point.shapeArgs;
 
@@ -21475,6 +23061,13 @@
},
 
/**
* Negative points are not valid (#1530, #3623, #5322)
*/
isValid: function() {
return H.isNumber(this.y, true) && this.y >= 0;
},
 
/**
* Toggle the visibility of the pie slice
* @param {Boolean} vis Whether to show the slice or not. If undefined, the
* visibility is toggled
@@ -21707,7 +23300,7 @@
hasRendered = series.hasRendered || 0,
str,
dataLabelsGroup,
defer = pick(options.defer, true),
defer = pick(options.defer, !!seriesOptions.animation),
renderer = series.chart.renderer;
 
if (options.enabled || series._hasPointLabels) {
@@ -21750,7 +23343,6 @@
dataLabel = point.dataLabel,
labelConfig,
attr,
name,
rotation,
connector = point.connector,
isNew = !dataLabel,
@@ -21776,8 +23368,8 @@
// Get automated contrast color
if (style.color === 'contrast') {
point.contrastColor = renderer.getContrast(point.color || series.color);
style.color = options.inside || options.distance < 0 || !!seriesOptions.stacking ?
point.contrastColor : '#000000';
style.color = options.inside || pick(point.labelDistance, options.distance) < 0 ||
!!seriesOptions.stacking ? point.contrastColor : '#000000';
}
if (seriesOptions.cursor) {
style.cursor = seriesOptions.cursor;
@@ -21798,11 +23390,11 @@
};
 
// Remove unused attributes (#947)
for (name in attr) {
if (attr[name] === undefined) {
H.objectEach(attr, function(val, name) {
if (val === undefined) {
delete attr[name];
}
}
});
}
// If the point is outside the plot area, destroy it. #678, #820
if (dataLabel && (!enabled || !defined(str))) {
@@ -21959,9 +23551,9 @@
 
// When we're using a shape, make it possible with a connector or an arrow pointing to thie point
if (options.shape && !rotation) {
dataLabel.attr({
anchorX: point.plotX,
anchorY: point.plotY
dataLabel[isNew ? 'attr' : 'animate']({
anchorX: inverted ? chart.plotWidth - point.plotY : point.plotX,
anchorY: inverted ? chart.plotHeight - point.plotX : point.plotY
});
}
}
@@ -22055,11 +23647,9 @@
plotWidth = chart.plotWidth,
plotHeight = chart.plotHeight,
connector,
distanceOption = options.distance,
seriesCenter = series.center,
radius = seriesCenter[2] / 2,
centerY = seriesCenter[1],
outside = distanceOption > 0,
dataLabel,
dataLabelWidth,
labelPos,
@@ -22116,9 +23706,10 @@
var top,
bottom,
length = points.length,
positions,
positions = [],
naturalY,
sideOverflow,
positionsIndex, // Point index in positions array.
size;
 
if (!length) {
@@ -22127,39 +23718,61 @@
 
// Sort by angle
series.sortByAngle(points, i - 0.5);
// Only do anti-collision when we have dataLabels outside the pie
// and have connectors. (#856)
if (series.maxLabelDistance > 0) {
top = Math.max(
0,
centerY - radius - series.maxLabelDistance
);
bottom = Math.min(
centerY + radius + series.maxLabelDistance,
chart.plotHeight
);
each(points, function(point) {
// check if specific points' label is outside the pie
if (point.labelDistance > 0 && point.dataLabel) {
// point.top depends on point.labelDistance value
// Used for calculation of y value in getX method
point.top = Math.max(
0,
centerY - radius - point.labelDistance
);
point.bottom = Math.min(
centerY + radius + point.labelDistance,
chart.plotHeight
);
size = point.dataLabel.getBBox().height || 21;
 
// Only do anti-collision when we are outside the pie and have connectors (#856)
if (distanceOption > 0) {
top = Math.max(0, centerY - radius - distanceOption);
bottom = Math.min(centerY + radius + distanceOption, chart.plotHeight);
positions = map(points, function(point) {
if (point.dataLabel) {
size = point.dataLabel.getBBox().height || 21;
return {
target: point.labelPos[1] - top + size / 2,
// point.positionsIndex is needed for getting index of
// parameter related to specific point inside positions
// array - not every point is in positions array.
point.positionsIndex = positions.push({
target: point.labelPos[1] - point.top + size / 2,
size: size,
rank: point.y
};
}) - 1;
}
});
H.distribute(positions, bottom + size - top);
}
 
// now the used slots are sorted, fill them up sequentially
// Now the used slots are sorted, fill them up sequentially
for (j = 0; j < length; j++) {
 
point = points[j];
positionsIndex = point.positionsIndex;
labelPos = point.labelPos;
dataLabel = point.dataLabel;
visibility = point.visible === false ? 'hidden' : 'inherit';
naturalY = labelPos[1];
 
if (positions) {
if (positions[j].pos === undefined) {
if (positions && defined(positions[positionsIndex])) {
if (positions[positionsIndex].pos === undefined) {
visibility = 'hidden';
} else {
labelHeight = positions[j].size;
y = top + positions[j].pos;
labelHeight = positions[positionsIndex].size;
y = point.top + positions[positionsIndex].pos;
}
 
} else {
@@ -22166,12 +23779,18 @@
y = naturalY;
}
 
// 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
// It is needed to delete point.positionIndex for
// dynamically added points etc.
 
delete point.positionIndex;
 
// 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
if (options.justify) {
x = seriesCenter[0] + (i ? -1 : 1) * (radius + distanceOption);
x = seriesCenter[0] + (i ? -1 : 1) * (radius + point.labelDistance);
} else {
x = series.getX(y < top + 2 || y > bottom - 2 ? naturalY : y, i);
x = series.getX(y < point.top + 2 || y > point.bottom - 2 ? naturalY : y, i, point);
}
 
 
@@ -22193,41 +23812,39 @@
 
 
// Detect overflowing data labels
if (series.options.size === null) {
dataLabelWidth = dataLabel.getBBox().width;
dataLabelWidth = dataLabel.getBBox().width;
 
sideOverflow = null;
// Overflow left
if (x - dataLabelWidth < connectorPadding) {
sideOverflow = Math.round(
dataLabelWidth - x + connectorPadding
);
overflow[3] = Math.max(sideOverflow, overflow[3]);
sideOverflow = null;
// Overflow left
if (x - dataLabelWidth < connectorPadding) {
sideOverflow = Math.round(
dataLabelWidth - x + connectorPadding
);
overflow[3] = Math.max(sideOverflow, overflow[3]);
 
// Overflow right
} else if (x + dataLabelWidth > plotWidth - connectorPadding) {
sideOverflow = Math.round(
x + dataLabelWidth - plotWidth + connectorPadding
);
overflow[1] = Math.max(sideOverflow, overflow[1]);
}
// Overflow right
} else if (x + dataLabelWidth > plotWidth - connectorPadding) {
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 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]
);
}
dataLabel.sideOverflow = sideOverflow;
// Overflow left
} else if (y + labelHeight / 2 > plotHeight) {
overflow[2] = Math.max(
Math.round(y + labelHeight / 2 - plotHeight),
overflow[2]
);
}
dataLabel.sideOverflow = sideOverflow;
} // for each point
}); // for each half
 
@@ -22239,7 +23856,7 @@
this.placeDataLabels();
 
// Draw the connectors
if (outside && connectorWidth) {
if (connectorWidth) {
each(this.points, function(point) {
var isNew;
 
@@ -22246,7 +23863,12 @@
connector = point.connector;
dataLabel = point.dataLabel;
 
if (dataLabel && dataLabel._pos && point.visible) {
if (
dataLabel &&
dataLabel._pos &&
point.visible &&
point.labelDistance > 0
) {
visibility = dataLabel._attr.visibility;
 
isNew = !connector;
@@ -22352,47 +23974,62 @@
centerOption = options.center,
minSize = options.minSize || 80,
newSize = minSize,
ret;
// If a size is set, return true and don't try to shrink the pie
// to fit the labels.
ret = options.size !== null;
 
// Handle horizontal size and center
if (centerOption[0] !== null) { // Fixed center
newSize = Math.max(center[2] - Math.max(overflow[1], overflow[3]), minSize);
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(
center[2] - overflow[1] - overflow[3], // horizontal overflow
minSize
);
center[0] += (overflow[3] - overflow[1]) / 2; // horizontal center
}
} 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 = Math.max(Math.min(newSize, center[2] - Math.max(overflow[0], overflow[2])), minSize);
// Handle vertical size and center
if (centerOption[1] !== null) { // Fixed center
newSize = Math.max(Math.min(newSize, center[2] -
Math.max(overflow[0], overflow[2])), minSize);
 
} else { // Auto center
newSize = Math.max(
Math.min(
newSize,
center[2] - overflow[0] - overflow[2] // vertical overflow
),
minSize
);
center[1] += (overflow[0] - overflow[2]) / 2; // vertical center
}
} else { // Auto center
newSize = Math.max(
Math.min(
newSize,
// vertical overflow
center[2] - overflow[0] - overflow[2]
),
minSize
);
// 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(relativeLength(options.innerSize || 0, newSize), newSize); // #3632
this.translate(center);
// 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
relativeLength(options.innerSize || 0, newSize),
newSize
);
this.translate(center);
 
if (this.drawDataLabels) {
this.drawDataLabels();
if (this.drawDataLabels) {
this.drawDataLabels();
}
// Else, return true to indicate that the pie and its labels is
// within the plot area
} else {
ret = true;
}
// Else, return true to indicate that the pie and its labels is within the plot area
} else {
ret = true;
}
return ret;
};
@@ -22683,9 +24320,9 @@
pointer = chart.pointer,
onMouseOver = function(e) {
var point = pointer.getPointFromEvent(e);
 
// undefined on graph in scatterchart
if (point !== undefined) {
pointer.isDirectTouch = true;
point.onMouseOver(e);
}
};
@@ -22876,7 +24513,7 @@
})
.on('mouseout', function() {
 
legendItem.css(item.visible ? legend.itemStyle : legend.itemHiddenStyle);
legendItem.css(merge(item.visible ? legend.itemStyle : legend.itemHiddenStyle));
 
 
// A CSS class to dim or hide other than the hovered series
@@ -23062,18 +24699,35 @@
flipped = panMax < panMin,
newMin = flipped ? panMax : panMin,
newMax = flipped ? panMin : panMax,
paddedMin = axis.toValue(
axis.toPixels(extremes.min) - axis.minPixelPadding
paddedMin = Math.min(
extremes.dataMin,
axis.toValue(
axis.toPixels(extremes.min) - axis.minPixelPadding
)
),
paddedMax = axis.toValue(
axis.toPixels(extremes.max) + axis.minPixelPadding
paddedMax = Math.max(
extremes.dataMax,
axis.toValue(
axis.toPixels(extremes.max) + axis.minPixelPadding
)
),
distMin = Math.min(extremes.dataMin, paddedMin) - newMin,
distMax = newMax - Math.max(extremes.dataMax, paddedMax);
spill;
 
// Negative distMin and distMax means that we're still inside the
// data range.
if (axis.series.length && distMin < 0 && distMax < 0) {
// If the new range spills over, either to the min or max, adjust
// the new range.
spill = paddedMin - newMin;
if (spill > 0) {
newMax += spill;
newMin = paddedMin;
}
spill = newMax - paddedMax;
if (spill > 0) {
newMax = paddedMax;
newMin -= spill;
}
 
// Set new extremes if they are actually new
if (axis.series.length && newMin !== extremes.min && newMax !== extremes.max) {
axis.setExtremes(
newMin,
newMax,
@@ -23100,12 +24754,28 @@
/*
* Extend the Point object with interaction
*/
extend(Point.prototype, /** @lends Point.prototype */ {
extend(Point.prototype, /** @lends Highcharts.Point.prototype */ {
/**
* Toggle the selection status of a point
* @param {Boolean} selected Whether to select or unselect the point.
* @param {Boolean} accumulate Whether to add to the previous selection. By default,
* this happens if the control key (Cmd on Mac) was pressed during clicking.
* Toggle the selection status of a point.
* @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 {@link http://api.highcharts.com/highcharts/plotOptions.series.allowPointSelect|allowPointSelect}
* is `true`, selected points are accumulated on Control, Shift or
* Cmd clicking the point.
*
* @see Highcharts.Chart#getSelectedPoints
*
* @sample highcharts/members/point-select/
* Select a point from a button
* @sample highcharts/chart/events-selection-points/
* Select a range of points through a drag selection
* @sample maps/series/data-id/
* Select a point in Highmaps
*/
select: function(selected, accumulate) {
var point = this,
@@ -23118,6 +24788,14 @@
point.firePointEvent(selected ? 'select' : 'unselect', {
accumulate: accumulate
}, function() {
 
/**
* Whether the point is selected or not.
* @see Highcharts.Point#select
* @memberof Highcharts.Point
* @name selected
* @type {Boolean}
*/
point.selected = point.options.selected = selected;
series.options.data[inArray(point, series.data)] = point.options;
 
@@ -23147,7 +24825,10 @@
series = point.series,
chart = series.chart,
pointer = chart.pointer;
point.firePointEvent('mouseOver');
e = e ?
pointer.normalize(e) :
// In cases where onMouseOver is called directly without an event
pointer.getChartCoordinatesFromPoint(point, chart.inverted);
pointer.runPointActions(e, point);
},
 
@@ -23172,14 +24853,13 @@
if (!this.hasImportedEvents) {
var point = this,
options = merge(point.series.options.point, point.options),
events = options.events,
eventType;
events = options.events;
 
point.events = events;
 
for (eventType in events) {
addEvent(point, eventType, events[eventType]);
}
H.objectEach(events, function(event, eventType) {
addEvent(point, eventType, event);
});
this.hasImportedEvents = true;
 
}
@@ -23320,8 +25000,8 @@
if (haloOptions && haloOptions.size) {
if (!halo) {
series.halo = halo = chart.renderer.path()
// #5818, #5903
.add(hasMarkers ? series.markerGroup : series.group);
// #5818, #5903, #6705
.add((point.graphic || stateMarkerGraphic).parentGroup);
}
halo[move ? 'animate' : 'attr']({
d: point.haloPath(haloOptions.size)
@@ -23372,7 +25052,7 @@
* Extend the Series object with interaction
*/
 
extend(Series.prototype, /** @lends Series.prototype */ {
extend(Series.prototype, /** @lends Highcharts.Series.prototype */ {
/**
* Series mouse over handler
*/
@@ -23500,10 +25180,15 @@
},
 
/**
* Set the visibility of the graph
* Show or hide the series.
*
* @param vis {Boolean} True to show the series, false to hide. If undefined,
* the visibility is toggled.
* @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.
*/
setVisible: function(vis, redraw) {
var series = this,
@@ -23563,7 +25248,10 @@
},
 
/**
* Show the graph
* Show the series if hidden.
*
* @sample highcharts/members/series-hide/
* Toggle visibility from a button
*/
show: function() {
this.setVisible(true);
@@ -23570,7 +25258,13 @@
},
 
/**
* Hide the graph
* Hide the series if visible. If the {@link
* https://api.highcharts.com/highcharts/chart.ignoreHiddenSeries|
* chart.ignoreHiddenSeries} option is true, the chart is redrawn without
* this series.
*
* @sample highcharts/members/series-hide/
* Toggle visibility from a button
*/
hide: function() {
this.setVisible(false);
@@ -23578,16 +25272,25 @@
 
 
/**
* Set the selected state of the graph
* 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.
*
* @param selected {Boolean} True to select the series, false to unselect. If
* undefined, the selection state is toggled.
* @param {Boolean} [selected]
* True to select the series, false to unselect. If undefined, the
* selection state is toggled.
*
* @sample highcharts/members/series-select/
* Select a series from a button
*/
select: function(selected) {
var series = this;
// if called without an argument, toggle
series.selected = selected = (selected === undefined) ? !series.selected : selected;
 
series.selected = selected = (selected === undefined) ?
!series.selected :
selected;
 
if (series.checkbox) {
series.checkbox.checked = selected;
}
@@ -23702,8 +25405,8 @@
* and store the current values in the ret object.
*/
function getCurrent(options, curr, ret, depth) {
var key, i;
for (key in options) {
var i;
H.objectEach(options, function(val, key) {
if (!depth && inArray(key, ['series', 'xAxis', 'yAxis']) > -1) {
options[key] = splat(options[key]);
 
@@ -23715,7 +25418,7 @@
if (curr[key][i]) { // Item exists in current data (#6347)
ret[key][i] = {};
getCurrent(
options[key][i],
val[i],
curr[key][i],
ret[key][i],
depth + 1
@@ -23722,18 +25425,13 @@
);
}
}
} else if (isObject(options[key])) {
ret[key] = isArray(options[key]) ? [] : {};
getCurrent(
options[key],
curr[key] || {},
ret[key],
depth + 1
);
} else if (isObject(val)) {
ret[key] = isArray(val) ? [] : {};
getCurrent(val, curr[key] || {}, ret[key], depth + 1);
} else {
ret[key] = curr[key] || null;
}
}
});
}
 
getCurrent(options, this.options, ret, 0);
@@ -24418,37 +26116,6 @@
}
});
 
 
 
/**
* Extend getGraphPath by identifying gaps in the ordinal data so that we can draw a gap in the
* line or area
*/
Series.prototype.gappedPath = function() {
var gapSize = this.options.gapSize,
points = this.points.slice(),
i = points.length - 1;
 
if (gapSize && i > 0) { // #5008
 
// extension for ordinal breaks
while (i--) {
if (points[i + 1].x - points[i].x > this.closestPointRange * gapSize) {
points.splice( // insert after this one
i + 1,
0, {
isNull: true
}
);
}
}
}
 
// Call base method
//return proceed.call(this, points, a, b);
return this.getGraphPath(points);
};
 
/* ****************************************************************************
* End ordinal axis logic *
*****************************************************************************/
@@ -24610,17 +26277,14 @@
length = 0,
inBrk,
repeat,
brk,
min = axis.userMin || axis.min,
max = axis.userMax || axis.max,
pointRangePadding = pick(axis.pointRangePadding, 0),
start,
i,
j;
i;
 
// Min & max check (#4247)
for (i in breaks) {
brk = breaks[i];
each(breaks, function(brk) {
repeat = brk.repeat || Infinity;
if (axis.isInBreak(brk, min)) {
min += (brk.to % repeat) - (min % repeat);
@@ -24628,11 +26292,10 @@
if (axis.isInBreak(brk, max)) {
max -= (max % repeat) - (brk.from % repeat);
}
}
});
 
// Construct an array holding all breaks in the axis
for (i in breaks) {
brk = breaks[i];
each(breaks, function(brk) {
start = brk.from;
repeat = brk.repeat || Infinity;
 
@@ -24643,18 +26306,18 @@
start += repeat;
}
 
for (j = start; j < max; j += repeat) {
for (i = start; i < max; i += repeat) {
breakArrayT.push({
value: j,
value: i,
move: 'in'
});
breakArrayT.push({
value: j + (brk.to - brk.from),
value: i + (brk.to - brk.from),
move: 'out',
size: brk.breakSize
});
}
}
});
 
breakArrayT.sort(function(a, b) {
var ret;
@@ -24670,8 +26333,7 @@
inBrk = 0;
start = min;
 
for (i in breakArrayT) {
brk = breakArrayT[i];
each(breakArrayT, function(brk) {
inBrk += (brk.move === 'in' ? 1 : -1);
 
if (inBrk === 1 && brk.move === 'in') {
@@ -24685,7 +26347,7 @@
});
length += brk.value - start - (brk.size || 0);
}
}
});
 
axis.breakArray = breakArray;
 
@@ -24697,7 +26359,7 @@
 
if (axis.options.staticScale) {
axis.transA = axis.options.staticScale;
} else {
} else if (axis.unitLength) {
axis.transA *= (max - axis.min + pointRangePadding) /
axis.unitLength;
}
@@ -24784,6 +26446,36 @@
});
};
 
 
/**
* 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.
*/
H.Series.prototype.gappedPath = function() {
var gapSize = this.options.gapSize,
points = this.points.slice(),
i = points.length - 1;
 
if (gapSize && i > 0) { // #5008
 
// extension for ordinal breaks
while (i--) {
if (points[i + 1].x - points[i].x > this.closestPointRange * gapSize) {
points.splice( // insert after this one
i + 1,
0, {
isNull: true
}
);
}
}
}
 
// Call base method
return this.getGraphPath(points);
};
 
wrap(H.seriesTypes.column.prototype, 'drawPoints', drawPointsWrapped);
wrap(H.Series.prototype, 'drawPoints', drawPointsWrapped);
 
@@ -24904,9 +26596,10 @@
 
 
/**
* 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
* 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.
*/
approximations = {
@@ -24933,8 +26626,8 @@
var len = arr.length,
ret = approximations.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 we have a number, return it divided by the length. If not,
// return null or undefined based on what the sum method finds.
if (isNumber(ret) && len) {
ret = ret / len;
}
@@ -24941,6 +26634,17 @@
 
return ret;
},
// The same as average, but for series with multiple values, like area
// ranges.
averages: function() { // #5479
var ret = [];
 
each(arguments, function(arr) {
ret.push(approximations.average(arr));
});
 
return ret;
},
open: function(arr) {
return arr.length ? arr[0] : (arr.hasNulls ? null : undefined);
},
@@ -24971,6 +26675,8 @@
 
if (isNumber(low) || isNumber(high)) {
return [low, high];
} else if (low === null && high === null) {
return null;
}
// else, return is undefined
}
@@ -24978,8 +26684,8 @@
 
 
/**
* 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.
* 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.
*/
seriesProto.groupData = function(xData, yData, groupPositions, approximation) {
var series = this,
@@ -24992,20 +26698,36 @@
pointX,
pointY,
groupedY,
handleYData = !!yData, // when grouping the fake extended axis for panning, we don't need to consider y
values = [
[],
[],
[],
[]
],
approximationFn = typeof approximation === 'function' ? approximation : approximations[approximation],
// when grouping the fake extended axis for panning,
// we don't need to consider y
handleYData = !!yData,
values = [],
approximationFn = typeof approximation === 'function' ?
approximation :
approximations[approximation] ||
// if the approximation is not found use default series type
// approximation (#2914)
(
specificOptions[series.type] &&
approximations[specificOptions[series.type].approximation]
) || approximations[commonOptions.approximation],
pointArrayMap = series.pointArrayMap,
pointArrayMapLength = pointArrayMap && pointArrayMap.length,
i,
pos = 0,
start = 0;
start = 0,
valuesLen,
i, j;
 
// Calculate values array size from pointArrayMap length
if (pointArrayMapLength) {
each(pointArrayMap, function() {
values.push([]);
});
} else {
values.push([]);
}
valuesLen = pointArrayMapLength || 1;
 
// Start with the first point within the X axis range (#2696)
for (i = 0; i <= dataLength; i++) {
if (xData[i] >= groupPositions[0]) {
@@ -25015,9 +26737,12 @@
 
for (i; i <= dataLength; i++) {
 
// when a new group is entered, summarize and initiate the previous group
while ((groupPositions[pos + 1] !== undefined && xData[i] >= groupPositions[pos + 1]) ||
i === dataLength) { // get the last group
// when a new group is entered, summarize and initiate
// the previous group
while ((
groupPositions[pos + 1] !== undefined &&
xData[i] >= groupPositions[pos + 1]
) || i === dataLength) { // get the last group
 
// get group x and y
pointX = groupPositions[pos];
@@ -25036,10 +26761,10 @@
 
// reset the aggregate arrays
start = i;
values[0] = [];
values[1] = [];
values[2] = [];
values[3] = [];
for (j = 0; j < valuesLen; j++) {
values[j].length = 0; // faster than values[j] = []
values[j].hasNulls = false;
}
 
// Advance on the group positions
pos += 1;
@@ -25055,14 +26780,15 @@
break;
}
 
// for each raw data point, push it to an array that contains all values for this specific group
// for each raw data point, push it to an array that contains all values
// for this specific group
if (pointArrayMap) {
 
var index = series.cropStart + i,
point = (data && data[index]) || series.pointClass.prototype.applyOptions.apply({
point = (data && data[index]) ||
series.pointClass.prototype.applyOptions.apply({
series: series
}, [dataOptions[index]]),
j,
val;
 
for (j = 0; j < pointArrayMapLength; j++) {
@@ -25126,6 +26852,7 @@
hasGroupedData = true;
 
series.isDirty = true; // force recreation of point instances in series.translate, #5699
series.points = null; // #6709
 
var extremes = xAxis.getExtremes(),
xMin = extremes.min,
@@ -25382,7 +27109,17 @@
};
 
/**
* Force data grouping on all the axis' series.
* Highstock only. Force data grouping on all the axis' series.
*
* @param {SeriesDatagroupingOptions} [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
* @memberOf Axis.prototype
*/
Axis.prototype.setDataGrouping = function(dataGrouping, redraw) {
var i;
@@ -25469,7 +27206,7 @@
toYData: function(point) { // return a plain array for speedy calculation
return [point.open, point.high, point.low, point.close];
},
pointValKey: 'high',
pointValKey: 'close',
 
 
pointAttrToOptions: {
@@ -25522,6 +27259,10 @@
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;
});
},
 
@@ -25566,7 +27307,7 @@
'M',
crispX, Math.round(point.yBottom),
'L',
crispX, Math.round(point.plotY)
crispX, Math.round(point.plotHigh)
];
 
// open
@@ -25736,7 +27477,7 @@
topBox = Math.min(plotOpen, plotClose);
bottomBox = Math.max(plotOpen, plotClose);
halfWidth = Math.round(point.shapeArgs.width / 2);
hasTopWhisker = Math.round(topBox) !== Math.round(point.plotY);
hasTopWhisker = Math.round(topBox) !== Math.round(point.plotHigh);
hasBottomWhisker = bottomBox !== point.yBottom;
topBox = Math.round(topBox) + crispCorr;
bottomBox = Math.round(bottomBox) + crispCorr;
@@ -25759,7 +27500,7 @@
'M',
crispX, topBox,
'L',
crispX, hasTopWhisker ? Math.round(point.plotY) : topBox, // #460, #2094
crispX, hasTopWhisker ? Math.round(point.plotHigh) : topBox, // #460, #2094
'M',
crispX, bottomBox,
'L',
@@ -26783,7 +28524,7 @@
each(this._events, function(args) {
removeEvent.apply(null, args);
});
this._events = undefined;
this._events.length = 0;
},
 
/**
@@ -26855,6 +28596,7 @@
scrollMin = Math.min(pick(axis.options.min, axis.min), axis.min, axis.dataMin),
scrollMax = Math.max(pick(axis.options.max, axis.max), axis.max, axis.dataMax),
scrollbar = axis.scrollbar,
titleOffset = axis.titleOffset || 0,
offsetsIndex,
from,
to;
@@ -26867,7 +28609,10 @@
scrollbar.position(
axis.left,
axis.top + axis.height + 2 + axis.chart.scrollbarsOffsets[1] +
(axis.opposite ? 0 : axis.axisTitleMargin + axis.offset),
(axis.opposite ?
0 :
titleOffset + axis.axisTitleMargin + axis.offset
),
axis.width,
axis.height
);
@@ -26875,7 +28620,10 @@
} else {
scrollbar.position(
axis.left + axis.width + 2 + axis.chart.scrollbarsOffsets[0] +
(axis.opposite ? axis.axisTitleMargin + axis.offset : 0),
(axis.opposite ?
titleOffset + axis.axisTitleMargin + axis.offset :
0
),
axis.top,
axis.width,
axis.height
@@ -27305,7 +29053,7 @@
(index === 1 ? '-inside' : '-outside'))
 
.attr({
fill: hasMask ? navigatorOptions.maskFill : 'transparent'
fill: hasMask ? navigatorOptions.maskFill : 'rgba(0,0,0,0)'
})
.css(index === 1 && mouseCursor)
 
@@ -27376,6 +29124,7 @@
scrollbarHeight = navigator.scrollbarHeight,
navigatorSize,
xAxis = navigator.xAxis,
scrollbarXAxis = xAxis.fake ? chart.xAxis[0] : xAxis,
navigatorEnabled = navigator.navigatorEnabled,
zoomedMin,
zoomedMax,
@@ -27476,7 +29225,12 @@
if (inverted) {
scrollbarTop = navigator.top - scrollbarHeight;
scrollbarLeft = navigator.left - scrollbarHeight +
(navigatorEnabled ? 0 : navigator.height);
(navigatorEnabled || !scrollbarXAxis.opposite ? 0 :
// Multiple axes has offsets:
(scrollbarXAxis.titleOffset || 0) +
// Self margin from the axis.title
scrollbarXAxis.axisTitleMargin
);
scrollbarHeight = navigatorSize + 2 * scrollbarHeight;
} else {
scrollbarTop = navigator.top +
@@ -29253,15 +31007,15 @@
* Destroys allocated elements.
*/
destroy: function() {
var minInput = this.minInput,
maxInput = this.maxInput,
key;
var rSelector = this,
minInput = rSelector.minInput,
maxInput = rSelector.maxInput;
 
this.unMouseDown();
this.unResize();
rSelector.unMouseDown();
rSelector.unResize();
 
// Destroy elements in collections
destroyObjectProperties(this.buttons);
destroyObjectProperties(rSelector.buttons);
 
// Clear input element events
if (minInput) {
@@ -29272,18 +31026,18 @@
}
 
// Destroy HTML and SVG elements
for (key in this) {
if (this[key] && key !== 'chart') {
if (this[key].destroy) { // SVGElement
this[key].destroy();
} else if (this[key].nodeType) { // HTML element
H.objectEach(rSelector, function(val, key) {
if (val && key !== 'chart') {
if (val.destroy) { // SVGElement
val.destroy();
} else if (val.nodeType) { // HTML element
discardElement(this[key]);
}
}
if (this[key] !== RangeSelector.prototype[key]) {
this[key] = null;
if (val !== RangeSelector.prototype[key]) {
rSelector[key] = null;
}
}
}, this);
}
};
 
@@ -29337,8 +31091,15 @@
range,
// Get the true range from a start date
getTrueRange = function(base, count) {
var date = new Date(base);
date['set' + timeName](date['get' + timeName]() + count);
var date = new Date(base),
basePeriod = date['get' + timeName]();
 
date['set' + timeName](basePeriod + count);
 
if (basePeriod === date['get' + timeName]()) {
date.setDate(0); // #6537
}
 
return date.getTime() - base;
};
 
@@ -29446,6 +31207,7 @@
each = H.each,
extend = H.extend,
format = H.format,
grep = H.grep,
inArray = H.inArray,
isNumber = H.isNumber,
isString = H.isString,
@@ -29465,8 +31227,39 @@
seriesInit = seriesProto.init,
seriesProcessData = seriesProto.processData,
pointTooltipFormatter = Point.prototype.tooltipFormatter;
 
/**
* A wrapper for Chart with all the default values for a Stock chart
* Factory function for creating new stock charts. Creates a new {@link Chart|
* Chart} object with different default options than the basic Chart.
*
* @function #stockChart
* @memberOf Highcharts
*
* @param {String|HTMLDOMElement} renderTo
* The DOM element to render to, or its id.
* @param {Options} options
* The chart options structure as described in the {@link
* https://api.highcharts.com/highstock|options reference}.
* @param {Function} callback
* A function to execute when the chart object is finished loading and
* rendering. In most cases the chart is built in one thread, but in
* Internet Explorer version 8 or less the chart is sometimes initiated
* before the document is ready, and in these cases the chart object
* will not be finished synchronously. As a consequence, code that
* relies on the newly built Chart object should always run in the
* callback. Defining a {@link https://api.highcharts.com/highstock/chart.events.load|
* chart.event.load} handler is equivalent.
*
* @return {Chart}
* The chart object.
*
* @example
* var chart = Highcharts.stockChart('container', {
* series: [{
* data: [1, 2, 3, 4, 5, 6, 7, 8, 9],
* pointInterval: 24 * 60 * 60 * 1000
* }]
* });
*/
H.StockChart = H.stockChart = function(a, b, c) {
var hasRenderToArg = isString(a) || a.nodeName,
@@ -29677,8 +31470,8 @@
});
}
 
// Ignore in case of color Axis. #3360, #3524
if (axis.coll === 'colorAxis') {
// Ignore in case of colorAxis or zAxis. #3360, #3524, #6720
if (axis.coll !== 'xAxis' && axis.coll !== 'yAxis') {
return proceed.apply(this, [].slice.call(arguments, 1));
}
 
@@ -29704,7 +31497,13 @@
// lines (#2796).
uniqueAxes = axes.length ? [] : [axis.isXAxis ? chart.yAxis[0] : chart.xAxis[0]]; //#3742
each(axes, function(axis2) {
if (inArray(axis2, uniqueAxes) === -1) {
if (
inArray(axis2, uniqueAxes) === -1 &&
// Do not draw on axis which overlap completely. #5424
!H.find(uniqueAxes, function(unique) {
return unique.pos === axis2.pos && unique.len && axis2.len;
})
) {
uniqueAxes.push(axis2);
}
});
@@ -29977,7 +31776,17 @@
};
 
/**
* The setCompare method can be called also from the outside after render time
* Highstock only. Set the {@link
* http://api.highcharts.com/highstock/plotOptions.series.compare|
* 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 setCompare
* @memberOf Series.prototype
*
* @param {String} compare
* Can be one of `null`, `"percent"` or `"value"`.
*/
seriesProto.setCompare = function(compare) {
 
@@ -30051,7 +31860,7 @@
 
// find the first value for comparison
for (i = 0; i < length - 1; i++) {
compareValue = keyIndex > -1 ?
compareValue = processedYData[i] && keyIndex > -1 ?
processedYData[i][keyIndex] :
processedYData[i];
if (isNumber(compareValue) && processedXData[i + 1] >= series.xAxis.min && compareValue !== 0) {
@@ -30078,7 +31887,23 @@
});
 
/**
* Add a utility method, setCompare, to the Y axis
* Highstock only. Set the compare mode on all series belonging to an Y axis
* after render time.
*
* @param {String} compare
* The compare mode. Can be one of `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 setCompare
* @memberOf Axis.prototype
*
* @see {@link https://api.highcharts.com/highstock/series.plotOptions.compare|
* series.plotOptions.compare}
*
* @sample stock/members/axis-setcompare/
* Set compoare
*/
Axis.prototype.setCompare = function(compare, redraw) {
if (!this.isXAxis) {
@@ -30147,6 +31972,33 @@
proceed.call(this);
});
 
wrap(Chart.prototype, 'getSelectedPoints', function(proceed) {
var points = proceed.call(this);
 
each(this.series, function(serie) {
// series.points - for grouped points (#6445)
if (serie.hasGroupedData) {
points = points.concat(grep(serie.points || [], function(point) {
return point.selected;
}));
}
});
return points;
});
 
wrap(Chart.prototype, 'update', function(proceed, 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 && this.navigator) {
merge(true, this.options.scrollbar, options.scrollbar);
this.navigator.update({}, false);
delete options.scrollbar;
}
 
return proceed.apply(this, Array.prototype.slice.call(arguments, 1));
});
 
}(Highcharts));
(function() {