Dynamics.js 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313
  1. /* *
  2. *
  3. * (c) 2010-2019 Torstein Honsi
  4. *
  5. * License: www.highcharts.com/license
  6. *
  7. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  8. *
  9. * */
  10. 'use strict';
  11. import H from './Globals.js';
  12. import U from './Utilities.js';
  13. var defined = U.defined, erase = U.erase, extend = U.extend, isArray = U.isArray, isNumber = U.isNumber, isObject = U.isObject, isString = U.isString, objectEach = U.objectEach, pick = U.pick, relativeLength = U.relativeLength, setAnimation = U.setAnimation, splat = U.splat;
  14. import './Axis.js';
  15. import './Chart.js';
  16. import './Point.js';
  17. import './Series.js';
  18. var addEvent = H.addEvent, animate = H.animate, Axis = H.Axis, Chart = H.Chart, createElement = H.createElement, css = H.css, fireEvent = H.fireEvent, merge = H.merge, Point = H.Point, Series = H.Series, seriesTypes = H.seriesTypes;
  19. /* eslint-disable valid-jsdoc */
  20. /**
  21. * Remove settings that have not changed, to avoid unnecessary rendering or
  22. * computing (#9197).
  23. * @private
  24. */
  25. H.cleanRecursively = function (newer, older) {
  26. var result = {};
  27. objectEach(newer, function (val, key) {
  28. var ob;
  29. // Dive into objects (except DOM nodes)
  30. if (isObject(newer[key], true) &&
  31. !newer.nodeType && // #10044
  32. older[key]) {
  33. ob = H.cleanRecursively(newer[key], older[key]);
  34. if (Object.keys(ob).length) {
  35. result[key] = ob;
  36. }
  37. // Arrays, primitives and DOM nodes are copied directly
  38. }
  39. else if (isObject(newer[key]) ||
  40. newer[key] !== older[key]) {
  41. result[key] = newer[key];
  42. }
  43. });
  44. return result;
  45. };
  46. // Extend the Chart prototype for dynamic methods
  47. extend(Chart.prototype, /** @lends Highcharts.Chart.prototype */ {
  48. /**
  49. * Add a series to the chart after render time. Note that this method should
  50. * never be used when adding data synchronously at chart render time, as it
  51. * adds expense to the calculations and rendering. When adding data at the
  52. * same time as the chart is initialized, add the series as a configuration
  53. * option instead. With multiple axes, the `offset` is dynamically adjusted.
  54. *
  55. * @sample highcharts/members/chart-addseries/
  56. * Add a series from a button
  57. * @sample stock/members/chart-addseries/
  58. * Add a series in Highstock
  59. *
  60. * @function Highcharts.Chart#addSeries
  61. *
  62. * @param {Highcharts.SeriesOptionsType} options
  63. * The config options for the series.
  64. *
  65. * @param {boolean} [redraw=true]
  66. * Whether to redraw the chart after adding.
  67. *
  68. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  69. * Whether to apply animation, and optionally animation
  70. * configuration.
  71. *
  72. * @return {Highcharts.Series}
  73. * The newly created series object.
  74. *
  75. * @fires Highcharts.Chart#event:addSeries
  76. * @fires Highcharts.Chart#event:afterAddSeries
  77. */
  78. addSeries: function (options, redraw, animation) {
  79. var series, chart = this;
  80. if (options) { // <- not necessary
  81. redraw = pick(redraw, true); // defaults to true
  82. fireEvent(chart, 'addSeries', { options: options }, function () {
  83. series = chart.initSeries(options);
  84. chart.isDirtyLegend = true;
  85. chart.linkSeries();
  86. if (series.enabledDataSorting) {
  87. // We need to call `setData` after `linkSeries`
  88. series.setData(options.data, false);
  89. }
  90. fireEvent(chart, 'afterAddSeries', { series: series });
  91. if (redraw) {
  92. chart.redraw(animation);
  93. }
  94. });
  95. }
  96. return series;
  97. },
  98. /**
  99. * Add an axis to the chart after render time. Note that this method should
  100. * never be used when adding data synchronously at chart render time, as it
  101. * adds expense to the calculations and rendering. When adding data at the
  102. * same time as the chart is initialized, add the axis as a configuration
  103. * option instead.
  104. *
  105. * @sample highcharts/members/chart-addaxis/
  106. * Add and remove axes
  107. *
  108. * @function Highcharts.Chart#addAxis
  109. *
  110. * @param {Highcharts.AxisOptions} options
  111. * The axis options.
  112. *
  113. * @param {boolean} [isX=false]
  114. * Whether it is an X axis or a value axis.
  115. *
  116. * @param {boolean} [redraw=true]
  117. * Whether to redraw the chart after adding.
  118. *
  119. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  120. * Whether and how to apply animation in the redraw.
  121. *
  122. * @return {Highcharts.Axis}
  123. * The newly generated Axis object.
  124. */
  125. addAxis: function (options, isX, redraw, animation) {
  126. return this.createAxis(isX ? 'xAxis' : 'yAxis', { axis: options, redraw: redraw, animation: animation });
  127. },
  128. /**
  129. * Add a color axis to the chart after render time. Note that this method
  130. * should never be used when adding data synchronously at chart render time,
  131. * as it adds expense to the calculations and rendering. When adding data at
  132. * the same time as the chart is initialized, add the axis as a
  133. * configuration option instead.
  134. *
  135. * @sample highcharts/members/chart-addaxis/
  136. * Add and remove axes
  137. *
  138. * @function Highcharts.Chart#addColorAxis
  139. *
  140. * @param {Highcharts.ColorAxisOptions} options
  141. * The axis options.
  142. *
  143. * @param {boolean} [redraw=true]
  144. * Whether to redraw the chart after adding.
  145. *
  146. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  147. * Whether and how to apply animation in the redraw.
  148. *
  149. * @return {Highcharts.ColorAxis}
  150. * The newly generated Axis object.
  151. */
  152. addColorAxis: function (options, redraw, animation) {
  153. return this.createAxis('colorAxis', { axis: options, redraw: redraw, animation: animation });
  154. },
  155. /**
  156. * Factory for creating different axis types.
  157. *
  158. * @private
  159. * @function Highcharts.Chart#createAxis
  160. *
  161. * @param {string} type
  162. * An axis type.
  163. *
  164. * @param {...Array<*>} arguments
  165. * All arguments for the constructor.
  166. *
  167. * @return {Highcharts.Axis | Highcharts.ColorAxis}
  168. * The newly generated Axis object.
  169. */
  170. createAxis: function (type, options) {
  171. var chartOptions = this.options, isColorAxis = type === 'colorAxis', axisOptions = options.axis, redraw = options.redraw, animation = options.animation, userOptions = merge(axisOptions, {
  172. index: this[type].length,
  173. isX: type === 'xAxis'
  174. }), axis;
  175. if (isColorAxis) {
  176. axis = new H.ColorAxis(this, userOptions);
  177. }
  178. else {
  179. axis = new Axis(this, userOptions);
  180. }
  181. // Push the new axis options to the chart options
  182. chartOptions[type] = splat(chartOptions[type] || {});
  183. chartOptions[type].push(userOptions);
  184. if (isColorAxis) {
  185. this.isDirtyLegend = true;
  186. // Clear before 'bindAxes' (#11924)
  187. this.axes.forEach(function (axis) {
  188. axis.series = [];
  189. });
  190. this.series.forEach(function (series) {
  191. series.bindAxes();
  192. series.isDirtyData = true;
  193. });
  194. }
  195. if (pick(redraw, true)) {
  196. this.redraw(animation);
  197. }
  198. return axis;
  199. },
  200. /**
  201. * Dim the chart and show a loading text or symbol. Options for the loading
  202. * screen are defined in {@link
  203. * https://api.highcharts.com/highcharts/loading|the loading options}.
  204. *
  205. * @sample highcharts/members/chart-hideloading/
  206. * Show and hide loading from a button
  207. * @sample highcharts/members/chart-showloading/
  208. * Apply different text labels
  209. * @sample stock/members/chart-show-hide-loading/
  210. * Toggle loading in Highstock
  211. *
  212. * @function Highcharts.Chart#showLoading
  213. *
  214. * @param {string} [str]
  215. * An optional text to show in the loading label instead of the
  216. * default one. The default text is set in
  217. * [lang.loading](http://api.highcharts.com/highcharts/lang.loading).
  218. *
  219. * @return {void}
  220. */
  221. showLoading: function (str) {
  222. var chart = this, options = chart.options, loadingDiv = chart.loadingDiv, loadingOptions = options.loading, setLoadingSize = function () {
  223. if (loadingDiv) {
  224. css(loadingDiv, {
  225. left: chart.plotLeft + 'px',
  226. top: chart.plotTop + 'px',
  227. width: chart.plotWidth + 'px',
  228. height: chart.plotHeight + 'px'
  229. });
  230. }
  231. };
  232. // create the layer at the first call
  233. if (!loadingDiv) {
  234. chart.loadingDiv = loadingDiv = createElement('div', {
  235. className: 'highcharts-loading highcharts-loading-hidden'
  236. }, null, chart.container);
  237. chart.loadingSpan = createElement('span', { className: 'highcharts-loading-inner' }, null, loadingDiv);
  238. addEvent(chart, 'redraw', setLoadingSize); // #1080
  239. }
  240. loadingDiv.className = 'highcharts-loading';
  241. // Update text
  242. chart.loadingSpan.innerHTML =
  243. pick(str, options.lang.loading, '');
  244. if (!chart.styledMode) {
  245. // Update visuals
  246. css(loadingDiv, extend(loadingOptions.style, {
  247. zIndex: 10
  248. }));
  249. css(chart.loadingSpan, loadingOptions.labelStyle);
  250. // Show it
  251. if (!chart.loadingShown) {
  252. css(loadingDiv, {
  253. opacity: 0,
  254. display: ''
  255. });
  256. animate(loadingDiv, {
  257. opacity: loadingOptions.style.opacity || 0.5
  258. }, {
  259. duration: loadingOptions.showDuration || 0
  260. });
  261. }
  262. }
  263. chart.loadingShown = true;
  264. setLoadingSize();
  265. },
  266. /**
  267. * Hide the loading layer.
  268. *
  269. * @see Highcharts.Chart#showLoading
  270. *
  271. * @sample highcharts/members/chart-hideloading/
  272. * Show and hide loading from a button
  273. * @sample stock/members/chart-show-hide-loading/
  274. * Toggle loading in Highstock
  275. *
  276. * @function Highcharts.Chart#hideLoading
  277. *
  278. * @return {void}
  279. */
  280. hideLoading: function () {
  281. var options = this.options, loadingDiv = this.loadingDiv;
  282. if (loadingDiv) {
  283. loadingDiv.className =
  284. 'highcharts-loading highcharts-loading-hidden';
  285. if (!this.styledMode) {
  286. animate(loadingDiv, {
  287. opacity: 0
  288. }, {
  289. duration: options.loading.hideDuration || 100,
  290. complete: function () {
  291. css(loadingDiv, { display: 'none' });
  292. }
  293. });
  294. }
  295. }
  296. this.loadingShown = false;
  297. },
  298. /**
  299. * These properties cause isDirtyBox to be set to true when updating. Can be
  300. * extended from plugins.
  301. */
  302. propsRequireDirtyBox: [
  303. 'backgroundColor',
  304. 'borderColor',
  305. 'borderWidth',
  306. 'borderRadius',
  307. 'plotBackgroundColor',
  308. 'plotBackgroundImage',
  309. 'plotBorderColor',
  310. 'plotBorderWidth',
  311. 'plotShadow',
  312. 'shadow'
  313. ],
  314. /**
  315. * These properties require a full reflow of chart elements, best
  316. * implemented through running `Chart.setSize` internally (#8190).
  317. * @type {Array}
  318. */
  319. propsRequireReflow: [
  320. 'margin',
  321. 'marginTop',
  322. 'marginRight',
  323. 'marginBottom',
  324. 'marginLeft',
  325. 'spacing',
  326. 'spacingTop',
  327. 'spacingRight',
  328. 'spacingBottom',
  329. 'spacingLeft'
  330. ],
  331. /**
  332. * These properties cause all series to be updated when updating. Can be
  333. * extended from plugins.
  334. */
  335. propsRequireUpdateSeries: [
  336. 'chart.inverted',
  337. 'chart.polar',
  338. 'chart.ignoreHiddenSeries',
  339. 'chart.type',
  340. 'colors',
  341. 'plotOptions',
  342. 'time',
  343. 'tooltip'
  344. ],
  345. /**
  346. * These collections (arrays) implement update() methods with support for
  347. * one-to-one option.
  348. */
  349. collectionsWithUpdate: [
  350. 'xAxis',
  351. 'yAxis',
  352. 'zAxis',
  353. 'series'
  354. ],
  355. /**
  356. * A generic function to update any element of the chart. Elements can be
  357. * enabled and disabled, moved, re-styled, re-formatted etc.
  358. *
  359. * A special case is configuration objects that take arrays, for example
  360. * [xAxis](https://api.highcharts.com/highcharts/xAxis),
  361. * [yAxis](https://api.highcharts.com/highcharts/yAxis) or
  362. * [series](https://api.highcharts.com/highcharts/series). For these
  363. * collections, an `id` option is used to map the new option set to an
  364. * existing object. If an existing object of the same id is not found, the
  365. * corresponding item is updated. So for example, running `chart.update`
  366. * with a series item without an id, will cause the existing chart's series
  367. * with the same index in the series array to be updated. When the
  368. * `oneToOne` parameter is true, `chart.update` will also take care of
  369. * adding and removing items from the collection. Read more under the
  370. * parameter description below.
  371. *
  372. * Note that when changing series data, `chart.update` may mutate the passed
  373. * data options.
  374. *
  375. * See also the
  376. * [responsive option set](https://api.highcharts.com/highcharts/responsive).
  377. * Switching between `responsive.rules` basically runs `chart.update` under
  378. * the hood.
  379. *
  380. * @sample highcharts/members/chart-update/
  381. * Update chart geometry
  382. *
  383. * @function Highcharts.Chart#update
  384. *
  385. * @param {Highcharts.Options} options
  386. * A configuration object for the new chart options.
  387. *
  388. * @param {boolean} [redraw=true]
  389. * Whether to redraw the chart.
  390. *
  391. * @param {boolean} [oneToOne=false]
  392. * When `true`, the `series`, `xAxis`, `yAxis` and `annotations`
  393. * collections will be updated one to one, and items will be either
  394. * added or removed to match the new updated options. For example,
  395. * if the chart has two series and we call `chart.update` with a
  396. * configuration containing three series, one will be added. If we
  397. * call `chart.update` with one series, one will be removed. Setting
  398. * an empty `series` array will remove all series, but leaving out
  399. * the`series` property will leave all series untouched. If the
  400. * series have id's, the new series options will be matched by id,
  401. * and the remaining ones removed.
  402. *
  403. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  404. * Whether to apply animation, and optionally animation
  405. * configuration.
  406. *
  407. * @return {void}
  408. *
  409. * @fires Highcharts.Chart#event:update
  410. * @fires Highcharts.Chart#event:afterUpdate
  411. */
  412. update: function (options, redraw, oneToOne, animation) {
  413. var chart = this, adders = {
  414. credits: 'addCredits',
  415. title: 'setTitle',
  416. subtitle: 'setSubtitle',
  417. caption: 'setCaption'
  418. }, optionsChart, updateAllAxes, updateAllSeries, newWidth, newHeight, runSetSize, isResponsiveOptions = options.isResponsiveOptions, itemsForRemoval = [];
  419. fireEvent(chart, 'update', { options: options });
  420. // If there are responsive rules in action, undo the responsive rules
  421. // before we apply the updated options and replay the responsive rules
  422. // on top from the chart.redraw function (#9617).
  423. if (!isResponsiveOptions) {
  424. chart.setResponsive(false, true);
  425. }
  426. options = H.cleanRecursively(options, chart.options);
  427. merge(true, chart.userOptions, options);
  428. // If the top-level chart option is present, some special updates are
  429. // required
  430. optionsChart = options.chart;
  431. if (optionsChart) {
  432. merge(true, chart.options.chart, optionsChart);
  433. // Setter function
  434. if ('className' in optionsChart) {
  435. chart.setClassName(optionsChart.className);
  436. }
  437. if ('reflow' in optionsChart) {
  438. chart.setReflow(optionsChart.reflow);
  439. }
  440. if ('inverted' in optionsChart ||
  441. 'polar' in optionsChart ||
  442. 'type' in optionsChart) {
  443. // Parse options.chart.inverted and options.chart.polar together
  444. // with the available series.
  445. chart.propFromSeries();
  446. updateAllAxes = true;
  447. }
  448. if ('alignTicks' in optionsChart) { // #6452
  449. updateAllAxes = true;
  450. }
  451. objectEach(optionsChart, function (val, key) {
  452. if (chart.propsRequireUpdateSeries.indexOf('chart.' + key) !==
  453. -1) {
  454. updateAllSeries = true;
  455. }
  456. // Only dirty box
  457. if (chart.propsRequireDirtyBox.indexOf(key) !== -1) {
  458. chart.isDirtyBox = true;
  459. }
  460. // Chart setSize
  461. if (!isResponsiveOptions &&
  462. chart.propsRequireReflow.indexOf(key) !== -1) {
  463. runSetSize = true;
  464. }
  465. });
  466. if (!chart.styledMode && 'style' in optionsChart) {
  467. chart.renderer.setStyle(optionsChart.style);
  468. }
  469. }
  470. // Moved up, because tooltip needs updated plotOptions (#6218)
  471. if (!chart.styledMode && options.colors) {
  472. this.options.colors = options.colors;
  473. }
  474. if (options.plotOptions) {
  475. merge(true, this.options.plotOptions, options.plotOptions);
  476. }
  477. // Maintaining legacy global time. If the chart is instanciated first
  478. // with global time, then updated with time options, we need to create a
  479. // new Time instance to avoid mutating the global time (#10536).
  480. if (options.time && this.time === H.time) {
  481. this.time = new H.Time(options.time);
  482. }
  483. // Some option stuctures correspond one-to-one to chart objects that
  484. // have update methods, for example
  485. // options.credits => chart.credits
  486. // options.legend => chart.legend
  487. // options.title => chart.title
  488. // options.tooltip => chart.tooltip
  489. // options.subtitle => chart.subtitle
  490. // options.mapNavigation => chart.mapNavigation
  491. // options.navigator => chart.navigator
  492. // options.scrollbar => chart.scrollbar
  493. objectEach(options, function (val, key) {
  494. if (chart[key] &&
  495. typeof chart[key].update === 'function') {
  496. chart[key].update(val, false);
  497. // If a one-to-one object does not exist, look for an adder function
  498. }
  499. else if (typeof chart[adders[key]] === 'function') {
  500. chart[adders[key]](val);
  501. }
  502. if (key !== 'chart' &&
  503. chart.propsRequireUpdateSeries.indexOf(key) !== -1) {
  504. updateAllSeries = true;
  505. }
  506. });
  507. // Setters for collections. For axes and series, each item is referred
  508. // by an id. If the id is not found, it defaults to the corresponding
  509. // item in the collection, so setting one series without an id, will
  510. // update the first series in the chart. Setting two series without
  511. // an id will update the first and the second respectively (#6019)
  512. // chart.update and responsive.
  513. this.collectionsWithUpdate.forEach(function (coll) {
  514. var indexMap;
  515. if (options[coll]) {
  516. // In stock charts, the navigator series are also part of the
  517. // chart.series array, but those series should not be handled
  518. // here (#8196).
  519. if (coll === 'series') {
  520. indexMap = [];
  521. chart[coll].forEach(function (s, i) {
  522. if (!s.options.isInternal) {
  523. indexMap.push(pick(s.options.index, i));
  524. }
  525. });
  526. }
  527. splat(options[coll]).forEach(function (newOptions, i) {
  528. var item = (defined(newOptions.id) &&
  529. chart.get(newOptions.id)) || chart[coll][indexMap ? indexMap[i] : i];
  530. if (item && item.coll === coll) {
  531. item.update(newOptions, false);
  532. if (oneToOne) {
  533. item.touched = true;
  534. }
  535. }
  536. // If oneToOne and no matching item is found, add one
  537. if (!item && oneToOne && chart.collectionsWithInit[coll]) {
  538. chart.collectionsWithInit[coll][0].apply(chart,
  539. // [newOptions, ...extraArguments, redraw=false]
  540. [
  541. newOptions
  542. ].concat(
  543. // Not all initializers require extra args
  544. chart.collectionsWithInit[coll][1] || []).concat([
  545. false
  546. ])).touched = true;
  547. }
  548. });
  549. // Add items for removal
  550. if (oneToOne) {
  551. chart[coll].forEach(function (item) {
  552. if (!item.touched && !item.options.isInternal) {
  553. itemsForRemoval.push(item);
  554. }
  555. else {
  556. delete item.touched;
  557. }
  558. });
  559. }
  560. }
  561. });
  562. itemsForRemoval.forEach(function (item) {
  563. if (item.remove) {
  564. item.remove(false);
  565. }
  566. });
  567. if (updateAllAxes) {
  568. chart.axes.forEach(function (axis) {
  569. axis.update({}, false);
  570. });
  571. }
  572. // Certain options require the whole series structure to be thrown away
  573. // and rebuilt
  574. if (updateAllSeries) {
  575. chart.getSeriesOrderByLinks().forEach(function (series) {
  576. // Avoid removed navigator series
  577. if (series.chart) {
  578. series.update({}, false);
  579. }
  580. }, this);
  581. }
  582. // For loading, just update the options, do not redraw
  583. if (options.loading) {
  584. merge(true, chart.options.loading, options.loading);
  585. }
  586. // Update size. Redraw is forced.
  587. newWidth = optionsChart && optionsChart.width;
  588. newHeight = optionsChart && optionsChart.height;
  589. if (isString(newHeight)) {
  590. newHeight = relativeLength(newHeight, newWidth || chart.chartWidth);
  591. }
  592. if (
  593. // In this case, run chart.setSize with newWidth and newHeight which
  594. // are undefined, only for reflowing chart elements because margin
  595. // or spacing has been set (#8190)
  596. runSetSize ||
  597. // In this case, the size is actually set
  598. (isNumber(newWidth) && newWidth !== chart.chartWidth) ||
  599. (isNumber(newHeight) && newHeight !== chart.chartHeight)) {
  600. chart.setSize(newWidth, newHeight, animation);
  601. }
  602. else if (pick(redraw, true)) {
  603. chart.redraw(animation);
  604. }
  605. fireEvent(chart, 'afterUpdate', {
  606. options: options,
  607. redraw: redraw,
  608. animation: animation
  609. });
  610. },
  611. /**
  612. * Shortcut to set the subtitle options. This can also be done from {@link
  613. * Chart#update} or {@link Chart#setTitle}.
  614. *
  615. * @function Highcharts.Chart#setSubtitle
  616. *
  617. * @param {Highcharts.SubtitleOptions} options
  618. * New subtitle options. The subtitle text itself is set by the
  619. * `options.text` property.
  620. *
  621. * @return {void}
  622. */
  623. setSubtitle: function (options, redraw) {
  624. this.applyDescription('subtitle', options);
  625. this.layOutTitles(redraw);
  626. },
  627. /**
  628. * Set the caption options. This can also be done from {@link
  629. * Chart#update}.
  630. *
  631. * @function Highcharts.Chart#setCaption
  632. *
  633. * @param {Highcharts.CaptionOptions} options
  634. * New caption options. The caption text itself is set by the
  635. * `options.text` property.
  636. *
  637. * @return {void}
  638. */
  639. setCaption: function (options, redraw) {
  640. this.applyDescription('caption', options);
  641. this.layOutTitles(redraw);
  642. }
  643. });
  644. /**
  645. * These collections (arrays) implement `Chart.addSomethig` method used in
  646. * chart.update() to create new object in the collection. Equivalent for
  647. * deleting is resolved by simple `Somethig.remove()`.
  648. *
  649. * Note: We need to define these references after initializers are bound to
  650. * chart's prototype.
  651. */
  652. Chart.prototype.collectionsWithInit = {
  653. // collectionName: [ initializingMethod, [extraArguments] ]
  654. xAxis: [Chart.prototype.addAxis, [true]],
  655. yAxis: [Chart.prototype.addAxis, [false]],
  656. series: [Chart.prototype.addSeries]
  657. };
  658. // extend the Point prototype for dynamic methods
  659. extend(Point.prototype, /** @lends Highcharts.Point.prototype */ {
  660. /**
  661. * Update point with new options (typically x/y data) and optionally redraw
  662. * the series.
  663. *
  664. * @sample highcharts/members/point-update-column/
  665. * Update column value
  666. * @sample highcharts/members/point-update-pie/
  667. * Update pie slice
  668. * @sample maps/members/point-update/
  669. * Update map area value in Highmaps
  670. *
  671. * @function Highcharts.Point#update
  672. *
  673. * @param {Highcharts.PointOptionsType} options
  674. * The point options. Point options are handled as described under
  675. * the `series.type.data` item for each series type. For example
  676. * for a line series, if options is a single number, the point will
  677. * be given that number as the marin y value. If it is an array, it
  678. * will be interpreted as x and y values respectively. If it is an
  679. * object, advanced options are applied.
  680. *
  681. * @param {boolean} [redraw=true]
  682. * Whether to redraw the chart after the point is updated. If doing
  683. * more operations on the chart, it is best practice to set
  684. * `redraw` to false and call `chart.redraw()` after.
  685. *
  686. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  687. * Whether to apply animation, and optionally animation
  688. * configuration.
  689. *
  690. * @return {void}
  691. *
  692. * @fires Highcharts.Point#event:update
  693. */
  694. update: function (options, redraw, animation, runEvent) {
  695. var point = this, series = point.series, graphic = point.graphic, i, chart = series.chart, seriesOptions = series.options;
  696. redraw = pick(redraw, true);
  697. /**
  698. * @private
  699. */
  700. function update() {
  701. point.applyOptions(options);
  702. // Update visuals
  703. if (point.y === null && graphic) { // #4146
  704. point.graphic = graphic.destroy();
  705. }
  706. if (isObject(options, true)) {
  707. // Destroy so we can get new elements
  708. if (graphic && graphic.element) {
  709. // "null" is also a valid symbol
  710. if (options &&
  711. options.marker &&
  712. typeof options.marker.symbol !== 'undefined') {
  713. point.graphic = graphic.destroy();
  714. }
  715. }
  716. if (options && options.dataLabels && point.dataLabel) {
  717. point.dataLabel = point.dataLabel.destroy(); // #2468
  718. }
  719. if (point.connector) {
  720. point.connector = point.connector.destroy(); // #7243
  721. }
  722. }
  723. // record changes in the parallel arrays
  724. i = point.index;
  725. series.updateParallelArrays(point, i);
  726. // Record the options to options.data. If the old or the new config
  727. // is an object, use point options, otherwise use raw options
  728. // (#4701, #4916).
  729. seriesOptions.data[i] = (isObject(seriesOptions.data[i], true) ||
  730. isObject(options, true)) ?
  731. point.options :
  732. pick(options, seriesOptions.data[i]);
  733. // redraw
  734. series.isDirty = series.isDirtyData = true;
  735. if (!series.fixedBox && series.hasCartesianSeries) { // #1906, #2320
  736. chart.isDirtyBox = true;
  737. }
  738. if (seriesOptions.legendType === 'point') { // #1831, #1885
  739. chart.isDirtyLegend = true;
  740. }
  741. if (redraw) {
  742. chart.redraw(animation);
  743. }
  744. }
  745. // Fire the event with a default handler of doing the update
  746. if (runEvent === false) { // When called from setData
  747. update();
  748. }
  749. else {
  750. point.firePointEvent('update', { options: options }, update);
  751. }
  752. },
  753. /**
  754. * Remove a point and optionally redraw the series and if necessary the axes
  755. *
  756. * @sample highcharts/plotoptions/series-point-events-remove/
  757. * Remove point and confirm
  758. * @sample highcharts/members/point-remove/
  759. * Remove pie slice
  760. * @sample maps/members/point-remove/
  761. * Remove selected points in Highmaps
  762. *
  763. * @function Highcharts.Point#remove
  764. *
  765. * @param {boolean} [redraw=true]
  766. * Whether to redraw the chart or wait for an explicit call. When
  767. * doing more operations on the chart, for example running
  768. * `point.remove()` in a loop, it is best practice to set `redraw`
  769. * to false and call `chart.redraw()` after.
  770. *
  771. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=false]
  772. * Whether to apply animation, and optionally animation
  773. * configuration.
  774. *
  775. * @return {void}
  776. */
  777. remove: function (redraw, animation) {
  778. this.series.removePoint(this.series.data.indexOf(this), redraw, animation);
  779. }
  780. });
  781. // Extend the series prototype for dynamic methods
  782. extend(Series.prototype, /** @lends Series.prototype */ {
  783. /**
  784. * Add a point to the series after render time. The point can be added at
  785. * the end, or by giving it an X value, to the start or in the middle of the
  786. * series.
  787. *
  788. * @sample highcharts/members/series-addpoint-append/
  789. * Append point
  790. * @sample highcharts/members/series-addpoint-append-and-shift/
  791. * Append and shift
  792. * @sample highcharts/members/series-addpoint-x-and-y/
  793. * Both X and Y values given
  794. * @sample highcharts/members/series-addpoint-pie/
  795. * Append pie slice
  796. * @sample stock/members/series-addpoint/
  797. * Append 100 points in Highstock
  798. * @sample stock/members/series-addpoint-shift/
  799. * Append and shift in Highstock
  800. * @sample maps/members/series-addpoint/
  801. * Add a point in Highmaps
  802. *
  803. * @function Highcharts.Series#addPoint
  804. *
  805. * @param {Highcharts.PointOptionsType} options
  806. * The point options. If options is a single number, a point with
  807. * that y value is appended to the series. If it is an array, it will
  808. * be interpreted as x and y values respectively. If it is an
  809. * object, advanced options as outlined under `series.data` are
  810. * applied.
  811. *
  812. * @param {boolean} [redraw=true]
  813. * Whether to redraw the chart after the point is added. When adding
  814. * more than one point, it is highly recommended that the redraw
  815. * option be set to false, and instead {@link Chart#redraw} is
  816. * explicitly called after the adding of points is finished.
  817. * Otherwise, the chart will redraw after adding each point.
  818. *
  819. * @param {boolean} [shift=false]
  820. * If true, a point is shifted off the start of the series as one is
  821. * appended to the end.
  822. *
  823. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  824. * Whether to apply animation, and optionally animation
  825. * configuration.
  826. *
  827. * @param {boolean} [withEvent=true]
  828. * Used internally, whether to fire the series `addPoint` event.
  829. *
  830. * @return {void}
  831. *
  832. * @fires Highcharts.Series#event:addPoint
  833. */
  834. addPoint: function (options, redraw, shift, animation, withEvent) {
  835. 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;
  836. // Optional redraw, defaults to true
  837. redraw = pick(redraw, true);
  838. // Get options and push the point to xData, yData and series.options. In
  839. // series.generatePoints the Point instance will be created on demand
  840. // and pushed to the series.data array.
  841. point = { series: series };
  842. series.pointClass.prototype.applyOptions.apply(point, [options]);
  843. x = point.x;
  844. // Get the insertion point
  845. i = xData.length;
  846. if (series.requireSorting && x < xData[i - 1]) {
  847. isInTheMiddle = true;
  848. while (i && xData[i - 1] > x) {
  849. i--;
  850. }
  851. }
  852. // Insert undefined item
  853. series.updateParallelArrays(point, 'splice', i, 0, 0);
  854. // Update it
  855. series.updateParallelArrays(point, i);
  856. if (names && point.name) {
  857. names[x] = point.name;
  858. }
  859. dataOptions.splice(i, 0, options);
  860. if (isInTheMiddle) {
  861. series.data.splice(i, 0, null);
  862. series.processData();
  863. }
  864. // Generate points to be added to the legend (#1329)
  865. if (seriesOptions.legendType === 'point') {
  866. series.generatePoints();
  867. }
  868. // Shift the first point off the parallel arrays
  869. if (shift) {
  870. if (data[0] && data[0].remove) {
  871. data[0].remove(false);
  872. }
  873. else {
  874. data.shift();
  875. series.updateParallelArrays(point, 'shift');
  876. dataOptions.shift();
  877. }
  878. }
  879. // Fire event
  880. if (withEvent !== false) {
  881. fireEvent(series, 'addPoint', { point: point });
  882. }
  883. // redraw
  884. series.isDirty = true;
  885. series.isDirtyData = true;
  886. if (redraw) {
  887. chart.redraw(animation); // Animation is set anyway on redraw, #5665
  888. }
  889. },
  890. /**
  891. * Remove a point from the series. Unlike the
  892. * {@link Highcharts.Point#remove} method, this can also be done on a point
  893. * that is not instanciated because it is outside the view or subject to
  894. * Highstock data grouping.
  895. *
  896. * @sample highcharts/members/series-removepoint/
  897. * Remove cropped point
  898. *
  899. * @function Highcharts.Series#removePoint
  900. *
  901. * @param {number} i
  902. * The index of the point in the {@link Highcharts.Series.data|data}
  903. * array.
  904. *
  905. * @param {boolean} [redraw=true]
  906. * Whether to redraw the chart after the point is added. When
  907. * removing more than one point, it is highly recommended that the
  908. * `redraw` option be set to `false`, and instead {@link
  909. * Highcharts.Chart#redraw} is explicitly called after the adding of
  910. * points is finished.
  911. *
  912. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  913. * Whether and optionally how the series should be animated.
  914. *
  915. * @return {void}
  916. *
  917. * @fires Highcharts.Point#event:remove
  918. */
  919. removePoint: function (i, redraw, animation) {
  920. var series = this, data = series.data, point = data[i], points = series.points, chart = series.chart, remove = function () {
  921. if (points && points.length === data.length) { // #4935
  922. points.splice(i, 1);
  923. }
  924. data.splice(i, 1);
  925. series.options.data.splice(i, 1);
  926. series.updateParallelArrays(point || { series: series }, 'splice', i, 1);
  927. if (point) {
  928. point.destroy();
  929. }
  930. // redraw
  931. series.isDirty = true;
  932. series.isDirtyData = true;
  933. if (redraw) {
  934. chart.redraw();
  935. }
  936. };
  937. setAnimation(animation, chart);
  938. redraw = pick(redraw, true);
  939. // Fire the event with a default handler of removing the point
  940. if (point) {
  941. point.firePointEvent('remove', null, remove);
  942. }
  943. else {
  944. remove();
  945. }
  946. },
  947. /**
  948. * Remove a series and optionally redraw the chart.
  949. *
  950. * @sample highcharts/members/series-remove/
  951. * Remove first series from a button
  952. *
  953. * @function Highcharts.Series#remove
  954. *
  955. * @param {boolean} [redraw=true]
  956. * Whether to redraw the chart or wait for an explicit call to
  957. * {@link Highcharts.Chart#redraw}.
  958. *
  959. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  960. * Whether to apply animation, and optionally animation
  961. * configuration.
  962. *
  963. * @param {boolean} [withEvent=true]
  964. * Used internally, whether to fire the series `remove` event.
  965. *
  966. * @return {void}
  967. *
  968. * @fires Highcharts.Series#event:remove
  969. */
  970. remove: function (redraw, animation, withEvent, keepEvents) {
  971. var series = this, chart = series.chart;
  972. /**
  973. * @private
  974. */
  975. function remove() {
  976. // Destroy elements
  977. series.destroy(keepEvents);
  978. series.remove = null; // Prevent from doing again (#9097)
  979. // Redraw
  980. chart.isDirtyLegend = chart.isDirtyBox = true;
  981. chart.linkSeries();
  982. if (pick(redraw, true)) {
  983. chart.redraw(animation);
  984. }
  985. }
  986. // Fire the event with a default handler of removing the point
  987. if (withEvent !== false) {
  988. fireEvent(series, 'remove', null, remove);
  989. }
  990. else {
  991. remove();
  992. }
  993. },
  994. /**
  995. * Update the series with a new set of options. For a clean and precise
  996. * handling of new options, all methods and elements from the series are
  997. * removed, and it is initialized from scratch. Therefore, this method is
  998. * more performance expensive than some other utility methods like {@link
  999. * Series#setData} or {@link Series#setVisible}.
  1000. *
  1001. * Note that `Series.update` may mutate the passed `data` options.
  1002. *
  1003. * @sample highcharts/members/series-update/
  1004. * Updating series options
  1005. * @sample maps/members/series-update/
  1006. * Update series options in Highmaps
  1007. *
  1008. * @function Highcharts.Series#update
  1009. *
  1010. * @param {Highcharts.SeriesOptionsType} options
  1011. * New options that will be merged with the series' existing options.
  1012. *
  1013. * @param {boolean} [redraw=true]
  1014. * Whether to redraw the chart after the series is altered. If doing
  1015. * more operations on the chart, it is a good idea to set redraw to
  1016. * false and call {@link Chart#redraw} after.
  1017. *
  1018. * @return {void}
  1019. *
  1020. * @fires Highcharts.Series#event:update
  1021. * @fires Highcharts.Series#event:afterUpdate
  1022. */
  1023. update: function (options, redraw) {
  1024. options = H.cleanRecursively(options, this.userOptions);
  1025. fireEvent(this, 'update', { options: options });
  1026. var series = this, chart = series.chart,
  1027. // must use user options when changing type because series.options
  1028. // is merged in with type specific plotOptions
  1029. oldOptions = series.userOptions, seriesOptions, initialType = series.initialType || series.type, newType = (options.type ||
  1030. oldOptions.type ||
  1031. chart.options.chart.type), keepPoints = !(
  1032. // Indicators, histograms etc recalculate the data. It should be
  1033. // possible to omit this.
  1034. this.hasDerivedData ||
  1035. // Changes to data grouping requires new points in new groups
  1036. options.dataGrouping ||
  1037. // New type requires new point classes
  1038. (newType && newType !== this.type) ||
  1039. // New options affecting how the data points are built
  1040. typeof options.pointStart !== 'undefined' ||
  1041. options.pointInterval ||
  1042. options.pointIntervalUnit ||
  1043. options.keys), initialSeriesProto = seriesTypes[initialType].prototype, n, groups = [
  1044. 'group',
  1045. 'markerGroup',
  1046. 'dataLabelsGroup',
  1047. 'transformGroup'
  1048. ], preserve = [
  1049. 'eventOptions',
  1050. 'navigatorSeries',
  1051. 'baseSeries'
  1052. ],
  1053. // Animation must be enabled when calling update before the initial
  1054. // animation has first run. This happens when calling update
  1055. // directly after chart initialization, or when applying responsive
  1056. // rules (#6912).
  1057. animation = series.finishedAnimating && { animation: false }, kinds = {};
  1058. if (keepPoints) {
  1059. preserve.push('data', 'isDirtyData', 'points', 'processedXData', 'processedYData', 'xIncrement', '_hasPointMarkers', '_hasPointLabels',
  1060. // Map specific, consider moving it to series-specific preserve-
  1061. // properties (#10617)
  1062. 'mapMap', 'mapData', 'minY', 'maxY', 'minX', 'maxX');
  1063. if (options.visible !== false) {
  1064. preserve.push('area', 'graph');
  1065. }
  1066. series.parallelArrays.forEach(function (key) {
  1067. preserve.push(key + 'Data');
  1068. });
  1069. if (options.data) {
  1070. // setData uses dataSorting options so we need to update them
  1071. // earlier
  1072. if (options.dataSorting) {
  1073. extend(series.options.dataSorting, options.dataSorting);
  1074. }
  1075. this.setData(options.data, false);
  1076. }
  1077. }
  1078. // Do the merge, with some forced options
  1079. options = merge(oldOptions, animation, {
  1080. // When oldOptions.index is null it should't be cleared.
  1081. // Otherwise navigator series will have wrong indexes (#10193).
  1082. index: typeof oldOptions.index === 'undefined' ?
  1083. series.index : oldOptions.index,
  1084. pointStart: pick(
  1085. // when updating from blank (#7933)
  1086. oldOptions.pointStart,
  1087. // when updating after addPoint
  1088. series.xData[0])
  1089. }, (!keepPoints && { data: series.options.data }), options);
  1090. // Merge does not merge arrays, but replaces them. Since points were
  1091. // updated, `series.options.data` has correct merged options, use it:
  1092. if (keepPoints && options.data) {
  1093. options.data = series.options.data;
  1094. }
  1095. // Make sure preserved properties are not destroyed (#3094)
  1096. preserve = groups.concat(preserve);
  1097. preserve.forEach(function (prop) {
  1098. preserve[prop] = series[prop];
  1099. delete series[prop];
  1100. });
  1101. // Destroy the series and delete all properties. Reinsert all
  1102. // methods and properties from the new type prototype (#2270,
  1103. // #3719).
  1104. series.remove(false, null, false, true);
  1105. for (n in initialSeriesProto) { // eslint-disable-line guard-for-in
  1106. series[n] = void 0;
  1107. }
  1108. if (seriesTypes[newType || initialType]) {
  1109. extend(series, seriesTypes[newType || initialType].prototype);
  1110. }
  1111. else {
  1112. H.error(17, true, chart, { missingModuleFor: (newType || initialType) });
  1113. }
  1114. // Re-register groups (#3094) and other preserved properties
  1115. preserve.forEach(function (prop) {
  1116. series[prop] = preserve[prop];
  1117. });
  1118. series.init(chart, options);
  1119. // Remove particular elements of the points. Check `series.options`
  1120. // because we need to consider the options being set on plotOptions as
  1121. // well.
  1122. if (keepPoints && this.points) {
  1123. seriesOptions = series.options;
  1124. // What kind of elements to destroy
  1125. if (seriesOptions.visible === false) {
  1126. kinds.graphic = 1;
  1127. kinds.dataLabel = 1;
  1128. }
  1129. else if (!series._hasPointLabels) {
  1130. var marker = seriesOptions.marker, dataLabels = seriesOptions.dataLabels;
  1131. if (marker && (marker.enabled === false ||
  1132. 'symbol' in marker // #10870
  1133. )) {
  1134. kinds.graphic = 1;
  1135. }
  1136. if (dataLabels &&
  1137. dataLabels.enabled === false) {
  1138. kinds.dataLabel = 1;
  1139. }
  1140. }
  1141. this.points.forEach(function (point) {
  1142. if (point && point.series) {
  1143. point.resolveColor();
  1144. // Destroy elements in order to recreate based on updated
  1145. // series options.
  1146. if (Object.keys(kinds).length) {
  1147. point.destroyElements(kinds);
  1148. }
  1149. if (seriesOptions.showInLegend === false &&
  1150. point.legendItem) {
  1151. chart.legend.destroyItem(point);
  1152. }
  1153. }
  1154. }, this);
  1155. }
  1156. // Update the Z index of groups (#3380, #7397)
  1157. if (options.zIndex !== oldOptions.zIndex) {
  1158. groups.forEach(function (groupName) {
  1159. if (series[groupName]) {
  1160. series[groupName].attr({
  1161. zIndex: options.zIndex
  1162. });
  1163. }
  1164. });
  1165. }
  1166. series.initialType = initialType;
  1167. chart.linkSeries(); // Links are lost in series.remove (#3028)
  1168. fireEvent(this, 'afterUpdate');
  1169. if (pick(redraw, true)) {
  1170. chart.redraw(keepPoints ? void 0 : false);
  1171. }
  1172. },
  1173. /**
  1174. * Used from within series.update
  1175. *
  1176. * @private
  1177. * @function Highcharts.Series#setName
  1178. *
  1179. * @param {string} name
  1180. *
  1181. * @return {void}
  1182. */
  1183. setName: function (name) {
  1184. this.name = this.options.name = this.userOptions.name = name;
  1185. this.chart.isDirtyLegend = true;
  1186. }
  1187. });
  1188. // Extend the Axis.prototype for dynamic methods
  1189. extend(Axis.prototype, /** @lends Highcharts.Axis.prototype */ {
  1190. /**
  1191. * Update an axis object with a new set of options. The options are merged
  1192. * with the existing options, so only new or altered options need to be
  1193. * specified.
  1194. *
  1195. * @sample highcharts/members/axis-update/
  1196. * Axis update demo
  1197. *
  1198. * @function Highcharts.Axis#update
  1199. *
  1200. * @param {Highcharts.AxisOptions} options
  1201. * The new options that will be merged in with existing options on
  1202. * the axis.
  1203. *
  1204. * @param {boolean} [redraw=true]
  1205. * Whether to redraw the chart after the axis is altered. If doing
  1206. * more operations on the chart, it is a good idea to set redraw to
  1207. * false and call {@link Chart#redraw} after.
  1208. *
  1209. * @return {void}
  1210. */
  1211. update: function (options, redraw) {
  1212. var chart = this.chart, newEvents = ((options && options.events) || {});
  1213. options = merge(this.userOptions, options);
  1214. // Color Axis is not an array,
  1215. // This change is applied in the ColorAxis wrapper
  1216. if (chart.options[this.coll].indexOf) {
  1217. // Don't use this.options.index,
  1218. // StockChart has Axes in navigator too
  1219. chart.options[this.coll][chart.options[this.coll].indexOf(this.userOptions)] = options;
  1220. }
  1221. // Remove old events, if no new exist (#8161)
  1222. objectEach(chart.options[this.coll].events, function (fn, ev) {
  1223. if (typeof newEvents[ev] === 'undefined') {
  1224. newEvents[ev] = void 0;
  1225. }
  1226. });
  1227. this.destroy(true);
  1228. this.init(chart, extend(options, { events: newEvents }));
  1229. chart.isDirtyBox = true;
  1230. if (pick(redraw, true)) {
  1231. chart.redraw();
  1232. }
  1233. },
  1234. /**
  1235. * Remove the axis from the chart.
  1236. *
  1237. * @sample highcharts/members/chart-addaxis/
  1238. * Add and remove axes
  1239. *
  1240. * @function Highcharts.Axis#remove
  1241. *
  1242. * @param {boolean} [redraw=true]
  1243. * Whether to redraw the chart following the remove.
  1244. *
  1245. * @return {void}
  1246. */
  1247. remove: function (redraw) {
  1248. var chart = this.chart, key = this.coll, // xAxis or yAxis
  1249. axisSeries = this.series, i = axisSeries.length;
  1250. // Remove associated series (#2687)
  1251. while (i--) {
  1252. if (axisSeries[i]) {
  1253. axisSeries[i].remove(false);
  1254. }
  1255. }
  1256. // Remove the axis
  1257. erase(chart.axes, this);
  1258. erase(chart[key], this);
  1259. if (isArray(chart.options[key])) {
  1260. chart.options[key].splice(this.options.index, 1);
  1261. }
  1262. else { // color axis, #6488
  1263. delete chart.options[key];
  1264. }
  1265. chart[key].forEach(function (axis, i) {
  1266. // Re-index, #1706, #8075
  1267. axis.options.index = axis.userOptions.index = i;
  1268. });
  1269. this.destroy();
  1270. chart.isDirtyBox = true;
  1271. if (pick(redraw, true)) {
  1272. chart.redraw();
  1273. }
  1274. },
  1275. /**
  1276. * Update the axis title by options after render time.
  1277. *
  1278. * @sample highcharts/members/axis-settitle/
  1279. * Set a new Y axis title
  1280. *
  1281. * @function Highcharts.Axis#setTitle
  1282. *
  1283. * @param {Highcharts.AxisTitleOptions} titleOptions
  1284. * The additional title options.
  1285. *
  1286. * @param {boolean} [redraw=true]
  1287. * Whether to redraw the chart after setting the title.
  1288. *
  1289. * @return {void}
  1290. */
  1291. setTitle: function (titleOptions, redraw) {
  1292. this.update({ title: titleOptions }, redraw);
  1293. },
  1294. /**
  1295. * Set new axis categories and optionally redraw.
  1296. *
  1297. * @sample highcharts/members/axis-setcategories/
  1298. * Set categories by click on a button
  1299. *
  1300. * @function Highcharts.Axis#setCategories
  1301. *
  1302. * @param {Array<string>} categories
  1303. * The new categories.
  1304. *
  1305. * @param {boolean} [redraw=true]
  1306. * Whether to redraw the chart.
  1307. *
  1308. * @return {void}
  1309. */
  1310. setCategories: function (categories, redraw) {
  1311. this.update({ categories: categories }, redraw);
  1312. }
  1313. });