UNPKG

highcharts

Version:
1,258 lines (1,250 loc) 99.8 kB
/** * @license Highcharts JS v9.0.1 (2021-02-16) * * (c) 2014-2021 Highsoft AS * Authors: Jon Arild Nygard / Oystein Moseng * * License: www.highcharts.com/license */ 'use strict'; (function (factory) { if (typeof module === 'object' && module.exports) { factory['default'] = factory; module.exports = factory; } else if (typeof define === 'function' && define.amd) { define('highcharts/modules/treemap', ['highcharts'], function (Highcharts) { factory(Highcharts); factory.Highcharts = Highcharts; return factory; }); } else { factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined); } }(function (Highcharts) { var _modules = Highcharts ? Highcharts._modules : {}; function _registerModule(obj, path, args, fn) { if (!obj.hasOwnProperty(path)) { obj[path] = fn.apply(null, args); } } _registerModule(_modules, 'Mixins/ColorMapSeries.js', [_modules['Core/Globals.js'], _modules['Core/Series/Point.js'], _modules['Core/Utilities.js']], function (H, Point, U) { /* * * * (c) 2010-2021 Torstein Honsi * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ var defined = U.defined; var noop = H.noop, seriesTypes = H.seriesTypes; /** * Mixin for maps and heatmaps * * @private * @mixin Highcharts.colorMapPointMixin */ var colorMapPointMixin = { dataLabelOnNull: true, /* eslint-disable valid-jsdoc */ /** * Color points have a value option that determines whether or not it is * a null point * @private */ isValid: function () { // undefined is allowed return (this.value !== null && this.value !== Infinity && this.value !== -Infinity); }, /** * @private */ setState: function (state) { Point.prototype.setState.call(this, state); if (this.graphic) { this.graphic.attr({ zIndex: state === 'hover' ? 1 : 0 }); } } /* eslint-enable valid-jsdoc */ }; /** * @private * @mixin Highcharts.colorMapSeriesMixin */ var colorMapSeriesMixin = { pointArrayMap: ['value'], axisTypes: ['xAxis', 'yAxis', 'colorAxis'], trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'], getSymbol: noop, parallelArrays: ['x', 'y', 'value'], colorKey: 'value', pointAttribs: seriesTypes.column.prototype.pointAttribs, /* eslint-disable valid-jsdoc */ /** * Get the color attibutes to apply on the graphic * @private * @function Highcharts.colorMapSeriesMixin.colorAttribs * @param {Highcharts.Point} point * @return {Highcharts.SVGAttributes} */ colorAttribs: function (point) { var ret = {}; if (defined(point.color)) { ret[this.colorProp || 'fill'] = point.color; } return ret; } }; var exports = { colorMapPointMixin: colorMapPointMixin, colorMapSeriesMixin: colorMapSeriesMixin }; return exports; }); _registerModule(_modules, 'Series/Treemap/TreemapAlgorithmGroup.js', [], function () { /* * * * (c) 2014-2021 Highsoft AS * * Authors: Jon Arild Nygard / Oystein Moseng * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ /* * * * Class * * */ var TreemapAlgorithmGroup = /** @class */ (function () { /* * * * Constructor * * */ function TreemapAlgorithmGroup(h, w, d, p) { this.height = h; this.width = w; this.plot = p; this.direction = d; this.startDirection = d; this.total = 0; this.nW = 0; this.lW = 0; this.nH = 0; this.lH = 0; this.elArr = []; this.lP = { total: 0, lH: 0, nH: 0, lW: 0, nW: 0, nR: 0, lR: 0, aspectRatio: function (w, h) { return Math.max((w / h), (h / w)); } }; } /* * * * Functions * * */ /* eslint-disable valid-jsdoc */ TreemapAlgorithmGroup.prototype.addElement = function (el) { this.lP.total = this.elArr[this.elArr.length - 1]; this.total = this.total + el; if (this.direction === 0) { // Calculate last point old aspect ratio this.lW = this.nW; this.lP.lH = this.lP.total / this.lW; this.lP.lR = this.lP.aspectRatio(this.lW, this.lP.lH); // Calculate last point new aspect ratio this.nW = this.total / this.height; this.lP.nH = this.lP.total / this.nW; this.lP.nR = this.lP.aspectRatio(this.nW, this.lP.nH); } else { // Calculate last point old aspect ratio this.lH = this.nH; this.lP.lW = this.lP.total / this.lH; this.lP.lR = this.lP.aspectRatio(this.lP.lW, this.lH); // Calculate last point new aspect ratio this.nH = this.total / this.width; this.lP.nW = this.lP.total / this.nH; this.lP.nR = this.lP.aspectRatio(this.lP.nW, this.nH); } this.elArr.push(el); }; TreemapAlgorithmGroup.prototype.reset = function () { this.nW = 0; this.lW = 0; this.elArr = []; this.total = 0; }; return TreemapAlgorithmGroup; }()); /* * * * Default Export * * */ return TreemapAlgorithmGroup; }); _registerModule(_modules, 'Mixins/DrawPoint.js', [], function () { /* * * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ var isFn = function (x) { return typeof x === 'function'; }; /* eslint-disable no-invalid-this, valid-jsdoc */ /** * Handles the drawing of a component. * Can be used for any type of component that reserves the graphic property, and * provides a shouldDraw on its context. * * @private * @function draw * @param {DrawPointParams} params * Parameters. * * @todo add type checking. * @todo export this function to enable usage */ var draw = function draw(params) { var _a; var component = this, graphic = component.graphic, animatableAttribs = params.animatableAttribs, onComplete = params.onComplete, css = params.css, renderer = params.renderer, animation = (_a = component.series) === null || _a === void 0 ? void 0 : _a.options.animation; if (component.shouldDraw()) { if (!graphic) { component.graphic = graphic = renderer[params.shapeType](params.shapeArgs) .add(params.group); } graphic .css(css) .attr(params.attribs) .animate(animatableAttribs, params.isNew ? false : animation, onComplete); } else if (graphic) { var destroy = function () { component.graphic = graphic = graphic.destroy(); if (isFn(onComplete)) { onComplete(); } }; // animate only runs complete callback if something was animated. if (Object.keys(animatableAttribs).length) { graphic.animate(animatableAttribs, void 0, function () { destroy(); }); } else { destroy(); } } }; /** * An extended version of draw customized for points. * It calls additional methods that is expected when rendering a point. * @private * @param {Highcharts.Dictionary<any>} params Parameters */ var drawPoint = function drawPoint(params) { var point = this, attribs = params.attribs = params.attribs || {}; // Assigning class in dot notation does go well in IE8 // eslint-disable-next-line dot-notation attribs['class'] = point.getClassName(); // Call draw to render component draw.call(point, params); }; var drawPointModule = { draw: draw, drawPoint: drawPoint, isFn: isFn }; return drawPointModule; }); _registerModule(_modules, 'Series/Treemap/TreemapPoint.js', [_modules['Mixins/DrawPoint.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (DrawPointMixin, SeriesRegistry, U) { /* * * * (c) 2014-2021 Highsoft AS * * Authors: Jon Arild Nygard / Oystein Moseng * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var Point = SeriesRegistry.series.prototype.pointClass, _a = SeriesRegistry.seriesTypes, PiePoint = _a.pie.prototype.pointClass, ScatterPoint = _a.scatter.prototype.pointClass; var extend = U.extend, isNumber = U.isNumber, pick = U.pick; /* * * * Class * * */ var TreemapPoint = /** @class */ (function (_super) { __extends(TreemapPoint, _super); function TreemapPoint() { /* * * * Properties * * */ var _this = _super !== null && _super.apply(this, arguments) || this; _this.name = void 0; _this.node = void 0; _this.options = void 0; _this.series = void 0; _this.value = void 0; return _this; /* eslint-enable valid-jsdoc */ } /* * * * Functions * * */ /* eslint-disable valid-jsdoc */ TreemapPoint.prototype.getClassName = function () { var className = Point.prototype.getClassName.call(this), series = this.series, options = series.options; // Above the current level if (this.node.level <= series.nodeMap[series.rootNode].level) { className += ' highcharts-above-level'; } else if (!this.node.isLeaf && !pick(options.interactByLeaf, !options.allowTraversingTree)) { className += ' highcharts-internal-node-interactive'; } else if (!this.node.isLeaf) { className += ' highcharts-internal-node'; } return className; }; /** * A tree point is valid if it has han id too, assume it may be a parent * item. * * @private * @function Highcharts.Point#isValid */ TreemapPoint.prototype.isValid = function () { return Boolean(this.id || isNumber(this.value)); }; TreemapPoint.prototype.setState = function (state) { Point.prototype.setState.call(this, state); // Graphic does not exist when point is not visible. if (this.graphic) { this.graphic.attr({ zIndex: state === 'hover' ? 1 : 0 }); } }; TreemapPoint.prototype.shouldDraw = function () { return isNumber(this.plotY) && this.y !== null; }; return TreemapPoint; }(ScatterPoint)); extend(TreemapPoint.prototype, { draw: DrawPointMixin.drawPoint, setVisible: PiePoint.prototype.setVisible }); /* * * * Default Export * * */ return TreemapPoint; }); _registerModule(_modules, 'Series/Treemap/TreemapUtilities.js', [_modules['Core/Utilities.js']], function (U) { /* * * * (c) 2014-2021 Highsoft AS * * Authors: Jon Arild Nygard / Oystein Moseng * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ /* * * * Imports * * */ var objectEach = U.objectEach; /* * * * Namespace * * */ var TreemapUtilities; (function (TreemapUtilities) { TreemapUtilities.AXIS_MAX = 100; /* eslint-disable no-invalid-this, valid-jsdoc */ /** * @todo Similar to eachObject, this function is likely redundant */ function isBoolean(x) { return typeof x === 'boolean'; } TreemapUtilities.isBoolean = isBoolean; /** * @todo Similar to recursive, this function is likely redundant */ function eachObject(list, func, context) { context = context || this; objectEach(list, function (val, key) { func.call(context, val, key, list); }); } TreemapUtilities.eachObject = eachObject; /** * @todo find correct name for this function. * @todo Similar to reduce, this function is likely redundant */ function recursive(item, func, context) { if (context === void 0) { context = this; } var next; next = func.call(context, item); if (next !== false) { recursive(next, func, context); } } TreemapUtilities.recursive = recursive; })(TreemapUtilities || (TreemapUtilities = {})); /* * * * Default Export * * */ return TreemapUtilities; }); _registerModule(_modules, 'Mixins/TreeSeries.js', [_modules['Core/Color/Color.js'], _modules['Core/Utilities.js']], function (Color, U) { /* * * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ var extend = U.extend, isArray = U.isArray, isNumber = U.isNumber, isObject = U.isObject, merge = U.merge, pick = U.pick; var isBoolean = function (x) { return typeof x === 'boolean'; }, isFn = function (x) { return typeof x === 'function'; }; /* eslint-disable valid-jsdoc */ /** * @todo Combine buildTree and buildNode with setTreeValues * @todo Remove logic from Treemap and make it utilize this mixin. * @private */ var setTreeValues = function setTreeValues(tree, options) { var before = options.before, idRoot = options.idRoot, mapIdToNode = options.mapIdToNode, nodeRoot = mapIdToNode[idRoot], levelIsConstant = (isBoolean(options.levelIsConstant) ? options.levelIsConstant : true), points = options.points, point = points[tree.i], optionsPoint = point && point.options || {}, childrenTotal = 0, children = [], value; extend(tree, { levelDynamic: tree.level - (levelIsConstant ? 0 : nodeRoot.level), name: pick(point && point.name, ''), visible: (idRoot === tree.id || (isBoolean(options.visible) ? options.visible : false)) }); if (isFn(before)) { tree = before(tree, options); } // First give the children some values tree.children.forEach(function (child, i) { var newOptions = extend({}, options); extend(newOptions, { index: i, siblings: tree.children.length, visible: tree.visible }); child = setTreeValues(child, newOptions); children.push(child); if (child.visible) { childrenTotal += child.val; } }); tree.visible = childrenTotal > 0 || tree.visible; // Set the values value = pick(optionsPoint.value, childrenTotal); extend(tree, { children: children, childrenTotal: childrenTotal, isLeaf: tree.visible && !childrenTotal, val: value }); return tree; }; /** * @private */ var getColor = function getColor(node, options) { var index = options.index, mapOptionsToLevel = options.mapOptionsToLevel, parentColor = options.parentColor, parentColorIndex = options.parentColorIndex, series = options.series, colors = options.colors, siblings = options.siblings, points = series.points, getColorByPoint, chartOptionsChart = series.chart.options.chart, point, level, colorByPoint, colorIndexByPoint, color, colorIndex; /** * @private */ function variation(color) { var colorVariation = level && level.colorVariation; if (colorVariation) { if (colorVariation.key === 'brightness') { return Color.parse(color).brighten(colorVariation.to * (index / siblings)).get(); } } return color; } if (node) { point = points[node.i]; level = mapOptionsToLevel[node.level] || {}; getColorByPoint = point && level.colorByPoint; if (getColorByPoint) { colorIndexByPoint = point.index % (colors ? colors.length : chartOptionsChart.colorCount); colorByPoint = colors && colors[colorIndexByPoint]; } // Select either point color, level color or inherited color. if (!series.chart.styledMode) { color = pick(point && point.options.color, level && level.color, colorByPoint, parentColor && variation(parentColor), series.color); } colorIndex = pick(point && point.options.colorIndex, level && level.colorIndex, colorIndexByPoint, parentColorIndex, options.colorIndex); } return { color: color, colorIndex: colorIndex }; }; /** * Creates a map from level number to its given options. * * @private * @function getLevelOptions * @param {object} params * Object containing parameters. * - `defaults` Object containing default options. The default options * are merged with the userOptions to get the final options for a * specific level. * - `from` The lowest level number. * - `levels` User options from series.levels. * - `to` The highest level number. * @return {Highcharts.Dictionary<object>|null} * Returns a map from level number to its given options. */ var getLevelOptions = function getLevelOptions(params) { var result = null, defaults, converted, i, from, to, levels; if (isObject(params)) { result = {}; from = isNumber(params.from) ? params.from : 1; levels = params.levels; converted = {}; defaults = isObject(params.defaults) ? params.defaults : {}; if (isArray(levels)) { converted = levels.reduce(function (obj, item) { var level, levelIsConstant, options; if (isObject(item) && isNumber(item.level)) { options = merge({}, item); levelIsConstant = (isBoolean(options.levelIsConstant) ? options.levelIsConstant : defaults.levelIsConstant); // Delete redundant properties. delete options.levelIsConstant; delete options.level; // Calculate which level these options apply to. level = item.level + (levelIsConstant ? 0 : from - 1); if (isObject(obj[level])) { extend(obj[level], options); } else { obj[level] = options; } } return obj; }, {}); } to = isNumber(params.to) ? params.to : 1; for (i = 0; i <= to; i++) { result[i] = merge({}, defaults, isObject(converted[i]) ? converted[i] : {}); } } return result; }; /** * Update the rootId property on the series. Also makes sure that it is * accessible to exporting. * * @private * @function updateRootId * * @param {object} series * The series to operate on. * * @return {string} * Returns the resulting rootId after update. */ var updateRootId = function (series) { var rootId, options; if (isObject(series)) { // Get the series options. options = isObject(series.options) ? series.options : {}; // Calculate the rootId. rootId = pick(series.rootNode, options.rootId, ''); // Set rootId on series.userOptions to pick it up in exporting. if (isObject(series.userOptions)) { series.userOptions.rootId = rootId; } // Set rootId on series to pick it up on next update. series.rootNode = rootId; } return rootId; }; var result = { getColor: getColor, getLevelOptions: getLevelOptions, setTreeValues: setTreeValues, updateRootId: updateRootId }; return result; }); _registerModule(_modules, 'Series/Treemap/TreemapComposition.js', [_modules['Core/Series/SeriesRegistry.js'], _modules['Series/Treemap/TreemapUtilities.js'], _modules['Core/Utilities.js']], function (SeriesRegistry, TreemapUtilities, U) { /* * * * (c) 2014-2021 Highsoft AS * * Authors: Jon Arild Nygard / Oystein Moseng * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ /* * * * Imports * * */ var Series = SeriesRegistry.series; var addEvent = U.addEvent, extend = U.extend; /* * * * Composition * * */ var treemapAxisDefaultValues = false; addEvent(Series, 'afterBindAxes', function () { // eslint-disable-next-line no-invalid-this var series = this, xAxis = series.xAxis, yAxis = series.yAxis, treeAxis; if (xAxis && yAxis) { if (series.is('treemap')) { treeAxis = { endOnTick: false, gridLineWidth: 0, lineWidth: 0, min: 0, dataMin: 0, minPadding: 0, max: TreemapUtilities.AXIS_MAX, dataMax: TreemapUtilities.AXIS_MAX, maxPadding: 0, startOnTick: false, title: null, tickPositions: [] }; extend(yAxis.options, treeAxis); extend(xAxis.options, treeAxis); treemapAxisDefaultValues = true; } else if (treemapAxisDefaultValues) { yAxis.setOptions(yAxis.userOptions); xAxis.setOptions(xAxis.userOptions); treemapAxisDefaultValues = false; } } }); }); _registerModule(_modules, 'Series/Treemap/TreemapSeries.js', [_modules['Core/Color/Color.js'], _modules['Mixins/ColorMapSeries.js'], _modules['Core/Globals.js'], _modules['Mixins/LegendSymbol.js'], _modules['Core/Color/Palette.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Series/Treemap/TreemapAlgorithmGroup.js'], _modules['Series/Treemap/TreemapPoint.js'], _modules['Series/Treemap/TreemapUtilities.js'], _modules['Mixins/TreeSeries.js'], _modules['Core/Utilities.js']], function (Color, ColorMapMixin, H, LegendSymbolMixin, palette, SeriesRegistry, TreemapAlgorithmGroup, TreemapPoint, TreemapUtilities, TreeSeriesMixin, U) { /* * * * (c) 2014-2021 Highsoft AS * * Authors: Jon Arild Nygard / Oystein Moseng * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var color = Color.parse; var colorMapSeriesMixin = ColorMapMixin.colorMapSeriesMixin; var noop = H.noop; var Series = SeriesRegistry.series, _a = SeriesRegistry.seriesTypes, ColumnSeries = _a.column, HeatmapSeries = _a.heatmap, ScatterSeries = _a.scatter; var getColor = TreeSeriesMixin.getColor, getLevelOptions = TreeSeriesMixin.getLevelOptions, updateRootId = TreeSeriesMixin.updateRootId; var addEvent = U.addEvent, correctFloat = U.correctFloat, defined = U.defined, error = U.error, extend = U.extend, fireEvent = U.fireEvent, isArray = U.isArray, isObject = U.isObject, isString = U.isString, merge = U.merge, pick = U.pick, stableSort = U.stableSort; /* * * * Class * * */ /** * @private * @class * @name Highcharts.seriesTypes.treemap * * @augments Highcharts.Series */ var TreemapSeries = /** @class */ (function (_super) { __extends(TreemapSeries, _super); function TreemapSeries() { /* * * * Static Properties * * */ var _this = _super !== null && _super.apply(this, arguments) || this; /* * * * Properties * * */ _this.axisRatio = void 0; _this.data = void 0; _this.mapOptionsToLevel = void 0; _this.nodeMap = void 0; _this.options = void 0; _this.points = void 0; _this.rootNode = void 0; _this.tree = void 0; return _this; /* eslint-enable valid-jsdoc */ } /* * * * Function * * */ /* eslint-disable valid-jsdoc */ TreemapSeries.prototype.algorithmCalcPoints = function (directionChange, last, group, childrenArea) { var pX, pY, pW, pH, gW = group.lW, gH = group.lH, plot = group.plot, keep, i = 0, end = group.elArr.length - 1; if (last) { gW = group.nW; gH = group.nH; } else { keep = group.elArr[group.elArr.length - 1]; } group.elArr.forEach(function (p) { if (last || (i < end)) { if (group.direction === 0) { pX = plot.x; pY = plot.y; pW = gW; pH = p / pW; } else { pX = plot.x; pY = plot.y; pH = gH; pW = p / pH; } childrenArea.push({ x: pX, y: pY, width: pW, height: correctFloat(pH) }); if (group.direction === 0) { plot.y = plot.y + pH; } else { plot.x = plot.x + pW; } } i = i + 1; }); // Reset variables group.reset(); if (group.direction === 0) { group.width = group.width - gW; } else { group.height = group.height - gH; } plot.y = plot.parent.y + (plot.parent.height - group.height); plot.x = plot.parent.x + (plot.parent.width - group.width); if (directionChange) { group.direction = 1 - group.direction; } // If not last, then add uncalculated element if (!last) { group.addElement(keep); } }; TreemapSeries.prototype.algorithmFill = function (directionChange, parent, children) { var childrenArea = [], pTot, direction = parent.direction, x = parent.x, y = parent.y, width = parent.width, height = parent.height, pX, pY, pW, pH; children.forEach(function (child) { pTot = (parent.width * parent.height) * (child.val / parent.val); pX = x; pY = y; if (direction === 0) { pH = height; pW = pTot / pH; width = width - pW; x = x + pW; } else { pW = width; pH = pTot / pW; height = height - pH; y = y + pH; } childrenArea.push({ x: pX, y: pY, width: pW, height: pH }); if (directionChange) { direction = 1 - direction; } }); return childrenArea; }; TreemapSeries.prototype.algorithmLowAspectRatio = function (directionChange, parent, children) { var childrenArea = [], series = this, pTot, plot = { x: parent.x, y: parent.y, parent: parent }, direction = parent.direction, i = 0, end = children.length - 1, group = new TreemapAlgorithmGroup(parent.height, parent.width, direction, plot); // Loop through and calculate all areas children.forEach(function (child) { pTot = (parent.width * parent.height) * (child.val / parent.val); group.addElement(pTot); if (group.lP.nR > group.lP.lR) { series.algorithmCalcPoints(directionChange, false, group, childrenArea, plot // @todo no supported ); } // If last child, then calculate all remaining areas if (i === end) { series.algorithmCalcPoints(directionChange, true, group, childrenArea, plot // @todo not supported ); } i = i + 1; }); return childrenArea; }; /** * Over the alignment method by setting z index. * @private */ TreemapSeries.prototype.alignDataLabel = function (point, dataLabel, labelOptions) { var style = labelOptions.style; // #8160: Prevent the label from exceeding the point's // boundaries in treemaps by applying ellipsis overflow. // The issue was happening when datalabel's text contained a // long sequence of characters without a whitespace. if (!defined(style.textOverflow) && dataLabel.text && dataLabel.getBBox().width > dataLabel.text.textWidth) { dataLabel.css({ textOverflow: 'ellipsis', // unit (px) is required when useHTML is true width: style.width += 'px' }); } ColumnSeries.prototype.alignDataLabel.apply(this, arguments); if (point.dataLabel) { // point.node.zIndex could be undefined (#6956) point.dataLabel.attr({ zIndex: (point.node.zIndex || 0) + 1 }); } }; TreemapSeries.prototype.buildNode = function (id, i, level, list, parent) { var series = this, children = [], point = series.points[i], height = 0, node, child; // Actions ((list[id] || [])).forEach(function (i) { child = series.buildNode(series.points[i].id, i, (level + 1), list, id); height = Math.max(child.height + 1, height); children.push(child); }); node = { id: id, i: i, children: children, height: height, level: level, parent: parent, visible: false // @todo move this to better location }; series.nodeMap[node.id] = node; if (point) { point.node = node; } return node; }; /** * Recursive function which calculates the area for all children of a * node. * * @private * @function Highcharts.Series#calculateChildrenAreas * * @param {object} node * The node which is parent to the children. * * @param {object} area * The rectangular area of the parent. */ TreemapSeries.prototype.calculateChildrenAreas = function (parent, area) { var series = this, options = series.options, mapOptionsToLevel = series.mapOptionsToLevel, level = mapOptionsToLevel[parent.level + 1], algorithm = pick((series[(level && level.layoutAlgorithm)] && level.layoutAlgorithm), options.layoutAlgorithm), alternate = options.alternateStartingDirection, childrenValues = [], children; // Collect all children which should be included children = parent.children.filter(function (n) { return !n.ignore; }); if (level && level.layoutStartingDirection) { area.direction = level.layoutStartingDirection === 'vertical' ? 0 : 1; } childrenValues = series[algorithm](area, children); children.forEach(function (child, index) { var values = childrenValues[index]; child.values = merge(values, { val: child.childrenTotal, direction: (alternate ? 1 - area.direction : area.direction) }); child.pointValues = merge(values, { x: (values.x / series.axisRatio), // Flip y-values to avoid visual regression with csvCoord in // Axis.translate at setPointValues. #12488 y: TreemapUtilities.AXIS_MAX - values.y - values.height, width: (values.width / series.axisRatio) }); // If node has children, then call method recursively if (child.children.length) { series.calculateChildrenAreas(child, child.values); } }); }; /** * Extend drawDataLabels with logic to handle custom options related to * the treemap series: * * - Points which is not a leaf node, has dataLabels disabled by * default. * * - Options set on series.levels is merged in. * * - Width of the dataLabel is set to match the width of the point * shape. * * @private */ TreemapSeries.prototype.drawDataLabels = function () { var series = this, mapOptionsToLevel = series.mapOptionsToLevel, points = series.points.filter(function (n) { return n.node.visible; }), options, level; points.forEach(function (point) { level = mapOptionsToLevel[point.node.level]; // Set options to new object to avoid problems with scope options = { style: {} }; // If not a leaf, then label should be disabled as default if (!point.node.isLeaf) { options.enabled = false; } // If options for level exists, include them as well if (level && level.dataLabels) { options = merge(options, level.dataLabels); series._hasPointLabels = true; } // Set dataLabel width to the width of the point shape. if (point.shapeArgs) { options.style.width = point.shapeArgs.width; if (point.dataLabel) { point.dataLabel.css({ width: point.shapeArgs.width + 'px' }); } } // Merge custom options with point options point.dlOptions = merge(options, point.options.dataLabels); }); Series.prototype.drawDataLabels.call(this); }; /** * Override drawPoints * @private */ TreemapSeries.prototype.drawPoints = function () { var series = this, chart = series.chart, renderer = chart.renderer, points = series.points, styledMode = chart.styledMode, options = series.options, shadow = styledMode ? {} : options.shadow, borderRadius = options.borderRadius, withinAnimationLimit = chart.pointCount < options.animationLimit, allowTraversingTree = options.allowTraversingTree; points.forEach(function (point) { var levelDynamic = point.node.levelDynamic, animatableAttribs = {}, attribs = {}, css = {}, groupKey = 'level-group-' + point.node.level, hasGraphic = !!point.graphic, shouldAnimate = withinAnimationLimit && hasGraphic, shapeArgs = point.shapeArgs; // Don't bother with calculate styling if the point is not drawn if (point.shouldDraw()) { if (borderRadius) { attribs.r = borderRadius; } merge(true, // Extend object // Which object to extend shouldAnimate ? animatableAttribs : attribs, // Add shapeArgs to animate/attr if graphic exists hasGraphic ? shapeArgs : {}, // Add style attribs if !styleMode styledMode ? {} : series.pointAttribs(point, point.selected ? 'select' : void 0)); // In styled mode apply point.color. Use CSS, otherwise the // fill used in the style sheet will take precedence over // the fill attribute. if (series.colorAttribs && styledMode) { // Heatmap is loaded extend(css, series.colorAttribs(point)); } if (!series[groupKey]) { series[groupKey] = renderer.g(groupKey) .attr({ // @todo Set the zIndex based upon the number of // levels, instead of using 1000 zIndex: 1000 - (levelDynamic || 0) }) .add(series.group); series[groupKey].survive = true; } } // Draw the point point.draw({ animatableAttribs: animatableAttribs, attribs: attribs, css: css, group: series[groupKey], renderer: renderer, shadow: shadow, shapeArgs: shapeArgs, shapeType: 'rect' }); // If setRootNode is allowed, set a point cursor on clickables & // add drillId to point if (allowTraversingTree && point.graphic) { point.drillId = options.interactByLeaf ? series.drillToByLeaf(point) : series.drillToByGroup(point); } }); }; /** * Finds the drill id for a parent node. Returns false if point should * not have a click event. * @private */ TreemapSeries.prototype.drillToByGroup = function (point) { var series = this, drillId = false; if ((point.node.level - series.nodeMap[series.rootNode].level) === 1 && !point.node.isLeaf) { drillId = point.id; } return drillId; }; /** * Finds the drill id for a leaf node. Returns false if point should not * have a click event * @private */ TreemapSeries.prototype.drillToByLeaf = function (point) { var series = this, drillId = false, nodeParent; if ((point.node.parent !== series.rootNode) && point.node.isLeaf) { nodeParent = point.node; while (!drillId) { nodeParent = series.nodeMap[nodeParent.parent]; if (nodeParent.parent === series.rootNode) { drillId = nodeParent.id; }