/* *
|
*
|
* (c) 2010-2019 Torstein Honsi
|
*
|
* License: www.highcharts.com/license
|
*
|
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
*
|
* */
|
'use strict';
|
|
var _Globals = require('./Globals.js');
|
|
var _Globals2 = _interopRequireDefault(_Globals);
|
|
var _Utilities = require('./Utilities.js');
|
|
var _Utilities2 = _interopRequireDefault(_Utilities);
|
|
require('./Axis.js');
|
|
require('./Chart.js');
|
|
require('./Point.js');
|
|
require('./Series.js');
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
var defined = _Utilities2.default.defined,
|
erase = _Utilities2.default.erase,
|
isArray = _Utilities2.default.isArray,
|
isNumber = _Utilities2.default.isNumber,
|
isObject = _Utilities2.default.isObject,
|
isString = _Utilities2.default.isString,
|
objectEach = _Utilities2.default.objectEach,
|
splat = _Utilities2.default.splat;
|
|
var addEvent = _Globals2.default.addEvent,
|
animate = _Globals2.default.animate,
|
Axis = _Globals2.default.Axis,
|
Chart = _Globals2.default.Chart,
|
createElement = _Globals2.default.createElement,
|
css = _Globals2.default.css,
|
extend = _Globals2.default.extend,
|
fireEvent = _Globals2.default.fireEvent,
|
merge = _Globals2.default.merge,
|
pick = _Globals2.default.pick,
|
Point = _Globals2.default.Point,
|
Series = _Globals2.default.Series,
|
seriesTypes = _Globals2.default.seriesTypes,
|
setAnimation = _Globals2.default.setAnimation;
|
/* eslint-disable valid-jsdoc */
|
/**
|
* Remove settings that have not changed, to avoid unnecessary rendering or
|
* computing (#9197).
|
* @private
|
*/
|
_Globals2.default.cleanRecursively = function (newer, older) {
|
var result = {};
|
objectEach(newer, function (val, key) {
|
var ob;
|
// Dive into objects (except DOM nodes)
|
if (isObject(newer[key], true) && !newer.nodeType && // #10044
|
older[key]) {
|
ob = _Globals2.default.cleanRecursively(newer[key], older[key]);
|
if (Object.keys(ob).length) {
|
result[key] = ob;
|
}
|
// Arrays, primitives and DOM nodes are copied directly
|
} else if (isObject(newer[key]) || newer[key] !== older[key]) {
|
result[key] = newer[key];
|
}
|
});
|
return result;
|
};
|
// Extend the Chart prototype for dynamic methods
|
extend(Chart.prototype, /** @lends Highcharts.Chart.prototype */{
|
/**
|
* Add a series to the chart after render time. Note that this method should
|
* never be used when adding data synchronously at chart render time, as it
|
* adds expense to the calculations and rendering. When adding data at the
|
* same time as the chart is initialized, add the series as a configuration
|
* option instead. With multiple axes, the `offset` is dynamically adjusted.
|
*
|
* @sample highcharts/members/chart-addseries/
|
* Add a series from a button
|
* @sample stock/members/chart-addseries/
|
* Add a series in Highstock
|
*
|
* @function Highcharts.Chart#addSeries
|
*
|
* @param {Highcharts.SeriesOptionsType} options
|
* The config options for the series.
|
*
|
* @param {boolean} [redraw=true]
|
* Whether to redraw the chart after adding.
|
*
|
* @param {boolean|Highcharts.AnimationOptionsObject} [animation]
|
* Whether to apply animation, and optionally animation
|
* configuration.
|
*
|
* @return {Highcharts.Series}
|
* The newly created series object.
|
*
|
* @fires Highcharts.Chart#event:addSeries
|
* @fires Highcharts.Chart#event:afterAddSeries
|
*/
|
addSeries: function addSeries(options, redraw, animation) {
|
var series,
|
chart = this;
|
if (options) {
|
// <- not necessary
|
redraw = pick(redraw, true); // defaults to true
|
fireEvent(chart, 'addSeries', { options: options }, function () {
|
series = chart.initSeries(options);
|
chart.isDirtyLegend = true;
|
chart.linkSeries();
|
fireEvent(chart, 'afterAddSeries', { series: series });
|
if (redraw) {
|
chart.redraw(animation);
|
}
|
});
|
}
|
return series;
|
},
|
/**
|
* Add an axis to the chart after render time. Note that this method should
|
* never be used when adding data synchronously at chart render time, as it
|
* adds expense to the calculations and rendering. When adding data at the
|
* same time as the chart is initialized, add the axis as a configuration
|
* option instead.
|
*
|
* @sample highcharts/members/chart-addaxis/
|
* Add and remove axes
|
*
|
* @function Highcharts.Chart#addAxis
|
*
|
* @param {Highcharts.AxisOptions} options
|
* The axis options.
|
*
|
* @param {boolean} [isX=false]
|
* Whether it is an X axis or a value axis.
|
*
|
* @param {boolean} [redraw=true]
|
* Whether to redraw the chart after adding.
|
*
|
* @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
|
* Whether and how to apply animation in the redraw.
|
*
|
* @return {Highcharts.Axis}
|
* The newly generated Axis object.
|
*/
|
addAxis: function addAxis(options, isX, redraw, animation) {
|
var key = isX ? 'xAxis' : 'yAxis',
|
chartOptions = this.options,
|
userOptions = merge(options, {
|
index: this[key].length,
|
isX: isX
|
}),
|
axis;
|
axis = new Axis(this, userOptions);
|
// Push the new axis options to the chart options
|
chartOptions[key] = splat(chartOptions[key] || {});
|
chartOptions[key].push(userOptions);
|
if (pick(redraw, true)) {
|
this.redraw(animation);
|
}
|
return axis;
|
},
|
/**
|
* Dim the chart and show a loading text or symbol. Options for the loading
|
* screen are defined in {@link
|
* https://api.highcharts.com/highcharts/loading|the loading options}.
|
*
|
* @sample highcharts/members/chart-hideloading/
|
* Show and hide loading from a button
|
* @sample highcharts/members/chart-showloading/
|
* Apply different text labels
|
* @sample stock/members/chart-show-hide-loading/
|
* Toggle loading in Highstock
|
*
|
* @function Highcharts.Chart#showLoading
|
*
|
* @param {string} [str]
|
* An optional text to show in the loading label instead of the
|
* default one. The default text is set in
|
* [lang.loading](http://api.highcharts.com/highcharts/lang.loading).
|
*
|
* @return {void}
|
*/
|
showLoading: function showLoading(str) {
|
var chart = this,
|
options = chart.options,
|
loadingDiv = chart.loadingDiv,
|
loadingOptions = options.loading,
|
setLoadingSize = function setLoadingSize() {
|
if (loadingDiv) {
|
css(loadingDiv, {
|
left: chart.plotLeft + 'px',
|
top: chart.plotTop + 'px',
|
width: chart.plotWidth + 'px',
|
height: chart.plotHeight + 'px'
|
});
|
}
|
};
|
// create the layer at the first call
|
if (!loadingDiv) {
|
chart.loadingDiv = loadingDiv = createElement('div', {
|
className: 'highcharts-loading highcharts-loading-hidden'
|
}, null, chart.container);
|
chart.loadingSpan = createElement('span', { className: 'highcharts-loading-inner' }, null, loadingDiv);
|
addEvent(chart, 'redraw', setLoadingSize); // #1080
|
}
|
loadingDiv.className = 'highcharts-loading';
|
// Update text
|
chart.loadingSpan.innerHTML = pick(str, options.lang.loading, '');
|
if (!chart.styledMode) {
|
// Update visuals
|
css(loadingDiv, extend(loadingOptions.style, {
|
zIndex: 10
|
}));
|
css(chart.loadingSpan, loadingOptions.labelStyle);
|
// Show it
|
if (!chart.loadingShown) {
|
css(loadingDiv, {
|
opacity: 0,
|
display: ''
|
});
|
animate(loadingDiv, {
|
opacity: loadingOptions.style.opacity || 0.5
|
}, {
|
duration: loadingOptions.showDuration || 0
|
});
|
}
|
}
|
chart.loadingShown = true;
|
setLoadingSize();
|
},
|
/**
|
* Hide the loading layer.
|
*
|
* @see Highcharts.Chart#showLoading
|
*
|
* @sample highcharts/members/chart-hideloading/
|
* Show and hide loading from a button
|
* @sample stock/members/chart-show-hide-loading/
|
* Toggle loading in Highstock
|
*
|
* @function Highcharts.Chart#hideLoading
|
*
|
* @return {void}
|
*/
|
hideLoading: function hideLoading() {
|
var options = this.options,
|
loadingDiv = this.loadingDiv;
|
if (loadingDiv) {
|
loadingDiv.className = 'highcharts-loading highcharts-loading-hidden';
|
if (!this.styledMode) {
|
animate(loadingDiv, {
|
opacity: 0
|
}, {
|
duration: options.loading.hideDuration || 100,
|
complete: function complete() {
|
css(loadingDiv, { display: 'none' });
|
}
|
});
|
}
|
}
|
this.loadingShown = false;
|
},
|
/**
|
* These properties cause isDirtyBox to be set to true when updating. Can be
|
* extended from plugins.
|
*/
|
propsRequireDirtyBox: ['backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'plotBackgroundColor', 'plotBackgroundImage', 'plotBorderColor', 'plotBorderWidth', 'plotShadow', 'shadow'],
|
/**
|
* These properties require a full reflow of chart elements, best
|
* implemented through running `Chart.setSize` internally (#8190).
|
* @type {Array}
|
*/
|
propsRequireReflow: ['margin', 'marginTop', 'marginRight', 'marginBottom', 'marginLeft', 'spacing', 'spacingTop', 'spacingRight', 'spacingBottom', 'spacingLeft'],
|
/**
|
* These properties cause all series to be updated when updating. Can be
|
* extended from plugins.
|
*/
|
propsRequireUpdateSeries: ['chart.inverted', 'chart.polar', 'chart.ignoreHiddenSeries', 'chart.type', 'colors', 'plotOptions', 'time', 'tooltip'],
|
/**
|
* These collections (arrays) implement update() methods with support for
|
* one-to-one option.
|
*/
|
collectionsWithUpdate: ['xAxis', 'yAxis', 'zAxis', 'series', 'colorAxis', 'pane'],
|
/**
|
* A generic function to update any element of the chart. Elements can be
|
* enabled and disabled, moved, re-styled, re-formatted etc.
|
*
|
* A special case is configuration objects that take arrays, for example
|
* [xAxis](https://api.highcharts.com/highcharts/xAxis),
|
* [yAxis](https://api.highcharts.com/highcharts/yAxis) or
|
* [series](https://api.highcharts.com/highcharts/series). For these
|
* collections, an `id` option is used to map the new option set to an
|
* existing object. If an existing object of the same id is not found, the
|
* corresponding item is updated. So for example, running `chart.update`
|
* with a series item without an id, will cause the existing chart's series
|
* with the same index in the series array to be updated. When the
|
* `oneToOne` parameter is true, `chart.update` will also take care of
|
* adding and removing items from the collection. Read more under the
|
* parameter description below.
|
*
|
* Note that when changing series data, `chart.update` may mutate the passed
|
* data options.
|
*
|
* See also the
|
* [responsive option set](https://api.highcharts.com/highcharts/responsive).
|
* Switching between `responsive.rules` basically runs `chart.update` under
|
* the hood.
|
*
|
* @sample highcharts/members/chart-update/
|
* Update chart geometry
|
*
|
* @function Highcharts.Chart#update
|
*
|
* @param {Highcharts.Options} options
|
* A configuration object for the new chart options.
|
*
|
* @param {boolean} [redraw=true]
|
* Whether to redraw the chart.
|
*
|
* @param {boolean} [oneToOne=false]
|
* When `true`, the `series`, `xAxis`, `yAxis` and `annotations`
|
* collections will be updated one to one, and items will be either
|
* added or removed to match the new updated options. For example,
|
* if the chart has two series and we call `chart.update` with a
|
* configuration containing three series, one will be added. If we
|
* call `chart.update` with one series, one will be removed. Setting
|
* an empty `series` array will remove all series, but leaving out
|
* the`series` property will leave all series untouched. If the
|
* series have id's, the new series options will be matched by id,
|
* and the remaining ones removed.
|
*
|
* @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
|
* Whether to apply animation, and optionally animation
|
* configuration.
|
*
|
* @return {void}
|
*
|
* @fires Highcharts.Chart#event:update
|
* @fires Highcharts.Chart#event:afterUpdate
|
*/
|
update: function update(options, redraw, oneToOne, animation) {
|
var chart = this,
|
adders = {
|
credits: 'addCredits',
|
title: 'setTitle',
|
subtitle: 'setSubtitle'
|
},
|
optionsChart,
|
updateAllAxes,
|
updateAllSeries,
|
newWidth,
|
newHeight,
|
runSetSize,
|
isResponsiveOptions = options.isResponsiveOptions,
|
itemsForRemoval = [];
|
fireEvent(chart, 'update', { options: options });
|
// If there are responsive rules in action, undo the responsive rules
|
// before we apply the updated options and replay the responsive rules
|
// on top from the chart.redraw function (#9617).
|
if (!isResponsiveOptions) {
|
chart.setResponsive(false, true);
|
}
|
options = _Globals2.default.cleanRecursively(options, chart.options);
|
merge(true, chart.userOptions, options);
|
// If the top-level chart option is present, some special updates are
|
// required
|
optionsChart = options.chart;
|
if (optionsChart) {
|
merge(true, chart.options.chart, optionsChart);
|
// Setter function
|
if ('className' in optionsChart) {
|
chart.setClassName(optionsChart.className);
|
}
|
if ('reflow' in optionsChart) {
|
chart.setReflow(optionsChart.reflow);
|
}
|
if ('inverted' in optionsChart || 'polar' in optionsChart || 'type' in optionsChart) {
|
// Parse options.chart.inverted and options.chart.polar together
|
// with the available series.
|
chart.propFromSeries();
|
updateAllAxes = true;
|
}
|
if ('alignTicks' in optionsChart) {
|
// #6452
|
updateAllAxes = true;
|
}
|
objectEach(optionsChart, function (val, key) {
|
if (chart.propsRequireUpdateSeries.indexOf('chart.' + key) !== -1) {
|
updateAllSeries = true;
|
}
|
// Only dirty box
|
if (chart.propsRequireDirtyBox.indexOf(key) !== -1) {
|
chart.isDirtyBox = true;
|
}
|
// Chart setSize
|
if (!isResponsiveOptions && chart.propsRequireReflow.indexOf(key) !== -1) {
|
runSetSize = true;
|
}
|
});
|
if (!chart.styledMode && 'style' in optionsChart) {
|
chart.renderer.setStyle(optionsChart.style);
|
}
|
}
|
// Moved up, because tooltip needs updated plotOptions (#6218)
|
if (!chart.styledMode && options.colors) {
|
this.options.colors = options.colors;
|
}
|
if (options.plotOptions) {
|
merge(true, this.options.plotOptions, options.plotOptions);
|
}
|
// Maintaining legacy global time. If the chart is instanciated first
|
// with global time, then updated with time options, we need to create a
|
// new Time instance to avoid mutating the global time (#10536).
|
if (options.time && this.time === _Globals2.default.time) {
|
this.time = new _Globals2.default.Time(options.time);
|
}
|
// 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
|
// options.tooltip => chart.tooltip
|
// options.subtitle => chart.subtitle
|
// options.mapNavigation => chart.mapNavigation
|
// options.navigator => chart.navigator
|
// options.scrollbar => chart.scrollbar
|
objectEach(options, function (val, key) {
|
if (chart[key] && typeof chart[key].update === 'function') {
|
chart[key].update(val, false);
|
// If a one-to-one object does not exist, look for an adder function
|
} else if (typeof chart[adders[key]] === 'function') {
|
chart[adders[key]](val);
|
}
|
if (key !== 'chart' && chart.propsRequireUpdateSeries.indexOf(key) !== -1) {
|
updateAllSeries = true;
|
}
|
});
|
// Setters for collections. For axes and series, each item is referred
|
// by an id. If the id is not found, it defaults to the corresponding
|
// item in the collection, so setting one series without an id, will
|
// update the first series in the chart. Setting two series without
|
// an id will update the first and the second respectively (#6019)
|
// chart.update and responsive.
|
this.collectionsWithUpdate.forEach(function (coll) {
|
var indexMap;
|
if (options[coll]) {
|
// In stock charts, the navigator series are also part of the
|
// chart.series array, but those series should not be handled
|
// here (#8196).
|
if (coll === 'series') {
|
indexMap = [];
|
chart[coll].forEach(function (s, i) {
|
if (!s.options.isInternal) {
|
indexMap.push(pick(s.options.index, i));
|
}
|
});
|
}
|
splat(options[coll]).forEach(function (newOptions, i) {
|
var item = defined(newOptions.id) && chart.get(newOptions.id) || chart[coll][indexMap ? indexMap[i] : i];
|
if (item && item.coll === coll) {
|
item.update(newOptions, false);
|
if (oneToOne) {
|
item.touched = true;
|
}
|
}
|
// If oneToOne and no matching item is found, add one
|
if (!item && oneToOne && chart.collectionsWithInit[coll]) {
|
chart.collectionsWithInit[coll][0].apply(chart,
|
// [newOptions, ...extraArguments, redraw=false]
|
[newOptions].concat(
|
// Not all initializers require extra args
|
chart.collectionsWithInit[coll][1] || []).concat([false])).touched = true;
|
}
|
});
|
// Add items for removal
|
if (oneToOne) {
|
chart[coll].forEach(function (item) {
|
if (!item.touched && !item.options.isInternal) {
|
itemsForRemoval.push(item);
|
} else {
|
delete item.touched;
|
}
|
});
|
}
|
}
|
});
|
itemsForRemoval.forEach(function (item) {
|
if (item.remove) {
|
item.remove(false);
|
}
|
});
|
if (updateAllAxes) {
|
chart.axes.forEach(function (axis) {
|
axis.update({}, false);
|
});
|
}
|
// Certain options require the whole series structure to be thrown away
|
// and rebuilt
|
if (updateAllSeries) {
|
chart.series.forEach(function (series) {
|
series.update({}, false);
|
});
|
}
|
// For loading, just update the options, do not redraw
|
if (options.loading) {
|
merge(true, chart.options.loading, options.loading);
|
}
|
// Update size. Redraw is forced.
|
newWidth = optionsChart && optionsChart.width;
|
newHeight = optionsChart && optionsChart.height;
|
if (isString(newHeight)) {
|
newHeight = _Globals2.default.relativeLength(newHeight, newWidth || chart.chartWidth);
|
}
|
if (
|
// In this case, run chart.setSize with newWidth and newHeight which
|
// are undefined, only for reflowing chart elements because margin
|
// or spacing has been set (#8190)
|
runSetSize ||
|
// In this case, the size is actually set
|
isNumber(newWidth) && newWidth !== chart.chartWidth || isNumber(newHeight) && newHeight !== chart.chartHeight) {
|
chart.setSize(newWidth, newHeight, animation);
|
} else if (pick(redraw, true)) {
|
chart.redraw(animation);
|
}
|
fireEvent(chart, 'afterUpdate', {
|
options: options,
|
redraw: redraw,
|
animation: animation
|
});
|
},
|
/**
|
* Shortcut to set the subtitle options. This can also be done from {@link
|
* Chart#update} or {@link Chart#setTitle}.
|
*
|
* @function Highcharts.Chart#setSubtitle
|
*
|
* @param {Highcharts.SubtitleOptions} options
|
* New subtitle options. The subtitle text itself is set by the
|
* `options.text` property.
|
*
|
* @return {void}
|
*/
|
setSubtitle: function setSubtitle(options) {
|
this.setTitle(undefined, options);
|
}
|
});
|
/**
|
* These collections (arrays) implement `Chart.addSomethig` method used in
|
* chart.update() to create new object in the collection. Equivalent for
|
* deleting is resolved by simple `Somethig.remove()`.
|
*
|
* Note: We need to define these references after initializers are bound to
|
* chart's prototype.
|
*/
|
Chart.prototype.collectionsWithInit = {
|
// collectionName: [ initializingMethod, [extraArguments] ]
|
xAxis: [Chart.prototype.addAxis, [true]],
|
yAxis: [Chart.prototype.addAxis, [false]],
|
series: [Chart.prototype.addSeries]
|
};
|
// extend the Point prototype for dynamic methods
|
extend(Point.prototype, /** @lends Highcharts.Point.prototype */{
|
/**
|
* Update point with new options (typically x/y data) and optionally redraw
|
* the series.
|
*
|
* @sample highcharts/members/point-update-column/
|
* Update column value
|
* @sample highcharts/members/point-update-pie/
|
* Update pie slice
|
* @sample maps/members/point-update/
|
* Update map area value in Highmaps
|
*
|
* @function Highcharts.Point#update
|
*
|
* @param {Highcharts.PointOptionsType} options
|
* The point options. Point options are handled as described under
|
* the `series.type.data` item for each series type. For example
|
* for a line series, if options is a single number, the point will
|
* be given that number as the marin y value. If it is an array, it
|
* will be interpreted as x and y values respectively. If it is an
|
* object, advanced options are applied.
|
*
|
* @param {boolean} [redraw=true]
|
* Whether to redraw the chart after the point is updated. If doing
|
* more operations on the chart, it is best practice to set
|
* `redraw` to false and call `chart.redraw()` after.
|
*
|
* @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
|
* Whether to apply animation, and optionally animation
|
* configuration.
|
*
|
* @return {void}
|
*
|
* @fires Highcharts.Point#event:update
|
*/
|
update: function update(options, redraw, animation, runEvent) {
|
var point = this,
|
series = point.series,
|
graphic = point.graphic,
|
i,
|
chart = series.chart,
|
seriesOptions = series.options;
|
redraw = pick(redraw, true);
|
/**
|
* @private
|
*/
|
function update() {
|
point.applyOptions(options);
|
// Update visuals
|
if (point.y === null && graphic) {
|
// #4146
|
point.graphic = graphic.destroy();
|
}
|
if (isObject(options, true)) {
|
// Destroy so we can get new elements
|
if (graphic && graphic.element) {
|
// "null" is also a valid symbol
|
if (options && options.marker && options.marker.symbol !== undefined) {
|
point.graphic = graphic.destroy();
|
}
|
}
|
if (options && options.dataLabels && point.dataLabel) {
|
point.dataLabel = point.dataLabel.destroy(); // #2468
|
}
|
if (point.connector) {
|
point.connector = point.connector.destroy(); // #7243
|
}
|
}
|
// record changes in the parallel arrays
|
i = point.index;
|
series.updateParallelArrays(point, i);
|
// Record the options to options.data. If the old or the new config
|
// is an object, use point options, otherwise use raw options
|
// (#4701, #4916).
|
seriesOptions.data[i] = isObject(seriesOptions.data[i], true) || isObject(options, true) ? point.options : pick(options, seriesOptions.data[i]);
|
// redraw
|
series.isDirty = series.isDirtyData = true;
|
if (!series.fixedBox && series.hasCartesianSeries) {
|
// #1906, #2320
|
chart.isDirtyBox = true;
|
}
|
if (seriesOptions.legendType === 'point') {
|
// #1831, #1885
|
chart.isDirtyLegend = true;
|
}
|
if (redraw) {
|
chart.redraw(animation);
|
}
|
}
|
// Fire the event with a default handler of doing the update
|
if (runEvent === false) {
|
// When called from setData
|
update();
|
} else {
|
point.firePointEvent('update', { options: options }, update);
|
}
|
},
|
/**
|
* Remove a point and optionally redraw the series and if necessary the axes
|
*
|
* @sample highcharts/plotoptions/series-point-events-remove/
|
* Remove point and confirm
|
* @sample highcharts/members/point-remove/
|
* Remove pie slice
|
* @sample maps/members/point-remove/
|
* Remove selected points in Highmaps
|
*
|
* @function Highcharts.Point#remove
|
*
|
* @param {boolean} [redraw=true]
|
* Whether to redraw the chart or wait for an explicit call. When
|
* doing more operations on the chart, for example running
|
* `point.remove()` in a loop, it is best practice to set `redraw`
|
* to false and call `chart.redraw()` after.
|
*
|
* @param {boolean|Highcharts.AnimationOptionsObject} [animation=false]
|
* Whether to apply animation, and optionally animation
|
* configuration.
|
*
|
* @return {void}
|
*/
|
remove: function remove(redraw, animation) {
|
this.series.removePoint(this.series.data.indexOf(this), redraw, animation);
|
}
|
});
|
// Extend the series prototype for dynamic methods
|
extend(Series.prototype, /** @lends Series.prototype */{
|
/**
|
* Add a point to the series after render time. The point can be added at
|
* the end, or by giving it an X value, to the start or in the middle of the
|
* series.
|
*
|
* @sample highcharts/members/series-addpoint-append/
|
* Append point
|
* @sample highcharts/members/series-addpoint-append-and-shift/
|
* Append and shift
|
* @sample highcharts/members/series-addpoint-x-and-y/
|
* Both X and Y values given
|
* @sample highcharts/members/series-addpoint-pie/
|
* Append pie slice
|
* @sample stock/members/series-addpoint/
|
* Append 100 points in Highstock
|
* @sample stock/members/series-addpoint-shift/
|
* Append and shift in Highstock
|
* @sample maps/members/series-addpoint/
|
* Add a point in Highmaps
|
*
|
* @function Highcharts.Series#addPoint
|
*
|
* @param {Highcharts.PointOptionsType} options
|
* The point options. If options is a single number, a point with
|
* that y value is appended to the series. If it is an array, it will
|
* be interpreted as x and y values respectively. If it is an
|
* object, advanced options as outlined under `series.data` are
|
* applied.
|
*
|
* @param {boolean} [redraw=true]
|
* Whether to redraw the chart after the point is added. When adding
|
* more than one point, it is highly recommended that the redraw
|
* option be set to false, and instead {@link Chart#redraw} is
|
* explicitly called after the adding of points is finished.
|
* Otherwise, the chart will redraw after adding each point.
|
*
|
* @param {boolean} [shift=false]
|
* If true, a point is shifted off the start of the series as one is
|
* appended to the end.
|
*
|
* @param {boolean|Highcharts.AnimationOptionsObject} [animation]
|
* Whether to apply animation, and optionally animation
|
* configuration.
|
*
|
* @param {boolean} [withEvent=true]
|
* Used internally, whether to fire the series `addPoint` event.
|
*
|
* @return {void}
|
*
|
* @fires Highcharts.Series#event:addPoint
|
*/
|
addPoint: function addPoint(options, redraw, shift, animation, withEvent) {
|
var series = this,
|
seriesOptions = series.options,
|
data = series.data,
|
chart = series.chart,
|
xAxis = series.xAxis,
|
names = xAxis && xAxis.hasNames && xAxis.names,
|
dataOptions = seriesOptions.data,
|
point,
|
xData = series.xData,
|
isInTheMiddle,
|
i,
|
x;
|
// Optional redraw, defaults to true
|
redraw = pick(redraw, true);
|
// Get options and push the point to xData, yData and series.options. In
|
// series.generatePoints the Point instance will be created on demand
|
// and pushed to the series.data array.
|
point = { series: series };
|
series.pointClass.prototype.applyOptions.apply(point, [options]);
|
x = point.x;
|
// Get the insertion point
|
i = xData.length;
|
if (series.requireSorting && x < xData[i - 1]) {
|
isInTheMiddle = true;
|
while (i && xData[i - 1] > x) {
|
i--;
|
}
|
}
|
// Insert undefined item
|
series.updateParallelArrays(point, 'splice', i, 0, 0);
|
// Update it
|
series.updateParallelArrays(point, i);
|
if (names && point.name) {
|
names[x] = point.name;
|
}
|
dataOptions.splice(i, 0, options);
|
if (isInTheMiddle) {
|
series.data.splice(i, 0, null);
|
series.processData();
|
}
|
// Generate points to be added to the legend (#1329)
|
if (seriesOptions.legendType === 'point') {
|
series.generatePoints();
|
}
|
// Shift the first point off the parallel arrays
|
if (shift) {
|
if (data[0] && data[0].remove) {
|
data[0].remove(false);
|
} else {
|
data.shift();
|
series.updateParallelArrays(point, 'shift');
|
dataOptions.shift();
|
}
|
}
|
// Fire event
|
if (withEvent !== false) {
|
fireEvent(series, 'addPoint', { point: point });
|
}
|
// redraw
|
series.isDirty = true;
|
series.isDirtyData = true;
|
if (redraw) {
|
chart.redraw(animation); // Animation is set anyway on redraw, #5665
|
}
|
},
|
/**
|
* Remove a point from the series. Unlike the
|
* {@link Highcharts.Point#remove} method, this can also be done on a point
|
* that is not instanciated because it is outside the view or subject to
|
* Highstock data grouping.
|
*
|
* @sample highcharts/members/series-removepoint/
|
* Remove cropped point
|
*
|
* @function Highcharts.Series#removePoint
|
*
|
* @param {number} i
|
* The index of the point in the {@link Highcharts.Series.data|data}
|
* array.
|
*
|
* @param {boolean} [redraw=true]
|
* Whether to redraw the chart after the point is added. When
|
* removing more than one point, it is highly recommended that the
|
* `redraw` option be set to `false`, and instead {@link
|
* Highcharts.Chart#redraw} is explicitly called after the adding of
|
* points is finished.
|
*
|
* @param {boolean|Highcharts.AnimationOptionsObject} [animation]
|
* Whether and optionally how the series should be animated.
|
*
|
* @return {void}
|
*
|
* @fires Highcharts.Point#event:remove
|
*/
|
removePoint: function removePoint(i, redraw, animation) {
|
var series = this,
|
data = series.data,
|
point = data[i],
|
points = series.points,
|
chart = series.chart,
|
remove = function remove() {
|
if (points && points.length === data.length) {
|
// #4935
|
points.splice(i, 1);
|
}
|
data.splice(i, 1);
|
series.options.data.splice(i, 1);
|
series.updateParallelArrays(point || { series: series }, 'splice', i, 1);
|
if (point) {
|
point.destroy();
|
}
|
// redraw
|
series.isDirty = true;
|
series.isDirtyData = true;
|
if (redraw) {
|
chart.redraw();
|
}
|
};
|
setAnimation(animation, chart);
|
redraw = pick(redraw, true);
|
// Fire the event with a default handler of removing the point
|
if (point) {
|
point.firePointEvent('remove', null, remove);
|
} else {
|
remove();
|
}
|
},
|
/**
|
* Remove a series and optionally redraw the chart.
|
*
|
* @sample highcharts/members/series-remove/
|
* Remove first series from a button
|
*
|
* @function Highcharts.Series#remove
|
*
|
* @param {boolean} [redraw=true]
|
* Whether to redraw the chart or wait for an explicit call to
|
* {@link Highcharts.Chart#redraw}.
|
*
|
* @param {boolean|Highcharts.AnimationOptionsObject} [animation]
|
* Whether to apply animation, and optionally animation
|
* configuration.
|
*
|
* @param {boolean} [withEvent=true]
|
* Used internally, whether to fire the series `remove` event.
|
*
|
* @return {void}
|
*
|
* @fires Highcharts.Series#event:remove
|
*/
|
remove: function remove(redraw, animation, withEvent, keepEvents) {
|
var series = this,
|
chart = series.chart;
|
/**
|
* @private
|
*/
|
function remove() {
|
// Destroy elements
|
series.destroy(keepEvents);
|
series.remove = null; // Prevent from doing again (#9097)
|
// Redraw
|
chart.isDirtyLegend = chart.isDirtyBox = true;
|
chart.linkSeries();
|
if (pick(redraw, true)) {
|
chart.redraw(animation);
|
}
|
}
|
// Fire the event with a default handler of removing the point
|
if (withEvent !== false) {
|
fireEvent(series, 'remove', null, remove);
|
} else {
|
remove();
|
}
|
},
|
/**
|
* Update the series with a new set of options. For a clean and precise
|
* handling of new options, all methods and elements from the series are
|
* removed, and it is initialized from scratch. Therefore, this method is
|
* more performance expensive than some other utility methods like {@link
|
* Series#setData} or {@link Series#setVisible}.
|
*
|
* Note that `Series.update` may mutate the passed `data` options.
|
*
|
* @sample highcharts/members/series-update/
|
* Updating series options
|
* @sample maps/members/series-update/
|
* Update series options in Highmaps
|
*
|
* @function Highcharts.Series#update
|
*
|
* @param {Highcharts.SeriesOptionsType} options
|
* New options that will be merged with the series' existing options.
|
*
|
* @param {boolean} [redraw=true]
|
* Whether to redraw the chart after the series is altered. If doing
|
* more operations on the chart, it is a good idea to set redraw to
|
* false and call {@link Chart#redraw} after.
|
*
|
* @return {void}
|
*
|
* @fires Highcharts.Series#event:update
|
* @fires Highcharts.Series#event:afterUpdate
|
*/
|
update: function update(options, redraw) {
|
options = _Globals2.default.cleanRecursively(options, this.userOptions);
|
fireEvent(this, 'update', { options: options });
|
var series = this,
|
chart = series.chart,
|
|
// must use user options when changing type because series.options
|
// is merged in with type specific plotOptions
|
oldOptions = series.userOptions,
|
seriesOptions,
|
initialType = series.initialType || series.type,
|
newType = options.type || oldOptions.type || chart.options.chart.type,
|
keepPoints = !(
|
// Indicators, histograms etc recalculate the data. It should be
|
// possible to omit this.
|
this.hasDerivedData ||
|
// Changes to data grouping requires new points in new groups
|
options.dataGrouping ||
|
// New type requires new point classes
|
newType && newType !== this.type ||
|
// New options affecting how the data points are built
|
options.pointStart !== undefined || options.pointInterval || options.pointIntervalUnit || options.keys),
|
initialSeriesProto = seriesTypes[initialType].prototype,
|
n,
|
groups = ['group', 'markerGroup', 'dataLabelsGroup', 'transformGroup'],
|
preserve = ['eventOptions', 'navigatorSeries', 'baseSeries'],
|
|
// Animation must be enabled when calling update before the initial
|
// animation has first run. This happens when calling update
|
// directly after chart initialization, or when applying responsive
|
// rules (#6912).
|
animation = series.finishedAnimating && { animation: false },
|
kinds = {};
|
if (keepPoints) {
|
preserve.push('data', 'isDirtyData', 'points', 'processedXData', 'processedYData', 'xIncrement', '_hasPointMarkers', '_hasPointLabels',
|
// Map specific, consider moving it to series-specific preserve-
|
// properties (#10617)
|
'mapMap', 'mapData', 'minY', 'maxY', 'minX', 'maxX');
|
if (options.visible !== false) {
|
preserve.push('area', 'graph');
|
}
|
series.parallelArrays.forEach(function (key) {
|
preserve.push(key + 'Data');
|
});
|
if (options.data) {
|
this.setData(options.data, false);
|
}
|
}
|
// Do the merge, with some forced options
|
options = merge(oldOptions, animation, {
|
// When oldOptions.index is null it should't be cleared.
|
// Otherwise navigator series will have wrong indexes (#10193).
|
index: oldOptions.index === undefined ? series.index : oldOptions.index,
|
pointStart: pick(
|
// when updating from blank (#7933)
|
oldOptions.pointStart,
|
// when updating after addPoint
|
series.xData[0])
|
}, !keepPoints && { data: series.options.data }, options);
|
// Merge does not merge arrays, but replaces them. Since points were
|
// updated, `series.options.data` has correct merged options, use it:
|
if (keepPoints && options.data) {
|
options.data = series.options.data;
|
}
|
// Make sure preserved properties are not destroyed (#3094)
|
preserve = groups.concat(preserve);
|
preserve.forEach(function (prop) {
|
preserve[prop] = series[prop];
|
delete series[prop];
|
});
|
// Destroy the series and delete all properties. Reinsert all
|
// methods and properties from the new type prototype (#2270,
|
// #3719).
|
series.remove(false, null, false, true);
|
for (n in initialSeriesProto) {
|
// eslint-disable-line guard-for-in
|
series[n] = undefined;
|
}
|
if (seriesTypes[newType || initialType]) {
|
extend(series, seriesTypes[newType || initialType].prototype);
|
} else {
|
_Globals2.default.error(17, true, chart);
|
}
|
// Re-register groups (#3094) and other preserved properties
|
preserve.forEach(function (prop) {
|
series[prop] = preserve[prop];
|
});
|
series.init(chart, options);
|
// Remove particular elements of the points. Check `series.options`
|
// because we need to consider the options being set on plotOptions as
|
// well.
|
if (keepPoints && this.points) {
|
seriesOptions = series.options;
|
// What kind of elements to destroy
|
if (seriesOptions.visible === false) {
|
kinds.graphic = 1;
|
kinds.dataLabel = 1;
|
} else if (!series._hasPointLabels) {
|
var marker = seriesOptions.marker,
|
dataLabels = seriesOptions.dataLabels;
|
if (marker && (marker.enabled === false || 'symbol' in marker // #10870
|
)) {
|
kinds.graphic = 1;
|
}
|
if (dataLabels && dataLabels.enabled === false) {
|
kinds.dataLabel = 1;
|
}
|
}
|
this.points.forEach(function (point) {
|
if (point && point.series) {
|
point.resolveColor();
|
// Destroy elements in order to recreate based on updated
|
// series options.
|
if (Object.keys(kinds).length) {
|
point.destroyElements(kinds);
|
}
|
if (seriesOptions.showInLegend === false && point.legendItem) {
|
chart.legend.destroyItem(point);
|
}
|
}
|
}, this);
|
}
|
// Update the Z index of groups (#3380, #7397)
|
if (options.zIndex !== oldOptions.zIndex) {
|
groups.forEach(function (groupName) {
|
if (series[groupName]) {
|
series[groupName].attr({
|
zIndex: options.zIndex
|
});
|
}
|
});
|
}
|
series.initialType = initialType;
|
chart.linkSeries(); // Links are lost in series.remove (#3028)
|
fireEvent(this, 'afterUpdate');
|
if (pick(redraw, true)) {
|
chart.redraw(keepPoints ? undefined : false);
|
}
|
},
|
/**
|
* Used from within series.update
|
*
|
* @private
|
* @function Highcharts.Series#setName
|
*
|
* @param {string} name
|
*
|
* @return {void}
|
*/
|
setName: function setName(name) {
|
this.name = this.options.name = this.userOptions.name = name;
|
this.chart.isDirtyLegend = true;
|
}
|
});
|
// Extend the Axis.prototype for dynamic methods
|
extend(Axis.prototype, /** @lends Highcharts.Axis.prototype */{
|
/**
|
* Update an axis object with a new set of options. The options are merged
|
* with the existing options, so only new or altered options need to be
|
* specified.
|
*
|
* @sample highcharts/members/axis-update/
|
* Axis update demo
|
*
|
* @function Highcharts.Axis#update
|
*
|
* @param {Highcharts.AxisOptions} options
|
* The new options that will be merged in with existing options on
|
* the axis.
|
*
|
* @param {boolean} [redraw=true]
|
* Whether to redraw the chart after the axis is altered. If doing
|
* more operations on the chart, it is a good idea to set redraw to
|
* false and call {@link Chart#redraw} after.
|
*
|
* @return {void}
|
*/
|
update: function update(options, redraw) {
|
var chart = this.chart,
|
newEvents = options && options.events || {};
|
options = merge(this.userOptions, options);
|
// Color Axis is not an array,
|
// This change is applied in the ColorAxis wrapper
|
if (chart.options[this.coll].indexOf) {
|
// Don't use this.options.index,
|
// StockChart has Axes in navigator too
|
chart.options[this.coll][chart.options[this.coll].indexOf(this.userOptions)] = options;
|
}
|
// Remove old events, if no new exist (#8161)
|
objectEach(chart.options[this.coll].events, function (fn, ev) {
|
if (typeof newEvents[ev] === 'undefined') {
|
newEvents[ev] = undefined;
|
}
|
});
|
this.destroy(true);
|
this.init(chart, extend(options, { events: newEvents }));
|
chart.isDirtyBox = true;
|
if (pick(redraw, true)) {
|
chart.redraw();
|
}
|
},
|
/**
|
* Remove the axis from the chart.
|
*
|
* @sample highcharts/members/chart-addaxis/
|
* Add and remove axes
|
*
|
* @function Highcharts.Axis#remove
|
*
|
* @param {boolean} [redraw=true]
|
* Whether to redraw the chart following the remove.
|
*
|
* @return {void}
|
*/
|
remove: function remove(redraw) {
|
var chart = this.chart,
|
key = this.coll,
|
// xAxis or yAxis
|
axisSeries = this.series,
|
i = axisSeries.length;
|
// Remove associated series (#2687)
|
while (i--) {
|
if (axisSeries[i]) {
|
axisSeries[i].remove(false);
|
}
|
}
|
// Remove the axis
|
erase(chart.axes, this);
|
erase(chart[key], this);
|
if (isArray(chart.options[key])) {
|
chart.options[key].splice(this.options.index, 1);
|
} else {
|
// color axis, #6488
|
delete chart.options[key];
|
}
|
chart[key].forEach(function (axis, i) {
|
// Re-index, #1706, #8075
|
axis.options.index = axis.userOptions.index = i;
|
});
|
this.destroy();
|
chart.isDirtyBox = true;
|
if (pick(redraw, true)) {
|
chart.redraw();
|
}
|
},
|
/**
|
* Update the axis title by options after render time.
|
*
|
* @sample highcharts/members/axis-settitle/
|
* Set a new Y axis title
|
*
|
* @function Highcharts.Axis#setTitle
|
*
|
* @param {Highcharts.AxisTitleOptions} titleOptions
|
* The additional title options.
|
*
|
* @param {boolean} [redraw=true]
|
* Whether to redraw the chart after setting the title.
|
*
|
* @return {void}
|
*/
|
setTitle: function setTitle(titleOptions, redraw) {
|
this.update({ title: titleOptions }, redraw);
|
},
|
/**
|
* Set new axis categories and optionally redraw.
|
*
|
* @sample highcharts/members/axis-setcategories/
|
* Set categories by click on a button
|
*
|
* @function Highcharts.Axis#setCategories
|
*
|
* @param {Array<string>} categories
|
* The new categories.
|
*
|
* @param {boolean} [redraw=true]
|
* Whether to redraw the chart.
|
*
|
* @return {void}
|
*/
|
setCategories: function setCategories(categories, redraw) {
|
this.update({ categories: categories }, redraw);
|
}
|
});
|