blazer 2.6.5 → 3.0.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +13 -28
  5. data/app/assets/javascripts/blazer/ace/ace.js +7235 -8906
  6. data/app/assets/javascripts/blazer/ace/ext-language_tools.js +762 -774
  7. data/app/assets/javascripts/blazer/ace/mode-sql.js +177 -72
  8. data/app/assets/javascripts/blazer/ace/snippets/sql.js +5 -29
  9. data/app/assets/javascripts/blazer/ace/snippets/text.js +1 -6
  10. data/app/assets/javascripts/blazer/ace/theme-twilight.js +8 -106
  11. data/app/assets/javascripts/blazer/application.js +9 -6
  12. data/app/assets/javascripts/blazer/chart.umd.js +13 -0
  13. data/app/assets/javascripts/blazer/chartjs-adapter-date-fns.bundle.js +6322 -0
  14. data/app/assets/javascripts/blazer/chartkick.js +1020 -914
  15. data/app/assets/javascripts/blazer/highlight.min.js +466 -3
  16. data/app/assets/javascripts/blazer/mapkick.bundle.js +1029 -0
  17. data/app/assets/javascripts/blazer/moment-timezone-with-data.js +39 -38
  18. data/app/assets/javascripts/blazer/moment.js +105 -88
  19. data/app/assets/javascripts/blazer/queries.js +10 -1
  20. data/app/assets/javascripts/blazer/rails-ujs.js +746 -0
  21. data/app/assets/javascripts/blazer/vue.global.prod.js +1 -0
  22. data/app/assets/stylesheets/blazer/bootstrap.css +1 -1
  23. data/app/assets/stylesheets/blazer/selectize.css +1 -1
  24. data/app/controllers/blazer/base_controller.rb +85 -84
  25. data/app/controllers/blazer/checks_controller.rb +6 -6
  26. data/app/controllers/blazer/dashboards_controller.rb +24 -24
  27. data/app/controllers/blazer/queries_controller.rb +208 -186
  28. data/app/controllers/blazer/uploads_controller.rb +49 -49
  29. data/app/helpers/blazer/base_helper.rb +0 -4
  30. data/app/models/blazer/query.rb +1 -12
  31. data/app/views/blazer/checks/index.html.erb +1 -1
  32. data/app/views/blazer/dashboards/_form.html.erb +11 -5
  33. data/app/views/blazer/queries/_form.html.erb +19 -14
  34. data/app/views/blazer/queries/docs.html.erb +11 -1
  35. data/app/views/blazer/queries/home.html.erb +9 -6
  36. data/app/views/blazer/queries/run.html.erb +17 -32
  37. data/app/views/blazer/queries/show.html.erb +12 -20
  38. data/app/views/layouts/blazer/application.html.erb +1 -5
  39. data/lib/blazer/adapters/sql_adapter.rb +1 -1
  40. data/lib/blazer/adapters.rb +17 -0
  41. data/lib/blazer/anomaly_detectors.rb +22 -0
  42. data/lib/blazer/data_source.rb +29 -40
  43. data/lib/blazer/engine.rb +11 -9
  44. data/lib/blazer/forecasters.rb +7 -0
  45. data/lib/blazer/result.rb +13 -71
  46. data/lib/blazer/result_cache.rb +71 -0
  47. data/lib/blazer/run_statement.rb +1 -1
  48. data/lib/blazer/run_statement_job.rb +2 -2
  49. data/lib/blazer/statement.rb +3 -1
  50. data/lib/blazer/version.rb +1 -1
  51. data/lib/blazer.rb +51 -53
  52. data/licenses/LICENSE-chart.js.txt +1 -1
  53. data/licenses/LICENSE-chartjs-adapter-date-fns.txt +9 -0
  54. data/licenses/LICENSE-chartkick.js.txt +1 -1
  55. data/licenses/LICENSE-date-fns.txt +21 -0
  56. data/licenses/LICENSE-kurkle-color.txt +9 -0
  57. data/licenses/LICENSE-mapkick-bundle.txt +1029 -0
  58. data/licenses/{LICENSE-jquery-ujs.txt → LICENSE-rails-ujs.txt} +1 -1
  59. data/licenses/LICENSE-vue.txt +1 -1
  60. metadata +26 -18
  61. data/app/assets/javascripts/blazer/Chart.js +0 -16172
  62. data/app/assets/javascripts/blazer/jquery-ujs.js +0 -555
  63. data/app/assets/javascripts/blazer/vue.js +0 -12014
  64. data/lib/blazer/adapters/mongodb_adapter.rb +0 -43
  65. data/lib/blazer/detect_anomalies.R +0 -19
@@ -1,16 +1,15 @@
1
- /*
2
- * Chartkick.js
1
+ /*!
2
+ * Chartkick.js v5.0.1
3
3
  * Create beautiful charts with one line of JavaScript
4
4
  * https://github.com/ankane/chartkick.js
5
- * v3.2.1
6
5
  * MIT License
7
6
  */
8
7
 
9
8
  (function (global, factory) {
10
9
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
11
10
  typeof define === 'function' && define.amd ? define(factory) :
12
- (global = global || self, global.Chartkick = factory());
13
- }(this, (function () { 'use strict';
11
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Chartkick = factory());
12
+ })(this, (function () { 'use strict';
14
13
 
15
14
  function isArray(variable) {
16
15
  return Object.prototype.toString.call(variable) === "[object Array]";
@@ -27,8 +26,7 @@
27
26
 
28
27
  // https://github.com/madrobby/zepto/blob/master/src/zepto.js
29
28
  function extend(target, source) {
30
- var key;
31
- for (key in source) {
29
+ for (var key in source) {
32
30
  // protect against prototype pollution, defense 1
33
31
  if (key === "__proto__") { continue; }
34
32
 
@@ -53,49 +51,12 @@
53
51
  return target;
54
52
  }
55
53
 
56
- var DATE_PATTERN = /^(\d\d\d\d)(-)?(\d\d)(-)?(\d\d)$/i;
57
-
58
- // https://github.com/Do/iso8601.js
59
- var ISO8601_PATTERN = /(\d\d\d\d)(-)?(\d\d)(-)?(\d\d)(T)?(\d\d)(:)?(\d\d)?(:)?(\d\d)?([.,]\d+)?($|Z|([+-])(\d\d)(:)?(\d\d)?)/i;
60
- var DECIMAL_SEPARATOR = String(1.5).charAt(1);
61
-
62
- function parseISO8601(input) {
63
- var day, hour, matches, milliseconds, minutes, month, offset, result, seconds, type, year;
64
- type = Object.prototype.toString.call(input);
65
- if (type === "[object Date]") {
66
- return input;
67
- }
68
- if (type !== "[object String]") {
69
- return;
70
- }
71
- matches = input.match(ISO8601_PATTERN);
72
- if (matches) {
73
- year = parseInt(matches[1], 10);
74
- month = parseInt(matches[3], 10) - 1;
75
- day = parseInt(matches[5], 10);
76
- hour = parseInt(matches[7], 10);
77
- minutes = matches[9] ? parseInt(matches[9], 10) : 0;
78
- seconds = matches[11] ? parseInt(matches[11], 10) : 0;
79
- milliseconds = matches[12] ? parseFloat(DECIMAL_SEPARATOR + matches[12].slice(1)) * 1000 : 0;
80
- result = Date.UTC(year, month, day, hour, minutes, seconds, milliseconds);
81
- if (matches[13] && matches[14]) {
82
- offset = matches[15] * 60;
83
- if (matches[17]) {
84
- offset += parseInt(matches[17], 10);
85
- }
86
- offset *= matches[14] === "-" ? -1 : 1;
87
- result -= offset * 60 * 1000;
88
- }
89
- return new Date(result);
90
- }
91
- }
92
- // end iso8601.js
54
+ var DATE_PATTERN = /^(\d\d\d\d)(?:-)?(\d\d)(?:-)?(\d\d)$/i;
93
55
 
94
56
  function negativeValues(series) {
95
- var i, j, data;
96
- for (i = 0; i < series.length; i++) {
97
- data = series[i].data;
98
- for (j = 0; j < data.length; j++) {
57
+ for (var i = 0; i < series.length; i++) {
58
+ var data = series[i].data;
59
+ for (var j = 0; j < data.length; j++) {
99
60
  if (data[j][1] < 0) {
100
61
  return true;
101
62
  }
@@ -104,48 +65,49 @@
104
65
  return false;
105
66
  }
106
67
 
107
- function toStr(n) {
108
- return "" + n;
68
+ function toStr(obj) {
69
+ return "" + obj;
109
70
  }
110
71
 
111
- function toFloat(n) {
112
- return parseFloat(n);
72
+ function toFloat(obj) {
73
+ return parseFloat(obj);
113
74
  }
114
75
 
115
- function toDate(n) {
116
- var matches, year, month, day;
117
- if (typeof n !== "object") {
118
- if (typeof n === "number") {
119
- n = new Date(n * 1000); // ms
120
- } else {
121
- n = toStr(n);
122
- if ((matches = n.match(DATE_PATTERN))) {
123
- year = parseInt(matches[1], 10);
124
- month = parseInt(matches[3], 10) - 1;
125
- day = parseInt(matches[5], 10);
76
+ function toDate(obj) {
77
+ if (obj instanceof Date) {
78
+ return obj;
79
+ } else if (typeof obj === "number") {
80
+ return new Date(obj * 1000); // ms
81
+ } else {
82
+ var s = toStr(obj);
83
+ var matches = s.match(DATE_PATTERN);
84
+ if (matches) {
85
+ var year = parseInt(matches[1], 10);
86
+ var month = parseInt(matches[2], 10) - 1;
87
+ var day = parseInt(matches[3], 10);
126
88
  return new Date(year, month, day);
127
- } else { // str
128
- // try our best to get the str into iso8601
129
- // TODO be smarter about this
130
- var str = n.replace(/ /, "T").replace(" ", "").replace("UTC", "Z");
131
- n = parseISO8601(str) || new Date(n);
132
- }
89
+ } else {
90
+ // try our best to get the str into iso8601
91
+ // TODO be smarter about this
92
+ var str = s.replace(/ /, "T").replace(" ", "").replace("UTC", "Z");
93
+ // Date.parse returns milliseconds if valid and NaN if invalid
94
+ return new Date(Date.parse(str) || s);
133
95
  }
134
96
  }
135
- return n;
136
97
  }
137
98
 
138
- function toArr(n) {
139
- if (!isArray(n)) {
140
- var arr = [], i;
141
- for (i in n) {
142
- if (n.hasOwnProperty(i)) {
143
- arr.push([i, n[i]]);
99
+ function toArr(obj) {
100
+ if (isArray(obj)) {
101
+ return obj;
102
+ } else {
103
+ var arr = [];
104
+ for (var i in obj) {
105
+ if (Object.prototype.hasOwnProperty.call(obj, i)) {
106
+ arr.push([i, obj[i]]);
144
107
  }
145
108
  }
146
- n = arr;
109
+ return arr;
147
110
  }
148
- return n;
149
111
  }
150
112
 
151
113
  function jsOptionsFunc(defaultOptions, hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle) {
@@ -154,8 +116,8 @@
154
116
  var options = merge({}, defaultOptions);
155
117
  options = merge(options, chartOptions || {});
156
118
 
157
- if (chart.hideLegend || "legend" in opts) {
158
- hideLegend(options, opts.legend, chart.hideLegend);
119
+ if (chart.singleSeriesFormat || "legend" in opts) {
120
+ hideLegend(options, opts.legend, chart.singleSeriesFormat);
159
121
  }
160
122
 
161
123
  if (opts.title) {
@@ -205,32 +167,63 @@
205
167
  return a[0] - b[0];
206
168
  }
207
169
 
170
+ // needed since sort() without arguments does string comparison
208
171
  function sortByNumber(a, b) {
209
172
  return a - b;
210
173
  }
211
174
 
212
- function isMinute(d) {
213
- return d.getMilliseconds() === 0 && d.getSeconds() === 0;
175
+ function every(values, fn) {
176
+ for (var i = 0; i < values.length; i++) {
177
+ if (!fn(values[i])) {
178
+ return false;
179
+ }
180
+ }
181
+ return true;
214
182
  }
215
183
 
216
- function isHour(d) {
217
- return isMinute(d) && d.getMinutes() === 0;
184
+ function isDay(timeUnit) {
185
+ return timeUnit === "day" || timeUnit === "week" || timeUnit === "month" || timeUnit === "year";
218
186
  }
219
187
 
220
- function isDay(d) {
221
- return isHour(d) && d.getHours() === 0;
222
- }
188
+ function calculateTimeUnit(values, maxDay) {
189
+ if ( maxDay === void 0 ) maxDay = false;
223
190
 
224
- function isWeek(d, dayOfWeek) {
225
- return isDay(d) && d.getDay() === dayOfWeek;
226
- }
191
+ if (values.length === 0) {
192
+ return null;
193
+ }
227
194
 
228
- function isMonth(d) {
229
- return isDay(d) && d.getDate() === 1;
230
- }
195
+ var minute = every(values, function (d) { return d.getMilliseconds() === 0 && d.getSeconds() === 0; });
196
+ if (!minute) {
197
+ return null;
198
+ }
199
+
200
+ var hour = every(values, function (d) { return d.getMinutes() === 0; });
201
+ if (!hour) {
202
+ return "minute";
203
+ }
204
+
205
+ var day = every(values, function (d) { return d.getHours() === 0; });
206
+ if (!day) {
207
+ return "hour";
208
+ }
209
+
210
+ if (maxDay) {
211
+ return "day";
212
+ }
213
+
214
+ var month = every(values, function (d) { return d.getDate() === 1; });
215
+ if (!month) {
216
+ var dayOfWeek = values[0].getDay();
217
+ var week = every(values, function (d) { return d.getDay() === dayOfWeek; });
218
+ return (week ? "week" : "day");
219
+ }
220
+
221
+ var year = every(values, function (d) { return d.getMonth() === 0; });
222
+ if (!year) {
223
+ return "month";
224
+ }
231
225
 
232
- function isYear(d) {
233
- return isMonth(d) && d.getMonth() === 0;
226
+ return "year";
234
227
  }
235
228
 
236
229
  function isDate(obj) {
@@ -258,9 +251,14 @@
258
251
  var round = options.round;
259
252
 
260
253
  if (options.byteScale) {
261
- var suffixIdx;
254
+ var positive = value >= 0;
255
+ if (!positive) {
256
+ value *= -1;
257
+ }
258
+
262
259
  var baseValue = axis ? options.byteScale : value;
263
260
 
261
+ var suffixIdx;
264
262
  if (baseValue >= 1152921504606846976) {
265
263
  value /= 1152921504606846976;
266
264
  suffixIdx = 6;
@@ -294,6 +292,11 @@
294
292
  precision = value >= 1000 ? 4 : 3;
295
293
  }
296
294
  suffix = " " + byteSuffixes[suffixIdx];
295
+
296
+ // flip value back
297
+ if (!positive) {
298
+ value *= -1;
299
+ }
297
300
  }
298
301
 
299
302
  if (precision !== undefined && round !== undefined) {
@@ -345,58 +348,52 @@
345
348
  return null;
346
349
  }
347
350
 
348
- function allZeros(data) {
349
- var i, j, d;
350
- for (i = 0; i < data.length; i++) {
351
- d = data[i].data;
352
- for (j = 0; j < d.length; j++) {
353
- if (d[j][1] != 0) {
354
- return false;
355
- }
356
- }
357
- }
358
- return true;
359
- }
360
-
361
351
  var baseOptions = {
362
352
  maintainAspectRatio: false,
363
353
  animation: false,
364
- tooltips: {
365
- displayColors: false,
366
- callbacks: {}
354
+ plugins: {
355
+ legend: {},
356
+ tooltip: {
357
+ displayColors: false,
358
+ callbacks: {}
359
+ },
360
+ title: {
361
+ font: {
362
+ size: 20
363
+ },
364
+ color: "#333"
365
+ }
367
366
  },
368
- legend: {},
369
- title: {fontSize: 20, fontColor: "#333"}
367
+ interaction: {}
370
368
  };
371
369
 
372
- var defaultOptions = {
370
+ var defaultOptions$2 = {
373
371
  scales: {
374
- yAxes: [
375
- {
376
- ticks: {
377
- maxTicksLimit: 4
378
- },
379
- scaleLabel: {
380
- fontSize: 16,
381
- // fontStyle: "bold",
382
- fontColor: "#333"
383
- }
384
- }
385
- ],
386
- xAxes: [
387
- {
388
- gridLines: {
389
- drawOnChartArea: false
372
+ y: {
373
+ ticks: {
374
+ maxTicksLimit: 4
375
+ },
376
+ title: {
377
+ font: {
378
+ size: 16
390
379
  },
391
- scaleLabel: {
392
- fontSize: 16,
393
- // fontStyle: "bold",
394
- fontColor: "#333"
380
+ color: "#333"
381
+ },
382
+ grid: {}
383
+ },
384
+ x: {
385
+ grid: {
386
+ drawOnChartArea: false
387
+ },
388
+ title: {
389
+ font: {
390
+ size: 16
395
391
  },
396
- time: {},
397
- ticks: {}
398
- }
399
- ]
392
+ color: "#333"
393
+ },
394
+ time: {},
395
+ ticks: {}
396
+ }
400
397
  }
401
398
  };
402
399
 
@@ -407,79 +404,77 @@
407
404
  "#6633CC", "#E67300", "#8B0707", "#329262", "#5574A6", "#651067"
408
405
  ];
409
406
 
410
- var hideLegend = function (options, legend, hideLegend) {
407
+ function hideLegend$2(options, legend, hideLegend) {
411
408
  if (legend !== undefined) {
412
- options.legend.display = !!legend;
409
+ options.plugins.legend.display = !!legend;
413
410
  if (legend && legend !== true) {
414
- options.legend.position = legend;
411
+ options.plugins.legend.position = legend;
415
412
  }
416
413
  } else if (hideLegend) {
417
- options.legend.display = false;
414
+ options.plugins.legend.display = false;
418
415
  }
419
- };
416
+ }
420
417
 
421
- var setTitle = function (options, title) {
422
- options.title.display = true;
423
- options.title.text = title;
424
- };
418
+ function setTitle$2(options, title) {
419
+ options.plugins.title.display = true;
420
+ options.plugins.title.text = title;
421
+ }
425
422
 
426
- var setMin = function (options, min) {
423
+ function setMin$2(options, min) {
427
424
  if (min !== null) {
428
- options.scales.yAxes[0].ticks.min = toFloat(min);
425
+ options.scales.y.min = toFloat(min);
429
426
  }
430
- };
427
+ }
431
428
 
432
- var setMax = function (options, max) {
433
- options.scales.yAxes[0].ticks.max = toFloat(max);
434
- };
429
+ function setMax$2(options, max) {
430
+ options.scales.y.max = toFloat(max);
431
+ }
435
432
 
436
- var setBarMin = function (options, min) {
433
+ function setBarMin$1(options, min) {
437
434
  if (min !== null) {
438
- options.scales.xAxes[0].ticks.min = toFloat(min);
435
+ options.scales.x.min = toFloat(min);
439
436
  }
440
- };
437
+ }
441
438
 
442
- var setBarMax = function (options, max) {
443
- options.scales.xAxes[0].ticks.max = toFloat(max);
444
- };
439
+ function setBarMax$1(options, max) {
440
+ options.scales.x.max = toFloat(max);
441
+ }
445
442
 
446
- var setStacked = function (options, stacked) {
447
- options.scales.xAxes[0].stacked = !!stacked;
448
- options.scales.yAxes[0].stacked = !!stacked;
449
- };
443
+ function setStacked$2(options, stacked) {
444
+ options.scales.x.stacked = !!stacked;
445
+ options.scales.y.stacked = !!stacked;
446
+ }
450
447
 
451
- var setXtitle = function (options, title) {
452
- options.scales.xAxes[0].scaleLabel.display = true;
453
- options.scales.xAxes[0].scaleLabel.labelString = title;
454
- };
448
+ function setXtitle$2(options, title) {
449
+ options.scales.x.title.display = true;
450
+ options.scales.x.title.text = title;
451
+ }
455
452
 
456
- var setYtitle = function (options, title) {
457
- options.scales.yAxes[0].scaleLabel.display = true;
458
- options.scales.yAxes[0].scaleLabel.labelString = title;
459
- };
453
+ function setYtitle$2(options, title) {
454
+ options.scales.y.title.display = true;
455
+ options.scales.y.title.text = title;
456
+ }
460
457
 
461
458
  // https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
462
- var addOpacity = function(hex, opacity) {
459
+ function addOpacity(hex, opacity) {
463
460
  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
464
461
  return result ? "rgba(" + parseInt(result[1], 16) + ", " + parseInt(result[2], 16) + ", " + parseInt(result[3], 16) + ", " + opacity + ")" : hex;
465
- };
462
+ }
466
463
 
467
- // check if not null or undefined
468
- // https://stackoverflow.com/a/27757708/1177228
469
- var notnull = function(x) {
470
- return x != null;
471
- };
464
+ function notnull(x) {
465
+ return x !== null && x !== undefined;
466
+ }
472
467
 
473
- var setLabelSize = function (chart, data, options) {
468
+ function setLabelSize(chart, data, options) {
474
469
  var maxLabelSize = Math.ceil(chart.element.offsetWidth / 4.0 / data.labels.length);
475
470
  if (maxLabelSize > 25) {
476
471
  maxLabelSize = 25;
477
472
  } else if (maxLabelSize < 10) {
478
473
  maxLabelSize = 10;
479
474
  }
480
- if (!options.scales.xAxes[0].ticks.callback) {
481
- options.scales.xAxes[0].ticks.callback = function (value) {
482
- value = toStr(value);
475
+ if (!options.scales.x.ticks.callback) {
476
+ options.scales.x.ticks.callback = function (value) {
477
+ value = toStr(this.getLabelForValue(value));
483
478
  if (value.length > maxLabelSize) {
484
479
  return value.substring(0, maxLabelSize - 2) + "...";
485
480
  } else {
@@ -487,18 +482,33 @@
487
482
  }
488
483
  };
489
484
  }
490
- };
485
+ }
491
486
 
492
- var setFormatOptions = function(chart, options, chartType) {
493
- var formatOptions = {
487
+ function calculateScale(series) {
488
+ var scale = 1;
489
+ var max = maxAbsY(series);
490
+ while (max >= 1024) {
491
+ scale *= 1024;
492
+ max /= 1024;
493
+ }
494
+ return scale;
495
+ }
496
+
497
+ function setFormatOptions$1(chart, options, chartType) {
498
+ // options to apply to x and r values for scatter and bubble
499
+ var numericOptions = {
500
+ thousands: chart.options.thousands,
501
+ decimal: chart.options.decimal
502
+ };
503
+
504
+ // options to apply to y value
505
+ var formatOptions = merge({
494
506
  prefix: chart.options.prefix,
495
507
  suffix: chart.options.suffix,
496
- thousands: chart.options.thousands,
497
- decimal: chart.options.decimal,
498
508
  precision: chart.options.precision,
499
509
  round: chart.options.round,
500
510
  zeros: chart.options.zeros
501
- };
511
+ }, numericOptions);
502
512
 
503
513
  if (chart.options.bytes) {
504
514
  var series = chart.data;
@@ -506,232 +516,303 @@
506
516
  series = [{data: series}];
507
517
  }
508
518
 
509
- // calculate max
510
- var max = 0;
511
- for (var i = 0; i < series.length; i++) {
512
- var s = series[i];
513
- for (var j = 0; j < s.data.length; j++) {
514
- if (s.data[j][1] > max) {
515
- max = s.data[j][1];
516
- }
517
- }
518
- }
519
-
520
- // calculate scale
521
- var scale = 1;
522
- while (max >= 1024) {
523
- scale *= 1024;
524
- max /= 1024;
525
- }
526
-
527
519
  // set step size
528
- formatOptions.byteScale = scale;
520
+ formatOptions.byteScale = calculateScale(series);
529
521
  }
530
522
 
531
523
  if (chartType !== "pie") {
532
- var myAxes = options.scales.yAxes;
524
+ var axis = options.scales.y;
533
525
  if (chartType === "bar") {
534
- myAxes = options.scales.xAxes;
526
+ axis = options.scales.x;
535
527
  }
536
528
 
537
529
  if (formatOptions.byteScale) {
538
- if (!myAxes[0].ticks.stepSize) {
539
- myAxes[0].ticks.stepSize = formatOptions.byteScale / 2;
530
+ if (!axis.ticks.stepSize) {
531
+ axis.ticks.stepSize = formatOptions.byteScale / 2;
540
532
  }
541
- if (!myAxes[0].ticks.maxTicksLimit) {
542
- myAxes[0].ticks.maxTicksLimit = 4;
533
+ if (!axis.ticks.maxTicksLimit) {
534
+ axis.ticks.maxTicksLimit = 4;
543
535
  }
544
536
  }
545
537
 
546
- if (!myAxes[0].ticks.callback) {
547
- myAxes[0].ticks.callback = function (value) {
538
+ if (!axis.ticks.callback) {
539
+ axis.ticks.callback = function (value) {
548
540
  return formatValue("", value, formatOptions, true);
549
541
  };
550
542
  }
543
+
544
+ if ((chartType === "scatter" || chartType === "bubble") && !options.scales.x.ticks.callback) {
545
+ options.scales.x.ticks.callback = function (value) {
546
+ return formatValue("", value, numericOptions, true);
547
+ };
548
+ }
551
549
  }
552
550
 
553
- if (!options.tooltips.callbacks.label) {
551
+ if (!options.plugins.tooltip.callbacks.label) {
554
552
  if (chartType === "scatter") {
555
- options.tooltips.callbacks.label = function (item, data) {
556
- var label = data.datasets[item.datasetIndex].label || '';
553
+ options.plugins.tooltip.callbacks.label = function (context) {
554
+ var label = context.dataset.label || '';
557
555
  if (label) {
558
556
  label += ': ';
559
557
  }
560
- return label + '(' + item.xLabel + ', ' + item.yLabel + ')';
558
+
559
+ var dataPoint = context.parsed;
560
+ return label + '(' + formatValue('', dataPoint.x, numericOptions) + ', ' + formatValue('', dataPoint.y, formatOptions) + ')';
561
561
  };
562
562
  } else if (chartType === "bubble") {
563
- options.tooltips.callbacks.label = function (item, data) {
564
- var label = data.datasets[item.datasetIndex].label || '';
563
+ options.plugins.tooltip.callbacks.label = function (context) {
564
+ var label = context.dataset.label || '';
565
565
  if (label) {
566
566
  label += ': ';
567
567
  }
568
- var dataPoint = data.datasets[item.datasetIndex].data[item.index];
569
- return label + '(' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.v + ')';
568
+ var dataPoint = context.raw;
569
+ return label + '(' + formatValue('', dataPoint.x, numericOptions) + ', ' + formatValue('', dataPoint.y, formatOptions) + ', ' + formatValue('', dataPoint.v, numericOptions) + ')';
570
570
  };
571
571
  } else if (chartType === "pie") {
572
572
  // need to use separate label for pie charts
573
- options.tooltips.callbacks.label = function (tooltipItem, data) {
574
- var dataLabel = data.labels[tooltipItem.index];
575
- var value = ': ';
576
-
577
- if (isArray(dataLabel)) {
578
- // show value on first line of multiline label
579
- // need to clone because we are changing the value
580
- dataLabel = dataLabel.slice();
581
- dataLabel[0] += value;
582
- } else {
583
- dataLabel += value;
584
- }
585
-
586
- return formatValue(dataLabel, data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index], formatOptions);
573
+ options.plugins.tooltip.callbacks.label = function (context) {
574
+ return formatValue('', context.parsed, formatOptions);
587
575
  };
588
576
  } else {
589
- var valueLabel = chartType === "bar" ? "xLabel" : "yLabel";
590
- options.tooltips.callbacks.label = function (tooltipItem, data) {
591
- var label = data.datasets[tooltipItem.datasetIndex].label || '';
577
+ var valueLabel = chartType === "bar" ? "x" : "y";
578
+ options.plugins.tooltip.callbacks.label = function (context) {
579
+ // don't show null values for stacked charts
580
+ if (context.parsed[valueLabel] === null) {
581
+ return;
582
+ }
583
+
584
+ var label = context.dataset.label || '';
592
585
  if (label) {
593
586
  label += ': ';
594
587
  }
595
- return formatValue(label, tooltipItem[valueLabel], formatOptions);
588
+ return formatValue(label, context.parsed[valueLabel], formatOptions);
596
589
  };
597
590
  }
598
591
  }
599
- };
600
-
601
- var jsOptions = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle);
602
-
603
- var createDataTable = function (chart, options, chartType, library) {
604
- var datasets = [];
605
- var labels = [];
606
592
 
607
- var colors = chart.options.colors || defaultColors;
593
+ // avoid formatting x-axis labels
594
+ // by default, Chart.js applies locale
595
+ if ((chartType === "line" || chartType === "area") && chart.xtype === "number") {
596
+ if (!options.scales.x.ticks.callback) {
597
+ options.scales.x.ticks.callback = function (value) {
598
+ return toStr(value);
599
+ };
600
+ }
608
601
 
609
- var day = true;
610
- var week = true;
611
- var dayOfWeek;
612
- var month = true;
613
- var year = true;
614
- var hour = true;
615
- var minute = true;
602
+ if (!options.plugins.tooltip.callbacks.title) {
603
+ options.plugins.tooltip.callbacks.title = function (context) {
604
+ return toStr(context[0].parsed.x);
605
+ };
606
+ }
607
+ }
608
+ }
616
609
 
617
- var series = chart.data;
610
+ function maxAbsY(series) {
611
+ var max = 0;
612
+ for (var i = 0; i < series.length; i++) {
613
+ var data = series[i].data;
614
+ for (var j = 0; j < data.length; j++) {
615
+ var v = Math.abs(data[j][1]);
616
+ if (v > max) {
617
+ max = v;
618
+ }
619
+ }
620
+ }
621
+ return max;
622
+ }
618
623
 
624
+ function maxR(series) {
625
+ // start at zero since radius must be positive
619
626
  var max = 0;
620
- if (chartType === "bubble") {
621
- for (var i$1 = 0; i$1 < series.length; i$1++) {
622
- var s$1 = series[i$1];
623
- for (var j$1 = 0; j$1 < s$1.data.length; j$1++) {
624
- if (s$1.data[j$1][2] > max) {
625
- max = s$1.data[j$1][2];
626
- }
627
+ for (var i = 0; i < series.length; i++) {
628
+ var data = series[i].data;
629
+ for (var j = 0; j < data.length; j++) {
630
+ var v = data[j][2];
631
+ if (v > max) {
632
+ max = v;
627
633
  }
628
634
  }
629
635
  }
636
+ return max;
637
+ }
630
638
 
631
- var i, j, s, d, key, rows = [], rows2 = [];
639
+ var jsOptions$2 = jsOptionsFunc(merge(baseOptions, defaultOptions$2), hideLegend$2, setTitle$2, setMin$2, setMax$2, setStacked$2, setXtitle$2, setYtitle$2);
632
640
 
633
- if (chartType === "bar" || chartType === "column" || (chart.xtype !== "number" && chart.xtype !== "bubble")) {
634
- var sortedLabels = [];
641
+ function prepareDefaultData(chart) {
642
+ var series = chart.data;
643
+ var rows = {};
644
+ var keys = [];
645
+ var labels = [];
646
+ var values = [];
635
647
 
636
- for (i = 0; i < series.length; i++) {
637
- s = series[i];
648
+ for (var i = 0; i < series.length; i++) {
649
+ var data = series[i].data;
638
650
 
639
- for (j = 0; j < s.data.length; j++) {
640
- d = s.data[j];
641
- key = chart.xtype == "datetime" ? d[0].getTime() : d[0];
642
- if (!rows[key]) {
643
- rows[key] = new Array(series.length);
644
- }
645
- rows[key][i] = toFloat(d[1]);
646
- if (sortedLabels.indexOf(key) === -1) {
647
- sortedLabels.push(key);
648
- }
651
+ for (var j = 0; j < data.length; j++) {
652
+ var d = data[j];
653
+ var key = chart.xtype === "datetime" ? d[0].getTime() : d[0];
654
+ if (!rows[key]) {
655
+ rows[key] = new Array(series.length);
656
+ keys.push(key);
649
657
  }
658
+ rows[key][i] = d[1];
650
659
  }
660
+ }
661
+
662
+ if (chart.xtype === "datetime" || chart.xtype === "number") {
663
+ keys.sort(sortByNumber);
664
+ }
665
+
666
+ for (var i$1 = 0; i$1 < series.length; i$1++) {
667
+ values.push([]);
668
+ }
669
+
670
+ for (var i$2 = 0; i$2 < keys.length; i$2++) {
671
+ var key$1 = keys[i$2];
651
672
 
652
- if (chart.xtype === "datetime" || chart.xtype === "number") {
653
- sortedLabels.sort(sortByNumber);
673
+ var label = chart.xtype === "datetime" ? new Date(key$1) : key$1;
674
+ labels.push(label);
675
+
676
+ var row = rows[key$1];
677
+ for (var j$1 = 0; j$1 < series.length; j$1++) {
678
+ var v = row[j$1];
679
+ // Chart.js doesn't like undefined
680
+ values[j$1].push(v === undefined ? null : v);
654
681
  }
682
+ }
683
+
684
+ return {
685
+ labels: labels,
686
+ values: values
687
+ };
688
+ }
655
689
 
656
- for (j = 0; j < series.length; j++) {
657
- rows2.push([]);
690
+ function prepareBubbleData(chart) {
691
+ var series = chart.data;
692
+ var values = [];
693
+ var max = maxR(series);
694
+
695
+ for (var i = 0; i < series.length; i++) {
696
+ var data = series[i].data;
697
+ var points = [];
698
+ for (var j = 0; j < data.length; j++) {
699
+ var v = data[j];
700
+ points.push({
701
+ x: v[0],
702
+ y: v[1],
703
+ r: v[2] * 20 / max,
704
+ // custom attribute, for tooltip
705
+ v: v[2]
706
+ });
658
707
  }
708
+ values.push(points);
709
+ }
659
710
 
660
- var value;
661
- var k;
662
- for (k = 0; k < sortedLabels.length; k++) {
663
- i = sortedLabels[k];
664
- if (chart.xtype === "datetime") {
665
- value = new Date(toFloat(i));
666
- // TODO make this efficient
667
- day = day && isDay(value);
668
- if (!dayOfWeek) {
669
- dayOfWeek = value.getDay();
670
- }
671
- week = week && isWeek(value, dayOfWeek);
672
- month = month && isMonth(value);
673
- year = year && isYear(value);
674
- hour = hour && isHour(value);
675
- minute = minute && isMinute(value);
676
- } else {
677
- value = i;
678
- }
679
- labels.push(value);
680
- for (j = 0; j < series.length; j++) {
681
- // Chart.js doesn't like undefined
682
- rows2[j].push(rows[i][j] === undefined ? null : rows[i][j]);
683
- }
711
+ return {
712
+ labels: [],
713
+ values: values
714
+ };
715
+ }
716
+
717
+ // scatter or numeric line/area
718
+ function prepareNumberData(chart) {
719
+ var series = chart.data;
720
+ var values = [];
721
+
722
+ for (var i = 0; i < series.length; i++) {
723
+ var data = series[i].data;
724
+
725
+ data.sort(sortByNumberSeries);
726
+
727
+ var points = [];
728
+ for (var j = 0; j < data.length; j++) {
729
+ var v = data[j];
730
+ points.push({
731
+ x: v[0],
732
+ y: v[1]
733
+ });
684
734
  }
735
+ values.push(points);
736
+ }
737
+
738
+ return {
739
+ labels: [],
740
+ values: values
741
+ };
742
+ }
743
+
744
+ function prepareData(chart, chartType) {
745
+ if (chartType === "bubble") {
746
+ return prepareBubbleData(chart);
747
+ } else if (chart.xtype === "number" && chartType !== "bar" && chartType !== "column") {
748
+ return prepareNumberData(chart);
685
749
  } else {
686
- for (var i$2 = 0; i$2 < series.length; i$2++) {
687
- var s$2 = series[i$2];
688
- var d$1 = [];
689
- for (var j$2 = 0; j$2 < s$2.data.length; j$2++) {
690
- var point = {
691
- x: toFloat(s$2.data[j$2][0]),
692
- y: toFloat(s$2.data[j$2][1])
693
- };
694
- if (chartType === "bubble") {
695
- point.r = toFloat(s$2.data[j$2][2]) * 20 / max;
696
- // custom attribute, for tooltip
697
- point.v = s$2.data[j$2][2];
698
- }
699
- d$1.push(point);
700
- }
701
- rows2.push(d$1);
702
- }
750
+ return prepareDefaultData(chart);
703
751
  }
752
+ }
704
753
 
705
- for (i = 0; i < series.length; i++) {
706
- s = series[i];
754
+ function createDataTable(chart, options, chartType) {
755
+ var ref = prepareData(chart, chartType);
756
+ var labels = ref.labels;
757
+ var values = ref.values;
707
758
 
708
- var color = s.color || colors[i];
709
- var backgroundColor = chartType !== "line" ? addOpacity(color, 0.5) : color;
759
+ var series = chart.data;
760
+ var datasets = [];
761
+ var colors = chart.options.colors || defaultColors;
762
+ for (var i = 0; i < series.length; i++) {
763
+ var s = series[i];
764
+
765
+ // use colors for each bar for single series format
766
+ var color = (void 0);
767
+ var backgroundColor = (void 0);
768
+ if (chart.options.colors && chart.singleSeriesFormat && (chartType === "bar" || chartType === "column") && !s.color && isArray(chart.options.colors) && !isArray(chart.options.colors[0])) {
769
+ color = colors;
770
+ backgroundColor = [];
771
+ for (var j = 0; j < colors.length; j++) {
772
+ backgroundColor[j] = addOpacity(color[j], 0.5);
773
+ }
774
+ } else {
775
+ color = s.color || colors[i];
776
+ backgroundColor = chartType !== "line" ? addOpacity(color, 0.5) : color;
777
+ }
710
778
 
711
779
  var dataset = {
712
780
  label: s.name || "",
713
- data: rows2[i],
781
+ data: values[i],
714
782
  fill: chartType === "area",
715
783
  borderColor: color,
716
784
  backgroundColor: backgroundColor,
717
- pointBackgroundColor: color,
718
- borderWidth: 2,
719
- pointHoverBackgroundColor: color
785
+ borderWidth: 2
720
786
  };
721
787
 
788
+ var pointChart = chartType === "line" || chartType === "area" || chartType === "scatter" || chartType === "bubble";
789
+ if (pointChart) {
790
+ dataset.pointBackgroundColor = color;
791
+ dataset.pointHoverBackgroundColor = color;
792
+ dataset.pointHitRadius = 50;
793
+ }
794
+
795
+ if (chartType === "bubble") {
796
+ dataset.pointBackgroundColor = backgroundColor;
797
+ dataset.pointHoverBackgroundColor = backgroundColor;
798
+ dataset.pointHoverBorderWidth = 2;
799
+ }
800
+
722
801
  if (s.stack) {
723
802
  dataset.stack = s.stack;
724
803
  }
725
804
 
726
805
  var curve = seriesOption(chart, s, "curve");
727
806
  if (curve === false) {
728
- dataset.lineTension = 0;
807
+ dataset.tension = 0;
808
+ } else if (pointChart) {
809
+ dataset.tension = 0.4;
729
810
  }
730
811
 
731
812
  var points = seriesOption(chart, s, "points");
732
813
  if (points === false) {
733
814
  dataset.pointRadius = 0;
734
- dataset.pointHitRadius = 5;
815
+ dataset.pointHoverRadius = 0;
735
816
  }
736
817
 
737
818
  dataset = merge(dataset, chart.options.dataset || {});
@@ -745,149 +826,149 @@
745
826
  var xmax = chart.options.xmax;
746
827
 
747
828
  if (chart.xtype === "datetime") {
748
- // hacky check for Chart.js >= 2.9.0
749
- // https://github.com/chartjs/Chart.js/compare/v2.8.0...v2.9.0
750
- var gte29 = "math" in library.helpers;
751
- var ticksKey = gte29 ? "ticks" : "time";
752
829
  if (notnull(xmin)) {
753
- options.scales.xAxes[0][ticksKey].min = toDate(xmin).getTime();
830
+ options.scales.x.min = toDate(xmin).getTime();
754
831
  }
755
832
  if (notnull(xmax)) {
756
- options.scales.xAxes[0][ticksKey].max = toDate(xmax).getTime();
833
+ options.scales.x.max = toDate(xmax).getTime();
757
834
  }
758
835
  } else if (chart.xtype === "number") {
759
836
  if (notnull(xmin)) {
760
- options.scales.xAxes[0].ticks.min = xmin;
761
- }
762
- if (notnull(xmax)) {
763
- options.scales.xAxes[0].ticks.max = xmax;
764
- }
765
- }
766
-
767
- // for empty datetime chart
768
- if (chart.xtype === "datetime" && labels.length === 0) {
769
- if (notnull(xmin)) {
770
- labels.push(toDate(xmin));
837
+ options.scales.x.min = xmin;
771
838
  }
772
839
  if (notnull(xmax)) {
773
- labels.push(toDate(xmax));
840
+ options.scales.x.max = xmax;
774
841
  }
775
- day = false;
776
- week = false;
777
- month = false;
778
- year = false;
779
- hour = false;
780
- minute = false;
781
842
  }
782
843
 
783
- if (chart.xtype === "datetime" && labels.length > 0) {
784
- var minTime = (notnull(xmin) ? toDate(xmin) : labels[0]).getTime();
785
- var maxTime = (notnull(xmax) ? toDate(xmax) : labels[0]).getTime();
844
+ if (chart.xtype === "datetime") {
845
+ var timeUnit = calculateTimeUnit(labels);
786
846
 
787
- for (i = 1; i < labels.length; i++) {
788
- var value$1 = labels[i].getTime();
789
- if (value$1 < minTime) {
790
- minTime = value$1;
847
+ // for empty datetime chart
848
+ if (labels.length === 0) {
849
+ if (notnull(xmin)) {
850
+ labels.push(toDate(xmin));
791
851
  }
792
- if (value$1 > maxTime) {
793
- maxTime = value$1;
852
+ if (notnull(xmax)) {
853
+ labels.push(toDate(xmax));
794
854
  }
795
855
  }
796
856
 
797
- var timeDiff = (maxTime - minTime) / (86400 * 1000.0);
798
-
799
- if (!options.scales.xAxes[0].time.unit) {
800
- var step;
801
- if (year || timeDiff > 365 * 10) {
802
- options.scales.xAxes[0].time.unit = "year";
803
- step = 365;
804
- } else if (month || timeDiff > 30 * 10) {
805
- options.scales.xAxes[0].time.unit = "month";
806
- step = 30;
807
- } else if (day || timeDiff > 10) {
808
- options.scales.xAxes[0].time.unit = "day";
809
- step = 1;
810
- } else if (hour || timeDiff > 0.5) {
811
- options.scales.xAxes[0].time.displayFormats = {hour: "MMM D, h a"};
812
- options.scales.xAxes[0].time.unit = "hour";
813
- step = 1 / 24.0;
814
- } else if (minute) {
815
- options.scales.xAxes[0].time.displayFormats = {minute: "h:mm a"};
816
- options.scales.xAxes[0].time.unit = "minute";
817
- step = 1 / 24.0 / 60.0;
857
+ if (labels.length > 0) {
858
+ var minTime = (notnull(xmin) ? toDate(xmin) : labels[0]).getTime();
859
+ var maxTime = (notnull(xmax) ? toDate(xmax) : labels[0]).getTime();
860
+
861
+ for (var i$1 = 1; i$1 < labels.length; i$1++) {
862
+ var value = labels[i$1].getTime();
863
+ if (value < minTime) {
864
+ minTime = value;
865
+ }
866
+ if (value > maxTime) {
867
+ maxTime = value;
868
+ }
818
869
  }
819
870
 
820
- if (step && timeDiff > 0) {
821
- var unitStepSize = Math.ceil(timeDiff / step / (chart.element.offsetWidth / 100.0));
822
- if (week && step === 1) {
823
- unitStepSize = Math.ceil(unitStepSize / 7.0) * 7;
871
+ var timeDiff = (maxTime - minTime) / (86400 * 1000.0);
872
+
873
+ if (!options.scales.x.time.unit) {
874
+ var step;
875
+ if (timeUnit === "year" || timeDiff > 365 * 10) {
876
+ options.scales.x.time.unit = "year";
877
+ step = 365;
878
+ } else if (timeUnit === "month" || timeDiff > 30 * 10) {
879
+ options.scales.x.time.unit = "month";
880
+ step = 30;
881
+ } else if (timeUnit === "week" || timeUnit === "day" || timeDiff > 10) {
882
+ options.scales.x.time.unit = "day";
883
+ step = 1;
884
+ } else if (timeUnit === "hour" || timeDiff > 0.5) {
885
+ options.scales.x.time.displayFormats = {hour: "MMM d, h a"};
886
+ options.scales.x.time.unit = "hour";
887
+ step = 1 / 24.0;
888
+ } else if (timeUnit === "minute") {
889
+ options.scales.x.time.displayFormats = {minute: "h:mm a"};
890
+ options.scales.x.time.unit = "minute";
891
+ step = 1 / 24.0 / 60.0;
892
+ }
893
+
894
+ if (step && timeDiff > 0) {
895
+ // width not available for hidden elements
896
+ var width = chart.element.offsetWidth;
897
+ if (width > 0) {
898
+ var unitStepSize = Math.ceil(timeDiff / step / (width / 100.0));
899
+ if (timeUnit === "week" && step === 1) {
900
+ unitStepSize = Math.ceil(unitStepSize / 7.0) * 7;
901
+ }
902
+ options.scales.x.ticks.stepSize = unitStepSize;
903
+ }
824
904
  }
825
- options.scales.xAxes[0].time.unitStepSize = unitStepSize;
826
905
  }
827
- }
828
906
 
829
- if (!options.scales.xAxes[0].time.tooltipFormat) {
830
- if (day) {
831
- options.scales.xAxes[0].time.tooltipFormat = "ll";
832
- } else if (hour) {
833
- options.scales.xAxes[0].time.tooltipFormat = "MMM D, h a";
834
- } else if (minute) {
835
- options.scales.xAxes[0].time.tooltipFormat = "h:mm a";
907
+ if (!options.scales.x.time.tooltipFormat) {
908
+ if (timeUnit === "year") {
909
+ options.scales.x.time.tooltipFormat = "yyyy";
910
+ } else if (timeUnit === "month") {
911
+ options.scales.x.time.tooltipFormat = "MMM yyyy";
912
+ } else if (timeUnit === "week" || timeUnit === "day") {
913
+ options.scales.x.time.tooltipFormat = "PP";
914
+ } else if (timeUnit === "hour") {
915
+ options.scales.x.time.tooltipFormat = "MMM d, h a";
916
+ } else if (timeUnit === "minute") {
917
+ options.scales.x.time.tooltipFormat = "h:mm a";
918
+ }
836
919
  }
837
920
  }
838
921
  }
839
922
 
840
- var data = {
923
+ return {
841
924
  labels: labels,
842
925
  datasets: datasets
843
926
  };
927
+ }
844
928
 
845
- return data;
846
- };
847
-
848
- var defaultExport = function defaultExport(library) {
929
+ var defaultExport$2 = function defaultExport(library) {
849
930
  this.name = "chartjs";
850
931
  this.library = library;
851
932
  };
852
933
 
853
- defaultExport.prototype.renderLineChart = function renderLineChart (chart, chartType) {
854
- var chartOptions = {};
855
- // fix for https://github.com/chartjs/Chart.js/issues/2441
856
- if (!chart.options.max && allZeros(chart.data)) {
857
- chartOptions.max = 1;
934
+ defaultExport$2.prototype.renderLineChart = function renderLineChart (chart, chartType) {
935
+ if (!chartType) {
936
+ chartType = "line";
858
937
  }
859
938
 
860
- var options = jsOptions(chart, merge(chartOptions, chart.options));
861
- setFormatOptions(chart, options, chartType);
939
+ var chartOptions = {};
862
940
 
863
- var data = createDataTable(chart, options, chartType || "line", this.library);
941
+ var options = jsOptions$2(chart, merge(chartOptions, chart.options));
942
+ setFormatOptions$1(chart, options, chartType);
943
+
944
+ var data = createDataTable(chart, options, chartType);
864
945
 
865
946
  if (chart.xtype === "number") {
866
- options.scales.xAxes[0].type = "linear";
867
- options.scales.xAxes[0].position = "bottom";
947
+ options.scales.x.type = options.scales.x.type || "linear";
948
+ options.scales.x.position = options.scales.x.position || "bottom";
868
949
  } else {
869
- options.scales.xAxes[0].type = chart.xtype === "string" ? "category" : "time";
950
+ options.scales.x.type = chart.xtype === "string" ? "category" : "time";
870
951
  }
871
952
 
872
953
  this.drawChart(chart, "line", data, options);
873
954
  };
874
955
 
875
- defaultExport.prototype.renderPieChart = function renderPieChart (chart) {
956
+ defaultExport$2.prototype.renderPieChart = function renderPieChart (chart) {
876
957
  var options = merge({}, baseOptions);
877
958
  if (chart.options.donut) {
878
- options.cutoutPercentage = 50;
959
+ options.cutout = "50%";
879
960
  }
880
961
 
881
962
  if ("legend" in chart.options) {
882
- hideLegend(options, chart.options.legend);
963
+ hideLegend$2(options, chart.options.legend);
883
964
  }
884
965
 
885
966
  if (chart.options.title) {
886
- setTitle(options, chart.options.title);
967
+ setTitle$2(options, chart.options.title);
887
968
  }
888
969
 
889
970
  options = merge(options, chart.options.library || {});
890
- setFormatOptions(chart, options, "pie");
971
+ setFormatOptions$1(chart, options, "pie");
891
972
 
892
973
  var labels = [];
893
974
  var values = [];
@@ -911,61 +992,76 @@
911
992
  this.drawChart(chart, "pie", data, options);
912
993
  };
913
994
 
914
- defaultExport.prototype.renderColumnChart = function renderColumnChart (chart, chartType) {
995
+ defaultExport$2.prototype.renderColumnChart = function renderColumnChart (chart, chartType) {
915
996
  var options;
916
997
  if (chartType === "bar") {
917
- var barOptions = merge(baseOptions, defaultOptions);
918
- delete barOptions.scales.yAxes[0].ticks.maxTicksLimit;
919
- options = jsOptionsFunc(barOptions, hideLegend, setTitle, setBarMin, setBarMax, setStacked, setXtitle, setYtitle)(chart, chart.options);
998
+ var barOptions = merge(baseOptions, defaultOptions$2);
999
+ barOptions.indexAxis = "y";
1000
+
1001
+ // ensure gridlines have proper orientation
1002
+ barOptions.scales.x.grid.drawOnChartArea = true;
1003
+ barOptions.scales.y.grid.drawOnChartArea = false;
1004
+ delete barOptions.scales.y.ticks.maxTicksLimit;
1005
+
1006
+ options = jsOptionsFunc(barOptions, hideLegend$2, setTitle$2, setBarMin$1, setBarMax$1, setStacked$2, setXtitle$2, setYtitle$2)(chart, chart.options);
920
1007
  } else {
921
- options = jsOptions(chart, chart.options);
1008
+ options = jsOptions$2(chart, chart.options);
922
1009
  }
923
- setFormatOptions(chart, options, chartType);
924
- var data = createDataTable(chart, options, "column", this.library);
1010
+ setFormatOptions$1(chart, options, chartType);
1011
+ var data = createDataTable(chart, options, "column");
925
1012
  if (chartType !== "bar") {
926
1013
  setLabelSize(chart, data, options);
927
1014
  }
928
- this.drawChart(chart, (chartType === "bar" ? "horizontalBar" : "bar"), data, options);
1015
+ if (!("mode" in options.interaction)) {
1016
+ options.interaction.mode = "index";
1017
+ }
1018
+ this.drawChart(chart, "bar", data, options);
929
1019
  };
930
1020
 
931
- defaultExport.prototype.renderAreaChart = function renderAreaChart (chart) {
1021
+ defaultExport$2.prototype.renderAreaChart = function renderAreaChart (chart) {
932
1022
  this.renderLineChart(chart, "area");
933
1023
  };
934
1024
 
935
- defaultExport.prototype.renderBarChart = function renderBarChart (chart) {
1025
+ defaultExport$2.prototype.renderBarChart = function renderBarChart (chart) {
936
1026
  this.renderColumnChart(chart, "bar");
937
1027
  };
938
1028
 
939
- defaultExport.prototype.renderScatterChart = function renderScatterChart (chart, chartType) {
1029
+ defaultExport$2.prototype.renderScatterChart = function renderScatterChart (chart, chartType) {
940
1030
  chartType = chartType || "scatter";
941
1031
 
942
- var options = jsOptions(chart, chart.options);
943
- setFormatOptions(chart, options, chartType);
1032
+ var options = jsOptions$2(chart, chart.options);
1033
+ setFormatOptions$1(chart, options, chartType);
944
1034
 
945
- if (!("showLines" in options)) {
946
- options.showLines = false;
1035
+ if (!("showLine" in options)) {
1036
+ options.showLine = false;
947
1037
  }
948
1038
 
949
- var data = createDataTable(chart, options, chartType, this.library);
1039
+ var data = createDataTable(chart, options, chartType);
1040
+
1041
+ options.scales.x.type = options.scales.x.type || "linear";
1042
+ options.scales.x.position = options.scales.x.position || "bottom";
950
1043
 
951
- options.scales.xAxes[0].type = "linear";
952
- options.scales.xAxes[0].position = "bottom";
1044
+ // prevent grouping hover and tooltips
1045
+ if (!("mode" in options.interaction)) {
1046
+ options.interaction.mode = "nearest";
1047
+ }
953
1048
 
954
1049
  this.drawChart(chart, chartType, data, options);
955
1050
  };
956
1051
 
957
- defaultExport.prototype.renderBubbleChart = function renderBubbleChart (chart) {
1052
+ defaultExport$2.prototype.renderBubbleChart = function renderBubbleChart (chart) {
958
1053
  this.renderScatterChart(chart, "bubble");
959
1054
  };
960
1055
 
961
- defaultExport.prototype.destroy = function destroy (chart) {
1056
+ defaultExport$2.prototype.destroy = function destroy (chart) {
962
1057
  if (chart.chart) {
963
1058
  chart.chart.destroy();
964
1059
  }
965
1060
  };
966
1061
 
967
- defaultExport.prototype.drawChart = function drawChart (chart, type, data, options) {
1062
+ defaultExport$2.prototype.drawChart = function drawChart (chart, type, data, options) {
968
1063
  this.destroy(chart);
1064
+ if (chart.destroyed) { return; }
969
1065
 
970
1066
  var chartOptions = {
971
1067
  type: type,
@@ -1024,10 +1120,13 @@
1024
1120
  series: {
1025
1121
  marker: {}
1026
1122
  }
1123
+ },
1124
+ time: {
1125
+ useUTC: false
1027
1126
  }
1028
1127
  };
1029
1128
 
1030
- var hideLegend$1 = function (options, legend, hideLegend) {
1129
+ function hideLegend$1(options, legend, hideLegend) {
1031
1130
  if (legend !== undefined) {
1032
1131
  options.legend.enabled = !!legend;
1033
1132
  if (legend && legend !== true) {
@@ -1042,38 +1141,38 @@
1042
1141
  } else if (hideLegend) {
1043
1142
  options.legend.enabled = false;
1044
1143
  }
1045
- };
1144
+ }
1046
1145
 
1047
- var setTitle$1 = function (options, title) {
1146
+ function setTitle$1(options, title) {
1048
1147
  options.title.text = title;
1049
- };
1148
+ }
1050
1149
 
1051
- var setMin$1 = function (options, min) {
1150
+ function setMin$1(options, min) {
1052
1151
  options.yAxis.min = min;
1053
- };
1152
+ }
1054
1153
 
1055
- var setMax$1 = function (options, max) {
1154
+ function setMax$1(options, max) {
1056
1155
  options.yAxis.max = max;
1057
- };
1156
+ }
1058
1157
 
1059
- var setStacked$1 = function (options, stacked) {
1158
+ function setStacked$1(options, stacked) {
1060
1159
  var stackedValue = stacked ? (stacked === true ? "normal" : stacked) : null;
1061
1160
  options.plotOptions.series.stacking = stackedValue;
1062
1161
  options.plotOptions.area.stacking = stackedValue;
1063
1162
  options.plotOptions.areaspline.stacking = stackedValue;
1064
- };
1163
+ }
1065
1164
 
1066
- var setXtitle$1 = function (options, title) {
1165
+ function setXtitle$1(options, title) {
1067
1166
  options.xAxis.title.text = title;
1068
- };
1167
+ }
1069
1168
 
1070
- var setYtitle$1 = function (options, title) {
1169
+ function setYtitle$1(options, title) {
1071
1170
  options.yAxis.title.text = title;
1072
- };
1171
+ }
1073
1172
 
1074
1173
  var jsOptions$1 = jsOptionsFunc(defaultOptions$1, hideLegend$1, setTitle$1, setMin$1, setMax$1, setStacked$1, setXtitle$1, setYtitle$1);
1075
1174
 
1076
- var setFormatOptions$1 = function(chart, options, chartType) {
1175
+ function setFormatOptions(chart, options, chartType) {
1077
1176
  var formatOptions = {
1078
1177
  prefix: chart.options.prefix,
1079
1178
  suffix: chart.options.suffix,
@@ -1084,18 +1183,19 @@
1084
1183
  zeros: chart.options.zeros
1085
1184
  };
1086
1185
 
1087
- if (chartType !== "pie" && !options.yAxis.labels.formatter) {
1186
+ // skip when axis is an array (like with min/max)
1187
+ if (chartType !== "pie" && !isArray(options.yAxis) && !options.yAxis.labels.formatter) {
1088
1188
  options.yAxis.labels.formatter = function () {
1089
1189
  return formatValue("", this.value, formatOptions);
1090
1190
  };
1091
1191
  }
1092
1192
 
1093
- if (!options.tooltip.pointFormatter) {
1193
+ if (!options.tooltip.pointFormatter && !options.tooltip.pointFormat) {
1094
1194
  options.tooltip.pointFormatter = function () {
1095
1195
  return '<span style="color:' + this.color + '">\u25CF</span> ' + formatValue(this.series.name + ': <b>', this.y, formatOptions) + '</b><br/>';
1096
1196
  };
1097
1197
  }
1098
- };
1198
+ }
1099
1199
 
1100
1200
  var defaultExport$1 = function defaultExport(library) {
1101
1201
  this.name = "highcharts";
@@ -1131,21 +1231,27 @@
1131
1231
  }
1132
1232
  }
1133
1233
 
1134
- var options = jsOptions$1(chart, chart.options, chartOptions), data, i, j;
1135
- options.xAxis.type = chart.xtype === "string" ? "category" : (chart.xtype === "number" ? "linear" : "datetime");
1234
+ var options = jsOptions$1(chart, chart.options, chartOptions);
1235
+ if (chart.xtype === "number") {
1236
+ options.xAxis.type = options.xAxis.type || "linear";
1237
+ } else {
1238
+ options.xAxis.type = chart.xtype === "string" ? "category" : "datetime";
1239
+ }
1136
1240
  if (!options.chart.type) {
1137
1241
  options.chart.type = chartType;
1138
1242
  }
1139
- setFormatOptions$1(chart, options, chartType);
1243
+ setFormatOptions(chart, options, chartType);
1140
1244
 
1141
1245
  var series = chart.data;
1142
- for (i = 0; i < series.length; i++) {
1246
+ for (var i = 0; i < series.length; i++) {
1143
1247
  series[i].name = series[i].name || "Value";
1144
- data = series[i].data;
1248
+ var data = series[i].data;
1145
1249
  if (chart.xtype === "datetime") {
1146
- for (j = 0; j < data.length; j++) {
1250
+ for (var j = 0; j < data.length; j++) {
1147
1251
  data[j][0] = data[j][0].getTime();
1148
1252
  }
1253
+ } else if (chart.xtype === "number") {
1254
+ data.sort(sortByNumberSeries);
1149
1255
  }
1150
1256
  series[i].marker = {symbol: "circle"};
1151
1257
  if (chart.options.points === false) {
@@ -1181,7 +1287,7 @@
1181
1287
  }
1182
1288
 
1183
1289
  var options = merge(chartOptions, chart.options.library || {});
1184
- setFormatOptions$1(chart, options, "pie");
1290
+ setFormatOptions(chart, options, "pie");
1185
1291
  var series = [{
1186
1292
  type: "pie",
1187
1293
  name: chart.options.label || "Value",
@@ -1194,15 +1300,17 @@
1194
1300
  defaultExport$1.prototype.renderColumnChart = function renderColumnChart (chart, chartType) {
1195
1301
  chartType = chartType || "column";
1196
1302
  var series = chart.data;
1197
- var options = jsOptions$1(chart, chart.options), i, j, s, d, rows = [], categories = [];
1303
+ var options = jsOptions$1(chart, chart.options);
1304
+ var rows = [];
1305
+ var categories = [];
1198
1306
  options.chart.type = chartType;
1199
- setFormatOptions$1(chart, options, chartType);
1307
+ setFormatOptions(chart, options, chartType);
1200
1308
 
1201
- for (i = 0; i < series.length; i++) {
1202
- s = series[i];
1309
+ for (var i = 0; i < series.length; i++) {
1310
+ var s = series[i];
1203
1311
 
1204
- for (j = 0; j < s.data.length; j++) {
1205
- d = s.data[j];
1312
+ for (var j = 0; j < s.data.length; j++) {
1313
+ var d = s.data[j];
1206
1314
  if (!rows[d[0]]) {
1207
1315
  rows[d[0]] = new Array(series.length);
1208
1316
  categories.push(d[0]);
@@ -1217,19 +1325,19 @@
1217
1325
 
1218
1326
  options.xAxis.categories = categories;
1219
1327
 
1220
- var newSeries = [], d2;
1221
- for (i = 0; i < series.length; i++) {
1222
- d = [];
1223
- for (j = 0; j < categories.length; j++) {
1224
- d.push(rows[categories[j]][i] || 0);
1328
+ var newSeries = [];
1329
+ for (var i$1 = 0; i$1 < series.length; i$1++) {
1330
+ var d$1 = [];
1331
+ for (var j$1 = 0; j$1 < categories.length; j$1++) {
1332
+ d$1.push(rows[categories[j$1]][i$1] || 0);
1225
1333
  }
1226
1334
 
1227
- d2 = {
1228
- name: series[i].name || "Value",
1229
- data: d
1335
+ var d2 = {
1336
+ name: series[i$1].name || "Value",
1337
+ data: d$1
1230
1338
  };
1231
- if (series[i].stack) {
1232
- d2.stack = series[i].stack;
1339
+ if (series[i$1].stack) {
1340
+ d2.stack = series[i$1].stack;
1233
1341
  }
1234
1342
 
1235
1343
  newSeries.push(d2);
@@ -1254,6 +1362,7 @@
1254
1362
 
1255
1363
  defaultExport$1.prototype.drawChart = function drawChart (chart, data, options) {
1256
1364
  this.destroy(chart);
1365
+ if (chart.destroyed) { return; }
1257
1366
 
1258
1367
  options.chart.renderTo = chart.element.id;
1259
1368
  options.series = data;
@@ -1269,7 +1378,7 @@
1269
1378
  var callbacks = [];
1270
1379
 
1271
1380
  // Set chart options
1272
- var defaultOptions$2 = {
1381
+ var defaultOptions = {
1273
1382
  chartArea: {},
1274
1383
  fontName: "'Lucida Grande', 'Lucida Sans Unicode', Verdana, Arial, Helvetica, sans-serif",
1275
1384
  pointSize: 6,
@@ -1311,7 +1420,7 @@
1311
1420
  }
1312
1421
  };
1313
1422
 
1314
- var hideLegend$2 = function (options, legend, hideLegend) {
1423
+ function hideLegend(options, legend, hideLegend) {
1315
1424
  if (legend !== undefined) {
1316
1425
  var position;
1317
1426
  if (!legend) {
@@ -1325,61 +1434,61 @@
1325
1434
  } else if (hideLegend) {
1326
1435
  options.legend.position = "none";
1327
1436
  }
1328
- };
1437
+ }
1329
1438
 
1330
- var setTitle$2 = function (options, title) {
1439
+ function setTitle(options, title) {
1331
1440
  options.title = title;
1332
1441
  options.titleTextStyle = {color: "#333", fontSize: "20px"};
1333
- };
1442
+ }
1334
1443
 
1335
- var setMin$2 = function (options, min) {
1444
+ function setMin(options, min) {
1336
1445
  options.vAxis.viewWindow.min = min;
1337
- };
1446
+ }
1338
1447
 
1339
- var setMax$2 = function (options, max) {
1448
+ function setMax(options, max) {
1340
1449
  options.vAxis.viewWindow.max = max;
1341
- };
1450
+ }
1342
1451
 
1343
- var setBarMin$1 = function (options, min) {
1452
+ function setBarMin(options, min) {
1344
1453
  options.hAxis.viewWindow.min = min;
1345
- };
1454
+ }
1346
1455
 
1347
- var setBarMax$1 = function (options, max) {
1456
+ function setBarMax(options, max) {
1348
1457
  options.hAxis.viewWindow.max = max;
1349
- };
1458
+ }
1350
1459
 
1351
- var setStacked$2 = function (options, stacked) {
1352
- options.isStacked = stacked ? stacked : false;
1353
- };
1460
+ function setStacked(options, stacked) {
1461
+ options.isStacked = stacked || false;
1462
+ }
1354
1463
 
1355
- var setXtitle$2 = function (options, title) {
1464
+ function setXtitle(options, title) {
1356
1465
  options.hAxis.title = title;
1357
1466
  options.hAxis.titleTextStyle.italic = false;
1358
- };
1467
+ }
1359
1468
 
1360
- var setYtitle$2 = function (options, title) {
1469
+ function setYtitle(options, title) {
1361
1470
  options.vAxis.title = title;
1362
1471
  options.vAxis.titleTextStyle.italic = false;
1363
- };
1472
+ }
1364
1473
 
1365
- var jsOptions$2 = jsOptionsFunc(defaultOptions$2, hideLegend$2, setTitle$2, setMin$2, setMax$2, setStacked$2, setXtitle$2, setYtitle$2);
1474
+ var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle);
1366
1475
 
1367
- var resize = function (callback) {
1476
+ function resize(callback) {
1368
1477
  if (window.attachEvent) {
1369
1478
  window.attachEvent("onresize", callback);
1370
1479
  } else if (window.addEventListener) {
1371
1480
  window.addEventListener("resize", callback, true);
1372
1481
  }
1373
1482
  callback();
1374
- };
1483
+ }
1375
1484
 
1376
- var defaultExport$2 = function defaultExport(library) {
1485
+ var defaultExport = function defaultExport(library) {
1377
1486
  this.name = "google";
1378
1487
  this.library = library;
1379
1488
  };
1380
1489
 
1381
- defaultExport$2.prototype.renderLineChart = function renderLineChart (chart) {
1382
- var this$1 = this;
1490
+ defaultExport.prototype.renderLineChart = function renderLineChart (chart) {
1491
+ var this$1$1 = this;
1383
1492
 
1384
1493
  this.waitForLoaded(chart, function () {
1385
1494
  var chartOptions = {};
@@ -1392,15 +1501,15 @@
1392
1501
  chartOptions.pointSize = 0;
1393
1502
  }
1394
1503
 
1395
- var options = jsOptions$2(chart, chart.options, chartOptions);
1396
- var data = this$1.createDataTable(chart.data, chart.xtype);
1504
+ var options = jsOptions(chart, chart.options, chartOptions);
1505
+ var data = this$1$1.createDataTable(chart.data, chart.xtype);
1397
1506
 
1398
- this$1.drawChart(chart, "LineChart", data, options);
1507
+ this$1$1.drawChart(chart, "LineChart", data, options);
1399
1508
  });
1400
1509
  };
1401
1510
 
1402
- defaultExport$2.prototype.renderPieChart = function renderPieChart (chart) {
1403
- var this$1 = this;
1511
+ defaultExport.prototype.renderPieChart = function renderPieChart (chart) {
1512
+ var this$1$1 = this;
1404
1513
 
1405
1514
  this.waitForLoaded(chart, function () {
1406
1515
  var chartOptions = {
@@ -1417,35 +1526,35 @@
1417
1526
  chartOptions.pieHole = 0.5;
1418
1527
  }
1419
1528
  if ("legend" in chart.options) {
1420
- hideLegend$2(chartOptions, chart.options.legend);
1529
+ hideLegend(chartOptions, chart.options.legend);
1421
1530
  }
1422
1531
  if (chart.options.title) {
1423
- setTitle$2(chartOptions, chart.options.title);
1532
+ setTitle(chartOptions, chart.options.title);
1424
1533
  }
1425
- var options = merge(merge(defaultOptions$2, chartOptions), chart.options.library || {});
1534
+ var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
1426
1535
 
1427
- var data = new this$1.library.visualization.DataTable();
1536
+ var data = new this$1$1.library.visualization.DataTable();
1428
1537
  data.addColumn("string", "");
1429
1538
  data.addColumn("number", "Value");
1430
1539
  data.addRows(chart.data);
1431
1540
 
1432
- this$1.drawChart(chart, "PieChart", data, options);
1541
+ this$1$1.drawChart(chart, "PieChart", data, options);
1433
1542
  });
1434
1543
  };
1435
1544
 
1436
- defaultExport$2.prototype.renderColumnChart = function renderColumnChart (chart) {
1437
- var this$1 = this;
1545
+ defaultExport.prototype.renderColumnChart = function renderColumnChart (chart) {
1546
+ var this$1$1 = this;
1438
1547
 
1439
1548
  this.waitForLoaded(chart, function () {
1440
- var options = jsOptions$2(chart, chart.options);
1441
- var data = this$1.createDataTable(chart.data, chart.xtype);
1549
+ var options = jsOptions(chart, chart.options);
1550
+ var data = this$1$1.createDataTable(chart.data, chart.xtype);
1442
1551
 
1443
- this$1.drawChart(chart, "ColumnChart", data, options);
1552
+ this$1$1.drawChart(chart, "ColumnChart", data, options);
1444
1553
  });
1445
1554
  };
1446
1555
 
1447
- defaultExport$2.prototype.renderBarChart = function renderBarChart (chart) {
1448
- var this$1 = this;
1556
+ defaultExport.prototype.renderBarChart = function renderBarChart (chart) {
1557
+ var this$1$1 = this;
1449
1558
 
1450
1559
  this.waitForLoaded(chart, function () {
1451
1560
  var chartOptions = {
@@ -1455,15 +1564,15 @@
1455
1564
  }
1456
1565
  }
1457
1566
  };
1458
- var options = jsOptionsFunc(defaultOptions$2, hideLegend$2, setTitle$2, setBarMin$1, setBarMax$1, setStacked$2, setXtitle$2, setYtitle$2)(chart, chart.options, chartOptions);
1459
- var data = this$1.createDataTable(chart.data, chart.xtype);
1567
+ var options = jsOptionsFunc(defaultOptions, hideLegend, setTitle, setBarMin, setBarMax, setStacked, setXtitle, setYtitle)(chart, chart.options, chartOptions);
1568
+ var data = this$1$1.createDataTable(chart.data, chart.xtype);
1460
1569
 
1461
- this$1.drawChart(chart, "BarChart", data, options);
1570
+ this$1$1.drawChart(chart, "BarChart", data, options);
1462
1571
  });
1463
1572
  };
1464
1573
 
1465
- defaultExport$2.prototype.renderAreaChart = function renderAreaChart (chart) {
1466
- var this$1 = this;
1574
+ defaultExport.prototype.renderAreaChart = function renderAreaChart (chart) {
1575
+ var this$1$1 = this;
1467
1576
 
1468
1577
  this.waitForLoaded(chart, function () {
1469
1578
  var chartOptions = {
@@ -1472,15 +1581,15 @@
1472
1581
  areaOpacity: 0.5
1473
1582
  };
1474
1583
 
1475
- var options = jsOptions$2(chart, chart.options, chartOptions);
1476
- var data = this$1.createDataTable(chart.data, chart.xtype);
1584
+ var options = jsOptions(chart, chart.options, chartOptions);
1585
+ var data = this$1$1.createDataTable(chart.data, chart.xtype);
1477
1586
 
1478
- this$1.drawChart(chart, "AreaChart", data, options);
1587
+ this$1$1.drawChart(chart, "AreaChart", data, options);
1479
1588
  });
1480
1589
  };
1481
1590
 
1482
- defaultExport$2.prototype.renderGeoChart = function renderGeoChart (chart) {
1483
- var this$1 = this;
1591
+ defaultExport.prototype.renderGeoChart = function renderGeoChart (chart) {
1592
+ var this$1$1 = this;
1484
1593
 
1485
1594
  this.waitForLoaded(chart, "geochart", function () {
1486
1595
  var chartOptions = {
@@ -1489,29 +1598,30 @@
1489
1598
  colors: chart.options.colors || ["#f6c7b6", "#ce502d"]
1490
1599
  }
1491
1600
  };
1492
- var options = merge(merge(defaultOptions$2, chartOptions), chart.options.library || {});
1601
+ var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
1493
1602
 
1494
- var data = new this$1.library.visualization.DataTable();
1603
+ var data = new this$1$1.library.visualization.DataTable();
1495
1604
  data.addColumn("string", "");
1496
1605
  data.addColumn("number", chart.options.label || "Value");
1497
1606
  data.addRows(chart.data);
1498
1607
 
1499
- this$1.drawChart(chart, "GeoChart", data, options);
1608
+ this$1$1.drawChart(chart, "GeoChart", data, options);
1500
1609
  });
1501
1610
  };
1502
1611
 
1503
- defaultExport$2.prototype.renderScatterChart = function renderScatterChart (chart) {
1504
- var this$1 = this;
1612
+ defaultExport.prototype.renderScatterChart = function renderScatterChart (chart) {
1613
+ var this$1$1 = this;
1505
1614
 
1506
1615
  this.waitForLoaded(chart, function () {
1507
1616
  var chartOptions = {};
1508
- var options = jsOptions$2(chart, chart.options, chartOptions);
1617
+ var options = jsOptions(chart, chart.options, chartOptions);
1509
1618
 
1510
- var series = chart.data, rows2 = [], i, j, data, d;
1511
- for (i = 0; i < series.length; i++) {
1619
+ var series = chart.data;
1620
+ var rows2 = [];
1621
+ for (var i = 0; i < series.length; i++) {
1512
1622
  series[i].name = series[i].name || "Value";
1513
- d = series[i].data;
1514
- for (j = 0; j < d.length; j++) {
1623
+ var d = series[i].data;
1624
+ for (var j = 0; j < d.length; j++) {
1515
1625
  var row = new Array(series.length + 1);
1516
1626
  row[0] = d[j][0];
1517
1627
  row[i + 1] = d[j][1];
@@ -1519,19 +1629,19 @@
1519
1629
  }
1520
1630
  }
1521
1631
 
1522
- data = new this$1.library.visualization.DataTable();
1632
+ var data = new this$1$1.library.visualization.DataTable();
1523
1633
  data.addColumn("number", "");
1524
- for (i = 0; i < series.length; i++) {
1525
- data.addColumn("number", series[i].name);
1634
+ for (var i$1 = 0; i$1 < series.length; i$1++) {
1635
+ data.addColumn("number", series[i$1].name);
1526
1636
  }
1527
1637
  data.addRows(rows2);
1528
1638
 
1529
- this$1.drawChart(chart, "ScatterChart", data, options);
1639
+ this$1$1.drawChart(chart, "ScatterChart", data, options);
1530
1640
  });
1531
1641
  };
1532
1642
 
1533
- defaultExport$2.prototype.renderTimeline = function renderTimeline (chart) {
1534
- var this$1 = this;
1643
+ defaultExport.prototype.renderTimeline = function renderTimeline (chart) {
1644
+ var this$1$1 = this;
1535
1645
 
1536
1646
  this.waitForLoaded(chart, "timeline", function () {
1537
1647
  var chartOptions = {
@@ -1541,9 +1651,9 @@
1541
1651
  if (chart.options.colors) {
1542
1652
  chartOptions.colors = chart.options.colors;
1543
1653
  }
1544
- var options = merge(merge(defaultOptions$2, chartOptions), chart.options.library || {});
1654
+ var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
1545
1655
 
1546
- var data = new this$1.library.visualization.DataTable();
1656
+ var data = new this$1$1.library.visualization.DataTable();
1547
1657
  data.addColumn({type: "string", id: "Name"});
1548
1658
  data.addColumn({type: "date", id: "Start"});
1549
1659
  data.addColumn({type: "date", id: "End"});
@@ -1551,18 +1661,20 @@
1551
1661
 
1552
1662
  chart.element.style.lineHeight = "normal";
1553
1663
 
1554
- this$1.drawChart(chart, "Timeline", data, options);
1664
+ this$1$1.drawChart(chart, "Timeline", data, options);
1555
1665
  });
1556
1666
  };
1557
1667
 
1558
- defaultExport$2.prototype.destroy = function destroy (chart) {
1668
+ // TODO remove resize events
1669
+ defaultExport.prototype.destroy = function destroy (chart) {
1559
1670
  if (chart.chart) {
1560
1671
  chart.chart.clearChart();
1561
1672
  }
1562
1673
  };
1563
1674
 
1564
- defaultExport$2.prototype.drawChart = function drawChart (chart, type, data, options) {
1675
+ defaultExport.prototype.drawChart = function drawChart (chart, type, data, options) {
1565
1676
  this.destroy(chart);
1677
+ if (chart.destroyed) { return; }
1566
1678
 
1567
1679
  if (chart.options.code) {
1568
1680
  window.console.log("var data = new google.visualization.DataTable(" + data.toJSON() + ");\nvar chart = new google.visualization." + type + "(element);\nchart.draw(data, " + JSON.stringify(options) + ");");
@@ -1574,8 +1686,8 @@
1574
1686
  });
1575
1687
  };
1576
1688
 
1577
- defaultExport$2.prototype.waitForLoaded = function waitForLoaded (chart, pack, callback) {
1578
- var this$1 = this;
1689
+ defaultExport.prototype.waitForLoaded = function waitForLoaded (chart, pack, callback) {
1690
+ var this$1$1 = this;
1579
1691
 
1580
1692
  if (!callback) {
1581
1693
  callback = pack;
@@ -1592,7 +1704,7 @@
1592
1704
  // https://groups.google.com/forum/#!topic/google-visualization-api/fMKJcyA2yyI
1593
1705
  var loadOptions = {
1594
1706
  packages: [pack],
1595
- callback: function () { this$1.runCallbacks(); }
1707
+ callback: function () { this$1$1.runCallbacks(); }
1596
1708
  };
1597
1709
  var config = chart.__config();
1598
1710
  if (config.language) {
@@ -1606,11 +1718,10 @@
1606
1718
  }
1607
1719
  };
1608
1720
 
1609
- defaultExport$2.prototype.runCallbacks = function runCallbacks () {
1610
- var cb, call;
1721
+ defaultExport.prototype.runCallbacks = function runCallbacks () {
1611
1722
  for (var i = 0; i < callbacks.length; i++) {
1612
- cb = callbacks[i];
1613
- call = this.library.visualization && ((cb.pack === "corechart" && this.library.visualization.LineChart) || (cb.pack === "timeline" && this.library.visualization.Timeline) || (cb.pack === "geochart" && this.library.visualization.GeoChart));
1723
+ var cb = callbacks[i];
1724
+ var call = this.library.visualization && ((cb.pack === "corechart" && this.library.visualization.LineChart) || (cb.pack === "timeline" && this.library.visualization.Timeline) || (cb.pack === "geochart" && this.library.visualization.GeoChart));
1614
1725
  if (call) {
1615
1726
  cb.callback();
1616
1727
  callbacks.splice(i, 1);
@@ -1620,45 +1731,49 @@
1620
1731
  };
1621
1732
 
1622
1733
  // cant use object as key
1623
- defaultExport$2.prototype.createDataTable = function createDataTable (series, columnType) {
1624
- var i, j, s, d, key, rows = [], sortedLabels = [];
1625
- for (i = 0; i < series.length; i++) {
1626
- s = series[i];
1734
+ defaultExport.prototype.createDataTable = function createDataTable (series, columnType) {
1735
+ var rows = [];
1736
+ var sortedLabels = [];
1737
+ for (var i = 0; i < series.length; i++) {
1738
+ var s = series[i];
1627
1739
  series[i].name = series[i].name || "Value";
1628
1740
 
1629
- for (j = 0; j < s.data.length; j++) {
1630
- d = s.data[j];
1631
- key = (columnType === "datetime") ? d[0].getTime() : d[0];
1741
+ for (var j = 0; j < s.data.length; j++) {
1742
+ var d = s.data[j];
1743
+ var key = columnType === "datetime" ? d[0].getTime() : d[0];
1632
1744
  if (!rows[key]) {
1633
1745
  rows[key] = new Array(series.length);
1634
1746
  sortedLabels.push(key);
1635
1747
  }
1636
- rows[key][i] = toFloat(d[1]);
1748
+ rows[key][i] = d[1];
1637
1749
  }
1638
1750
  }
1639
1751
 
1640
1752
  var rows2 = [];
1641
- var day = true;
1642
- var value;
1643
- for (j = 0; j < sortedLabels.length; j++) {
1644
- i = sortedLabels[j];
1753
+ var values = [];
1754
+ for (var j$1 = 0; j$1 < sortedLabels.length; j$1++) {
1755
+ var i$1 = sortedLabels[j$1];
1756
+ var value = (void 0);
1645
1757
  if (columnType === "datetime") {
1646
- value = new Date(toFloat(i));
1647
- day = day && isDay(value);
1648
- } else if (columnType === "number") {
1649
- value = toFloat(i);
1758
+ value = new Date(i$1);
1759
+ values.push(value);
1650
1760
  } else {
1651
- value = i;
1761
+ value = i$1;
1652
1762
  }
1653
- rows2.push([value].concat(rows[i]));
1763
+ rows2.push([value].concat(rows[i$1]));
1654
1764
  }
1765
+
1766
+ var day = true;
1655
1767
  if (columnType === "datetime") {
1656
1768
  rows2.sort(sortByTime);
1769
+
1770
+ var timeUnit = calculateTimeUnit(values, true);
1771
+ day = isDay(timeUnit);
1657
1772
  } else if (columnType === "number") {
1658
1773
  rows2.sort(sortByNumberSeries);
1659
1774
 
1660
- for (i = 0; i < rows2.length; i++) {
1661
- rows2[i][0] = toStr(rows2[i][0]);
1775
+ for (var i$2 = 0; i$2 < rows2.length; i$2++) {
1776
+ rows2[i$2][0] = toStr(rows2[i$2][0]);
1662
1777
  }
1663
1778
 
1664
1779
  columnType = "string";
@@ -1668,215 +1783,24 @@
1668
1783
  var data = new this.library.visualization.DataTable();
1669
1784
  columnType = columnType === "datetime" && day ? "date" : columnType;
1670
1785
  data.addColumn(columnType, "");
1671
- for (i = 0; i < series.length; i++) {
1672
- data.addColumn("number", series[i].name);
1786
+ for (var i$3 = 0; i$3 < series.length; i$3++) {
1787
+ data.addColumn("number", series[i$3].name);
1673
1788
  }
1674
1789
  data.addRows(rows2);
1675
1790
 
1676
1791
  return data;
1677
1792
  };
1678
1793
 
1679
- var pendingRequests = [], runningRequests = 0, maxRequests = 4;
1680
-
1681
- function pushRequest(url, success, error) {
1682
- pendingRequests.push([url, success, error]);
1683
- runNext();
1684
- }
1685
-
1686
- function runNext() {
1687
- if (runningRequests < maxRequests) {
1688
- var request = pendingRequests.shift();
1689
- if (request) {
1690
- runningRequests++;
1691
- getJSON(request[0], request[1], request[2]);
1692
- runNext();
1693
- }
1694
- }
1695
- }
1696
-
1697
- function requestComplete() {
1698
- runningRequests--;
1699
- runNext();
1700
- }
1701
-
1702
- function getJSON(url, success, error) {
1703
- ajaxCall(url, success, function (jqXHR, textStatus, errorThrown) {
1704
- var message = (typeof errorThrown === "string") ? errorThrown : errorThrown.message;
1705
- error(message);
1706
- });
1707
- }
1708
-
1709
- function ajaxCall(url, success, error) {
1710
- var $ = window.jQuery || window.Zepto || window.$;
1711
-
1712
- if ($ && $.ajax) {
1713
- $.ajax({
1714
- dataType: "json",
1715
- url: url,
1716
- success: success,
1717
- error: error,
1718
- complete: requestComplete
1719
- });
1720
- } else {
1721
- var xhr = new XMLHttpRequest();
1722
- xhr.open("GET", url, true);
1723
- xhr.setRequestHeader("Content-Type", "application/json");
1724
- xhr.onload = function () {
1725
- requestComplete();
1726
- if (xhr.status === 200) {
1727
- success(JSON.parse(xhr.responseText), xhr.statusText, xhr);
1728
- } else {
1729
- error(xhr, "error", xhr.statusText);
1730
- }
1731
- };
1732
- xhr.send();
1733
- }
1734
- }
1735
-
1736
- var config = {};
1737
1794
  var adapters = [];
1738
1795
 
1739
- // helpers
1740
-
1741
- function setText(element, text) {
1742
- if (document.body.innerText) {
1743
- element.innerText = text;
1744
- } else {
1745
- element.textContent = text;
1746
- }
1747
- }
1748
-
1749
- // TODO remove prefix for all messages
1750
- function chartError(element, message, noPrefix) {
1751
- if (!noPrefix) {
1752
- message = "Error Loading Chart: " + message;
1753
- }
1754
- setText(element, message);
1755
- element.style.color = "#ff0000";
1756
- }
1757
-
1758
- function errorCatcher(chart) {
1759
- try {
1760
- chart.__render();
1761
- } catch (err) {
1762
- chartError(chart.element, err.message);
1763
- throw err;
1764
- }
1765
- }
1766
-
1767
- function fetchDataSource(chart, dataSource) {
1768
- if (typeof dataSource === "string") {
1769
- pushRequest(dataSource, function (data) {
1770
- chart.rawData = data;
1771
- errorCatcher(chart);
1772
- }, function (message) {
1773
- chartError(chart.element, message);
1774
- });
1775
- } else if (typeof dataSource === "function") {
1776
- try {
1777
- dataSource(function (data) {
1778
- chart.rawData = data;
1779
- errorCatcher(chart);
1780
- }, function (message) {
1781
- chartError(chart.element, message, true);
1782
- });
1783
- } catch (err) {
1784
- chartError(chart.element, err, true);
1785
- }
1786
- } else {
1787
- chart.rawData = dataSource;
1788
- errorCatcher(chart);
1789
- }
1790
- }
1791
-
1792
- function addDownloadButton(chart) {
1793
- var element = chart.element;
1794
- var link = document.createElement("a");
1795
-
1796
- var download = chart.options.download;
1797
- if (download === true) {
1798
- download = {};
1799
- } else if (typeof download === "string") {
1800
- download = {filename: download};
1801
- }
1802
- link.download = download.filename || "chart.png"; // https://caniuse.com/download
1803
-
1804
- link.style.position = "absolute";
1805
- link.style.top = "20px";
1806
- link.style.right = "20px";
1807
- link.style.zIndex = 1000;
1808
- link.style.lineHeight = "20px";
1809
- link.target = "_blank"; // for safari
1810
- var image = document.createElement("img");
1811
- image.alt = "Download";
1812
- image.style.border = "none";
1813
- // icon from font-awesome
1814
- // http://fa2png.io/
1815
- image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAMAAAC6V+0/AAABCFBMVEUAAADMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMywEsqxAAAAV3RSTlMAAQIDBggJCgsMDQ4PERQaHB0eISIjJCouLzE0OTo/QUJHSUpLTU5PUllhYmltcHh5foWLjI+SlaCio6atr7S1t7m6vsHHyM7R2tze5Obo7fHz9ff5+/1hlxK2AAAA30lEQVQYGUXBhVYCQQBA0TdYWAt2d3d3YWAHyur7/z9xgD16Lw0DW+XKx+1GgX+FRzM3HWQWrHl5N/oapW5RPe0PkBu+UYeICvozTWZVK23Ao04B79oJrOsJDOoxkZoQPWgX29pHpCZEk7rEvQYiNSFq1UMqvlCjJkRBS1R8hb00Vb/TajtBL7nTHE1X1vyMQF732dQhyF2o6SAwrzP06iUQzvwsArlnzcOdrgBhJyHa1QOgO9U1GsKuvjUTjavliZYQ8nNPapG6sap/3nrIdJ6bOWzmX/fy0XVpfzZP3S8OJT3g9EEiJwAAAABJRU5ErkJggg==";
1816
- link.appendChild(image);
1817
- element.style.position = "relative";
1818
-
1819
- chart.__downloadAttached = true;
1820
-
1821
- // mouseenter
1822
- chart.__enterEvent = addEvent(element, "mouseover", function(e) {
1823
- var related = e.relatedTarget;
1824
- // check download option again to ensure it wasn't changed
1825
- if ((!related || (related !== this && !childOf(this, related))) && chart.options.download) {
1826
- link.href = chart.toImage(download);
1827
- element.appendChild(link);
1828
- }
1829
- });
1830
-
1831
- // mouseleave
1832
- chart.__leaveEvent = addEvent(element, "mouseout", function(e) {
1833
- var related = e.relatedTarget;
1834
- if (!related || (related !== this && !childOf(this, related))) {
1835
- if (link.parentNode) {
1836
- link.parentNode.removeChild(link);
1837
- }
1838
- }
1839
- });
1840
- }
1841
-
1842
- // https://stackoverflow.com/questions/10149963/adding-event-listener-cross-browser
1843
- function addEvent(elem, event, fn) {
1844
- if (elem.addEventListener) {
1845
- elem.addEventListener(event, fn, false);
1846
- return fn;
1847
- } else {
1848
- var fn2 = function() {
1849
- // set the this pointer same as addEventListener when fn is called
1850
- return(fn.call(elem, window.event));
1851
- };
1852
- elem.attachEvent("on" + event, fn2);
1853
- return fn2;
1854
- }
1855
- }
1856
-
1857
- function removeEvent(elem, event, fn) {
1858
- if (elem.removeEventListener) {
1859
- elem.removeEventListener(event, fn, false);
1860
- } else {
1861
- elem.detachEvent("on" + event, fn);
1862
- }
1863
- }
1864
-
1865
- // https://gist.github.com/shawnbot/4166283
1866
- function childOf(p, c) {
1867
- if (p === c) { return false; }
1868
- while (c && c !== p) { c = c.parentNode; }
1869
- return c === p;
1870
- }
1871
-
1872
1796
  function getAdapterType(library) {
1873
1797
  if (library) {
1874
1798
  if (library.product === "Highcharts") {
1875
1799
  return defaultExport$1;
1876
1800
  } else if (library.charts) {
1877
- return defaultExport$2;
1878
- } else if (isFunction(library)) {
1879
1801
  return defaultExport;
1802
+ } else if (isFunction(library)) {
1803
+ return defaultExport$2;
1880
1804
  }
1881
1805
  }
1882
1806
  throw new Error("Unknown adapter");
@@ -1884,11 +1808,14 @@
1884
1808
 
1885
1809
  function addAdapter(library) {
1886
1810
  var adapterType = getAdapterType(library);
1887
- var adapter = new adapterType(library);
1888
1811
 
1889
- if (adapters.indexOf(adapter) === -1) {
1890
- adapters.push(adapter);
1812
+ for (var i = 0; i < adapters.length; i++) {
1813
+ if (adapters[i].library === library) {
1814
+ return;
1815
+ }
1891
1816
  }
1817
+
1818
+ adapters.push(new adapterType(library));
1892
1819
  }
1893
1820
 
1894
1821
  function loadAdapters() {
@@ -1905,41 +1832,16 @@
1905
1832
  }
1906
1833
  }
1907
1834
 
1908
- function dataEmpty(data, chartType) {
1909
- if (chartType === "PieChart" || chartType === "GeoChart" || chartType === "Timeline") {
1910
- return data.length === 0;
1911
- } else {
1912
- for (var i = 0; i < data.length; i++) {
1913
- if (data[i].data.length > 0) {
1914
- return false;
1915
- }
1916
- }
1917
- return true;
1918
- }
1919
- }
1920
-
1921
- function renderChart(chartType, chart) {
1922
- if (chart.options.messages && chart.options.messages.empty && dataEmpty(chart.data, chartType)) {
1923
- setText(chart.element, chart.options.messages.empty);
1924
- } else {
1925
- callAdapter(chartType, chart);
1926
- if (chart.options.download && !chart.__downloadAttached && chart.adapter === "chartjs") {
1927
- addDownloadButton(chart);
1928
- }
1929
- }
1930
- }
1931
-
1932
1835
  // TODO remove chartType if cross-browser way
1933
1836
  // to get the name of the chart class
1934
1837
  function callAdapter(chartType, chart) {
1935
- var i, adapter, fnName, adapterName;
1936
- fnName = "render" + chartType;
1937
- adapterName = chart.options.adapter;
1838
+ var fnName = "render" + chartType;
1839
+ var adapterName = chart.options.adapter;
1938
1840
 
1939
1841
  loadAdapters();
1940
1842
 
1941
- for (i = 0; i < adapters.length; i++) {
1942
- adapter = adapters[i];
1843
+ for (var i = 0; i < adapters.length; i++) {
1844
+ var adapter = adapters[i];
1943
1845
  if ((!adapterName || adapterName === adapter.name) && isFunction(adapter[fnName])) {
1944
1846
  chart.adapter = adapter.name;
1945
1847
  chart.__adapterObject = adapter;
@@ -1954,36 +1856,73 @@
1954
1856
  }
1955
1857
  }
1956
1858
 
1957
- // process data
1859
+ var Chartkick = {
1860
+ charts: {},
1861
+ configure: function (options) {
1862
+ for (var key in options) {
1863
+ if (Object.prototype.hasOwnProperty.call(options, key)) {
1864
+ Chartkick.config[key] = options[key];
1865
+ }
1866
+ }
1867
+ },
1868
+ setDefaultOptions: function (opts) {
1869
+ Chartkick.options = opts;
1870
+ },
1871
+ eachChart: function (callback) {
1872
+ for (var chartId in Chartkick.charts) {
1873
+ if (Object.prototype.hasOwnProperty.call(Chartkick.charts, chartId)) {
1874
+ callback(Chartkick.charts[chartId]);
1875
+ }
1876
+ }
1877
+ },
1878
+ destroyAll: function () {
1879
+ for (var chartId in Chartkick.charts) {
1880
+ if (Object.prototype.hasOwnProperty.call(Chartkick.charts, chartId)) {
1881
+ Chartkick.charts[chartId].destroy();
1882
+ delete Chartkick.charts[chartId];
1883
+ }
1884
+ }
1885
+ },
1886
+ config: {},
1887
+ options: {},
1888
+ adapters: adapters,
1889
+ addAdapter: addAdapter,
1890
+ use: function (adapter) {
1891
+ addAdapter(adapter);
1892
+ return Chartkick;
1893
+ }
1894
+ };
1958
1895
 
1959
- var toFormattedKey = function (key, keyType) {
1896
+ function formatSeriesBubble(data) {
1897
+ var r = [];
1898
+ for (var i = 0; i < data.length; i++) {
1899
+ r.push([toFloat(data[i][0]), toFloat(data[i][1]), toFloat(data[i][2])]);
1900
+ }
1901
+ return r;
1902
+ }
1903
+
1904
+ // casts data to proper type
1905
+ // sorting is left to adapters
1906
+ function formatSeriesData(data, keyType) {
1907
+ if (keyType === "bubble") {
1908
+ return formatSeriesBubble(data);
1909
+ }
1910
+
1911
+ var keyFunc;
1960
1912
  if (keyType === "number") {
1961
- key = toFloat(key);
1913
+ keyFunc = toFloat;
1962
1914
  } else if (keyType === "datetime") {
1963
- key = toDate(key);
1915
+ keyFunc = toDate;
1964
1916
  } else {
1965
- key = toStr(key);
1917
+ keyFunc = toStr;
1966
1918
  }
1967
- return key;
1968
- };
1969
1919
 
1970
- var formatSeriesData = function (data, keyType) {
1971
- var r = [], key, j;
1972
- for (j = 0; j < data.length; j++) {
1973
- if (keyType === "bubble") {
1974
- r.push([toFloat(data[j][0]), toFloat(data[j][1]), toFloat(data[j][2])]);
1975
- } else {
1976
- key = toFormattedKey(data[j][0], keyType);
1977
- r.push([key, toFloat(data[j][1])]);
1978
- }
1979
- }
1980
- if (keyType === "datetime") {
1981
- r.sort(sortByTime);
1982
- } else if (keyType === "number") {
1983
- r.sort(sortByNumberSeries);
1920
+ var r = [];
1921
+ for (var i = 0; i < data.length; i++) {
1922
+ r.push([keyFunc(data[i][0]), toFloat(data[i][1])]);
1984
1923
  }
1985
1924
  return r;
1986
- };
1925
+ }
1987
1926
 
1988
1927
  function detectXType(series, noDatetime, options) {
1989
1928
  if (dataEmpty(series)) {
@@ -2002,10 +1941,9 @@
2002
1941
  }
2003
1942
 
2004
1943
  function detectXTypeWithFunction(series, func) {
2005
- var i, j, data;
2006
- for (i = 0; i < series.length; i++) {
2007
- data = toArr(series[i].data);
2008
- for (j = 0; j < data.length; j++) {
1944
+ for (var i = 0; i < series.length; i++) {
1945
+ var data = toArr(series[i].data);
1946
+ for (var j = 0; j < data.length; j++) {
2009
1947
  if (!func(data[j][0])) {
2010
1948
  return false;
2011
1949
  }
@@ -2017,11 +1955,11 @@
2017
1955
  // creates a shallow copy of each element of the array
2018
1956
  // elements are expected to be objects
2019
1957
  function copySeries(series) {
2020
- var newSeries = [], i, j;
2021
- for (i = 0; i < series.length; i++) {
1958
+ var newSeries = [];
1959
+ for (var i = 0; i < series.length; i++) {
2022
1960
  var copy = {};
2023
- for (j in series[i]) {
2024
- if (series[i].hasOwnProperty(j)) {
1961
+ for (var j in series[i]) {
1962
+ if (Object.prototype.hasOwnProperty.call(series[i], j)) {
2025
1963
  copy[j] = series[i][j];
2026
1964
  }
2027
1965
  }
@@ -2031,62 +1969,237 @@
2031
1969
  }
2032
1970
 
2033
1971
  function processSeries(chart, keyType, noDatetime) {
2034
- var i;
2035
-
2036
1972
  var opts = chart.options;
2037
1973
  var series = chart.rawData;
2038
1974
 
2039
1975
  // see if one series or multiple
2040
- if (!isArray(series) || typeof series[0] !== "object" || isArray(series[0])) {
1976
+ chart.singleSeriesFormat = !isArray(series) || !isPlainObject(series[0]);
1977
+ if (chart.singleSeriesFormat) {
2041
1978
  series = [{name: opts.label, data: series}];
2042
- chart.hideLegend = true;
2043
- } else {
2044
- chart.hideLegend = false;
2045
1979
  }
2046
1980
 
2047
1981
  // convert to array
2048
1982
  // must come before dataEmpty check
2049
1983
  series = copySeries(series);
2050
- for (i = 0; i < series.length; i++) {
1984
+ for (var i = 0; i < series.length; i++) {
2051
1985
  series[i].data = toArr(series[i].data);
2052
1986
  }
2053
1987
 
2054
- chart.xtype = keyType ? keyType : (opts.discrete ? "string" : detectXType(series, noDatetime, opts));
1988
+ chart.xtype = keyType || (opts.discrete ? "string" : detectXType(series, noDatetime, opts));
2055
1989
 
2056
1990
  // right format
2057
- for (i = 0; i < series.length; i++) {
2058
- series[i].data = formatSeriesData(series[i].data, chart.xtype);
1991
+ for (var i$1 = 0; i$1 < series.length; i$1++) {
1992
+ series[i$1].data = formatSeriesData(series[i$1].data, chart.xtype);
2059
1993
  }
2060
1994
 
2061
1995
  return series;
2062
1996
  }
2063
1997
 
2064
1998
  function processSimple(chart) {
2065
- var perfectData = toArr(chart.rawData), i;
2066
- for (i = 0; i < perfectData.length; i++) {
1999
+ var perfectData = toArr(chart.rawData);
2000
+ for (var i = 0; i < perfectData.length; i++) {
2067
2001
  perfectData[i] = [toStr(perfectData[i][0]), toFloat(perfectData[i][1])];
2068
2002
  }
2069
2003
  return perfectData;
2070
2004
  }
2071
2005
 
2072
- // define classes
2006
+ function dataEmpty(data, chartType) {
2007
+ if (chartType === "PieChart" || chartType === "GeoChart" || chartType === "Timeline") {
2008
+ return data.length === 0;
2009
+ } else {
2010
+ for (var i = 0; i < data.length; i++) {
2011
+ if (data[i].data.length > 0) {
2012
+ return false;
2013
+ }
2014
+ }
2015
+ return true;
2016
+ }
2017
+ }
2073
2018
 
2074
- var Chart = function Chart(element, dataSource, options) {
2075
- var elementId;
2019
+ function addDownloadButton(chart) {
2020
+ var download = chart.options.download;
2021
+ if (download === true) {
2022
+ download = {};
2023
+ } else if (typeof download === "string") {
2024
+ download = {filename: download};
2025
+ }
2026
+
2027
+ var link = document.createElement("a");
2028
+ link.download = download.filename || "chart.png";
2029
+ link.style.position = "absolute";
2030
+ link.style.top = "20px";
2031
+ link.style.right = "20px";
2032
+ link.style.zIndex = 1000;
2033
+ link.style.lineHeight = "20px";
2034
+ link.target = "_blank"; // for safari
2035
+
2036
+ var image = document.createElement("img");
2037
+ // icon from Font Awesome, modified to set fill color
2038
+ var svg = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path fill=\"#CCCCCC\" d=\"M344 240h-56L287.1 152c0-13.25-10.75-24-24-24h-16C234.7 128 223.1 138.8 223.1 152L224 240h-56c-9.531 0-18.16 5.656-22 14.38C142.2 263.1 143.9 273.3 150.4 280.3l88.75 96C243.7 381.2 250.1 384 256.8 384c7.781-.3125 13.25-2.875 17.75-7.844l87.25-96c6.406-7.031 8.031-17.19 4.188-25.88S353.5 240 344 240zM256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 464c-114.7 0-208-93.31-208-208S141.3 48 256 48s208 93.31 208 208S370.7 464 256 464z\"/></svg>";
2039
+ image.src = "data:image/svg+xml;utf8," + (encodeURIComponent(svg));
2040
+ image.alt = "Download";
2041
+ image.style.width = "20px";
2042
+ image.style.height = "20px";
2043
+ image.style.border = "none";
2044
+ link.appendChild(image);
2045
+
2046
+ var element = chart.element;
2047
+ element.style.position = "relative";
2048
+
2049
+ chart.__downloadAttached = true;
2050
+
2051
+ // mouseenter
2052
+ chart.__enterEvent = element.addEventListener("mouseover", function (e) {
2053
+ var related = e.relatedTarget;
2054
+ // check download option again to ensure it wasn't changed
2055
+ if ((!related || (related !== this && !this.contains(related))) && chart.options.download) {
2056
+ link.href = chart.toImage(download);
2057
+ element.appendChild(link);
2058
+ }
2059
+ });
2060
+
2061
+ // mouseleave
2062
+ chart.__leaveEvent = element.addEventListener("mouseout", function (e) {
2063
+ var related = e.relatedTarget;
2064
+ if (!related || (related !== this && !this.contains(related))) {
2065
+ if (link.parentNode) {
2066
+ link.parentNode.removeChild(link);
2067
+ }
2068
+ }
2069
+ });
2070
+ }
2071
+
2072
+ var pendingRequests = [];
2073
+ var runningRequests = 0;
2074
+ var maxRequests = 4;
2075
+
2076
+ function pushRequest(url, success, error) {
2077
+ pendingRequests.push([url, success, error]);
2078
+ runNext();
2079
+ }
2080
+
2081
+ function runNext() {
2082
+ if (runningRequests < maxRequests) {
2083
+ var request = pendingRequests.shift();
2084
+ if (request) {
2085
+ runningRequests++;
2086
+ getJSON(request[0], request[1], request[2]);
2087
+ runNext();
2088
+ }
2089
+ }
2090
+ }
2091
+
2092
+ function requestComplete() {
2093
+ runningRequests--;
2094
+ runNext();
2095
+ }
2096
+
2097
+ function getJSON(url, success, error) {
2098
+ var xhr = new XMLHttpRequest();
2099
+ xhr.open("GET", url, true);
2100
+ xhr.setRequestHeader("Content-Type", "application/json");
2101
+ xhr.onload = function () {
2102
+ requestComplete();
2103
+ if (xhr.status === 200) {
2104
+ success(JSON.parse(xhr.responseText));
2105
+ } else {
2106
+ error(xhr.statusText);
2107
+ }
2108
+ };
2109
+ xhr.send();
2110
+ }
2111
+
2112
+ // helpers
2113
+
2114
+ function setText(element, text) {
2115
+ element.textContent = text;
2116
+ }
2117
+
2118
+ // TODO remove prefix for all messages
2119
+ function chartError(element, message, noPrefix) {
2120
+ if (!noPrefix) {
2121
+ message = "Error Loading Chart: " + message;
2122
+ }
2123
+ setText(element, message);
2124
+ element.style.color = "#ff0000";
2125
+ }
2126
+
2127
+ function errorCatcher(chart) {
2128
+ try {
2129
+ chart.__render();
2130
+ } catch (err) {
2131
+ chartError(chart.element, err.message);
2132
+ throw err;
2133
+ }
2134
+ }
2135
+
2136
+ function fetchDataSource(chart, dataSource, showLoading) {
2137
+ // only show loading message for urls and callbacks
2138
+ if (showLoading && chart.options.loading && (typeof dataSource === "string" || typeof dataSource === "function")) {
2139
+ setText(chart.element, chart.options.loading);
2140
+ }
2141
+
2142
+ if (typeof dataSource === "string") {
2143
+ pushRequest(dataSource, function (data) {
2144
+ chart.rawData = data;
2145
+ errorCatcher(chart);
2146
+ }, function (message) {
2147
+ chartError(chart.element, message);
2148
+ });
2149
+ } else if (typeof dataSource === "function") {
2150
+ try {
2151
+ dataSource(function (data) {
2152
+ chart.rawData = data;
2153
+ errorCatcher(chart);
2154
+ }, function (message) {
2155
+ chartError(chart.element, message, true);
2156
+ });
2157
+ } catch (err) {
2158
+ chartError(chart.element, err, true);
2159
+ }
2160
+ } else {
2161
+ chart.rawData = dataSource;
2162
+ errorCatcher(chart);
2163
+ }
2164
+ }
2165
+
2166
+ function renderChart(chartType, chart) {
2167
+ if (dataEmpty(chart.data, chartType)) {
2168
+ var message = chart.options.empty || (chart.options.messages && chart.options.messages.empty) || "No data";
2169
+ setText(chart.element, message);
2170
+ } else {
2171
+ callAdapter(chartType, chart);
2172
+ // TODO add downloadSupported method to adapter
2173
+ if (chart.options.download && !chart.__downloadAttached && chart.adapter === "chartjs") {
2174
+ addDownloadButton(chart);
2175
+ }
2176
+ }
2177
+ }
2178
+
2179
+ function getElement(element) {
2076
2180
  if (typeof element === "string") {
2077
- elementId = element;
2181
+ var elementId = element;
2078
2182
  element = document.getElementById(element);
2079
2183
  if (!element) {
2080
2184
  throw new Error("No element with id " + elementId);
2081
2185
  }
2082
2186
  }
2083
- this.element = element;
2187
+ return element;
2188
+ }
2189
+
2190
+ // define classes
2191
+
2192
+ var Chart = function Chart(element, dataSource, options) {
2193
+ this.element = getElement(element);
2084
2194
  this.options = merge(Chartkick.options, options || {});
2085
2195
  this.dataSource = dataSource;
2086
2196
 
2087
- Chartkick.charts[element.id] = this;
2197
+ // TODO handle charts without an id for eachChart and destroyAll
2198
+ if (this.element.id) {
2199
+ Chartkick.charts[this.element.id] = this;
2200
+ }
2088
2201
 
2089
- fetchDataSource(this, dataSource);
2202
+ fetchDataSource(this, dataSource, true);
2090
2203
 
2091
2204
  if (this.options.refresh) {
2092
2205
  this.startRefresh();
@@ -2122,7 +2235,7 @@
2122
2235
  if (options) {
2123
2236
  this.__updateOptions(options);
2124
2237
  }
2125
- fetchDataSource(this, dataSource);
2238
+ fetchDataSource(this, dataSource, true);
2126
2239
  };
2127
2240
 
2128
2241
  Chart.prototype.setOptions = function setOptions (options) {
@@ -2146,7 +2259,7 @@
2146
2259
  };
2147
2260
 
2148
2261
  Chart.prototype.startRefresh = function startRefresh () {
2149
- var this$1 = this;
2262
+ var this$1$1 = this;
2150
2263
 
2151
2264
  var refresh = this.options.refresh;
2152
2265
 
@@ -2156,8 +2269,8 @@
2156
2269
 
2157
2270
  if (!this.intervalId) {
2158
2271
  if (refresh) {
2159
- this.intervalId = setInterval( function () {
2160
- this$1.refreshData();
2272
+ this.intervalId = setInterval(function () {
2273
+ this$1$1.refreshData();
2161
2274
  }, refresh * 1000);
2162
2275
  } else {
2163
2276
  throw new Error("No refresh interval");
@@ -2173,11 +2286,12 @@
2173
2286
  };
2174
2287
 
2175
2288
  Chart.prototype.toImage = function toImage (download) {
2289
+ // TODO move logic to adapter
2176
2290
  if (this.adapter === "chartjs") {
2177
2291
  if (download && download.background && download.background !== "transparent") {
2178
2292
  // https://stackoverflow.com/questions/30464750/chartjs-line-chart-set-background-color
2179
- var canvas = this.chart.chart.canvas;
2180
- var ctx = this.chart.chart.ctx;
2293
+ var canvas = this.chart.canvas;
2294
+ var ctx = this.chart.ctx;
2181
2295
  var tmpCanvas = document.createElement("canvas");
2182
2296
  var tmpCtx = tmpCanvas.getContext("2d");
2183
2297
  tmpCanvas.width = ctx.canvas.width;
@@ -2190,23 +2304,24 @@
2190
2304
  return this.chart.toBase64Image();
2191
2305
  }
2192
2306
  } else {
2193
- // TODO throw error in next major version
2194
- // throw new Error("Feature only available for Chart.js");
2195
- return null;
2307
+ throw new Error("Feature only available for Chart.js");
2196
2308
  }
2197
2309
  };
2198
2310
 
2199
2311
  Chart.prototype.destroy = function destroy () {
2312
+ this.destroyed = true;
2313
+ this.stopRefresh();
2314
+
2200
2315
  if (this.__adapterObject) {
2201
2316
  this.__adapterObject.destroy(this);
2202
2317
  }
2203
2318
 
2204
2319
  if (this.__enterEvent) {
2205
- removeEvent(this.element, "mouseover", this.__enterEvent);
2320
+ this.element.removeEventListener("mouseover", this.__enterEvent);
2206
2321
  }
2207
2322
 
2208
2323
  if (this.__leaveEvent) {
2209
- removeEvent(this.element, "mouseout", this.__leaveEvent);
2324
+ this.element.removeEventListener("mouseout", this.__leaveEvent);
2210
2325
  }
2211
2326
  };
2212
2327
 
@@ -2225,7 +2340,7 @@
2225
2340
  };
2226
2341
 
2227
2342
  Chart.prototype.__config = function __config () {
2228
- return config;
2343
+ return Chartkick.config;
2229
2344
  };
2230
2345
 
2231
2346
  var LineChart = /*@__PURE__*/(function (Chart) {
@@ -2398,8 +2513,8 @@
2398
2513
  Timeline.prototype.constructor = Timeline;
2399
2514
 
2400
2515
  Timeline.prototype.__processData = function __processData () {
2401
- var i, data = this.rawData;
2402
- for (i = 0; i < data.length; i++) {
2516
+ var data = this.rawData;
2517
+ for (var i = 0; i < data.length; i++) {
2403
2518
  data[i][1] = toDate(data[i][1]);
2404
2519
  data[i][2] = toDate(data[i][2]);
2405
2520
  }
@@ -2413,47 +2528,38 @@
2413
2528
  return Timeline;
2414
2529
  }(Chart));
2415
2530
 
2416
- var Chartkick = {
2417
- LineChart: LineChart,
2418
- PieChart: PieChart,
2419
- ColumnChart: ColumnChart,
2420
- BarChart: BarChart,
2421
- AreaChart: AreaChart,
2422
- GeoChart: GeoChart,
2423
- ScatterChart: ScatterChart,
2424
- BubbleChart: BubbleChart,
2425
- Timeline: Timeline,
2426
- charts: {},
2427
- configure: function (options) {
2428
- for (var key in options) {
2429
- if (options.hasOwnProperty(key)) {
2430
- config[key] = options[key];
2431
- }
2432
- }
2433
- },
2434
- setDefaultOptions: function (opts) {
2435
- Chartkick.options = opts;
2436
- },
2437
- eachChart: function (callback) {
2438
- for (var chartId in Chartkick.charts) {
2439
- if (Chartkick.charts.hasOwnProperty(chartId)) {
2440
- callback(Chartkick.charts[chartId]);
2441
- }
2442
- }
2443
- },
2444
- config: config,
2445
- options: {},
2446
- adapters: adapters,
2447
- addAdapter: addAdapter,
2448
- use: function(adapter) {
2449
- addAdapter(adapter);
2450
- return Chartkick;
2451
- }
2452
- };
2531
+ Chartkick.LineChart = LineChart;
2532
+ Chartkick.PieChart = PieChart;
2533
+ Chartkick.ColumnChart = ColumnChart;
2534
+ Chartkick.BarChart = BarChart;
2535
+ Chartkick.AreaChart = AreaChart;
2536
+ Chartkick.GeoChart = GeoChart;
2537
+ Chartkick.ScatterChart = ScatterChart;
2538
+ Chartkick.BubbleChart = BubbleChart;
2539
+ Chartkick.Timeline = Timeline;
2453
2540
 
2454
2541
  // not ideal, but allows for simpler integration
2455
2542
  if (typeof window !== "undefined" && !window.Chartkick) {
2456
2543
  window.Chartkick = Chartkick;
2544
+
2545
+ // clean up previous charts before Turbolinks loads new page
2546
+ document.addEventListener("turbolinks:before-render", function () {
2547
+ if (Chartkick.config.autoDestroy !== false) {
2548
+ Chartkick.destroyAll();
2549
+ }
2550
+ });
2551
+
2552
+ // clean up previous charts before Turbo loads new page
2553
+ document.addEventListener("turbo:before-render", function () {
2554
+ if (Chartkick.config.autoDestroy !== false) {
2555
+ Chartkick.destroyAll();
2556
+ }
2557
+ });
2558
+
2559
+ // use setTimeout so charting library can come later in same JS file
2560
+ setTimeout(function () {
2561
+ window.dispatchEvent(new Event("chartkick:load"));
2562
+ }, 0);
2457
2563
  }
2458
2564
 
2459
2565
  // backwards compatibility for esm require
@@ -2461,4 +2567,4 @@
2461
2567
 
2462
2568
  return Chartkick;
2463
2569
 
2464
- })));
2570
+ }));