highcharts-rails 4.0.3 → 4.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.markdown +36 -0
- data/app/assets/javascripts/highcharts.js +147 -106
- data/app/assets/javascripts/highcharts/adapters/standalone-framework.js +17 -4
- data/app/assets/javascripts/highcharts/highcharts-3d.js +68 -37
- data/app/assets/javascripts/highcharts/highcharts-more.js +3 -4
- data/app/assets/javascripts/highcharts/modules/canvas-tools.js +1 -1
- data/app/assets/javascripts/highcharts/modules/data.js +476 -73
- data/app/assets/javascripts/highcharts/modules/drilldown.js +43 -23
- data/app/assets/javascripts/highcharts/modules/exporting.js +1 -1
- data/app/assets/javascripts/highcharts/modules/funnel.js +1 -1
- data/app/assets/javascripts/highcharts/modules/heatmap.js +18 -6
- data/app/assets/javascripts/highcharts/modules/no-data-to-display.js +2 -2
- data/app/assets/javascripts/highcharts/modules/solid-gauge.js +1 -1
- data/lib/highcharts/version.rb +1 -1
- metadata +1 -1
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @license Highcharts JS v4.0.
|
2
|
+
* @license Highcharts JS v4.0.4 (2014-09-02)
|
3
3
|
*
|
4
4
|
* Standalone Highcharts Framework
|
5
5
|
*
|
@@ -15,6 +15,7 @@ var UNDEFINED,
|
|
15
15
|
emptyArray = [],
|
16
16
|
timers = [],
|
17
17
|
timerId,
|
18
|
+
animSetters = {},
|
18
19
|
Fx;
|
19
20
|
|
20
21
|
Math.easeInOutSine = function (t, b, c, d) {
|
@@ -184,6 +185,7 @@ function augment(obj) {
|
|
184
185
|
|
185
186
|
|
186
187
|
return {
|
188
|
+
|
187
189
|
/**
|
188
190
|
* Initialize the adapter. This is run once as Highcharts is first run.
|
189
191
|
*/
|
@@ -292,10 +294,14 @@ return {
|
|
292
294
|
elem = this.elem,
|
293
295
|
elemelem = elem.element; // if destroyed, it is null
|
294
296
|
|
297
|
+
// Animation setter defined from outside
|
298
|
+
if (animSetters[this.prop]) {
|
299
|
+
animSetters[this.prop](this);
|
300
|
+
|
295
301
|
// Animating a path definition on SVGElement
|
296
|
-
if (paths && elemelem) {
|
302
|
+
} else if (paths && elemelem) {
|
297
303
|
elem.attr('d', pathAnim.step(paths[0], paths[1], this.now, this.toD));
|
298
|
-
|
304
|
+
|
299
305
|
// Other animations on SVGElement
|
300
306
|
} else if (elem.attr) {
|
301
307
|
if (elemelem) {
|
@@ -440,7 +446,7 @@ return {
|
|
440
446
|
}
|
441
447
|
|
442
448
|
if (!end) {
|
443
|
-
end =
|
449
|
+
end = prop[name];
|
444
450
|
}
|
445
451
|
fx.custom(start, end, unit);
|
446
452
|
}
|
@@ -454,6 +460,13 @@ return {
|
|
454
460
|
return window.getComputedStyle(el, undefined).getPropertyValue(prop);
|
455
461
|
},
|
456
462
|
|
463
|
+
/**
|
464
|
+
* Add an animation setter for a specific property
|
465
|
+
*/
|
466
|
+
addAnimSetter: function (prop, fn) {
|
467
|
+
animSetters[prop] = fn;
|
468
|
+
},
|
469
|
+
|
457
470
|
/**
|
458
471
|
* Downloads a script and executes a callback when done.
|
459
472
|
* @param {String} scriptLocation
|
@@ -2,7 +2,7 @@
|
|
2
2
|
// @compilation_level SIMPLE_OPTIMIZATIONS
|
3
3
|
|
4
4
|
/**
|
5
|
-
* @license Highcharts JS v4.0.
|
5
|
+
* @license Highcharts JS v4.0.4 (2014-09-02)
|
6
6
|
*
|
7
7
|
* (c) 2009-2013 Torstein Hønsi
|
8
8
|
*
|
@@ -75,6 +75,11 @@ function perspective(points, angle2, angle1, origin) {
|
|
75
75
|
////// HELPER METHODS //////
|
76
76
|
var dFactor = (4 * (Math.sqrt(2) - 1) / 3) / (PI / 2);
|
77
77
|
|
78
|
+
function defined(obj) {
|
79
|
+
return obj !== undefined && obj !== null;
|
80
|
+
}
|
81
|
+
|
82
|
+
|
78
83
|
function curveTo(cx, cy, rx, ry, start, end, dx, dy) {
|
79
84
|
var result = [];
|
80
85
|
if ((end > start) && (end - start > PI / 2 + 0.0001)) {
|
@@ -150,7 +155,7 @@ Highcharts.SVGRenderer.prototype.cuboid = function (shapeArgs) {
|
|
150
155
|
};
|
151
156
|
|
152
157
|
result.attr = function (args) {
|
153
|
-
if (args.shapeArgs || args.x) {
|
158
|
+
if (args.shapeArgs || defined(args.x)) {
|
154
159
|
var shapeArgs = args.shapeArgs || args;
|
155
160
|
var paths = this.renderer.cuboidPath(shapeArgs);
|
156
161
|
this.front.attr({d: paths[0], zIndex: paths[3]});
|
@@ -164,7 +169,7 @@ Highcharts.SVGRenderer.prototype.cuboid = function (shapeArgs) {
|
|
164
169
|
};
|
165
170
|
|
166
171
|
result.animate = function (args, duration, complete) {
|
167
|
-
if (args.x && args.y) {
|
172
|
+
if (defined(args.x) && defined(args.y)) {
|
168
173
|
var paths = this.renderer.cuboidPath(args);
|
169
174
|
this.front.attr({zIndex: paths[3]}).animate({d: paths[0]}, duration, complete);
|
170
175
|
this.top.attr({zIndex: paths[4]}).animate({d: paths[1]}, duration, complete);
|
@@ -333,7 +338,7 @@ Highcharts.SVGRenderer.prototype.arc3d = function (shapeArgs) {
|
|
333
338
|
};
|
334
339
|
|
335
340
|
result.animate = function (args, duration, complete) {
|
336
|
-
if (args.end || args.start) {
|
341
|
+
if (defined(args.end) || defined(args.start)) {
|
337
342
|
this._shapeArgs = this.shapeArgs;
|
338
343
|
|
339
344
|
Highcharts.SVGElement.prototype.animate.call(this, {
|
@@ -348,6 +353,10 @@ Highcharts.SVGRenderer.prototype.arc3d = function (shapeArgs) {
|
|
348
353
|
end = fx.end,
|
349
354
|
pos = fx.pos,
|
350
355
|
sA = Highcharts.merge(start, {
|
356
|
+
x: start.x + ((end.x - start.x) * pos),
|
357
|
+
y: start.y + ((end.y - start.y) * pos),
|
358
|
+
r: start.r + ((end.r - start.r) * pos),
|
359
|
+
innerR: start.innerR + ((end.innerR - start.innerR) * pos),
|
351
360
|
start: start.start + ((end.start - start.start) * pos),
|
352
361
|
end: start.end + ((end.end - start.end) * pos)
|
353
362
|
});
|
@@ -518,7 +527,20 @@ defaultChartOptions.chart.options3d = {
|
|
518
527
|
back: { size: 1, color: 'rgba(255,255,255,0)' }
|
519
528
|
}
|
520
529
|
};
|
521
|
-
|
530
|
+
|
531
|
+
Highcharts.wrap(Highcharts.Chart.prototype, 'init', function (proceed) {
|
532
|
+
var args = [].slice.call(arguments, 1),
|
533
|
+
plotOptions,
|
534
|
+
pieOptions;
|
535
|
+
|
536
|
+
if (args[0].chart.options3d && args[0].chart.options3d.enabled) {
|
537
|
+
plotOptions = args[0].plotOptions || {};
|
538
|
+
pieOptions = plotOptions.pie || {};
|
539
|
+
|
540
|
+
pieOptions.borderColor = Highcharts.pick(pieOptions.borderColor, undefined);
|
541
|
+
}
|
542
|
+
proceed.apply(this, args);
|
543
|
+
});
|
522
544
|
|
523
545
|
Highcharts.wrap(Highcharts.Chart.prototype, 'setChartSize', function (proceed) {
|
524
546
|
proceed.apply(this, [].slice.call(arguments, 1));
|
@@ -851,20 +873,21 @@ Highcharts.wrap(Highcharts.seriesTypes.column.prototype, 'translate', function (
|
|
851
873
|
z += (seriesOptions.groupZPadding || 1);
|
852
874
|
|
853
875
|
Highcharts.each(series.data, function (point) {
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
point.shapeType = 'cuboid';
|
858
|
-
shapeArgs.alpha = alpha;
|
859
|
-
shapeArgs.beta = beta;
|
860
|
-
shapeArgs.z = z;
|
861
|
-
shapeArgs.origin = origin;
|
862
|
-
shapeArgs.depth = depth;
|
876
|
+
if (point.y !== null) {
|
877
|
+
var shapeArgs = point.shapeArgs,
|
878
|
+
tooltipPos = point.tooltipPos;
|
863
879
|
|
864
|
-
|
865
|
-
|
866
|
-
|
880
|
+
point.shapeType = 'cuboid';
|
881
|
+
shapeArgs.alpha = alpha;
|
882
|
+
shapeArgs.beta = beta;
|
883
|
+
shapeArgs.z = z;
|
884
|
+
shapeArgs.origin = origin;
|
885
|
+
shapeArgs.depth = depth;
|
867
886
|
|
887
|
+
// Translate the tooltip position in 3d space
|
888
|
+
tooltipPos = perspective([{ x: tooltipPos[0], y: tooltipPos[1], z: z }], alpha, beta, origin)[0];
|
889
|
+
point.tooltipPos = [tooltipPos.x, tooltipPos.y];
|
890
|
+
}
|
868
891
|
});
|
869
892
|
});
|
870
893
|
|
@@ -881,25 +904,29 @@ Highcharts.wrap(Highcharts.seriesTypes.column.prototype, 'animate', function (pr
|
|
881
904
|
if (Highcharts.svg) { // VML is too slow anyway
|
882
905
|
if (init) {
|
883
906
|
Highcharts.each(series.data, function (point) {
|
884
|
-
point.
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
if (
|
889
|
-
|
890
|
-
|
891
|
-
|
907
|
+
if (point.y !== null) {
|
908
|
+
point.height = point.shapeArgs.height;
|
909
|
+
point.shapey = point.shapeArgs.y; //#2968
|
910
|
+
point.shapeArgs.height = 1;
|
911
|
+
if (!reversed) {
|
912
|
+
if (point.stackY) {
|
913
|
+
point.shapeArgs.y = point.plotY + yAxis.translate(point.stackY);
|
914
|
+
} else {
|
915
|
+
point.shapeArgs.y = point.plotY + (point.negative ? -point.height : point.height);
|
916
|
+
}
|
892
917
|
}
|
893
918
|
}
|
894
919
|
});
|
895
920
|
|
896
921
|
} else { // run the animation
|
897
922
|
Highcharts.each(series.data, function (point) {
|
898
|
-
point.
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
point.graphic
|
923
|
+
if (point.y !== null) {
|
924
|
+
point.shapeArgs.height = point.height;
|
925
|
+
point.shapeArgs.y = point.shapey; //#2968
|
926
|
+
// null value do not have a graphic
|
927
|
+
if (point.graphic) {
|
928
|
+
point.graphic.animate(point.shapeArgs, series.options.animation);
|
929
|
+
}
|
903
930
|
}
|
904
931
|
});
|
905
932
|
|
@@ -951,14 +978,16 @@ function draw3DPoints(proceed) {
|
|
951
978
|
this.borderWidth = options.borderWidth = options.edgeWidth || 1;
|
952
979
|
|
953
980
|
Highcharts.each(this.data, function (point) {
|
954
|
-
|
981
|
+
if (point.y !== null) {
|
982
|
+
var pointAttr = point.pointAttr;
|
955
983
|
|
956
|
-
|
957
|
-
|
984
|
+
// Set the border color to the fill color to provide a smooth edge
|
985
|
+
this.borderColor = Highcharts.pick(options.edgeColor, pointAttr[''].fill);
|
958
986
|
|
959
|
-
|
960
|
-
|
961
|
-
|
987
|
+
pointAttr[''].stroke = this.borderColor;
|
988
|
+
pointAttr.hover.stroke = Highcharts.pick(states.hover.edgeColor, this.borderColor);
|
989
|
+
pointAttr.select.stroke = Highcharts.pick(states.select.edgeColor, this.borderColor);
|
990
|
+
}
|
962
991
|
});
|
963
992
|
}
|
964
993
|
|
@@ -1108,7 +1137,8 @@ Highcharts.wrap(Highcharts.seriesTypes.pie.prototype, 'translate', function (pro
|
|
1108
1137
|
});
|
1109
1138
|
|
1110
1139
|
Highcharts.wrap(Highcharts.seriesTypes.pie.prototype.pointClass.prototype, 'haloPath', function (proceed) {
|
1111
|
-
|
1140
|
+
var args = arguments;
|
1141
|
+
return this.series.chart.is3d() ? [] : proceed.call(this, args[1]);
|
1112
1142
|
});
|
1113
1143
|
|
1114
1144
|
Highcharts.wrap(Highcharts.seriesTypes.pie.prototype, 'drawPoints', function (proceed) {
|
@@ -1119,6 +1149,7 @@ Highcharts.wrap(Highcharts.seriesTypes.pie.prototype, 'drawPoints', function (pr
|
|
1119
1149
|
|
1120
1150
|
// Set the border color to the fill color to provide a smooth edge
|
1121
1151
|
this.borderWidth = options.borderWidth = options.edgeWidth || 1;
|
1152
|
+
this.borderColor = options.edgeColor = Highcharts.pick(options.edgeColor, options.borderColor, undefined);
|
1122
1153
|
|
1123
1154
|
states.hover.borderColor = Highcharts.pick(states.hover.edgeColor, this.borderColor);
|
1124
1155
|
states.hover.borderWidth = Highcharts.pick(states.hover.edgeWidth, this.borderWidth);
|
@@ -2,7 +2,7 @@
|
|
2
2
|
// @compilation_level SIMPLE_OPTIMIZATIONS
|
3
3
|
|
4
4
|
/**
|
5
|
-
* @license Highcharts JS v4.0.
|
5
|
+
* @license Highcharts JS v4.0.4 (2014-09-02)
|
6
6
|
*
|
7
7
|
* (c) 2009-2014 Torstein Honsi
|
8
8
|
*
|
@@ -1671,11 +1671,10 @@ seriesTypes.waterfall = extendClass(seriesTypes.column, {
|
|
1671
1671
|
*/
|
1672
1672
|
toYData: function (pt) {
|
1673
1673
|
if (pt.isSum) {
|
1674
|
-
return "sum";
|
1674
|
+
return (pt.x === 0 ? null : "sum"); //#3245 Error when first element is Sum or Intermediate Sum
|
1675
1675
|
} else if (pt.isIntermediateSum) {
|
1676
|
-
return "intermediateSum";
|
1676
|
+
return (pt.x === 0 ? null : "intermediateSum"); //#3245
|
1677
1677
|
}
|
1678
|
-
|
1679
1678
|
return pt.y;
|
1680
1679
|
},
|
1681
1680
|
|
@@ -2908,7 +2908,7 @@ if (CanvasRenderingContext2D) {
|
|
2908
2908
|
});
|
2909
2909
|
}
|
2910
2910
|
}/**
|
2911
|
-
* @license Highcharts JS v4.0.
|
2911
|
+
* @license Highcharts JS v4.0.4 (2014-09-02)
|
2912
2912
|
* CanVGRenderer Extension module
|
2913
2913
|
*
|
2914
2914
|
* (c) 2011-2012 Torstein Honsi, Erik Olsson
|
@@ -32,6 +32,11 @@
|
|
32
32
|
* A comma delimited string to be parsed. Related options are startRow, endRow, startColumn
|
33
33
|
* and endColumn to delimit what part of the table is used. The lineDelimiter and
|
34
34
|
* itemDelimiter options define the CSV delimiter formats.
|
35
|
+
*
|
36
|
+
* - dateFormat: String
|
37
|
+
* Which of the predefined date formats in Date.prototype.dateFormats to use to parse date
|
38
|
+
* columns, for example "dd/mm/YYYY" or "YYYY-mm-dd". Defaults to a best guess based on
|
39
|
+
* what format gives valid dates, and prefers ordered dates.
|
35
40
|
*
|
36
41
|
* - endColumn : Integer
|
37
42
|
* In tabular input data, the first row (indexed by 0) to use. Defaults to the last
|
@@ -60,7 +65,8 @@
|
|
60
65
|
* A callback function to access the parsed columns, the two-dimentional input data
|
61
66
|
* array directly, before they are interpreted into series data and categories. See also
|
62
67
|
* the complete callback, that goes in on a later stage where the raw columns are interpreted
|
63
|
-
* into a Highcharts option structure.
|
68
|
+
* into a Highcharts option structure. Return false to stop completion, or call this.complete()
|
69
|
+
* to continue async.
|
64
70
|
*
|
65
71
|
* - parseDate : Function
|
66
72
|
* A callback function to parse string representations of dates into JavaScript timestamps.
|
@@ -69,6 +75,10 @@
|
|
69
75
|
* - rows : Array<Array<Mixed>>
|
70
76
|
* The same as the columns input option, but defining rows intead of columns.
|
71
77
|
*
|
78
|
+
* - seriesMapping : Array<Object>
|
79
|
+
* An array containing object with Point property names along with what column id the
|
80
|
+
* property should be taken from.
|
81
|
+
*
|
72
82
|
* - startColumn : Integer
|
73
83
|
* In tabular input data, the first column (indexed by 0) to use.
|
74
84
|
*
|
@@ -84,13 +94,23 @@
|
|
84
94
|
* endRow, startColumn and endColumn to delimit what part of the table is used.
|
85
95
|
*/
|
86
96
|
|
97
|
+
/*
|
98
|
+
* TODO:
|
99
|
+
* - Handle various date formats
|
100
|
+
* - http://jsfiddle.net/highcharts/114wejdx/
|
101
|
+
* - http://jsfiddle.net/highcharts/ryv67bkq/
|
102
|
+
*/
|
103
|
+
|
87
104
|
// JSLint options:
|
88
|
-
/*global jQuery */
|
105
|
+
/*global jQuery, HighchartsAdapter */
|
89
106
|
|
90
107
|
(function (Highcharts) { // docs
|
91
108
|
|
92
109
|
// Utilities
|
93
|
-
var each = Highcharts.each
|
110
|
+
var each = Highcharts.each,
|
111
|
+
inArray = HighchartsAdapter.inArray,
|
112
|
+
splat = Highcharts.splat,
|
113
|
+
SeriesBuilder;
|
94
114
|
|
95
115
|
|
96
116
|
// The Data constructor
|
@@ -109,6 +129,12 @@
|
|
109
129
|
this.chartOptions = chartOptions;
|
110
130
|
this.columns = options.columns || this.rowsToColumns(options.rows) || [];
|
111
131
|
|
132
|
+
// This is a two-dimensional array holding the raw, trimmed string values
|
133
|
+
// with the same organisation as the columns array. It makes it possible
|
134
|
+
// for example to revert from interpreted timestamps to string-based
|
135
|
+
// categories.
|
136
|
+
this.rawColumns = [];
|
137
|
+
|
112
138
|
// No need to parse or interpret anything
|
113
139
|
if (this.columns.length) {
|
114
140
|
this.dataFound();
|
@@ -135,19 +161,79 @@
|
|
135
161
|
*/
|
136
162
|
getColumnDistribution: function () {
|
137
163
|
var chartOptions = this.chartOptions,
|
164
|
+
options = this.options,
|
165
|
+
xColumns = [],
|
138
166
|
getValueCount = function (type) {
|
139
167
|
return (Highcharts.seriesTypes[type || 'line'].prototype.pointArrayMap || [0]).length;
|
140
168
|
},
|
169
|
+
getPointArrayMap = function (type) {
|
170
|
+
return Highcharts.seriesTypes[type || 'line'].prototype.pointArrayMap;
|
171
|
+
},
|
141
172
|
globalType = chartOptions && chartOptions.chart && chartOptions.chart.type,
|
142
|
-
individualCounts = []
|
173
|
+
individualCounts = [],
|
174
|
+
seriesBuilders = [],
|
175
|
+
seriesIndex,
|
176
|
+
i;
|
143
177
|
|
144
178
|
each((chartOptions && chartOptions.series) || [], function (series) {
|
145
179
|
individualCounts.push(getValueCount(series.type || globalType));
|
146
180
|
});
|
147
181
|
|
182
|
+
// Collect the x-column indexes from seriesMapping
|
183
|
+
each((options && options.seriesMapping) || [], function (mapping) {
|
184
|
+
xColumns.push(mapping.x || 0);
|
185
|
+
});
|
186
|
+
|
187
|
+
// If there are no defined series with x-columns, use the first column as x column
|
188
|
+
if (xColumns.length === 0) {
|
189
|
+
xColumns.push(0);
|
190
|
+
}
|
191
|
+
|
192
|
+
// Loop all seriesMappings and constructs SeriesBuilders from
|
193
|
+
// the mapping options.
|
194
|
+
each((options && options.seriesMapping) || [], function (mapping) {
|
195
|
+
var builder = new SeriesBuilder(),
|
196
|
+
name,
|
197
|
+
numberOfValueColumnsNeeded = individualCounts[seriesIndex] || getValueCount(globalType),
|
198
|
+
seriesArr = (chartOptions && chartOptions.series) || [],
|
199
|
+
series = seriesArr[seriesIndex] || {},
|
200
|
+
pointArrayMap = getPointArrayMap(series.type || globalType) || ['y'];
|
201
|
+
|
202
|
+
// Add an x reader from the x property or from an undefined column
|
203
|
+
// if the property is not set. It will then be auto populated later.
|
204
|
+
builder.addColumnReader(mapping.x, 'x');
|
205
|
+
|
206
|
+
// Add all column mappings
|
207
|
+
for (name in mapping) {
|
208
|
+
if (mapping.hasOwnProperty(name) && name !== 'x') {
|
209
|
+
builder.addColumnReader(mapping[name], name);
|
210
|
+
}
|
211
|
+
}
|
212
|
+
|
213
|
+
// Add missing columns
|
214
|
+
for (i = 0; i < numberOfValueColumnsNeeded; i++) {
|
215
|
+
if (!builder.hasReader(pointArrayMap[i])) {
|
216
|
+
//builder.addNextColumnReader(pointArrayMap[i]);
|
217
|
+
// Create and add a column reader for the next free column index
|
218
|
+
builder.addColumnReader(undefined, pointArrayMap[i]);
|
219
|
+
}
|
220
|
+
}
|
221
|
+
|
222
|
+
seriesBuilders.push(builder);
|
223
|
+
seriesIndex++;
|
224
|
+
});
|
225
|
+
|
226
|
+
var globalPointArrayMap = getPointArrayMap(globalType);
|
227
|
+
if (globalPointArrayMap === undefined) {
|
228
|
+
globalPointArrayMap = ['y'];
|
229
|
+
}
|
230
|
+
|
148
231
|
this.valueCount = {
|
149
232
|
global: getValueCount(globalType),
|
150
|
-
|
233
|
+
xColumns: xColumns,
|
234
|
+
individual: individualCounts,
|
235
|
+
seriesBuilders: seriesBuilders,
|
236
|
+
globalPointArrayMap: globalPointArrayMap
|
151
237
|
};
|
152
238
|
},
|
153
239
|
|
@@ -161,6 +247,9 @@
|
|
161
247
|
this.columns = this.rowsToColumns(this.columns);
|
162
248
|
}
|
163
249
|
|
250
|
+
// Interpret the info about series and columns
|
251
|
+
this.getColumnDistribution();
|
252
|
+
|
164
253
|
// Interpret the values into right types
|
165
254
|
this.parseTypes();
|
166
255
|
|
@@ -168,10 +257,11 @@
|
|
168
257
|
this.findHeaderRow();
|
169
258
|
|
170
259
|
// Handle columns if a handleColumns callback is given
|
171
|
-
this.parsed()
|
260
|
+
if (this.parsed() !== false) {
|
172
261
|
|
173
|
-
|
174
|
-
|
262
|
+
// Complete if a complete callback is given
|
263
|
+
this.complete();
|
264
|
+
}
|
175
265
|
|
176
266
|
},
|
177
267
|
|
@@ -338,11 +428,11 @@
|
|
338
428
|
findHeaderRow: function () {
|
339
429
|
var headerRow = 0;
|
340
430
|
each(this.columns, function (column) {
|
341
|
-
if (typeof column[0] !== 'string') {
|
431
|
+
if (column.isNumeric && typeof column[0] !== 'string') {
|
342
432
|
headerRow = null;
|
343
433
|
}
|
344
434
|
});
|
345
|
-
this.headerRow =
|
435
|
+
this.headerRow = headerRow;
|
346
436
|
},
|
347
437
|
|
348
438
|
/**
|
@@ -357,22 +447,37 @@
|
|
357
447
|
*/
|
358
448
|
parseTypes: function () {
|
359
449
|
var columns = this.columns,
|
450
|
+
rawColumns = this.rawColumns,
|
360
451
|
col = columns.length,
|
361
452
|
row,
|
362
453
|
val,
|
363
454
|
floatVal,
|
364
455
|
trimVal,
|
365
|
-
|
366
|
-
|
456
|
+
isXColumn,
|
457
|
+
dateVal,
|
458
|
+
descending,
|
459
|
+
backup = [],
|
460
|
+
diff,
|
461
|
+
hasHeaderRow,
|
462
|
+
forceCategory,
|
463
|
+
chartOptions = this.chartOptions;
|
464
|
+
|
367
465
|
while (col--) {
|
368
466
|
row = columns[col].length;
|
467
|
+
rawColumns[col] = [];
|
468
|
+
isXColumn = inArray(col, this.valueCount.xColumns) !== -1;
|
469
|
+
forceCategory = isXColumn && chartOptions && chartOptions.xAxis && splat(chartOptions.xAxis)[0].type === 'category';
|
369
470
|
while (row--) {
|
370
|
-
val = columns[col][row];
|
471
|
+
val = backup[row] || columns[col][row];
|
371
472
|
floatVal = parseFloat(val);
|
372
|
-
trimVal = this.trim(val);
|
473
|
+
trimVal = rawColumns[col][row] = this.trim(val);
|
474
|
+
|
475
|
+
// Disable number or date parsing by setting the X axis type to category
|
476
|
+
if (forceCategory) {
|
477
|
+
columns[col][row] = trimVal;
|
373
478
|
|
374
479
|
/*jslint eqeq: true*/
|
375
|
-
if (trimVal == floatVal) { // is numeric
|
480
|
+
} else if (trimVal == floatVal) { // is numeric
|
376
481
|
/*jslint eqeq: false*/
|
377
482
|
columns[col][row] = floatVal;
|
378
483
|
|
@@ -385,16 +490,56 @@
|
|
385
490
|
|
386
491
|
} else { // string, continue to determine if it is a date string or really a string
|
387
492
|
dateVal = this.parseDate(val);
|
388
|
-
|
389
|
-
if (
|
493
|
+
// Only allow parsing of dates if this column is an x-column
|
494
|
+
if (isXColumn && typeof dateVal === 'number' && !isNaN(dateVal)) { // is date
|
495
|
+
backup[row] = val;
|
390
496
|
columns[col][row] = dateVal;
|
391
497
|
columns[col].isDatetime = true;
|
498
|
+
|
499
|
+
// Check if the dates are uniformly descending or ascending. If they
|
500
|
+
// are not, chances are that they are a different time format, so check
|
501
|
+
// for alternative.
|
502
|
+
if (columns[col][row + 1] !== undefined) {
|
503
|
+
diff = dateVal > columns[col][row + 1];
|
504
|
+
if (diff !== descending && descending !== undefined) {
|
505
|
+
if (this.alternativeFormat) {
|
506
|
+
this.dateFormat = this.alternativeFormat;
|
507
|
+
row = columns[col].length;
|
508
|
+
this.alternativeFormat = this.dateFormats[this.dateFormat].alternative;
|
509
|
+
} else {
|
510
|
+
columns[col].unsorted = true;
|
511
|
+
}
|
512
|
+
}
|
513
|
+
descending = diff;
|
514
|
+
}
|
392
515
|
|
393
516
|
} else { // string
|
394
517
|
columns[col][row] = trimVal === '' ? null : trimVal;
|
518
|
+
if (row !== 0 && (columns[col].isDatetime || columns[col].isNumeric)) {
|
519
|
+
columns[col].mixed = true;
|
520
|
+
}
|
395
521
|
}
|
396
522
|
}
|
397
|
-
|
523
|
+
}
|
524
|
+
|
525
|
+
// If strings are intermixed with numbers or dates in a parsed column, it is an indication
|
526
|
+
// that parsing went wrong or the data was not intended to display as numbers or dates and
|
527
|
+
// parsing is too aggressive. Fall back to categories. Demonstrated in the
|
528
|
+
// highcharts/demo/column-drilldown sample.
|
529
|
+
if (isXColumn && columns[col].mixed) {
|
530
|
+
columns[col] = rawColumns[col];
|
531
|
+
}
|
532
|
+
}
|
533
|
+
|
534
|
+
// If the 0 column is date and descending, reverse all columns.
|
535
|
+
// TODO: probably this should apply to xColumns, not 0 column alone.
|
536
|
+
if (columns[0].isDatetime && descending) {
|
537
|
+
hasHeaderRow = typeof columns[0][0] !== 'number';
|
538
|
+
for (col = 0; col < columns.length; col++) {
|
539
|
+
columns[col].reverse();
|
540
|
+
if (hasHeaderRow) {
|
541
|
+
columns[col].unshift(columns[col].pop());
|
542
|
+
}
|
398
543
|
}
|
399
544
|
}
|
400
545
|
},
|
@@ -405,10 +550,37 @@
|
|
405
550
|
*/
|
406
551
|
dateFormats: {
|
407
552
|
'YYYY-mm-dd': {
|
408
|
-
regex:
|
553
|
+
regex: /^([0-9]{4})[\-\/\.]([0-9]{2})[\-\/\.]([0-9]{2})$/,
|
409
554
|
parser: function (match) {
|
410
555
|
return Date.UTC(+match[1], match[2] - 1, +match[3]);
|
411
556
|
}
|
557
|
+
},
|
558
|
+
'dd/mm/YYYY': {
|
559
|
+
regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{4})$/,
|
560
|
+
parser: function (match) {
|
561
|
+
return Date.UTC(+match[3], match[2] - 1, +match[1]);
|
562
|
+
},
|
563
|
+
alternative: 'mm/dd/YYYY' // different format with the same regex
|
564
|
+
},
|
565
|
+
'mm/dd/YYYY': {
|
566
|
+
regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{4})$/,
|
567
|
+
parser: function (match) {
|
568
|
+
return Date.UTC(+match[3], match[1] - 1, +match[2]);
|
569
|
+
}
|
570
|
+
},
|
571
|
+
'dd/mm/YY': {
|
572
|
+
regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{2})$/,
|
573
|
+
parser: function (match) {
|
574
|
+
return Date.UTC(+match[3] + 2000, match[2] - 1, +match[1]);
|
575
|
+
},
|
576
|
+
alternative: 'mm/dd/YY' // different format with the same regex
|
577
|
+
},
|
578
|
+
'mm/dd/YY': {
|
579
|
+
regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{2})$/,
|
580
|
+
parser: function (match) {
|
581
|
+
console.log(match)
|
582
|
+
return Date.UTC(+match[3] + 2000, match[1] - 1, +match[2]);
|
583
|
+
}
|
412
584
|
}
|
413
585
|
},
|
414
586
|
|
@@ -420,20 +592,47 @@
|
|
420
592
|
ret,
|
421
593
|
key,
|
422
594
|
format,
|
595
|
+
dateFormat = this.options.dateFormat || this.dateFormat,
|
423
596
|
match;
|
424
597
|
|
425
598
|
if (parseDate) {
|
426
599
|
ret = parseDate(val);
|
427
600
|
}
|
428
|
-
|
601
|
+
|
429
602
|
if (typeof val === 'string') {
|
430
|
-
|
431
|
-
|
603
|
+
// Auto-detect the date format the first time
|
604
|
+
if (!dateFormat) {
|
605
|
+
for (key in this.dateFormats) {
|
606
|
+
format = this.dateFormats[key];
|
607
|
+
match = val.match(format.regex);
|
608
|
+
if (match) {
|
609
|
+
this.dateFormat = dateFormat = key;
|
610
|
+
this.alternativeFormat = format.alternative;
|
611
|
+
ret = format.parser(match);
|
612
|
+
break;
|
613
|
+
}
|
614
|
+
}
|
615
|
+
// Next time, use the one previously found
|
616
|
+
} else {
|
617
|
+
format = this.dateFormats[dateFormat];
|
432
618
|
match = val.match(format.regex);
|
433
619
|
if (match) {
|
434
620
|
ret = format.parser(match);
|
435
621
|
}
|
436
622
|
}
|
623
|
+
// Fall back to Date.parse
|
624
|
+
if (!match) {
|
625
|
+
match = Date.parse(val);
|
626
|
+
// External tools like Date.js and MooTools extend Date object and
|
627
|
+
// returns a date.
|
628
|
+
if (typeof match === 'object' && match !== null && match.getTime) {
|
629
|
+
ret = match.getTime() - match.getTimezoneOffset() * 60000;
|
630
|
+
|
631
|
+
// Timestamp
|
632
|
+
} else if (typeof match === 'number' && !isNaN(match)) {
|
633
|
+
ret = match - (new Date(match)).getTimezoneOffset() * 60000;
|
634
|
+
}
|
635
|
+
}
|
437
636
|
}
|
438
637
|
return ret;
|
439
638
|
},
|
@@ -469,9 +668,40 @@
|
|
469
668
|
*/
|
470
669
|
parsed: function () {
|
471
670
|
if (this.options.parsed) {
|
472
|
-
this.options.parsed.call(this, this.columns);
|
671
|
+
return this.options.parsed.call(this, this.columns);
|
473
672
|
}
|
474
673
|
},
|
674
|
+
|
675
|
+
getFreeIndexes: function (numberOfColumns, seriesBuilders) {
|
676
|
+
var s,
|
677
|
+
i,
|
678
|
+
freeIndexes = [],
|
679
|
+
freeIndexValues = [],
|
680
|
+
referencedIndexes;
|
681
|
+
|
682
|
+
// Add all columns as free
|
683
|
+
for (i = 0; i < numberOfColumns; i = i + 1) {
|
684
|
+
freeIndexes.push(true);
|
685
|
+
}
|
686
|
+
|
687
|
+
// Loop all defined builders and remove their referenced columns
|
688
|
+
for (s = 0; s < seriesBuilders.length; s = s + 1) {
|
689
|
+
referencedIndexes = seriesBuilders[s].getReferencedColumnIndexes();
|
690
|
+
|
691
|
+
for (i = 0; i < referencedIndexes.length; i = i + 1) {
|
692
|
+
freeIndexes[referencedIndexes[i]] = false;
|
693
|
+
}
|
694
|
+
}
|
695
|
+
|
696
|
+
// Collect the values for the free indexes
|
697
|
+
for (i = 0; i < freeIndexes.length; i = i + 1) {
|
698
|
+
if (freeIndexes[i]) {
|
699
|
+
freeIndexValues.push(i);
|
700
|
+
}
|
701
|
+
}
|
702
|
+
|
703
|
+
return freeIndexValues;
|
704
|
+
},
|
475
705
|
|
476
706
|
/**
|
477
707
|
* If a complete callback function is provided in the options, interpret the
|
@@ -480,36 +710,24 @@
|
|
480
710
|
complete: function () {
|
481
711
|
|
482
712
|
var columns = this.columns,
|
483
|
-
|
713
|
+
xColumns = [],
|
484
714
|
type,
|
485
715
|
options = this.options,
|
486
|
-
valueCount,
|
487
716
|
series,
|
488
717
|
data,
|
489
718
|
i,
|
490
719
|
j,
|
720
|
+
r,
|
491
721
|
seriesIndex,
|
492
|
-
chartOptions
|
493
|
-
|
494
|
-
|
495
|
-
|
722
|
+
chartOptions,
|
723
|
+
allSeriesBuilders = [],
|
724
|
+
builder,
|
725
|
+
freeIndexes,
|
726
|
+
typeCol,
|
727
|
+
index;
|
496
728
|
|
497
|
-
|
498
|
-
|
499
|
-
// Use first column for X data or categories?
|
500
|
-
if (columns.length > 1) {
|
501
|
-
firstCol = columns.shift();
|
502
|
-
if (this.headerRow === 0) {
|
503
|
-
firstCol.shift(); // remove the first cell
|
504
|
-
}
|
505
|
-
|
506
|
-
|
507
|
-
if (firstCol.isDatetime) {
|
508
|
-
type = 'datetime';
|
509
|
-
} else if (!firstCol.isNumeric) {
|
510
|
-
type = 'category';
|
511
|
-
}
|
512
|
-
}
|
729
|
+
xColumns.length = columns.length;
|
730
|
+
if (options.complete || options.afterComplete) {
|
513
731
|
|
514
732
|
// Get the names and shift the top row
|
515
733
|
for (i = 0; i < columns.length; i++) {
|
@@ -520,46 +738,84 @@
|
|
520
738
|
|
521
739
|
// Use the next columns for series
|
522
740
|
series = [];
|
523
|
-
|
741
|
+
freeIndexes = this.getFreeIndexes(columns.length, this.valueCount.seriesBuilders);
|
742
|
+
|
743
|
+
// Populate defined series
|
744
|
+
for (seriesIndex = 0; seriesIndex < this.valueCount.seriesBuilders.length; seriesIndex++) {
|
745
|
+
builder = this.valueCount.seriesBuilders[seriesIndex];
|
524
746
|
|
525
|
-
//
|
526
|
-
|
747
|
+
// If the builder can be populated with remaining columns, then add it to allBuilders
|
748
|
+
if (builder.populateColumns(freeIndexes)) {
|
749
|
+
allSeriesBuilders.push(builder);
|
750
|
+
}
|
751
|
+
}
|
752
|
+
|
753
|
+
// Populate dynamic series
|
754
|
+
while (freeIndexes.length > 0) {
|
755
|
+
builder = new SeriesBuilder();
|
756
|
+
builder.addColumnReader(0, 'x');
|
527
757
|
|
528
|
-
//
|
529
|
-
|
758
|
+
// Mark index as used (not free)
|
759
|
+
index = inArray(0, freeIndexes);
|
760
|
+
if (index !== -1) {
|
761
|
+
freeIndexes.splice(index, 1);
|
762
|
+
}
|
530
763
|
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
764
|
+
for (i = 0; i < this.valueCount.global; i++) {
|
765
|
+
// Create and add a column reader for the next free column index
|
766
|
+
builder.addColumnReader(undefined, this.valueCount.globalPointArrayMap[i]);
|
767
|
+
}
|
768
|
+
|
769
|
+
// If the builder can be populated with remaining columns, then add it to allBuilders
|
770
|
+
if (builder.populateColumns(freeIndexes)) {
|
771
|
+
allSeriesBuilders.push(builder);
|
772
|
+
}
|
773
|
+
}
|
774
|
+
|
775
|
+
// Get the data-type from the first series x column
|
776
|
+
if (allSeriesBuilders.length > 0 && allSeriesBuilders[0].readers.length > 0) {
|
777
|
+
typeCol = columns[allSeriesBuilders[0].readers[0].columnIndex];
|
778
|
+
if (typeCol !== undefined) {
|
779
|
+
if (typeCol.isDatetime) {
|
780
|
+
type = 'datetime';
|
781
|
+
} else if (!typeCol.isNumeric) {
|
782
|
+
type = 'category';
|
783
|
+
}
|
784
|
+
}
|
785
|
+
}
|
786
|
+
// Axis type is category, then the "x" column should be called "name"
|
787
|
+
if (type === 'category') {
|
788
|
+
for (seriesIndex = 0; seriesIndex < allSeriesBuilders.length; seriesIndex++) {
|
789
|
+
builder = allSeriesBuilders[seriesIndex];
|
790
|
+
for (r = 0; r < builder.readers.length; r++) {
|
791
|
+
if (builder.readers[r].configName === 'x') {
|
792
|
+
builder.readers[r].configName = 'name';
|
550
793
|
}
|
551
794
|
}
|
552
795
|
}
|
796
|
+
}
|
797
|
+
|
798
|
+
// Read data for all builders
|
799
|
+
for (seriesIndex = 0; seriesIndex < allSeriesBuilders.length; seriesIndex++) {
|
800
|
+
builder = allSeriesBuilders[seriesIndex];
|
801
|
+
|
802
|
+
// Iterate down the cells of each column and add data to the series
|
803
|
+
data = [];
|
804
|
+
for (j = 0; j < columns[0].length; j++) { // TODO: which column's length should we use here
|
805
|
+
data[j] = builder.read(columns, j);
|
806
|
+
}
|
553
807
|
|
554
808
|
// Add the series
|
555
809
|
series[seriesIndex] = {
|
556
|
-
name: columns[i].name,
|
557
810
|
data: data
|
558
811
|
};
|
559
|
-
|
560
|
-
|
812
|
+
if (builder.name) {
|
813
|
+
series[seriesIndex].name = builder.name;
|
814
|
+
}
|
561
815
|
}
|
562
|
-
|
816
|
+
|
817
|
+
|
818
|
+
|
563
819
|
// Do the callback
|
564
820
|
chartOptions = {
|
565
821
|
xAxis: {
|
@@ -570,6 +826,7 @@
|
|
570
826
|
if (options.complete) {
|
571
827
|
options.complete(chartOptions);
|
572
828
|
}
|
829
|
+
|
573
830
|
// The afterComplete hook is used internally to avoid conflict with the externally
|
574
831
|
// available complete option.
|
575
832
|
if (options.afterComplete) {
|
@@ -592,6 +849,7 @@
|
|
592
849
|
|
593
850
|
if (userOptions && userOptions.data) {
|
594
851
|
Highcharts.data(Highcharts.extend(userOptions.data, {
|
852
|
+
|
595
853
|
afterComplete: function (dataOptions) {
|
596
854
|
var i, series;
|
597
855
|
|
@@ -619,4 +877,149 @@
|
|
619
877
|
}
|
620
878
|
});
|
621
879
|
|
880
|
+
/**
|
881
|
+
* Creates a new SeriesBuilder. A SeriesBuilder consists of a number
|
882
|
+
* of ColumnReaders that reads columns and give them a name.
|
883
|
+
* Ex: A series builder can be constructed to read column 3 as 'x' and
|
884
|
+
* column 7 and 8 as 'y1' and 'y2'.
|
885
|
+
* The output would then be points/rows of the form {x: 11, y1: 22, y2: 33}
|
886
|
+
*
|
887
|
+
* The name of the builder is taken from the second column. In the above
|
888
|
+
* example it would be the column with index 7.
|
889
|
+
* @constructor
|
890
|
+
*/
|
891
|
+
SeriesBuilder = function () {
|
892
|
+
this.readers = [];
|
893
|
+
this.pointIsArray = true;
|
894
|
+
};
|
895
|
+
|
896
|
+
/**
|
897
|
+
* Populates readers with column indexes. A reader can be added without
|
898
|
+
* a specific index and for those readers the index is taken sequentially
|
899
|
+
* from the free columns (this is handled by the ColumnCursor instance).
|
900
|
+
* @returns {boolean}
|
901
|
+
*/
|
902
|
+
SeriesBuilder.prototype.populateColumns = function (freeIndexes) {
|
903
|
+
var builder = this,
|
904
|
+
enoughColumns = true;
|
905
|
+
|
906
|
+
// Loop each reader and give it an index if its missing.
|
907
|
+
// The freeIndexes.shift() will return undefined if there
|
908
|
+
// are no more columns.
|
909
|
+
each(builder.readers, function (reader) {
|
910
|
+
if (reader.columnIndex === undefined) {
|
911
|
+
reader.columnIndex = freeIndexes.shift();
|
912
|
+
}
|
913
|
+
});
|
914
|
+
|
915
|
+
// Now, all readers should have columns mapped. If not
|
916
|
+
// then return false to signal that this series should
|
917
|
+
// not be added.
|
918
|
+
each(builder.readers, function (reader) {
|
919
|
+
if (reader.columnIndex === undefined) {
|
920
|
+
enoughColumns = false;
|
921
|
+
}
|
922
|
+
});
|
923
|
+
|
924
|
+
return enoughColumns;
|
925
|
+
};
|
926
|
+
|
927
|
+
/**
|
928
|
+
* Reads a row from the dataset and returns a point or array depending
|
929
|
+
* on the names of the readers.
|
930
|
+
* @param columns
|
931
|
+
* @param rowIndex
|
932
|
+
* @returns {Array | Object}
|
933
|
+
*/
|
934
|
+
SeriesBuilder.prototype.read = function (columns, rowIndex) {
|
935
|
+
var builder = this,
|
936
|
+
pointIsArray = builder.pointIsArray,
|
937
|
+
point = pointIsArray ? [] : {},
|
938
|
+
columnIndexes;
|
939
|
+
|
940
|
+
// Loop each reader and ask it to read its value.
|
941
|
+
// Then, build an array or point based on the readers names.
|
942
|
+
each(builder.readers, function (reader) {
|
943
|
+
var value = columns[reader.columnIndex][rowIndex];
|
944
|
+
if (pointIsArray) {
|
945
|
+
point.push(value);
|
946
|
+
} else {
|
947
|
+
point[reader.configName] = value;
|
948
|
+
}
|
949
|
+
});
|
950
|
+
|
951
|
+
// The name comes from the first column (excluding the x column)
|
952
|
+
if (this.name === undefined && builder.readers.length >= 2) {
|
953
|
+
columnIndexes = builder.getReferencedColumnIndexes();
|
954
|
+
if (columnIndexes.length >= 2) {
|
955
|
+
// remove the first one (x col)
|
956
|
+
columnIndexes.shift();
|
957
|
+
|
958
|
+
// Sort the remaining
|
959
|
+
columnIndexes.sort();
|
960
|
+
|
961
|
+
// Now use the lowest index as name column
|
962
|
+
this.name = columns[columnIndexes.shift()].name;
|
963
|
+
}
|
964
|
+
}
|
965
|
+
|
966
|
+
return point;
|
967
|
+
};
|
968
|
+
|
969
|
+
/**
|
970
|
+
* Creates and adds ColumnReader from the given columnIndex and configName.
|
971
|
+
* ColumnIndex can be undefined and in that case the reader will be given
|
972
|
+
* an index when columns are populated.
|
973
|
+
* @param columnIndex {Number | undefined}
|
974
|
+
* @param configName
|
975
|
+
*/
|
976
|
+
SeriesBuilder.prototype.addColumnReader = function (columnIndex, configName) {
|
977
|
+
this.readers.push({
|
978
|
+
columnIndex: columnIndex,
|
979
|
+
configName: configName
|
980
|
+
});
|
981
|
+
|
982
|
+
if (!(configName === 'x' || configName === 'y' || configName === undefined)) {
|
983
|
+
this.pointIsArray = false;
|
984
|
+
}
|
985
|
+
};
|
986
|
+
|
987
|
+
/**
|
988
|
+
* Returns an array of column indexes that the builder will use when
|
989
|
+
* reading data.
|
990
|
+
* @returns {Array}
|
991
|
+
*/
|
992
|
+
SeriesBuilder.prototype.getReferencedColumnIndexes = function () {
|
993
|
+
var i,
|
994
|
+
referencedColumnIndexes = [],
|
995
|
+
columnReader;
|
996
|
+
|
997
|
+
for (i = 0; i < this.readers.length; i = i + 1) {
|
998
|
+
columnReader = this.readers[i];
|
999
|
+
if (columnReader.columnIndex !== undefined) {
|
1000
|
+
referencedColumnIndexes.push(columnReader.columnIndex);
|
1001
|
+
}
|
1002
|
+
}
|
1003
|
+
|
1004
|
+
return referencedColumnIndexes;
|
1005
|
+
};
|
1006
|
+
|
1007
|
+
/**
|
1008
|
+
* Returns true if the builder has a reader for the given configName.
|
1009
|
+
* @param configName
|
1010
|
+
* @returns {boolean}
|
1011
|
+
*/
|
1012
|
+
SeriesBuilder.prototype.hasReader = function (configName) {
|
1013
|
+
var i, columnReader;
|
1014
|
+
for (i = 0; i < this.readers.length; i = i + 1) {
|
1015
|
+
columnReader = this.readers[i];
|
1016
|
+
if (columnReader.configName === configName) {
|
1017
|
+
return true;
|
1018
|
+
}
|
1019
|
+
}
|
1020
|
+
// Else return undefined
|
1021
|
+
};
|
1022
|
+
|
1023
|
+
|
1024
|
+
|
622
1025
|
}(Highcharts));
|