/base/000_base/bower_components/highcharts/highcharts.src.js |
@@ -1,5 +1,5 @@ |
/** |
* @license Highcharts JS v5.0.10 (2017-03-31) |
* @license Highcharts 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: 'Highcharts', |
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 Highcharts 5.0.10')); |
desc.element.appendChild(doc.createTextNode('Created with Highcharts 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); |