TreemapSeries.js 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560
  1. /* *
  2. *
  3. * (c) 2014-2020 Highsoft AS
  4. *
  5. * Authors: Jon Arild Nygard / Oystein Moseng
  6. *
  7. * License: www.highcharts.com/license
  8. *
  9. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  10. *
  11. * */
  12. 'use strict';
  13. import H from '../Core/Globals.js';
  14. import mixinTreeSeries from '../Mixins/TreeSeries.js';
  15. var getColor = mixinTreeSeries.getColor, getLevelOptions = mixinTreeSeries.getLevelOptions, updateRootId = mixinTreeSeries.updateRootId;
  16. import drawPointModule from '../Mixins/DrawPoint.js';
  17. var drawPoint = drawPointModule.drawPoint;
  18. import Color from '../Core/Color.js';
  19. var color = Color.parse;
  20. import LegendSymbolMixin from '../Mixins/LegendSymbol.js';
  21. import Point from '../Core/Series/Point.js';
  22. import U from '../Core/Utilities.js';
  23. var addEvent = U.addEvent, correctFloat = U.correctFloat, defined = U.defined, 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, seriesType = U.seriesType, stableSort = U.stableSort;
  24. import '../Core/Options.js';
  25. import '../Core/Series/Series.js';
  26. /* eslint-disable no-invalid-this */
  27. var AXIS_MAX = 100;
  28. var seriesTypes = H.seriesTypes, noop = H.noop,
  29. // @todo Similar to eachObject, this function is likely redundant
  30. isBoolean = function (x) {
  31. return typeof x === 'boolean';
  32. }, Series = H.Series,
  33. // @todo Similar to recursive, this function is likely redundant
  34. eachObject = function (list, func, context) {
  35. context = context || this;
  36. objectEach(list, function (val, key) {
  37. func.call(context, val, key, list);
  38. });
  39. },
  40. // @todo find correct name for this function.
  41. // @todo Similar to reduce, this function is likely redundant
  42. recursive = function (item, func, context) {
  43. var next;
  44. context = context || this;
  45. next = func.call(context, item);
  46. if (next !== false) {
  47. recursive(next, func, context);
  48. }
  49. }, treemapAxisDefaultValues = false;
  50. /* eslint-enable no-invalid-this */
  51. /**
  52. * @private
  53. * @class
  54. * @name Highcharts.seriesTypes.treemap
  55. *
  56. * @augments Highcharts.Series
  57. */
  58. seriesType('treemap', 'scatter'
  59. /**
  60. * A treemap displays hierarchical data using nested rectangles. The data
  61. * can be laid out in varying ways depending on options.
  62. *
  63. * @sample highcharts/demo/treemap-large-dataset/
  64. * Treemap
  65. *
  66. * @extends plotOptions.scatter
  67. * @excluding dragDrop, marker, jitter, dataSorting
  68. * @product highcharts
  69. * @requires modules/treemap
  70. * @optionparent plotOptions.treemap
  71. */
  72. , {
  73. /**
  74. * When enabled the user can click on a point which is a parent and
  75. * zoom in on its children. Deprecated and replaced by
  76. * [allowTraversingTree](#plotOptions.treemap.allowTraversingTree).
  77. *
  78. * @sample {highcharts} highcharts/plotoptions/treemap-allowdrilltonode/
  79. * Enabled
  80. *
  81. * @deprecated
  82. * @type {boolean}
  83. * @default false
  84. * @since 4.1.0
  85. * @product highcharts
  86. * @apioption plotOptions.treemap.allowDrillToNode
  87. */
  88. /**
  89. * When enabled the user can click on a point which is a parent and
  90. * zoom in on its children.
  91. *
  92. * @sample {highcharts} highcharts/plotoptions/treemap-allowtraversingtree/
  93. * Enabled
  94. *
  95. * @since 7.0.3
  96. * @product highcharts
  97. */
  98. allowTraversingTree: false,
  99. animationLimit: 250,
  100. /**
  101. * When the series contains less points than the crop threshold, all
  102. * points are drawn, event if the points fall outside the visible plot
  103. * area at the current zoom. The advantage of drawing all points
  104. * (including markers and columns), is that animation is performed on
  105. * updates. On the other hand, when the series contains more points than
  106. * the crop threshold, the series data is cropped to only contain points
  107. * that fall within the plot area. The advantage of cropping away
  108. * invisible points is to increase performance on large series.
  109. *
  110. * @type {number}
  111. * @default 300
  112. * @since 4.1.0
  113. * @product highcharts
  114. * @apioption plotOptions.treemap.cropThreshold
  115. */
  116. /**
  117. * Fires on a request for change of root node for the tree, before the
  118. * update is made. An event object is passed to the function, containing
  119. * additional properties `newRootId`, `previousRootId`, `redraw` and
  120. * `trigger`.
  121. *
  122. * @type {function}
  123. * @default undefined
  124. * @sample {highcharts} highcharts/plotoptions/treemap-events-setrootnode/
  125. * Alert update information on setRootNode event.
  126. * @since 7.0.3
  127. * @product highcharts
  128. * @apioption plotOptions.treemap.events.setRootNode
  129. */
  130. /**
  131. * This option decides if the user can interact with the parent nodes
  132. * or just the leaf nodes. When this option is undefined, it will be
  133. * true by default. However when allowTraversingTree is true, then it
  134. * will be false by default.
  135. *
  136. * @sample {highcharts} highcharts/plotoptions/treemap-interactbyleaf-false/
  137. * False
  138. * @sample {highcharts} highcharts/plotoptions/treemap-interactbyleaf-true-and-allowtraversingtree/
  139. * InteractByLeaf and allowTraversingTree is true
  140. *
  141. * @type {boolean}
  142. * @since 4.1.2
  143. * @product highcharts
  144. * @apioption plotOptions.treemap.interactByLeaf
  145. */
  146. /**
  147. * The sort index of the point inside the treemap level.
  148. *
  149. * @sample {highcharts} highcharts/plotoptions/treemap-sortindex/
  150. * Sort by years
  151. *
  152. * @type {number}
  153. * @since 4.1.10
  154. * @product highcharts
  155. * @apioption plotOptions.treemap.sortIndex
  156. */
  157. /**
  158. * A series specific or series type specific color set to apply instead
  159. * of the global [colors](#colors) when
  160. * [colorByPoint](#plotOptions.treemap.colorByPoint) is true.
  161. *
  162. * @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
  163. * @since 3.0
  164. * @product highcharts
  165. * @apioption plotOptions.treemap.colors
  166. */
  167. /**
  168. * Whether to display this series type or specific series item in the
  169. * legend.
  170. */
  171. showInLegend: false,
  172. /**
  173. * @ignore-option
  174. */
  175. marker: false,
  176. /**
  177. * When using automatic point colors pulled from the `options.colors`
  178. * collection, this option determines whether the chart should receive
  179. * one color per series or one color per point.
  180. *
  181. * @see [series colors](#plotOptions.treemap.colors)
  182. *
  183. * @since 2.0
  184. * @product highcharts
  185. * @apioption plotOptions.treemap.colorByPoint
  186. */
  187. colorByPoint: false,
  188. /**
  189. * @since 4.1.0
  190. */
  191. dataLabels: {
  192. defer: false,
  193. enabled: true,
  194. formatter: function () {
  195. var point = this && this.point ?
  196. this.point :
  197. {}, name = isString(point.name) ? point.name : '';
  198. return name;
  199. },
  200. inside: true,
  201. verticalAlign: 'middle'
  202. },
  203. tooltip: {
  204. headerFormat: '',
  205. pointFormat: '<b>{point.name}</b>: {point.value}<br/>'
  206. },
  207. /**
  208. * Whether to ignore hidden points when the layout algorithm runs.
  209. * If `false`, hidden points will leave open spaces.
  210. *
  211. * @since 5.0.8
  212. */
  213. ignoreHiddenPoint: true,
  214. /**
  215. * This option decides which algorithm is used for setting position
  216. * and dimensions of the points.
  217. *
  218. * @see [How to write your own algorithm](https://www.highcharts.com/docs/chart-and-series-types/treemap)
  219. *
  220. * @sample {highcharts} highcharts/plotoptions/treemap-layoutalgorithm-sliceanddice/
  221. * SliceAndDice by default
  222. * @sample {highcharts} highcharts/plotoptions/treemap-layoutalgorithm-stripes/
  223. * Stripes
  224. * @sample {highcharts} highcharts/plotoptions/treemap-layoutalgorithm-squarified/
  225. * Squarified
  226. * @sample {highcharts} highcharts/plotoptions/treemap-layoutalgorithm-strip/
  227. * Strip
  228. *
  229. * @since 4.1.0
  230. * @validvalue ["sliceAndDice", "stripes", "squarified", "strip"]
  231. */
  232. layoutAlgorithm: 'sliceAndDice',
  233. /**
  234. * Defines which direction the layout algorithm will start drawing.
  235. *
  236. * @since 4.1.0
  237. * @validvalue ["vertical", "horizontal"]
  238. */
  239. layoutStartingDirection: 'vertical',
  240. /**
  241. * Enabling this option will make the treemap alternate the drawing
  242. * direction between vertical and horizontal. The next levels starting
  243. * direction will always be the opposite of the previous.
  244. *
  245. * @sample {highcharts} highcharts/plotoptions/treemap-alternatestartingdirection-true/
  246. * Enabled
  247. *
  248. * @since 4.1.0
  249. */
  250. alternateStartingDirection: false,
  251. /**
  252. * Used together with the levels and allowTraversingTree options. When
  253. * set to false the first level visible to be level one, which is
  254. * dynamic when traversing the tree. Otherwise the level will be the
  255. * same as the tree structure.
  256. *
  257. * @since 4.1.0
  258. */
  259. levelIsConstant: true,
  260. /**
  261. * Options for the button appearing when drilling down in a treemap.
  262. * Deprecated and replaced by
  263. * [traverseUpButton](#plotOptions.treemap.traverseUpButton).
  264. *
  265. * @deprecated
  266. */
  267. drillUpButton: {
  268. /**
  269. * The position of the button.
  270. *
  271. * @deprecated
  272. */
  273. position: {
  274. /**
  275. * Vertical alignment of the button.
  276. *
  277. * @deprecated
  278. * @type {Highcharts.VerticalAlignValue}
  279. * @default top
  280. * @product highcharts
  281. * @apioption plotOptions.treemap.drillUpButton.position.verticalAlign
  282. */
  283. /**
  284. * Horizontal alignment of the button.
  285. *
  286. * @deprecated
  287. * @type {Highcharts.AlignValue}
  288. */
  289. align: 'right',
  290. /**
  291. * Horizontal offset of the button.
  292. *
  293. * @deprecated
  294. */
  295. x: -10,
  296. /**
  297. * Vertical offset of the button.
  298. *
  299. * @deprecated
  300. */
  301. y: 10
  302. }
  303. },
  304. /**
  305. * Options for the button appearing when traversing down in a treemap.
  306. */
  307. traverseUpButton: {
  308. /**
  309. * The position of the button.
  310. */
  311. position: {
  312. /**
  313. * Vertical alignment of the button.
  314. *
  315. * @type {Highcharts.VerticalAlignValue}
  316. * @default top
  317. * @product highcharts
  318. * @apioption plotOptions.treemap.traverseUpButton.position.verticalAlign
  319. */
  320. /**
  321. * Horizontal alignment of the button.
  322. *
  323. * @type {Highcharts.AlignValue}
  324. */
  325. align: 'right',
  326. /**
  327. * Horizontal offset of the button.
  328. */
  329. x: -10,
  330. /**
  331. * Vertical offset of the button.
  332. */
  333. y: 10
  334. }
  335. },
  336. /**
  337. * Set options on specific levels. Takes precedence over series options,
  338. * but not point options.
  339. *
  340. * @sample {highcharts} highcharts/plotoptions/treemap-levels/
  341. * Styling dataLabels and borders
  342. * @sample {highcharts} highcharts/demo/treemap-with-levels/
  343. * Different layoutAlgorithm
  344. *
  345. * @type {Array<*>}
  346. * @since 4.1.0
  347. * @product highcharts
  348. * @apioption plotOptions.treemap.levels
  349. */
  350. /**
  351. * Can set a `borderColor` on all points which lies on the same level.
  352. *
  353. * @type {Highcharts.ColorString}
  354. * @since 4.1.0
  355. * @product highcharts
  356. * @apioption plotOptions.treemap.levels.borderColor
  357. */
  358. /**
  359. * Set the dash style of the border of all the point which lies on the
  360. * level. See
  361. * [plotOptions.scatter.dashStyle](#plotoptions.scatter.dashstyle)
  362. * for possible options.
  363. *
  364. * @type {Highcharts.DashStyleValue}
  365. * @since 4.1.0
  366. * @product highcharts
  367. * @apioption plotOptions.treemap.levels.borderDashStyle
  368. */
  369. /**
  370. * Can set the borderWidth on all points which lies on the same level.
  371. *
  372. * @type {number}
  373. * @since 4.1.0
  374. * @product highcharts
  375. * @apioption plotOptions.treemap.levels.borderWidth
  376. */
  377. /**
  378. * Can set a color on all points which lies on the same level.
  379. *
  380. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  381. * @since 4.1.0
  382. * @product highcharts
  383. * @apioption plotOptions.treemap.levels.color
  384. */
  385. /**
  386. * A configuration object to define how the color of a child varies from
  387. * the parent's color. The variation is distributed among the children
  388. * of node. For example when setting brightness, the brightness change
  389. * will range from the parent's original brightness on the first child,
  390. * to the amount set in the `to` setting on the last node. This allows a
  391. * gradient-like color scheme that sets children out from each other
  392. * while highlighting the grouping on treemaps and sectors on sunburst
  393. * charts.
  394. *
  395. * @sample highcharts/demo/sunburst/
  396. * Sunburst with color variation
  397. *
  398. * @since 6.0.0
  399. * @product highcharts
  400. * @apioption plotOptions.treemap.levels.colorVariation
  401. */
  402. /**
  403. * The key of a color variation. Currently supports `brightness` only.
  404. *
  405. * @type {string}
  406. * @since 6.0.0
  407. * @product highcharts
  408. * @validvalue ["brightness"]
  409. * @apioption plotOptions.treemap.levels.colorVariation.key
  410. */
  411. /**
  412. * The ending value of a color variation. The last sibling will receive
  413. * this value.
  414. *
  415. * @type {number}
  416. * @since 6.0.0
  417. * @product highcharts
  418. * @apioption plotOptions.treemap.levels.colorVariation.to
  419. */
  420. /**
  421. * Can set the options of dataLabels on each point which lies on the
  422. * level.
  423. * [plotOptions.treemap.dataLabels](#plotOptions.treemap.dataLabels) for
  424. * possible values.
  425. *
  426. * @extends plotOptions.treemap.dataLabels
  427. * @since 4.1.0
  428. * @product highcharts
  429. * @apioption plotOptions.treemap.levels.dataLabels
  430. */
  431. /**
  432. * Can set the layoutAlgorithm option on a specific level.
  433. *
  434. * @type {string}
  435. * @since 4.1.0
  436. * @product highcharts
  437. * @validvalue ["sliceAndDice", "stripes", "squarified", "strip"]
  438. * @apioption plotOptions.treemap.levels.layoutAlgorithm
  439. */
  440. /**
  441. * Can set the layoutStartingDirection option on a specific level.
  442. *
  443. * @type {string}
  444. * @since 4.1.0
  445. * @product highcharts
  446. * @validvalue ["vertical", "horizontal"]
  447. * @apioption plotOptions.treemap.levels.layoutStartingDirection
  448. */
  449. /**
  450. * Decides which level takes effect from the options set in the levels
  451. * object.
  452. *
  453. * @sample {highcharts} highcharts/plotoptions/treemap-levels/
  454. * Styling of both levels
  455. *
  456. * @type {number}
  457. * @since 4.1.0
  458. * @product highcharts
  459. * @apioption plotOptions.treemap.levels.level
  460. */
  461. // Presentational options
  462. /**
  463. * The color of the border surrounding each tree map item.
  464. *
  465. * @type {Highcharts.ColorString}
  466. */
  467. borderColor: '#e6e6e6',
  468. /**
  469. * The width of the border surrounding each tree map item.
  470. */
  471. borderWidth: 1,
  472. colorKey: 'colorValue',
  473. /**
  474. * The opacity of a point in treemap. When a point has children, the
  475. * visibility of the children is determined by the opacity.
  476. *
  477. * @since 4.2.4
  478. */
  479. opacity: 0.15,
  480. /**
  481. * A wrapper object for all the series options in specific states.
  482. *
  483. * @extends plotOptions.heatmap.states
  484. */
  485. states: {
  486. /**
  487. * Options for the hovered series
  488. *
  489. * @extends plotOptions.heatmap.states.hover
  490. * @excluding halo
  491. */
  492. hover: {
  493. /**
  494. * The border color for the hovered state.
  495. */
  496. borderColor: '#999999',
  497. /**
  498. * Brightness for the hovered point. Defaults to 0 if the
  499. * heatmap series is loaded first, otherwise 0.1.
  500. *
  501. * @type {number}
  502. * @default undefined
  503. */
  504. brightness: seriesTypes.heatmap ? 0 : 0.1,
  505. /**
  506. * @extends plotOptions.heatmap.states.hover.halo
  507. */
  508. halo: false,
  509. /**
  510. * The opacity of a point in treemap. When a point has children,
  511. * the visibility of the children is determined by the opacity.
  512. *
  513. * @since 4.2.4
  514. */
  515. opacity: 0.75,
  516. /**
  517. * The shadow option for hovered state.
  518. */
  519. shadow: false
  520. }
  521. }
  522. // Prototype members
  523. }, {
  524. pointArrayMap: ['value'],
  525. directTouch: true,
  526. optionalAxis: 'colorAxis',
  527. getSymbol: noop,
  528. parallelArrays: ['x', 'y', 'value', 'colorValue'],
  529. colorKey: 'colorValue',
  530. trackerGroups: ['group', 'dataLabelsGroup'],
  531. /* eslint-disable no-invalid-this, valid-jsdoc */
  532. /**
  533. * Creates an object map from parent id to childrens index.
  534. *
  535. * @private
  536. * @function Highcharts.Series#getListOfParents
  537. *
  538. * @param {Highcharts.SeriesTreemapDataOptions} [data]
  539. * List of points set in options.
  540. *
  541. * @param {Array<string>} [existingIds]
  542. * List of all point ids.
  543. *
  544. * @return {object}
  545. * Map from parent id to children index in data.
  546. */
  547. getListOfParents: function (data, existingIds) {
  548. var arr = isArray(data) ? data : [], ids = isArray(existingIds) ? existingIds : [], listOfParents = arr.reduce(function (prev, curr, i) {
  549. var parent = pick(curr.parent, '');
  550. if (typeof prev[parent] === 'undefined') {
  551. prev[parent] = [];
  552. }
  553. prev[parent].push(i);
  554. return prev;
  555. }, {
  556. '': [] // Root of tree
  557. });
  558. // If parent does not exist, hoist parent to root of tree.
  559. eachObject(listOfParents, function (children, parent, list) {
  560. if ((parent !== '') && (ids.indexOf(parent) === -1)) {
  561. children.forEach(function (child) {
  562. list[''].push(child);
  563. });
  564. delete list[parent];
  565. }
  566. });
  567. return listOfParents;
  568. },
  569. // Creates a tree structured object from the series points
  570. getTree: function () {
  571. var series = this, allIds = this.data.map(function (d) {
  572. return d.id;
  573. }), parentList = series.getListOfParents(this.data, allIds);
  574. series.nodeMap = [];
  575. return series.buildNode('', -1, 0, parentList, null);
  576. },
  577. // Define hasData function for non-cartesian series.
  578. // Returns true if the series has points at all.
  579. hasData: function () {
  580. return !!this.processedXData.length; // != 0
  581. },
  582. init: function (chart, options) {
  583. var series = this, colorMapSeriesMixin = H.colorMapSeriesMixin, setOptionsEvent;
  584. // If color series logic is loaded, add some properties
  585. if (colorMapSeriesMixin) {
  586. this.colorAttribs = colorMapSeriesMixin.colorAttribs;
  587. }
  588. setOptionsEvent = addEvent(series, 'setOptions', function (event) {
  589. var options = event.userOptions;
  590. if (defined(options.allowDrillToNode) &&
  591. !defined(options.allowTraversingTree)) {
  592. options.allowTraversingTree = options.allowDrillToNode;
  593. delete options.allowDrillToNode;
  594. }
  595. if (defined(options.drillUpButton) &&
  596. !defined(options.traverseUpButton)) {
  597. options.traverseUpButton = options.drillUpButton;
  598. delete options.drillUpButton;
  599. }
  600. });
  601. Series.prototype.init.call(series, chart, options);
  602. // Treemap's opacity is a different option from other series
  603. delete series.opacity;
  604. // Handle deprecated options.
  605. series.eventsToUnbind.push(setOptionsEvent);
  606. if (series.options.allowTraversingTree) {
  607. series.eventsToUnbind.push(addEvent(series, 'click', series.onClickDrillToNode));
  608. }
  609. },
  610. buildNode: function (id, i, level, list, parent) {
  611. var series = this, children = [], point = series.points[i], height = 0, node, child;
  612. // Actions
  613. ((list[id] || [])).forEach(function (i) {
  614. child = series.buildNode(series.points[i].id, i, (level + 1), list, id);
  615. height = Math.max(child.height + 1, height);
  616. children.push(child);
  617. });
  618. node = {
  619. id: id,
  620. i: i,
  621. children: children,
  622. height: height,
  623. level: level,
  624. parent: parent,
  625. visible: false // @todo move this to better location
  626. };
  627. series.nodeMap[node.id] = node;
  628. if (point) {
  629. point.node = node;
  630. }
  631. return node;
  632. },
  633. setTreeValues: function (tree) {
  634. var series = this, options = series.options, idRoot = series.rootNode, mapIdToNode = series.nodeMap, nodeRoot = mapIdToNode[idRoot], levelIsConstant = (isBoolean(options.levelIsConstant) ?
  635. options.levelIsConstant :
  636. true), childrenTotal = 0, children = [], val, point = series.points[tree.i];
  637. // First give the children some values
  638. tree.children.forEach(function (child) {
  639. child = series.setTreeValues(child);
  640. children.push(child);
  641. if (!child.ignore) {
  642. childrenTotal += child.val;
  643. }
  644. });
  645. // Sort the children
  646. stableSort(children, function (a, b) {
  647. return a.sortIndex - b.sortIndex;
  648. });
  649. // Set the values
  650. val = pick(point && point.options.value, childrenTotal);
  651. if (point) {
  652. point.value = val;
  653. }
  654. extend(tree, {
  655. children: children,
  656. childrenTotal: childrenTotal,
  657. // Ignore this node if point is not visible
  658. ignore: !(pick(point && point.visible, true) && (val > 0)),
  659. isLeaf: tree.visible && !childrenTotal,
  660. levelDynamic: (tree.level - (levelIsConstant ? 0 : nodeRoot.level)),
  661. name: pick(point && point.name, ''),
  662. sortIndex: pick(point && point.sortIndex, -val),
  663. val: val
  664. });
  665. return tree;
  666. },
  667. /**
  668. * Recursive function which calculates the area for all children of a
  669. * node.
  670. *
  671. * @private
  672. * @function Highcharts.Series#calculateChildrenAreas
  673. *
  674. * @param {object} node
  675. * The node which is parent to the children.
  676. *
  677. * @param {object} area
  678. * The rectangular area of the parent.
  679. */
  680. calculateChildrenAreas: function (parent, area) {
  681. var series = this, options = series.options, mapOptionsToLevel = series.mapOptionsToLevel, level = mapOptionsToLevel[parent.level + 1], algorithm = pick((series[(level && level.layoutAlgorithm)] &&
  682. level.layoutAlgorithm), options.layoutAlgorithm), alternate = options.alternateStartingDirection, childrenValues = [], children;
  683. // Collect all children which should be included
  684. children = parent.children.filter(function (n) {
  685. return !n.ignore;
  686. });
  687. if (level && level.layoutStartingDirection) {
  688. area.direction = level.layoutStartingDirection === 'vertical' ?
  689. 0 :
  690. 1;
  691. }
  692. childrenValues = series[algorithm](area, children);
  693. children.forEach(function (child, index) {
  694. var values = childrenValues[index];
  695. child.values = merge(values, {
  696. val: child.childrenTotal,
  697. direction: (alternate ? 1 - area.direction : area.direction)
  698. });
  699. child.pointValues = merge(values, {
  700. x: (values.x / series.axisRatio),
  701. // Flip y-values to avoid visual regression with csvCoord in
  702. // Axis.translate at setPointValues. #12488
  703. y: AXIS_MAX - values.y - values.height,
  704. width: (values.width / series.axisRatio)
  705. });
  706. // If node has children, then call method recursively
  707. if (child.children.length) {
  708. series.calculateChildrenAreas(child, child.values);
  709. }
  710. });
  711. },
  712. setPointValues: function () {
  713. var series = this;
  714. var points = series.points, xAxis = series.xAxis, yAxis = series.yAxis;
  715. var styledMode = series.chart.styledMode;
  716. // Get the crisp correction in classic mode. For this to work in
  717. // styled mode, we would need to first add the shape (without x,
  718. // y, width and height), then read the rendered stroke width
  719. // using point.graphic.strokeWidth(), then modify and apply the
  720. // shapeArgs. This applies also to column series, but the
  721. // downside is performance and code complexity.
  722. var getCrispCorrection = function (point) { return (styledMode ?
  723. 0 :
  724. ((series.pointAttribs(point)['stroke-width'] || 0) % 2) / 2); };
  725. points.forEach(function (point) {
  726. var _a = point.node, values = _a.pointValues, visible = _a.visible;
  727. // Points which is ignored, have no values.
  728. if (values && visible) {
  729. var height = values.height, width = values.width, x = values.x, y = values.y;
  730. var crispCorr = getCrispCorrection(point);
  731. var x1 = Math.round(xAxis.toPixels(x, true)) - crispCorr;
  732. var x2 = Math.round(xAxis.toPixels(x + width, true)) - crispCorr;
  733. var y1 = Math.round(yAxis.toPixels(y, true)) - crispCorr;
  734. var y2 = Math.round(yAxis.toPixels(y + height, true)) - crispCorr;
  735. // Set point values
  736. point.shapeArgs = {
  737. x: Math.min(x1, x2),
  738. y: Math.min(y1, y2),
  739. width: Math.abs(x2 - x1),
  740. height: Math.abs(y2 - y1)
  741. };
  742. point.plotX =
  743. point.shapeArgs.x + (point.shapeArgs.width / 2);
  744. point.plotY =
  745. point.shapeArgs.y + (point.shapeArgs.height / 2);
  746. }
  747. else {
  748. // Reset visibility
  749. delete point.plotX;
  750. delete point.plotY;
  751. }
  752. });
  753. },
  754. // Set the node's color recursively, from the parent down.
  755. setColorRecursive: function (node, parentColor, colorIndex, index, siblings) {
  756. var series = this, chart = series && series.chart, colors = chart && chart.options && chart.options.colors, colorInfo, point;
  757. if (node) {
  758. colorInfo = getColor(node, {
  759. colors: colors,
  760. index: index,
  761. mapOptionsToLevel: series.mapOptionsToLevel,
  762. parentColor: parentColor,
  763. parentColorIndex: colorIndex,
  764. series: series,
  765. siblings: siblings
  766. });
  767. point = series.points[node.i];
  768. if (point) {
  769. point.color = colorInfo.color;
  770. point.colorIndex = colorInfo.colorIndex;
  771. }
  772. // Do it all again with the children
  773. (node.children || []).forEach(function (child, i) {
  774. series.setColorRecursive(child, colorInfo.color, colorInfo.colorIndex, i, node.children.length);
  775. });
  776. }
  777. },
  778. algorithmGroup: function (h, w, d, p) {
  779. this.height = h;
  780. this.width = w;
  781. this.plot = p;
  782. this.direction = d;
  783. this.startDirection = d;
  784. this.total = 0;
  785. this.nW = 0;
  786. this.lW = 0;
  787. this.nH = 0;
  788. this.lH = 0;
  789. this.elArr = [];
  790. this.lP = {
  791. total: 0,
  792. lH: 0,
  793. nH: 0,
  794. lW: 0,
  795. nW: 0,
  796. nR: 0,
  797. lR: 0,
  798. aspectRatio: function (w, h) {
  799. return Math.max((w / h), (h / w));
  800. }
  801. };
  802. this.addElement = function (el) {
  803. this.lP.total = this.elArr[this.elArr.length - 1];
  804. this.total = this.total + el;
  805. if (this.direction === 0) {
  806. // Calculate last point old aspect ratio
  807. this.lW = this.nW;
  808. this.lP.lH = this.lP.total / this.lW;
  809. this.lP.lR = this.lP.aspectRatio(this.lW, this.lP.lH);
  810. // Calculate last point new aspect ratio
  811. this.nW = this.total / this.height;
  812. this.lP.nH = this.lP.total / this.nW;
  813. this.lP.nR = this.lP.aspectRatio(this.nW, this.lP.nH);
  814. }
  815. else {
  816. // Calculate last point old aspect ratio
  817. this.lH = this.nH;
  818. this.lP.lW = this.lP.total / this.lH;
  819. this.lP.lR = this.lP.aspectRatio(this.lP.lW, this.lH);
  820. // Calculate last point new aspect ratio
  821. this.nH = this.total / this.width;
  822. this.lP.nW = this.lP.total / this.nH;
  823. this.lP.nR = this.lP.aspectRatio(this.lP.nW, this.nH);
  824. }
  825. this.elArr.push(el);
  826. };
  827. this.reset = function () {
  828. this.nW = 0;
  829. this.lW = 0;
  830. this.elArr = [];
  831. this.total = 0;
  832. };
  833. },
  834. algorithmCalcPoints: function (directionChange, last, group, childrenArea) {
  835. var pX, pY, pW, pH, gW = group.lW, gH = group.lH, plot = group.plot, keep, i = 0, end = group.elArr.length - 1;
  836. if (last) {
  837. gW = group.nW;
  838. gH = group.nH;
  839. }
  840. else {
  841. keep = group.elArr[group.elArr.length - 1];
  842. }
  843. group.elArr.forEach(function (p) {
  844. if (last || (i < end)) {
  845. if (group.direction === 0) {
  846. pX = plot.x;
  847. pY = plot.y;
  848. pW = gW;
  849. pH = p / pW;
  850. }
  851. else {
  852. pX = plot.x;
  853. pY = plot.y;
  854. pH = gH;
  855. pW = p / pH;
  856. }
  857. childrenArea.push({
  858. x: pX,
  859. y: pY,
  860. width: pW,
  861. height: correctFloat(pH)
  862. });
  863. if (group.direction === 0) {
  864. plot.y = plot.y + pH;
  865. }
  866. else {
  867. plot.x = plot.x + pW;
  868. }
  869. }
  870. i = i + 1;
  871. });
  872. // Reset variables
  873. group.reset();
  874. if (group.direction === 0) {
  875. group.width = group.width - gW;
  876. }
  877. else {
  878. group.height = group.height - gH;
  879. }
  880. plot.y = plot.parent.y + (plot.parent.height - group.height);
  881. plot.x = plot.parent.x + (plot.parent.width - group.width);
  882. if (directionChange) {
  883. group.direction = 1 - group.direction;
  884. }
  885. // If not last, then add uncalculated element
  886. if (!last) {
  887. group.addElement(keep);
  888. }
  889. },
  890. algorithmLowAspectRatio: function (directionChange, parent, children) {
  891. var childrenArea = [], series = this, pTot, plot = {
  892. x: parent.x,
  893. y: parent.y,
  894. parent: parent
  895. }, direction = parent.direction, i = 0, end = children.length - 1, group = new this.algorithmGroup(// eslint-disable-line new-cap
  896. parent.height, parent.width, direction, plot);
  897. // Loop through and calculate all areas
  898. children.forEach(function (child) {
  899. pTot =
  900. (parent.width * parent.height) * (child.val / parent.val);
  901. group.addElement(pTot);
  902. if (group.lP.nR > group.lP.lR) {
  903. series.algorithmCalcPoints(directionChange, false, group, childrenArea, plot // @todo no supported
  904. );
  905. }
  906. // If last child, then calculate all remaining areas
  907. if (i === end) {
  908. series.algorithmCalcPoints(directionChange, true, group, childrenArea, plot // @todo not supported
  909. );
  910. }
  911. i = i + 1;
  912. });
  913. return childrenArea;
  914. },
  915. algorithmFill: function (directionChange, parent, children) {
  916. var childrenArea = [], pTot, direction = parent.direction, x = parent.x, y = parent.y, width = parent.width, height = parent.height, pX, pY, pW, pH;
  917. children.forEach(function (child) {
  918. pTot =
  919. (parent.width * parent.height) * (child.val / parent.val);
  920. pX = x;
  921. pY = y;
  922. if (direction === 0) {
  923. pH = height;
  924. pW = pTot / pH;
  925. width = width - pW;
  926. x = x + pW;
  927. }
  928. else {
  929. pW = width;
  930. pH = pTot / pW;
  931. height = height - pH;
  932. y = y + pH;
  933. }
  934. childrenArea.push({
  935. x: pX,
  936. y: pY,
  937. width: pW,
  938. height: pH
  939. });
  940. if (directionChange) {
  941. direction = 1 - direction;
  942. }
  943. });
  944. return childrenArea;
  945. },
  946. strip: function (parent, children) {
  947. return this.algorithmLowAspectRatio(false, parent, children);
  948. },
  949. squarified: function (parent, children) {
  950. return this.algorithmLowAspectRatio(true, parent, children);
  951. },
  952. sliceAndDice: function (parent, children) {
  953. return this.algorithmFill(true, parent, children);
  954. },
  955. stripes: function (parent, children) {
  956. return this.algorithmFill(false, parent, children);
  957. },
  958. translate: function () {
  959. var series = this, options = series.options,
  960. // NOTE: updateRootId modifies series.
  961. rootId = updateRootId(series), rootNode, pointValues, seriesArea, tree, val;
  962. // Call prototype function
  963. Series.prototype.translate.call(series);
  964. // @todo Only if series.isDirtyData is true
  965. tree = series.tree = series.getTree();
  966. rootNode = series.nodeMap[rootId];
  967. series.renderTraverseUpButton(rootId);
  968. series.mapOptionsToLevel = getLevelOptions({
  969. from: rootNode.level + 1,
  970. levels: options.levels,
  971. to: tree.height,
  972. defaults: {
  973. levelIsConstant: series.options.levelIsConstant,
  974. colorByPoint: options.colorByPoint
  975. }
  976. });
  977. if (rootId !== '' &&
  978. (!rootNode || !rootNode.children.length)) {
  979. series.setRootNode('', false);
  980. rootId = series.rootNode;
  981. rootNode = series.nodeMap[rootId];
  982. }
  983. // Parents of the root node is by default visible
  984. recursive(series.nodeMap[series.rootNode], function (node) {
  985. var next = false, p = node.parent;
  986. node.visible = true;
  987. if (p || p === '') {
  988. next = series.nodeMap[p];
  989. }
  990. return next;
  991. });
  992. // Children of the root node is by default visible
  993. recursive(series.nodeMap[series.rootNode].children, function (children) {
  994. var next = false;
  995. children.forEach(function (child) {
  996. child.visible = true;
  997. if (child.children.length) {
  998. next = (next || []).concat(child.children);
  999. }
  1000. });
  1001. return next;
  1002. });
  1003. series.setTreeValues(tree);
  1004. // Calculate plotting values.
  1005. series.axisRatio = (series.xAxis.len / series.yAxis.len);
  1006. series.nodeMap[''].pointValues = pointValues = {
  1007. x: 0,
  1008. y: 0,
  1009. width: AXIS_MAX,
  1010. height: AXIS_MAX
  1011. };
  1012. series.nodeMap[''].values = seriesArea = merge(pointValues, {
  1013. width: (pointValues.width * series.axisRatio),
  1014. direction: (options.layoutStartingDirection === 'vertical' ? 0 : 1),
  1015. val: tree.val
  1016. });
  1017. series.calculateChildrenAreas(tree, seriesArea);
  1018. // Logic for point colors
  1019. if (!series.colorAxis &&
  1020. !options.colorByPoint) {
  1021. series.setColorRecursive(series.tree);
  1022. }
  1023. // Update axis extremes according to the root node.
  1024. if (options.allowTraversingTree) {
  1025. val = rootNode.pointValues;
  1026. series.xAxis.setExtremes(val.x, val.x + val.width, false);
  1027. series.yAxis.setExtremes(val.y, val.y + val.height, false);
  1028. series.xAxis.setScale();
  1029. series.yAxis.setScale();
  1030. }
  1031. // Assign values to points.
  1032. series.setPointValues();
  1033. },
  1034. /**
  1035. * Extend drawDataLabels with logic to handle custom options related to
  1036. * the treemap series:
  1037. *
  1038. * - Points which is not a leaf node, has dataLabels disabled by
  1039. * default.
  1040. *
  1041. * - Options set on series.levels is merged in.
  1042. *
  1043. * - Width of the dataLabel is set to match the width of the point
  1044. * shape.
  1045. *
  1046. * @private
  1047. * @function Highcharts.Series#drawDataLabels
  1048. */
  1049. drawDataLabels: function () {
  1050. var series = this, mapOptionsToLevel = series.mapOptionsToLevel, points = series.points.filter(function (n) {
  1051. return n.node.visible;
  1052. }), options, level;
  1053. points.forEach(function (point) {
  1054. level = mapOptionsToLevel[point.node.level];
  1055. // Set options to new object to avoid problems with scope
  1056. options = { style: {} };
  1057. // If not a leaf, then label should be disabled as default
  1058. if (!point.node.isLeaf) {
  1059. options.enabled = false;
  1060. }
  1061. // If options for level exists, include them as well
  1062. if (level && level.dataLabels) {
  1063. options = merge(options, level.dataLabels);
  1064. series._hasPointLabels = true;
  1065. }
  1066. // Set dataLabel width to the width of the point shape.
  1067. if (point.shapeArgs) {
  1068. options.style.width = point.shapeArgs.width;
  1069. if (point.dataLabel) {
  1070. point.dataLabel.css({
  1071. width: point.shapeArgs.width + 'px'
  1072. });
  1073. }
  1074. }
  1075. // Merge custom options with point options
  1076. point.dlOptions = merge(options, point.options.dataLabels);
  1077. });
  1078. Series.prototype.drawDataLabels.call(this);
  1079. },
  1080. // Over the alignment method by setting z index
  1081. alignDataLabel: function (point, dataLabel, labelOptions) {
  1082. var style = labelOptions.style;
  1083. // #8160: Prevent the label from exceeding the point's
  1084. // boundaries in treemaps by applying ellipsis overflow.
  1085. // The issue was happening when datalabel's text contained a
  1086. // long sequence of characters without a whitespace.
  1087. if (!defined(style.textOverflow) &&
  1088. dataLabel.text &&
  1089. dataLabel.getBBox().width > dataLabel.text.textWidth) {
  1090. dataLabel.css({
  1091. textOverflow: 'ellipsis',
  1092. // unit (px) is required when useHTML is true
  1093. width: style.width += 'px'
  1094. });
  1095. }
  1096. seriesTypes.column.prototype.alignDataLabel.apply(this, arguments);
  1097. if (point.dataLabel) {
  1098. // point.node.zIndex could be undefined (#6956)
  1099. point.dataLabel.attr({ zIndex: (point.node.zIndex || 0) + 1 });
  1100. }
  1101. },
  1102. // Get presentational attributes
  1103. pointAttribs: function (point, state) {
  1104. var series = this, mapOptionsToLevel = (isObject(series.mapOptionsToLevel) ?
  1105. series.mapOptionsToLevel :
  1106. {}), level = point && mapOptionsToLevel[point.node.level] || {}, options = this.options, attr, stateOptions = (state && options.states[state]) || {}, className = (point && point.getClassName()) || '', opacity;
  1107. // Set attributes by precedence. Point trumps level trumps series.
  1108. // Stroke width uses pick because it can be 0.
  1109. attr = {
  1110. 'stroke': (point && point.borderColor) ||
  1111. level.borderColor ||
  1112. stateOptions.borderColor ||
  1113. options.borderColor,
  1114. 'stroke-width': pick(point && point.borderWidth, level.borderWidth, stateOptions.borderWidth, options.borderWidth),
  1115. 'dashstyle': (point && point.borderDashStyle) ||
  1116. level.borderDashStyle ||
  1117. stateOptions.borderDashStyle ||
  1118. options.borderDashStyle,
  1119. 'fill': (point && point.color) || this.color
  1120. };
  1121. // Hide levels above the current view
  1122. if (className.indexOf('highcharts-above-level') !== -1) {
  1123. attr.fill = 'none';
  1124. attr['stroke-width'] = 0;
  1125. // Nodes with children that accept interaction
  1126. }
  1127. else if (className.indexOf('highcharts-internal-node-interactive') !== -1) {
  1128. opacity = pick(stateOptions.opacity, options.opacity);
  1129. attr.fill = color(attr.fill).setOpacity(opacity).get();
  1130. attr.cursor = 'pointer';
  1131. // Hide nodes that have children
  1132. }
  1133. else if (className.indexOf('highcharts-internal-node') !== -1) {
  1134. attr.fill = 'none';
  1135. }
  1136. else if (state) {
  1137. // Brighten and hoist the hover nodes
  1138. attr.fill = color(attr.fill)
  1139. .brighten(stateOptions.brightness)
  1140. .get();
  1141. }
  1142. return attr;
  1143. },
  1144. // Override drawPoints
  1145. drawPoints: function () {
  1146. var series = this, chart = series.chart, renderer = chart.renderer, points = series.points, styledMode = chart.styledMode, options = series.options, shadow = styledMode ? {} : options.shadow, borderRadius = options.borderRadius, withinAnimationLimit = chart.pointCount < options.animationLimit, allowTraversingTree = options.allowTraversingTree;
  1147. points.forEach(function (point) {
  1148. var levelDynamic = point.node.levelDynamic, animate = {}, attr = {}, css = {}, groupKey = 'level-group-' + levelDynamic, hasGraphic = !!point.graphic, shouldAnimate = withinAnimationLimit && hasGraphic, shapeArgs = point.shapeArgs;
  1149. // Don't bother with calculate styling if the point is not drawn
  1150. if (point.shouldDraw()) {
  1151. if (borderRadius) {
  1152. attr.r = borderRadius;
  1153. }
  1154. merge(true, // Extend object
  1155. // Which object to extend
  1156. shouldAnimate ? animate : attr,
  1157. // Add shapeArgs to animate/attr if graphic exists
  1158. hasGraphic ? shapeArgs : {},
  1159. // Add style attribs if !styleMode
  1160. styledMode ?
  1161. {} :
  1162. series.pointAttribs(point, (point.selected && 'select')));
  1163. // In styled mode apply point.color. Use CSS, otherwise the
  1164. // fill used in the style sheet will take precedence over
  1165. // the fill attribute.
  1166. if (series.colorAttribs && styledMode) {
  1167. // Heatmap is loaded
  1168. extend(css, series.colorAttribs(point));
  1169. }
  1170. if (!series[groupKey]) {
  1171. series[groupKey] = renderer.g(groupKey)
  1172. .attr({
  1173. // @todo Set the zIndex based upon the number of
  1174. // levels, instead of using 1000
  1175. zIndex: 1000 - levelDynamic
  1176. })
  1177. .add(series.group);
  1178. series[groupKey].survive = true;
  1179. }
  1180. }
  1181. // Draw the point
  1182. point.draw({
  1183. animatableAttribs: animate,
  1184. attribs: attr,
  1185. css: css,
  1186. group: series[groupKey],
  1187. renderer: renderer,
  1188. shadow: shadow,
  1189. shapeArgs: shapeArgs,
  1190. shapeType: 'rect'
  1191. });
  1192. // If setRootNode is allowed, set a point cursor on clickables &
  1193. // add drillId to point
  1194. if (allowTraversingTree && point.graphic) {
  1195. point.drillId = options.interactByLeaf ?
  1196. series.drillToByLeaf(point) :
  1197. series.drillToByGroup(point);
  1198. }
  1199. });
  1200. },
  1201. // Add drilling on the suitable points
  1202. onClickDrillToNode: function (event) {
  1203. var series = this, point = event.point, drillId = point && point.drillId;
  1204. // If a drill id is returned, add click event and cursor.
  1205. if (isString(drillId) &&
  1206. (series.isDrillAllowed ? series.isDrillAllowed(drillId) : true)) {
  1207. point.setState(''); // Remove hover
  1208. series.setRootNode(drillId, true, { trigger: 'click' });
  1209. }
  1210. },
  1211. /**
  1212. * Finds the drill id for a parent node. Returns false if point should
  1213. * not have a click event.
  1214. *
  1215. * @private
  1216. * @function Highcharts.Series#drillToByGroup
  1217. *
  1218. * @param {Highcharts.Point} point
  1219. *
  1220. * @return {boolean|string}
  1221. * Drill to id or false when point should not have a click
  1222. * event.
  1223. */
  1224. drillToByGroup: function (point) {
  1225. var series = this, drillId = false;
  1226. if ((point.node.level - series.nodeMap[series.rootNode].level) ===
  1227. 1 &&
  1228. !point.node.isLeaf) {
  1229. drillId = point.id;
  1230. }
  1231. return drillId;
  1232. },
  1233. /**
  1234. * Finds the drill id for a leaf node. Returns false if point should not
  1235. * have a click event
  1236. *
  1237. * @private
  1238. * @function Highcharts.Series#drillToByLeaf
  1239. *
  1240. * @param {Highcharts.Point} point
  1241. *
  1242. * @return {boolean|string}
  1243. * Drill to id or false when point should not have a click
  1244. * event.
  1245. */
  1246. drillToByLeaf: function (point) {
  1247. var series = this, drillId = false, nodeParent;
  1248. if ((point.node.parent !== series.rootNode) &&
  1249. point.node.isLeaf) {
  1250. nodeParent = point.node;
  1251. while (!drillId) {
  1252. nodeParent = series.nodeMap[nodeParent.parent];
  1253. if (nodeParent.parent === series.rootNode) {
  1254. drillId = nodeParent.id;
  1255. }
  1256. }
  1257. }
  1258. return drillId;
  1259. },
  1260. drillUp: function () {
  1261. var series = this, node = series.nodeMap[series.rootNode];
  1262. if (node && isString(node.parent)) {
  1263. series.setRootNode(node.parent, true, { trigger: 'traverseUpButton' });
  1264. }
  1265. },
  1266. // TODO remove this function at a suitable version.
  1267. drillToNode: function (id, redraw) {
  1268. error(32, false, void 0, { 'treemap.drillToNode': 'use treemap.setRootNode' });
  1269. this.setRootNode(id, redraw);
  1270. },
  1271. /**
  1272. * Sets a new root node for the series.
  1273. *
  1274. * @private
  1275. * @function Highcharts.Series#setRootNode
  1276. *
  1277. * @param {string} id The id of the new root node.
  1278. * @param {boolean} [redraw=true] Wether to redraw the chart or not.
  1279. * @param {object} [eventArguments] Arguments to be accessed in
  1280. * event handler.
  1281. * @param {string} [eventArguments.newRootId] Id of the new root.
  1282. * @param {string} [eventArguments.previousRootId] Id of the previous
  1283. * root.
  1284. * @param {boolean} [eventArguments.redraw] Wether to redraw the
  1285. * chart after.
  1286. * @param {object} [eventArguments.series] The series to update the root
  1287. * of.
  1288. * @param {string} [eventArguments.trigger] The action which
  1289. * triggered the event. Undefined if the setRootNode is called
  1290. * directly.
  1291. * @return {void}
  1292. *
  1293. * @fires Highcharts.Series#event:setRootNode
  1294. */
  1295. setRootNode: function (id, redraw, eventArguments) {
  1296. var series = this, eventArgs = extend({
  1297. newRootId: id,
  1298. previousRootId: series.rootNode,
  1299. redraw: pick(redraw, true),
  1300. series: series
  1301. }, eventArguments);
  1302. /**
  1303. * The default functionality of the setRootNode event.
  1304. *
  1305. * @private
  1306. * @param {object} args The event arguments.
  1307. * @param {string} args.newRootId Id of the new root.
  1308. * @param {string} args.previousRootId Id of the previous root.
  1309. * @param {boolean} args.redraw Wether to redraw the chart after.
  1310. * @param {object} args.series The series to update the root of.
  1311. * @param {string} [args.trigger=undefined] The action which
  1312. * triggered the event. Undefined if the setRootNode is called
  1313. * directly.
  1314. * @return {void}
  1315. */
  1316. var defaultFn = function (args) {
  1317. var series = args.series;
  1318. // Store previous and new root ids on the series.
  1319. series.idPreviousRoot = args.previousRootId;
  1320. series.rootNode = args.newRootId;
  1321. // Redraw the chart
  1322. series.isDirty = true; // Force redraw
  1323. if (args.redraw) {
  1324. series.chart.redraw();
  1325. }
  1326. };
  1327. // Fire setRootNode event.
  1328. fireEvent(series, 'setRootNode', eventArgs, defaultFn);
  1329. },
  1330. /**
  1331. * Check if the drill up/down is allowed.
  1332. *
  1333. * @private
  1334. */
  1335. isDrillAllowed: function (targetNode) {
  1336. var tree = this.tree, firstChild = tree.children[0];
  1337. // The sunburst series looks exactly the same on the level ''
  1338. // and level 1 if there’s only one element on level 1. Disable
  1339. // drilling up/down when it doesn't perform any visual
  1340. // difference (#13388).
  1341. return !(tree.children.length === 1 && ((this.rootNode === '' && targetNode === firstChild.id) ||
  1342. (this.rootNode === firstChild.id && targetNode === '')));
  1343. },
  1344. renderTraverseUpButton: function (rootId) {
  1345. var series = this, nodeMap = series.nodeMap, node = nodeMap[rootId], name = node.name, buttonOptions = series.options.traverseUpButton, backText = pick(buttonOptions.text, name, '< Back'), attr, states;
  1346. if (rootId === '' ||
  1347. (series.isDrillAllowed ?
  1348. !(isString(node.parent) && series.isDrillAllowed(node.parent)) : false)) {
  1349. if (series.drillUpButton) {
  1350. series.drillUpButton =
  1351. series.drillUpButton.destroy();
  1352. }
  1353. }
  1354. else if (!this.drillUpButton) {
  1355. attr = buttonOptions.theme;
  1356. states = attr && attr.states;
  1357. this.drillUpButton = this.chart.renderer
  1358. .button(backText, null, null, function () {
  1359. series.drillUp();
  1360. }, attr, states && states.hover, states && states.select)
  1361. .addClass('highcharts-drillup-button')
  1362. .attr({
  1363. align: buttonOptions.position.align,
  1364. zIndex: 7
  1365. })
  1366. .add()
  1367. .align(buttonOptions.position, false, buttonOptions.relativeTo || 'plotBox');
  1368. }
  1369. else {
  1370. this.drillUpButton.placed = false;
  1371. this.drillUpButton.attr({
  1372. text: backText
  1373. })
  1374. .align();
  1375. }
  1376. },
  1377. buildKDTree: noop,
  1378. drawLegendSymbol: LegendSymbolMixin.drawRectangle,
  1379. getExtremes: function () {
  1380. // Get the extremes from the value data
  1381. var _a = Series.prototype.getExtremes
  1382. .call(this, this.colorValueData), dataMin = _a.dataMin, dataMax = _a.dataMax;
  1383. this.valueMin = dataMin;
  1384. this.valueMax = dataMax;
  1385. // Get the extremes from the y data
  1386. return Series.prototype.getExtremes.call(this);
  1387. },
  1388. getExtremesFromAll: true,
  1389. /**
  1390. * Workaround for `inactive` state. Since `series.opacity` option is
  1391. * already reserved, don't use that state at all by disabling
  1392. * `inactiveOtherPoints` and not inheriting states by points.
  1393. *
  1394. * @private
  1395. */
  1396. setState: function (state) {
  1397. this.options.inactiveOtherPoints = true;
  1398. Series.prototype.setState.call(this, state, false);
  1399. this.options.inactiveOtherPoints = false;
  1400. },
  1401. utils: {
  1402. recursive: recursive
  1403. }
  1404. /* eslint-enable no-invalid-this, valid-jsdoc */
  1405. }, {
  1406. draw: drawPoint,
  1407. setVisible: seriesTypes.pie.prototype.pointClass.prototype.setVisible,
  1408. /* eslint-disable no-invalid-this, valid-jsdoc */
  1409. getClassName: function () {
  1410. var className = Point.prototype.getClassName.call(this), series = this.series, options = series.options;
  1411. // Above the current level
  1412. if (this.node.level <= series.nodeMap[series.rootNode].level) {
  1413. className += ' highcharts-above-level';
  1414. }
  1415. else if (!this.node.isLeaf &&
  1416. !pick(options.interactByLeaf, !options.allowTraversingTree)) {
  1417. className += ' highcharts-internal-node-interactive';
  1418. }
  1419. else if (!this.node.isLeaf) {
  1420. className += ' highcharts-internal-node';
  1421. }
  1422. return className;
  1423. },
  1424. /**
  1425. * A tree point is valid if it has han id too, assume it may be a parent
  1426. * item.
  1427. *
  1428. * @private
  1429. * @function Highcharts.Point#isValid
  1430. */
  1431. isValid: function () {
  1432. return this.id || isNumber(this.value);
  1433. },
  1434. setState: function (state) {
  1435. Point.prototype.setState.call(this, state);
  1436. // Graphic does not exist when point is not visible.
  1437. if (this.graphic) {
  1438. this.graphic.attr({
  1439. zIndex: state === 'hover' ? 1 : 0
  1440. });
  1441. }
  1442. },
  1443. shouldDraw: function () {
  1444. var point = this;
  1445. return isNumber(point.plotY) && point.y !== null;
  1446. }
  1447. });
  1448. addEvent(H.Series, 'afterBindAxes', function () {
  1449. var series = this, xAxis = series.xAxis, yAxis = series.yAxis, treeAxis;
  1450. if (xAxis && yAxis) {
  1451. if (series.is('treemap')) {
  1452. treeAxis = {
  1453. endOnTick: false,
  1454. gridLineWidth: 0,
  1455. lineWidth: 0,
  1456. min: 0,
  1457. dataMin: 0,
  1458. minPadding: 0,
  1459. max: AXIS_MAX,
  1460. dataMax: AXIS_MAX,
  1461. maxPadding: 0,
  1462. startOnTick: false,
  1463. title: null,
  1464. tickPositions: []
  1465. };
  1466. extend(yAxis.options, treeAxis);
  1467. extend(xAxis.options, treeAxis);
  1468. treemapAxisDefaultValues = true;
  1469. }
  1470. else if (treemapAxisDefaultValues) {
  1471. yAxis.setOptions(yAxis.userOptions);
  1472. xAxis.setOptions(xAxis.userOptions);
  1473. treemapAxisDefaultValues = false;
  1474. }
  1475. }
  1476. });
  1477. /* eslint-enable no-invalid-this, valid-jsdoc */
  1478. /**
  1479. * A `treemap` series. If the [type](#series.treemap.type) option is
  1480. * not specified, it is inherited from [chart.type](#chart.type).
  1481. *
  1482. * @extends series,plotOptions.treemap
  1483. * @excluding dataParser, dataURL, stack, dataSorting
  1484. * @product highcharts
  1485. * @requires modules/treemap
  1486. * @apioption series.treemap
  1487. */
  1488. /**
  1489. * An array of data points for the series. For the `treemap` series
  1490. * type, points can be given in the following ways:
  1491. *
  1492. * 1. An array of numerical values. In this case, the numerical values will be
  1493. * interpreted as `value` options. Example:
  1494. * ```js
  1495. * data: [0, 5, 3, 5]
  1496. * ```
  1497. *
  1498. * 2. An array of objects with named values. The following snippet shows only a
  1499. * few settings, see the complete options set below. If the total number of
  1500. * data points exceeds the series'
  1501. * [turboThreshold](#series.treemap.turboThreshold),
  1502. * this option is not available.
  1503. * ```js
  1504. * data: [{
  1505. * value: 9,
  1506. * name: "Point2",
  1507. * color: "#00FF00"
  1508. * }, {
  1509. * value: 6,
  1510. * name: "Point1",
  1511. * color: "#FF00FF"
  1512. * }]
  1513. * ```
  1514. *
  1515. * @sample {highcharts} highcharts/chart/reflow-true/
  1516. * Numerical values
  1517. * @sample {highcharts} highcharts/series/data-array-of-objects/
  1518. * Config objects
  1519. *
  1520. * @type {Array<number|null|*>}
  1521. * @extends series.heatmap.data
  1522. * @excluding x, y
  1523. * @product highcharts
  1524. * @apioption series.treemap.data
  1525. */
  1526. /**
  1527. * The value of the point, resulting in a relative area of the point
  1528. * in the treemap.
  1529. *
  1530. * @type {number|null}
  1531. * @product highcharts
  1532. * @apioption series.treemap.data.value
  1533. */
  1534. /**
  1535. * Serves a purpose only if a `colorAxis` object is defined in the chart
  1536. * options. This value will decide which color the point gets from the
  1537. * scale of the colorAxis.
  1538. *
  1539. * @type {number}
  1540. * @since 4.1.0
  1541. * @product highcharts
  1542. * @apioption series.treemap.data.colorValue
  1543. */
  1544. /**
  1545. * Only for treemap. Use this option to build a tree structure. The
  1546. * value should be the id of the point which is the parent. If no points
  1547. * has a matching id, or this option is undefined, then the parent will
  1548. * be set to the root.
  1549. *
  1550. * @sample {highcharts} highcharts/point/parent/
  1551. * Point parent
  1552. * @sample {highcharts} highcharts/demo/treemap-with-levels/
  1553. * Example where parent id is not matching
  1554. *
  1555. * @type {string}
  1556. * @since 4.1.0
  1557. * @product highcharts
  1558. * @apioption series.treemap.data.parent
  1559. */
  1560. ''; // adds doclets above to transpiled file