chartkick 3.4.2 → 4.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,15 +1,15 @@
1
- /*
1
+ /*!
2
2
  * Chartkick.js
3
3
  * Create beautiful charts with one line of JavaScript
4
4
  * https://github.com/ankane/chartkick.js
5
- * v3.2.1
5
+ * v4.0.4
6
6
  * MIT License
7
7
  */
8
8
 
9
9
  (function (global, factory) {
10
10
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
11
11
  typeof define === 'function' && define.amd ? define(factory) :
12
- (global = global || self, global.Chartkick = factory());
12
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Chartkick = factory());
13
13
  }(this, (function () { 'use strict';
14
14
 
15
15
  function isArray(variable) {
@@ -55,42 +55,6 @@
55
55
 
56
56
  var DATE_PATTERN = /^(\d\d\d\d)(-)?(\d\d)(-)?(\d\d)$/i;
57
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
93
-
94
58
  function negativeValues(series) {
95
59
  var i, j, data;
96
60
  for (i = 0; i < series.length; i++) {
@@ -120,15 +84,16 @@
120
84
  } else {
121
85
  n = toStr(n);
122
86
  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);
126
- return new Date(year, month, day);
127
- } else { // str
87
+ year = parseInt(matches[1], 10);
88
+ month = parseInt(matches[3], 10) - 1;
89
+ day = parseInt(matches[5], 10);
90
+ return new Date(year, month, day);
91
+ } else {
128
92
  // try our best to get the str into iso8601
129
93
  // TODO be smarter about this
130
94
  var str = n.replace(/ /, "T").replace(" ", "").replace("UTC", "Z");
131
- n = parseISO8601(str) || new Date(n);
95
+ // Date.parse returns milliseconds if valid and NaN if invalid
96
+ n = new Date(Date.parse(str) || n);
132
97
  }
133
98
  }
134
99
  }
@@ -154,8 +119,8 @@
154
119
  var options = merge({}, defaultOptions);
155
120
  options = merge(options, chartOptions || {});
156
121
 
157
- if (chart.hideLegend || "legend" in opts) {
158
- hideLegend(options, opts.legend, chart.hideLegend);
122
+ if (chart.singleSeriesFormat || "legend" in opts) {
123
+ hideLegend(options, opts.legend, chart.singleSeriesFormat);
159
124
  }
160
125
 
161
126
  if (opts.title) {
@@ -361,42 +326,49 @@
361
326
  var baseOptions = {
362
327
  maintainAspectRatio: false,
363
328
  animation: false,
364
- tooltips: {
365
- displayColors: false,
366
- callbacks: {}
329
+ plugins: {
330
+ legend: {},
331
+ tooltip: {
332
+ displayColors: false,
333
+ callbacks: {}
334
+ },
335
+ title: {
336
+ font: {
337
+ size: 20
338
+ },
339
+ color: "#333"
340
+ }
367
341
  },
368
- legend: {},
369
- title: {fontSize: 20, fontColor: "#333"}
342
+ interaction: {}
370
343
  };
371
344
 
372
- var defaultOptions = {
345
+ var defaultOptions$2 = {
373
346
  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
347
+ y: {
348
+ ticks: {
349
+ maxTicksLimit: 4
350
+ },
351
+ title: {
352
+ font: {
353
+ size: 16
390
354
  },
391
- scaleLabel: {
392
- fontSize: 16,
393
- // fontStyle: "bold",
394
- fontColor: "#333"
355
+ color: "#333"
356
+ },
357
+ grid: {}
358
+ },
359
+ x: {
360
+ grid: {
361
+ drawOnChartArea: false
362
+ },
363
+ title: {
364
+ font: {
365
+ size: 16
395
366
  },
396
- time: {},
397
- ticks: {}
398
- }
399
- ]
367
+ color: "#333"
368
+ },
369
+ time: {},
370
+ ticks: {}
371
+ }
400
372
  }
401
373
  };
402
374
 
@@ -407,66 +379,66 @@
407
379
  "#6633CC", "#E67300", "#8B0707", "#329262", "#5574A6", "#651067"
408
380
  ];
409
381
 
410
- var hideLegend = function (options, legend, hideLegend) {
382
+ var hideLegend$2 = function (options, legend, hideLegend) {
411
383
  if (legend !== undefined) {
412
- options.legend.display = !!legend;
384
+ options.plugins.legend.display = !!legend;
413
385
  if (legend && legend !== true) {
414
- options.legend.position = legend;
386
+ options.plugins.legend.position = legend;
415
387
  }
416
388
  } else if (hideLegend) {
417
- options.legend.display = false;
389
+ options.plugins.legend.display = false;
418
390
  }
419
391
  };
420
392
 
421
- var setTitle = function (options, title) {
422
- options.title.display = true;
423
- options.title.text = title;
393
+ var setTitle$2 = function (options, title) {
394
+ options.plugins.title.display = true;
395
+ options.plugins.title.text = title;
424
396
  };
425
397
 
426
- var setMin = function (options, min) {
398
+ var setMin$2 = function (options, min) {
427
399
  if (min !== null) {
428
- options.scales.yAxes[0].ticks.min = toFloat(min);
400
+ options.scales.y.min = toFloat(min);
429
401
  }
430
402
  };
431
403
 
432
- var setMax = function (options, max) {
433
- options.scales.yAxes[0].ticks.max = toFloat(max);
404
+ var setMax$2 = function (options, max) {
405
+ options.scales.y.max = toFloat(max);
434
406
  };
435
407
 
436
- var setBarMin = function (options, min) {
408
+ var setBarMin$1 = function (options, min) {
437
409
  if (min !== null) {
438
- options.scales.xAxes[0].ticks.min = toFloat(min);
410
+ options.scales.x.min = toFloat(min);
439
411
  }
440
412
  };
441
413
 
442
- var setBarMax = function (options, max) {
443
- options.scales.xAxes[0].ticks.max = toFloat(max);
414
+ var setBarMax$1 = function (options, max) {
415
+ options.scales.x.max = toFloat(max);
444
416
  };
445
417
 
446
- var setStacked = function (options, stacked) {
447
- options.scales.xAxes[0].stacked = !!stacked;
448
- options.scales.yAxes[0].stacked = !!stacked;
418
+ var setStacked$2 = function (options, stacked) {
419
+ options.scales.x.stacked = !!stacked;
420
+ options.scales.y.stacked = !!stacked;
449
421
  };
450
422
 
451
- var setXtitle = function (options, title) {
452
- options.scales.xAxes[0].scaleLabel.display = true;
453
- options.scales.xAxes[0].scaleLabel.labelString = title;
423
+ var setXtitle$2 = function (options, title) {
424
+ options.scales.x.title.display = true;
425
+ options.scales.x.title.text = title;
454
426
  };
455
427
 
456
- var setYtitle = function (options, title) {
457
- options.scales.yAxes[0].scaleLabel.display = true;
458
- options.scales.yAxes[0].scaleLabel.labelString = title;
428
+ var setYtitle$2 = function (options, title) {
429
+ options.scales.y.title.display = true;
430
+ options.scales.y.title.text = title;
459
431
  };
460
432
 
461
433
  // https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
462
- var addOpacity = function(hex, opacity) {
434
+ var addOpacity = function (hex, opacity) {
463
435
  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
464
436
  return result ? "rgba(" + parseInt(result[1], 16) + ", " + parseInt(result[2], 16) + ", " + parseInt(result[3], 16) + ", " + opacity + ")" : hex;
465
437
  };
466
438
 
467
439
  // check if not null or undefined
468
440
  // https://stackoverflow.com/a/27757708/1177228
469
- var notnull = function(x) {
441
+ var notnull = function (x) {
470
442
  return x != null;
471
443
  };
472
444
 
@@ -477,9 +449,9 @@
477
449
  } else if (maxLabelSize < 10) {
478
450
  maxLabelSize = 10;
479
451
  }
480
- if (!options.scales.xAxes[0].ticks.callback) {
481
- options.scales.xAxes[0].ticks.callback = function (value) {
482
- value = toStr(value);
452
+ if (!options.scales.x.ticks.callback) {
453
+ options.scales.x.ticks.callback = function (value) {
454
+ value = toStr(this.getLabelForValue(value));
483
455
  if (value.length > maxLabelSize) {
484
456
  return value.substring(0, maxLabelSize - 2) + "...";
485
457
  } else {
@@ -489,7 +461,7 @@
489
461
  }
490
462
  };
491
463
 
492
- var setFormatOptions = function(chart, options, chartType) {
464
+ var setFormatOptions$1 = function (chart, options, chartType) {
493
465
  var formatOptions = {
494
466
  prefix: chart.options.prefix,
495
467
  suffix: chart.options.suffix,
@@ -529,49 +501,49 @@
529
501
  }
530
502
 
531
503
  if (chartType !== "pie") {
532
- var myAxes = options.scales.yAxes;
504
+ var axis = options.scales.y;
533
505
  if (chartType === "bar") {
534
- myAxes = options.scales.xAxes;
506
+ axis = options.scales.x;
535
507
  }
536
508
 
537
509
  if (formatOptions.byteScale) {
538
- if (!myAxes[0].ticks.stepSize) {
539
- myAxes[0].ticks.stepSize = formatOptions.byteScale / 2;
510
+ if (!axis.ticks.stepSize) {
511
+ axis.ticks.stepSize = formatOptions.byteScale / 2;
540
512
  }
541
- if (!myAxes[0].ticks.maxTicksLimit) {
542
- myAxes[0].ticks.maxTicksLimit = 4;
513
+ if (!axis.ticks.maxTicksLimit) {
514
+ axis.ticks.maxTicksLimit = 4;
543
515
  }
544
516
  }
545
517
 
546
- if (!myAxes[0].ticks.callback) {
547
- myAxes[0].ticks.callback = function (value) {
518
+ if (!axis.ticks.callback) {
519
+ axis.ticks.callback = function (value) {
548
520
  return formatValue("", value, formatOptions, true);
549
521
  };
550
522
  }
551
523
  }
552
524
 
553
- if (!options.tooltips.callbacks.label) {
525
+ if (!options.plugins.tooltip.callbacks.label) {
554
526
  if (chartType === "scatter") {
555
- options.tooltips.callbacks.label = function (item, data) {
556
- var label = data.datasets[item.datasetIndex].label || '';
527
+ options.plugins.tooltip.callbacks.label = function (context) {
528
+ var label = context.dataset.label || '';
557
529
  if (label) {
558
530
  label += ': ';
559
531
  }
560
- return label + '(' + item.xLabel + ', ' + item.yLabel + ')';
532
+ return label + '(' + context.label + ', ' + context.formattedValue + ')';
561
533
  };
562
534
  } else if (chartType === "bubble") {
563
- options.tooltips.callbacks.label = function (item, data) {
564
- var label = data.datasets[item.datasetIndex].label || '';
535
+ options.plugins.tooltip.callbacks.label = function (context) {
536
+ var label = context.dataset.label || '';
565
537
  if (label) {
566
538
  label += ': ';
567
539
  }
568
- var dataPoint = data.datasets[item.datasetIndex].data[item.index];
569
- return label + '(' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.v + ')';
540
+ var dataPoint = context.raw;
541
+ return label + '(' + dataPoint.x + ', ' + dataPoint.y + ', ' + dataPoint.v + ')';
570
542
  };
571
543
  } else if (chartType === "pie") {
572
544
  // need to use separate label for pie charts
573
- options.tooltips.callbacks.label = function (tooltipItem, data) {
574
- var dataLabel = data.labels[tooltipItem.index];
545
+ options.plugins.tooltip.callbacks.label = function (context) {
546
+ var dataLabel = context.label;
575
547
  var value = ': ';
576
548
 
577
549
  if (isArray(dataLabel)) {
@@ -583,24 +555,29 @@
583
555
  dataLabel += value;
584
556
  }
585
557
 
586
- return formatValue(dataLabel, data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index], formatOptions);
558
+ return formatValue(dataLabel, context.parsed, formatOptions);
587
559
  };
588
560
  } else {
589
- var valueLabel = chartType === "bar" ? "xLabel" : "yLabel";
590
- options.tooltips.callbacks.label = function (tooltipItem, data) {
591
- var label = data.datasets[tooltipItem.datasetIndex].label || '';
561
+ var valueLabel = chartType === "bar" ? "x" : "y";
562
+ options.plugins.tooltip.callbacks.label = function (context) {
563
+ // don't show null values for stacked charts
564
+ if (context.parsed[valueLabel] === null) {
565
+ return;
566
+ }
567
+
568
+ var label = context.dataset.label || '';
592
569
  if (label) {
593
570
  label += ': ';
594
571
  }
595
- return formatValue(label, tooltipItem[valueLabel], formatOptions);
572
+ return formatValue(label, context.parsed[valueLabel], formatOptions);
596
573
  };
597
574
  }
598
575
  }
599
576
  };
600
577
 
601
- var jsOptions = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle);
578
+ var jsOptions$2 = jsOptionsFunc(merge(baseOptions, defaultOptions$2), hideLegend$2, setTitle$2, setMin$2, setMax$2, setStacked$2, setXtitle$2, setYtitle$2);
602
579
 
603
- var createDataTable = function (chart, options, chartType, library) {
580
+ var createDataTable = function (chart, options, chartType) {
604
581
  var datasets = [];
605
582
  var labels = [];
606
583
 
@@ -702,11 +679,23 @@
702
679
  }
703
680
  }
704
681
 
682
+ var color;
683
+ var backgroundColor;
684
+
705
685
  for (i = 0; i < series.length; i++) {
706
686
  s = series[i];
707
687
 
708
- var color = s.color || colors[i];
709
- var backgroundColor = chartType !== "line" ? addOpacity(color, 0.5) : color;
688
+ // use colors for each bar for single series format
689
+ if (chart.options.colors && chart.singleSeriesFormat && (chartType === "bar" || chartType === "column") && !s.color && isArray(chart.options.colors) && !isArray(chart.options.colors[0])) {
690
+ color = colors;
691
+ backgroundColor = [];
692
+ for (var j$3 = 0; j$3 < colors.length; j$3++) {
693
+ backgroundColor[j$3] = addOpacity(color[j$3], 0.5);
694
+ }
695
+ } else {
696
+ color = s.color || colors[i];
697
+ backgroundColor = chartType !== "line" ? addOpacity(color, 0.5) : color;
698
+ }
710
699
 
711
700
  var dataset = {
712
701
  label: s.name || "",
@@ -714,24 +703,37 @@
714
703
  fill: chartType === "area",
715
704
  borderColor: color,
716
705
  backgroundColor: backgroundColor,
717
- pointBackgroundColor: color,
718
- borderWidth: 2,
719
- pointHoverBackgroundColor: color
706
+ borderWidth: 2
720
707
  };
721
708
 
709
+ var pointChart = chartType === "line" || chartType === "area" || chartType === "scatter" || chartType === "bubble";
710
+ if (pointChart) {
711
+ dataset.pointBackgroundColor = color;
712
+ dataset.pointHoverBackgroundColor = color;
713
+ dataset.pointHitRadius = 50;
714
+ }
715
+
716
+ if (chartType === "bubble") {
717
+ dataset.pointBackgroundColor = backgroundColor;
718
+ dataset.pointHoverBackgroundColor = backgroundColor;
719
+ dataset.pointHoverBorderWidth = 2;
720
+ }
721
+
722
722
  if (s.stack) {
723
723
  dataset.stack = s.stack;
724
724
  }
725
725
 
726
726
  var curve = seriesOption(chart, s, "curve");
727
727
  if (curve === false) {
728
- dataset.lineTension = 0;
728
+ dataset.tension = 0;
729
+ } else if (pointChart) {
730
+ dataset.tension = 0.4;
729
731
  }
730
732
 
731
733
  var points = seriesOption(chart, s, "points");
732
734
  if (points === false) {
733
735
  dataset.pointRadius = 0;
734
- dataset.pointHitRadius = 5;
736
+ dataset.pointHoverRadius = 0;
735
737
  }
736
738
 
737
739
  dataset = merge(dataset, chart.options.dataset || {});
@@ -745,22 +747,18 @@
745
747
  var xmax = chart.options.xmax;
746
748
 
747
749
  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
750
  if (notnull(xmin)) {
753
- options.scales.xAxes[0][ticksKey].min = toDate(xmin).getTime();
751
+ options.scales.x.ticks.min = toDate(xmin).getTime();
754
752
  }
755
753
  if (notnull(xmax)) {
756
- options.scales.xAxes[0][ticksKey].max = toDate(xmax).getTime();
754
+ options.scales.x.ticks.max = toDate(xmax).getTime();
757
755
  }
758
756
  } else if (chart.xtype === "number") {
759
757
  if (notnull(xmin)) {
760
- options.scales.xAxes[0].ticks.min = xmin;
758
+ options.scales.x.ticks.min = xmin;
761
759
  }
762
760
  if (notnull(xmax)) {
763
- options.scales.xAxes[0].ticks.max = xmax;
761
+ options.scales.x.ticks.max = xmax;
764
762
  }
765
763
  }
766
764
 
@@ -796,43 +794,47 @@
796
794
 
797
795
  var timeDiff = (maxTime - minTime) / (86400 * 1000.0);
798
796
 
799
- if (!options.scales.xAxes[0].time.unit) {
797
+ if (!options.scales.x.time.unit) {
800
798
  var step;
801
799
  if (year || timeDiff > 365 * 10) {
802
- options.scales.xAxes[0].time.unit = "year";
800
+ options.scales.x.time.unit = "year";
803
801
  step = 365;
804
802
  } else if (month || timeDiff > 30 * 10) {
805
- options.scales.xAxes[0].time.unit = "month";
803
+ options.scales.x.time.unit = "month";
806
804
  step = 30;
807
805
  } else if (day || timeDiff > 10) {
808
- options.scales.xAxes[0].time.unit = "day";
806
+ options.scales.x.time.unit = "day";
809
807
  step = 1;
810
808
  } 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";
809
+ options.scales.x.time.displayFormats = {hour: "MMM d, h a"};
810
+ options.scales.x.time.unit = "hour";
813
811
  step = 1 / 24.0;
814
812
  } else if (minute) {
815
- options.scales.xAxes[0].time.displayFormats = {minute: "h:mm a"};
816
- options.scales.xAxes[0].time.unit = "minute";
813
+ options.scales.x.time.displayFormats = {minute: "h:mm a"};
814
+ options.scales.x.time.unit = "minute";
817
815
  step = 1 / 24.0 / 60.0;
818
816
  }
819
817
 
820
818
  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;
819
+ // width not available for hidden elements
820
+ var width = chart.element.offsetWidth;
821
+ if (width > 0) {
822
+ var unitStepSize = Math.ceil(timeDiff / step / (width / 100.0));
823
+ if (week && step === 1) {
824
+ unitStepSize = Math.ceil(unitStepSize / 7.0) * 7;
825
+ }
826
+ options.scales.x.time.stepSize = unitStepSize;
824
827
  }
825
- options.scales.xAxes[0].time.unitStepSize = unitStepSize;
826
828
  }
827
829
  }
828
830
 
829
- if (!options.scales.xAxes[0].time.tooltipFormat) {
831
+ if (!options.scales.x.time.tooltipFormat) {
830
832
  if (day) {
831
- options.scales.xAxes[0].time.tooltipFormat = "ll";
833
+ options.scales.x.time.tooltipFormat = "PP";
832
834
  } else if (hour) {
833
- options.scales.xAxes[0].time.tooltipFormat = "MMM D, h a";
835
+ options.scales.x.time.tooltipFormat = "MMM d, h a";
834
836
  } else if (minute) {
835
- options.scales.xAxes[0].time.tooltipFormat = "h:mm a";
837
+ options.scales.x.time.tooltipFormat = "h:mm a";
836
838
  }
837
839
  }
838
840
  }
@@ -845,49 +847,49 @@
845
847
  return data;
846
848
  };
847
849
 
848
- var defaultExport = function defaultExport(library) {
850
+ var defaultExport$2 = function defaultExport(library) {
849
851
  this.name = "chartjs";
850
852
  this.library = library;
851
853
  };
852
854
 
853
- defaultExport.prototype.renderLineChart = function renderLineChart (chart, chartType) {
855
+ defaultExport$2.prototype.renderLineChart = function renderLineChart (chart, chartType) {
854
856
  var chartOptions = {};
855
857
  // fix for https://github.com/chartjs/Chart.js/issues/2441
856
858
  if (!chart.options.max && allZeros(chart.data)) {
857
859
  chartOptions.max = 1;
858
860
  }
859
861
 
860
- var options = jsOptions(chart, merge(chartOptions, chart.options));
861
- setFormatOptions(chart, options, chartType);
862
+ var options = jsOptions$2(chart, merge(chartOptions, chart.options));
863
+ setFormatOptions$1(chart, options, chartType);
862
864
 
863
- var data = createDataTable(chart, options, chartType || "line", this.library);
865
+ var data = createDataTable(chart, options, chartType || "line");
864
866
 
865
867
  if (chart.xtype === "number") {
866
- options.scales.xAxes[0].type = "linear";
867
- options.scales.xAxes[0].position = "bottom";
868
+ options.scales.x.type = "linear";
869
+ options.scales.x.position = "bottom";
868
870
  } else {
869
- options.scales.xAxes[0].type = chart.xtype === "string" ? "category" : "time";
871
+ options.scales.x.type = chart.xtype === "string" ? "category" : "time";
870
872
  }
871
873
 
872
874
  this.drawChart(chart, "line", data, options);
873
875
  };
874
876
 
875
- defaultExport.prototype.renderPieChart = function renderPieChart (chart) {
877
+ defaultExport$2.prototype.renderPieChart = function renderPieChart (chart) {
876
878
  var options = merge({}, baseOptions);
877
879
  if (chart.options.donut) {
878
- options.cutoutPercentage = 50;
880
+ options.cutout = "50%";
879
881
  }
880
882
 
881
883
  if ("legend" in chart.options) {
882
- hideLegend(options, chart.options.legend);
884
+ hideLegend$2(options, chart.options.legend);
883
885
  }
884
886
 
885
887
  if (chart.options.title) {
886
- setTitle(options, chart.options.title);
888
+ setTitle$2(options, chart.options.title);
887
889
  }
888
890
 
889
891
  options = merge(options, chart.options.library || {});
890
- setFormatOptions(chart, options, "pie");
892
+ setFormatOptions$1(chart, options, "pie");
891
893
 
892
894
  var labels = [];
893
895
  var values = [];
@@ -911,61 +913,73 @@
911
913
  this.drawChart(chart, "pie", data, options);
912
914
  };
913
915
 
914
- defaultExport.prototype.renderColumnChart = function renderColumnChart (chart, chartType) {
916
+ defaultExport$2.prototype.renderColumnChart = function renderColumnChart (chart, chartType) {
915
917
  var options;
916
918
  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);
919
+ var barOptions = merge(baseOptions, defaultOptions$2);
920
+ barOptions.indexAxis = "y";
921
+
922
+ // ensure gridlines have proper orientation
923
+ barOptions.scales.x.grid.drawOnChartArea = true;
924
+ barOptions.scales.y.grid.drawOnChartArea = false;
925
+ delete barOptions.scales.y.ticks.maxTicksLimit;
926
+
927
+ options = jsOptionsFunc(barOptions, hideLegend$2, setTitle$2, setBarMin$1, setBarMax$1, setStacked$2, setXtitle$2, setYtitle$2)(chart, chart.options);
920
928
  } else {
921
- options = jsOptions(chart, chart.options);
929
+ options = jsOptions$2(chart, chart.options);
922
930
  }
923
- setFormatOptions(chart, options, chartType);
924
- var data = createDataTable(chart, options, "column", this.library);
931
+ setFormatOptions$1(chart, options, chartType);
932
+ var data = createDataTable(chart, options, "column");
925
933
  if (chartType !== "bar") {
926
934
  setLabelSize(chart, data, options);
927
935
  }
928
- this.drawChart(chart, (chartType === "bar" ? "horizontalBar" : "bar"), data, options);
936
+ this.drawChart(chart, "bar", data, options);
929
937
  };
930
938
 
931
- defaultExport.prototype.renderAreaChart = function renderAreaChart (chart) {
939
+ defaultExport$2.prototype.renderAreaChart = function renderAreaChart (chart) {
932
940
  this.renderLineChart(chart, "area");
933
941
  };
934
942
 
935
- defaultExport.prototype.renderBarChart = function renderBarChart (chart) {
943
+ defaultExport$2.prototype.renderBarChart = function renderBarChart (chart) {
936
944
  this.renderColumnChart(chart, "bar");
937
945
  };
938
946
 
939
- defaultExport.prototype.renderScatterChart = function renderScatterChart (chart, chartType) {
947
+ defaultExport$2.prototype.renderScatterChart = function renderScatterChart (chart, chartType) {
940
948
  chartType = chartType || "scatter";
941
949
 
942
- var options = jsOptions(chart, chart.options);
943
- setFormatOptions(chart, options, chartType);
950
+ var options = jsOptions$2(chart, chart.options);
951
+ setFormatOptions$1(chart, options, chartType);
944
952
 
945
- if (!("showLines" in options)) {
946
- options.showLines = false;
953
+ if (!("showLine" in options)) {
954
+ options.showLine = false;
947
955
  }
948
956
 
949
- var data = createDataTable(chart, options, chartType, this.library);
957
+ var data = createDataTable(chart, options, chartType);
958
+
959
+ options.scales.x.type = "linear";
960
+ options.scales.x.position = "bottom";
950
961
 
951
- options.scales.xAxes[0].type = "linear";
952
- options.scales.xAxes[0].position = "bottom";
962
+ // prevent grouping hover and tooltips
963
+ if (!("mode" in options.interaction)) {
964
+ options.interaction.mode = "nearest";
965
+ }
953
966
 
954
967
  this.drawChart(chart, chartType, data, options);
955
968
  };
956
969
 
957
- defaultExport.prototype.renderBubbleChart = function renderBubbleChart (chart) {
970
+ defaultExport$2.prototype.renderBubbleChart = function renderBubbleChart (chart) {
958
971
  this.renderScatterChart(chart, "bubble");
959
972
  };
960
973
 
961
- defaultExport.prototype.destroy = function destroy (chart) {
974
+ defaultExport$2.prototype.destroy = function destroy (chart) {
962
975
  if (chart.chart) {
963
976
  chart.chart.destroy();
964
977
  }
965
978
  };
966
979
 
967
- defaultExport.prototype.drawChart = function drawChart (chart, type, data, options) {
980
+ defaultExport$2.prototype.drawChart = function drawChart (chart, type, data, options) {
968
981
  this.destroy(chart);
982
+ if (chart.destroyed) { return; }
969
983
 
970
984
  var chartOptions = {
971
985
  type: type,
@@ -1024,6 +1038,9 @@
1024
1038
  series: {
1025
1039
  marker: {}
1026
1040
  }
1041
+ },
1042
+ time: {
1043
+ useUTC: false
1027
1044
  }
1028
1045
  };
1029
1046
 
@@ -1073,7 +1090,7 @@
1073
1090
 
1074
1091
  var jsOptions$1 = jsOptionsFunc(defaultOptions$1, hideLegend$1, setTitle$1, setMin$1, setMax$1, setStacked$1, setXtitle$1, setYtitle$1);
1075
1092
 
1076
- var setFormatOptions$1 = function(chart, options, chartType) {
1093
+ var setFormatOptions = function(chart, options, chartType) {
1077
1094
  var formatOptions = {
1078
1095
  prefix: chart.options.prefix,
1079
1096
  suffix: chart.options.suffix,
@@ -1136,7 +1153,7 @@
1136
1153
  if (!options.chart.type) {
1137
1154
  options.chart.type = chartType;
1138
1155
  }
1139
- setFormatOptions$1(chart, options, chartType);
1156
+ setFormatOptions(chart, options, chartType);
1140
1157
 
1141
1158
  var series = chart.data;
1142
1159
  for (i = 0; i < series.length; i++) {
@@ -1181,7 +1198,7 @@
1181
1198
  }
1182
1199
 
1183
1200
  var options = merge(chartOptions, chart.options.library || {});
1184
- setFormatOptions$1(chart, options, "pie");
1201
+ setFormatOptions(chart, options, "pie");
1185
1202
  var series = [{
1186
1203
  type: "pie",
1187
1204
  name: chart.options.label || "Value",
@@ -1196,7 +1213,7 @@
1196
1213
  var series = chart.data;
1197
1214
  var options = jsOptions$1(chart, chart.options), i, j, s, d, rows = [], categories = [];
1198
1215
  options.chart.type = chartType;
1199
- setFormatOptions$1(chart, options, chartType);
1216
+ setFormatOptions(chart, options, chartType);
1200
1217
 
1201
1218
  for (i = 0; i < series.length; i++) {
1202
1219
  s = series[i];
@@ -1254,6 +1271,7 @@
1254
1271
 
1255
1272
  defaultExport$1.prototype.drawChart = function drawChart (chart, data, options) {
1256
1273
  this.destroy(chart);
1274
+ if (chart.destroyed) { return; }
1257
1275
 
1258
1276
  options.chart.renderTo = chart.element.id;
1259
1277
  options.series = data;
@@ -1269,7 +1287,7 @@
1269
1287
  var callbacks = [];
1270
1288
 
1271
1289
  // Set chart options
1272
- var defaultOptions$2 = {
1290
+ var defaultOptions = {
1273
1291
  chartArea: {},
1274
1292
  fontName: "'Lucida Grande', 'Lucida Sans Unicode', Verdana, Arial, Helvetica, sans-serif",
1275
1293
  pointSize: 6,
@@ -1311,7 +1329,7 @@
1311
1329
  }
1312
1330
  };
1313
1331
 
1314
- var hideLegend$2 = function (options, legend, hideLegend) {
1332
+ var hideLegend = function (options, legend, hideLegend) {
1315
1333
  if (legend !== undefined) {
1316
1334
  var position;
1317
1335
  if (!legend) {
@@ -1327,42 +1345,42 @@
1327
1345
  }
1328
1346
  };
1329
1347
 
1330
- var setTitle$2 = function (options, title) {
1348
+ var setTitle = function (options, title) {
1331
1349
  options.title = title;
1332
1350
  options.titleTextStyle = {color: "#333", fontSize: "20px"};
1333
1351
  };
1334
1352
 
1335
- var setMin$2 = function (options, min) {
1353
+ var setMin = function (options, min) {
1336
1354
  options.vAxis.viewWindow.min = min;
1337
1355
  };
1338
1356
 
1339
- var setMax$2 = function (options, max) {
1357
+ var setMax = function (options, max) {
1340
1358
  options.vAxis.viewWindow.max = max;
1341
1359
  };
1342
1360
 
1343
- var setBarMin$1 = function (options, min) {
1361
+ var setBarMin = function (options, min) {
1344
1362
  options.hAxis.viewWindow.min = min;
1345
1363
  };
1346
1364
 
1347
- var setBarMax$1 = function (options, max) {
1365
+ var setBarMax = function (options, max) {
1348
1366
  options.hAxis.viewWindow.max = max;
1349
1367
  };
1350
1368
 
1351
- var setStacked$2 = function (options, stacked) {
1369
+ var setStacked = function (options, stacked) {
1352
1370
  options.isStacked = stacked ? stacked : false;
1353
1371
  };
1354
1372
 
1355
- var setXtitle$2 = function (options, title) {
1373
+ var setXtitle = function (options, title) {
1356
1374
  options.hAxis.title = title;
1357
1375
  options.hAxis.titleTextStyle.italic = false;
1358
1376
  };
1359
1377
 
1360
- var setYtitle$2 = function (options, title) {
1378
+ var setYtitle = function (options, title) {
1361
1379
  options.vAxis.title = title;
1362
1380
  options.vAxis.titleTextStyle.italic = false;
1363
1381
  };
1364
1382
 
1365
- var jsOptions$2 = jsOptionsFunc(defaultOptions$2, hideLegend$2, setTitle$2, setMin$2, setMax$2, setStacked$2, setXtitle$2, setYtitle$2);
1383
+ var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle);
1366
1384
 
1367
1385
  var resize = function (callback) {
1368
1386
  if (window.attachEvent) {
@@ -1373,12 +1391,12 @@
1373
1391
  callback();
1374
1392
  };
1375
1393
 
1376
- var defaultExport$2 = function defaultExport(library) {
1394
+ var defaultExport = function defaultExport(library) {
1377
1395
  this.name = "google";
1378
1396
  this.library = library;
1379
1397
  };
1380
1398
 
1381
- defaultExport$2.prototype.renderLineChart = function renderLineChart (chart) {
1399
+ defaultExport.prototype.renderLineChart = function renderLineChart (chart) {
1382
1400
  var this$1 = this;
1383
1401
 
1384
1402
  this.waitForLoaded(chart, function () {
@@ -1392,14 +1410,14 @@
1392
1410
  chartOptions.pointSize = 0;
1393
1411
  }
1394
1412
 
1395
- var options = jsOptions$2(chart, chart.options, chartOptions);
1413
+ var options = jsOptions(chart, chart.options, chartOptions);
1396
1414
  var data = this$1.createDataTable(chart.data, chart.xtype);
1397
1415
 
1398
1416
  this$1.drawChart(chart, "LineChart", data, options);
1399
1417
  });
1400
1418
  };
1401
1419
 
1402
- defaultExport$2.prototype.renderPieChart = function renderPieChart (chart) {
1420
+ defaultExport.prototype.renderPieChart = function renderPieChart (chart) {
1403
1421
  var this$1 = this;
1404
1422
 
1405
1423
  this.waitForLoaded(chart, function () {
@@ -1417,12 +1435,12 @@
1417
1435
  chartOptions.pieHole = 0.5;
1418
1436
  }
1419
1437
  if ("legend" in chart.options) {
1420
- hideLegend$2(chartOptions, chart.options.legend);
1438
+ hideLegend(chartOptions, chart.options.legend);
1421
1439
  }
1422
1440
  if (chart.options.title) {
1423
- setTitle$2(chartOptions, chart.options.title);
1441
+ setTitle(chartOptions, chart.options.title);
1424
1442
  }
1425
- var options = merge(merge(defaultOptions$2, chartOptions), chart.options.library || {});
1443
+ var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
1426
1444
 
1427
1445
  var data = new this$1.library.visualization.DataTable();
1428
1446
  data.addColumn("string", "");
@@ -1433,18 +1451,18 @@
1433
1451
  });
1434
1452
  };
1435
1453
 
1436
- defaultExport$2.prototype.renderColumnChart = function renderColumnChart (chart) {
1454
+ defaultExport.prototype.renderColumnChart = function renderColumnChart (chart) {
1437
1455
  var this$1 = this;
1438
1456
 
1439
1457
  this.waitForLoaded(chart, function () {
1440
- var options = jsOptions$2(chart, chart.options);
1458
+ var options = jsOptions(chart, chart.options);
1441
1459
  var data = this$1.createDataTable(chart.data, chart.xtype);
1442
1460
 
1443
1461
  this$1.drawChart(chart, "ColumnChart", data, options);
1444
1462
  });
1445
1463
  };
1446
1464
 
1447
- defaultExport$2.prototype.renderBarChart = function renderBarChart (chart) {
1465
+ defaultExport.prototype.renderBarChart = function renderBarChart (chart) {
1448
1466
  var this$1 = this;
1449
1467
 
1450
1468
  this.waitForLoaded(chart, function () {
@@ -1455,14 +1473,14 @@
1455
1473
  }
1456
1474
  }
1457
1475
  };
1458
- var options = jsOptionsFunc(defaultOptions$2, hideLegend$2, setTitle$2, setBarMin$1, setBarMax$1, setStacked$2, setXtitle$2, setYtitle$2)(chart, chart.options, chartOptions);
1476
+ var options = jsOptionsFunc(defaultOptions, hideLegend, setTitle, setBarMin, setBarMax, setStacked, setXtitle, setYtitle)(chart, chart.options, chartOptions);
1459
1477
  var data = this$1.createDataTable(chart.data, chart.xtype);
1460
1478
 
1461
1479
  this$1.drawChart(chart, "BarChart", data, options);
1462
1480
  });
1463
1481
  };
1464
1482
 
1465
- defaultExport$2.prototype.renderAreaChart = function renderAreaChart (chart) {
1483
+ defaultExport.prototype.renderAreaChart = function renderAreaChart (chart) {
1466
1484
  var this$1 = this;
1467
1485
 
1468
1486
  this.waitForLoaded(chart, function () {
@@ -1472,14 +1490,14 @@
1472
1490
  areaOpacity: 0.5
1473
1491
  };
1474
1492
 
1475
- var options = jsOptions$2(chart, chart.options, chartOptions);
1493
+ var options = jsOptions(chart, chart.options, chartOptions);
1476
1494
  var data = this$1.createDataTable(chart.data, chart.xtype);
1477
1495
 
1478
1496
  this$1.drawChart(chart, "AreaChart", data, options);
1479
1497
  });
1480
1498
  };
1481
1499
 
1482
- defaultExport$2.prototype.renderGeoChart = function renderGeoChart (chart) {
1500
+ defaultExport.prototype.renderGeoChart = function renderGeoChart (chart) {
1483
1501
  var this$1 = this;
1484
1502
 
1485
1503
  this.waitForLoaded(chart, "geochart", function () {
@@ -1489,7 +1507,7 @@
1489
1507
  colors: chart.options.colors || ["#f6c7b6", "#ce502d"]
1490
1508
  }
1491
1509
  };
1492
- var options = merge(merge(defaultOptions$2, chartOptions), chart.options.library || {});
1510
+ var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
1493
1511
 
1494
1512
  var data = new this$1.library.visualization.DataTable();
1495
1513
  data.addColumn("string", "");
@@ -1500,12 +1518,12 @@
1500
1518
  });
1501
1519
  };
1502
1520
 
1503
- defaultExport$2.prototype.renderScatterChart = function renderScatterChart (chart) {
1521
+ defaultExport.prototype.renderScatterChart = function renderScatterChart (chart) {
1504
1522
  var this$1 = this;
1505
1523
 
1506
1524
  this.waitForLoaded(chart, function () {
1507
1525
  var chartOptions = {};
1508
- var options = jsOptions$2(chart, chart.options, chartOptions);
1526
+ var options = jsOptions(chart, chart.options, chartOptions);
1509
1527
 
1510
1528
  var series = chart.data, rows2 = [], i, j, data, d;
1511
1529
  for (i = 0; i < series.length; i++) {
@@ -1530,7 +1548,7 @@
1530
1548
  });
1531
1549
  };
1532
1550
 
1533
- defaultExport$2.prototype.renderTimeline = function renderTimeline (chart) {
1551
+ defaultExport.prototype.renderTimeline = function renderTimeline (chart) {
1534
1552
  var this$1 = this;
1535
1553
 
1536
1554
  this.waitForLoaded(chart, "timeline", function () {
@@ -1541,7 +1559,7 @@
1541
1559
  if (chart.options.colors) {
1542
1560
  chartOptions.colors = chart.options.colors;
1543
1561
  }
1544
- var options = merge(merge(defaultOptions$2, chartOptions), chart.options.library || {});
1562
+ var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
1545
1563
 
1546
1564
  var data = new this$1.library.visualization.DataTable();
1547
1565
  data.addColumn({type: "string", id: "Name"});
@@ -1555,14 +1573,16 @@
1555
1573
  });
1556
1574
  };
1557
1575
 
1558
- defaultExport$2.prototype.destroy = function destroy (chart) {
1576
+ // TODO remove resize events
1577
+ defaultExport.prototype.destroy = function destroy (chart) {
1559
1578
  if (chart.chart) {
1560
1579
  chart.chart.clearChart();
1561
1580
  }
1562
1581
  };
1563
1582
 
1564
- defaultExport$2.prototype.drawChart = function drawChart (chart, type, data, options) {
1583
+ defaultExport.prototype.drawChart = function drawChart (chart, type, data, options) {
1565
1584
  this.destroy(chart);
1585
+ if (chart.destroyed) { return; }
1566
1586
 
1567
1587
  if (chart.options.code) {
1568
1588
  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,7 +1594,7 @@
1574
1594
  });
1575
1595
  };
1576
1596
 
1577
- defaultExport$2.prototype.waitForLoaded = function waitForLoaded (chart, pack, callback) {
1597
+ defaultExport.prototype.waitForLoaded = function waitForLoaded (chart, pack, callback) {
1578
1598
  var this$1 = this;
1579
1599
 
1580
1600
  if (!callback) {
@@ -1606,7 +1626,7 @@
1606
1626
  }
1607
1627
  };
1608
1628
 
1609
- defaultExport$2.prototype.runCallbacks = function runCallbacks () {
1629
+ defaultExport.prototype.runCallbacks = function runCallbacks () {
1610
1630
  var cb, call;
1611
1631
  for (var i = 0; i < callbacks.length; i++) {
1612
1632
  cb = callbacks[i];
@@ -1620,7 +1640,7 @@
1620
1640
  };
1621
1641
 
1622
1642
  // cant use object as key
1623
- defaultExport$2.prototype.createDataTable = function createDataTable (series, columnType) {
1643
+ defaultExport.prototype.createDataTable = function createDataTable (series, columnType) {
1624
1644
  var i, j, s, d, key, rows = [], sortedLabels = [];
1625
1645
  for (i = 0; i < series.length; i++) {
1626
1646
  s = series[i];
@@ -1676,116 +1696,128 @@
1676
1696
  return data;
1677
1697
  };
1678
1698
 
1679
- var pendingRequests = [], runningRequests = 0, maxRequests = 4;
1699
+ function formatSeriesData(data, keyType) {
1700
+ var r = [], j, keyFunc;
1680
1701
 
1681
- function pushRequest(url, success, error) {
1682
- pendingRequests.push([url, success, error]);
1683
- runNext();
1684
- }
1702
+ if (keyType === "number") {
1703
+ keyFunc = toFloat;
1704
+ } else if (keyType === "datetime") {
1705
+ keyFunc = toDate;
1706
+ } else {
1707
+ keyFunc = toStr;
1708
+ }
1685
1709
 
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();
1710
+ if (keyType === "bubble") {
1711
+ for (j = 0; j < data.length; j++) {
1712
+ r.push([toFloat(data[j][0]), toFloat(data[j][1]), toFloat(data[j][2])]);
1713
+ }
1714
+ } else {
1715
+ for (j = 0; j < data.length; j++) {
1716
+ r.push([keyFunc(data[j][0]), toFloat(data[j][1])]);
1693
1717
  }
1694
1718
  }
1695
- }
1696
1719
 
1697
- function requestComplete() {
1698
- runningRequests--;
1699
- runNext();
1720
+ if (keyType === "datetime") {
1721
+ r.sort(sortByTime);
1722
+ } else if (keyType === "number") {
1723
+ r.sort(sortByNumberSeries);
1724
+ }
1725
+
1726
+ return r;
1700
1727
  }
1701
1728
 
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
- });
1729
+ function detectXType(series, noDatetime, options) {
1730
+ if (dataEmpty(series)) {
1731
+ if ((options.xmin || options.xmax) && (!options.xmin || isDate(options.xmin)) && (!options.xmax || isDate(options.xmax))) {
1732
+ return "datetime";
1733
+ } else {
1734
+ return "number";
1735
+ }
1736
+ } else if (detectXTypeWithFunction(series, isNumber)) {
1737
+ return "number";
1738
+ } else if (!noDatetime && detectXTypeWithFunction(series, isDate)) {
1739
+ return "datetime";
1740
+ } else {
1741
+ return "string";
1742
+ }
1707
1743
  }
1708
1744
 
1709
- function ajaxCall(url, success, error) {
1710
- var $ = window.jQuery || window.Zepto || window.$;
1745
+ function detectXTypeWithFunction(series, func) {
1746
+ var i, j, data;
1747
+ for (i = 0; i < series.length; i++) {
1748
+ data = toArr(series[i].data);
1749
+ for (j = 0; j < data.length; j++) {
1750
+ if (!func(data[j][0])) {
1751
+ return false;
1752
+ }
1753
+ }
1754
+ }
1755
+ return true;
1756
+ }
1711
1757
 
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);
1758
+ // creates a shallow copy of each element of the array
1759
+ // elements are expected to be objects
1760
+ function copySeries(series) {
1761
+ var newSeries = [], i, j;
1762
+ for (i = 0; i < series.length; i++) {
1763
+ var copy = {};
1764
+ for (j in series[i]) {
1765
+ if (series[i].hasOwnProperty(j)) {
1766
+ copy[j] = series[i][j];
1730
1767
  }
1731
- };
1732
- xhr.send();
1768
+ }
1769
+ newSeries.push(copy);
1733
1770
  }
1771
+ return newSeries;
1734
1772
  }
1735
1773
 
1736
- var config = {};
1737
- var adapters = [];
1774
+ function processSeries(chart, keyType, noDatetime) {
1775
+ var i;
1738
1776
 
1739
- // helpers
1777
+ var opts = chart.options;
1778
+ var series = chart.rawData;
1740
1779
 
1741
- function setText(element, text) {
1742
- if (document.body.innerText) {
1743
- element.innerText = text;
1744
- } else {
1745
- element.textContent = text;
1780
+ // see if one series or multiple
1781
+ chart.singleSeriesFormat = (!isArray(series) || typeof series[0] !== "object" || isArray(series[0]));
1782
+ if (chart.singleSeriesFormat) {
1783
+ series = [{name: opts.label, data: series}];
1746
1784
  }
1747
- }
1748
1785
 
1749
- // TODO remove prefix for all messages
1750
- function chartError(element, message, noPrefix) {
1751
- if (!noPrefix) {
1752
- message = "Error Loading Chart: " + message;
1786
+ // convert to array
1787
+ // must come before dataEmpty check
1788
+ series = copySeries(series);
1789
+ for (i = 0; i < series.length; i++) {
1790
+ series[i].data = toArr(series[i].data);
1753
1791
  }
1754
- setText(element, message);
1755
- element.style.color = "#ff0000";
1756
- }
1757
1792
 
1758
- function errorCatcher(chart) {
1759
- try {
1760
- chart.__render();
1761
- } catch (err) {
1762
- chartError(chart.element, err.message);
1763
- throw err;
1793
+ chart.xtype = keyType ? keyType : (opts.discrete ? "string" : detectXType(series, noDatetime, opts));
1794
+
1795
+ // right format
1796
+ for (i = 0; i < series.length; i++) {
1797
+ series[i].data = formatSeriesData(series[i].data, chart.xtype);
1764
1798
  }
1799
+
1800
+ return series;
1765
1801
  }
1766
1802
 
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
- }
1803
+ function processSimple(chart) {
1804
+ var perfectData = toArr(chart.rawData), i;
1805
+ for (i = 0; i < perfectData.length; i++) {
1806
+ perfectData[i] = [toStr(perfectData[i][0]), toFloat(perfectData[i][1])];
1807
+ }
1808
+ return perfectData;
1809
+ }
1810
+
1811
+ function dataEmpty(data, chartType) {
1812
+ if (chartType === "PieChart" || chartType === "GeoChart" || chartType === "Timeline") {
1813
+ return data.length === 0;
1786
1814
  } else {
1787
- chart.rawData = dataSource;
1788
- errorCatcher(chart);
1815
+ for (var i = 0; i < data.length; i++) {
1816
+ if (data[i].data.length > 0) {
1817
+ return false;
1818
+ }
1819
+ }
1820
+ return true;
1789
1821
  }
1790
1822
  }
1791
1823
 
@@ -1869,14 +1901,132 @@
1869
1901
  return c === p;
1870
1902
  }
1871
1903
 
1904
+ var pendingRequests = [], runningRequests = 0, maxRequests = 4;
1905
+
1906
+ function pushRequest(url, success, error) {
1907
+ pendingRequests.push([url, success, error]);
1908
+ runNext();
1909
+ }
1910
+
1911
+ function runNext() {
1912
+ if (runningRequests < maxRequests) {
1913
+ var request = pendingRequests.shift();
1914
+ if (request) {
1915
+ runningRequests++;
1916
+ getJSON(request[0], request[1], request[2]);
1917
+ runNext();
1918
+ }
1919
+ }
1920
+ }
1921
+
1922
+ function requestComplete() {
1923
+ runningRequests--;
1924
+ runNext();
1925
+ }
1926
+
1927
+ function getJSON(url, success, error) {
1928
+ ajaxCall(url, success, function (jqXHR, textStatus, errorThrown) {
1929
+ var message = (typeof errorThrown === "string") ? errorThrown : errorThrown.message;
1930
+ error(message);
1931
+ });
1932
+ }
1933
+
1934
+ function ajaxCall(url, success, error) {
1935
+ var $ = window.jQuery || window.Zepto || window.$;
1936
+
1937
+ if ($ && $.ajax) {
1938
+ $.ajax({
1939
+ dataType: "json",
1940
+ url: url,
1941
+ success: success,
1942
+ error: error,
1943
+ complete: requestComplete
1944
+ });
1945
+ } else {
1946
+ var xhr = new XMLHttpRequest();
1947
+ xhr.open("GET", url, true);
1948
+ xhr.setRequestHeader("Content-Type", "application/json");
1949
+ xhr.onload = function () {
1950
+ requestComplete();
1951
+ if (xhr.status === 200) {
1952
+ success(JSON.parse(xhr.responseText), xhr.statusText, xhr);
1953
+ } else {
1954
+ error(xhr, "error", xhr.statusText);
1955
+ }
1956
+ };
1957
+ xhr.send();
1958
+ }
1959
+ }
1960
+
1961
+ var config = {};
1962
+ var adapters = [];
1963
+
1964
+ // helpers
1965
+
1966
+ function setText(element, text) {
1967
+ if (document.body.innerText) {
1968
+ element.innerText = text;
1969
+ } else {
1970
+ element.textContent = text;
1971
+ }
1972
+ }
1973
+
1974
+ // TODO remove prefix for all messages
1975
+ function chartError(element, message, noPrefix) {
1976
+ if (!noPrefix) {
1977
+ message = "Error Loading Chart: " + message;
1978
+ }
1979
+ setText(element, message);
1980
+ element.style.color = "#ff0000";
1981
+ }
1982
+
1983
+ function errorCatcher(chart) {
1984
+ try {
1985
+ chart.__render();
1986
+ } catch (err) {
1987
+ chartError(chart.element, err.message);
1988
+ throw err;
1989
+ }
1990
+ }
1991
+
1992
+ function fetchDataSource(chart, dataSource, showLoading) {
1993
+ // only show loading message for urls and callbacks
1994
+ if (showLoading && chart.options.loading && (typeof dataSource === "string" || typeof dataSource === "function")) {
1995
+ setText(chart.element, chart.options.loading);
1996
+ }
1997
+
1998
+ if (typeof dataSource === "string") {
1999
+ pushRequest(dataSource, function (data) {
2000
+ chart.rawData = data;
2001
+ errorCatcher(chart);
2002
+ }, function (message) {
2003
+ chartError(chart.element, message);
2004
+ });
2005
+ } else if (typeof dataSource === "function") {
2006
+ try {
2007
+ dataSource(function (data) {
2008
+ chart.rawData = data;
2009
+ errorCatcher(chart);
2010
+ }, function (message) {
2011
+ chartError(chart.element, message, true);
2012
+ });
2013
+ } catch (err) {
2014
+ chartError(chart.element, err, true);
2015
+ }
2016
+ } else {
2017
+ chart.rawData = dataSource;
2018
+ errorCatcher(chart);
2019
+ }
2020
+ }
2021
+
1872
2022
  function getAdapterType(library) {
1873
2023
  if (library) {
1874
2024
  if (library.product === "Highcharts") {
1875
2025
  return defaultExport$1;
1876
2026
  } else if (library.charts) {
1877
- return defaultExport$2;
1878
- } else if (isFunction(library)) {
1879
2027
  return defaultExport;
2028
+ } else if (isFunction(library)) {
2029
+ return defaultExport$2;
1880
2030
  }
1881
2031
  }
1882
2032
  throw new Error("Unknown adapter");
@@ -1905,22 +2055,10 @@
1905
2055
  }
1906
2056
  }
1907
2057
 
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
2058
  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);
2059
+ if (dataEmpty(chart.data, chartType)) {
2060
+ var message = chart.options.empty || (chart.options.messages && chart.options.messages.empty) || "No data";
2061
+ setText(chart.element, message);
1924
2062
  } else {
1925
2063
  callAdapter(chartType, chart);
1926
2064
  if (chart.options.download && !chart.__downloadAttached && chart.adapter === "chartjs") {
@@ -1954,121 +2092,6 @@
1954
2092
  }
1955
2093
  }
1956
2094
 
1957
- // process data
1958
-
1959
- var toFormattedKey = function (key, keyType) {
1960
- if (keyType === "number") {
1961
- key = toFloat(key);
1962
- } else if (keyType === "datetime") {
1963
- key = toDate(key);
1964
- } else {
1965
- key = toStr(key);
1966
- }
1967
- return key;
1968
- };
1969
-
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);
1984
- }
1985
- return r;
1986
- };
1987
-
1988
- function detectXType(series, noDatetime, options) {
1989
- if (dataEmpty(series)) {
1990
- if ((options.xmin || options.xmax) && (!options.xmin || isDate(options.xmin)) && (!options.xmax || isDate(options.xmax))) {
1991
- return "datetime";
1992
- } else {
1993
- return "number";
1994
- }
1995
- } else if (detectXTypeWithFunction(series, isNumber)) {
1996
- return "number";
1997
- } else if (!noDatetime && detectXTypeWithFunction(series, isDate)) {
1998
- return "datetime";
1999
- } else {
2000
- return "string";
2001
- }
2002
- }
2003
-
2004
- 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++) {
2009
- if (!func(data[j][0])) {
2010
- return false;
2011
- }
2012
- }
2013
- }
2014
- return true;
2015
- }
2016
-
2017
- // creates a shallow copy of each element of the array
2018
- // elements are expected to be objects
2019
- function copySeries(series) {
2020
- var newSeries = [], i, j;
2021
- for (i = 0; i < series.length; i++) {
2022
- var copy = {};
2023
- for (j in series[i]) {
2024
- if (series[i].hasOwnProperty(j)) {
2025
- copy[j] = series[i][j];
2026
- }
2027
- }
2028
- newSeries.push(copy);
2029
- }
2030
- return newSeries;
2031
- }
2032
-
2033
- function processSeries(chart, keyType, noDatetime) {
2034
- var i;
2035
-
2036
- var opts = chart.options;
2037
- var series = chart.rawData;
2038
-
2039
- // see if one series or multiple
2040
- if (!isArray(series) || typeof series[0] !== "object" || isArray(series[0])) {
2041
- series = [{name: opts.label, data: series}];
2042
- chart.hideLegend = true;
2043
- } else {
2044
- chart.hideLegend = false;
2045
- }
2046
-
2047
- // convert to array
2048
- // must come before dataEmpty check
2049
- series = copySeries(series);
2050
- for (i = 0; i < series.length; i++) {
2051
- series[i].data = toArr(series[i].data);
2052
- }
2053
-
2054
- chart.xtype = keyType ? keyType : (opts.discrete ? "string" : detectXType(series, noDatetime, opts));
2055
-
2056
- // right format
2057
- for (i = 0; i < series.length; i++) {
2058
- series[i].data = formatSeriesData(series[i].data, chart.xtype);
2059
- }
2060
-
2061
- return series;
2062
- }
2063
-
2064
- function processSimple(chart) {
2065
- var perfectData = toArr(chart.rawData), i;
2066
- for (i = 0; i < perfectData.length; i++) {
2067
- perfectData[i] = [toStr(perfectData[i][0]), toFloat(perfectData[i][1])];
2068
- }
2069
- return perfectData;
2070
- }
2071
-
2072
2095
  // define classes
2073
2096
 
2074
2097
  var Chart = function Chart(element, dataSource, options) {
@@ -2086,7 +2109,7 @@
2086
2109
 
2087
2110
  Chartkick.charts[element.id] = this;
2088
2111
 
2089
- fetchDataSource(this, dataSource);
2112
+ fetchDataSource(this, dataSource, true);
2090
2113
 
2091
2114
  if (this.options.refresh) {
2092
2115
  this.startRefresh();
@@ -2122,7 +2145,7 @@
2122
2145
  if (options) {
2123
2146
  this.__updateOptions(options);
2124
2147
  }
2125
- fetchDataSource(this, dataSource);
2148
+ fetchDataSource(this, dataSource, true);
2126
2149
  };
2127
2150
 
2128
2151
  Chart.prototype.setOptions = function setOptions (options) {
@@ -2176,8 +2199,8 @@
2176
2199
  if (this.adapter === "chartjs") {
2177
2200
  if (download && download.background && download.background !== "transparent") {
2178
2201
  // 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;
2202
+ var canvas = this.chart.canvas;
2203
+ var ctx = this.chart.ctx;
2181
2204
  var tmpCanvas = document.createElement("canvas");
2182
2205
  var tmpCtx = tmpCanvas.getContext("2d");
2183
2206
  tmpCanvas.width = ctx.canvas.width;
@@ -2190,13 +2213,14 @@
2190
2213
  return this.chart.toBase64Image();
2191
2214
  }
2192
2215
  } else {
2193
- // TODO throw error in next major version
2194
- // throw new Error("Feature only available for Chart.js");
2195
- return null;
2216
+ throw new Error("Feature only available for Chart.js");
2196
2217
  }
2197
2218
  };
2198
2219
 
2199
2220
  Chart.prototype.destroy = function destroy () {
2221
+ this.destroyed = true;
2222
+ this.stopRefresh();
2223
+
2200
2224
  if (this.__adapterObject) {
2201
2225
  this.__adapterObject.destroy(this);
2202
2226
  }
@@ -2441,6 +2465,14 @@
2441
2465
  }
2442
2466
  }
2443
2467
  },
2468
+ destroyAll: function() {
2469
+ for (var chartId in Chartkick.charts) {
2470
+ if (Chartkick.charts.hasOwnProperty(chartId)) {
2471
+ Chartkick.charts[chartId].destroy();
2472
+ delete Chartkick.charts[chartId];
2473
+ }
2474
+ }
2475
+ },
2444
2476
  config: config,
2445
2477
  options: {},
2446
2478
  adapters: adapters,
@@ -2454,6 +2486,16 @@
2454
2486
  // not ideal, but allows for simpler integration
2455
2487
  if (typeof window !== "undefined" && !window.Chartkick) {
2456
2488
  window.Chartkick = Chartkick;
2489
+
2490
+ // clean up previous charts before Turbolinks loads new page
2491
+ document.addEventListener("turbolinks:before-render", function() {
2492
+ Chartkick.destroyAll();
2493
+ });
2494
+
2495
+ // use setTimeout so charting library can come later in same JS file
2496
+ setTimeout(function() {
2497
+ window.dispatchEvent(new Event("chartkick:load"));
2498
+ }, 0);
2457
2499
  }
2458
2500
 
2459
2501
  // backwards compatibility for esm require