treemap.src.js 55 KB

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