Dynamics.js 43 KB

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