treemap.src.js 55 KB

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