highcharts-rails 4.1.9 → 4.1.10
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 +55 -0
- data/app/assets/images/highcharts/search.png +0 -0
- data/app/assets/javascripts/highcharts.js +18962 -18796
- data/app/assets/javascripts/highcharts/adapters/standalone-framework.js +152 -131
- data/app/assets/javascripts/highcharts/highcharts-3d.js +1702 -1561
- data/app/assets/javascripts/highcharts/highcharts-more.js +2655 -2639
- data/app/assets/javascripts/highcharts/modules/boost.js +72 -35
- data/app/assets/javascripts/highcharts/modules/broken-axis.js +45 -32
- data/app/assets/javascripts/highcharts/modules/canvas-tools.js +8 -7
- data/app/assets/javascripts/highcharts/modules/data.js +646 -642
- data/app/assets/javascripts/highcharts/modules/drilldown.js +15 -11
- data/app/assets/javascripts/highcharts/modules/exporting.js +17 -12
- data/app/assets/javascripts/highcharts/modules/funnel.js +19 -12
- data/app/assets/javascripts/highcharts/modules/heatmap.js +702 -696
- data/app/assets/javascripts/highcharts/modules/no-data-to-display.js +10 -4
- data/app/assets/javascripts/highcharts/modules/offline-exporting.js +241 -238
- data/app/assets/javascripts/highcharts/modules/solid-gauge.js +12 -9
- data/app/assets/javascripts/highcharts/modules/treemap.js +67 -86
- data/app/assets/javascripts/highcharts/themes/grid-light.js +1 -1
- data/app/assets/javascripts/highcharts/themes/sand-signika.js +1 -1
- data/lib/highcharts/version.rb +1 -1
- metadata +3 -2
@@ -42,35 +42,42 @@
|
|
42
42
|
* number of points drawn gets higher, and you may want to set the threshold lower in order to
|
43
43
|
* use optimizations.
|
44
44
|
*/
|
45
|
-
|
46
|
-
|
45
|
+
|
46
|
+
/* eslint indent: [2, 4] */
|
47
|
+
(function (factory) {
|
48
|
+
if (typeof module === 'object' && module.exports) {
|
49
|
+
module.exports = factory;
|
50
|
+
} else {
|
51
|
+
factory(Highcharts);
|
52
|
+
}
|
53
|
+
}(function (H) {
|
47
54
|
|
48
55
|
'use strict';
|
49
56
|
|
50
|
-
var noop = function () {
|
57
|
+
var noop = function () {},
|
51
58
|
Color = H.Color,
|
52
59
|
Series = H.Series,
|
53
60
|
seriesTypes = H.seriesTypes,
|
54
61
|
each = H.each,
|
55
62
|
extend = H.extend,
|
56
|
-
addEvent =
|
57
|
-
fireEvent =
|
63
|
+
addEvent = H.addEvent,
|
64
|
+
fireEvent = H.fireEvent,
|
58
65
|
merge = H.merge,
|
59
66
|
pick = H.pick,
|
60
67
|
wrap = H.wrap,
|
61
68
|
plotOptions = H.getOptions().plotOptions,
|
62
69
|
CHUNK_SIZE = 50000;
|
63
70
|
|
64
|
-
function eachAsync(arr, fn,
|
71
|
+
function eachAsync(arr, fn, finalFunc, chunkSize, i) {
|
65
72
|
i = i || 0;
|
66
73
|
chunkSize = chunkSize || CHUNK_SIZE;
|
67
74
|
each(arr.slice(i, i + chunkSize), fn);
|
68
75
|
if (i + chunkSize < arr.length) {
|
69
76
|
setTimeout(function () {
|
70
|
-
eachAsync(arr, fn,
|
77
|
+
eachAsync(arr, fn, finalFunc, chunkSize, i + chunkSize);
|
71
78
|
});
|
72
|
-
} else if (
|
73
|
-
|
79
|
+
} else if (finalFunc) {
|
80
|
+
finalFunc();
|
74
81
|
}
|
75
82
|
}
|
76
83
|
|
@@ -161,14 +168,16 @@
|
|
161
168
|
point,
|
162
169
|
i;
|
163
170
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
point
|
171
|
+
if (points) {
|
172
|
+
for (i = 0; i < points.length; i = i + 1) {
|
173
|
+
point = points[i];
|
174
|
+
if (point && point.graphic) {
|
175
|
+
point.graphic = point.graphic.destroy();
|
176
|
+
}
|
168
177
|
}
|
169
178
|
}
|
170
179
|
|
171
|
-
each(['graph', 'area'], function (prop) {
|
180
|
+
each(['graph', 'area', 'tracker'], function (prop) {
|
172
181
|
if (series[prop]) {
|
173
182
|
series[prop] = series[prop].destroy();
|
174
183
|
}
|
@@ -180,15 +189,25 @@
|
|
180
189
|
* to an SVG image element.
|
181
190
|
*/
|
182
191
|
getContext: function () {
|
183
|
-
var
|
184
|
-
|
192
|
+
var chart = this.chart,
|
193
|
+
width = chart.plotWidth,
|
194
|
+
height = chart.plotHeight,
|
195
|
+
ctx = this.ctx,
|
196
|
+
swapXY = function (proceed, x, y, a, b, c, d) {
|
197
|
+
proceed.call(this, y, x, a, b, c, d);
|
198
|
+
};
|
185
199
|
|
186
200
|
if (!this.canvas) {
|
187
201
|
this.canvas = document.createElement('canvas');
|
188
|
-
this.image =
|
189
|
-
this.ctx = this.canvas.getContext('2d');
|
202
|
+
this.image = chart.renderer.image('', 0, 0, width, height).add(this.group);
|
203
|
+
this.ctx = ctx = this.canvas.getContext('2d');
|
204
|
+
if (chart.inverted) {
|
205
|
+
each(['moveTo', 'lineTo', 'rect', 'arc'], function (fn) {
|
206
|
+
wrap(ctx, fn, swapXY);
|
207
|
+
});
|
208
|
+
}
|
190
209
|
} else {
|
191
|
-
|
210
|
+
ctx.clearRect(0, 0, width, height);
|
192
211
|
}
|
193
212
|
|
194
213
|
this.canvas.setAttribute('width', width);
|
@@ -198,7 +217,7 @@
|
|
198
217
|
height: height
|
199
218
|
});
|
200
219
|
|
201
|
-
return
|
220
|
+
return ctx;
|
202
221
|
},
|
203
222
|
|
204
223
|
/**
|
@@ -308,18 +327,24 @@
|
|
308
327
|
// The k-d tree requires series points. Reduce the amount of points, since the time to build the
|
309
328
|
// tree increases exponentially.
|
310
329
|
if (enableMouseTracking && !pointTaken[clientX + ',' + plotY]) {
|
330
|
+
pointTaken[clientX + ',' + plotY] = true;
|
331
|
+
|
332
|
+
if (chart.inverted) {
|
333
|
+
clientX = xAxis.len - clientX;
|
334
|
+
plotY = yAxis.len - plotY;
|
335
|
+
}
|
336
|
+
|
311
337
|
points.push({
|
312
338
|
clientX: clientX,
|
313
339
|
plotX: clientX,
|
314
340
|
plotY: plotY,
|
315
341
|
i: cropStart + i
|
316
342
|
});
|
317
|
-
pointTaken[clientX + ',' + plotY] = true;
|
318
343
|
}
|
319
344
|
};
|
320
345
|
|
321
346
|
// If we are zooming out from SVG mode, destroy the graphics
|
322
|
-
if (this.points) {
|
347
|
+
if (this.points || this.graph) {
|
323
348
|
this.destroyGraphics();
|
324
349
|
}
|
325
350
|
|
@@ -488,7 +513,7 @@
|
|
488
513
|
}
|
489
514
|
|
490
515
|
// Pass tests in Pointer.
|
491
|
-
//
|
516
|
+
// Replace this with a single property, and replace when zooming in
|
492
517
|
// below boostThreshold.
|
493
518
|
series.directTouch = false;
|
494
519
|
series.options.stickyTracking = true;
|
@@ -535,20 +560,32 @@
|
|
535
560
|
sampling: true
|
536
561
|
});
|
537
562
|
|
563
|
+
/**
|
564
|
+
* Return a full Point object based on the index. The boost module uses stripped point objects
|
565
|
+
* for performance reasons.
|
566
|
+
* @param {Number} boostPoint A stripped-down point object
|
567
|
+
* @returns {Object} A Point object as per http://api.highcharts.com/highcharts#Point
|
568
|
+
*/
|
569
|
+
Series.prototype.getPoint = function (boostPoint) {
|
570
|
+
var point = boostPoint;
|
571
|
+
|
572
|
+
if (boostPoint && !(boostPoint instanceof this.pointClass)) {
|
573
|
+
point = (new this.pointClass()).init(this, this.options.data[boostPoint.i]);
|
574
|
+
point.dist = boostPoint.dist;
|
575
|
+
point.category = point.x;
|
576
|
+
point.plotX = boostPoint.plotX;
|
577
|
+
point.plotY = boostPoint.plotY;
|
578
|
+
}
|
579
|
+
|
580
|
+
return point;
|
581
|
+
};
|
582
|
+
|
538
583
|
/**
|
539
584
|
* Return a point instance from the k-d-tree
|
540
585
|
*/
|
541
586
|
wrap(Series.prototype, 'searchPoint', function (proceed) {
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
if (point && !(point instanceof this.pointClass)) {
|
546
|
-
ret = (new this.pointClass()).init(this, this.options.data[point.i]);
|
547
|
-
ret.dist = point.dist;
|
548
|
-
ret.category = ret.x;
|
549
|
-
ret.plotX = point.plotX;
|
550
|
-
ret.plotY = point.plotY;
|
551
|
-
}
|
552
|
-
return ret;
|
587
|
+
return this.getPoint(
|
588
|
+
proceed.apply(this, [].slice.call(arguments, 1))
|
589
|
+
);
|
553
590
|
});
|
554
|
-
}
|
591
|
+
}));
|
@@ -1,20 +1,25 @@
|
|
1
1
|
/**
|
2
|
-
* Highcharts JS v4.1.
|
2
|
+
* Highcharts JS v4.1.10 (2015-12-07)
|
3
3
|
* Highcharts Broken Axis module
|
4
4
|
*
|
5
|
-
* Author: Stephane Vanraes, Torstein Honsi
|
6
5
|
* License: www.highcharts.com/license
|
7
6
|
*/
|
8
7
|
|
9
|
-
|
10
|
-
(
|
8
|
+
(function (factory) {
|
9
|
+
if (typeof module === 'object' && module.exports) {
|
10
|
+
module.exports = factory;
|
11
|
+
} else {
|
12
|
+
factory(Highcharts);
|
13
|
+
}
|
14
|
+
}(function (H) {
|
11
15
|
|
12
|
-
|
16
|
+
'use strict';
|
13
17
|
|
14
18
|
var pick = H.pick,
|
15
19
|
wrap = H.wrap,
|
20
|
+
each = H.each,
|
16
21
|
extend = H.extend,
|
17
|
-
fireEvent =
|
22
|
+
fireEvent = H.fireEvent,
|
18
23
|
Axis = H.Axis,
|
19
24
|
Series = H.Series;
|
20
25
|
|
@@ -207,11 +212,13 @@
|
|
207
212
|
}
|
208
213
|
|
209
214
|
breakArrayT.sort(function (a, b) {
|
215
|
+
var ret;
|
210
216
|
if (a.value === b.value) {
|
211
|
-
|
217
|
+
ret = (a.move === 'in' ? 0 : 1) - (b.move === 'in' ? 0 : 1);
|
212
218
|
} else {
|
213
|
-
|
219
|
+
ret = a.value - b.value;
|
214
220
|
}
|
221
|
+
return ret;
|
215
222
|
});
|
216
223
|
|
217
224
|
// Simplify the breaks
|
@@ -277,38 +284,44 @@
|
|
277
284
|
|
278
285
|
});
|
279
286
|
|
280
|
-
|
287
|
+
function drawPointsWrapped(proceed) {
|
281
288
|
proceed.apply(this);
|
289
|
+
this.drawBreaks();
|
290
|
+
}
|
282
291
|
|
292
|
+
H.Series.prototype.drawBreaks = function () {
|
283
293
|
var series = this,
|
284
294
|
points = series.points,
|
285
|
-
|
286
|
-
breaks
|
287
|
-
threshold
|
295
|
+
axis,
|
296
|
+
breaks,
|
297
|
+
threshold,
|
298
|
+
axisName = 'Axis',
|
288
299
|
eventName,
|
289
|
-
point,
|
290
|
-
brk,
|
291
|
-
i,
|
292
|
-
j,
|
293
300
|
y;
|
294
301
|
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
302
|
+
each(['y', 'x'], function (key) {
|
303
|
+
axis = series[key + axisName];
|
304
|
+
breaks = axis.breakArray || [];
|
305
|
+
threshold = axis.isXAxis ? axis.min : pick(series.options.threshold, axis.min);
|
306
|
+
each(points, function (point) {
|
307
|
+
y = pick(point['stack' + key.toUpperCase()], point[key]);
|
308
|
+
each(breaks, function (brk) {
|
309
|
+
eventName = false;
|
301
310
|
|
302
|
-
|
311
|
+
if ((threshold < brk.from && y > brk.to) || (threshold > brk.from && y < brk.from)) {
|
303
312
|
eventName = 'pointBreak';
|
304
|
-
|
313
|
+
} else if ((threshold < brk.from && y > brk.from && y < brk.to) || (threshold > brk.from && y > brk.to && y < brk.from)) { // point falls inside the break
|
305
314
|
eventName = 'pointInBreak'; // docs
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
315
|
+
}
|
316
|
+
if (eventName) {
|
317
|
+
fireEvent(axis, eventName, { point: point, brk: brk });
|
318
|
+
}
|
319
|
+
});
|
320
|
+
});
|
321
|
+
});
|
322
|
+
};
|
312
323
|
|
313
|
-
|
314
|
-
|
324
|
+
wrap(H.seriesTypes.column.prototype, 'drawPoints', drawPointsWrapped);
|
325
|
+
wrap(H.Series.prototype, 'drawPoints', drawPointsWrapped);
|
326
|
+
|
327
|
+
}));
|
@@ -2908,7 +2908,7 @@ if (CanvasRenderingContext2D) {
|
|
2908
2908
|
});
|
2909
2909
|
}
|
2910
2910
|
}/**
|
2911
|
-
* @license Highcharts JS v4.1.
|
2911
|
+
* @license Highcharts JS v4.1.10 (2015-12-07)
|
2912
2912
|
* CanVGRenderer Extension module
|
2913
2913
|
*
|
2914
2914
|
* (c) 2011-2012 Torstein Honsi, Erik Olsson
|
@@ -2916,10 +2916,7 @@ if (CanvasRenderingContext2D) {
|
|
2916
2916
|
* License: www.highcharts.com/license
|
2917
2917
|
*/
|
2918
2918
|
|
2919
|
-
|
2920
|
-
/*global Highcharts */
|
2921
|
-
|
2922
|
-
(function (Highcharts) { // encapsulate
|
2919
|
+
(function (Highcharts) {
|
2923
2920
|
var UNDEFINED,
|
2924
2921
|
DIV = 'div',
|
2925
2922
|
ABSOLUTE = 'absolute',
|
@@ -2955,7 +2952,7 @@ if (CanvasRenderingContext2D) {
|
|
2955
2952
|
canvas,
|
2956
2953
|
initialHiddenStyle = { visibility: HIDDEN, position: ABSOLUTE };
|
2957
2954
|
|
2958
|
-
this.init
|
2955
|
+
this.init(container, chartWidth, chartHeight);
|
2959
2956
|
|
2960
2957
|
// add the canvas above it
|
2961
2958
|
canvas = createElement('canvas', {
|
@@ -3033,7 +3030,11 @@ if (CanvasRenderingContext2D) {
|
|
3033
3030
|
tooltipDiv.innerHTML = args.text;
|
3034
3031
|
|
3035
3032
|
// Compute the best position for the tooltip based on the divs size and container size.
|
3036
|
-
position = chart.tooltip.getPosition(
|
3033
|
+
position = chart.tooltip.getPosition(
|
3034
|
+
tooltipDiv.offsetWidth,
|
3035
|
+
tooltipDiv.offsetHeight,
|
3036
|
+
{ plotX: args.x, plotY: args.y }
|
3037
|
+
);
|
3037
3038
|
|
3038
3039
|
css(tooltipDiv, {
|
3039
3040
|
visibility: VISIBLE,
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @license Highcharts JS v4.1.
|
2
|
+
* @license Highcharts JS v4.1.10 (2015-12-07)
|
3
3
|
* Data module
|
4
4
|
*
|
5
5
|
* (c) 2012-2014 Torstein Honsi
|
@@ -7,15 +7,19 @@
|
|
7
7
|
* License: www.highcharts.com/license
|
8
8
|
*/
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
/*global jQuery */
|
11
|
+
(function (factory) {
|
12
|
+
if (typeof module === 'object' && module.exports) {
|
13
|
+
module.exports = factory;
|
14
|
+
} else {
|
15
|
+
factory(Highcharts);
|
16
|
+
}
|
17
|
+
}(function (Highcharts) {
|
14
18
|
|
15
19
|
// Utilities
|
16
20
|
var each = Highcharts.each,
|
17
21
|
pick = Highcharts.pick,
|
18
|
-
inArray =
|
22
|
+
inArray = Highcharts.inArray,
|
19
23
|
splat = Highcharts.splat,
|
20
24
|
SeriesBuilder;
|
21
25
|
|
@@ -28,740 +32,740 @@
|
|
28
32
|
// Set the prototype properties
|
29
33
|
Highcharts.extend(Data.prototype, {
|
30
34
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
35
|
+
/**
|
36
|
+
* Initialize the Data object with the given options
|
37
|
+
*/
|
38
|
+
init: function (options, chartOptions) {
|
39
|
+
this.options = options;
|
40
|
+
this.chartOptions = chartOptions;
|
41
|
+
this.columns = options.columns || this.rowsToColumns(options.rows) || [];
|
42
|
+
this.firstRowAsNames = pick(options.firstRowAsNames, true);
|
43
|
+
this.decimalRegex = options.decimalPoint && new RegExp('^(-?[0-9]+)' + options.decimalPoint + '([0-9]+)$');
|
44
|
+
|
45
|
+
// This is a two-dimensional array holding the raw, trimmed string values
|
46
|
+
// with the same organisation as the columns array. It makes it possible
|
47
|
+
// for example to revert from interpreted timestamps to string-based
|
48
|
+
// categories.
|
49
|
+
this.rawColumns = [];
|
50
|
+
|
51
|
+
// No need to parse or interpret anything
|
52
|
+
if (this.columns.length) {
|
53
|
+
this.dataFound();
|
54
|
+
|
55
|
+
// Parse and interpret
|
56
|
+
} else {
|
53
57
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
58
|
+
// Parse a CSV string if options.csv is given
|
59
|
+
this.parseCSV();
|
60
|
+
|
61
|
+
// Parse a HTML table if options.table is given
|
62
|
+
this.parseTable();
|
59
63
|
|
60
|
-
|
61
|
-
|
62
|
-
|
64
|
+
// Parse a Google Spreadsheet
|
65
|
+
this.parseGoogleSpreadsheet();
|
66
|
+
}
|
63
67
|
|
64
|
-
|
68
|
+
},
|
65
69
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
70
|
+
/**
|
71
|
+
* Get the column distribution. For example, a line series takes a single column for
|
72
|
+
* Y values. A range series takes two columns for low and high values respectively,
|
73
|
+
* and an OHLC series takes four columns.
|
74
|
+
*/
|
75
|
+
getColumnDistribution: function () {
|
76
|
+
var chartOptions = this.chartOptions,
|
77
|
+
options = this.options,
|
78
|
+
xColumns = [],
|
79
|
+
getValueCount = function (type) {
|
80
|
+
return (Highcharts.seriesTypes[type || 'line'].prototype.pointArrayMap || [0]).length;
|
81
|
+
},
|
82
|
+
getPointArrayMap = function (type) {
|
83
|
+
return Highcharts.seriesTypes[type || 'line'].prototype.pointArrayMap;
|
84
|
+
},
|
85
|
+
globalType = chartOptions && chartOptions.chart && chartOptions.chart.type,
|
86
|
+
individualCounts = [],
|
87
|
+
seriesBuilders = [],
|
88
|
+
seriesIndex = 0,
|
89
|
+
i;
|
90
|
+
|
91
|
+
each((chartOptions && chartOptions.series) || [], function (series) {
|
92
|
+
individualCounts.push(getValueCount(series.type || globalType));
|
93
|
+
});
|
90
94
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
+
// Collect the x-column indexes from seriesMapping
|
96
|
+
each((options && options.seriesMapping) || [], function (mapping) {
|
97
|
+
xColumns.push(mapping.x || 0);
|
98
|
+
});
|
95
99
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
+
// If there are no defined series with x-columns, use the first column as x column
|
101
|
+
if (xColumns.length === 0) {
|
102
|
+
xColumns.push(0);
|
103
|
+
}
|
100
104
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
105
|
+
// Loop all seriesMappings and constructs SeriesBuilders from
|
106
|
+
// the mapping options.
|
107
|
+
each((options && options.seriesMapping) || [], function (mapping) {
|
108
|
+
var builder = new SeriesBuilder(),
|
109
|
+
name,
|
110
|
+
numberOfValueColumnsNeeded = individualCounts[seriesIndex] || getValueCount(globalType),
|
111
|
+
seriesArr = (chartOptions && chartOptions.series) || [],
|
112
|
+
series = seriesArr[seriesIndex] || {},
|
113
|
+
pointArrayMap = getPointArrayMap(series.type || globalType) || ['y'];
|
114
|
+
|
115
|
+
// Add an x reader from the x property or from an undefined column
|
116
|
+
// if the property is not set. It will then be auto populated later.
|
117
|
+
builder.addColumnReader(mapping.x, 'x');
|
118
|
+
|
119
|
+
// Add all column mappings
|
120
|
+
for (name in mapping) {
|
121
|
+
if (mapping.hasOwnProperty(name) && name !== 'x') {
|
122
|
+
builder.addColumnReader(mapping[name], name);
|
123
|
+
}
|
119
124
|
}
|
120
|
-
}
|
121
125
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
126
|
+
// Add missing columns
|
127
|
+
for (i = 0; i < numberOfValueColumnsNeeded; i++) {
|
128
|
+
if (!builder.hasReader(pointArrayMap[i])) {
|
129
|
+
//builder.addNextColumnReader(pointArrayMap[i]);
|
130
|
+
// Create and add a column reader for the next free column index
|
131
|
+
builder.addColumnReader(undefined, pointArrayMap[i]);
|
132
|
+
}
|
128
133
|
}
|
129
|
-
}
|
130
134
|
|
131
|
-
|
132
|
-
|
133
|
-
|
135
|
+
seriesBuilders.push(builder);
|
136
|
+
seriesIndex++;
|
137
|
+
});
|
134
138
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
+
var globalPointArrayMap = getPointArrayMap(globalType);
|
140
|
+
if (globalPointArrayMap === undefined) {
|
141
|
+
globalPointArrayMap = ['y'];
|
142
|
+
}
|
139
143
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
144
|
+
this.valueCount = {
|
145
|
+
global: getValueCount(globalType),
|
146
|
+
xColumns: xColumns,
|
147
|
+
individual: individualCounts,
|
148
|
+
seriesBuilders: seriesBuilders,
|
149
|
+
globalPointArrayMap: globalPointArrayMap
|
150
|
+
};
|
151
|
+
},
|
148
152
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
153
|
+
/**
|
154
|
+
* When the data is parsed into columns, either by CSV, table, GS or direct input,
|
155
|
+
* continue with other operations.
|
156
|
+
*/
|
157
|
+
dataFound: function () {
|
158
|
+
|
159
|
+
if (this.options.switchRowsAndColumns) {
|
160
|
+
this.columns = this.rowsToColumns(this.columns);
|
161
|
+
}
|
158
162
|
|
159
|
-
|
160
|
-
|
163
|
+
// Interpret the info about series and columns
|
164
|
+
this.getColumnDistribution();
|
161
165
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
// Handle columns if a handleColumns callback is given
|
166
|
-
if (this.parsed() !== false) {
|
167
|
-
|
168
|
-
// Complete if a complete callback is given
|
169
|
-
this.complete();
|
170
|
-
}
|
171
|
-
|
172
|
-
},
|
173
|
-
|
174
|
-
/**
|
175
|
-
* Parse a CSV input string
|
176
|
-
*/
|
177
|
-
parseCSV: function () {
|
178
|
-
var self = this,
|
179
|
-
options = this.options,
|
180
|
-
csv = options.csv,
|
181
|
-
columns = this.columns,
|
182
|
-
startRow = options.startRow || 0,
|
183
|
-
endRow = options.endRow || Number.MAX_VALUE,
|
184
|
-
startColumn = options.startColumn || 0,
|
185
|
-
endColumn = options.endColumn || Number.MAX_VALUE,
|
186
|
-
itemDelimiter,
|
187
|
-
lines,
|
188
|
-
activeRowNo = 0;
|
166
|
+
// Interpret the values into right types
|
167
|
+
this.parseTypes();
|
189
168
|
|
190
|
-
|
169
|
+
// Handle columns if a handleColumns callback is given
|
170
|
+
if (this.parsed() !== false) {
|
191
171
|
|
192
|
-
|
193
|
-
.
|
194
|
-
|
195
|
-
.split(options.lineDelimiter || "\n");
|
196
|
-
|
197
|
-
itemDelimiter = options.itemDelimiter || (csv.indexOf('\t') !== -1 ? '\t' : ',');
|
172
|
+
// Complete if a complete callback is given
|
173
|
+
this.complete();
|
174
|
+
}
|
198
175
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
176
|
+
},
|
177
|
+
|
178
|
+
/**
|
179
|
+
* Parse a CSV input string
|
180
|
+
*/
|
181
|
+
parseCSV: function () {
|
182
|
+
var self = this,
|
183
|
+
options = this.options,
|
184
|
+
csv = options.csv,
|
185
|
+
columns = this.columns,
|
186
|
+
startRow = options.startRow || 0,
|
187
|
+
endRow = options.endRow || Number.MAX_VALUE,
|
188
|
+
startColumn = options.startColumn || 0,
|
189
|
+
endColumn = options.endColumn || Number.MAX_VALUE,
|
190
|
+
itemDelimiter,
|
191
|
+
lines,
|
192
|
+
activeRowNo = 0;
|
193
|
+
|
194
|
+
if (csv) {
|
195
|
+
|
196
|
+
lines = csv
|
197
|
+
.replace(/\r\n/g, '\n') // Unix
|
198
|
+
.replace(/\r/g, '\n') // Mac
|
199
|
+
.split(options.lineDelimiter || '\n');
|
200
|
+
|
201
|
+
itemDelimiter = options.itemDelimiter || (csv.indexOf('\t') !== -1 ? '\t' : ',');
|
204
202
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
203
|
+
each(lines, function (line, rowNo) {
|
204
|
+
var trimmed = self.trim(line),
|
205
|
+
isComment = trimmed.indexOf('#') === 0,
|
206
|
+
isBlank = trimmed === '',
|
207
|
+
items;
|
208
|
+
|
209
|
+
if (rowNo >= startRow && rowNo <= endRow && !isComment && !isBlank) {
|
210
|
+
items = line.split(itemDelimiter);
|
211
|
+
each(items, function (item, colNo) {
|
212
|
+
if (colNo >= startColumn && colNo <= endColumn) {
|
213
|
+
if (!columns[colNo - startColumn]) {
|
214
|
+
columns[colNo - startColumn] = [];
|
215
|
+
}
|
216
|
+
|
217
|
+
columns[colNo - startColumn][activeRowNo] = item;
|
211
218
|
}
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
activeRowNo += 1;
|
217
|
-
}
|
218
|
-
});
|
219
|
+
});
|
220
|
+
activeRowNo += 1;
|
221
|
+
}
|
222
|
+
});
|
219
223
|
|
220
|
-
|
221
|
-
}
|
222
|
-
},
|
223
|
-
|
224
|
-
/**
|
225
|
-
* Parse a HTML table
|
226
|
-
*/
|
227
|
-
parseTable: function () {
|
228
|
-
var options = this.options,
|
229
|
-
table = options.table,
|
230
|
-
columns = this.columns,
|
231
|
-
startRow = options.startRow || 0,
|
232
|
-
endRow = options.endRow || Number.MAX_VALUE,
|
233
|
-
startColumn = options.startColumn || 0,
|
234
|
-
endColumn = options.endColumn || Number.MAX_VALUE;
|
235
|
-
|
236
|
-
if (table) {
|
237
|
-
|
238
|
-
if (typeof table === 'string') {
|
239
|
-
table = document.getElementById(table);
|
224
|
+
this.dataFound();
|
240
225
|
}
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
226
|
+
},
|
227
|
+
|
228
|
+
/**
|
229
|
+
* Parse a HTML table
|
230
|
+
*/
|
231
|
+
parseTable: function () {
|
232
|
+
var options = this.options,
|
233
|
+
table = options.table,
|
234
|
+
columns = this.columns,
|
235
|
+
startRow = options.startRow || 0,
|
236
|
+
endRow = options.endRow || Number.MAX_VALUE,
|
237
|
+
startColumn = options.startColumn || 0,
|
238
|
+
endColumn = options.endColumn || Number.MAX_VALUE;
|
239
|
+
|
240
|
+
if (table) {
|
241
|
+
|
242
|
+
if (typeof table === 'string') {
|
243
|
+
table = document.getElementById(table);
|
253
244
|
}
|
254
|
-
});
|
255
|
-
|
256
|
-
this.dataFound(); // continue
|
257
|
-
}
|
258
|
-
},
|
259
|
-
|
260
|
-
/**
|
261
|
-
*/
|
262
|
-
parseGoogleSpreadsheet: function () {
|
263
|
-
var self = this,
|
264
|
-
options = this.options,
|
265
|
-
googleSpreadsheetKey = options.googleSpreadsheetKey,
|
266
|
-
columns = this.columns,
|
267
|
-
startRow = options.startRow || 0,
|
268
|
-
endRow = options.endRow || Number.MAX_VALUE,
|
269
|
-
startColumn = options.startColumn || 0,
|
270
|
-
endColumn = options.endColumn || Number.MAX_VALUE,
|
271
|
-
gr, // google row
|
272
|
-
gc; // google column
|
273
|
-
|
274
|
-
if (googleSpreadsheetKey) {
|
275
|
-
jQuery.ajax({
|
276
|
-
dataType: 'json',
|
277
|
-
url: 'https://spreadsheets.google.com/feeds/cells/' +
|
278
|
-
googleSpreadsheetKey + '/' + (options.googleSpreadsheetWorksheet || 'od6') +
|
279
|
-
'/public/values?alt=json-in-script&callback=?',
|
280
|
-
error: options.error,
|
281
|
-
success: function (json) {
|
282
|
-
// Prepare the data from the spreadsheat
|
283
|
-
var cells = json.feed.entry,
|
284
|
-
cell,
|
285
|
-
cellCount = cells.length,
|
286
|
-
colCount = 0,
|
287
|
-
rowCount = 0,
|
288
|
-
i;
|
289
245
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
246
|
+
each(table.getElementsByTagName('tr'), function (tr, rowNo) {
|
247
|
+
if (rowNo >= startRow && rowNo <= endRow) {
|
248
|
+
each(tr.children, function (item, colNo) {
|
249
|
+
if ((item.tagName === 'TD' || item.tagName === 'TH') && colNo >= startColumn && colNo <= endColumn) {
|
250
|
+
if (!columns[colNo - startColumn]) {
|
251
|
+
columns[colNo - startColumn] = [];
|
252
|
+
}
|
253
|
+
|
254
|
+
columns[colNo - startColumn][rowNo - startRow] = item.innerHTML;
|
255
|
+
}
|
256
|
+
});
|
296
257
|
}
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
258
|
+
});
|
259
|
+
|
260
|
+
this.dataFound(); // continue
|
261
|
+
}
|
262
|
+
},
|
263
|
+
|
264
|
+
/**
|
265
|
+
*/
|
266
|
+
parseGoogleSpreadsheet: function () {
|
267
|
+
var self = this,
|
268
|
+
options = this.options,
|
269
|
+
googleSpreadsheetKey = options.googleSpreadsheetKey,
|
270
|
+
columns = this.columns,
|
271
|
+
startRow = options.startRow || 0,
|
272
|
+
endRow = options.endRow || Number.MAX_VALUE,
|
273
|
+
startColumn = options.startColumn || 0,
|
274
|
+
endColumn = options.endColumn || Number.MAX_VALUE,
|
275
|
+
gr, // google row
|
276
|
+
gc; // google column
|
277
|
+
|
278
|
+
if (googleSpreadsheetKey) {
|
279
|
+
jQuery.ajax({
|
280
|
+
dataType: 'json',
|
281
|
+
url: 'https://spreadsheets.google.com/feeds/cells/' +
|
282
|
+
googleSpreadsheetKey + '/' + (options.googleSpreadsheetWorksheet || 'od6') +
|
283
|
+
'/public/values?alt=json-in-script&callback=?',
|
284
|
+
error: options.error,
|
285
|
+
success: function (json) {
|
286
|
+
// Prepare the data from the spreadsheat
|
287
|
+
var cells = json.feed.entry,
|
288
|
+
cell,
|
289
|
+
cellCount = cells.length,
|
290
|
+
colCount = 0,
|
291
|
+
rowCount = 0,
|
292
|
+
i;
|
293
|
+
|
294
|
+
// First, find the total number of columns and rows that
|
295
|
+
// are actually filled with data
|
296
|
+
for (i = 0; i < cellCount; i++) {
|
297
|
+
cell = cells[i];
|
298
|
+
colCount = Math.max(colCount, cell.gs$cell.col);
|
299
|
+
rowCount = Math.max(rowCount, cell.gs$cell.row);
|
306
300
|
}
|
307
|
-
}
|
308
301
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
302
|
+
// Set up arrays containing the column data
|
303
|
+
for (i = 0; i < colCount; i++) {
|
304
|
+
if (i >= startColumn && i <= endColumn) {
|
305
|
+
// Create new columns with the length of either end-start or rowCount
|
306
|
+
columns[i - startColumn] = [];
|
307
|
+
|
308
|
+
// Setting the length to avoid jslint warning
|
309
|
+
columns[i - startColumn].length = Math.min(rowCount, endRow - startRow);
|
310
|
+
}
|
311
|
+
}
|
312
|
+
|
313
|
+
// Loop over the cells and assign the value to the right
|
314
|
+
// place in the column arrays
|
315
|
+
for (i = 0; i < cellCount; i++) {
|
316
|
+
cell = cells[i];
|
317
|
+
gr = cell.gs$cell.row - 1; // rows start at 1
|
318
|
+
gc = cell.gs$cell.col - 1; // columns start at 1
|
319
|
+
|
320
|
+
// If both row and col falls inside start and end
|
321
|
+
// set the transposed cell value in the newly created columns
|
322
|
+
if (gc >= startColumn && gc <= endColumn &&
|
323
|
+
gr >= startRow && gr <= endRow) {
|
324
|
+
columns[gc - startColumn][gr - startRow] = cell.content.$t;
|
325
|
+
}
|
321
326
|
}
|
327
|
+
self.dataFound();
|
322
328
|
}
|
323
|
-
|
329
|
+
});
|
330
|
+
}
|
331
|
+
},
|
332
|
+
|
333
|
+
/**
|
334
|
+
* Trim a string from whitespace
|
335
|
+
*/
|
336
|
+
trim: function (str, inside) {
|
337
|
+
if (typeof str === 'string') {
|
338
|
+
str = str.replace(/^\s+|\s+$/g, '');
|
339
|
+
|
340
|
+
// Clear white space insdie the string, like thousands separators
|
341
|
+
if (inside && /^[0-9\s]+$/.test(str)) {
|
342
|
+
str = str.replace(/\s/g, '');
|
324
343
|
}
|
325
|
-
});
|
326
|
-
}
|
327
|
-
},
|
328
|
-
|
329
|
-
/**
|
330
|
-
* Trim a string from whitespace
|
331
|
-
*/
|
332
|
-
trim: function (str, inside) {
|
333
|
-
if (typeof str === 'string') {
|
334
|
-
str = str.replace(/^\s+|\s+$/g, '');
|
335
344
|
|
336
|
-
|
337
|
-
|
338
|
-
|
345
|
+
if (this.decimalRegex) {
|
346
|
+
str = str.replace(this.decimalRegex, '$1.$2');
|
347
|
+
}
|
339
348
|
}
|
340
|
-
|
341
|
-
|
342
|
-
|
349
|
+
return str;
|
350
|
+
},
|
351
|
+
|
352
|
+
/**
|
353
|
+
* Parse numeric cells in to number types and date types in to true dates.
|
354
|
+
*/
|
355
|
+
parseTypes: function () {
|
356
|
+
var columns = this.columns,
|
357
|
+
col = columns.length;
|
358
|
+
|
359
|
+
while (col--) {
|
360
|
+
this.parseColumn(columns[col], col);
|
343
361
|
}
|
344
|
-
}
|
345
|
-
return str;
|
346
|
-
},
|
347
|
-
|
348
|
-
/**
|
349
|
-
* Parse numeric cells in to number types and date types in to true dates.
|
350
|
-
*/
|
351
|
-
parseTypes: function () {
|
352
|
-
var columns = this.columns,
|
353
|
-
col = columns.length;
|
354
|
-
|
355
|
-
while (col--) {
|
356
|
-
this.parseColumn(columns[col], col);
|
357
|
-
}
|
358
362
|
|
359
|
-
|
363
|
+
},
|
360
364
|
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
if (!rawColumns[col]) {
|
384
|
-
rawColumns[col] = [];
|
385
|
-
}
|
386
|
-
while (row--) {
|
387
|
-
val = backup[row] || column[row];
|
365
|
+
/**
|
366
|
+
* Parse a single column. Set properties like .isDatetime and .isNumeric.
|
367
|
+
*/
|
368
|
+
parseColumn: function (column, col) {
|
369
|
+
var rawColumns = this.rawColumns,
|
370
|
+
columns = this.columns,
|
371
|
+
row = column.length,
|
372
|
+
val,
|
373
|
+
floatVal,
|
374
|
+
trimVal,
|
375
|
+
trimInsideVal,
|
376
|
+
firstRowAsNames = this.firstRowAsNames,
|
377
|
+
isXColumn = inArray(col, this.valueCount.xColumns) !== -1,
|
378
|
+
dateVal,
|
379
|
+
backup = [],
|
380
|
+
diff,
|
381
|
+
chartOptions = this.chartOptions,
|
382
|
+
descending,
|
383
|
+
columnTypes = this.options.columnTypes || [],
|
384
|
+
columnType = columnTypes[col],
|
385
|
+
forceCategory = isXColumn && ((chartOptions && chartOptions.xAxis && splat(chartOptions.xAxis)[0].type === 'category') || columnType === 'string');
|
388
386
|
|
389
|
-
|
390
|
-
|
391
|
-
floatVal = parseFloat(trimInsideVal);
|
392
|
-
|
393
|
-
// Set it the first time
|
394
|
-
if (rawColumns[col][row] === undefined) {
|
395
|
-
rawColumns[col][row] = trimVal;
|
387
|
+
if (!rawColumns[col]) {
|
388
|
+
rawColumns[col] = [];
|
396
389
|
}
|
397
|
-
|
398
|
-
|
399
|
-
if (forceCategory || (row === 0 && firstRowAsNames)) {
|
400
|
-
column[row] = trimVal;
|
401
|
-
|
402
|
-
} else if (+trimInsideVal === floatVal) { // is numeric
|
403
|
-
|
404
|
-
column[row] = floatVal;
|
390
|
+
while (row--) {
|
391
|
+
val = backup[row] || column[row];
|
405
392
|
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
} else {
|
410
|
-
column.isNumeric = true;
|
411
|
-
}
|
393
|
+
trimVal = this.trim(val);
|
394
|
+
trimInsideVal = this.trim(val, true);
|
395
|
+
floatVal = parseFloat(trimInsideVal);
|
412
396
|
|
413
|
-
|
414
|
-
|
397
|
+
// Set it the first time
|
398
|
+
if (rawColumns[col][row] === undefined) {
|
399
|
+
rawColumns[col][row] = trimVal;
|
415
400
|
}
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
if (
|
422
|
-
|
423
|
-
column[row] =
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
401
|
+
|
402
|
+
// Disable number or date parsing by setting the X axis type to category
|
403
|
+
if (forceCategory || (row === 0 && firstRowAsNames)) {
|
404
|
+
column[row] = trimVal;
|
405
|
+
|
406
|
+
} else if (+trimInsideVal === floatVal) { // is numeric
|
407
|
+
|
408
|
+
column[row] = floatVal;
|
409
|
+
|
410
|
+
// If the number is greater than milliseconds in a year, assume datetime
|
411
|
+
if (floatVal > 365 * 24 * 3600 * 1000 && columnType !== 'float') {
|
412
|
+
column.isDatetime = true;
|
413
|
+
} else {
|
414
|
+
column.isNumeric = true;
|
415
|
+
}
|
416
|
+
|
429
417
|
if (column[row + 1] !== undefined) {
|
430
|
-
|
431
|
-
if (diff !== descending && descending !== undefined) {
|
432
|
-
if (this.alternativeFormat) {
|
433
|
-
this.dateFormat = this.alternativeFormat;
|
434
|
-
row = column.length;
|
435
|
-
this.alternativeFormat = this.dateFormats[this.dateFormat].alternative;
|
436
|
-
} else {
|
437
|
-
column.unsorted = true;
|
438
|
-
}
|
439
|
-
}
|
440
|
-
descending = diff;
|
418
|
+
descending = floatVal > column[row + 1];
|
441
419
|
}
|
442
420
|
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
421
|
+
// String, continue to determine if it is a date string or really a string
|
422
|
+
} else {
|
423
|
+
dateVal = this.parseDate(val);
|
424
|
+
// Only allow parsing of dates if this column is an x-column
|
425
|
+
if (isXColumn && typeof dateVal === 'number' && !isNaN(dateVal) && columnType !== 'float') { // is date
|
426
|
+
backup[row] = val;
|
427
|
+
column[row] = dateVal;
|
428
|
+
column.isDatetime = true;
|
429
|
+
|
430
|
+
// Check if the dates are uniformly descending or ascending. If they
|
431
|
+
// are not, chances are that they are a different time format, so check
|
432
|
+
// for alternative.
|
433
|
+
if (column[row + 1] !== undefined) {
|
434
|
+
diff = dateVal > column[row + 1];
|
435
|
+
if (diff !== descending && descending !== undefined) {
|
436
|
+
if (this.alternativeFormat) {
|
437
|
+
this.dateFormat = this.alternativeFormat;
|
438
|
+
row = column.length;
|
439
|
+
this.alternativeFormat = this.dateFormats[this.dateFormat].alternative;
|
440
|
+
} else {
|
441
|
+
column.unsorted = true;
|
442
|
+
}
|
443
|
+
}
|
444
|
+
descending = diff;
|
445
|
+
}
|
446
|
+
|
447
|
+
} else { // string
|
448
|
+
column[row] = trimVal === '' ? null : trimVal;
|
449
|
+
if (row !== 0 && (column.isDatetime || column.isNumeric)) {
|
450
|
+
column.mixed = true;
|
451
|
+
}
|
447
452
|
}
|
448
453
|
}
|
449
454
|
}
|
450
|
-
}
|
451
455
|
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
456
|
+
// If strings are intermixed with numbers or dates in a parsed column, it is an indication
|
457
|
+
// that parsing went wrong or the data was not intended to display as numbers or dates and
|
458
|
+
// parsing is too aggressive. Fall back to categories. Demonstrated in the
|
459
|
+
// highcharts/demo/column-drilldown sample.
|
460
|
+
if (isXColumn && column.mixed) {
|
461
|
+
columns[col] = rawColumns[col];
|
462
|
+
}
|
459
463
|
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
464
|
+
// If the 0 column is date or number and descending, reverse all columns.
|
465
|
+
if (isXColumn && descending && this.options.sort) {
|
466
|
+
for (col = 0; col < columns.length; col++) {
|
467
|
+
columns[col].reverse();
|
468
|
+
if (firstRowAsNames) {
|
469
|
+
columns[col].unshift(columns[col].pop());
|
470
|
+
}
|
466
471
|
}
|
467
472
|
}
|
468
|
-
}
|
469
|
-
},
|
470
|
-
|
471
|
-
/**
|
472
|
-
* A collection of available date formats, extendable from the outside to support
|
473
|
-
* custom date formats.
|
474
|
-
*/
|
475
|
-
dateFormats: {
|
476
|
-
'YYYY-mm-dd': {
|
477
|
-
regex: /^([0-9]{4})[\-\/\.]([0-9]{2})[\-\/\.]([0-9]{2})$/,
|
478
|
-
parser: function (match) {
|
479
|
-
return Date.UTC(+match[1], match[2] - 1, +match[3]);
|
480
|
-
}
|
481
473
|
},
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
474
|
+
|
475
|
+
/**
|
476
|
+
* A collection of available date formats, extendable from the outside to support
|
477
|
+
* custom date formats.
|
478
|
+
*/
|
479
|
+
dateFormats: {
|
480
|
+
'YYYY-mm-dd': {
|
481
|
+
regex: /^([0-9]{4})[\-\/\.]([0-9]{2})[\-\/\.]([0-9]{2})$/,
|
482
|
+
parser: function (match) {
|
483
|
+
return Date.UTC(+match[1], match[2] - 1, +match[3]);
|
484
|
+
}
|
486
485
|
},
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
}
|
494
|
-
},
|
495
|
-
'dd/mm/YY': {
|
496
|
-
regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{2})$/,
|
497
|
-
parser: function (match) {
|
498
|
-
return Date.UTC(+match[3] + 2000, match[2] - 1, +match[1]);
|
486
|
+
'dd/mm/YYYY': {
|
487
|
+
regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{4})$/,
|
488
|
+
parser: function (match) {
|
489
|
+
return Date.UTC(+match[3], match[2] - 1, +match[1]);
|
490
|
+
},
|
491
|
+
alternative: 'mm/dd/YYYY' // different format with the same regex
|
499
492
|
},
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
493
|
+
'mm/dd/YYYY': {
|
494
|
+
regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{4})$/,
|
495
|
+
parser: function (match) {
|
496
|
+
return Date.UTC(+match[3], match[1] - 1, +match[2]);
|
497
|
+
}
|
498
|
+
},
|
499
|
+
'dd/mm/YY': {
|
500
|
+
regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{2})$/,
|
501
|
+
parser: function (match) {
|
502
|
+
return Date.UTC(+match[3] + 2000, match[2] - 1, +match[1]);
|
503
|
+
},
|
504
|
+
alternative: 'mm/dd/YY' // different format with the same regex
|
505
|
+
},
|
506
|
+
'mm/dd/YY': {
|
507
|
+
regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{2})$/,
|
508
|
+
parser: function (match) {
|
509
|
+
return Date.UTC(+match[3] + 2000, match[1] - 1, +match[2]);
|
510
|
+
}
|
506
511
|
}
|
507
|
-
}
|
508
|
-
},
|
509
|
-
|
510
|
-
/**
|
511
|
-
* Parse a date and return it as a number. Overridable through options.parseDate.
|
512
|
-
*/
|
513
|
-
parseDate: function (val) {
|
514
|
-
var parseDate = this.options.parseDate,
|
515
|
-
ret,
|
516
|
-
key,
|
517
|
-
format,
|
518
|
-
dateFormat = this.options.dateFormat || this.dateFormat,
|
519
|
-
match;
|
520
|
-
|
521
|
-
if (parseDate) {
|
522
|
-
ret = parseDate(val);
|
512
|
+
},
|
523
513
|
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
514
|
+
/**
|
515
|
+
* Parse a date and return it as a number. Overridable through options.parseDate.
|
516
|
+
*/
|
517
|
+
parseDate: function (val) {
|
518
|
+
var parseDate = this.options.parseDate,
|
519
|
+
ret,
|
520
|
+
key,
|
521
|
+
format,
|
522
|
+
dateFormat = this.options.dateFormat || this.dateFormat,
|
523
|
+
match;
|
524
|
+
|
525
|
+
if (parseDate) {
|
526
|
+
ret = parseDate(val);
|
527
|
+
|
528
|
+
} else if (typeof val === 'string') {
|
529
|
+
// Auto-detect the date format the first time
|
530
|
+
if (!dateFormat) {
|
531
|
+
for (key in this.dateFormats) {
|
532
|
+
format = this.dateFormats[key];
|
533
|
+
match = val.match(format.regex);
|
534
|
+
if (match) {
|
535
|
+
this.dateFormat = dateFormat = key;
|
536
|
+
this.alternativeFormat = format.alternative;
|
537
|
+
ret = format.parser(match);
|
538
|
+
break;
|
539
|
+
}
|
540
|
+
}
|
541
|
+
// Next time, use the one previously found
|
542
|
+
} else {
|
543
|
+
format = this.dateFormats[dateFormat];
|
529
544
|
match = val.match(format.regex);
|
530
545
|
if (match) {
|
531
|
-
this.dateFormat = dateFormat = key;
|
532
|
-
this.alternativeFormat = format.alternative;
|
533
546
|
ret = format.parser(match);
|
534
|
-
break;
|
535
547
|
}
|
536
548
|
}
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
// returns a date.
|
550
|
-
if (typeof match === 'object' && match !== null && match.getTime) {
|
551
|
-
ret = match.getTime() - match.getTimezoneOffset() * 60000;
|
552
|
-
|
553
|
-
// Timestamp
|
554
|
-
} else if (typeof match === 'number' && !isNaN(match)) {
|
555
|
-
ret = match - (new Date(match)).getTimezoneOffset() * 60000;
|
549
|
+
// Fall back to Date.parse
|
550
|
+
if (!match) {
|
551
|
+
match = Date.parse(val);
|
552
|
+
// External tools like Date.js and MooTools extend Date object and
|
553
|
+
// returns a date.
|
554
|
+
if (typeof match === 'object' && match !== null && match.getTime) {
|
555
|
+
ret = match.getTime() - match.getTimezoneOffset() * 60000;
|
556
|
+
|
557
|
+
// Timestamp
|
558
|
+
} else if (typeof match === 'number' && !isNaN(match)) {
|
559
|
+
ret = match - (new Date(match)).getTimezoneOffset() * 60000;
|
560
|
+
}
|
556
561
|
}
|
557
562
|
}
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
563
|
+
return ret;
|
564
|
+
},
|
565
|
+
|
566
|
+
/**
|
567
|
+
* Reorganize rows into columns
|
568
|
+
*/
|
569
|
+
rowsToColumns: function (rows) {
|
570
|
+
var row,
|
571
|
+
rowsLength,
|
572
|
+
col,
|
573
|
+
colsLength,
|
574
|
+
columns;
|
575
|
+
|
576
|
+
if (rows) {
|
577
|
+
columns = [];
|
578
|
+
rowsLength = rows.length;
|
579
|
+
for (row = 0; row < rowsLength; row++) {
|
580
|
+
colsLength = rows[row].length;
|
581
|
+
for (col = 0; col < colsLength; col++) {
|
582
|
+
if (!columns[col]) {
|
583
|
+
columns[col] = [];
|
584
|
+
}
|
585
|
+
columns[col][row] = rows[row][col];
|
580
586
|
}
|
581
|
-
columns[col][row] = rows[row][col];
|
582
587
|
}
|
583
588
|
}
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
}
|
595
|
-
},
|
596
|
-
|
597
|
-
getFreeIndexes: function (numberOfColumns, seriesBuilders) {
|
598
|
-
var s,
|
599
|
-
i,
|
600
|
-
freeIndexes = [],
|
601
|
-
freeIndexValues = [],
|
602
|
-
referencedIndexes;
|
603
|
-
|
604
|
-
// Add all columns as free
|
605
|
-
for (i = 0; i < numberOfColumns; i = i + 1) {
|
606
|
-
freeIndexes.push(true);
|
607
|
-
}
|
589
|
+
return columns;
|
590
|
+
},
|
591
|
+
|
592
|
+
/**
|
593
|
+
* A hook for working directly on the parsed columns
|
594
|
+
*/
|
595
|
+
parsed: function () {
|
596
|
+
if (this.options.parsed) {
|
597
|
+
return this.options.parsed.call(this, this.columns);
|
598
|
+
}
|
599
|
+
},
|
608
600
|
|
609
|
-
|
610
|
-
|
611
|
-
|
601
|
+
getFreeIndexes: function (numberOfColumns, seriesBuilders) {
|
602
|
+
var s,
|
603
|
+
i,
|
604
|
+
freeIndexes = [],
|
605
|
+
freeIndexValues = [],
|
606
|
+
referencedIndexes;
|
612
607
|
|
613
|
-
|
614
|
-
|
608
|
+
// Add all columns as free
|
609
|
+
for (i = 0; i < numberOfColumns; i = i + 1) {
|
610
|
+
freeIndexes.push(true);
|
615
611
|
}
|
616
|
-
}
|
617
612
|
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
freeIndexValues.push(i);
|
622
|
-
}
|
623
|
-
}
|
613
|
+
// Loop all defined builders and remove their referenced columns
|
614
|
+
for (s = 0; s < seriesBuilders.length; s = s + 1) {
|
615
|
+
referencedIndexes = seriesBuilders[s].getReferencedColumnIndexes();
|
624
616
|
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
/**
|
629
|
-
* If a complete callback function is provided in the options, interpret the
|
630
|
-
* columns into a Highcharts options object.
|
631
|
-
*/
|
632
|
-
complete: function () {
|
633
|
-
|
634
|
-
var columns = this.columns,
|
635
|
-
xColumns = [],
|
636
|
-
type,
|
637
|
-
options = this.options,
|
638
|
-
series,
|
639
|
-
data,
|
640
|
-
i,
|
641
|
-
j,
|
642
|
-
r,
|
643
|
-
seriesIndex,
|
644
|
-
chartOptions,
|
645
|
-
allSeriesBuilders = [],
|
646
|
-
builder,
|
647
|
-
freeIndexes,
|
648
|
-
typeCol,
|
649
|
-
index;
|
650
|
-
|
651
|
-
xColumns.length = columns.length;
|
652
|
-
if (options.complete || options.afterComplete) {
|
653
|
-
|
654
|
-
// Get the names and shift the top row
|
655
|
-
for (i = 0; i < columns.length; i++) {
|
656
|
-
if (this.firstRowAsNames) {
|
657
|
-
columns[i].name = columns[i].shift();
|
617
|
+
for (i = 0; i < referencedIndexes.length; i = i + 1) {
|
618
|
+
freeIndexes[referencedIndexes[i]] = false;
|
658
619
|
}
|
659
620
|
}
|
660
|
-
|
661
|
-
// Use the next columns for series
|
662
|
-
series = [];
|
663
|
-
freeIndexes = this.getFreeIndexes(columns.length, this.valueCount.seriesBuilders);
|
664
621
|
|
665
|
-
//
|
666
|
-
for (
|
667
|
-
|
668
|
-
|
669
|
-
// If the builder can be populated with remaining columns, then add it to allBuilders
|
670
|
-
if (builder.populateColumns(freeIndexes)) {
|
671
|
-
allSeriesBuilders.push(builder);
|
622
|
+
// Collect the values for the free indexes
|
623
|
+
for (i = 0; i < freeIndexes.length; i = i + 1) {
|
624
|
+
if (freeIndexes[i]) {
|
625
|
+
freeIndexValues.push(i);
|
672
626
|
}
|
673
627
|
}
|
674
628
|
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
629
|
+
return freeIndexValues;
|
630
|
+
},
|
631
|
+
|
632
|
+
/**
|
633
|
+
* If a complete callback function is provided in the options, interpret the
|
634
|
+
* columns into a Highcharts options object.
|
635
|
+
*/
|
636
|
+
complete: function () {
|
637
|
+
|
638
|
+
var columns = this.columns,
|
639
|
+
xColumns = [],
|
640
|
+
type,
|
641
|
+
options = this.options,
|
642
|
+
series,
|
643
|
+
data,
|
644
|
+
i,
|
645
|
+
j,
|
646
|
+
r,
|
647
|
+
seriesIndex,
|
648
|
+
chartOptions,
|
649
|
+
allSeriesBuilders = [],
|
650
|
+
builder,
|
651
|
+
freeIndexes,
|
652
|
+
typeCol,
|
653
|
+
index;
|
654
|
+
|
655
|
+
xColumns.length = columns.length;
|
656
|
+
if (options.complete || options.afterComplete) {
|
657
|
+
|
658
|
+
// Get the names and shift the top row
|
659
|
+
for (i = 0; i < columns.length; i++) {
|
660
|
+
if (this.firstRowAsNames) {
|
661
|
+
columns[i].name = columns[i].shift();
|
662
|
+
}
|
684
663
|
}
|
664
|
+
|
665
|
+
// Use the next columns for series
|
666
|
+
series = [];
|
667
|
+
freeIndexes = this.getFreeIndexes(columns.length, this.valueCount.seriesBuilders);
|
685
668
|
|
686
|
-
|
687
|
-
|
688
|
-
builder
|
669
|
+
// Populate defined series
|
670
|
+
for (seriesIndex = 0; seriesIndex < this.valueCount.seriesBuilders.length; seriesIndex++) {
|
671
|
+
builder = this.valueCount.seriesBuilders[seriesIndex];
|
672
|
+
|
673
|
+
// If the builder can be populated with remaining columns, then add it to allBuilders
|
674
|
+
if (builder.populateColumns(freeIndexes)) {
|
675
|
+
allSeriesBuilders.push(builder);
|
676
|
+
}
|
689
677
|
}
|
690
678
|
|
691
|
-
//
|
692
|
-
|
693
|
-
|
679
|
+
// Populate dynamic series
|
680
|
+
while (freeIndexes.length > 0) {
|
681
|
+
builder = new SeriesBuilder();
|
682
|
+
builder.addColumnReader(0, 'x');
|
683
|
+
|
684
|
+
// Mark index as used (not free)
|
685
|
+
index = inArray(0, freeIndexes);
|
686
|
+
if (index !== -1) {
|
687
|
+
freeIndexes.splice(index, 1);
|
688
|
+
}
|
689
|
+
|
690
|
+
for (i = 0; i < this.valueCount.global; i++) {
|
691
|
+
// Create and add a column reader for the next free column index
|
692
|
+
builder.addColumnReader(undefined, this.valueCount.globalPointArrayMap[i]);
|
693
|
+
}
|
694
|
+
|
695
|
+
// If the builder can be populated with remaining columns, then add it to allBuilders
|
696
|
+
if (builder.populateColumns(freeIndexes)) {
|
697
|
+
allSeriesBuilders.push(builder);
|
698
|
+
}
|
694
699
|
}
|
695
|
-
}
|
696
700
|
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
701
|
+
// Get the data-type from the first series x column
|
702
|
+
if (allSeriesBuilders.length > 0 && allSeriesBuilders[0].readers.length > 0) {
|
703
|
+
typeCol = columns[allSeriesBuilders[0].readers[0].columnIndex];
|
704
|
+
if (typeCol !== undefined) {
|
705
|
+
if (typeCol.isDatetime) {
|
706
|
+
type = 'datetime';
|
707
|
+
} else if (!typeCol.isNumeric) {
|
708
|
+
type = 'category';
|
709
|
+
}
|
705
710
|
}
|
706
711
|
}
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
712
|
+
// Axis type is category, then the "x" column should be called "name"
|
713
|
+
if (type === 'category') {
|
714
|
+
for (seriesIndex = 0; seriesIndex < allSeriesBuilders.length; seriesIndex++) {
|
715
|
+
builder = allSeriesBuilders[seriesIndex];
|
716
|
+
for (r = 0; r < builder.readers.length; r++) {
|
717
|
+
if (builder.readers[r].configName === 'x') {
|
718
|
+
builder.readers[r].configName = 'name';
|
719
|
+
}
|
715
720
|
}
|
716
721
|
}
|
717
722
|
}
|
718
|
-
}
|
719
723
|
|
720
|
-
|
721
|
-
|
722
|
-
|
724
|
+
// Read data for all builders
|
725
|
+
for (seriesIndex = 0; seriesIndex < allSeriesBuilders.length; seriesIndex++) {
|
726
|
+
builder = allSeriesBuilders[seriesIndex];
|
723
727
|
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
728
|
+
// Iterate down the cells of each column and add data to the series
|
729
|
+
data = [];
|
730
|
+
for (j = 0; j < columns[0].length; j++) {
|
731
|
+
data[j] = builder.read(columns, j);
|
732
|
+
}
|
729
733
|
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
734
|
+
// Add the series
|
735
|
+
series[seriesIndex] = {
|
736
|
+
data: data
|
737
|
+
};
|
738
|
+
if (builder.name) {
|
739
|
+
series[seriesIndex].name = builder.name;
|
740
|
+
}
|
741
|
+
if (type === 'category') {
|
742
|
+
series[seriesIndex].turboThreshold = 0;
|
743
|
+
}
|
739
744
|
}
|
740
|
-
}
|
741
745
|
|
742
746
|
|
743
747
|
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
};
|
748
|
-
if (type) {
|
749
|
-
chartOptions.xAxis = {
|
750
|
-
type: type
|
748
|
+
// Do the callback
|
749
|
+
chartOptions = {
|
750
|
+
series: series
|
751
751
|
};
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
752
|
+
if (type) {
|
753
|
+
chartOptions.xAxis = {
|
754
|
+
type: type
|
755
|
+
};
|
756
|
+
}
|
757
|
+
|
758
|
+
if (options.complete) {
|
759
|
+
options.complete(chartOptions);
|
760
|
+
}
|
757
761
|
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
+
// The afterComplete hook is used internally to avoid conflict with the externally
|
763
|
+
// available complete option.
|
764
|
+
if (options.afterComplete) {
|
765
|
+
options.afterComplete(chartOptions);
|
766
|
+
}
|
762
767
|
}
|
763
768
|
}
|
764
|
-
}
|
765
769
|
});
|
766
770
|
|
767
771
|
// Register the Data prototype and data function on Highcharts
|
@@ -950,4 +954,4 @@
|
|
950
954
|
|
951
955
|
|
952
956
|
|
953
|
-
}
|
957
|
+
}));
|