highcharts-rails 4.1.9 → 4.1.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- /*global document, Highcharts, HighchartsAdapter, setTimeout */
46
- (function (H, HA) {
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 () { return undefined; },
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 = HA.addEvent,
57
- fireEvent = HA.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, callback, chunkSize, i) {
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, callback, chunkSize, i + chunkSize);
77
+ eachAsync(arr, fn, finalFunc, chunkSize, i + chunkSize);
71
78
  });
72
- } else if (callback) {
73
- callback();
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
- for (i = 0; i < points.length; i = i + 1) {
165
- point = points[i];
166
- if (point && point.graphic) {
167
- point.graphic = point.graphic.destroy();
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 width = this.chart.plotWidth,
184
- height = this.chart.plotHeight;
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 = this.chart.renderer.image('', 0, 0, width, height).add(this.group);
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
- this.ctx.clearRect(0, 0, width, height);
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 this.ctx;
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
- // TODO: Replace this with a single property, and replace when zooming in
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
- var point = proceed.apply(this, [].slice.call(arguments, 1)),
543
- ret = point;
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
- }(Highcharts, HighchartsAdapter));
591
+ }));
@@ -1,20 +1,25 @@
1
1
  /**
2
- * Highcharts JS v4.1.9 (2015-10-07)
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
- /*global HighchartsAdapter*/
10
- (function (H) {
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
- "use strict";
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 = HighchartsAdapter.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
- return (a.move === 'in' ? 0 : 1) - (b.move === 'in' ? 0 : 1);
217
+ ret = (a.move === 'in' ? 0 : 1) - (b.move === 'in' ? 0 : 1);
212
218
  } else {
213
- return a.value - b.value;
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
- wrap(H.seriesTypes.column.prototype, 'drawPoints', function (proceed) {
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
- yAxis = series.yAxis,
286
- breaks = yAxis.breakArray || [],
287
- threshold = pick(this.options.threshold, yAxis.min),
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
- for (i = 0; i < points.length; i++) {
296
- point = points[i];
297
- y = point.stackY || point.y;
298
- for (j = 0; j < breaks.length; j++) {
299
- brk = breaks[j];
300
- eventName = false;
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
- if ((threshold < brk.from && y > brk.to) || (threshold > brk.from && y < brk.from)) {
311
+ if ((threshold < brk.from && y > brk.to) || (threshold > brk.from && y < brk.from)) {
303
312
  eventName = 'pointBreak';
304
- } 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
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
- if (eventName) {
308
- fireEvent(yAxis, eventName, {point: point, brk: brk});
309
- }
310
- }
311
- }
315
+ }
316
+ if (eventName) {
317
+ fireEvent(axis, eventName, { point: point, brk: brk });
318
+ }
319
+ });
320
+ });
321
+ });
322
+ };
312
323
 
313
- });
314
- }(Highcharts));
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.9 (2015-10-07)
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
- // JSLint options:
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.apply(this, [container, chartWidth, chartHeight]);
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(tooltipDiv.offsetWidth, tooltipDiv.offsetHeight, {plotX: args.x, plotY: args.y});
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.9 (2015-10-07)
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
- // JSLint options:
11
- /*global jQuery, HighchartsAdapter */
12
-
13
- (function (Highcharts) {
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 = HighchartsAdapter.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
- * Initialize the Data object with the given options
33
- */
34
- init: function (options, chartOptions) {
35
- this.options = options;
36
- this.chartOptions = chartOptions;
37
- this.columns = options.columns || this.rowsToColumns(options.rows) || [];
38
- this.firstRowAsNames = pick(options.firstRowAsNames, true);
39
- this.decimalRegex = options.decimalPoint && new RegExp('^([0-9]+)' + options.decimalPoint + '([0-9]+)$');
40
-
41
- // This is a two-dimensional array holding the raw, trimmed string values
42
- // with the same organisation as the columns array. It makes it possible
43
- // for example to revert from interpreted timestamps to string-based
44
- // categories.
45
- this.rawColumns = [];
46
-
47
- // No need to parse or interpret anything
48
- if (this.columns.length) {
49
- this.dataFound();
50
-
51
- // Parse and interpret
52
- } else {
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
- // Parse a CSV string if options.csv is given
55
- this.parseCSV();
56
-
57
- // Parse a HTML table if options.table is given
58
- this.parseTable();
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
- // Parse a Google Spreadsheet
61
- this.parseGoogleSpreadsheet();
62
- }
64
+ // Parse a Google Spreadsheet
65
+ this.parseGoogleSpreadsheet();
66
+ }
63
67
 
64
- },
68
+ },
65
69
 
66
- /**
67
- * Get the column distribution. For example, a line series takes a single column for
68
- * Y values. A range series takes two columns for low and high values respectively,
69
- * and an OHLC series takes four columns.
70
- */
71
- getColumnDistribution: function () {
72
- var chartOptions = this.chartOptions,
73
- options = this.options,
74
- xColumns = [],
75
- getValueCount = function (type) {
76
- return (Highcharts.seriesTypes[type || 'line'].prototype.pointArrayMap || [0]).length;
77
- },
78
- getPointArrayMap = function (type) {
79
- return Highcharts.seriesTypes[type || 'line'].prototype.pointArrayMap;
80
- },
81
- globalType = chartOptions && chartOptions.chart && chartOptions.chart.type,
82
- individualCounts = [],
83
- seriesBuilders = [],
84
- seriesIndex = 0,
85
- i;
86
-
87
- each((chartOptions && chartOptions.series) || [], function (series) {
88
- individualCounts.push(getValueCount(series.type || globalType));
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
- // Collect the x-column indexes from seriesMapping
92
- each((options && options.seriesMapping) || [], function (mapping) {
93
- xColumns.push(mapping.x || 0);
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
- // If there are no defined series with x-columns, use the first column as x column
97
- if (xColumns.length === 0) {
98
- xColumns.push(0);
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
- // Loop all seriesMappings and constructs SeriesBuilders from
102
- // the mapping options.
103
- each((options && options.seriesMapping) || [], function (mapping) {
104
- var builder = new SeriesBuilder(),
105
- name,
106
- numberOfValueColumnsNeeded = individualCounts[seriesIndex] || getValueCount(globalType),
107
- seriesArr = (chartOptions && chartOptions.series) || [],
108
- series = seriesArr[seriesIndex] || {},
109
- pointArrayMap = getPointArrayMap(series.type || globalType) || ['y'];
110
-
111
- // Add an x reader from the x property or from an undefined column
112
- // if the property is not set. It will then be auto populated later.
113
- builder.addColumnReader(mapping.x, 'x');
114
-
115
- // Add all column mappings
116
- for (name in mapping) {
117
- if (mapping.hasOwnProperty(name) && name !== 'x') {
118
- builder.addColumnReader(mapping[name], name);
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
- // Add missing columns
123
- for (i = 0; i < numberOfValueColumnsNeeded; i++) {
124
- if (!builder.hasReader(pointArrayMap[i])) {
125
- //builder.addNextColumnReader(pointArrayMap[i]);
126
- // Create and add a column reader for the next free column index
127
- builder.addColumnReader(undefined, pointArrayMap[i]);
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
- seriesBuilders.push(builder);
132
- seriesIndex++;
133
- });
135
+ seriesBuilders.push(builder);
136
+ seriesIndex++;
137
+ });
134
138
 
135
- var globalPointArrayMap = getPointArrayMap(globalType);
136
- if (globalPointArrayMap === undefined) {
137
- globalPointArrayMap = ['y'];
138
- }
139
+ var globalPointArrayMap = getPointArrayMap(globalType);
140
+ if (globalPointArrayMap === undefined) {
141
+ globalPointArrayMap = ['y'];
142
+ }
139
143
 
140
- this.valueCount = {
141
- global: getValueCount(globalType),
142
- xColumns: xColumns,
143
- individual: individualCounts,
144
- seriesBuilders: seriesBuilders,
145
- globalPointArrayMap: globalPointArrayMap
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
- * When the data is parsed into columns, either by CSV, table, GS or direct input,
151
- * continue with other operations.
152
- */
153
- dataFound: function () {
154
-
155
- if (this.options.switchRowsAndColumns) {
156
- this.columns = this.rowsToColumns(this.columns);
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
- // Interpret the info about series and columns
160
- this.getColumnDistribution();
163
+ // Interpret the info about series and columns
164
+ this.getColumnDistribution();
161
165
 
162
- // Interpret the values into right types
163
- this.parseTypes();
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
- if (csv) {
169
+ // Handle columns if a handleColumns callback is given
170
+ if (this.parsed() !== false) {
191
171
 
192
- lines = csv
193
- .replace(/\r\n/g, "\n") // Unix
194
- .replace(/\r/g, "\n") // Mac
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
- each(lines, function (line, rowNo) {
200
- var trimmed = self.trim(line),
201
- isComment = trimmed.indexOf('#') === 0,
202
- isBlank = trimmed === '',
203
- items;
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
- if (rowNo >= startRow && rowNo <= endRow && !isComment && !isBlank) {
206
- items = line.split(itemDelimiter);
207
- each(items, function (item, colNo) {
208
- if (colNo >= startColumn && colNo <= endColumn) {
209
- if (!columns[colNo - startColumn]) {
210
- columns[colNo - startColumn] = [];
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
- columns[colNo - startColumn][activeRowNo] = item;
214
- }
215
- });
216
- activeRowNo += 1;
217
- }
218
- });
219
+ });
220
+ activeRowNo += 1;
221
+ }
222
+ });
219
223
 
220
- this.dataFound();
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
- each(table.getElementsByTagName('tr'), function (tr, rowNo) {
243
- if (rowNo >= startRow && rowNo <= endRow) {
244
- each(tr.children, function (item, colNo) {
245
- if ((item.tagName === 'TD' || item.tagName === 'TH') && colNo >= startColumn && colNo <= endColumn) {
246
- if (!columns[colNo - startColumn]) {
247
- columns[colNo - startColumn] = [];
248
- }
249
-
250
- columns[colNo - startColumn][rowNo - startRow] = item.innerHTML;
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
- // First, find the total number of columns and rows that
291
- // are actually filled with data
292
- for (i = 0; i < cellCount; i++) {
293
- cell = cells[i];
294
- colCount = Math.max(colCount, cell.gs$cell.col);
295
- rowCount = Math.max(rowCount, cell.gs$cell.row);
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
- // Set up arrays containing the column data
299
- for (i = 0; i < colCount; i++) {
300
- if (i >= startColumn && i <= endColumn) {
301
- // Create new columns with the length of either end-start or rowCount
302
- columns[i - startColumn] = [];
303
-
304
- // Setting the length to avoid jslint warning
305
- columns[i - startColumn].length = Math.min(rowCount, endRow - startRow);
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
- // Loop over the cells and assign the value to the right
310
- // place in the column arrays
311
- for (i = 0; i < cellCount; i++) {
312
- cell = cells[i];
313
- gr = cell.gs$cell.row - 1; // rows start at 1
314
- gc = cell.gs$cell.col - 1; // columns start at 1
315
-
316
- // If both row and col falls inside start and end
317
- // set the transposed cell value in the newly created columns
318
- if (gc >= startColumn && gc <= endColumn &&
319
- gr >= startRow && gr <= endRow) {
320
- columns[gc - startColumn][gr - startRow] = cell.content.$t;
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
- self.dataFound();
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
- // Clear white space insdie the string, like thousands separators
337
- if (inside && /^[0-9\s]+$/.test(str)) {
338
- str = str.replace(/\s/g, '');
345
+ if (this.decimalRegex) {
346
+ str = str.replace(this.decimalRegex, '$1.$2');
347
+ }
339
348
  }
340
-
341
- if (this.decimalRegex) {
342
- str = str.replace(this.decimalRegex, '$1.$2');
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
- * Parse a single column. Set properties like .isDatetime and .isNumeric.
363
- */
364
- parseColumn: function (column, col) {
365
- var rawColumns = this.rawColumns,
366
- columns = this.columns,
367
- row = column.length,
368
- val,
369
- floatVal,
370
- trimVal,
371
- trimInsideVal,
372
- firstRowAsNames = this.firstRowAsNames,
373
- isXColumn = inArray(col, this.valueCount.xColumns) !== -1,
374
- dateVal,
375
- backup = [],
376
- diff,
377
- chartOptions = this.chartOptions,
378
- descending,
379
- columnTypes = this.options.columnTypes || [],
380
- columnType = columnTypes[col],
381
- forceCategory = isXColumn && ((chartOptions && chartOptions.xAxis && splat(chartOptions.xAxis)[0].type === 'category') || columnType === 'string');
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
- trimVal = this.trim(val);
390
- trimInsideVal = this.trim(val, true);
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
- // Disable number or date parsing by setting the X axis type to category
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
- // If the number is greater than milliseconds in a year, assume datetime
407
- if (floatVal > 365 * 24 * 3600 * 1000 && columnType !== 'float') {
408
- column.isDatetime = true;
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
- if (column[row + 1] !== undefined) {
414
- descending = floatVal > column[row + 1];
397
+ // Set it the first time
398
+ if (rawColumns[col][row] === undefined) {
399
+ rawColumns[col][row] = trimVal;
415
400
  }
416
-
417
- // String, continue to determine if it is a date string or really a string
418
- } else {
419
- dateVal = this.parseDate(val);
420
- // Only allow parsing of dates if this column is an x-column
421
- if (isXColumn && typeof dateVal === 'number' && !isNaN(dateVal) && columnType !== 'float') { // is date
422
- backup[row] = val;
423
- column[row] = dateVal;
424
- column.isDatetime = true;
425
-
426
- // Check if the dates are uniformly descending or ascending. If they
427
- // are not, chances are that they are a different time format, so check
428
- // for alternative.
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
- diff = dateVal > column[row + 1];
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
- } else { // string
444
- column[row] = trimVal === '' ? null : trimVal;
445
- if (row !== 0 && (column.isDatetime || column.isNumeric)) {
446
- column.mixed = true;
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
- // If strings are intermixed with numbers or dates in a parsed column, it is an indication
453
- // that parsing went wrong or the data was not intended to display as numbers or dates and
454
- // parsing is too aggressive. Fall back to categories. Demonstrated in the
455
- // highcharts/demo/column-drilldown sample.
456
- if (isXColumn && column.mixed) {
457
- columns[col] = rawColumns[col];
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
- // If the 0 column is date or number and descending, reverse all columns.
461
- if (isXColumn && descending && this.options.sort) {
462
- for (col = 0; col < columns.length; col++) {
463
- columns[col].reverse();
464
- if (firstRowAsNames) {
465
- columns[col].unshift(columns[col].pop());
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
- 'dd/mm/YYYY': {
483
- regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{4})$/,
484
- parser: function (match) {
485
- return Date.UTC(+match[3], match[2] - 1, +match[1]);
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
- alternative: 'mm/dd/YYYY' // different format with the same regex
488
- },
489
- 'mm/dd/YYYY': {
490
- regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{4})$/,
491
- parser: function (match) {
492
- return Date.UTC(+match[3], match[1] - 1, +match[2]);
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
- alternative: 'mm/dd/YY' // different format with the same regex
501
- },
502
- 'mm/dd/YY': {
503
- regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{2})$/,
504
- parser: function (match) {
505
- return Date.UTC(+match[3] + 2000, match[1] - 1, +match[2]);
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
- } else if (typeof val === 'string') {
525
- // Auto-detect the date format the first time
526
- if (!dateFormat) {
527
- for (key in this.dateFormats) {
528
- format = this.dateFormats[key];
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
- // Next time, use the one previously found
538
- } else {
539
- format = this.dateFormats[dateFormat];
540
- match = val.match(format.regex);
541
- if (match) {
542
- ret = format.parser(match);
543
- }
544
- }
545
- // Fall back to Date.parse
546
- if (!match) {
547
- match = Date.parse(val);
548
- // External tools like Date.js and MooTools extend Date object and
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
- return ret;
560
- },
561
-
562
- /**
563
- * Reorganize rows into columns
564
- */
565
- rowsToColumns: function (rows) {
566
- var row,
567
- rowsLength,
568
- col,
569
- colsLength,
570
- columns;
571
-
572
- if (rows) {
573
- columns = [];
574
- rowsLength = rows.length;
575
- for (row = 0; row < rowsLength; row++) {
576
- colsLength = rows[row].length;
577
- for (col = 0; col < colsLength; col++) {
578
- if (!columns[col]) {
579
- columns[col] = [];
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
- return columns;
586
- },
587
-
588
- /**
589
- * A hook for working directly on the parsed columns
590
- */
591
- parsed: function () {
592
- if (this.options.parsed) {
593
- return this.options.parsed.call(this, this.columns);
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
- // Loop all defined builders and remove their referenced columns
610
- for (s = 0; s < seriesBuilders.length; s = s + 1) {
611
- referencedIndexes = seriesBuilders[s].getReferencedColumnIndexes();
601
+ getFreeIndexes: function (numberOfColumns, seriesBuilders) {
602
+ var s,
603
+ i,
604
+ freeIndexes = [],
605
+ freeIndexValues = [],
606
+ referencedIndexes;
612
607
 
613
- for (i = 0; i < referencedIndexes.length; i = i + 1) {
614
- freeIndexes[referencedIndexes[i]] = false;
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
- // Collect the values for the free indexes
619
- for (i = 0; i < freeIndexes.length; i = i + 1) {
620
- if (freeIndexes[i]) {
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
- return freeIndexValues;
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
- // Populate defined series
666
- for (seriesIndex = 0; seriesIndex < this.valueCount.seriesBuilders.length; seriesIndex++) {
667
- builder = this.valueCount.seriesBuilders[seriesIndex];
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
- // Populate dynamic series
676
- while (freeIndexes.length > 0) {
677
- builder = new SeriesBuilder();
678
- builder.addColumnReader(0, 'x');
679
-
680
- // Mark index as used (not free)
681
- index = inArray(0, freeIndexes);
682
- if (index !== -1) {
683
- freeIndexes.splice(index, 1);
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
- for (i = 0; i < this.valueCount.global; i++) {
687
- // Create and add a column reader for the next free column index
688
- builder.addColumnReader(undefined, this.valueCount.globalPointArrayMap[i]);
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
- // If the builder can be populated with remaining columns, then add it to allBuilders
692
- if (builder.populateColumns(freeIndexes)) {
693
- allSeriesBuilders.push(builder);
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
- // Get the data-type from the first series x column
698
- if (allSeriesBuilders.length > 0 && allSeriesBuilders[0].readers.length > 0) {
699
- typeCol = columns[allSeriesBuilders[0].readers[0].columnIndex];
700
- if (typeCol !== undefined) {
701
- if (typeCol.isDatetime) {
702
- type = 'datetime';
703
- } else if (!typeCol.isNumeric) {
704
- type = 'category';
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
- // Axis type is category, then the "x" column should be called "name"
709
- if (type === 'category') {
710
- for (seriesIndex = 0; seriesIndex < allSeriesBuilders.length; seriesIndex++) {
711
- builder = allSeriesBuilders[seriesIndex];
712
- for (r = 0; r < builder.readers.length; r++) {
713
- if (builder.readers[r].configName === 'x') {
714
- builder.readers[r].configName = 'name';
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
- // Read data for all builders
721
- for (seriesIndex = 0; seriesIndex < allSeriesBuilders.length; seriesIndex++) {
722
- builder = allSeriesBuilders[seriesIndex];
724
+ // Read data for all builders
725
+ for (seriesIndex = 0; seriesIndex < allSeriesBuilders.length; seriesIndex++) {
726
+ builder = allSeriesBuilders[seriesIndex];
723
727
 
724
- // Iterate down the cells of each column and add data to the series
725
- data = [];
726
- for (j = 0; j < columns[0].length; j++) { // TODO: which column's length should we use here
727
- data[j] = builder.read(columns, j);
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
- // Add the series
731
- series[seriesIndex] = {
732
- data: data
733
- };
734
- if (builder.name) {
735
- series[seriesIndex].name = builder.name;
736
- }
737
- if (type === 'category') {
738
- series[seriesIndex].turboThreshold = 0;
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
- // Do the callback
745
- chartOptions = {
746
- series: series
747
- };
748
- if (type) {
749
- chartOptions.xAxis = {
750
- type: type
748
+ // Do the callback
749
+ chartOptions = {
750
+ series: series
751
751
  };
752
- }
753
-
754
- if (options.complete) {
755
- options.complete(chartOptions);
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
- // The afterComplete hook is used internally to avoid conflict with the externally
759
- // available complete option.
760
- if (options.afterComplete) {
761
- options.afterComplete(chartOptions);
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
- }(Highcharts));
957
+ }));