highcharts-rails 4.0.4.1 → 4.1.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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Highcharts JS v4.0.4 (2014-09-02)
2
+ * @license Highcharts JS v4.1.0 (2015-02-16)
3
3
  * Exporting module
4
4
  *
5
5
  * (c) 2010-2014 Torstein Honsi
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  // JSLint options:
11
- /*global Highcharts, document, window, Math, setTimeout */
11
+ /*global Highcharts, HighchartsAdapter, document, window, Math, setTimeout */
12
12
 
13
13
  (function (Highcharts) { // encapsulate
14
14
 
@@ -16,6 +16,7 @@
16
16
  var Chart = Highcharts.Chart,
17
17
  addEvent = Highcharts.addEvent,
18
18
  removeEvent = Highcharts.removeEvent,
19
+ fireEvent = HighchartsAdapter.fireEvent,
19
20
  createElement = Highcharts.createElement,
20
21
  discardElement = Highcharts.discardElement,
21
22
  css = Highcharts.css,
@@ -244,6 +245,7 @@ extend(Chart.prototype, {
244
245
  height: sourceHeight
245
246
  });
246
247
  options.exporting.enabled = false; // hide buttons in print
248
+ delete options.data; // #3004
247
249
 
248
250
  // prepare for replicating the chart
249
251
  options.series = [];
@@ -298,7 +300,9 @@ extend(Chart.prototype, {
298
300
  // Any HTML added to the container after the SVG (#894)
299
301
  .replace(/<\/svg>.*?$/, '</svg>')
300
302
  // Batik doesn't support rgba fills and strokes (#3095)
301
- .replace(/(fill|stroke)="rgba\(([ 0-9]+,[ 0-9]+,[ 0-9]+),([ 0-9\.]+)\)"/g, '$1="rgb($2)" $1-opacity="$3"')
303
+ .replace(/(fill|stroke)="rgba\(([ 0-9]+,[ 0-9]+,[ 0-9]+),([ 0-9\.]+)\)"/g, '$1="rgb($2)" $1-opacity="$3"')
304
+ // An issue with PhantomJS as of 2015-01-11. Revisit with newer versions. (#3649)
305
+ .replace(/(text-shadow:[ 0-9a-z]+),[^"]+([;"])/g, '$1$2')
302
306
  /* This fails in IE < 8
303
307
  .replace(/([0-9]+)\.([0-9]+)/g, function(s1, s2, s3) { // round off to save weight
304
308
  return s2 +'.'+ s3[0];
@@ -382,6 +386,8 @@ extend(Chart.prototype, {
382
386
 
383
387
  chart.isPrinting = true;
384
388
 
389
+ fireEvent(chart, 'beforePrint');
390
+
385
391
  // hide all body content
386
392
  each(childNodes, function (node, i) {
387
393
  if (node.nodeType === 1) {
@@ -412,6 +418,8 @@ extend(Chart.prototype, {
412
418
 
413
419
  chart.isPrinting = false;
414
420
 
421
+ fireEvent(chart, 'afterPrint');
422
+
415
423
  }, 1000);
416
424
 
417
425
  },
@@ -504,7 +512,9 @@ extend(Chart.prototype, {
504
512
  },
505
513
  onclick: function () {
506
514
  hide();
507
- item.onclick.apply(chart, arguments);
515
+ if (item.onclick) {
516
+ item.onclick.apply(chart, arguments);
517
+ }
508
518
  },
509
519
  innerHTML: item.text || chart.options.lang[item.textKey]
510
520
  }, extend({
@@ -18,7 +18,8 @@ var defaultOptions = Highcharts.getOptions(),
18
18
  seriesTypes = Highcharts.seriesTypes,
19
19
  merge = Highcharts.merge,
20
20
  noop = function () {},
21
- each = Highcharts.each;
21
+ each = Highcharts.each,
22
+ pick = Highcharts.pick;
22
23
 
23
24
  // set default options
24
25
  defaultPlotOptions.funnel = merge(defaultPlotOptions.pie, {
@@ -49,7 +50,6 @@ seriesTypes.funnel = Highcharts.extendClass(seriesTypes.pie, {
49
50
 
50
51
  type: 'funnel',
51
52
  animate: noop,
52
- singularTooltips: true,
53
53
 
54
54
  /**
55
55
  * Overrides the pie translate method
@@ -222,16 +222,16 @@ seriesTypes.funnel = Highcharts.extendClass(seriesTypes.pie, {
222
222
  renderer = chart.renderer;
223
223
 
224
224
  each(series.data, function (point) {
225
-
226
- var graphic = point.graphic,
225
+ var pointOptions = point.options,
226
+ graphic = point.graphic,
227
227
  shapeArgs = point.shapeArgs;
228
228
 
229
- if (!graphic) { // Create the shapes
229
+ if (!graphic) { // Create the shapes
230
230
  point.graphic = renderer.path(shapeArgs).
231
231
  attr({
232
232
  fill: point.color,
233
- stroke: options.borderColor,
234
- 'stroke-width': options.borderWidth
233
+ stroke: pick(pointOptions.borderColor, options.borderColor),
234
+ 'stroke-width': pick(pointOptions.borderWidth, options.borderWidth)
235
235
  }).
236
236
  add(series.group);
237
237
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Highcharts JS v4.0.4 (2014-09-02)
2
+ * @license Highcharts JS v4.1.0 (2015-02-16)
3
3
  *
4
4
  * (c) 2011-2014 Torstein Honsi
5
5
  *
@@ -17,7 +17,6 @@ var UNDEFINED,
17
17
  Legend = Highcharts.Legend,
18
18
  LegendSymbolMixin = Highcharts.LegendSymbolMixin,
19
19
  Series = Highcharts.Series,
20
- SVGRenderer = Highcharts.SVGRenderer,
21
20
 
22
21
  defaultOptions = Highcharts.getOptions(),
23
22
  each = Highcharts.each,
@@ -25,7 +24,6 @@ var UNDEFINED,
25
24
  extendClass = Highcharts.extendClass,
26
25
  merge = Highcharts.merge,
27
26
  pick = Highcharts.pick,
28
- numberFormat = Highcharts.numberFormat,
29
27
  seriesTypes = Highcharts.seriesTypes,
30
28
  wrap = Highcharts.wrap,
31
29
  noop = function () {};
@@ -103,12 +101,19 @@ extend(ColorAxis.prototype, {
103
101
  tweenColors: function (from, to, pos) {
104
102
  // Check for has alpha, because rgba colors perform worse due to lack of
105
103
  // support in WebKit.
106
- var hasAlpha = (to.rgba[3] !== 1 || from.rgba[3] !== 1);
104
+ var hasAlpha;
105
+
106
+ from = from.rgba;
107
+ to = to.rgba;
108
+ hasAlpha = (to[3] !== 1 || from[3] !== 1);
109
+ if (!to.length || !from.length) {
110
+ Highcharts.error(23);
111
+ }
107
112
  return (hasAlpha ? 'rgba(' : 'rgb(') +
108
- Math.round(to.rgba[0] + (from.rgba[0] - to.rgba[0]) * (1 - pos)) + ',' +
109
- Math.round(to.rgba[1] + (from.rgba[1] - to.rgba[1]) * (1 - pos)) + ',' +
110
- Math.round(to.rgba[2] + (from.rgba[2] - to.rgba[2]) * (1 - pos)) +
111
- (hasAlpha ? (',' + (to.rgba[3] + (from.rgba[3] - to.rgba[3]) * (1 - pos))) : '') + ')';
113
+ Math.round(to[0] + (from[0] - to[0]) * (1 - pos)) + ',' +
114
+ Math.round(to[1] + (from[1] - to[1]) * (1 - pos)) + ',' +
115
+ Math.round(to[2] + (from[2] - to[2]) * (1 - pos)) +
116
+ (hasAlpha ? (',' + (to[3] + (from[3] - to[3]) * (1 - pos))) : '') + ')';
112
117
  },
113
118
 
114
119
  initDataClasses: function (userOptions) {
@@ -258,6 +263,9 @@ extend(ColorAxis.prototype, {
258
263
  this.labelGroup.add(group);
259
264
 
260
265
  this.added = true;
266
+
267
+ this.labelLeft = 0;
268
+ this.labelRight = this.width;
261
269
  }
262
270
  // Reset it to avoid color axis reserving space
263
271
  this.chart.axisOffset[this.side] = sideOffset;
@@ -270,9 +278,10 @@ extend(ColorAxis.prototype, {
270
278
  setLegendColor: function () {
271
279
  var grad,
272
280
  horiz = this.horiz,
273
- options = this.options;
281
+ options = this.options,
282
+ reversed = this.reversed;
274
283
 
275
- grad = horiz ? [0, 0, 1, 0] : [0, 0, 0, 1];
284
+ grad = horiz ? [+reversed, 0, +!reversed, 0] : [0, +!reversed, 0, +reversed]; // #3190
276
285
  this.legendColor = {
277
286
  linearGradient: { x1: grad[0], y1: grad[1], x2: grad[2], y2: grad[3] },
278
287
  stops: options.stops || [
@@ -335,7 +344,7 @@ extend(ColorAxis.prototype, {
335
344
  axisLen = this.len;
336
345
 
337
346
  if (point) {
338
- crossPos = this.toPixels(point.value);
347
+ crossPos = this.toPixels(point[point.series.colorKey]);
339
348
  if (crossPos < axisPos) {
340
349
  crossPos = axisPos - 2;
341
350
  } else if (crossPos > axisPos + axisLen) {
@@ -353,7 +362,7 @@ extend(ColorAxis.prototype, {
353
362
  .attr({
354
363
  fill: this.crosshair.color
355
364
  })
356
- .add(this.labelGroup);
365
+ .add(this.legendGroup);
357
366
  }
358
367
  }
359
368
  },
@@ -404,13 +413,13 @@ extend(ColorAxis.prototype, {
404
413
  name = '> ';
405
414
  }
406
415
  if (from !== UNDEFINED) {
407
- name += numberFormat(from, valueDecimals) + valueSuffix;
416
+ name += Highcharts.numberFormat(from, valueDecimals) + valueSuffix;
408
417
  }
409
418
  if (from !== UNDEFINED && to !== UNDEFINED) {
410
419
  name += ' - ';
411
420
  }
412
421
  if (to !== UNDEFINED) {
413
- name += numberFormat(to, valueDecimals) + valueSuffix;
422
+ name += Highcharts.numberFormat(to, valueDecimals) + valueSuffix;
414
423
  }
415
424
 
416
425
  // Add a mock object to the legend items
@@ -533,55 +542,6 @@ var colorSeriesMixin = {
533
542
  });
534
543
  }
535
544
  };
536
-
537
-
538
- /**
539
- * Wrap the buildText method and add the hook for add text stroke
540
- */
541
- wrap(SVGRenderer.prototype, 'buildText', function (proceed, wrapper) {
542
-
543
- var textStroke = wrapper.styles && wrapper.styles.HcTextStroke;
544
-
545
- proceed.call(this, wrapper);
546
-
547
- // Apply the text stroke
548
- if (textStroke && wrapper.applyTextStroke) {
549
- wrapper.applyTextStroke(textStroke);
550
- }
551
- });
552
-
553
- /**
554
- * Apply an outside text stroke to data labels, based on the custom CSS property, HcTextStroke.
555
- * Consider moving this to Highcharts core, also makes sense on stacked columns etc.
556
- */
557
- SVGRenderer.prototype.Element.prototype.applyTextStroke = function (textStroke) {
558
- var elem = this.element,
559
- tspans,
560
- firstChild;
561
-
562
- textStroke = textStroke.split(' ');
563
- tspans = elem.getElementsByTagName('tspan');
564
- firstChild = elem.firstChild;
565
-
566
- // In order to get the right y position of the clones,
567
- // copy over the y setter
568
- this.ySetter = this.xSetter;
569
-
570
- each([].slice.call(tspans), function (tspan, y) {
571
- var clone;
572
- if (y === 0) {
573
- tspan.setAttribute('x', elem.getAttribute('x'));
574
- if ((y = elem.getAttribute('y')) !== null) {
575
- tspan.setAttribute('y', y);
576
- }
577
- }
578
- clone = tspan.cloneNode(1);
579
- clone.setAttribute('stroke', textStroke[1]);
580
- clone.setAttribute('stroke-width', textStroke[0]);
581
- clone.setAttribute('stroke-linejoin', 'round');
582
- elem.insertBefore(clone, firstChild);
583
- });
584
- };
585
545
  /**
586
546
  * Extend the default options with map options
587
547
  */
@@ -593,14 +553,10 @@ defaultOptions.plotOptions.heatmap = merge(defaultOptions.plotOptions.scatter, {
593
553
  formatter: function () { // #2945
594
554
  return this.point.value;
595
555
  },
556
+ inside: true,
596
557
  verticalAlign: 'middle',
597
558
  crop: false,
598
- overflow: false,
599
- style: {
600
- color: 'white',
601
- fontWeight: 'bold',
602
- HcTextStroke: '1px rgba(0,0,0,0.5)'
603
- }
559
+ overflow: false
604
560
  },
605
561
  marker: null,
606
562
  tooltip: {
@@ -611,6 +567,7 @@ defaultOptions.plotOptions.heatmap = merge(defaultOptions.plotOptions.scatter, {
611
567
  animation: true
612
568
  },
613
569
  hover: {
570
+ halo: false, // #3406, halo is not required on heatmaps
614
571
  brightness: 0.2
615
572
  }
616
573
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Highcharts JS v4.0.4 (2014-09-02)
2
+ * @license Highcharts JS v4.1.0 (2015-02-16)
3
3
  * Plugin for displaying a message when there is no data visible in chart.
4
4
  *
5
5
  * (c) 2010-2014 Highsoft AS
@@ -57,7 +57,7 @@
57
57
  }
58
58
 
59
59
  H.Series.prototype.hasData = function () {
60
- return this.dataMax !== undefined && this.dataMin !== undefined;
60
+ return this.visible && this.dataMax !== undefined && this.dataMin !== undefined; // #3703
61
61
  };
62
62
 
63
63
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Highcharts JS v4.0.4 (2014-09-02)
2
+ * @license Highcharts JS v4.1.0 (2015-02-16)
3
3
  * Solid angular gauge module
4
4
  *
5
5
  * (c) 2010-2014 Torstein Honsi
@@ -7,7 +7,7 @@
7
7
  * License: www.highcharts.com/license
8
8
  */
9
9
 
10
- /*global Highcharts*/
10
+ /*global Highcharts, HighchartsAdapter*/
11
11
  (function (H) {
12
12
  "use strict";
13
13
 
@@ -136,6 +136,15 @@
136
136
  }
137
137
  };
138
138
 
139
+ /**
140
+ * Handle animation of the color attributes directly
141
+ */
142
+ each(['fill', 'stroke'], function (prop) {
143
+ HighchartsAdapter.addAnimSetter(prop, function (fx) {
144
+ fx.elem.attr(prop, colorAxisMethods.tweenColors(H.Color(fx.start), H.Color(fx.end), fx.pos));
145
+ });
146
+ });
147
+
139
148
  // The series prototype
140
149
  H.seriesTypes.solidgauge = H.extendClass(H.seriesTypes.gauge, {
141
150
  type: 'solidgauge',
@@ -162,18 +171,21 @@
162
171
  yAxis = series.yAxis,
163
172
  center = yAxis.center,
164
173
  options = series.options,
174
+ radius = series.radius = (pInt(pick(options.radius, 100)) * center[2]) / 200,
165
175
  renderer = series.chart.renderer;
166
176
 
167
177
  H.each(series.points, function (point) {
168
178
  var graphic = point.graphic,
169
179
  rotation = yAxis.startAngleRad + yAxis.translate(point.y, null, null, null, true),
170
- radius = (pInt(pick(options.radius, 100)) * center[2]) / 200,
171
180
  innerRadius = (pInt(pick(options.innerRadius, 60)) * center[2]) / 200,
172
181
  shapeArgs,
173
182
  d,
174
183
  toColor = yAxis.toColor(point.y, point),
175
184
  fromColor;
176
185
 
186
+ if (toColor === 'none') { // #3708
187
+ toColor = point.color || series.color || 'none';
188
+ }
177
189
  if (toColor !== 'none') {
178
190
  fromColor = point.color;
179
191
  point.color = toColor;
@@ -194,41 +206,44 @@
194
206
  maxAngle = minAngle + 2 * Math.PI;
195
207
  }
196
208
 
197
- shapeArgs = {
209
+ point.shapeArgs = shapeArgs = {
198
210
  x: center[0],
199
211
  y: center[1],
200
212
  r: radius,
201
213
  innerR: innerRadius,
202
214
  start: minAngle,
203
- end: maxAngle
215
+ end: maxAngle,
216
+ fill: toColor
204
217
  };
205
218
 
206
219
  if (graphic) {
207
220
  d = shapeArgs.d;
208
-
209
- /*jslint unparam: true*/
210
- graphic.attr({
211
- fill: point.color
212
- }).animate(shapeArgs, {
213
- step: function (value, fx) {
214
- graphic.attr('fill', colorAxisMethods.tweenColors(H.Color(fromColor), H.Color(toColor), fx.pos));
215
- }
216
- });
217
- /*jslint unparam: false*/
221
+ graphic.animate(shapeArgs);
218
222
  shapeArgs.d = d; // animate alters it
219
223
  } else {
220
224
  point.graphic = renderer.arc(shapeArgs)
221
225
  .attr({
222
226
  stroke: options.borderColor || 'none',
223
227
  'stroke-width': options.borderWidth || 0,
224
- fill: point.color,
228
+ fill: toColor,
225
229
  'sweep-flag': 0
226
230
  })
227
231
  .add(series.group);
228
232
  }
229
233
  });
230
234
  },
231
- animate: null
235
+
236
+ /**
237
+ * Extend the pie slice animation by animating from start angle and up
238
+ */
239
+ animate: function (init) {
240
+
241
+ this.center = this.yAxis.center;
242
+ this.center[3] = 2 * this.radius;
243
+ this.startAngleRad = this.yAxis.startAngleRad;
244
+
245
+ H.seriesTypes.pie.prototype.animate.call(this, init);
246
+ }
232
247
  });
233
248
 
234
249
  }(Highcharts));
@@ -0,0 +1,806 @@
1
+ /**
2
+ * @license Highcharts JS v4.1.0 (2015-02-16)
3
+ *
4
+ * (c) 2014 Highsoft AS
5
+ * Authors: Jon Arild Nygard / Oystein Moseng
6
+ *
7
+ * License: www.highcharts.com/license
8
+ */
9
+
10
+ /*global HighchartsAdapter */
11
+ (function (H) {
12
+ var seriesTypes = H.seriesTypes,
13
+ merge = H.merge,
14
+ extendClass = H.extendClass,
15
+ defaultOptions = H.getOptions(),
16
+ plotOptions = defaultOptions.plotOptions,
17
+ noop = function () { return; },
18
+ each = H.each,
19
+ pick = H.pick,
20
+ Series = H.Series,
21
+ Color = H.Color;
22
+
23
+ // Define default options
24
+ plotOptions.treemap = merge(plotOptions.scatter, {
25
+ showInLegend: false,
26
+ marker: false,
27
+ borderColor: '#E0E0E0',
28
+ borderWidth: 1,
29
+ dataLabels: {
30
+ enabled: true,
31
+ defer: false,
32
+ verticalAlign: 'middle',
33
+ formatter: function () { // #2945
34
+ return this.point.name || this.point.id;
35
+ },
36
+ inside: true
37
+ },
38
+ tooltip: {
39
+ headerFormat: '',
40
+ pointFormat: '<b>{point.name}</b>: {point.value}</b><br/>'
41
+ },
42
+ layoutAlgorithm: 'sliceAndDice',
43
+ layoutStartingDirection: 'vertical',
44
+ alternateStartingDirection: false,
45
+ levelIsConstant: true,
46
+ states: {
47
+ hover: {
48
+ borderColor: '#A0A0A0',
49
+ brightness: seriesTypes.heatmap ? 0 : 0.1,
50
+ shadow: false
51
+ }
52
+ },
53
+ drillUpButton: {
54
+ position: {
55
+ align: 'left',
56
+ x: 10,
57
+ y: -50
58
+ }
59
+ }
60
+ });
61
+
62
+ // Stolen from heatmap
63
+ var colorSeriesMixin = {
64
+ // mapping between SVG attributes and the corresponding options
65
+ pointAttrToOptions: {
66
+ stroke: 'borderColor',
67
+ 'stroke-width': 'borderWidth',
68
+ fill: 'color',
69
+ dashstyle: 'borderDashStyle'
70
+ },
71
+ pointArrayMap: ['value'],
72
+ axisTypes: seriesTypes.heatmap ? ['xAxis', 'yAxis', 'colorAxis'] : ['xAxis', 'yAxis'],
73
+ optionalAxis: 'colorAxis',
74
+ getSymbol: noop,
75
+ parallelArrays: ['x', 'y', 'value', 'colorValue'],
76
+ colorKey: 'colorValue', // Point color option key
77
+ translateColors: seriesTypes.heatmap && seriesTypes.heatmap.prototype.translateColors
78
+ };
79
+
80
+ // The Treemap series type
81
+ seriesTypes.treemap = extendClass(seriesTypes.scatter, merge(colorSeriesMixin, {
82
+ type: 'treemap',
83
+ trackerGroups: ['group', 'dataLabelsGroup'],
84
+ pointClass: extendClass(H.Point, {
85
+ setState: function (state, move) {
86
+ H.Point.prototype.setState.call(this, state, move);
87
+ if (state === 'hover') {
88
+ if (this.dataLabel) {
89
+ this.dataLabel.attr({ zIndex: 1002 });
90
+ }
91
+ } else {
92
+ if (this.dataLabel) {
93
+ this.dataLabel.attr({ zIndex: (this.pointAttr[''].zIndex + 1) });
94
+ }
95
+ }
96
+ }
97
+ }),
98
+ handleLayout: function () {
99
+ var series = this,
100
+ tree = this.tree,
101
+ seriesArea;
102
+ if (this.points.length) {
103
+ // Assign variables
104
+ if (!tree) {
105
+ this.nodeMap = [];
106
+ tree = this.tree = this.getTree();
107
+ }
108
+ if (!this.rootNode) {
109
+ this.rootNode = "";
110
+ }
111
+ this.levelMap = this.getLevels();
112
+ each(series.points, function (point) {
113
+ // Reset visibility
114
+ delete point.plotX;
115
+ delete point.plotY;
116
+ });
117
+ seriesArea = this.getSeriesArea(tree.val);
118
+ this.nodeMap[""].values = seriesArea;
119
+ this.calculateArea(tree, seriesArea);
120
+ this.setPointValues();
121
+ }
122
+ },
123
+ /**
124
+ * Creates a tree structured object from the series points
125
+ */
126
+ getTree: function () {
127
+ var tree,
128
+ series = this,
129
+ i = 0,
130
+ parentList = [],
131
+ allIds = [],
132
+ key,
133
+ insertItem = function (key) {
134
+ each(parentList[key], function (item) {
135
+ parentList[""].push(item);
136
+ });
137
+ },
138
+ getNodeTree = function (id, i, level, list, points, parent) {
139
+ var children = [],
140
+ sortedChildren = [],
141
+ childrenTotal = 0,
142
+ val,
143
+ point = points[i],
144
+ nodeTree,
145
+ node,
146
+ insertNode,
147
+ name;
148
+ insertNode = function () {
149
+ var i = 0,
150
+ inserted = false;
151
+ if (sortedChildren.length !== 0) {
152
+ each(sortedChildren, function (child) {
153
+ if (node.val > child.val && !inserted) {
154
+ sortedChildren.splice(i, 0, node);
155
+ inserted = true;
156
+ }
157
+ i = i + 1;
158
+ });
159
+ }
160
+ if (!inserted) {
161
+ sortedChildren.push(node);
162
+ }
163
+ };
164
+
165
+ // Actions
166
+ if (point) {
167
+ name = point.name || "";
168
+ }
169
+ if (list[id] !== undefined) {
170
+ each(list[id], function (i) {
171
+ node = getNodeTree(points[i].id, i, (level + 1), list, points, id);
172
+ childrenTotal += node.val;
173
+ insertNode();
174
+ children.push(node);
175
+ });
176
+ }
177
+ val = pick((points[i] && points[i].value), childrenTotal, 0);
178
+ nodeTree = {
179
+ id: id,
180
+ i: i,
181
+ children: sortedChildren,
182
+ childrenTotal: childrenTotal,
183
+ val: val,
184
+ level: level,
185
+ parent: parent,
186
+ name: name
187
+ };
188
+ series.nodeMap[nodeTree.id] = nodeTree;
189
+ return nodeTree;
190
+ };
191
+ // Actions
192
+ // Map children to index
193
+ each(this.points, function (point) {
194
+ var parent = "";
195
+ allIds.push(point.id);
196
+ if (point.parent !== undefined) {
197
+ parent = point.parent;
198
+ }
199
+ if (parentList[parent] === undefined) {
200
+ parentList[parent] = [];
201
+ }
202
+ parentList[parent].push(i);
203
+ i = i + 1;
204
+ });
205
+ /*
206
+ * Quality check:
207
+ * - If parent does not exist, then set parent to tree root
208
+ * - Add node id to parents children list
209
+ */
210
+ for (key in parentList) {
211
+ if (parentList.hasOwnProperty(key)) {
212
+ if (key !== "") {
213
+ if (HighchartsAdapter.inArray(key, allIds) === -1) {
214
+ insertItem(key);
215
+ delete parentList[key];
216
+ }
217
+ }
218
+ }
219
+ }
220
+ tree = getNodeTree("", -1, 0, parentList, this.points, null);
221
+ return tree;
222
+ },
223
+ calculateArea: function (node, area) {
224
+ var childrenValues = [],
225
+ childValues,
226
+ series = this,
227
+ options = series.options,
228
+ algorithm = options.layoutAlgorithm,
229
+ alternate = options.alternateStartingDirection,
230
+ levelRoot = this.nodeMap[this.rootNode].level,
231
+ i = 0,
232
+ level,
233
+ levelNr = options.levelIsConstant ? node.level : (node.level - levelRoot),
234
+ point;
235
+ node.isVisible = (node.id === this.rootNode) || !!(this.nodeMap[node.parent] && this.nodeMap[node.parent].isVisible);
236
+ levelNr = (levelNr > 0) ? levelNr : 0;
237
+ // If layoutAlgorithm is set for the level of the children, then default is overwritten
238
+ if (this.levelMap[levelNr + 1]) {
239
+ level = this.levelMap[levelNr + 1];
240
+ if (level.layoutAlgorithm && series[level.layoutAlgorithm]) {
241
+ algorithm = level.layoutAlgorithm;
242
+ }
243
+ if (level.layoutStartingDirection) {
244
+ area.direction = level.layoutStartingDirection === 'vertical' ? 0 : 1;
245
+ }
246
+ }
247
+ childrenValues = series[algorithm](area, node.children);
248
+ each(node.children, function (child) {
249
+ levelNr = options.levelIsConstant ? child.level : (child.level - levelRoot);
250
+ point = series.points[child.i];
251
+ point.level = levelNr;
252
+ childValues = childrenValues[i];
253
+ childValues.val = child.childrenTotal;
254
+ childValues.direction = area.direction;
255
+ if (alternate) {
256
+ childValues.direction = 1 - childValues.direction;
257
+ }
258
+ child.values = childValues;
259
+ child.isVisible = node.isVisible;
260
+ point.node = child;
261
+ point.value = child.val;
262
+ point.isLeaf = true;
263
+ // If node has children, then call method recursively
264
+ if (child.children.length) {
265
+ point.isLeaf = false;
266
+ series.calculateArea(child, childValues);
267
+ }
268
+ i = i + 1;
269
+ });
270
+ },
271
+ setPointValues: function () {
272
+ var series = this,
273
+ xAxis = series.xAxis,
274
+ yAxis = series.yAxis;
275
+ series.nodeMap[""].values = {
276
+ x: 0,
277
+ y: 0,
278
+ width: 100,
279
+ height: 100
280
+ };
281
+ each(series.points, function (point) {
282
+ var node = point.node,
283
+ values = node.values,
284
+ x1,
285
+ x2,
286
+ y1,
287
+ y2;
288
+ values.x = values.x / series.axisRatio;
289
+ values.width = values.width / series.axisRatio;
290
+ x1 = Math.round(xAxis.translate(values.x, 0, 0, 0, 1));
291
+ x2 = Math.round(xAxis.translate(values.x + values.width, 0, 0, 0, 1));
292
+ y1 = Math.round(yAxis.translate(values.y, 0, 0, 0, 1));
293
+ y2 = Math.round(yAxis.translate(values.y + values.height, 0, 0, 0, 1));
294
+ if (point.value > 0) {
295
+ // Set point values
296
+ point.shapeType = 'rect';
297
+ point.shapeArgs = {
298
+ x: Math.min(x1, x2),
299
+ y: Math.min(y1, y2),
300
+ width: Math.abs(x2 - x1),
301
+ height: Math.abs(y2 - y1)
302
+ };
303
+ point.plotX = point.shapeArgs.x + (point.shapeArgs.width / 2);
304
+ point.plotY = point.shapeArgs.y + (point.shapeArgs.height / 2);
305
+ }
306
+ });
307
+ },
308
+ getSeriesArea: function (val) {
309
+ var x = 0,
310
+ y = 0,
311
+ h = 100,
312
+ r = this.axisRatio = (this.xAxis.len / this.yAxis.len),
313
+ w = 100 * r,
314
+ d = this.options.layoutStartingDirection === 'vertical' ? 0 : 1,
315
+ seriesArea = {
316
+ x: x,
317
+ y: y,
318
+ width: w,
319
+ height: h,
320
+ direction: d,
321
+ val: val
322
+ };
323
+ return seriesArea;
324
+ },
325
+ getLevels: function () {
326
+ var map = [],
327
+ levels = this.options.levels;
328
+ if (levels) {
329
+ each(levels, function (level) {
330
+ if (level.level !== undefined) {
331
+ map[level.level] = level;
332
+ }
333
+ });
334
+ }
335
+ return map;
336
+ },
337
+ setColorRecursive: function (node, color) {
338
+ var series = this,
339
+ point,
340
+ level;
341
+ if (node) {
342
+ point = series.points[node.i];
343
+ level = series.levelMap[node.level];
344
+ // Select either point color, level color or inherited color.
345
+ color = pick(point && point.options.color, level && level.color, color);
346
+ if (point) {
347
+ point.color = color;
348
+ }
349
+ // Do it all again with the children
350
+ if (node.children.length) {
351
+ each(node.children, function (child) {
352
+ series.setColorRecursive(child, color);
353
+ });
354
+ }
355
+ }
356
+ },
357
+ alg_func_group: function (h, w, d, p) {
358
+ this.height = h;
359
+ this.width = w;
360
+ this.plot = p;
361
+ this.direction = d;
362
+ this.startDirection = d;
363
+ this.total = 0;
364
+ this.nW = 0;
365
+ this.lW = 0;
366
+ this.nH = 0;
367
+ this.lH = 0;
368
+ this.elArr = [];
369
+ this.lP = {
370
+ total: 0,
371
+ lH: 0,
372
+ nH: 0,
373
+ lW: 0,
374
+ nW: 0,
375
+ nR: 0,
376
+ lR: 0,
377
+ aspectRatio: function (w, h) {
378
+ return Math.max((w / h), (h / w));
379
+ }
380
+ };
381
+ this.addElement = function (el) {
382
+ this.lP.total = this.elArr[this.elArr.length - 1];
383
+ this.total = this.total + el;
384
+ if (this.direction === 0) {
385
+ // Calculate last point old aspect ratio
386
+ this.lW = this.nW;
387
+ this.lP.lH = this.lP.total / this.lW;
388
+ this.lP.lR = this.lP.aspectRatio(this.lW, this.lP.lH);
389
+ // Calculate last point new aspect ratio
390
+ this.nW = this.total / this.height;
391
+ this.lP.nH = this.lP.total / this.nW;
392
+ this.lP.nR = this.lP.aspectRatio(this.nW, this.lP.nH);
393
+ } else {
394
+ // Calculate last point old aspect ratio
395
+ this.lH = this.nH;
396
+ this.lP.lW = this.lP.total / this.lH;
397
+ this.lP.lR = this.lP.aspectRatio(this.lP.lW, this.lH);
398
+ // Calculate last point new aspect ratio
399
+ this.nH = this.total / this.width;
400
+ this.lP.nW = this.lP.total / this.nH;
401
+ this.lP.nR = this.lP.aspectRatio(this.lP.nW, this.nH);
402
+ }
403
+ this.elArr.push(el);
404
+ };
405
+ this.reset = function () {
406
+ this.nW = 0;
407
+ this.lW = 0;
408
+ this.elArr = [];
409
+ this.total = 0;
410
+ };
411
+ },
412
+ alg_func_calcPoints: function (directionChange, last, group, childrenArea) {
413
+ var pX,
414
+ pY,
415
+ pW,
416
+ pH,
417
+ gW = group.lW,
418
+ gH = group.lH,
419
+ plot = group.plot,
420
+ keep,
421
+ i = 0,
422
+ end = group.elArr.length - 1;
423
+ if (last) {
424
+ gW = group.nW;
425
+ gH = group.nH;
426
+ } else {
427
+ keep = group.elArr[group.elArr.length - 1];
428
+ }
429
+ each(group.elArr, function (p) {
430
+ if (last || (i < end)) {
431
+ if (group.direction === 0) {
432
+ pX = plot.x;
433
+ pY = plot.y;
434
+ pW = gW;
435
+ pH = p / pW;
436
+ } else {
437
+ pX = plot.x;
438
+ pY = plot.y;
439
+ pH = gH;
440
+ pW = p / pH;
441
+ }
442
+ childrenArea.push({
443
+ x: pX,
444
+ y: pY,
445
+ width: pW,
446
+ height: pH
447
+ });
448
+ if (group.direction === 0) {
449
+ plot.y = plot.y + pH;
450
+ } else {
451
+ plot.x = plot.x + pW;
452
+ }
453
+ }
454
+ i = i + 1;
455
+ });
456
+ // Reset variables
457
+ group.reset();
458
+ if (group.direction === 0) {
459
+ group.width = group.width - gW;
460
+ } else {
461
+ group.height = group.height - gH;
462
+ }
463
+ plot.y = plot.parent.y + (plot.parent.height - group.height);
464
+ plot.x = plot.parent.x + (plot.parent.width - group.width);
465
+ if (directionChange) {
466
+ group.direction = 1 - group.direction;
467
+ }
468
+ // If not last, then add uncalculated element
469
+ if (!last) {
470
+ group.addElement(keep);
471
+ }
472
+ },
473
+ alg_func_lowAspectRatio: function (directionChange, parent, children) {
474
+ var childrenArea = [],
475
+ series = this,
476
+ pTot,
477
+ plot = {
478
+ x: parent.x,
479
+ y: parent.y,
480
+ parent: parent
481
+ },
482
+ direction = parent.direction,
483
+ i = 0,
484
+ end = children.length - 1,
485
+ group = new this.alg_func_group(parent.height, parent.width, direction, plot);
486
+ // Loop through and calculate all areas
487
+ each(children, function (child) {
488
+ pTot = (parent.width * parent.height) * (child.val / parent.val);
489
+ group.addElement(pTot);
490
+ if (group.lP.nR > group.lP.lR) {
491
+ series.alg_func_calcPoints(directionChange, false, group, childrenArea, plot);
492
+ }
493
+ // If last child, then calculate all remaining areas
494
+ if (i === end) {
495
+ series.alg_func_calcPoints(directionChange, true, group, childrenArea, plot);
496
+ }
497
+ i = i + 1;
498
+ });
499
+ return childrenArea;
500
+ },
501
+ alg_func_fill: function (directionChange, parent, children) {
502
+ var childrenArea = [],
503
+ pTot,
504
+ direction = parent.direction,
505
+ x = parent.x,
506
+ y = parent.y,
507
+ width = parent.width,
508
+ height = parent.height,
509
+ pX,
510
+ pY,
511
+ pW,
512
+ pH;
513
+ each(children, function (child) {
514
+ pTot = (parent.width * parent.height) * (child.val / parent.val);
515
+ pX = x;
516
+ pY = y;
517
+ if (direction === 0) {
518
+ pH = height;
519
+ pW = pTot / pH;
520
+ width = width - pW;
521
+ x = x + pW;
522
+ } else {
523
+ pW = width;
524
+ pH = pTot / pW;
525
+ height = height - pH;
526
+ y = y + pH;
527
+ }
528
+ childrenArea.push({
529
+ x: pX,
530
+ y: pY,
531
+ width: pW,
532
+ height: pH
533
+ });
534
+ if (directionChange) {
535
+ direction = 1 - direction;
536
+ }
537
+ });
538
+ return childrenArea;
539
+ },
540
+ strip: function (parent, children) {
541
+ return this.alg_func_lowAspectRatio(false, parent, children);
542
+ },
543
+ squarified: function (parent, children) {
544
+ return this.alg_func_lowAspectRatio(true, parent, children);
545
+ },
546
+ sliceAndDice: function (parent, children) {
547
+ return this.alg_func_fill(true, parent, children);
548
+ },
549
+ stripes: function (parent, children) {
550
+ return this.alg_func_fill(false, parent, children);
551
+ },
552
+ translate: function () {
553
+ // Call prototype function
554
+ Series.prototype.translate.call(this);
555
+ this.handleLayout();
556
+
557
+ // If a colorAxis is defined
558
+ if (this.colorAxis) {
559
+ this.translateColors();
560
+ } else {
561
+ this.setColorRecursive(this.tree, undefined);
562
+ }
563
+ },
564
+ /**
565
+ * Extend drawDataLabels with logic to handle the levels option
566
+ */
567
+ drawDataLabels: function () {
568
+ var series = this,
569
+ points = series.points,
570
+ options,
571
+ level,
572
+ dataLabelsGroup = this.dataLabelsGroup,
573
+ dataLabels;
574
+ each(points, function (point) {
575
+ if (point.node.isVisible) {
576
+ level = series.levelMap[point.level];
577
+ if (!point.isLeaf || level) {
578
+ options = undefined;
579
+ // If not a leaf, then label should be disabled as default
580
+ if (!point.isLeaf) {
581
+ options = {enabled: false};
582
+ }
583
+ if (level) {
584
+ dataLabels = level.dataLabels;
585
+ if (dataLabels) {
586
+ options = merge(options, dataLabels);
587
+ series._hasPointLabels = true;
588
+ }
589
+ }
590
+ options = merge(options, point.options.dataLabels);
591
+ point.dlOptions = options;
592
+ } else {
593
+ delete point.dlOptions;
594
+ }
595
+ }
596
+ });
597
+ this.dataLabelsGroup = this.group;
598
+ Series.prototype.drawDataLabels.call(this);
599
+ this.dataLabelsGroup = dataLabelsGroup;
600
+ },
601
+ alignDataLabel: seriesTypes.column.prototype.alignDataLabel,
602
+ /**
603
+ * Extending ColumnSeries drawPoints
604
+ */
605
+ drawPoints: function () {
606
+ var series = this,
607
+ points = series.points,
608
+ seriesOptions = series.options,
609
+ attr,
610
+ hover,
611
+ level;
612
+ each(points, function (point) {
613
+ if (point.node.isVisible) {
614
+ level = series.levelMap[point.level];
615
+ attr = {
616
+ stroke: seriesOptions.borderColor,
617
+ 'stroke-width': seriesOptions.borderWidth,
618
+ dashstyle: seriesOptions.borderDashStyle,
619
+ r: 0, // borderRadius gives wrong size relations and should always be disabled
620
+ fill: pick(point.color, series.color)
621
+ };
622
+ // Overwrite standard series options with level options
623
+ if (level) {
624
+ attr.stroke = level.borderColor || attr.stroke;
625
+ attr['stroke-width'] = level.borderWidth || attr['stroke-width'];
626
+ attr.dashstyle = level.borderDashStyle || attr.dashstyle;
627
+ }
628
+ // Merge with point attributes
629
+ attr.stroke = point.borderColor || attr.stroke;
630
+ attr['stroke-width'] = point.borderWidth || attr['stroke-width'];
631
+ attr.dashstyle = point.borderDashStyle || attr.dashstyle;
632
+ attr.zIndex = (1000 - (point.level * 2));
633
+
634
+ // Make a copy to prevent overwriting individual props
635
+ point.pointAttr = merge(point.pointAttr);
636
+ hover = point.pointAttr.hover;
637
+ hover.zIndex = 1001;
638
+ hover.fill = Color(attr.fill).brighten(seriesOptions.states.hover.brightness).get();
639
+ // If not a leaf, then remove fill
640
+ if (!point.isLeaf) {
641
+ if (seriesOptions.allowDrillToNode) {
642
+ // TODO: let users set the opacity
643
+ attr.fill = Color(attr.fill).setOpacity(0.15).get();
644
+ hover.fill = Color(hover.fill).setOpacity(0.75).get();
645
+ } else {
646
+ attr.fill = 'none';
647
+ delete hover.fill;
648
+ }
649
+ }
650
+ if (point.node.level <= series.nodeMap[series.rootNode].level) {
651
+ attr.fill = 'none';
652
+ attr.zIndex = 0;
653
+ delete hover.fill;
654
+ }
655
+ point.pointAttr[''] = H.extend(point.pointAttr[''], attr);
656
+ if (point.dataLabel) {
657
+ point.dataLabel.attr({ zIndex: (point.pointAttr[''].zIndex + 1) });
658
+ }
659
+ }
660
+ });
661
+ // Call standard drawPoints
662
+ seriesTypes.column.prototype.drawPoints.call(this);
663
+
664
+ each(points, function (point) {
665
+ if (point.graphic) {
666
+ point.graphic.attr(point.pointAttr['']);
667
+ }
668
+ });
669
+
670
+ // Set click events on points
671
+ if (seriesOptions.allowDrillToNode) {
672
+ series.drillCloser();
673
+ }
674
+ },
675
+ /**
676
+ * Add drilling on the suitable points
677
+ * TODO: review and better naming
678
+ */
679
+ drillCloser: function () {
680
+ var series = this,
681
+ points = series.points,
682
+ nodeParent;
683
+ each(points, function (point) {
684
+ var nodeParentName;
685
+ if (point.node.isVisible) {
686
+ H.removeEvent(point, 'click');
687
+ if (point.graphic) {
688
+ point.graphic.css({ cursor: 'default' });
689
+ }
690
+ if ((point.node.level - series.nodeMap[series.rootNode].level) === 1 && !point.isLeaf) {
691
+ nodeParent = series.nodeMap[series.nodeMap[point.id].parent];
692
+ nodeParentName = nodeParent.name || nodeParent.id;
693
+ if (point.graphic) {
694
+ point.graphic.css({ cursor: 'pointer' });
695
+ }
696
+ H.addEvent(point, 'click', function () {
697
+ // Remove hover
698
+ point.setState('');
699
+ series.drillToNode(point.id);
700
+ series.showDrillUpButton(nodeParentName);
701
+ });
702
+ }
703
+ }
704
+ });
705
+ },
706
+ drillUp: function () {
707
+ var drillPoint = null,
708
+ node,
709
+ parent;
710
+ if (this.rootNode) {
711
+ node = this.nodeMap[this.rootNode];
712
+ if (node.parent !== null) {
713
+ drillPoint = this.nodeMap[node.parent];
714
+ } else {
715
+ drillPoint = this.nodeMap[""];
716
+ }
717
+ }
718
+
719
+ if (drillPoint !== null) {
720
+ this.drillToNode(drillPoint.id);
721
+ if (drillPoint.id === "") {
722
+ this.drillUpButton = this.drillUpButton.destroy();
723
+ } else {
724
+ parent = this.nodeMap[drillPoint.parent];
725
+ this.showDrillUpButton((parent.name || parent.id));
726
+ }
727
+ }
728
+ },
729
+ drillToNode: function (id) {
730
+ var node = this.nodeMap[id],
731
+ val = node.values;
732
+ this.rootNode = id;
733
+ this.xAxis.setExtremes(val.x, val.x + val.width, false);
734
+ this.yAxis.setExtremes(val.y, val.y + val.height, false);
735
+ this.chart.redraw();
736
+ },
737
+ showDrillUpButton: function (name) {
738
+ var series = this,
739
+ backText = (name || '< Back'),
740
+ buttonOptions = series.options.drillUpButton,
741
+ attr,
742
+ states;
743
+
744
+ if (buttonOptions.text) {
745
+ backText = buttonOptions.text;
746
+ }
747
+ if (!this.drillUpButton) {
748
+ attr = buttonOptions.theme;
749
+ states = attr && attr.states;
750
+
751
+ this.drillUpButton = this.chart.renderer.button(
752
+ backText,
753
+ null,
754
+ null,
755
+ function () {
756
+ series.drillUp();
757
+ },
758
+ attr,
759
+ states && states.hover,
760
+ states && states.select
761
+ )
762
+ .attr({
763
+ align: buttonOptions.position.align,
764
+ zIndex: 9
765
+ })
766
+ .add()
767
+ .align(buttonOptions.position, false, buttonOptions.relativeTo || 'plotBox');
768
+ } else {
769
+ this.drillUpButton.attr({
770
+ text: backText
771
+ })
772
+ .align();
773
+ }
774
+ },
775
+ buildKDTree: noop,
776
+ drawLegendSymbol: H.LegendSymbolMixin.drawRectangle,
777
+ getExtremes: function () {
778
+ // Get the extremes from the value data
779
+ Series.prototype.getExtremes.call(this, this.colorValueData);
780
+ this.valueMin = this.dataMin;
781
+ this.valueMax = this.dataMax;
782
+
783
+ // Get the extremes from the y data
784
+ Series.prototype.getExtremes.call(this);
785
+ },
786
+ bindAxes: function () {
787
+ var treeAxis = {
788
+ endOnTick: false,
789
+ gridLineWidth: 0,
790
+ lineWidth: 0,
791
+ min: 0,
792
+ dataMin: 0,
793
+ minPadding: 0,
794
+ max: 100,
795
+ dataMax: 100,
796
+ maxPadding: 0,
797
+ startOnTick: false,
798
+ title: null,
799
+ tickPositions: []
800
+ };
801
+ Series.prototype.bindAxes.call(this);
802
+ H.extend(this.yAxis.options, treeAxis);
803
+ H.extend(this.xAxis.options, treeAxis);
804
+ }
805
+ }));
806
+ }(Highcharts));