highcharts
Version:
JavaScript charting framework
1,258 lines (1,147 loc) • 134 kB
JavaScript
/**
* @license Highcharts JS v7.1.2 (2019-06-04)
*
* (c) 2016-2019 Highsoft AS
* Authors: Jon Arild Nygard
*
* 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/sunburst', ['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/draw-point.js', [], function () {
var isFn = function (x) {
return typeof x === 'function';
};
/**
* 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 {object} params
* Parameters.
*
* TODO: add type checking.
* TODO: export this function to enable usage
*/
var draw = function draw(params) {
var component = this,
graphic = component.graphic,
animatableAttribs = params.animatableAttribs,
onComplete = params.onComplete,
css = params.css,
renderer = params.renderer;
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 : undefined,
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, undefined, function () {
destroy();
});
} else {
destroy();
}
}
};
/**
* An extended version of draw customized for points.
* It calls additional methods that is expected when rendering a point.
*
* @param {object} 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);
};
return drawPoint;
});
_registerModule(_modules, 'mixins/tree-series.js', [_modules['parts/Globals.js']], function (H) {
var extend = H.extend,
isArray = H.isArray,
isBoolean = function (x) {
return typeof x === 'boolean';
},
isFn = function (x) {
return typeof x === 'function';
},
isObject = H.isObject,
isNumber = H.isNumber,
merge = H.merge,
pick = H.pick;
// TODO Combine buildTree and buildNode with setTreeValues
// TODO Remove logic from Treemap and make it utilize this mixin.
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;
};
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;
function variation(color) {
var colorVariation = level && level.colorVariation;
if (colorVariation) {
if (colorVariation.key === 'brightness') {
return H.color(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>}
* 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, 'modules/treemap.src.js', [_modules['parts/Globals.js'], _modules['mixins/tree-series.js'], _modules['mixins/draw-point.js']], function (H, mixinTreeSeries, drawPoint) {
/* *
* (c) 2014-2019 Highsoft AS
*
* Authors: Jon Arild Nygard / Oystein Moseng
*
* License: www.highcharts.com/license
*/
var seriesType = H.seriesType,
seriesTypes = H.seriesTypes,
addEvent = H.addEvent,
merge = H.merge,
extend = H.extend,
error = H.error,
defined = H.defined,
noop = H.noop,
fireEvent = H.fireEvent,
getColor = mixinTreeSeries.getColor,
getLevelOptions = mixinTreeSeries.getLevelOptions,
isArray = H.isArray,
isBoolean = function (x) {
return typeof x === 'boolean';
},
isNumber = H.isNumber,
isObject = H.isObject,
isString = H.isString,
pick = H.pick,
Series = H.Series,
stableSort = H.stableSort,
color = H.Color,
eachObject = function (list, func, context) {
context = context || this;
H.objectEach(list, function (val, key) {
func.call(context, val, key, list);
});
},
// @todo find correct name for this function.
// @todo Similar to reduce, this function is likely redundant
recursive = function (item, func, context) {
var next;
context = context || this;
next = func.call(context, item);
if (next !== false) {
recursive(next, func, context);
}
},
updateRootId = mixinTreeSeries.updateRootId;
/**
* @private
* @class
* @name Highcharts.seriesTypes.treemap
*
* @augments Highcharts.Series
*/
seriesType(
'treemap',
'scatter'
/**
* A treemap displays hierarchical data using nested rectangles. The data
* can be laid out in varying ways depending on options.
*
* @sample highcharts/demo/treemap-large-dataset/
* Treemap
*
* @extends plotOptions.scatter
* @excluding marker, jitter
* @product highcharts
* @optionparent plotOptions.treemap
*/
, {
/**
* When enabled the user can click on a point which is a parent and
* zoom in on its children. Deprecated and replaced by
* [allowTraversingTree](#plotOptions.treemap.allowTraversingTree).
*
* @sample {highcharts} highcharts/plotoptions/treemap-allowdrilltonode/
* Enabled
*
* @deprecated
* @type {boolean}
* @default false
* @since 4.1.0
* @product highcharts
* @apioption plotOptions.treemap.allowDrillToNode
*/
/**
* When enabled the user can click on a point which is a parent and
* zoom in on its children.
*
* @sample {highcharts} highcharts/plotoptions/treemap-allowtraversingtree/
* Enabled
*
* @since 7.0.3
* @product highcharts
*/
allowTraversingTree: false,
animationLimit: 250,
/**
* When the series contains less points than the crop threshold, all
* points are drawn, event if the points fall outside the visible plot
* area at the current zoom. The advantage of drawing all points
* (including markers and columns), is that animation is performed on
* updates. On the other hand, when the series contains more points than
* the crop threshold, the series data is cropped to only contain points
* that fall within the plot area. The advantage of cropping away
* invisible points is to increase performance on large series.
*
* @type {number}
* @default 300
* @since 4.1.0
* @product highcharts
* @apioption plotOptions.treemap.cropThreshold
*/
/**
* Fires on a request for change of root node for the tree, before the
* update is made. An event object is passed to the function, containing
* additional properties `newRootId`, `previousRootId`, `redraw` and
* `trigger`.
*
* @type {function}
* @default undefined
* @sample {highcharts} highcharts/plotoptions/treemap-events-setrootnode/
* Alert update information on setRootNode event.
* @since 7.0.3
* @product highcharts
* @apioption plotOptions.treemap.events.setRootNode
*/
/**
* This option decides if the user can interact with the parent nodes
* or just the leaf nodes. When this option is undefined, it will be
* true by default. However when allowTraversingTree is true, then it
* will be false by default.
*
* @sample {highcharts} highcharts/plotoptions/treemap-interactbyleaf-false/
* False
* @sample {highcharts} highcharts/plotoptions/treemap-interactbyleaf-true-and-allowtraversingtree/
* InteractByLeaf and allowTraversingTree is true
*
* @type {boolean}
* @since 4.1.2
* @product highcharts
* @apioption plotOptions.treemap.interactByLeaf
*/
/**
* The sort index of the point inside the treemap level.
*
* @sample {highcharts} highcharts/plotoptions/treemap-sortindex/
* Sort by years
*
* @type {number}
* @since 4.1.10
* @product highcharts
* @apioption plotOptions.treemap.sortIndex
*/
/**
* When using automatic point colors pulled from the `options.colors`
* collection, this option determines whether the chart should receive
* one color per series or one color per point.
*
* @see [series colors](#plotOptions.treemap.colors)
*
* @type {boolean}
* @default false
* @since 2.0
* @product highcharts
* @apioption plotOptions.treemap.colorByPoint
*/
/**
* A series specific or series type specific color set to apply instead
* of the global [colors](#colors) when
* [colorByPoint](#plotOptions.treemap.colorByPoint) is true.
*
* @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
* @since 3.0
* @product highcharts
* @apioption plotOptions.treemap.colors
*/
/**
* Whether to display this series type or specific series item in the
* legend.
*/
showInLegend: false,
/**
* @ignore-option
*/
marker: false,
colorByPoint: false,
/**
* @since 4.1.0
*/
dataLabels: {
/** @ignore-option */
defer: false,
/** @ignore-option */
enabled: true,
/** @ignore-option */
formatter: function () {
var point = this && this.point ? this.point : {},
name = isString(point.name) ? point.name : '';
return name;
},
/** @ignore-option */
inside: true,
/** @ignore-option */
verticalAlign: 'middle'
},
tooltip: {
headerFormat: '',
pointFormat: '<b>{point.name}</b>: {point.value}<br/>'
},
/**
* Whether to ignore hidden points when the layout algorithm runs.
* If `false`, hidden points will leave open spaces.
*
* @since 5.0.8
*/
ignoreHiddenPoint: true,
/**
* This option decides which algorithm is used for setting position
* and dimensions of the points.
*
* @see [How to write your own algorithm](https://www.highcharts.com/docs/chart-and-series-types/treemap)
*
* @sample {highcharts} highcharts/plotoptions/treemap-layoutalgorithm-sliceanddice/
* SliceAndDice by default
* @sample {highcharts} highcharts/plotoptions/treemap-layoutalgorithm-stripes/
* Stripes
* @sample {highcharts} highcharts/plotoptions/treemap-layoutalgorithm-squarified/
* Squarified
* @sample {highcharts} highcharts/plotoptions/treemap-layoutalgorithm-strip/
* Strip
*
* @since 4.1.0
* @validvalue ["sliceAndDice", "stripes", "squarified", "strip"]
*/
layoutAlgorithm: 'sliceAndDice',
/**
* Defines which direction the layout algorithm will start drawing.
*
* @since 4.1.0
* @validvalue ["vertical", "horizontal"]
*/
layoutStartingDirection: 'vertical',
/**
* Enabling this option will make the treemap alternate the drawing
* direction between vertical and horizontal. The next levels starting
* direction will always be the opposite of the previous.
*
* @sample {highcharts} highcharts/plotoptions/treemap-alternatestartingdirection-true/
* Enabled
*
* @since 4.1.0
*/
alternateStartingDirection: false,
/**
* Used together with the levels and allowTraversingTree options. When
* set to false the first level visible to be level one, which is
* dynamic when traversing the tree. Otherwise the level will be the
* same as the tree structure.
*
* @since 4.1.0
*/
levelIsConstant: true,
/**
* Options for the button appearing when drilling down in a treemap.
* Deprecated and replaced by
* [traverseUpButton](#plotOptions.treemap.traverseUpButton).
*
* @deprecated
*/
drillUpButton: {
/**
* The position of the button.
*
* @deprecated
*/
position: {
/**
* Vertical alignment of the button.
*
* @deprecated
* @type {Highcharts.VerticalAlignValue}
* @default top
* @product highcharts
* @apioption plotOptions.treemap.drillUpButton.position.verticalAlign
*/
/**
* Horizontal alignment of the button.
*
* @deprecated
* @type {Highcharts.AlignValue}
*/
align: 'right',
/**
* Horizontal offset of the button.
*
* @deprecated
*/
x: -10,
/**
* Vertical offset of the button.
*
* @deprecated
*/
y: 10
}
},
/**
* Options for the button appearing when traversing down in a treemap.
*/
traverseUpButton: {
/**
* The position of the button.
*/
position: {
/**
* Vertical alignment of the button.
*
* @type {Highcharts.VerticalAlignValue}
* @default top
* @product highcharts
* @apioption plotOptions.treemap.traverseUpButton.position.verticalAlign
*/
/**
* Horizontal alignment of the button.
*
* @type {Highcharts.AlignValue}
*/
align: 'right',
/**
* Horizontal offset of the button.
*/
x: -10,
/**
* Vertical offset of the button.
*/
y: 10
}
},
/**
* Set options on specific levels. Takes precedence over series options,
* but not point options.
*
* @sample {highcharts} highcharts/plotoptions/treemap-levels/
* Styling dataLabels and borders
* @sample {highcharts} highcharts/demo/treemap-with-levels/
* Different layoutAlgorithm
*
* @type {Array<*>}
* @since 4.1.0
* @product highcharts
* @apioption plotOptions.treemap.levels
*/
/**
* Can set a `borderColor` on all points which lies on the same level.
*
* @type {Highcharts.ColorString}
* @since 4.1.0
* @product highcharts
* @apioption plotOptions.treemap.levels.borderColor
*/
/**
* Set the dash style of the border of all the point which lies on the
* level. See <a href"#plotoptions.scatter.dashstyle">
* plotOptions.scatter.dashStyle</a> for possible options.
*
* @type {string}
* @since 4.1.0
* @product highcharts
* @apioption plotOptions.treemap.levels.borderDashStyle
*/
/**
* Can set the borderWidth on all points which lies on the same level.
*
* @type {number}
* @since 4.1.0
* @product highcharts
* @apioption plotOptions.treemap.levels.borderWidth
*/
/**
* Can set a color on all points which lies on the same level.
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @since 4.1.0
* @product highcharts
* @apioption plotOptions.treemap.levels.color
*/
/**
* A configuration object to define how the color of a child varies from
* the parent's color. The variation is distributed among the children
* of node. For example when setting brightness, the brightness change
* will range from the parent's original brightness on the first child,
* to the amount set in the `to` setting on the last node. This allows a
* gradient-like color scheme that sets children out from each other
* while highlighting the grouping on treemaps and sectors on sunburst
* charts.
*
* @sample highcharts/demo/sunburst/
* Sunburst with color variation
*
* @since 6.0.0
* @product highcharts
* @apioption plotOptions.treemap.levels.colorVariation
*/
/**
* The key of a color variation. Currently supports `brightness` only.
*
* @type {string}
* @since 6.0.0
* @product highcharts
* @validvalue ["brightness"]
* @apioption plotOptions.treemap.levels.colorVariation.key
*/
/**
* The ending value of a color variation. The last sibling will receive
* this value.
*
* @type {number}
* @since 6.0.0
* @product highcharts
* @apioption plotOptions.treemap.levels.colorVariation.to
*/
/**
* Can set the options of dataLabels on each point which lies on the
* level.
* [plotOptions.treemap.dataLabels](#plotOptions.treemap.dataLabels) for
* possible values.
*
* @type {object}
* @since 4.1.0
* @product highcharts
* @apioption plotOptions.treemap.levels.dataLabels
*/
/**
* Can set the layoutAlgorithm option on a specific level.
*
* @type {string}
* @since 4.1.0
* @product highcharts
* @validvalue ["sliceAndDice", "stripes", "squarified", "strip"]
* @apioption plotOptions.treemap.levels.layoutAlgorithm
*/
/**
* Can set the layoutStartingDirection option on a specific level.
*
* @type {string}
* @since 4.1.0
* @product highcharts
* @validvalue ["vertical", "horizontal"]
* @apioption plotOptions.treemap.levels.layoutStartingDirection
*/
/**
* Decides which level takes effect from the options set in the levels
* object.
*
* @sample {highcharts} highcharts/plotoptions/treemap-levels/
* Styling of both levels
*
* @type {number}
* @since 4.1.0
* @product highcharts
* @apioption plotOptions.treemap.levels.level
*/
// Presentational options
/**
* The color of the border surrounding each tree map item.
*
* @type {Highcharts.ColorString}
*/
borderColor: '#e6e6e6',
/**
* The width of the border surrounding each tree map item.
*/
borderWidth: 1,
/**
* The opacity of a point in treemap. When a point has children, the
* visibility of the children is determined by the opacity.
*
* @since 4.2.4
*/
opacity: 0.15,
/**
* A wrapper object for all the series options in specific states.
*
* @extends plotOptions.heatmap.states
*/
states: {
/**
* Options for the hovered series
*
* @extends plotOptions.heatmap.states.hover
* @excluding halo
*/
hover: {
/**
* The border color for the hovered state.
*/
borderColor: '#999999',
/**
* Brightness for the hovered point. Defaults to 0 if the
* heatmap series is loaded first, otherwise 0.1.
*
* @type {number}
* @default undefined
*/
brightness: seriesTypes.heatmap ? 0 : 0.1,
/**
* @extends plotOptions.heatmap.states.hover.halo
*/
halo: false,
/**
* The opacity of a point in treemap. When a point has children,
* the visibility of the children is determined by the opacity.
*
* @since 4.2.4
*/
opacity: 0.75,
/**
* The shadow option for hovered state.
*/
shadow: false
}
}
// Prototype members
}, {
pointArrayMap: ['value'],
directTouch: true,
optionalAxis: 'colorAxis',
getSymbol: noop,
parallelArrays: ['x', 'y', 'value', 'colorValue'],
colorKey: 'colorValue', // Point color option key
trackerGroups: ['group', 'dataLabelsGroup'],
/**
* Creates an object map from parent id to childrens index.
*
* @private
* @function Highcharts.Series#getListOfParents
*
* @param {Highcharts.SeriesTreemapDataOptions} data
* List of points set in options.
*
* @param {Array<string>} existingIds
* List of all point ids.
*
* @return {object}
* Map from parent id to children index in data.
*/
getListOfParents: function (data, existingIds) {
var arr = isArray(data) ? data : [],
ids = isArray(existingIds) ? existingIds : [],
listOfParents = arr.reduce(function (prev, curr, i) {
var parent = pick(curr.parent, '');
if (prev[parent] === undefined) {
prev[parent] = [];
}
prev[parent].push(i);
return prev;
}, {
'': [] // Root of tree
});
// If parent does not exist, hoist parent to root of tree.
eachObject(listOfParents, function (children, parent, list) {
if ((parent !== '') && (ids.indexOf(parent) === -1)) {
children.forEach(function (child) {
list[''].push(child);
});
delete list[parent];
}
});
return listOfParents;
},
// Creates a tree structured object from the series points
getTree: function () {
var series = this,
allIds = this.data.map(function (d) {
return d.id;
}),
parentList = series.getListOfParents(this.data, allIds);
series.nodeMap = [];
return series.buildNode('', -1, 0, parentList, null);
},
// Define hasData function for non-cartesian series.
// Returns true if the series has points at all.
hasData: function () {
return !!this.processedXData.length; // != 0
},
init: function (chart, options) {
var series = this,
colorSeriesMixin = H.colorSeriesMixin;
// If color series logic is loaded, add some properties
if (H.colorSeriesMixin) {
this.translateColors = colorSeriesMixin.translateColors;
this.colorAttribs = colorSeriesMixin.colorAttribs;
this.axisTypes = colorSeriesMixin.axisTypes;
}
// Handle deprecated options.
addEvent(series, 'setOptions', function (event) {
var options = event.userOptions;
if (
defined(options.allowDrillToNode) &&
!defined(options.allowTraversingTree)
) {
options.allowTraversingTree = options.allowDrillToNode;
delete options.allowDrillToNode;
}
if (
defined(options.drillUpButton) &&
!defined(options.traverseUpButton)
) {
options.traverseUpButton = options.drillUpButton;
delete options.drillUpButton;
}
});
Series.prototype.init.call(series, chart, options);
if (series.options.allowTraversingTree) {
addEvent(series, 'click', series.onClickDrillToNode);
}
},
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;
},
setTreeValues: function (tree) {
var series = this,
options = series.options,
idRoot = series.rootNode,
mapIdToNode = series.nodeMap,
nodeRoot = mapIdToNode[idRoot],
levelIsConstant = (
isBoolean(options.levelIsConstant) ?
options.levelIsConstant :
true
),
childrenTotal = 0,
children = [],
val,
point = series.points[tree.i];
// First give the children some values
tree.children.forEach(function (child) {
child = series.setTreeValues(child);
children.push(child);
if (!child.ignore) {
childrenTotal += child.val;
}
});
// Sort the children
stableSort(children, function (a, b) {
return a.sortIndex - b.sortIndex;
});
// Set the values
val = pick(point && point.options.value, childrenTotal);
if (point) {
point.value = val;
}
extend(tree, {
children: children,
childrenTotal: childrenTotal,
// Ignore this node if point is not visible
ignore: !(pick(point && point.visible, true) && (val > 0)),
isLeaf: tree.visible && !childrenTotal,
levelDynamic: (
tree.level - (levelIsConstant ? 0 : nodeRoot.level)
),
name: pick(point && point.name, ''),
sortIndex: pick(point && point.sortIndex, -val),
val: val
});
return tree;
},
/**
* 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.
*/
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),
width: (values.width / series.axisRatio)
});
// If node has children, then call method recursively
if (child.children.length) {
series.calculateChildrenAreas(child, child.values);
}
});
},
setPointValues: function () {
var series = this,
xAxis = series.xAxis,
yAxis = series.yAxis;
series.points.forEach(function (point) {
var node = point.node,
values = node.pointValues,
x1,
x2,
y1,
y2,
crispCorr = 0;
// Get the crisp correction in classic mode. For this to work in
// styled mode, we would need to first add the shape (without x,
// y, width and height), then read the rendered stroke width
// using point.graphic.strokeWidth(), then modify and apply the
// shapeArgs. This applies also to column series, but the
// downside is performance and code complexity.
if (!series.chart.styledMode) {
crispCorr = (
(series.pointAttribs(point)['stroke-width'] || 0) % 2
) / 2;
}
// Points which is ignored, have no values.
if (values && node.visible) {
x1 = Math.round(
xAxis.translate(values.x, 0, 0, 0, 1)
) - crispCorr;
x2 = Math.round(
xAxis.translate(values.x + values.width, 0, 0, 0, 1)
) - crispCorr;
y1 = Math.round(
yAxis.translate(values.y, 0, 0, 0, 1)
) - crispCorr;
y2 = Math.round(
yAxis.translate(values.y + values.height, 0, 0, 0, 1)
) - crispCorr;
// Set point values
point.shapeArgs = {
x: Math.min(x1, x2),
y: Math.min(y1, y2),
width: Math.abs(x2 - x1),
height: Math.abs(y2 - y1)
};
point.plotX =
point.shapeArgs.x + (point.shapeArgs.width / 2);
point.plotY =
point.shapeArgs.y + (point.shapeArgs.height / 2);
} else {
// Reset visibility
delete point.plotX;
delete point.plotY;
}
});
},
// Set the node's color recur