treemap.src.js 59 KB

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