highcharts_rails 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +106 -0
  9. data/Rakefile +6 -0
  10. data/highcharts_rails.gemspec +27 -0
  11. data/lib/highcharts_rails/version.rb +3 -0
  12. data/lib/highcharts_rails.rb +8 -0
  13. data/vendor/assets/javascripts/highcharts-3d.src.js +2139 -0
  14. data/vendor/assets/javascripts/highcharts-more.src.js +2982 -0
  15. data/vendor/assets/javascripts/highcharts.src.js +22947 -0
  16. data/vendor/assets/javascripts/js/highcharts-3d.src.js +2085 -0
  17. data/vendor/assets/javascripts/js/highcharts-more.src.js +2820 -0
  18. data/vendor/assets/javascripts/js/highcharts.src.js +20917 -0
  19. data/vendor/assets/javascripts/js/modules/accessibility.src.js +1072 -0
  20. data/vendor/assets/javascripts/js/modules/annotations.src.js +408 -0
  21. data/vendor/assets/javascripts/js/modules/boost.src.js +652 -0
  22. data/vendor/assets/javascripts/js/modules/broken-axis.src.js +338 -0
  23. data/vendor/assets/javascripts/js/modules/data.src.js +981 -0
  24. data/vendor/assets/javascripts/js/modules/drilldown.src.js +756 -0
  25. data/vendor/assets/javascripts/js/modules/exporting.src.js +953 -0
  26. data/vendor/assets/javascripts/js/modules/funnel.src.js +290 -0
  27. data/vendor/assets/javascripts/js/modules/gantt.src.js +791 -0
  28. data/vendor/assets/javascripts/js/modules/grid-axis.src.js +545 -0
  29. data/vendor/assets/javascripts/js/modules/heatmap.src.js +798 -0
  30. data/vendor/assets/javascripts/js/modules/no-data-to-display.src.js +150 -0
  31. data/vendor/assets/javascripts/js/modules/offline-exporting.src.js +492 -0
  32. data/vendor/assets/javascripts/js/modules/overlapping-datalabels.src.js +164 -0
  33. data/vendor/assets/javascripts/js/modules/series-label.src.js +606 -0
  34. data/vendor/assets/javascripts/js/modules/solid-gauge.src.js +305 -0
  35. data/vendor/assets/javascripts/js/modules/treemap.src.js +881 -0
  36. data/vendor/assets/javascripts/js/modules/xrange-series.src.js +254 -0
  37. data/vendor/assets/javascripts/js/themes/dark-blue.js +317 -0
  38. data/vendor/assets/javascripts/js/themes/dark-green.js +314 -0
  39. data/vendor/assets/javascripts/js/themes/dark-unica.js +243 -0
  40. data/vendor/assets/javascripts/js/themes/gray.js +326 -0
  41. data/vendor/assets/javascripts/js/themes/grid-light.js +99 -0
  42. data/vendor/assets/javascripts/js/themes/grid.js +131 -0
  43. data/vendor/assets/javascripts/js/themes/sand-signika.js +129 -0
  44. data/vendor/assets/javascripts/js/themes/skies.js +112 -0
  45. data/vendor/assets/javascripts/lib/canvg.src.js +3073 -0
  46. data/vendor/assets/javascripts/lib/jspdf.src.js +3031 -0
  47. data/vendor/assets/javascripts/lib/rgbcolor.src.js +299 -0
  48. data/vendor/assets/javascripts/lib/svg2pdf.src.js +1451 -0
  49. data/vendor/assets/javascripts/modules/accessibility.src.js +1072 -0
  50. data/vendor/assets/javascripts/modules/annotations.src.js +408 -0
  51. data/vendor/assets/javascripts/modules/boost.src.js +652 -0
  52. data/vendor/assets/javascripts/modules/broken-axis.src.js +338 -0
  53. data/vendor/assets/javascripts/modules/data.src.js +981 -0
  54. data/vendor/assets/javascripts/modules/drilldown.src.js +797 -0
  55. data/vendor/assets/javascripts/modules/exporting.src.js +882 -0
  56. data/vendor/assets/javascripts/modules/funnel.src.js +304 -0
  57. data/vendor/assets/javascripts/modules/gantt.src.js +815 -0
  58. data/vendor/assets/javascripts/modules/grid-axis.src.js +547 -0
  59. data/vendor/assets/javascripts/modules/heatmap.src.js +810 -0
  60. data/vendor/assets/javascripts/modules/no-data-to-display.src.js +161 -0
  61. data/vendor/assets/javascripts/modules/offline-exporting.src.js +492 -0
  62. data/vendor/assets/javascripts/modules/overlapping-datalabels.src.js +164 -0
  63. data/vendor/assets/javascripts/modules/series-label.src.js +606 -0
  64. data/vendor/assets/javascripts/modules/solid-gauge.src.js +316 -0
  65. data/vendor/assets/javascripts/modules/treemap.src.js +935 -0
  66. data/vendor/assets/javascripts/modules/xrange-series.src.js +276 -0
  67. data/vendor/assets/javascripts/themes/dark-blue.js +317 -0
  68. data/vendor/assets/javascripts/themes/dark-green.js +314 -0
  69. data/vendor/assets/javascripts/themes/dark-unica.js +243 -0
  70. data/vendor/assets/javascripts/themes/gray.js +326 -0
  71. data/vendor/assets/javascripts/themes/grid-light.js +99 -0
  72. data/vendor/assets/javascripts/themes/grid.js +131 -0
  73. data/vendor/assets/javascripts/themes/sand-signika.js +129 -0
  74. data/vendor/assets/javascripts/themes/skies.js +112 -0
  75. data/vendor/assets/stylesheets/highcharts.scss +610 -0
  76. metadata +161 -0
@@ -0,0 +1,981 @@
1
+ /**
2
+ * @license Highcharts JS v5.0.6 (2016-12-07)
3
+ * Data module
4
+ *
5
+ * (c) 2012-2016 Torstein Honsi
6
+ *
7
+ * License: www.highcharts.com/license
8
+ */
9
+ (function(factory) {
10
+ if (typeof module === 'object' && module.exports) {
11
+ module.exports = factory;
12
+ } else {
13
+ factory(Highcharts);
14
+ }
15
+ }(function(Highcharts) {
16
+ (function(Highcharts) {
17
+ /**
18
+ * Data module
19
+ *
20
+ * (c) 2012-2016 Torstein Honsi
21
+ *
22
+ * License: www.highcharts.com/license
23
+ */
24
+
25
+ /* global jQuery */
26
+ 'use strict';
27
+
28
+ // Utilities
29
+ var win = Highcharts.win,
30
+ doc = win.document,
31
+ each = Highcharts.each,
32
+ pick = Highcharts.pick,
33
+ inArray = Highcharts.inArray,
34
+ isNumber = Highcharts.isNumber,
35
+ splat = Highcharts.splat,
36
+ SeriesBuilder;
37
+
38
+
39
+ // The Data constructor
40
+ var Data = function(dataOptions, chartOptions) {
41
+ this.init(dataOptions, chartOptions);
42
+ };
43
+
44
+ // Set the prototype properties
45
+ Highcharts.extend(Data.prototype, {
46
+
47
+ /**
48
+ * Initialize the Data object with the given options
49
+ */
50
+ init: function(options, chartOptions) {
51
+ this.options = options;
52
+ this.chartOptions = chartOptions;
53
+ this.columns = options.columns || this.rowsToColumns(options.rows) || [];
54
+ this.firstRowAsNames = pick(options.firstRowAsNames, true);
55
+ this.decimalRegex = options.decimalPoint && new RegExp('^(-?[0-9]+)' + options.decimalPoint + '([0-9]+)$');
56
+
57
+ // This is a two-dimensional array holding the raw, trimmed string values
58
+ // with the same organisation as the columns array. It makes it possible
59
+ // for example to revert from interpreted timestamps to string-based
60
+ // categories.
61
+ this.rawColumns = [];
62
+
63
+ // No need to parse or interpret anything
64
+ if (this.columns.length) {
65
+ this.dataFound();
66
+
67
+ // Parse and interpret
68
+ } else {
69
+
70
+ // Parse a CSV string if options.csv is given
71
+ this.parseCSV();
72
+
73
+ // Parse a HTML table if options.table is given
74
+ this.parseTable();
75
+
76
+ // Parse a Google Spreadsheet
77
+ this.parseGoogleSpreadsheet();
78
+ }
79
+
80
+ },
81
+
82
+ /**
83
+ * Get the column distribution. For example, a line series takes a single column for
84
+ * Y values. A range series takes two columns for low and high values respectively,
85
+ * and an OHLC series takes four columns.
86
+ */
87
+ getColumnDistribution: function() {
88
+ var chartOptions = this.chartOptions,
89
+ options = this.options,
90
+ xColumns = [],
91
+ getValueCount = function(type) {
92
+ return (Highcharts.seriesTypes[type || 'line'].prototype.pointArrayMap || [0]).length;
93
+ },
94
+ getPointArrayMap = function(type) {
95
+ return Highcharts.seriesTypes[type || 'line'].prototype.pointArrayMap;
96
+ },
97
+ globalType = chartOptions && chartOptions.chart && chartOptions.chart.type,
98
+ individualCounts = [],
99
+ seriesBuilders = [],
100
+ seriesIndex = 0,
101
+ i;
102
+
103
+ each((chartOptions && chartOptions.series) || [], function(series) {
104
+ individualCounts.push(getValueCount(series.type || globalType));
105
+ });
106
+
107
+ // Collect the x-column indexes from seriesMapping
108
+ each((options && options.seriesMapping) || [], function(mapping) {
109
+ xColumns.push(mapping.x || 0);
110
+ });
111
+
112
+ // If there are no defined series with x-columns, use the first column as x column
113
+ if (xColumns.length === 0) {
114
+ xColumns.push(0);
115
+ }
116
+
117
+ // Loop all seriesMappings and constructs SeriesBuilders from
118
+ // the mapping options.
119
+ each((options && options.seriesMapping) || [], function(mapping) {
120
+ var builder = new SeriesBuilder(),
121
+ name,
122
+ numberOfValueColumnsNeeded = individualCounts[seriesIndex] || getValueCount(globalType),
123
+ seriesArr = (chartOptions && chartOptions.series) || [],
124
+ series = seriesArr[seriesIndex] || {},
125
+ pointArrayMap = getPointArrayMap(series.type || globalType) || ['y'];
126
+
127
+ // Add an x reader from the x property or from an undefined column
128
+ // if the property is not set. It will then be auto populated later.
129
+ builder.addColumnReader(mapping.x, 'x');
130
+
131
+ // Add all column mappings
132
+ for (name in mapping) {
133
+ if (mapping.hasOwnProperty(name) && name !== 'x') {
134
+ builder.addColumnReader(mapping[name], name);
135
+ }
136
+ }
137
+
138
+ // Add missing columns
139
+ for (i = 0; i < numberOfValueColumnsNeeded; i++) {
140
+ if (!builder.hasReader(pointArrayMap[i])) {
141
+ //builder.addNextColumnReader(pointArrayMap[i]);
142
+ // Create and add a column reader for the next free column index
143
+ builder.addColumnReader(undefined, pointArrayMap[i]);
144
+ }
145
+ }
146
+
147
+ seriesBuilders.push(builder);
148
+ seriesIndex++;
149
+ });
150
+
151
+ var globalPointArrayMap = getPointArrayMap(globalType);
152
+ if (globalPointArrayMap === undefined) {
153
+ globalPointArrayMap = ['y'];
154
+ }
155
+
156
+ this.valueCount = {
157
+ global: getValueCount(globalType),
158
+ xColumns: xColumns,
159
+ individual: individualCounts,
160
+ seriesBuilders: seriesBuilders,
161
+ globalPointArrayMap: globalPointArrayMap
162
+ };
163
+ },
164
+
165
+ /**
166
+ * When the data is parsed into columns, either by CSV, table, GS or direct input,
167
+ * continue with other operations.
168
+ */
169
+ dataFound: function() {
170
+
171
+ if (this.options.switchRowsAndColumns) {
172
+ this.columns = this.rowsToColumns(this.columns);
173
+ }
174
+
175
+ // Interpret the info about series and columns
176
+ this.getColumnDistribution();
177
+
178
+ // Interpret the values into right types
179
+ this.parseTypes();
180
+
181
+ // Handle columns if a handleColumns callback is given
182
+ if (this.parsed() !== false) {
183
+
184
+ // Complete if a complete callback is given
185
+ this.complete();
186
+ }
187
+
188
+ },
189
+
190
+ /**
191
+ * Parse a CSV input string
192
+ */
193
+ parseCSV: function() {
194
+ var self = this,
195
+ options = this.options,
196
+ csv = options.csv,
197
+ columns = this.columns,
198
+ startRow = options.startRow || 0,
199
+ endRow = options.endRow || Number.MAX_VALUE,
200
+ startColumn = options.startColumn || 0,
201
+ endColumn = options.endColumn || Number.MAX_VALUE,
202
+ itemDelimiter,
203
+ lines,
204
+ activeRowNo = 0;
205
+
206
+ if (csv) {
207
+
208
+ lines = csv
209
+ .replace(/\r\n/g, '\n') // Unix
210
+ .replace(/\r/g, '\n') // Mac
211
+ .split(options.lineDelimiter || '\n');
212
+
213
+ itemDelimiter = options.itemDelimiter || (csv.indexOf('\t') !== -1 ? '\t' : ',');
214
+
215
+ each(lines, function(line, rowNo) {
216
+ var trimmed = self.trim(line),
217
+ isComment = trimmed.indexOf('#') === 0,
218
+ isBlank = trimmed === '',
219
+ items;
220
+
221
+ if (rowNo >= startRow && rowNo <= endRow && !isComment && !isBlank) {
222
+ items = line.split(itemDelimiter);
223
+ each(items, function(item, colNo) {
224
+ if (colNo >= startColumn && colNo <= endColumn) {
225
+ if (!columns[colNo - startColumn]) {
226
+ columns[colNo - startColumn] = [];
227
+ }
228
+
229
+ columns[colNo - startColumn][activeRowNo] = item;
230
+ }
231
+ });
232
+ activeRowNo += 1;
233
+ }
234
+ });
235
+
236
+ this.dataFound();
237
+ }
238
+ },
239
+
240
+ /**
241
+ * Parse a HTML table
242
+ */
243
+ parseTable: function() {
244
+ var options = this.options,
245
+ table = options.table,
246
+ columns = this.columns,
247
+ startRow = options.startRow || 0,
248
+ endRow = options.endRow || Number.MAX_VALUE,
249
+ startColumn = options.startColumn || 0,
250
+ endColumn = options.endColumn || Number.MAX_VALUE;
251
+
252
+ if (table) {
253
+
254
+ if (typeof table === 'string') {
255
+ table = doc.getElementById(table);
256
+ }
257
+
258
+ each(table.getElementsByTagName('tr'), function(tr, rowNo) {
259
+ if (rowNo >= startRow && rowNo <= endRow) {
260
+ each(tr.children, function(item, colNo) {
261
+ if ((item.tagName === 'TD' || item.tagName === 'TH') && colNo >= startColumn && colNo <= endColumn) {
262
+ if (!columns[colNo - startColumn]) {
263
+ columns[colNo - startColumn] = [];
264
+ }
265
+
266
+ columns[colNo - startColumn][rowNo - startRow] = item.innerHTML;
267
+ }
268
+ });
269
+ }
270
+ });
271
+
272
+ this.dataFound(); // continue
273
+ }
274
+ },
275
+
276
+ /**
277
+ */
278
+ parseGoogleSpreadsheet: function() {
279
+ var self = this,
280
+ options = this.options,
281
+ googleSpreadsheetKey = options.googleSpreadsheetKey,
282
+ columns = this.columns,
283
+ startRow = options.startRow || 0,
284
+ endRow = options.endRow || Number.MAX_VALUE,
285
+ startColumn = options.startColumn || 0,
286
+ endColumn = options.endColumn || Number.MAX_VALUE,
287
+ gr, // google row
288
+ gc; // google column
289
+
290
+ if (googleSpreadsheetKey) {
291
+ jQuery.ajax({
292
+ dataType: 'json',
293
+ url: 'https://spreadsheets.google.com/feeds/cells/' +
294
+ googleSpreadsheetKey + '/' + (options.googleSpreadsheetWorksheet || 'od6') +
295
+ '/public/values?alt=json-in-script&callback=?',
296
+ error: options.error,
297
+ success: function(json) {
298
+ // Prepare the data from the spreadsheat
299
+ var cells = json.feed.entry,
300
+ cell,
301
+ cellCount = cells.length,
302
+ colCount = 0,
303
+ rowCount = 0,
304
+ i;
305
+
306
+ // First, find the total number of columns and rows that
307
+ // are actually filled with data
308
+ for (i = 0; i < cellCount; i++) {
309
+ cell = cells[i];
310
+ colCount = Math.max(colCount, cell.gs$cell.col);
311
+ rowCount = Math.max(rowCount, cell.gs$cell.row);
312
+ }
313
+
314
+ // Set up arrays containing the column data
315
+ for (i = 0; i < colCount; i++) {
316
+ if (i >= startColumn && i <= endColumn) {
317
+ // Create new columns with the length of either end-start or rowCount
318
+ columns[i - startColumn] = [];
319
+
320
+ // Setting the length to avoid jslint warning
321
+ columns[i - startColumn].length = Math.min(rowCount, endRow - startRow);
322
+ }
323
+ }
324
+
325
+ // Loop over the cells and assign the value to the right
326
+ // place in the column arrays
327
+ for (i = 0; i < cellCount; i++) {
328
+ cell = cells[i];
329
+ gr = cell.gs$cell.row - 1; // rows start at 1
330
+ gc = cell.gs$cell.col - 1; // columns start at 1
331
+
332
+ // If both row and col falls inside start and end
333
+ // set the transposed cell value in the newly created columns
334
+ if (gc >= startColumn && gc <= endColumn &&
335
+ gr >= startRow && gr <= endRow) {
336
+ columns[gc - startColumn][gr - startRow] = cell.content.$t;
337
+ }
338
+ }
339
+
340
+ // Insert null for empty spreadsheet cells (#5298)
341
+ each(columns, function(column) {
342
+ for (i = 0; i < column.length; i++) {
343
+ if (column[i] === undefined) {
344
+ column[i] = null;
345
+ }
346
+ }
347
+ });
348
+
349
+ self.dataFound();
350
+ }
351
+ });
352
+ }
353
+ },
354
+
355
+ /**
356
+ * Trim a string from whitespace
357
+ */
358
+ trim: function(str, inside) {
359
+ if (typeof str === 'string') {
360
+ str = str.replace(/^\s+|\s+$/g, '');
361
+
362
+ // Clear white space insdie the string, like thousands separators
363
+ if (inside && /^[0-9\s]+$/.test(str)) {
364
+ str = str.replace(/\s/g, '');
365
+ }
366
+
367
+ if (this.decimalRegex) {
368
+ str = str.replace(this.decimalRegex, '$1.$2');
369
+ }
370
+ }
371
+ return str;
372
+ },
373
+
374
+ /**
375
+ * Parse numeric cells in to number types and date types in to true dates.
376
+ */
377
+ parseTypes: function() {
378
+ var columns = this.columns,
379
+ col = columns.length;
380
+
381
+ while (col--) {
382
+ this.parseColumn(columns[col], col);
383
+ }
384
+
385
+ },
386
+
387
+ /**
388
+ * Parse a single column. Set properties like .isDatetime and .isNumeric.
389
+ */
390
+ parseColumn: function(column, col) {
391
+ var rawColumns = this.rawColumns,
392
+ columns = this.columns,
393
+ row = column.length,
394
+ val,
395
+ floatVal,
396
+ trimVal,
397
+ trimInsideVal,
398
+ firstRowAsNames = this.firstRowAsNames,
399
+ isXColumn = inArray(col, this.valueCount.xColumns) !== -1,
400
+ dateVal,
401
+ backup = [],
402
+ diff,
403
+ chartOptions = this.chartOptions,
404
+ descending,
405
+ columnTypes = this.options.columnTypes || [],
406
+ columnType = columnTypes[col],
407
+ forceCategory = isXColumn && ((chartOptions && chartOptions.xAxis && splat(chartOptions.xAxis)[0].type === 'category') || columnType === 'string');
408
+
409
+ if (!rawColumns[col]) {
410
+ rawColumns[col] = [];
411
+ }
412
+ while (row--) {
413
+ val = backup[row] || column[row];
414
+
415
+ trimVal = this.trim(val);
416
+ trimInsideVal = this.trim(val, true);
417
+ floatVal = parseFloat(trimInsideVal);
418
+
419
+ // Set it the first time
420
+ if (rawColumns[col][row] === undefined) {
421
+ rawColumns[col][row] = trimVal;
422
+ }
423
+
424
+ // Disable number or date parsing by setting the X axis type to category
425
+ if (forceCategory || (row === 0 && firstRowAsNames)) {
426
+ column[row] = trimVal;
427
+
428
+ } else if (+trimInsideVal === floatVal) { // is numeric
429
+
430
+ column[row] = floatVal;
431
+
432
+ // If the number is greater than milliseconds in a year, assume datetime
433
+ if (floatVal > 365 * 24 * 3600 * 1000 && columnType !== 'float') {
434
+ column.isDatetime = true;
435
+ } else {
436
+ column.isNumeric = true;
437
+ }
438
+
439
+ if (column[row + 1] !== undefined) {
440
+ descending = floatVal > column[row + 1];
441
+ }
442
+
443
+ // String, continue to determine if it is a date string or really a string
444
+ } else {
445
+ dateVal = this.parseDate(val);
446
+ // Only allow parsing of dates if this column is an x-column
447
+ if (isXColumn && isNumber(dateVal) && columnType !== 'float') { // is date
448
+ backup[row] = val;
449
+ column[row] = dateVal;
450
+ column.isDatetime = true;
451
+
452
+ // Check if the dates are uniformly descending or ascending. If they
453
+ // are not, chances are that they are a different time format, so check
454
+ // for alternative.
455
+ if (column[row + 1] !== undefined) {
456
+ diff = dateVal > column[row + 1];
457
+ if (diff !== descending && descending !== undefined) {
458
+ if (this.alternativeFormat) {
459
+ this.dateFormat = this.alternativeFormat;
460
+ row = column.length;
461
+ this.alternativeFormat = this.dateFormats[this.dateFormat].alternative;
462
+ } else {
463
+ column.unsorted = true;
464
+ }
465
+ }
466
+ descending = diff;
467
+ }
468
+
469
+ } else { // string
470
+ column[row] = trimVal === '' ? null : trimVal;
471
+ if (row !== 0 && (column.isDatetime || column.isNumeric)) {
472
+ column.mixed = true;
473
+ }
474
+ }
475
+ }
476
+ }
477
+
478
+ // If strings are intermixed with numbers or dates in a parsed column, it is an indication
479
+ // that parsing went wrong or the data was not intended to display as numbers or dates and
480
+ // parsing is too aggressive. Fall back to categories. Demonstrated in the
481
+ // highcharts/demo/column-drilldown sample.
482
+ if (isXColumn && column.mixed) {
483
+ columns[col] = rawColumns[col];
484
+ }
485
+
486
+ // If the 0 column is date or number and descending, reverse all columns.
487
+ if (isXColumn && descending && this.options.sort) {
488
+ for (col = 0; col < columns.length; col++) {
489
+ columns[col].reverse();
490
+ if (firstRowAsNames) {
491
+ columns[col].unshift(columns[col].pop());
492
+ }
493
+ }
494
+ }
495
+ },
496
+
497
+ /**
498
+ * A collection of available date formats, extendable from the outside to support
499
+ * custom date formats.
500
+ */
501
+ dateFormats: {
502
+ 'YYYY-mm-dd': {
503
+ regex: /^([0-9]{4})[\-\/\.]([0-9]{2})[\-\/\.]([0-9]{2})$/,
504
+ parser: function(match) {
505
+ return Date.UTC(+match[1], match[2] - 1, +match[3]);
506
+ }
507
+ },
508
+ 'dd/mm/YYYY': {
509
+ regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{4})$/,
510
+ parser: function(match) {
511
+ return Date.UTC(+match[3], match[2] - 1, +match[1]);
512
+ },
513
+ alternative: 'mm/dd/YYYY' // different format with the same regex
514
+ },
515
+ 'mm/dd/YYYY': {
516
+ regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{4})$/,
517
+ parser: function(match) {
518
+ return Date.UTC(+match[3], match[1] - 1, +match[2]);
519
+ }
520
+ },
521
+ 'dd/mm/YY': {
522
+ regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{2})$/,
523
+ parser: function(match) {
524
+ return Date.UTC(+match[3] + 2000, match[2] - 1, +match[1]);
525
+ },
526
+ alternative: 'mm/dd/YY' // different format with the same regex
527
+ },
528
+ 'mm/dd/YY': {
529
+ regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{2})$/,
530
+ parser: function(match) {
531
+ return Date.UTC(+match[3] + 2000, match[1] - 1, +match[2]);
532
+ }
533
+ }
534
+ },
535
+
536
+ /**
537
+ * Parse a date and return it as a number. Overridable through options.parseDate.
538
+ */
539
+ parseDate: function(val) {
540
+ var parseDate = this.options.parseDate,
541
+ ret,
542
+ key,
543
+ format,
544
+ dateFormat = this.options.dateFormat || this.dateFormat,
545
+ match;
546
+
547
+ if (parseDate) {
548
+ ret = parseDate(val);
549
+
550
+ } else if (typeof val === 'string') {
551
+ // Auto-detect the date format the first time
552
+ if (!dateFormat) {
553
+ for (key in this.dateFormats) {
554
+ format = this.dateFormats[key];
555
+ match = val.match(format.regex);
556
+ if (match) {
557
+ this.dateFormat = dateFormat = key;
558
+ this.alternativeFormat = format.alternative;
559
+ ret = format.parser(match);
560
+ break;
561
+ }
562
+ }
563
+ // Next time, use the one previously found
564
+ } else {
565
+ format = this.dateFormats[dateFormat];
566
+ match = val.match(format.regex);
567
+ if (match) {
568
+ ret = format.parser(match);
569
+ }
570
+ }
571
+ // Fall back to Date.parse
572
+ if (!match) {
573
+ match = Date.parse(val);
574
+ // External tools like Date.js and MooTools extend Date object and
575
+ // returns a date.
576
+ if (typeof match === 'object' && match !== null && match.getTime) {
577
+ ret = match.getTime() - match.getTimezoneOffset() * 60000;
578
+
579
+ // Timestamp
580
+ } else if (isNumber(match)) {
581
+ ret = match - (new Date(match)).getTimezoneOffset() * 60000;
582
+ }
583
+ }
584
+ }
585
+ return ret;
586
+ },
587
+
588
+ /**
589
+ * Reorganize rows into columns
590
+ */
591
+ rowsToColumns: function(rows) {
592
+ var row,
593
+ rowsLength,
594
+ col,
595
+ colsLength,
596
+ columns;
597
+
598
+ if (rows) {
599
+ columns = [];
600
+ rowsLength = rows.length;
601
+ for (row = 0; row < rowsLength; row++) {
602
+ colsLength = rows[row].length;
603
+ for (col = 0; col < colsLength; col++) {
604
+ if (!columns[col]) {
605
+ columns[col] = [];
606
+ }
607
+ columns[col][row] = rows[row][col];
608
+ }
609
+ }
610
+ }
611
+ return columns;
612
+ },
613
+
614
+ /**
615
+ * A hook for working directly on the parsed columns
616
+ */
617
+ parsed: function() {
618
+ if (this.options.parsed) {
619
+ return this.options.parsed.call(this, this.columns);
620
+ }
621
+ },
622
+
623
+ getFreeIndexes: function(numberOfColumns, seriesBuilders) {
624
+ var s,
625
+ i,
626
+ freeIndexes = [],
627
+ freeIndexValues = [],
628
+ referencedIndexes;
629
+
630
+ // Add all columns as free
631
+ for (i = 0; i < numberOfColumns; i = i + 1) {
632
+ freeIndexes.push(true);
633
+ }
634
+
635
+ // Loop all defined builders and remove their referenced columns
636
+ for (s = 0; s < seriesBuilders.length; s = s + 1) {
637
+ referencedIndexes = seriesBuilders[s].getReferencedColumnIndexes();
638
+
639
+ for (i = 0; i < referencedIndexes.length; i = i + 1) {
640
+ freeIndexes[referencedIndexes[i]] = false;
641
+ }
642
+ }
643
+
644
+ // Collect the values for the free indexes
645
+ for (i = 0; i < freeIndexes.length; i = i + 1) {
646
+ if (freeIndexes[i]) {
647
+ freeIndexValues.push(i);
648
+ }
649
+ }
650
+
651
+ return freeIndexValues;
652
+ },
653
+
654
+ /**
655
+ * If a complete callback function is provided in the options, interpret the
656
+ * columns into a Highcharts options object.
657
+ */
658
+ complete: function() {
659
+
660
+ var columns = this.columns,
661
+ xColumns = [],
662
+ type,
663
+ options = this.options,
664
+ series,
665
+ data,
666
+ i,
667
+ j,
668
+ r,
669
+ seriesIndex,
670
+ chartOptions,
671
+ allSeriesBuilders = [],
672
+ builder,
673
+ freeIndexes,
674
+ typeCol,
675
+ index;
676
+
677
+ xColumns.length = columns.length;
678
+ if (options.complete || options.afterComplete) {
679
+
680
+ // Get the names and shift the top row
681
+ for (i = 0; i < columns.length; i++) {
682
+ if (this.firstRowAsNames) {
683
+ columns[i].name = columns[i].shift();
684
+ }
685
+ }
686
+
687
+ // Use the next columns for series
688
+ series = [];
689
+ freeIndexes = this.getFreeIndexes(columns.length, this.valueCount.seriesBuilders);
690
+
691
+ // Populate defined series
692
+ for (seriesIndex = 0; seriesIndex < this.valueCount.seriesBuilders.length; seriesIndex++) {
693
+ builder = this.valueCount.seriesBuilders[seriesIndex];
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
+ }
699
+ }
700
+
701
+ // Populate dynamic series
702
+ while (freeIndexes.length > 0) {
703
+ builder = new SeriesBuilder();
704
+ builder.addColumnReader(0, 'x');
705
+
706
+ // Mark index as used (not free)
707
+ index = inArray(0, freeIndexes);
708
+ if (index !== -1) {
709
+ freeIndexes.splice(index, 1);
710
+ }
711
+
712
+ for (i = 0; i < this.valueCount.global; i++) {
713
+ // Create and add a column reader for the next free column index
714
+ builder.addColumnReader(undefined, this.valueCount.globalPointArrayMap[i]);
715
+ }
716
+
717
+ // If the builder can be populated with remaining columns, then add it to allBuilders
718
+ if (builder.populateColumns(freeIndexes)) {
719
+ allSeriesBuilders.push(builder);
720
+ }
721
+ }
722
+
723
+ // Get the data-type from the first series x column
724
+ if (allSeriesBuilders.length > 0 && allSeriesBuilders[0].readers.length > 0) {
725
+ typeCol = columns[allSeriesBuilders[0].readers[0].columnIndex];
726
+ if (typeCol !== undefined) {
727
+ if (typeCol.isDatetime) {
728
+ type = 'datetime';
729
+ } else if (!typeCol.isNumeric) {
730
+ type = 'category';
731
+ }
732
+ }
733
+ }
734
+ // Axis type is category, then the "x" column should be called "name"
735
+ if (type === 'category') {
736
+ for (seriesIndex = 0; seriesIndex < allSeriesBuilders.length; seriesIndex++) {
737
+ builder = allSeriesBuilders[seriesIndex];
738
+ for (r = 0; r < builder.readers.length; r++) {
739
+ if (builder.readers[r].configName === 'x') {
740
+ builder.readers[r].configName = 'name';
741
+ }
742
+ }
743
+ }
744
+ }
745
+
746
+ // Read data for all builders
747
+ for (seriesIndex = 0; seriesIndex < allSeriesBuilders.length; seriesIndex++) {
748
+ builder = allSeriesBuilders[seriesIndex];
749
+
750
+ // Iterate down the cells of each column and add data to the series
751
+ data = [];
752
+ for (j = 0; j < columns[0].length; j++) {
753
+ data[j] = builder.read(columns, j);
754
+ }
755
+
756
+ // Add the series
757
+ series[seriesIndex] = {
758
+ data: data
759
+ };
760
+ if (builder.name) {
761
+ series[seriesIndex].name = builder.name;
762
+ }
763
+ if (type === 'category') {
764
+ series[seriesIndex].turboThreshold = 0;
765
+ }
766
+ }
767
+
768
+
769
+
770
+ // Do the callback
771
+ chartOptions = {
772
+ series: series
773
+ };
774
+ if (type) {
775
+ chartOptions.xAxis = {
776
+ type: type
777
+ };
778
+ if (type === 'category') {
779
+ chartOptions.xAxis.uniqueNames = false;
780
+ }
781
+ }
782
+
783
+ if (options.complete) {
784
+ options.complete(chartOptions);
785
+ }
786
+
787
+ // The afterComplete hook is used internally to avoid conflict with the externally
788
+ // available complete option.
789
+ if (options.afterComplete) {
790
+ options.afterComplete(chartOptions);
791
+ }
792
+ }
793
+ }
794
+ });
795
+
796
+ // Register the Data prototype and data function on Highcharts
797
+ Highcharts.Data = Data;
798
+ Highcharts.data = function(options, chartOptions) {
799
+ return new Data(options, chartOptions);
800
+ };
801
+
802
+ // Extend Chart.init so that the Chart constructor accepts a new configuration
803
+ // option group, data.
804
+ Highcharts.wrap(Highcharts.Chart.prototype, 'init', function(proceed, userOptions, callback) {
805
+ var chart = this;
806
+
807
+ if (userOptions && userOptions.data) {
808
+ Highcharts.data(Highcharts.extend(userOptions.data, {
809
+
810
+ afterComplete: function(dataOptions) {
811
+ var i, series;
812
+
813
+ // Merge series configs
814
+ if (userOptions.hasOwnProperty('series')) {
815
+ if (typeof userOptions.series === 'object') {
816
+ i = Math.max(userOptions.series.length, dataOptions.series.length);
817
+ while (i--) {
818
+ series = userOptions.series[i] || {};
819
+ userOptions.series[i] = Highcharts.merge(series, dataOptions.series[i]);
820
+ }
821
+ } else { // Allow merging in dataOptions.series (#2856)
822
+ delete userOptions.series;
823
+ }
824
+ }
825
+
826
+ // Do the merge
827
+ userOptions = Highcharts.merge(dataOptions, userOptions);
828
+
829
+ proceed.call(chart, userOptions, callback);
830
+ }
831
+ }), userOptions);
832
+ } else {
833
+ proceed.call(chart, userOptions, callback);
834
+ }
835
+ });
836
+
837
+ /**
838
+ * Creates a new SeriesBuilder. A SeriesBuilder consists of a number
839
+ * of ColumnReaders that reads columns and give them a name.
840
+ * Ex: A series builder can be constructed to read column 3 as 'x' and
841
+ * column 7 and 8 as 'y1' and 'y2'.
842
+ * The output would then be points/rows of the form {x: 11, y1: 22, y2: 33}
843
+ *
844
+ * The name of the builder is taken from the second column. In the above
845
+ * example it would be the column with index 7.
846
+ * @constructor
847
+ */
848
+ SeriesBuilder = function() {
849
+ this.readers = [];
850
+ this.pointIsArray = true;
851
+ };
852
+
853
+ /**
854
+ * Populates readers with column indexes. A reader can be added without
855
+ * a specific index and for those readers the index is taken sequentially
856
+ * from the free columns (this is handled by the ColumnCursor instance).
857
+ * @returns {boolean}
858
+ */
859
+ SeriesBuilder.prototype.populateColumns = function(freeIndexes) {
860
+ var builder = this,
861
+ enoughColumns = true;
862
+
863
+ // Loop each reader and give it an index if its missing.
864
+ // The freeIndexes.shift() will return undefined if there
865
+ // are no more columns.
866
+ each(builder.readers, function(reader) {
867
+ if (reader.columnIndex === undefined) {
868
+ reader.columnIndex = freeIndexes.shift();
869
+ }
870
+ });
871
+
872
+ // Now, all readers should have columns mapped. If not
873
+ // then return false to signal that this series should
874
+ // not be added.
875
+ each(builder.readers, function(reader) {
876
+ if (reader.columnIndex === undefined) {
877
+ enoughColumns = false;
878
+ }
879
+ });
880
+
881
+ return enoughColumns;
882
+ };
883
+
884
+ /**
885
+ * Reads a row from the dataset and returns a point or array depending
886
+ * on the names of the readers.
887
+ * @param columns
888
+ * @param rowIndex
889
+ * @returns {Array | Object}
890
+ */
891
+ SeriesBuilder.prototype.read = function(columns, rowIndex) {
892
+ var builder = this,
893
+ pointIsArray = builder.pointIsArray,
894
+ point = pointIsArray ? [] : {},
895
+ columnIndexes;
896
+
897
+ // Loop each reader and ask it to read its value.
898
+ // Then, build an array or point based on the readers names.
899
+ each(builder.readers, function(reader) {
900
+ var value = columns[reader.columnIndex][rowIndex];
901
+ if (pointIsArray) {
902
+ point.push(value);
903
+ } else {
904
+ point[reader.configName] = value;
905
+ }
906
+ });
907
+
908
+ // The name comes from the first column (excluding the x column)
909
+ if (this.name === undefined && builder.readers.length >= 2) {
910
+ columnIndexes = builder.getReferencedColumnIndexes();
911
+ if (columnIndexes.length >= 2) {
912
+ // remove the first one (x col)
913
+ columnIndexes.shift();
914
+
915
+ // Sort the remaining
916
+ columnIndexes.sort();
917
+
918
+ // Now use the lowest index as name column
919
+ this.name = columns[columnIndexes.shift()].name;
920
+ }
921
+ }
922
+
923
+ return point;
924
+ };
925
+
926
+ /**
927
+ * Creates and adds ColumnReader from the given columnIndex and configName.
928
+ * ColumnIndex can be undefined and in that case the reader will be given
929
+ * an index when columns are populated.
930
+ * @param columnIndex {Number | undefined}
931
+ * @param configName
932
+ */
933
+ SeriesBuilder.prototype.addColumnReader = function(columnIndex, configName) {
934
+ this.readers.push({
935
+ columnIndex: columnIndex,
936
+ configName: configName
937
+ });
938
+
939
+ if (!(configName === 'x' || configName === 'y' || configName === undefined)) {
940
+ this.pointIsArray = false;
941
+ }
942
+ };
943
+
944
+ /**
945
+ * Returns an array of column indexes that the builder will use when
946
+ * reading data.
947
+ * @returns {Array}
948
+ */
949
+ SeriesBuilder.prototype.getReferencedColumnIndexes = function() {
950
+ var i,
951
+ referencedColumnIndexes = [],
952
+ columnReader;
953
+
954
+ for (i = 0; i < this.readers.length; i = i + 1) {
955
+ columnReader = this.readers[i];
956
+ if (columnReader.columnIndex !== undefined) {
957
+ referencedColumnIndexes.push(columnReader.columnIndex);
958
+ }
959
+ }
960
+
961
+ return referencedColumnIndexes;
962
+ };
963
+
964
+ /**
965
+ * Returns true if the builder has a reader for the given configName.
966
+ * @param configName
967
+ * @returns {boolean}
968
+ */
969
+ SeriesBuilder.prototype.hasReader = function(configName) {
970
+ var i, columnReader;
971
+ for (i = 0; i < this.readers.length; i = i + 1) {
972
+ columnReader = this.readers[i];
973
+ if (columnReader.configName === configName) {
974
+ return true;
975
+ }
976
+ }
977
+ // Else return undefined
978
+ };
979
+
980
+ }(Highcharts));
981
+ }));