Dynamics.js 51 KB

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