highcharts-rails 5.0.14 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.markdown +60 -0
- data/Rakefile +54 -5
- data/app/assets/images/highcharts/earth.svg +432 -0
- data/app/assets/javascripts/highcharts.js +5103 -3147
- data/app/assets/javascripts/highcharts/highcharts-3d.js +930 -277
- data/app/assets/javascripts/highcharts/highcharts-more.js +1374 -249
- data/app/assets/javascripts/highcharts/lib/canvg.js +3073 -0
- data/app/assets/javascripts/highcharts/lib/jspdf.js +16624 -0
- data/app/assets/javascripts/highcharts/lib/rgbcolor.js +299 -0
- data/app/assets/javascripts/highcharts/lib/svg2pdf.js +3488 -0
- data/app/assets/javascripts/highcharts/modules/accessibility.js +654 -212
- data/app/assets/javascripts/highcharts/modules/annotations.js +1552 -274
- data/app/assets/javascripts/highcharts/modules/boost-canvas.js +773 -0
- data/app/assets/javascripts/highcharts/modules/boost.js +636 -210
- data/app/assets/javascripts/highcharts/modules/broken-axis.js +2 -2
- data/app/assets/javascripts/highcharts/modules/bullet.js +364 -0
- data/app/assets/javascripts/highcharts/modules/data.js +766 -38
- data/app/assets/javascripts/highcharts/modules/drag-panes.js +588 -0
- data/app/assets/javascripts/highcharts/modules/drilldown.js +106 -36
- data/app/assets/javascripts/highcharts/modules/export-data.js +597 -0
- data/app/assets/javascripts/highcharts/modules/exporting.js +424 -162
- data/app/assets/javascripts/highcharts/modules/funnel.js +144 -22
- data/app/assets/javascripts/highcharts/modules/gantt.js +1154 -0
- data/app/assets/javascripts/highcharts/modules/grid-axis.js +1 -1
- data/app/assets/javascripts/highcharts/modules/heatmap.js +406 -80
- data/app/assets/javascripts/highcharts/modules/histogram-bellcurve.js +513 -0
- data/app/assets/javascripts/highcharts/modules/item-series.js +126 -0
- data/app/assets/javascripts/highcharts/modules/no-data-to-display.js +31 -13
- data/app/assets/javascripts/highcharts/modules/offline-exporting.js +179 -57
- data/app/assets/javascripts/highcharts/modules/oldie.js +1378 -0
- data/app/assets/javascripts/highcharts/modules/overlapping-datalabels.js +8 -6
- data/app/assets/javascripts/highcharts/modules/parallel-coordinates.js +494 -0
- data/app/assets/javascripts/highcharts/modules/pareto.js +275 -0
- data/app/assets/javascripts/highcharts/modules/sankey.js +641 -0
- data/app/assets/javascripts/highcharts/modules/series-label.js +355 -145
- data/app/assets/javascripts/highcharts/modules/solid-gauge.js +122 -1
- data/app/assets/javascripts/highcharts/modules/static-scale.js +64 -0
- data/app/assets/javascripts/highcharts/modules/stock.js +1944 -676
- data/app/assets/javascripts/highcharts/modules/streamgraph.js +139 -0
- data/app/assets/javascripts/highcharts/modules/sunburst.js +2403 -0
- data/app/assets/javascripts/highcharts/modules/tilemap.js +1199 -0
- data/app/assets/javascripts/highcharts/modules/treemap.js +538 -134
- data/app/assets/javascripts/highcharts/modules/variable-pie.js +490 -0
- data/app/assets/javascripts/highcharts/modules/variwide.js +283 -0
- data/app/assets/javascripts/highcharts/modules/vector.js +294 -0
- data/app/assets/javascripts/highcharts/modules/windbarb.js +490 -0
- data/app/assets/javascripts/highcharts/modules/wordcloud.js +681 -0
- data/app/assets/javascripts/highcharts/modules/xrange.js +615 -0
- data/app/assets/javascripts/highcharts/themes/avocado.js +54 -0
- data/app/assets/javascripts/highcharts/themes/dark-blue.js +6 -6
- data/app/assets/javascripts/highcharts/themes/dark-green.js +6 -6
- data/app/assets/javascripts/highcharts/themes/dark-unica.js +6 -6
- data/app/assets/javascripts/highcharts/themes/gray.js +14 -10
- data/app/assets/javascripts/highcharts/themes/grid-light.js +6 -6
- data/app/assets/javascripts/highcharts/themes/grid.js +7 -5
- data/app/assets/javascripts/highcharts/themes/sand-signika.js +8 -7
- data/app/assets/javascripts/highcharts/themes/skies.js +15 -9
- data/app/assets/javascripts/highcharts/themes/sunset.js +53 -0
- data/app/assets/stylesheets/highcharts/highcharts.css +802 -0
- data/app/assets/stylesheets/highcharts/highcharts.scss +665 -0
- data/lib/highcharts/version.rb +1 -1
- metadata +31 -1
@@ -0,0 +1,139 @@
|
|
1
|
+
/**
|
2
|
+
* @license Highcharts JS v6.0.0 (2017-10-04)
|
3
|
+
* Streamgraph module
|
4
|
+
*
|
5
|
+
* (c) 2010-2017 Torstein Honsi
|
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
|
+
(function(H) {
|
18
|
+
/**
|
19
|
+
* Streamgraph module
|
20
|
+
*
|
21
|
+
* (c) 2010-2017 Torstein Honsi
|
22
|
+
*
|
23
|
+
* License: www.highcharts.com/license
|
24
|
+
*/
|
25
|
+
|
26
|
+
var seriesType = H.seriesType;
|
27
|
+
|
28
|
+
/**
|
29
|
+
* A streamgraph is a type of stacked area graph which is displaced around a
|
30
|
+
* central axis, resulting in a flowing, organic shape.
|
31
|
+
*
|
32
|
+
* @extends {plotOptions.areaspline}
|
33
|
+
* @product highcharts highstock
|
34
|
+
* @sample {highcharts|highstock} highcharts/demo/streamgraph/
|
35
|
+
* Streamgraph
|
36
|
+
* @since 6.0.0
|
37
|
+
* @optionparent plotOptions.streamgraph
|
38
|
+
*/
|
39
|
+
seriesType('streamgraph', 'areaspline', {
|
40
|
+
fillOpacity: 1,
|
41
|
+
lineWidth: 0,
|
42
|
+
marker: {
|
43
|
+
enabled: false
|
44
|
+
},
|
45
|
+
stacking: 'stream'
|
46
|
+
// Prototype functions
|
47
|
+
}, {
|
48
|
+
negStacks: false,
|
49
|
+
|
50
|
+
/**
|
51
|
+
* Modifier function for stream stacks. It simply moves the point up or down
|
52
|
+
* in order to center the full stack vertically.
|
53
|
+
*/
|
54
|
+
streamStacker: function(pointExtremes, stack, i) {
|
55
|
+
// Y bottom value
|
56
|
+
pointExtremes[0] -= stack.total / 2;
|
57
|
+
// Y value
|
58
|
+
pointExtremes[1] -= stack.total / 2;
|
59
|
+
this.stackedYData[i] = this.index === 0 ?
|
60
|
+
pointExtremes[1] :
|
61
|
+
pointExtremes[0];
|
62
|
+
}
|
63
|
+
});
|
64
|
+
|
65
|
+
|
66
|
+
/**
|
67
|
+
* A `streamgraph` series. If the [type](#series.streamgraph.type) option is not
|
68
|
+
* specified, it is inherited from [chart.type](#chart.type).
|
69
|
+
*
|
70
|
+
* For options that apply to multiple series, it is recommended to add
|
71
|
+
* them to the [plotOptions.series](#plotOptions.series) options structure.
|
72
|
+
* To apply to all series of this specific type, apply it to [plotOptions.
|
73
|
+
* streamgraph](#plotOptions.streamgraph).
|
74
|
+
*
|
75
|
+
* @type {Object}
|
76
|
+
* @extends series,plotOptions.streamgraph
|
77
|
+
* @excluding dataParser,dataURL
|
78
|
+
* @product highcharts highstock
|
79
|
+
* @apioption series.streamgraph
|
80
|
+
*/
|
81
|
+
|
82
|
+
/**
|
83
|
+
* An array of data points for the series. For the `streamgraph` series type,
|
84
|
+
* points can be given in the following ways:
|
85
|
+
*
|
86
|
+
* 1. An array of numerical values. In this case, the numerical values
|
87
|
+
* will be interpreted as `y` options. The `x` values will be automatically
|
88
|
+
* calculated, either starting at 0 and incremented by 1, or from `pointStart`
|
89
|
+
* and `pointInterval` given in the series options. If the axis has
|
90
|
+
* categories, these will be used. Example:
|
91
|
+
*
|
92
|
+
* ```js
|
93
|
+
* data: [0, 5, 3, 5]
|
94
|
+
* ```
|
95
|
+
*
|
96
|
+
* 2. An array of arrays with 2 values. In this case, the values correspond
|
97
|
+
* to `x,y`. If the first value is a string, it is applied as the name
|
98
|
+
* of the point, and the `x` value is inferred.
|
99
|
+
*
|
100
|
+
* ```js
|
101
|
+
* data: [
|
102
|
+
* [0, 9],
|
103
|
+
* [1, 7],
|
104
|
+
* [2, 6]
|
105
|
+
* ]
|
106
|
+
* ```
|
107
|
+
*
|
108
|
+
* 3. An array of objects with named values. The objects are point
|
109
|
+
* configuration objects as seen below. If the total number of data
|
110
|
+
* points exceeds the series' [turboThreshold](#series.area.turboThreshold),
|
111
|
+
* this option is not available.
|
112
|
+
*
|
113
|
+
* ```js
|
114
|
+
* data: [{
|
115
|
+
* x: 1,
|
116
|
+
* y: 9,
|
117
|
+
* name: "Point2",
|
118
|
+
* color: "#00FF00"
|
119
|
+
* }, {
|
120
|
+
* x: 1,
|
121
|
+
* y: 6,
|
122
|
+
* name: "Point1",
|
123
|
+
* color: "#FF00FF"
|
124
|
+
* }]
|
125
|
+
* ```
|
126
|
+
*
|
127
|
+
* @type {Array<Object|Array|Number>}
|
128
|
+
* @extends series.line.data
|
129
|
+
* @sample {highcharts} highcharts/chart/reflow-true/ Numerical values
|
130
|
+
* @sample {highcharts} highcharts/series/data-array-of-arrays/ Arrays of numeric x and y
|
131
|
+
* @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/ Arrays of datetime x and y
|
132
|
+
* @sample {highcharts} highcharts/series/data-array-of-name-value/ Arrays of point.name and y
|
133
|
+
* @sample {highcharts} highcharts/series/data-array-of-objects/ Config objects
|
134
|
+
* @product highcharts highstock
|
135
|
+
* @apioption series.streamgraph.data
|
136
|
+
*/
|
137
|
+
|
138
|
+
}(Highcharts));
|
139
|
+
}));
|
@@ -0,0 +1,2403 @@
|
|
1
|
+
/**
|
2
|
+
* @license Highcharts JS v6.0.0 (2017-10-04)
|
3
|
+
*
|
4
|
+
* (c) 2016 Highsoft AS
|
5
|
+
* Authors: Jon Arild Nygard
|
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
|
+
(function(H) {
|
18
|
+
/**
|
19
|
+
* (c) 2010-2017 Torstein Honsi
|
20
|
+
*
|
21
|
+
* License: www.highcharts.com/license
|
22
|
+
*/
|
23
|
+
var deg2rad = H.deg2rad,
|
24
|
+
isNumber = H.isNumber,
|
25
|
+
pick = H.pick,
|
26
|
+
relativeLength = H.relativeLength;
|
27
|
+
H.CenteredSeriesMixin = {
|
28
|
+
/**
|
29
|
+
* Get the center of the pie based on the size and center options relative to the
|
30
|
+
* plot area. Borrowed by the polar and gauge series types.
|
31
|
+
*/
|
32
|
+
getCenter: function() {
|
33
|
+
|
34
|
+
var options = this.options,
|
35
|
+
chart = this.chart,
|
36
|
+
slicingRoom = 2 * (options.slicedOffset || 0),
|
37
|
+
handleSlicingRoom,
|
38
|
+
plotWidth = chart.plotWidth - 2 * slicingRoom,
|
39
|
+
plotHeight = chart.plotHeight - 2 * slicingRoom,
|
40
|
+
centerOption = options.center,
|
41
|
+
positions = [pick(centerOption[0], '50%'), pick(centerOption[1], '50%'), options.size || '100%', options.innerSize || 0],
|
42
|
+
smallestSize = Math.min(plotWidth, plotHeight),
|
43
|
+
i,
|
44
|
+
value;
|
45
|
+
|
46
|
+
for (i = 0; i < 4; ++i) {
|
47
|
+
value = positions[i];
|
48
|
+
handleSlicingRoom = i < 2 || (i === 2 && /%$/.test(value));
|
49
|
+
|
50
|
+
// i == 0: centerX, relative to width
|
51
|
+
// i == 1: centerY, relative to height
|
52
|
+
// i == 2: size, relative to smallestSize
|
53
|
+
// i == 3: innerSize, relative to size
|
54
|
+
positions[i] = relativeLength(value, [plotWidth, plotHeight, smallestSize, positions[2]][i]) +
|
55
|
+
(handleSlicingRoom ? slicingRoom : 0);
|
56
|
+
|
57
|
+
}
|
58
|
+
// innerSize cannot be larger than size (#3632)
|
59
|
+
if (positions[3] > positions[2]) {
|
60
|
+
positions[3] = positions[2];
|
61
|
+
}
|
62
|
+
return positions;
|
63
|
+
},
|
64
|
+
/**
|
65
|
+
* getStartAndEndRadians - Calculates start and end angles in radians.
|
66
|
+
* Used in series types such as pie and sunburst.
|
67
|
+
*
|
68
|
+
* @param {Number} start Start angle in degrees.
|
69
|
+
* @param {Number} end Start angle in degrees.
|
70
|
+
* @return {object} Returns an object containing start and end angles as
|
71
|
+
* radians.
|
72
|
+
*/
|
73
|
+
getStartAndEndRadians: function getStartAndEndRadians(start, end) {
|
74
|
+
var startAngle = isNumber(start) ? start : 0, // must be a number
|
75
|
+
endAngle = (
|
76
|
+
(
|
77
|
+
isNumber(end) && // must be a number
|
78
|
+
end > startAngle && // must be larger than the start angle
|
79
|
+
// difference must be less than 360 degrees
|
80
|
+
(end - startAngle) < 360
|
81
|
+
) ?
|
82
|
+
end :
|
83
|
+
startAngle + 360
|
84
|
+
),
|
85
|
+
correction = -90;
|
86
|
+
return {
|
87
|
+
start: deg2rad * (startAngle + correction),
|
88
|
+
end: deg2rad * (endAngle + correction)
|
89
|
+
};
|
90
|
+
}
|
91
|
+
};
|
92
|
+
|
93
|
+
}(Highcharts));
|
94
|
+
var draw = (function() {
|
95
|
+
var isFn = function(x) {
|
96
|
+
return typeof x === 'function';
|
97
|
+
};
|
98
|
+
|
99
|
+
/**
|
100
|
+
* draw - Handles the drawing of a point.
|
101
|
+
* TODO: add type checking.
|
102
|
+
*
|
103
|
+
* @param {object} params Parameters.
|
104
|
+
* @return {undefined} Returns undefined.
|
105
|
+
*/
|
106
|
+
var draw = function draw(params) {
|
107
|
+
var point = this,
|
108
|
+
graphic = point.graphic,
|
109
|
+
animate = params.animate,
|
110
|
+
attr = params.attr,
|
111
|
+
onComplete = params.onComplete,
|
112
|
+
css = params.css,
|
113
|
+
group = params.group,
|
114
|
+
renderer = params.renderer,
|
115
|
+
shape = params.shapeArgs,
|
116
|
+
type = params.shapeType;
|
117
|
+
|
118
|
+
if (point.shouldDraw()) {
|
119
|
+
if (!graphic) {
|
120
|
+
point.graphic = graphic = renderer[type](shape).add(group);
|
121
|
+
}
|
122
|
+
graphic.css(css).attr(attr).animate(animate, undefined, onComplete);
|
123
|
+
} else if (graphic) {
|
124
|
+
graphic.animate(animate, undefined, function() {
|
125
|
+
point.graphic = graphic = graphic.destroy();
|
126
|
+
if (isFn(onComplete)) {
|
127
|
+
onComplete();
|
128
|
+
}
|
129
|
+
});
|
130
|
+
}
|
131
|
+
};
|
132
|
+
return draw;
|
133
|
+
}());
|
134
|
+
var result = (function(H) {
|
135
|
+
var each = H.each,
|
136
|
+
extend = H.extend,
|
137
|
+
isBoolean = function(x) {
|
138
|
+
return typeof x === 'boolean';
|
139
|
+
},
|
140
|
+
isFn = function(x) {
|
141
|
+
return typeof x === 'function';
|
142
|
+
},
|
143
|
+
pick = H.pick;
|
144
|
+
// TODO Combine buildTree and buildNode with setTreeValues
|
145
|
+
// TODO Remove logic from Treemap and make it utilize this mixin.
|
146
|
+
var setTreeValues = function setTreeValues(tree, options) {
|
147
|
+
var before = options.before,
|
148
|
+
idRoot = options.idRoot,
|
149
|
+
mapIdToNode = options.mapIdToNode,
|
150
|
+
nodeRoot = mapIdToNode[idRoot],
|
151
|
+
levelIsConstant = (
|
152
|
+
isBoolean(options.levelIsConstant) ?
|
153
|
+
options.levelIsConstant :
|
154
|
+
true
|
155
|
+
),
|
156
|
+
points = options.points,
|
157
|
+
point = points[tree.i],
|
158
|
+
optionsPoint = point && point.options || {},
|
159
|
+
childrenTotal = 0,
|
160
|
+
children = [],
|
161
|
+
value;
|
162
|
+
extend(tree, {
|
163
|
+
levelDynamic: tree.level - (levelIsConstant ? 0 : nodeRoot.level),
|
164
|
+
name: pick(point && point.name, ''),
|
165
|
+
visible: (
|
166
|
+
idRoot === tree.id ||
|
167
|
+
(isBoolean(options.visible) ? options.visible : false)
|
168
|
+
)
|
169
|
+
});
|
170
|
+
if (isFn(before)) {
|
171
|
+
tree = before(tree, options);
|
172
|
+
}
|
173
|
+
// First give the children some values
|
174
|
+
each(tree.children, function(child, i) {
|
175
|
+
var newOptions = extend({}, options);
|
176
|
+
extend(newOptions, {
|
177
|
+
index: i,
|
178
|
+
siblings: tree.children.length,
|
179
|
+
visible: tree.visible
|
180
|
+
});
|
181
|
+
child = setTreeValues(child, newOptions);
|
182
|
+
children.push(child);
|
183
|
+
if (child.visible) {
|
184
|
+
childrenTotal += child.val;
|
185
|
+
}
|
186
|
+
});
|
187
|
+
tree.visible = childrenTotal > 0 || tree.visible;
|
188
|
+
// Set the values
|
189
|
+
value = pick(optionsPoint.value, childrenTotal);
|
190
|
+
extend(tree, {
|
191
|
+
children: children,
|
192
|
+
childrenTotal: childrenTotal,
|
193
|
+
isLeaf: tree.visible && !childrenTotal,
|
194
|
+
val: value
|
195
|
+
});
|
196
|
+
return tree;
|
197
|
+
};
|
198
|
+
|
199
|
+
var getColor = function getColor(node, options) {
|
200
|
+
var index = options.index,
|
201
|
+
levelMap = options.levelMap,
|
202
|
+
parentColor = options.parentColor,
|
203
|
+
parentColorIndex = options.parentColorIndex,
|
204
|
+
series = options.series,
|
205
|
+
colors = options.colors,
|
206
|
+
siblings = options.siblings,
|
207
|
+
points = series.points,
|
208
|
+
getColorByPoint,
|
209
|
+
point,
|
210
|
+
level,
|
211
|
+
colorByPoint,
|
212
|
+
color,
|
213
|
+
colorIndex;
|
214
|
+
|
215
|
+
function variation(color) {
|
216
|
+
var colorVariation = level && level.colorVariation;
|
217
|
+
if (colorVariation) {
|
218
|
+
if (colorVariation.key === 'brightness') {
|
219
|
+
return H.color(color).brighten(
|
220
|
+
colorVariation.to * (index / siblings)
|
221
|
+
).get();
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
225
|
+
return color;
|
226
|
+
}
|
227
|
+
|
228
|
+
if (node) {
|
229
|
+
point = points[node.i];
|
230
|
+
level = levelMap[node.levelDynamic] || {};
|
231
|
+
getColorByPoint = (
|
232
|
+
point &&
|
233
|
+
(
|
234
|
+
isBoolean(level.colorByPoint) ?
|
235
|
+
level.colorByPoint :
|
236
|
+
!!series.options.colorByPoint
|
237
|
+
)
|
238
|
+
);
|
239
|
+
if (getColorByPoint) {
|
240
|
+
colorByPoint = colors[(point.index % colors.length)];
|
241
|
+
}
|
242
|
+
// Select either point color, level color or inherited color.
|
243
|
+
color = pick(
|
244
|
+
point && point.options.color,
|
245
|
+
level && level.color,
|
246
|
+
colorByPoint,
|
247
|
+
parentColor && variation(parentColor),
|
248
|
+
series.color
|
249
|
+
);
|
250
|
+
colorIndex = pick(
|
251
|
+
point && point.options.colorIndex,
|
252
|
+
level && level.colorIndex,
|
253
|
+
parentColorIndex,
|
254
|
+
options.colorIndex
|
255
|
+
);
|
256
|
+
}
|
257
|
+
return {
|
258
|
+
color: color,
|
259
|
+
colorIndex: colorIndex
|
260
|
+
};
|
261
|
+
};
|
262
|
+
|
263
|
+
var result = {
|
264
|
+
getColor: getColor,
|
265
|
+
setTreeValues: setTreeValues
|
266
|
+
};
|
267
|
+
return result;
|
268
|
+
}(Highcharts));
|
269
|
+
(function(H, mixinTreeSeries) {
|
270
|
+
/**
|
271
|
+
* (c) 2014 Highsoft AS
|
272
|
+
* Authors: Jon Arild Nygard / Oystein Moseng
|
273
|
+
*
|
274
|
+
* License: www.highcharts.com/license
|
275
|
+
*/
|
276
|
+
|
277
|
+
var seriesType = H.seriesType,
|
278
|
+
seriesTypes = H.seriesTypes,
|
279
|
+
map = H.map,
|
280
|
+
merge = H.merge,
|
281
|
+
extend = H.extend,
|
282
|
+
noop = H.noop,
|
283
|
+
each = H.each,
|
284
|
+
getColor = mixinTreeSeries.getColor,
|
285
|
+
grep = H.grep,
|
286
|
+
isNumber = H.isNumber,
|
287
|
+
isString = H.isString,
|
288
|
+
pick = H.pick,
|
289
|
+
Series = H.Series,
|
290
|
+
stableSort = H.stableSort,
|
291
|
+
color = H.Color,
|
292
|
+
eachObject = function(list, func, context) {
|
293
|
+
context = context || this;
|
294
|
+
H.objectEach(list, function(val, key) {
|
295
|
+
func.call(context, val, key, list);
|
296
|
+
});
|
297
|
+
},
|
298
|
+
reduce = H.reduce,
|
299
|
+
// @todo find correct name for this function.
|
300
|
+
// @todo Similar to reduce, this function is likely redundant
|
301
|
+
recursive = function(item, func, context) {
|
302
|
+
var next;
|
303
|
+
context = context || this;
|
304
|
+
next = func.call(context, item);
|
305
|
+
if (next !== false) {
|
306
|
+
recursive(next, func, context);
|
307
|
+
}
|
308
|
+
};
|
309
|
+
|
310
|
+
/**
|
311
|
+
* A treemap displays hierarchical data using nested rectangles. The data can be
|
312
|
+
* laid out in varying ways depending on options.
|
313
|
+
*
|
314
|
+
* @sample highcharts/demo/treemap-large-dataset/ Treemap
|
315
|
+
*
|
316
|
+
* @extends {plotOptions.scatter}
|
317
|
+
* @excluding marker
|
318
|
+
* @product highcharts
|
319
|
+
* @optionparent plotOptions.treemap
|
320
|
+
*/
|
321
|
+
seriesType('treemap', 'scatter', {
|
322
|
+
|
323
|
+
/**
|
324
|
+
* When enabled the user can click on a point which is a parent and
|
325
|
+
* zoom in on its children.
|
326
|
+
*
|
327
|
+
* @type {Boolean}
|
328
|
+
* @sample {highcharts} highcharts/plotoptions/treemap-allowdrilltonode/ Enabled
|
329
|
+
* @default false
|
330
|
+
* @since 4.1.0
|
331
|
+
* @product highcharts
|
332
|
+
* @apioption plotOptions.treemap.allowDrillToNode
|
333
|
+
*/
|
334
|
+
|
335
|
+
/**
|
336
|
+
* When the series contains less points than the crop threshold, all
|
337
|
+
* points are drawn, event if the points fall outside the visible plot
|
338
|
+
* area at the current zoom. The advantage of drawing all points (including
|
339
|
+
* markers and columns), is that animation is performed on updates.
|
340
|
+
* On the other hand, when the series contains more points than the
|
341
|
+
* crop threshold, the series data is cropped to only contain points
|
342
|
+
* that fall within the plot area. The advantage of cropping away invisible
|
343
|
+
* points is to increase performance on large series.
|
344
|
+
*
|
345
|
+
* @type {Number}
|
346
|
+
* @default 300
|
347
|
+
* @since 4.1.0
|
348
|
+
* @product highcharts
|
349
|
+
* @apioption plotOptions.treemap.cropThreshold
|
350
|
+
*/
|
351
|
+
|
352
|
+
/**
|
353
|
+
* This option decides if the user can interact with the parent nodes
|
354
|
+
* or just the leaf nodes. When this option is undefined, it will be
|
355
|
+
* true by default. However when allowDrillToNode is true, then it will
|
356
|
+
* be false by default.
|
357
|
+
*
|
358
|
+
* @type {Boolean}
|
359
|
+
* @sample {highcharts} highcharts/plotoptions/treemap-interactbyleaf-false/ False
|
360
|
+
* @sample {highcharts} highcharts/plotoptions/treemap-interactbyleaf-true-and-allowdrilltonode/ InteractByLeaf and allowDrillToNode is true
|
361
|
+
* @since 4.1.2
|
362
|
+
* @product highcharts
|
363
|
+
* @apioption plotOptions.treemap.interactByLeaf
|
364
|
+
*/
|
365
|
+
|
366
|
+
/**
|
367
|
+
* The sort index of the point inside the treemap level.
|
368
|
+
*
|
369
|
+
* @type {Number}
|
370
|
+
* @sample {highcharts} highcharts/plotoptions/treemap-sortindex/ Sort by years
|
371
|
+
* @since 4.1.10
|
372
|
+
* @product highcharts
|
373
|
+
* @apioption plotOptions.treemap.sortIndex
|
374
|
+
*/
|
375
|
+
|
376
|
+
/**
|
377
|
+
* Whether to display this series type or specific series item in the
|
378
|
+
* legend.
|
379
|
+
*
|
380
|
+
* @type {Boolean}
|
381
|
+
* @default false
|
382
|
+
* @product highcharts
|
383
|
+
*/
|
384
|
+
showInLegend: false,
|
385
|
+
|
386
|
+
/**
|
387
|
+
* @ignore
|
388
|
+
*/
|
389
|
+
marker: false,
|
390
|
+
|
391
|
+
/**
|
392
|
+
* @extends plotOptions.heatmap.dataLabels
|
393
|
+
* @since 4.1.0
|
394
|
+
* @product highcharts
|
395
|
+
*/
|
396
|
+
dataLabels: {
|
397
|
+
enabled: true,
|
398
|
+
defer: false,
|
399
|
+
verticalAlign: 'middle',
|
400
|
+
formatter: function() { // #2945
|
401
|
+
return this.point.name || this.point.id;
|
402
|
+
},
|
403
|
+
inside: true
|
404
|
+
},
|
405
|
+
|
406
|
+
tooltip: {
|
407
|
+
headerFormat: '',
|
408
|
+
pointFormat: '<b>{point.name}</b>: {point.value}<br/>'
|
409
|
+
},
|
410
|
+
|
411
|
+
/**
|
412
|
+
* Whether to ignore hidden points when the layout algorithm runs.
|
413
|
+
* If `false`, hidden points will leave open spaces.
|
414
|
+
*
|
415
|
+
* @type {Boolean}
|
416
|
+
* @default true
|
417
|
+
* @since 5.0.8
|
418
|
+
* @product highcharts
|
419
|
+
*/
|
420
|
+
ignoreHiddenPoint: true,
|
421
|
+
|
422
|
+
/**
|
423
|
+
* This option decides which algorithm is used for setting position
|
424
|
+
* and dimensions of the points. Can be one of `sliceAndDice`, `stripes`,
|
425
|
+
* `squarified` or `strip`.
|
426
|
+
*
|
427
|
+
* @validvalue ["sliceAndDice", "stripes", "squarified", "strip"]
|
428
|
+
* @type {String}
|
429
|
+
* @see [How to write your own algorithm](http://www.highcharts.com/docs/chart-
|
430
|
+
* and-series-types/treemap)
|
431
|
+
* @sample {highcharts} highcharts/plotoptions/treemap-layoutalgorithm-sliceanddice/ SliceAndDice by default
|
432
|
+
* @sample {highcharts} highcharts/plotoptions/treemap-layoutalgorithm-stripes/ Stripes
|
433
|
+
* @sample {highcharts} highcharts/plotoptions/treemap-layoutalgorithm-squarified/ Squarified
|
434
|
+
* @sample {highcharts} highcharts/plotoptions/treemap-layoutalgorithm-strip/ Strip
|
435
|
+
* @default sliceAndDice
|
436
|
+
* @since 4.1.0
|
437
|
+
* @product highcharts
|
438
|
+
*/
|
439
|
+
layoutAlgorithm: 'sliceAndDice',
|
440
|
+
|
441
|
+
/**
|
442
|
+
* Defines which direction the layout algorithm will start drawing.
|
443
|
+
* Possible values are "vertical" and "horizontal".
|
444
|
+
*
|
445
|
+
* @validvalue ["vertical", "horizontal"]
|
446
|
+
* @type {String}
|
447
|
+
* @default vertical
|
448
|
+
* @since 4.1.0
|
449
|
+
* @product highcharts
|
450
|
+
*/
|
451
|
+
layoutStartingDirection: 'vertical',
|
452
|
+
|
453
|
+
/**
|
454
|
+
* Enabling this option will make the treemap alternate the drawing
|
455
|
+
* direction between vertical and horizontal. The next levels starting
|
456
|
+
* direction will always be the opposite of the previous.
|
457
|
+
*
|
458
|
+
* @type {Boolean}
|
459
|
+
* @sample {highcharts} highcharts/plotoptions/treemap-alternatestartingdirection-true/ Enabled
|
460
|
+
* @default false
|
461
|
+
* @since 4.1.0
|
462
|
+
* @product highcharts
|
463
|
+
*/
|
464
|
+
alternateStartingDirection: false,
|
465
|
+
|
466
|
+
/**
|
467
|
+
* Used together with the levels and allowDrillToNode options. When
|
468
|
+
* set to false the first level visible when drilling is considered
|
469
|
+
* to be level one. Otherwise the level will be the same as the tree
|
470
|
+
* structure.
|
471
|
+
*
|
472
|
+
* @validvalue [true, false]
|
473
|
+
* @type {Boolean}
|
474
|
+
* @default true
|
475
|
+
* @since 4.1.0
|
476
|
+
* @product highcharts
|
477
|
+
*/
|
478
|
+
levelIsConstant: true,
|
479
|
+
|
480
|
+
/**
|
481
|
+
* Options for the button appearing when drilling down in a treemap.
|
482
|
+
*/
|
483
|
+
drillUpButton: {
|
484
|
+
|
485
|
+
/**
|
486
|
+
* The position of the button.
|
487
|
+
*/
|
488
|
+
position: {
|
489
|
+
|
490
|
+
/**
|
491
|
+
* Horizontal alignment of the button.
|
492
|
+
* @validvalue ["left", "center", "right"]
|
493
|
+
*/
|
494
|
+
align: 'right',
|
495
|
+
|
496
|
+
/**
|
497
|
+
* Horizontal offset of the button.
|
498
|
+
* @default -10
|
499
|
+
* @type {Number}
|
500
|
+
*/
|
501
|
+
x: -10,
|
502
|
+
|
503
|
+
/**
|
504
|
+
* Vertical offset of the button.
|
505
|
+
*/
|
506
|
+
y: 10
|
507
|
+
|
508
|
+
/**
|
509
|
+
* Vertical alignment of the button.
|
510
|
+
*
|
511
|
+
* @default top
|
512
|
+
* @validvalue ["top", "middle", "bottom"]
|
513
|
+
* @apioption plotOptions.treemap.drillUpButton.position.verticalAlign
|
514
|
+
*/
|
515
|
+
}
|
516
|
+
},
|
517
|
+
|
518
|
+
// Presentational options
|
519
|
+
|
520
|
+
/**
|
521
|
+
* The color of the border surrounding each tree map item.
|
522
|
+
*
|
523
|
+
* @type {Color}
|
524
|
+
* @default #e6e6e6
|
525
|
+
* @product highcharts
|
526
|
+
*/
|
527
|
+
borderColor: '#e6e6e6',
|
528
|
+
|
529
|
+
/**
|
530
|
+
* The width of the border surrounding each tree map item.
|
531
|
+
*/
|
532
|
+
borderWidth: 1,
|
533
|
+
|
534
|
+
/**
|
535
|
+
* The opacity of a point in treemap. When a point has children, the
|
536
|
+
* visibility of the children is determined by the opacity.
|
537
|
+
*
|
538
|
+
* @type {Number}
|
539
|
+
* @default 0.15
|
540
|
+
* @since 4.2.4
|
541
|
+
* @product highcharts
|
542
|
+
*/
|
543
|
+
opacity: 0.15,
|
544
|
+
|
545
|
+
/**
|
546
|
+
* A wrapper object for all the series options in specific states.
|
547
|
+
*
|
548
|
+
* @extends plotOptions.heatmap.states
|
549
|
+
* @product highcharts
|
550
|
+
*/
|
551
|
+
states: {
|
552
|
+
|
553
|
+
/**
|
554
|
+
* Options for the hovered series
|
555
|
+
*
|
556
|
+
* @extends plotOptions.heatmap.states.hover
|
557
|
+
* @excluding halo
|
558
|
+
* @product highcharts
|
559
|
+
*/
|
560
|
+
hover: {
|
561
|
+
|
562
|
+
/**
|
563
|
+
* The border color for the hovered state.
|
564
|
+
*/
|
565
|
+
borderColor: '#999999',
|
566
|
+
|
567
|
+
/**
|
568
|
+
* Brightness for the hovered point. Defaults to 0 if the heatmap
|
569
|
+
* series is loaded, otherwise 0.1.
|
570
|
+
*
|
571
|
+
* @default null
|
572
|
+
* @type {Number}
|
573
|
+
*/
|
574
|
+
brightness: seriesTypes.heatmap ? 0 : 0.1,
|
575
|
+
/**
|
576
|
+
* @extends plotOptions.heatmap.states.hover.halo
|
577
|
+
*/
|
578
|
+
halo: false,
|
579
|
+
/**
|
580
|
+
* The opacity of a point in treemap. When a point has children,
|
581
|
+
* the visibility of the children is determined by the opacity.
|
582
|
+
*
|
583
|
+
* @type {Number}
|
584
|
+
* @default 0.75
|
585
|
+
* @since 4.2.4
|
586
|
+
* @product highcharts
|
587
|
+
*/
|
588
|
+
opacity: 0.75,
|
589
|
+
|
590
|
+
/**
|
591
|
+
* The shadow option for hovered state.
|
592
|
+
*/
|
593
|
+
shadow: false
|
594
|
+
}
|
595
|
+
}
|
596
|
+
|
597
|
+
|
598
|
+
|
599
|
+
/**
|
600
|
+
* Set options on specific levels. Takes precedence over series options,
|
601
|
+
* but not point options.
|
602
|
+
*
|
603
|
+
* @type {Array<Object>}
|
604
|
+
* @sample {highcharts} highcharts/plotoptions/treemap-levels/
|
605
|
+
* Styling dataLabels and borders
|
606
|
+
* @sample {highcharts} highcharts/demo/treemap-with-levels/
|
607
|
+
* Different layoutAlgorithm
|
608
|
+
* @since 4.1.0
|
609
|
+
* @product highcharts
|
610
|
+
* @apioption plotOptions.treemap.levels
|
611
|
+
*/
|
612
|
+
|
613
|
+
/**
|
614
|
+
* Can set a `borderColor` on all points which lies on the same level.
|
615
|
+
*
|
616
|
+
* @type {Color}
|
617
|
+
* @since 4.1.0
|
618
|
+
* @product highcharts
|
619
|
+
* @apioption plotOptions.treemap.levels.borderColor
|
620
|
+
*/
|
621
|
+
|
622
|
+
/**
|
623
|
+
* Set the dash style of the border of all the point which lies on the
|
624
|
+
* level. See <a href"#plotoptions.scatter.dashstyle">
|
625
|
+
* plotOptions.scatter.dashStyle</a> for possible options.
|
626
|
+
*
|
627
|
+
* @type {String}
|
628
|
+
* @since 4.1.0
|
629
|
+
* @product highcharts
|
630
|
+
* @apioption plotOptions.treemap.levels.borderDashStyle
|
631
|
+
*/
|
632
|
+
|
633
|
+
/**
|
634
|
+
* Can set the borderWidth on all points which lies on the same level.
|
635
|
+
*
|
636
|
+
* @type {Number}
|
637
|
+
* @since 4.1.0
|
638
|
+
* @product highcharts
|
639
|
+
* @apioption plotOptions.treemap.levels.borderWidth
|
640
|
+
*/
|
641
|
+
|
642
|
+
/**
|
643
|
+
* Can set a color on all points which lies on the same level.
|
644
|
+
*
|
645
|
+
* @type {Color}
|
646
|
+
* @since 4.1.0
|
647
|
+
* @product highcharts
|
648
|
+
* @apioption plotOptions.treemap.levels.color
|
649
|
+
*/
|
650
|
+
|
651
|
+
/**
|
652
|
+
* A configuration object to define how the color of a child varies from the
|
653
|
+
* parent's color. The variation is distributed among the children of node.
|
654
|
+
* For example when setting brightness, the brightness change will range
|
655
|
+
* from the parent's original brightness on the first child, to the amount
|
656
|
+
* set in the `to` setting on the last node. This allows a gradient-like
|
657
|
+
* color scheme that sets children out from each other while highlighting
|
658
|
+
* the grouping on treemaps and sectors on sunburst charts.
|
659
|
+
*
|
660
|
+
* @type {Object}
|
661
|
+
* @sample highcharts/demo/sunburst/ Sunburst with color variation
|
662
|
+
* @since 6.0.0
|
663
|
+
* @product highcharts
|
664
|
+
* @apioption plotOptions.treemap.levels.colorVariation
|
665
|
+
*/
|
666
|
+
|
667
|
+
/**
|
668
|
+
* The key of a color variation. Currently supports `brightness` only.
|
669
|
+
*
|
670
|
+
* @type {String}
|
671
|
+
* @validvalue ["brightness"]
|
672
|
+
* @since 6.0.0
|
673
|
+
* @product highcharts
|
674
|
+
* @apioption plotOptions.treemap.levels.colorVariation.key
|
675
|
+
*/
|
676
|
+
|
677
|
+
/**
|
678
|
+
* The ending value of a color variation. The last sibling will receive this
|
679
|
+
* value.
|
680
|
+
*
|
681
|
+
* @type {Number}
|
682
|
+
* @since 6.0.0
|
683
|
+
* @product highcharts
|
684
|
+
* @apioption plotOptions.treemap.levels.colorVariation.to
|
685
|
+
*/
|
686
|
+
|
687
|
+
/**
|
688
|
+
* Can set the options of dataLabels on each point which lies on the
|
689
|
+
* level. [plotOptions.treemap.dataLabels](#plotOptions.treemap.dataLabels)
|
690
|
+
* for possible values.
|
691
|
+
*
|
692
|
+
* @type {Object}
|
693
|
+
* @default undefined
|
694
|
+
* @since 4.1.0
|
695
|
+
* @product highcharts
|
696
|
+
* @apioption plotOptions.treemap.levels.dataLabels
|
697
|
+
*/
|
698
|
+
|
699
|
+
/**
|
700
|
+
* Can set the layoutAlgorithm option on a specific level.
|
701
|
+
*
|
702
|
+
* @validvalue ["sliceAndDice", "stripes", "squarified", "strip"]
|
703
|
+
* @type {String}
|
704
|
+
* @since 4.1.0
|
705
|
+
* @product highcharts
|
706
|
+
* @apioption plotOptions.treemap.levels.layoutAlgorithm
|
707
|
+
*/
|
708
|
+
|
709
|
+
/**
|
710
|
+
* Can set the layoutStartingDirection option on a specific level.
|
711
|
+
*
|
712
|
+
* @validvalue ["vertical", "horizontal"]
|
713
|
+
* @type {String}
|
714
|
+
* @since 4.1.0
|
715
|
+
* @product highcharts
|
716
|
+
* @apioption plotOptions.treemap.levels.layoutStartingDirection
|
717
|
+
*/
|
718
|
+
|
719
|
+
/**
|
720
|
+
* Decides which level takes effect from the options set in the levels
|
721
|
+
* object.
|
722
|
+
*
|
723
|
+
* @type {Number}
|
724
|
+
* @sample {highcharts} highcharts/plotoptions/treemap-levels/
|
725
|
+
* Styling of both levels
|
726
|
+
* @since 4.1.0
|
727
|
+
* @product highcharts
|
728
|
+
* @apioption plotOptions.treemap.levels.level
|
729
|
+
*/
|
730
|
+
|
731
|
+
// Prototype members
|
732
|
+
}, {
|
733
|
+
pointArrayMap: ['value'],
|
734
|
+
axisTypes: seriesTypes.heatmap ? ['xAxis', 'yAxis', 'colorAxis'] : ['xAxis', 'yAxis'],
|
735
|
+
directTouch: true,
|
736
|
+
optionalAxis: 'colorAxis',
|
737
|
+
getSymbol: noop,
|
738
|
+
parallelArrays: ['x', 'y', 'value', 'colorValue'],
|
739
|
+
colorKey: 'colorValue', // Point color option key
|
740
|
+
translateColors: (
|
741
|
+
seriesTypes.heatmap &&
|
742
|
+
seriesTypes.heatmap.prototype.translateColors
|
743
|
+
),
|
744
|
+
colorAttribs: (
|
745
|
+
seriesTypes.heatmap &&
|
746
|
+
seriesTypes.heatmap.prototype.colorAttribs
|
747
|
+
),
|
748
|
+
trackerGroups: ['group', 'dataLabelsGroup'],
|
749
|
+
/**
|
750
|
+
* Creates an object map from parent id to childrens index.
|
751
|
+
* @param {Array} data List of points set in options.
|
752
|
+
* @param {string} data[].parent Parent id of point.
|
753
|
+
* @param {Array} ids List of all point ids.
|
754
|
+
* @return {Object} Map from parent id to children index in data.
|
755
|
+
*/
|
756
|
+
getListOfParents: function(data, ids) {
|
757
|
+
var listOfParents = reduce(data || [], function(prev, curr, i) {
|
758
|
+
var parent = pick(curr.parent, '');
|
759
|
+
if (prev[parent] === undefined) {
|
760
|
+
prev[parent] = [];
|
761
|
+
}
|
762
|
+
prev[parent].push(i);
|
763
|
+
return prev;
|
764
|
+
}, {});
|
765
|
+
|
766
|
+
// If parent does not exist, hoist parent to root of tree.
|
767
|
+
eachObject(listOfParents, function(children, parent, list) {
|
768
|
+
if ((parent !== '') && (H.inArray(parent, ids) === -1)) {
|
769
|
+
each(children, function(child) {
|
770
|
+
list[''].push(child);
|
771
|
+
});
|
772
|
+
delete list[parent];
|
773
|
+
}
|
774
|
+
});
|
775
|
+
return listOfParents;
|
776
|
+
},
|
777
|
+
/**
|
778
|
+
* Creates a tree structured object from the series points
|
779
|
+
*/
|
780
|
+
getTree: function() {
|
781
|
+
var series = this,
|
782
|
+
allIds = map(this.data, function(d) {
|
783
|
+
return d.id;
|
784
|
+
}),
|
785
|
+
parentList = series.getListOfParents(this.data, allIds);
|
786
|
+
|
787
|
+
series.nodeMap = [];
|
788
|
+
return series.buildNode('', -1, 0, parentList, null);
|
789
|
+
},
|
790
|
+
init: function(chart, options) {
|
791
|
+
var series = this;
|
792
|
+
Series.prototype.init.call(series, chart, options);
|
793
|
+
if (series.options.allowDrillToNode) {
|
794
|
+
H.addEvent(series, 'click', series.onClickDrillToNode);
|
795
|
+
}
|
796
|
+
},
|
797
|
+
buildNode: function(id, i, level, list, parent) {
|
798
|
+
var series = this,
|
799
|
+
children = [],
|
800
|
+
point = series.points[i],
|
801
|
+
height = 0,
|
802
|
+
node,
|
803
|
+
child;
|
804
|
+
|
805
|
+
// Actions
|
806
|
+
each((list[id] || []), function(i) {
|
807
|
+
child = series.buildNode(series.points[i].id, i, (level + 1), list, id);
|
808
|
+
height = Math.max(child.height + 1, height);
|
809
|
+
children.push(child);
|
810
|
+
});
|
811
|
+
node = {
|
812
|
+
id: id,
|
813
|
+
i: i,
|
814
|
+
children: children,
|
815
|
+
height: height,
|
816
|
+
level: level,
|
817
|
+
parent: parent,
|
818
|
+
visible: false // @todo move this to better location
|
819
|
+
};
|
820
|
+
series.nodeMap[node.id] = node;
|
821
|
+
if (point) {
|
822
|
+
point.node = node;
|
823
|
+
}
|
824
|
+
return node;
|
825
|
+
},
|
826
|
+
setTreeValues: function(tree) {
|
827
|
+
var series = this,
|
828
|
+
options = series.options,
|
829
|
+
childrenTotal = 0,
|
830
|
+
children = [],
|
831
|
+
val,
|
832
|
+
point = series.points[tree.i];
|
833
|
+
|
834
|
+
// First give the children some values
|
835
|
+
each(tree.children, function(child) {
|
836
|
+
child = series.setTreeValues(child);
|
837
|
+
children.push(child);
|
838
|
+
if (!child.ignore) {
|
839
|
+
childrenTotal += child.val;
|
840
|
+
}
|
841
|
+
});
|
842
|
+
// Sort the children
|
843
|
+
stableSort(children, function(a, b) {
|
844
|
+
return a.sortIndex - b.sortIndex;
|
845
|
+
});
|
846
|
+
// Set the values
|
847
|
+
val = pick(point && point.options.value, childrenTotal);
|
848
|
+
if (point) {
|
849
|
+
point.value = val;
|
850
|
+
}
|
851
|
+
extend(tree, {
|
852
|
+
children: children,
|
853
|
+
childrenTotal: childrenTotal,
|
854
|
+
// Ignore this node if point is not visible
|
855
|
+
ignore: !(pick(point && point.visible, true) && (val > 0)),
|
856
|
+
isLeaf: tree.visible && !childrenTotal,
|
857
|
+
levelDynamic: tree.level - (options.levelIsConstant ? series.nodeMap[series.rootNode].level : 0),
|
858
|
+
name: pick(point && point.name, ''),
|
859
|
+
sortIndex: pick(point && point.sortIndex, -val),
|
860
|
+
val: val
|
861
|
+
});
|
862
|
+
return tree;
|
863
|
+
},
|
864
|
+
/**
|
865
|
+
* Recursive function which calculates the area for all children of a node.
|
866
|
+
* @param {Object} node The node which is parent to the children.
|
867
|
+
* @param {Object} area The rectangular area of the parent.
|
868
|
+
*/
|
869
|
+
calculateChildrenAreas: function(parent, area) {
|
870
|
+
var series = this,
|
871
|
+
options = series.options,
|
872
|
+
level = this.levelMap[parent.levelDynamic + 1],
|
873
|
+
algorithm = pick((series[level && level.layoutAlgorithm] && level.layoutAlgorithm), options.layoutAlgorithm),
|
874
|
+
alternate = options.alternateStartingDirection,
|
875
|
+
childrenValues = [],
|
876
|
+
children;
|
877
|
+
|
878
|
+
// Collect all children which should be included
|
879
|
+
children = grep(parent.children, function(n) {
|
880
|
+
return !n.ignore;
|
881
|
+
});
|
882
|
+
|
883
|
+
if (level && level.layoutStartingDirection) {
|
884
|
+
area.direction = level.layoutStartingDirection === 'vertical' ? 0 : 1;
|
885
|
+
}
|
886
|
+
childrenValues = series[algorithm](area, children);
|
887
|
+
each(children, function(child, index) {
|
888
|
+
var values = childrenValues[index];
|
889
|
+
child.values = merge(values, {
|
890
|
+
val: child.childrenTotal,
|
891
|
+
direction: (alternate ? 1 - area.direction : area.direction)
|
892
|
+
});
|
893
|
+
child.pointValues = merge(values, {
|
894
|
+
x: (values.x / series.axisRatio),
|
895
|
+
width: (values.width / series.axisRatio)
|
896
|
+
});
|
897
|
+
// If node has children, then call method recursively
|
898
|
+
if (child.children.length) {
|
899
|
+
series.calculateChildrenAreas(child, child.values);
|
900
|
+
}
|
901
|
+
});
|
902
|
+
},
|
903
|
+
setPointValues: function() {
|
904
|
+
var series = this,
|
905
|
+
xAxis = series.xAxis,
|
906
|
+
yAxis = series.yAxis;
|
907
|
+
each(series.points, function(point) {
|
908
|
+
var node = point.node,
|
909
|
+
values = node.pointValues,
|
910
|
+
x1,
|
911
|
+
x2,
|
912
|
+
y1,
|
913
|
+
y2,
|
914
|
+
crispCorr = 0;
|
915
|
+
|
916
|
+
|
917
|
+
// Get the crisp correction in classic mode. For this to work in
|
918
|
+
// styled mode, we would need to first add the shape (without x, y,
|
919
|
+
// width and height), then read the rendered stroke width using
|
920
|
+
// point.graphic.strokeWidth(), then modify and apply the shapeArgs.
|
921
|
+
// This applies also to column series, but the downside is
|
922
|
+
// performance and code complexity.
|
923
|
+
crispCorr = (
|
924
|
+
(series.pointAttribs(point)['stroke-width'] || 0) % 2
|
925
|
+
) / 2;
|
926
|
+
|
927
|
+
|
928
|
+
// Points which is ignored, have no values.
|
929
|
+
if (values && node.visible) {
|
930
|
+
x1 = Math.round(xAxis.translate(values.x, 0, 0, 0, 1)) - crispCorr;
|
931
|
+
x2 = Math.round(xAxis.translate(values.x + values.width, 0, 0, 0, 1)) - crispCorr;
|
932
|
+
y1 = Math.round(yAxis.translate(values.y, 0, 0, 0, 1)) - crispCorr;
|
933
|
+
y2 = Math.round(yAxis.translate(values.y + values.height, 0, 0, 0, 1)) - crispCorr;
|
934
|
+
// Set point values
|
935
|
+
point.shapeType = 'rect';
|
936
|
+
point.shapeArgs = {
|
937
|
+
x: Math.min(x1, x2),
|
938
|
+
y: Math.min(y1, y2),
|
939
|
+
width: Math.abs(x2 - x1),
|
940
|
+
height: Math.abs(y2 - y1)
|
941
|
+
};
|
942
|
+
point.plotX = point.shapeArgs.x + (point.shapeArgs.width / 2);
|
943
|
+
point.plotY = point.shapeArgs.y + (point.shapeArgs.height / 2);
|
944
|
+
} else {
|
945
|
+
// Reset visibility
|
946
|
+
delete point.plotX;
|
947
|
+
delete point.plotY;
|
948
|
+
}
|
949
|
+
});
|
950
|
+
},
|
951
|
+
|
952
|
+
/**
|
953
|
+
* Set the node's color recursively, from the parent down.
|
954
|
+
*/
|
955
|
+
setColorRecursive: function(node, parentColor, colorIndex, index, siblings) {
|
956
|
+
var series = this,
|
957
|
+
chart = series && series.chart,
|
958
|
+
colors = chart && chart.options && chart.options.colors,
|
959
|
+
colorInfo,
|
960
|
+
point;
|
961
|
+
|
962
|
+
if (node) {
|
963
|
+
colorInfo = getColor(node, {
|
964
|
+
colors: colors,
|
965
|
+
index: index,
|
966
|
+
levelMap: series.levelMap,
|
967
|
+
parentColor: parentColor,
|
968
|
+
parentColorIndex: colorIndex,
|
969
|
+
series: series,
|
970
|
+
siblings: siblings
|
971
|
+
});
|
972
|
+
|
973
|
+
point = series.points[node.i];
|
974
|
+
if (point) {
|
975
|
+
point.color = colorInfo.color;
|
976
|
+
point.colorIndex = colorInfo.colorIndex;
|
977
|
+
}
|
978
|
+
|
979
|
+
// Do it all again with the children
|
980
|
+
each(node.children || [], function(child, i) {
|
981
|
+
series.setColorRecursive(
|
982
|
+
child,
|
983
|
+
colorInfo.color,
|
984
|
+
colorInfo.colorIndex,
|
985
|
+
i,
|
986
|
+
node.children.length
|
987
|
+
);
|
988
|
+
});
|
989
|
+
}
|
990
|
+
},
|
991
|
+
algorithmGroup: function(h, w, d, p) {
|
992
|
+
this.height = h;
|
993
|
+
this.width = w;
|
994
|
+
this.plot = p;
|
995
|
+
this.direction = d;
|
996
|
+
this.startDirection = d;
|
997
|
+
this.total = 0;
|
998
|
+
this.nW = 0;
|
999
|
+
this.lW = 0;
|
1000
|
+
this.nH = 0;
|
1001
|
+
this.lH = 0;
|
1002
|
+
this.elArr = [];
|
1003
|
+
this.lP = {
|
1004
|
+
total: 0,
|
1005
|
+
lH: 0,
|
1006
|
+
nH: 0,
|
1007
|
+
lW: 0,
|
1008
|
+
nW: 0,
|
1009
|
+
nR: 0,
|
1010
|
+
lR: 0,
|
1011
|
+
aspectRatio: function(w, h) {
|
1012
|
+
return Math.max((w / h), (h / w));
|
1013
|
+
}
|
1014
|
+
};
|
1015
|
+
this.addElement = function(el) {
|
1016
|
+
this.lP.total = this.elArr[this.elArr.length - 1];
|
1017
|
+
this.total = this.total + el;
|
1018
|
+
if (this.direction === 0) {
|
1019
|
+
// Calculate last point old aspect ratio
|
1020
|
+
this.lW = this.nW;
|
1021
|
+
this.lP.lH = this.lP.total / this.lW;
|
1022
|
+
this.lP.lR = this.lP.aspectRatio(this.lW, this.lP.lH);
|
1023
|
+
// Calculate last point new aspect ratio
|
1024
|
+
this.nW = this.total / this.height;
|
1025
|
+
this.lP.nH = this.lP.total / this.nW;
|
1026
|
+
this.lP.nR = this.lP.aspectRatio(this.nW, this.lP.nH);
|
1027
|
+
} else {
|
1028
|
+
// Calculate last point old aspect ratio
|
1029
|
+
this.lH = this.nH;
|
1030
|
+
this.lP.lW = this.lP.total / this.lH;
|
1031
|
+
this.lP.lR = this.lP.aspectRatio(this.lP.lW, this.lH);
|
1032
|
+
// Calculate last point new aspect ratio
|
1033
|
+
this.nH = this.total / this.width;
|
1034
|
+
this.lP.nW = this.lP.total / this.nH;
|
1035
|
+
this.lP.nR = this.lP.aspectRatio(this.lP.nW, this.nH);
|
1036
|
+
}
|
1037
|
+
this.elArr.push(el);
|
1038
|
+
};
|
1039
|
+
this.reset = function() {
|
1040
|
+
this.nW = 0;
|
1041
|
+
this.lW = 0;
|
1042
|
+
this.elArr = [];
|
1043
|
+
this.total = 0;
|
1044
|
+
};
|
1045
|
+
},
|
1046
|
+
algorithmCalcPoints: function(directionChange, last, group, childrenArea) {
|
1047
|
+
var pX,
|
1048
|
+
pY,
|
1049
|
+
pW,
|
1050
|
+
pH,
|
1051
|
+
gW = group.lW,
|
1052
|
+
gH = group.lH,
|
1053
|
+
plot = group.plot,
|
1054
|
+
keep,
|
1055
|
+
i = 0,
|
1056
|
+
end = group.elArr.length - 1;
|
1057
|
+
if (last) {
|
1058
|
+
gW = group.nW;
|
1059
|
+
gH = group.nH;
|
1060
|
+
} else {
|
1061
|
+
keep = group.elArr[group.elArr.length - 1];
|
1062
|
+
}
|
1063
|
+
each(group.elArr, function(p) {
|
1064
|
+
if (last || (i < end)) {
|
1065
|
+
if (group.direction === 0) {
|
1066
|
+
pX = plot.x;
|
1067
|
+
pY = plot.y;
|
1068
|
+
pW = gW;
|
1069
|
+
pH = p / pW;
|
1070
|
+
} else {
|
1071
|
+
pX = plot.x;
|
1072
|
+
pY = plot.y;
|
1073
|
+
pH = gH;
|
1074
|
+
pW = p / pH;
|
1075
|
+
}
|
1076
|
+
childrenArea.push({
|
1077
|
+
x: pX,
|
1078
|
+
y: pY,
|
1079
|
+
width: pW,
|
1080
|
+
height: pH
|
1081
|
+
});
|
1082
|
+
if (group.direction === 0) {
|
1083
|
+
plot.y = plot.y + pH;
|
1084
|
+
} else {
|
1085
|
+
plot.x = plot.x + pW;
|
1086
|
+
}
|
1087
|
+
}
|
1088
|
+
i = i + 1;
|
1089
|
+
});
|
1090
|
+
// Reset variables
|
1091
|
+
group.reset();
|
1092
|
+
if (group.direction === 0) {
|
1093
|
+
group.width = group.width - gW;
|
1094
|
+
} else {
|
1095
|
+
group.height = group.height - gH;
|
1096
|
+
}
|
1097
|
+
plot.y = plot.parent.y + (plot.parent.height - group.height);
|
1098
|
+
plot.x = plot.parent.x + (plot.parent.width - group.width);
|
1099
|
+
if (directionChange) {
|
1100
|
+
group.direction = 1 - group.direction;
|
1101
|
+
}
|
1102
|
+
// If not last, then add uncalculated element
|
1103
|
+
if (!last) {
|
1104
|
+
group.addElement(keep);
|
1105
|
+
}
|
1106
|
+
},
|
1107
|
+
algorithmLowAspectRatio: function(directionChange, parent, children) {
|
1108
|
+
var childrenArea = [],
|
1109
|
+
series = this,
|
1110
|
+
pTot,
|
1111
|
+
plot = {
|
1112
|
+
x: parent.x,
|
1113
|
+
y: parent.y,
|
1114
|
+
parent: parent
|
1115
|
+
},
|
1116
|
+
direction = parent.direction,
|
1117
|
+
i = 0,
|
1118
|
+
end = children.length - 1,
|
1119
|
+
group = new this.algorithmGroup(parent.height, parent.width, direction, plot); // eslint-disable-line new-cap
|
1120
|
+
// Loop through and calculate all areas
|
1121
|
+
each(children, function(child) {
|
1122
|
+
pTot = (parent.width * parent.height) * (child.val / parent.val);
|
1123
|
+
group.addElement(pTot);
|
1124
|
+
if (group.lP.nR > group.lP.lR) {
|
1125
|
+
series.algorithmCalcPoints(directionChange, false, group, childrenArea, plot);
|
1126
|
+
}
|
1127
|
+
// If last child, then calculate all remaining areas
|
1128
|
+
if (i === end) {
|
1129
|
+
series.algorithmCalcPoints(directionChange, true, group, childrenArea, plot);
|
1130
|
+
}
|
1131
|
+
i = i + 1;
|
1132
|
+
});
|
1133
|
+
return childrenArea;
|
1134
|
+
},
|
1135
|
+
algorithmFill: function(directionChange, parent, children) {
|
1136
|
+
var childrenArea = [],
|
1137
|
+
pTot,
|
1138
|
+
direction = parent.direction,
|
1139
|
+
x = parent.x,
|
1140
|
+
y = parent.y,
|
1141
|
+
width = parent.width,
|
1142
|
+
height = parent.height,
|
1143
|
+
pX,
|
1144
|
+
pY,
|
1145
|
+
pW,
|
1146
|
+
pH;
|
1147
|
+
each(children, function(child) {
|
1148
|
+
pTot = (parent.width * parent.height) * (child.val / parent.val);
|
1149
|
+
pX = x;
|
1150
|
+
pY = y;
|
1151
|
+
if (direction === 0) {
|
1152
|
+
pH = height;
|
1153
|
+
pW = pTot / pH;
|
1154
|
+
width = width - pW;
|
1155
|
+
x = x + pW;
|
1156
|
+
} else {
|
1157
|
+
pW = width;
|
1158
|
+
pH = pTot / pW;
|
1159
|
+
height = height - pH;
|
1160
|
+
y = y + pH;
|
1161
|
+
}
|
1162
|
+
childrenArea.push({
|
1163
|
+
x: pX,
|
1164
|
+
y: pY,
|
1165
|
+
width: pW,
|
1166
|
+
height: pH
|
1167
|
+
});
|
1168
|
+
if (directionChange) {
|
1169
|
+
direction = 1 - direction;
|
1170
|
+
}
|
1171
|
+
});
|
1172
|
+
return childrenArea;
|
1173
|
+
},
|
1174
|
+
strip: function(parent, children) {
|
1175
|
+
return this.algorithmLowAspectRatio(false, parent, children);
|
1176
|
+
},
|
1177
|
+
squarified: function(parent, children) {
|
1178
|
+
return this.algorithmLowAspectRatio(true, parent, children);
|
1179
|
+
},
|
1180
|
+
sliceAndDice: function(parent, children) {
|
1181
|
+
return this.algorithmFill(true, parent, children);
|
1182
|
+
},
|
1183
|
+
stripes: function(parent, children) {
|
1184
|
+
return this.algorithmFill(false, parent, children);
|
1185
|
+
},
|
1186
|
+
translate: function() {
|
1187
|
+
var series = this,
|
1188
|
+
rootId = series.rootNode = pick(series.rootNode, series.options.rootId, ''),
|
1189
|
+
rootNode,
|
1190
|
+
pointValues,
|
1191
|
+
seriesArea,
|
1192
|
+
tree,
|
1193
|
+
val;
|
1194
|
+
|
1195
|
+
// Call prototype function
|
1196
|
+
Series.prototype.translate.call(series);
|
1197
|
+
// Create a object map from level to options
|
1198
|
+
series.levelMap = reduce(series.options.levels || [],
|
1199
|
+
function(arr, item) {
|
1200
|
+
arr[item.level] = item;
|
1201
|
+
return arr;
|
1202
|
+
}, {});
|
1203
|
+
tree = series.tree = series.getTree(); // @todo Only if series.isDirtyData is true
|
1204
|
+
rootNode = series.nodeMap[rootId];
|
1205
|
+
if (
|
1206
|
+
rootId !== '' &&
|
1207
|
+
(!rootNode || !rootNode.children.length)
|
1208
|
+
) {
|
1209
|
+
series.drillToNode('', false);
|
1210
|
+
rootId = series.rootNode;
|
1211
|
+
rootNode = series.nodeMap[rootId];
|
1212
|
+
}
|
1213
|
+
// Parents of the root node is by default visible
|
1214
|
+
recursive(series.nodeMap[series.rootNode], function(node) {
|
1215
|
+
var next = false,
|
1216
|
+
p = node.parent;
|
1217
|
+
node.visible = true;
|
1218
|
+
if (p || p === '') {
|
1219
|
+
next = series.nodeMap[p];
|
1220
|
+
}
|
1221
|
+
return next;
|
1222
|
+
});
|
1223
|
+
// Children of the root node is by default visible
|
1224
|
+
recursive(series.nodeMap[series.rootNode].children, function(children) {
|
1225
|
+
var next = false;
|
1226
|
+
each(children, function(child) {
|
1227
|
+
child.visible = true;
|
1228
|
+
if (child.children.length) {
|
1229
|
+
next = (next || []).concat(child.children);
|
1230
|
+
}
|
1231
|
+
});
|
1232
|
+
return next;
|
1233
|
+
});
|
1234
|
+
series.setTreeValues(tree);
|
1235
|
+
|
1236
|
+
// Calculate plotting values.
|
1237
|
+
series.axisRatio = (series.xAxis.len / series.yAxis.len);
|
1238
|
+
series.nodeMap[''].pointValues = pointValues = {
|
1239
|
+
x: 0,
|
1240
|
+
y: 0,
|
1241
|
+
width: 100,
|
1242
|
+
height: 100
|
1243
|
+
};
|
1244
|
+
series.nodeMap[''].values = seriesArea = merge(pointValues, {
|
1245
|
+
width: (pointValues.width * series.axisRatio),
|
1246
|
+
direction: (series.options.layoutStartingDirection === 'vertical' ? 0 : 1),
|
1247
|
+
val: tree.val
|
1248
|
+
});
|
1249
|
+
series.calculateChildrenAreas(tree, seriesArea);
|
1250
|
+
|
1251
|
+
// Logic for point colors
|
1252
|
+
if (series.colorAxis) {
|
1253
|
+
series.translateColors();
|
1254
|
+
} else if (!series.options.colorByPoint) {
|
1255
|
+
series.setColorRecursive(series.tree);
|
1256
|
+
}
|
1257
|
+
|
1258
|
+
// Update axis extremes according to the root node.
|
1259
|
+
if (series.options.allowDrillToNode) {
|
1260
|
+
val = rootNode.pointValues;
|
1261
|
+
series.xAxis.setExtremes(val.x, val.x + val.width, false);
|
1262
|
+
series.yAxis.setExtremes(val.y, val.y + val.height, false);
|
1263
|
+
series.xAxis.setScale();
|
1264
|
+
series.yAxis.setScale();
|
1265
|
+
}
|
1266
|
+
|
1267
|
+
// Assign values to points.
|
1268
|
+
series.setPointValues();
|
1269
|
+
},
|
1270
|
+
/**
|
1271
|
+
* Extend drawDataLabels with logic to handle custom options related to the treemap series:
|
1272
|
+
* - Points which is not a leaf node, has dataLabels disabled by default.
|
1273
|
+
* - Options set on series.levels is merged in.
|
1274
|
+
* - Width of the dataLabel is set to match the width of the point shape.
|
1275
|
+
*/
|
1276
|
+
drawDataLabels: function() {
|
1277
|
+
var series = this,
|
1278
|
+
points = grep(series.points, function(n) {
|
1279
|
+
return n.node.visible;
|
1280
|
+
}),
|
1281
|
+
options,
|
1282
|
+
level;
|
1283
|
+
each(points, function(point) {
|
1284
|
+
level = series.levelMap[point.node.levelDynamic];
|
1285
|
+
// Set options to new object to avoid problems with scope
|
1286
|
+
options = {
|
1287
|
+
style: {}
|
1288
|
+
};
|
1289
|
+
|
1290
|
+
// If not a leaf, then label should be disabled as default
|
1291
|
+
if (!point.node.isLeaf) {
|
1292
|
+
options.enabled = false;
|
1293
|
+
}
|
1294
|
+
|
1295
|
+
// If options for level exists, include them as well
|
1296
|
+
if (level && level.dataLabels) {
|
1297
|
+
options = merge(options, level.dataLabels);
|
1298
|
+
series._hasPointLabels = true;
|
1299
|
+
}
|
1300
|
+
|
1301
|
+
// Set dataLabel width to the width of the point shape.
|
1302
|
+
if (point.shapeArgs) {
|
1303
|
+
options.style.width = point.shapeArgs.width;
|
1304
|
+
if (point.dataLabel) {
|
1305
|
+
point.dataLabel.css({
|
1306
|
+
width: point.shapeArgs.width + 'px'
|
1307
|
+
});
|
1308
|
+
}
|
1309
|
+
}
|
1310
|
+
|
1311
|
+
// Merge custom options with point options
|
1312
|
+
point.dlOptions = merge(options, point.options.dataLabels);
|
1313
|
+
});
|
1314
|
+
Series.prototype.drawDataLabels.call(this);
|
1315
|
+
},
|
1316
|
+
|
1317
|
+
/**
|
1318
|
+
* Over the alignment method by setting z index
|
1319
|
+
*/
|
1320
|
+
alignDataLabel: function(point) {
|
1321
|
+
seriesTypes.column.prototype.alignDataLabel.apply(this, arguments);
|
1322
|
+
if (point.dataLabel) {
|
1323
|
+
// point.node.zIndex could be undefined (#6956)
|
1324
|
+
point.dataLabel.attr({
|
1325
|
+
zIndex: (point.node.zIndex || 0) + 1
|
1326
|
+
});
|
1327
|
+
}
|
1328
|
+
},
|
1329
|
+
|
1330
|
+
|
1331
|
+
/**
|
1332
|
+
* Get presentational attributes
|
1333
|
+
*/
|
1334
|
+
pointAttribs: function(point, state) {
|
1335
|
+
var level = point && this.levelMap[point.node.levelDynamic] || {},
|
1336
|
+
options = this.options,
|
1337
|
+
attr,
|
1338
|
+
stateOptions = (state && options.states[state]) || {},
|
1339
|
+
className = (point && point.getClassName()) || '',
|
1340
|
+
opacity;
|
1341
|
+
|
1342
|
+
// Set attributes by precedence. Point trumps level trumps series. Stroke width uses pick
|
1343
|
+
// because it can be 0.
|
1344
|
+
attr = {
|
1345
|
+
'stroke':
|
1346
|
+
(point && point.borderColor) ||
|
1347
|
+
level.borderColor ||
|
1348
|
+
stateOptions.borderColor ||
|
1349
|
+
options.borderColor,
|
1350
|
+
'stroke-width': pick(
|
1351
|
+
point && point.borderWidth,
|
1352
|
+
level.borderWidth,
|
1353
|
+
stateOptions.borderWidth,
|
1354
|
+
options.borderWidth
|
1355
|
+
),
|
1356
|
+
'dashstyle':
|
1357
|
+
(point && point.borderDashStyle) ||
|
1358
|
+
level.borderDashStyle ||
|
1359
|
+
stateOptions.borderDashStyle ||
|
1360
|
+
options.borderDashStyle,
|
1361
|
+
'fill': (point && point.color) || this.color
|
1362
|
+
};
|
1363
|
+
|
1364
|
+
// Hide levels above the current view
|
1365
|
+
if (className.indexOf('highcharts-above-level') !== -1) {
|
1366
|
+
attr.fill = 'none';
|
1367
|
+
attr['stroke-width'] = 0;
|
1368
|
+
|
1369
|
+
// Nodes with children that accept interaction
|
1370
|
+
} else if (className.indexOf('highcharts-internal-node-interactive') !== -1) {
|
1371
|
+
opacity = pick(stateOptions.opacity, options.opacity);
|
1372
|
+
attr.fill = color(attr.fill).setOpacity(opacity).get();
|
1373
|
+
attr.cursor = 'pointer';
|
1374
|
+
// Hide nodes that have children
|
1375
|
+
} else if (className.indexOf('highcharts-internal-node') !== -1) {
|
1376
|
+
attr.fill = 'none';
|
1377
|
+
|
1378
|
+
} else if (state) {
|
1379
|
+
// Brighten and hoist the hover nodes
|
1380
|
+
attr.fill = color(attr.fill).brighten(stateOptions.brightness).get();
|
1381
|
+
}
|
1382
|
+
return attr;
|
1383
|
+
},
|
1384
|
+
|
1385
|
+
|
1386
|
+
/**
|
1387
|
+
* Extending ColumnSeries drawPoints
|
1388
|
+
*/
|
1389
|
+
drawPoints: function() {
|
1390
|
+
var series = this,
|
1391
|
+
points = grep(series.points, function(n) {
|
1392
|
+
return n.node.visible;
|
1393
|
+
});
|
1394
|
+
|
1395
|
+
each(points, function(point) {
|
1396
|
+
var groupKey = 'level-group-' + point.node.levelDynamic;
|
1397
|
+
if (!series[groupKey]) {
|
1398
|
+
series[groupKey] = series.chart.renderer.g(groupKey)
|
1399
|
+
.attr({
|
1400
|
+
zIndex: 1000 - point.node.levelDynamic // @todo Set the zIndex based upon the number of levels, instead of using 1000
|
1401
|
+
})
|
1402
|
+
.add(series.group);
|
1403
|
+
}
|
1404
|
+
point.group = series[groupKey];
|
1405
|
+
|
1406
|
+
});
|
1407
|
+
// Call standard drawPoints
|
1408
|
+
seriesTypes.column.prototype.drawPoints.call(this);
|
1409
|
+
|
1410
|
+
|
1411
|
+
|
1412
|
+
// If drillToNode is allowed, set a point cursor on clickables & add drillId to point
|
1413
|
+
if (series.options.allowDrillToNode) {
|
1414
|
+
each(points, function(point) {
|
1415
|
+
if (point.graphic) {
|
1416
|
+
point.drillId = series.options.interactByLeaf ? series.drillToByLeaf(point) : series.drillToByGroup(point);
|
1417
|
+
}
|
1418
|
+
});
|
1419
|
+
}
|
1420
|
+
},
|
1421
|
+
/**
|
1422
|
+
* Add drilling on the suitable points
|
1423
|
+
*/
|
1424
|
+
onClickDrillToNode: function(event) {
|
1425
|
+
var series = this,
|
1426
|
+
point = event.point,
|
1427
|
+
drillId = point && point.drillId;
|
1428
|
+
// If a drill id is returned, add click event and cursor.
|
1429
|
+
if (isString(drillId)) {
|
1430
|
+
point.setState(''); // Remove hover
|
1431
|
+
series.drillToNode(drillId);
|
1432
|
+
}
|
1433
|
+
},
|
1434
|
+
/**
|
1435
|
+
* Finds the drill id for a parent node.
|
1436
|
+
* Returns false if point should not have a click event
|
1437
|
+
* @param {Object} point
|
1438
|
+
* @return {String|Boolean} Drill to id or false when point should not have a click event
|
1439
|
+
*/
|
1440
|
+
drillToByGroup: function(point) {
|
1441
|
+
var series = this,
|
1442
|
+
drillId = false;
|
1443
|
+
if ((point.node.level - series.nodeMap[series.rootNode].level) === 1 && !point.node.isLeaf) {
|
1444
|
+
drillId = point.id;
|
1445
|
+
}
|
1446
|
+
return drillId;
|
1447
|
+
},
|
1448
|
+
/**
|
1449
|
+
* Finds the drill id for a leaf node.
|
1450
|
+
* Returns false if point should not have a click event
|
1451
|
+
* @param {Object} point
|
1452
|
+
* @return {String|Boolean} Drill to id or false when point should not have a click event
|
1453
|
+
*/
|
1454
|
+
drillToByLeaf: function(point) {
|
1455
|
+
var series = this,
|
1456
|
+
drillId = false,
|
1457
|
+
nodeParent;
|
1458
|
+
if ((point.node.parent !== series.rootNode) && (point.node.isLeaf)) {
|
1459
|
+
nodeParent = point.node;
|
1460
|
+
while (!drillId) {
|
1461
|
+
nodeParent = series.nodeMap[nodeParent.parent];
|
1462
|
+
if (nodeParent.parent === series.rootNode) {
|
1463
|
+
drillId = nodeParent.id;
|
1464
|
+
}
|
1465
|
+
}
|
1466
|
+
}
|
1467
|
+
return drillId;
|
1468
|
+
},
|
1469
|
+
drillUp: function() {
|
1470
|
+
var series = this,
|
1471
|
+
node = series.nodeMap[series.rootNode];
|
1472
|
+
if (node && isString(node.parent)) {
|
1473
|
+
series.drillToNode(node.parent);
|
1474
|
+
}
|
1475
|
+
},
|
1476
|
+
drillToNode: function(id, redraw) {
|
1477
|
+
var series = this,
|
1478
|
+
nodeMap = series.nodeMap,
|
1479
|
+
node = nodeMap[id];
|
1480
|
+
series.idPreviousRoot = series.rootNode;
|
1481
|
+
series.rootNode = id;
|
1482
|
+
if (id === '') {
|
1483
|
+
series.drillUpButton = series.drillUpButton.destroy();
|
1484
|
+
} else {
|
1485
|
+
series.showDrillUpButton((node && node.name || id));
|
1486
|
+
}
|
1487
|
+
this.isDirty = true; // Force redraw
|
1488
|
+
if (pick(redraw, true)) {
|
1489
|
+
this.chart.redraw();
|
1490
|
+
}
|
1491
|
+
},
|
1492
|
+
showDrillUpButton: function(name) {
|
1493
|
+
var series = this,
|
1494
|
+
backText = (name || '< Back'),
|
1495
|
+
buttonOptions = series.options.drillUpButton,
|
1496
|
+
attr,
|
1497
|
+
states;
|
1498
|
+
|
1499
|
+
if (buttonOptions.text) {
|
1500
|
+
backText = buttonOptions.text;
|
1501
|
+
}
|
1502
|
+
if (!this.drillUpButton) {
|
1503
|
+
attr = buttonOptions.theme;
|
1504
|
+
states = attr && attr.states;
|
1505
|
+
|
1506
|
+
this.drillUpButton = this.chart.renderer.button(
|
1507
|
+
backText,
|
1508
|
+
null,
|
1509
|
+
null,
|
1510
|
+
function() {
|
1511
|
+
series.drillUp();
|
1512
|
+
},
|
1513
|
+
attr,
|
1514
|
+
states && states.hover,
|
1515
|
+
states && states.select
|
1516
|
+
)
|
1517
|
+
.attr({
|
1518
|
+
align: buttonOptions.position.align,
|
1519
|
+
zIndex: 7
|
1520
|
+
})
|
1521
|
+
.add()
|
1522
|
+
.align(buttonOptions.position, false, buttonOptions.relativeTo || 'plotBox');
|
1523
|
+
} else {
|
1524
|
+
this.drillUpButton.attr({
|
1525
|
+
text: backText
|
1526
|
+
})
|
1527
|
+
.align();
|
1528
|
+
}
|
1529
|
+
},
|
1530
|
+
buildKDTree: noop,
|
1531
|
+
drawLegendSymbol: H.LegendSymbolMixin.drawRectangle,
|
1532
|
+
getExtremes: function() {
|
1533
|
+
// Get the extremes from the value data
|
1534
|
+
Series.prototype.getExtremes.call(this, this.colorValueData);
|
1535
|
+
this.valueMin = this.dataMin;
|
1536
|
+
this.valueMax = this.dataMax;
|
1537
|
+
|
1538
|
+
// Get the extremes from the y data
|
1539
|
+
Series.prototype.getExtremes.call(this);
|
1540
|
+
},
|
1541
|
+
getExtremesFromAll: true,
|
1542
|
+
bindAxes: function() {
|
1543
|
+
var treeAxis = {
|
1544
|
+
endOnTick: false,
|
1545
|
+
gridLineWidth: 0,
|
1546
|
+
lineWidth: 0,
|
1547
|
+
min: 0,
|
1548
|
+
dataMin: 0,
|
1549
|
+
minPadding: 0,
|
1550
|
+
max: 100,
|
1551
|
+
dataMax: 100,
|
1552
|
+
maxPadding: 0,
|
1553
|
+
startOnTick: false,
|
1554
|
+
title: null,
|
1555
|
+
tickPositions: []
|
1556
|
+
};
|
1557
|
+
Series.prototype.bindAxes.call(this);
|
1558
|
+
H.extend(this.yAxis.options, treeAxis);
|
1559
|
+
H.extend(this.xAxis.options, treeAxis);
|
1560
|
+
},
|
1561
|
+
utils: {
|
1562
|
+
recursive: recursive,
|
1563
|
+
reduce: reduce
|
1564
|
+
}
|
1565
|
+
|
1566
|
+
// Point class
|
1567
|
+
}, {
|
1568
|
+
getClassName: function() {
|
1569
|
+
var className = H.Point.prototype.getClassName.call(this),
|
1570
|
+
series = this.series,
|
1571
|
+
options = series.options;
|
1572
|
+
|
1573
|
+
// Above the current level
|
1574
|
+
if (this.node.level <= series.nodeMap[series.rootNode].level) {
|
1575
|
+
className += ' highcharts-above-level';
|
1576
|
+
|
1577
|
+
} else if (!this.node.isLeaf && !pick(options.interactByLeaf, !options.allowDrillToNode)) {
|
1578
|
+
className += ' highcharts-internal-node-interactive';
|
1579
|
+
|
1580
|
+
} else if (!this.node.isLeaf) {
|
1581
|
+
className += ' highcharts-internal-node';
|
1582
|
+
}
|
1583
|
+
return className;
|
1584
|
+
},
|
1585
|
+
|
1586
|
+
/**
|
1587
|
+
* A tree point is valid if it has han id too, assume it may be a parent
|
1588
|
+
* item.
|
1589
|
+
*/
|
1590
|
+
isValid: function() {
|
1591
|
+
return this.id || isNumber(this.value);
|
1592
|
+
},
|
1593
|
+
setState: function(state) {
|
1594
|
+
H.Point.prototype.setState.call(this, state);
|
1595
|
+
|
1596
|
+
// Graphic does not exist when point is not visible.
|
1597
|
+
if (this.graphic) {
|
1598
|
+
this.graphic.attr({
|
1599
|
+
zIndex: state === 'hover' ? 1 : 0
|
1600
|
+
});
|
1601
|
+
}
|
1602
|
+
},
|
1603
|
+
setVisible: seriesTypes.pie.prototype.pointClass.prototype.setVisible
|
1604
|
+
});
|
1605
|
+
|
1606
|
+
|
1607
|
+
/**
|
1608
|
+
* A `treemap` series. If the [type](#series.treemap.type) option is
|
1609
|
+
* not specified, it is inherited from [chart.type](#chart.type).
|
1610
|
+
*
|
1611
|
+
* For options that apply to multiple series, it is recommended to add
|
1612
|
+
* them to the [plotOptions.series](#plotOptions.series) options structure.
|
1613
|
+
* To apply to all series of this specific type, apply it to [plotOptions.
|
1614
|
+
* treemap](#plotOptions.treemap).
|
1615
|
+
*
|
1616
|
+
* @type {Object}
|
1617
|
+
* @extends series,plotOptions.treemap
|
1618
|
+
* @excluding dataParser,dataURL,stack
|
1619
|
+
* @product highcharts
|
1620
|
+
* @apioption series.treemap
|
1621
|
+
*/
|
1622
|
+
|
1623
|
+
/**
|
1624
|
+
* An array of data points for the series. For the `treemap` series
|
1625
|
+
* type, points can be given in the following ways:
|
1626
|
+
*
|
1627
|
+
* 1. An array of numerical values. In this case, the numerical values
|
1628
|
+
* will be interpreted as `value` options. Example:
|
1629
|
+
*
|
1630
|
+
* ```js
|
1631
|
+
* data: [0, 5, 3, 5]
|
1632
|
+
* ```
|
1633
|
+
*
|
1634
|
+
* 2. An array of objects with named values. The objects are point
|
1635
|
+
* configuration objects as seen below. If the total number of data
|
1636
|
+
* points exceeds the series' [turboThreshold](#series.treemap.turboThreshold),
|
1637
|
+
* this option is not available.
|
1638
|
+
*
|
1639
|
+
* ```js
|
1640
|
+
* data: [{
|
1641
|
+
* value: 9,
|
1642
|
+
* name: "Point2",
|
1643
|
+
* color: "#00FF00"
|
1644
|
+
* }, {
|
1645
|
+
* value: 6,
|
1646
|
+
* name: "Point1",
|
1647
|
+
* color: "#FF00FF"
|
1648
|
+
* }]
|
1649
|
+
* ```
|
1650
|
+
*
|
1651
|
+
* @type {Array<Object|Number>}
|
1652
|
+
* @extends series.heatmap.data
|
1653
|
+
* @excluding x,y
|
1654
|
+
* @sample {highcharts} highcharts/chart/reflow-true/ Numerical values
|
1655
|
+
* @sample {highcharts} highcharts/series/data-array-of-arrays/ Arrays of numeric x and y
|
1656
|
+
* @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/ Arrays of datetime x and y
|
1657
|
+
* @sample {highcharts} highcharts/series/data-array-of-name-value/ Arrays of point.name and y
|
1658
|
+
* @sample {highcharts} highcharts/series/data-array-of-objects/ Config objects
|
1659
|
+
* @product highcharts
|
1660
|
+
* @apioption series.treemap.data
|
1661
|
+
*/
|
1662
|
+
|
1663
|
+
/**
|
1664
|
+
* The value of the point, resulting in a relative area of the point
|
1665
|
+
* in the treemap.
|
1666
|
+
*
|
1667
|
+
* @type {Number}
|
1668
|
+
* @product highcharts
|
1669
|
+
* @apioption series.treemap.data.value
|
1670
|
+
*/
|
1671
|
+
|
1672
|
+
/**
|
1673
|
+
* Serves a purpose only if a `colorAxis` object is defined in the chart
|
1674
|
+
* options. This value will decide which color the point gets from the
|
1675
|
+
* scale of the colorAxis.
|
1676
|
+
*
|
1677
|
+
* @type {Number}
|
1678
|
+
* @default undefined
|
1679
|
+
* @since 4.1.0
|
1680
|
+
* @product highcharts
|
1681
|
+
* @apioption series.treemap.data.colorValue
|
1682
|
+
*/
|
1683
|
+
|
1684
|
+
/**
|
1685
|
+
* Only for treemap. Use this option to build a tree structure. The
|
1686
|
+
* value should be the id of the point which is the parent. If no points
|
1687
|
+
* has a matching id, or this option is undefined, then the parent will
|
1688
|
+
* be set to the root.
|
1689
|
+
*
|
1690
|
+
* @type {String}
|
1691
|
+
* @sample {highcharts} highcharts/point/parent/ Point parent
|
1692
|
+
* @sample {highcharts} highcharts/demo/treemap-with-levels/ Example where parent id is not matching
|
1693
|
+
* @default undefined
|
1694
|
+
* @since 4.1.0
|
1695
|
+
* @product highcharts
|
1696
|
+
* @apioption series.treemap.data.parent
|
1697
|
+
*/
|
1698
|
+
|
1699
|
+
}(Highcharts, result));
|
1700
|
+
(function(H, drawPoint, mixinTreeSeries) {
|
1701
|
+
/**
|
1702
|
+
* (c) 2016 Highsoft AS
|
1703
|
+
* Authors: Jon Arild Nygard
|
1704
|
+
*
|
1705
|
+
* License: www.highcharts.com/license
|
1706
|
+
*
|
1707
|
+
* This is an experimental Highcharts module which enables visualization
|
1708
|
+
* of a word cloud.
|
1709
|
+
*/
|
1710
|
+
var CenteredSeriesMixin = H.CenteredSeriesMixin,
|
1711
|
+
Series = H.Series,
|
1712
|
+
each = H.each,
|
1713
|
+
extend = H.extend,
|
1714
|
+
getCenter = CenteredSeriesMixin.getCenter,
|
1715
|
+
getColor = mixinTreeSeries.getColor,
|
1716
|
+
getStartAndEndRadians = CenteredSeriesMixin.getStartAndEndRadians,
|
1717
|
+
grep = H.grep,
|
1718
|
+
isBoolean = function(x) {
|
1719
|
+
return typeof x === 'boolean';
|
1720
|
+
},
|
1721
|
+
isNumber = H.isNumber,
|
1722
|
+
isObject = H.isObject,
|
1723
|
+
isString = H.isString,
|
1724
|
+
merge = H.merge,
|
1725
|
+
noop = H.noop,
|
1726
|
+
pick = H.pick,
|
1727
|
+
rad2deg = 180 / Math.PI,
|
1728
|
+
seriesType = H.seriesType,
|
1729
|
+
seriesTypes = H.seriesTypes,
|
1730
|
+
setTreeValues = mixinTreeSeries.setTreeValues,
|
1731
|
+
reduce = H.reduce;
|
1732
|
+
|
1733
|
+
var layoutAlgorithm = function layoutAlgorithm(parent, children) {
|
1734
|
+
var startAngle = parent.start,
|
1735
|
+
range = parent.end - startAngle,
|
1736
|
+
total = parent.val,
|
1737
|
+
x = parent.x,
|
1738
|
+
y = parent.y,
|
1739
|
+
innerRadius = parent.r,
|
1740
|
+
outerRadius = innerRadius + parent.radius;
|
1741
|
+
|
1742
|
+
return reduce(children || [], function(arr, child) {
|
1743
|
+
var percentage = (1 / total) * child.val,
|
1744
|
+
radians = percentage * range,
|
1745
|
+
values = {
|
1746
|
+
x: x,
|
1747
|
+
y: y,
|
1748
|
+
innerR: innerRadius,
|
1749
|
+
r: outerRadius,
|
1750
|
+
radius: parent.radius,
|
1751
|
+
start: startAngle,
|
1752
|
+
end: startAngle + radians
|
1753
|
+
};
|
1754
|
+
arr.push(values);
|
1755
|
+
startAngle = values.end;
|
1756
|
+
return arr;
|
1757
|
+
}, []);
|
1758
|
+
};
|
1759
|
+
|
1760
|
+
/**
|
1761
|
+
* getEndPoint - Find a set of coordinates given a start coordinates, an angle,
|
1762
|
+
* and a distance.
|
1763
|
+
*
|
1764
|
+
* @param {number} x Start coordinate x
|
1765
|
+
* @param {number} y Start coordinate y
|
1766
|
+
* @param {number} angle Angle in radians
|
1767
|
+
* @param {number} distance Distance from start to end coordinates
|
1768
|
+
* @return {object} Returns the end coordinates, x and y.
|
1769
|
+
*/
|
1770
|
+
var getEndPoint = function getEndPoint(x, y, angle, distance) {
|
1771
|
+
return {
|
1772
|
+
x: x + (Math.cos(angle) * distance),
|
1773
|
+
y: y + (Math.sin(angle) * distance)
|
1774
|
+
};
|
1775
|
+
};
|
1776
|
+
|
1777
|
+
var getDlOptions = function getDlOptions(params) {
|
1778
|
+
// Set options to new object to avoid problems with scope
|
1779
|
+
var shape = isObject(params.shapeArgs) ? params.shapeArgs : {},
|
1780
|
+
optionsSeries = (
|
1781
|
+
isObject(params.optionsSeries) ?
|
1782
|
+
params.optionsSeries.dataLabels : {}
|
1783
|
+
),
|
1784
|
+
optionsPoint = (
|
1785
|
+
isObject(params.optionsPoint) ?
|
1786
|
+
params.optionsPoint.dataLabels : {}
|
1787
|
+
),
|
1788
|
+
optionsLevel = (
|
1789
|
+
isObject(params.level) ?
|
1790
|
+
params.level.dataLabels : {}
|
1791
|
+
),
|
1792
|
+
options = merge({
|
1793
|
+
rotationMode: 'perpendicular',
|
1794
|
+
style: {
|
1795
|
+
width: shape.radius
|
1796
|
+
}
|
1797
|
+
}, optionsSeries, optionsLevel, optionsPoint),
|
1798
|
+
rotationRad,
|
1799
|
+
rotation;
|
1800
|
+
if (!isNumber(options.rotation)) {
|
1801
|
+
rotationRad = (shape.end - (shape.end - shape.start) / 2);
|
1802
|
+
rotation = (rotationRad * rad2deg) % 180;
|
1803
|
+
if (options.rotationMode === 'parallel') {
|
1804
|
+
rotation -= 90;
|
1805
|
+
}
|
1806
|
+
// Data labels should not rotate beyond 90 degrees, for readability.
|
1807
|
+
if (rotation > 90) {
|
1808
|
+
rotation -= 180;
|
1809
|
+
}
|
1810
|
+
options.rotation = rotation;
|
1811
|
+
}
|
1812
|
+
// NOTE: alignDataLabel positions the data label differntly when rotation is
|
1813
|
+
// 0. Avoiding this by setting rotation to a small number.
|
1814
|
+
if (options.rotation === 0) {
|
1815
|
+
options.rotation = 0.001;
|
1816
|
+
}
|
1817
|
+
return options;
|
1818
|
+
};
|
1819
|
+
|
1820
|
+
var getAnimation = function getAnimation(shape, params) {
|
1821
|
+
var to = {
|
1822
|
+
end: shape.end,
|
1823
|
+
start: shape.start,
|
1824
|
+
innerR: shape.innerR,
|
1825
|
+
r: shape.r
|
1826
|
+
},
|
1827
|
+
from = {},
|
1828
|
+
point = params.point,
|
1829
|
+
radians = params.radians,
|
1830
|
+
innerR = params.innerR,
|
1831
|
+
idRoot = params.idRoot,
|
1832
|
+
idPreviousRoot = params.idPreviousRoot,
|
1833
|
+
shapeExisting = params.shapeExisting,
|
1834
|
+
shapeRoot = params.shapeRoot,
|
1835
|
+
shapePreviousRoot = params.shapePreviousRoot,
|
1836
|
+
visible = params.visible;
|
1837
|
+
if (visible) {
|
1838
|
+
// Animate points in
|
1839
|
+
if (!point.graphic && shapePreviousRoot) {
|
1840
|
+
if (idRoot === point.id) {
|
1841
|
+
from = {
|
1842
|
+
start: radians.start,
|
1843
|
+
end: radians.end
|
1844
|
+
};
|
1845
|
+
} else {
|
1846
|
+
from = (shapePreviousRoot.end <= shape.start) ? {
|
1847
|
+
start: radians.end,
|
1848
|
+
end: radians.end
|
1849
|
+
} : {
|
1850
|
+
start: radians.start,
|
1851
|
+
end: radians.start
|
1852
|
+
};
|
1853
|
+
}
|
1854
|
+
// Animate from center and outwards.
|
1855
|
+
from.innerR = from.r = innerR;
|
1856
|
+
}
|
1857
|
+
} else {
|
1858
|
+
// Animate points out
|
1859
|
+
if (point.graphic) {
|
1860
|
+
if (idPreviousRoot === point.id) {
|
1861
|
+
to = {
|
1862
|
+
innerR: innerR,
|
1863
|
+
r: innerR
|
1864
|
+
};
|
1865
|
+
} else if (shapeRoot) {
|
1866
|
+
to = (shapeRoot.end <= shapeExisting.start) ? {
|
1867
|
+
innerR: innerR,
|
1868
|
+
r: innerR,
|
1869
|
+
start: radians.end,
|
1870
|
+
end: radians.end
|
1871
|
+
} : {
|
1872
|
+
innerR: innerR,
|
1873
|
+
r: innerR,
|
1874
|
+
start: radians.start,
|
1875
|
+
end: radians.start
|
1876
|
+
};
|
1877
|
+
}
|
1878
|
+
}
|
1879
|
+
}
|
1880
|
+
return {
|
1881
|
+
from: from,
|
1882
|
+
to: to
|
1883
|
+
};
|
1884
|
+
};
|
1885
|
+
|
1886
|
+
var setShapeArgs = function setShapeArgs(parent, parentValues) {
|
1887
|
+
var childrenValues = [],
|
1888
|
+
// Collect all children which should be included
|
1889
|
+
children = grep(parent.children, function(n) {
|
1890
|
+
return n.visible;
|
1891
|
+
});
|
1892
|
+
childrenValues = layoutAlgorithm(parentValues, children);
|
1893
|
+
each(children, function(child, index) {
|
1894
|
+
var values = childrenValues[index],
|
1895
|
+
angle = values.start + ((values.end - values.start) / 2),
|
1896
|
+
radius = values.innerR + ((values.r - values.innerR) / 2),
|
1897
|
+
isCircle = (
|
1898
|
+
values.innerR === 0 &&
|
1899
|
+
(values.end - values.start) > 6.28
|
1900
|
+
),
|
1901
|
+
center = (
|
1902
|
+
isCircle ? {
|
1903
|
+
x: values.x,
|
1904
|
+
y: values.y
|
1905
|
+
} :
|
1906
|
+
getEndPoint(values.x, values.y, angle, radius)
|
1907
|
+
),
|
1908
|
+
val = (
|
1909
|
+
child.val ?
|
1910
|
+
(
|
1911
|
+
child.childrenTotal > child.val ?
|
1912
|
+
child.childrenTotal :
|
1913
|
+
child.val
|
1914
|
+
) :
|
1915
|
+
child.childrenTotal
|
1916
|
+
);
|
1917
|
+
child.shapeArgs = merge(values, {
|
1918
|
+
plotX: center.x,
|
1919
|
+
plotY: center.y
|
1920
|
+
});
|
1921
|
+
child.values = merge(values, {
|
1922
|
+
val: val
|
1923
|
+
});
|
1924
|
+
// If node has children, then call method recursively
|
1925
|
+
if (child.children.length) {
|
1926
|
+
setShapeArgs(child, child.values);
|
1927
|
+
}
|
1928
|
+
});
|
1929
|
+
};
|
1930
|
+
|
1931
|
+
var getDrillId = function getDrillId(point, idRoot, mapIdToNode) {
|
1932
|
+
var drillId,
|
1933
|
+
node = point.node,
|
1934
|
+
nodeRoot;
|
1935
|
+
if (!node.isLeaf) {
|
1936
|
+
// When it is the root node, the drillId should be set to parent.
|
1937
|
+
if (idRoot === point.id) {
|
1938
|
+
nodeRoot = mapIdToNode[idRoot];
|
1939
|
+
drillId = nodeRoot.parent;
|
1940
|
+
} else {
|
1941
|
+
drillId = point.id;
|
1942
|
+
}
|
1943
|
+
}
|
1944
|
+
return drillId;
|
1945
|
+
};
|
1946
|
+
|
1947
|
+
var cbSetTreeValuesBefore = function before(node, options) {
|
1948
|
+
var mapIdToNode = options.mapIdToNode,
|
1949
|
+
nodeParent = mapIdToNode[node.parent],
|
1950
|
+
series = options.series,
|
1951
|
+
chart = series.chart,
|
1952
|
+
points = series.points,
|
1953
|
+
point = points[node.i],
|
1954
|
+
colorInfo = getColor(node, {
|
1955
|
+
colors: chart && chart.options && chart.options.colors,
|
1956
|
+
colorIndex: series.colorIndex,
|
1957
|
+
colorByPoint: series.colorByPoint,
|
1958
|
+
index: options.index,
|
1959
|
+
levelMap: options.levelMap,
|
1960
|
+
parentColor: nodeParent && nodeParent.color,
|
1961
|
+
parentColorIndex: nodeParent && nodeParent.colorIndex,
|
1962
|
+
series: options.series,
|
1963
|
+
siblings: options.siblings
|
1964
|
+
});
|
1965
|
+
node.color = colorInfo.color;
|
1966
|
+
node.colorIndex = colorInfo.colorIndex;
|
1967
|
+
if (point) {
|
1968
|
+
point.color = node.color;
|
1969
|
+
point.colorIndex = node.colorIndex;
|
1970
|
+
}
|
1971
|
+
return node;
|
1972
|
+
};
|
1973
|
+
|
1974
|
+
/**
|
1975
|
+
* A Sunburst displays hierarchical data, where a level in the hierarchy is represented by a circle.
|
1976
|
+
* The center represents the root node of the tree.
|
1977
|
+
* The visualization bears a resemblance to both treemap and pie charts.
|
1978
|
+
*
|
1979
|
+
* @extends {plotOptions.pie}
|
1980
|
+
* @sample highcharts/demo/sunburst Sunburst chart
|
1981
|
+
* @excluding allAreas, center, clip, colorAxis, compare, compareBase,
|
1982
|
+
* dataGrouping, depth, endAngle, gapSize, gapUnit,
|
1983
|
+
* ignoreHiddenPoint, innerSize, joinBy, legendType, linecap,
|
1984
|
+
* minSize, navigatorOptions, pointRange, slicedOffset
|
1985
|
+
* @product highcharts
|
1986
|
+
* @optionparent plotOptions.sunburst
|
1987
|
+
*/
|
1988
|
+
var sunburstOptions = {
|
1989
|
+
/**
|
1990
|
+
* The center of the sunburst chart relative to the plot area. Can be
|
1991
|
+
* percentages or pixel values.
|
1992
|
+
*
|
1993
|
+
* @type {Array<String|Number>}
|
1994
|
+
* @sample {highcharts} highcharts/plotoptions/pie-center/ Centered at 100, 100
|
1995
|
+
* @product highcharts
|
1996
|
+
*/
|
1997
|
+
center: ['50%', '50%'],
|
1998
|
+
/**
|
1999
|
+
* @extends plotOptions.series.dataLabels
|
2000
|
+
* @excluding align,allowOverlap,staggerLines,step
|
2001
|
+
*/
|
2002
|
+
dataLabels: {
|
2003
|
+
defer: true,
|
2004
|
+
style: {
|
2005
|
+
textOverflow: 'ellipsis'
|
2006
|
+
},
|
2007
|
+
/**
|
2008
|
+
* Decides how the data label will be rotated according to the perimeter
|
2009
|
+
* of the sunburst. It can either be parallel or perpendicular to the
|
2010
|
+
* perimeter.
|
2011
|
+
* `series.rotation` takes precedence over `rotationMode`.
|
2012
|
+
* @since 6.0.0
|
2013
|
+
* @validvalue ["perpendicular", "parallel"]
|
2014
|
+
*/
|
2015
|
+
rotationMode: 'perpendicular'
|
2016
|
+
},
|
2017
|
+
/**
|
2018
|
+
* Which point to use as a root in the visualization.
|
2019
|
+
*
|
2020
|
+
* @type {String|undefined}
|
2021
|
+
* @default undefined
|
2022
|
+
*/
|
2023
|
+
rootId: undefined,
|
2024
|
+
|
2025
|
+
/**
|
2026
|
+
* Used together with the levels and `allowDrillToNode` options. When
|
2027
|
+
* set to false the first level visible when drilling is considered
|
2028
|
+
* to be level one. Otherwise the level will be the same as the tree
|
2029
|
+
* structure.
|
2030
|
+
*/
|
2031
|
+
levelIsConstant: true
|
2032
|
+
/**
|
2033
|
+
* Set options on specific levels. Takes precedence over series options,
|
2034
|
+
* but not point options.
|
2035
|
+
*
|
2036
|
+
* @type {Array<Object>}
|
2037
|
+
* @sample highcharts/demo/sunburst Sunburst chart
|
2038
|
+
* @apioption plotOptions.sunburst.levels
|
2039
|
+
*/
|
2040
|
+
/**
|
2041
|
+
* Can set a `borderColor` on all points which lies on the same level.
|
2042
|
+
*
|
2043
|
+
* @type {String}
|
2044
|
+
* @apioption plotOptions.sunburst.levels.borderColor
|
2045
|
+
*/
|
2046
|
+
/**
|
2047
|
+
* Can set a `borderWidth` on all points which lies on the same level.
|
2048
|
+
*
|
2049
|
+
* @type {String}
|
2050
|
+
* @apioption plotOptions.sunburst.levels.borderWidth
|
2051
|
+
*/
|
2052
|
+
/**
|
2053
|
+
* Can set a `borderDashStyle` on all points which lies on the same level.
|
2054
|
+
*
|
2055
|
+
* @type {String}
|
2056
|
+
* @apioption plotOptions.sunburst.levels.borderDashStyle
|
2057
|
+
*/
|
2058
|
+
/**
|
2059
|
+
* Can set a `color` on all points which lies on the same level.
|
2060
|
+
*
|
2061
|
+
* @type {String}
|
2062
|
+
* @apioption plotOptions.sunburst.levels.color
|
2063
|
+
*/
|
2064
|
+
/**
|
2065
|
+
* Can set a `colorVariation` on all points which lies on the same level.
|
2066
|
+
*
|
2067
|
+
* @type {Object}
|
2068
|
+
* @apioption plotOptions.sunburst.levels.colorVariation
|
2069
|
+
*/
|
2070
|
+
/**
|
2071
|
+
* The key of a color variation. Currently supports `brightness` only.
|
2072
|
+
*
|
2073
|
+
* @type {String}
|
2074
|
+
* @apioption plotOptions.sunburst.levels.colorVariation.key
|
2075
|
+
*/
|
2076
|
+
/**
|
2077
|
+
* The ending value of a color variation. The last sibling will receive this
|
2078
|
+
* value.
|
2079
|
+
*
|
2080
|
+
* @type {String}
|
2081
|
+
* @apioption plotOptions.sunburst.levels.colorVariation.to
|
2082
|
+
*/
|
2083
|
+
/**
|
2084
|
+
* Can set a `dataLabels` on all points which lies on the same level.
|
2085
|
+
*
|
2086
|
+
* @type {Object}
|
2087
|
+
* @apioption plotOptions.sunburst.levels.dataLabels
|
2088
|
+
*/
|
2089
|
+
/**
|
2090
|
+
* Can set a `rotation` on all points which lies on the same level.
|
2091
|
+
*
|
2092
|
+
* @type {Number}
|
2093
|
+
* @apioption plotOptions.sunburst.levels.rotation
|
2094
|
+
*/
|
2095
|
+
/**
|
2096
|
+
* Can set a `rotationMode` on all points which lies on the same level.
|
2097
|
+
*
|
2098
|
+
* @type {String}
|
2099
|
+
* @apioption plotOptions.sunburst.levels.rotationMode
|
2100
|
+
*/
|
2101
|
+
/**
|
2102
|
+
* When enabled the user can click on a point which is a parent and
|
2103
|
+
* zoom in on its children.
|
2104
|
+
*
|
2105
|
+
* @sample highcharts/demo/sunburst
|
2106
|
+
* Allow drill to node
|
2107
|
+
* @type {Boolean}
|
2108
|
+
* @default false
|
2109
|
+
* @apioption plotOptions.sunburst.allowDrillToNode
|
2110
|
+
*/
|
2111
|
+
};
|
2112
|
+
|
2113
|
+
/**
|
2114
|
+
* Properties of the Sunburst series.
|
2115
|
+
*/
|
2116
|
+
var sunburstSeries = {
|
2117
|
+
drawDataLabels: noop, // drawDataLabels is called in drawPoints
|
2118
|
+
drawPoints: function drawPoints() {
|
2119
|
+
var series = this,
|
2120
|
+
levelMap = series.levelMap,
|
2121
|
+
shapeRoot = series.shapeRoot,
|
2122
|
+
group = series.group,
|
2123
|
+
hasRendered = series.hasRendered,
|
2124
|
+
idRoot = series.rootNode,
|
2125
|
+
idPreviousRoot = series.idPreviousRoot,
|
2126
|
+
nodeMap = series.nodeMap,
|
2127
|
+
nodePreviousRoot = nodeMap[idPreviousRoot],
|
2128
|
+
shapePreviousRoot = nodePreviousRoot && nodePreviousRoot.shapeArgs,
|
2129
|
+
points = series.points,
|
2130
|
+
radians = series.startAndEndRadians,
|
2131
|
+
chart = series.chart,
|
2132
|
+
optionsChart = chart && chart.options && chart.options.chart || {},
|
2133
|
+
animation = (
|
2134
|
+
isBoolean(optionsChart.animation) ?
|
2135
|
+
optionsChart.animation :
|
2136
|
+
true
|
2137
|
+
),
|
2138
|
+
positions = series.center,
|
2139
|
+
innerR = positions[3] / 2,
|
2140
|
+
renderer = series.chart.renderer,
|
2141
|
+
animateLabels,
|
2142
|
+
addedHack = false,
|
2143
|
+
hackDataLabelAnimation = !!(
|
2144
|
+
animation &&
|
2145
|
+
hasRendered &&
|
2146
|
+
idRoot !== idPreviousRoot &&
|
2147
|
+
series.dataLabelsGroup
|
2148
|
+
);
|
2149
|
+
|
2150
|
+
if (hackDataLabelAnimation) {
|
2151
|
+
series.dataLabelsGroup.attr({
|
2152
|
+
opacity: 0
|
2153
|
+
});
|
2154
|
+
animateLabels = function() {
|
2155
|
+
var s = series;
|
2156
|
+
if (s.dataLabelsGroup) {
|
2157
|
+
s.dataLabelsGroup.animate({
|
2158
|
+
opacity: 1,
|
2159
|
+
visibility: 'visible'
|
2160
|
+
});
|
2161
|
+
}
|
2162
|
+
};
|
2163
|
+
}
|
2164
|
+
each(points, function(point) {
|
2165
|
+
var node = point.node,
|
2166
|
+
level = levelMap[node.levelDynamic],
|
2167
|
+
shapeExisting = point.shapeExisting || {},
|
2168
|
+
shape = node.shapeArgs || {},
|
2169
|
+
attrStyle = series.pointAttribs(point, point.selected && 'select'),
|
2170
|
+
animationInfo,
|
2171
|
+
onComplete,
|
2172
|
+
visible = !!(node.visible && node.shapeArgs);
|
2173
|
+
if (hasRendered && animation) {
|
2174
|
+
animationInfo = getAnimation(shape, {
|
2175
|
+
point: point,
|
2176
|
+
radians: radians,
|
2177
|
+
innerR: innerR,
|
2178
|
+
idRoot: idRoot,
|
2179
|
+
idPreviousRoot: idPreviousRoot,
|
2180
|
+
shapeExisting: shapeExisting,
|
2181
|
+
shapeRoot: shapeRoot,
|
2182
|
+
shapePreviousRoot: shapePreviousRoot,
|
2183
|
+
visible: visible
|
2184
|
+
});
|
2185
|
+
} else {
|
2186
|
+
// When animation is disabled, attr is called from animation.
|
2187
|
+
animationInfo = {
|
2188
|
+
to: shape,
|
2189
|
+
from: {}
|
2190
|
+
};
|
2191
|
+
}
|
2192
|
+
extend(point, {
|
2193
|
+
shapeExisting: shape, // Store for use in animation
|
2194
|
+
tooltipPos: [shape.plotX, shape.plotY],
|
2195
|
+
drillId: getDrillId(point, idRoot, nodeMap),
|
2196
|
+
name: '' + (point.name || point.id || point.index),
|
2197
|
+
plotX: shape.plotX, // used for data label position
|
2198
|
+
plotY: shape.plotY, // used for data label position
|
2199
|
+
value: node.val,
|
2200
|
+
isNull: !visible // used for dataLabels & point.draw
|
2201
|
+
});
|
2202
|
+
point.dlOptions = getDlOptions({
|
2203
|
+
level: level,
|
2204
|
+
optionsPoint: point.options,
|
2205
|
+
optionsSeries: series.options,
|
2206
|
+
shapeArgs: shape
|
2207
|
+
});
|
2208
|
+
if (!addedHack && visible) {
|
2209
|
+
addedHack = true;
|
2210
|
+
onComplete = animateLabels;
|
2211
|
+
}
|
2212
|
+
point.draw({
|
2213
|
+
animate: animationInfo.to,
|
2214
|
+
attr: extend(animationInfo.from, attrStyle),
|
2215
|
+
onComplete: onComplete,
|
2216
|
+
group: group,
|
2217
|
+
renderer: renderer,
|
2218
|
+
shapeType: 'arc',
|
2219
|
+
shapeArgs: shape
|
2220
|
+
});
|
2221
|
+
});
|
2222
|
+
// Draw data labels after points
|
2223
|
+
// TODO draw labels one by one to avoid addtional looping
|
2224
|
+
if (hackDataLabelAnimation && addedHack) {
|
2225
|
+
series.hasRendered = false;
|
2226
|
+
series.options.dataLabels.defer = true;
|
2227
|
+
Series.prototype.drawDataLabels.call(series);
|
2228
|
+
series.hasRendered = true;
|
2229
|
+
} else {
|
2230
|
+
Series.prototype.drawDataLabels.call(series);
|
2231
|
+
}
|
2232
|
+
},
|
2233
|
+
pointAttribs: seriesTypes.column.prototype.pointAttribs,
|
2234
|
+
translate: function translate() {
|
2235
|
+
var series = this,
|
2236
|
+
options = series.options,
|
2237
|
+
positions = series.center = getCenter.call(series),
|
2238
|
+
radians = series.startAndEndRadians = getStartAndEndRadians(options.startAngle, options.endAngle),
|
2239
|
+
innerRadius = positions[3] / 2,
|
2240
|
+
outerRadius = positions[2] / 2,
|
2241
|
+
idRoot = series.rootNode = pick(series.rootNode, options.rootId, ''),
|
2242
|
+
mapIdToNode = series.nodeMap,
|
2243
|
+
idTop,
|
2244
|
+
nodeRoot = mapIdToNode && mapIdToNode[idRoot],
|
2245
|
+
nodeTop,
|
2246
|
+
radiusPerLevel,
|
2247
|
+
tree,
|
2248
|
+
height,
|
2249
|
+
values;
|
2250
|
+
series.shapeRoot = nodeRoot && nodeRoot.shapeArgs;
|
2251
|
+
// Call prototype function
|
2252
|
+
Series.prototype.translate.call(series);
|
2253
|
+
// Create a object map from level to options
|
2254
|
+
series.levelMap = reduce(series.options.levels || [],
|
2255
|
+
function(arr, item) {
|
2256
|
+
arr[item.level] = item;
|
2257
|
+
return arr;
|
2258
|
+
}, {});
|
2259
|
+
// @todo Only if series.isDirtyData is true
|
2260
|
+
tree = series.tree = series.getTree();
|
2261
|
+
mapIdToNode = series.nodeMap;
|
2262
|
+
nodeRoot = mapIdToNode[idRoot];
|
2263
|
+
idTop = isString(nodeRoot.parent) ? nodeRoot.parent : '';
|
2264
|
+
nodeTop = mapIdToNode[idTop];
|
2265
|
+
// TODO Try to combine setTreeValues & setColorRecursive to avoid
|
2266
|
+
// unnecessary looping.
|
2267
|
+
setTreeValues(tree, {
|
2268
|
+
before: cbSetTreeValuesBefore,
|
2269
|
+
idRoot: idRoot,
|
2270
|
+
levelIsConstant: options.levelIsConstant,
|
2271
|
+
levelMap: series.levelMap,
|
2272
|
+
mapIdToNode: mapIdToNode,
|
2273
|
+
points: series.points,
|
2274
|
+
series: series
|
2275
|
+
});
|
2276
|
+
height = (idRoot === idTop) ? nodeRoot.height : nodeRoot.height + 1;
|
2277
|
+
radiusPerLevel = (outerRadius - innerRadius) / height;
|
2278
|
+
values = mapIdToNode[''].shapeArgs = {
|
2279
|
+
end: radians.end,
|
2280
|
+
r: innerRadius,
|
2281
|
+
radius: radiusPerLevel,
|
2282
|
+
start: radians.start,
|
2283
|
+
val: nodeTop.val,
|
2284
|
+
x: positions[0],
|
2285
|
+
y: positions[1]
|
2286
|
+
};
|
2287
|
+
setShapeArgs(nodeTop, values);
|
2288
|
+
},
|
2289
|
+
|
2290
|
+
/**
|
2291
|
+
* Animate the slices in. Similar to the animation of polar charts.
|
2292
|
+
*/
|
2293
|
+
animate: function(init) {
|
2294
|
+
var chart = this.chart,
|
2295
|
+
center = [
|
2296
|
+
chart.plotWidth / 2,
|
2297
|
+
chart.plotHeight / 2
|
2298
|
+
],
|
2299
|
+
plotLeft = chart.plotLeft,
|
2300
|
+
plotTop = chart.plotTop,
|
2301
|
+
attribs,
|
2302
|
+
group = this.group;
|
2303
|
+
|
2304
|
+
// Initialize the animation
|
2305
|
+
if (init) {
|
2306
|
+
|
2307
|
+
// Scale down the group and place it in the center
|
2308
|
+
attribs = {
|
2309
|
+
translateX: center[0] + plotLeft,
|
2310
|
+
translateY: center[1] + plotTop,
|
2311
|
+
scaleX: 0.001, // #1499
|
2312
|
+
scaleY: 0.001,
|
2313
|
+
rotation: 10,
|
2314
|
+
opacity: 0.01
|
2315
|
+
};
|
2316
|
+
|
2317
|
+
group.attr(attribs);
|
2318
|
+
|
2319
|
+
// Run the animation
|
2320
|
+
} else {
|
2321
|
+
attribs = {
|
2322
|
+
translateX: plotLeft,
|
2323
|
+
translateY: plotTop,
|
2324
|
+
scaleX: 1,
|
2325
|
+
scaleY: 1,
|
2326
|
+
rotation: 0,
|
2327
|
+
opacity: 1
|
2328
|
+
};
|
2329
|
+
group.animate(attribs, this.options.animation);
|
2330
|
+
|
2331
|
+
// Delete this function to allow it only once
|
2332
|
+
this.animate = null;
|
2333
|
+
}
|
2334
|
+
}
|
2335
|
+
};
|
2336
|
+
|
2337
|
+
/**
|
2338
|
+
* Properties of the Sunburst series.
|
2339
|
+
*/
|
2340
|
+
var sunburstPoint = {
|
2341
|
+
draw: drawPoint,
|
2342
|
+
shouldDraw: function shouldDraw() {
|
2343
|
+
var point = this;
|
2344
|
+
return !point.isNull;
|
2345
|
+
}
|
2346
|
+
};
|
2347
|
+
|
2348
|
+
/**
|
2349
|
+
* A `sunburst` series. If the [type](#series.sunburst.type) option is
|
2350
|
+
* not specified, it is inherited from [chart.type](#chart.type).
|
2351
|
+
*
|
2352
|
+
* For options that apply to multiple series, it is recommended to add
|
2353
|
+
* them to the [plotOptions.series](#plotOptions.series) options structure.
|
2354
|
+
* To apply to all series of this specific type, apply it to [plotOptions.
|
2355
|
+
* sunburst](#plotOptions.sunburst).
|
2356
|
+
*
|
2357
|
+
* @type {Object}
|
2358
|
+
* @extends plotOptions.sunburst
|
2359
|
+
* @excluding dataParser,dataURL,stack
|
2360
|
+
* @product highcharts
|
2361
|
+
* @apioption series.sunburst
|
2362
|
+
*/
|
2363
|
+
|
2364
|
+
/**
|
2365
|
+
* @type {Array<Object|Number>}
|
2366
|
+
* @extends series.treemap.data
|
2367
|
+
* @excluding x,y
|
2368
|
+
* @product highcharts
|
2369
|
+
* @apioption series.sunburst.data
|
2370
|
+
*/
|
2371
|
+
|
2372
|
+
/**
|
2373
|
+
* The value of the point, resulting in a relative area of the point
|
2374
|
+
* in the sunburst.
|
2375
|
+
*
|
2376
|
+
* @type {Number}
|
2377
|
+
* @default undefined
|
2378
|
+
* @since 6.0.0
|
2379
|
+
* @product highcharts
|
2380
|
+
* @apioption series.sunburst.data.value
|
2381
|
+
*/
|
2382
|
+
|
2383
|
+
/**
|
2384
|
+
* Use this option to build a tree structure. The value should be the id of the
|
2385
|
+
* point which is the parent. If no points has a matching id, or this option is
|
2386
|
+
* undefined, then the parent will be set to the root.
|
2387
|
+
*
|
2388
|
+
* @type {String|undefined}
|
2389
|
+
* @default undefined
|
2390
|
+
* @since 6.0.0
|
2391
|
+
* @product highcharts
|
2392
|
+
* @apioption series.treemap.data.parent
|
2393
|
+
*/
|
2394
|
+
seriesType(
|
2395
|
+
'sunburst',
|
2396
|
+
'treemap',
|
2397
|
+
sunburstOptions,
|
2398
|
+
sunburstSeries,
|
2399
|
+
sunburstPoint
|
2400
|
+
);
|
2401
|
+
|
2402
|
+
}(Highcharts, draw, result));
|
2403
|
+
}));
|