treemap.src.js 57 KB

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