highcharts-rails 4.0.4.1 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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));