chartkick 3.2.1 → 3.4.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.
@@ -2,7 +2,7 @@
2
2
  * Chartkick.js
3
3
  * Create beautiful charts with one line of JavaScript
4
4
  * https://github.com/ankane/chartkick.js
5
- * v3.1.1
5
+ * v3.2.1
6
6
  * MIT License
7
7
  */
8
8
 
@@ -10,7 +10,7 @@
10
10
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
11
11
  typeof define === 'function' && define.amd ? define(factory) :
12
12
  (global = global || self, global.Chartkick = factory());
13
- }(this, function () { 'use strict';
13
+ }(this, (function () { 'use strict';
14
14
 
15
15
  function isArray(variable) {
16
16
  return Object.prototype.toString.call(variable) === "[object Array]";
@@ -21,13 +21,17 @@
21
21
  }
22
22
 
23
23
  function isPlainObject(variable) {
24
- return Object.prototype.toString.call(variable) === "[object Object]";
24
+ // protect against prototype pollution, defense 2
25
+ return Object.prototype.toString.call(variable) === "[object Object]" && !isFunction(variable) && variable instanceof Object;
25
26
  }
26
27
 
27
28
  // https://github.com/madrobby/zepto/blob/master/src/zepto.js
28
29
  function extend(target, source) {
29
30
  var key;
30
31
  for (key in source) {
32
+ // protect against prototype pollution, defense 1
33
+ if (key === "__proto__") { continue; }
34
+
31
35
  if (isPlainObject(source[key]) || isArray(source[key])) {
32
36
  if (isPlainObject(source[key]) && !isPlainObject(target[key])) {
33
37
  target[key] = {};
@@ -237,7 +241,9 @@
237
241
  return typeof obj === "number";
238
242
  }
239
243
 
240
- function formatValue(pre, value, options) {
244
+ var byteSuffixes = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB"];
245
+
246
+ function formatValue(pre, value, options, axis) {
241
247
  pre = pre || "";
242
248
  if (options.prefix) {
243
249
  if (value < 0) {
@@ -247,6 +253,74 @@
247
253
  pre += options.prefix;
248
254
  }
249
255
 
256
+ var suffix = options.suffix || "";
257
+ var precision = options.precision;
258
+ var round = options.round;
259
+
260
+ if (options.byteScale) {
261
+ var suffixIdx;
262
+ var baseValue = axis ? options.byteScale : value;
263
+
264
+ if (baseValue >= 1152921504606846976) {
265
+ value /= 1152921504606846976;
266
+ suffixIdx = 6;
267
+ } else if (baseValue >= 1125899906842624) {
268
+ value /= 1125899906842624;
269
+ suffixIdx = 5;
270
+ } else if (baseValue >= 1099511627776) {
271
+ value /= 1099511627776;
272
+ suffixIdx = 4;
273
+ } else if (baseValue >= 1073741824) {
274
+ value /= 1073741824;
275
+ suffixIdx = 3;
276
+ } else if (baseValue >= 1048576) {
277
+ value /= 1048576;
278
+ suffixIdx = 2;
279
+ } else if (baseValue >= 1024) {
280
+ value /= 1024;
281
+ suffixIdx = 1;
282
+ } else {
283
+ suffixIdx = 0;
284
+ }
285
+
286
+ // TODO handle manual precision case
287
+ if (precision === undefined && round === undefined) {
288
+ if (value >= 1023.5) {
289
+ if (suffixIdx < byteSuffixes.length - 1) {
290
+ value = 1.0;
291
+ suffixIdx += 1;
292
+ }
293
+ }
294
+ precision = value >= 1000 ? 4 : 3;
295
+ }
296
+ suffix = " " + byteSuffixes[suffixIdx];
297
+ }
298
+
299
+ if (precision !== undefined && round !== undefined) {
300
+ throw Error("Use either round or precision, not both");
301
+ }
302
+
303
+ if (!axis) {
304
+ if (precision !== undefined) {
305
+ value = value.toPrecision(precision);
306
+ if (!options.zeros) {
307
+ value = parseFloat(value);
308
+ }
309
+ }
310
+
311
+ if (round !== undefined) {
312
+ if (round < 0) {
313
+ var num = Math.pow(10, -1 * round);
314
+ value = parseInt((1.0 * value / num).toFixed(0)) * num;
315
+ } else {
316
+ value = value.toFixed(round);
317
+ if (!options.zeros) {
318
+ value = parseFloat(value);
319
+ }
320
+ }
321
+ }
322
+ }
323
+
250
324
  if (options.thousands || options.decimal) {
251
325
  value = toStr(value);
252
326
  var parts = value.split(".");
@@ -259,7 +333,7 @@
259
333
  }
260
334
  }
261
335
 
262
- return pre + value + (options.suffix || "");
336
+ return pre + value + suffix;
263
337
  }
264
338
 
265
339
  function seriesOption(chart, series, option) {
@@ -420,18 +494,58 @@
420
494
  prefix: chart.options.prefix,
421
495
  suffix: chart.options.suffix,
422
496
  thousands: chart.options.thousands,
423
- decimal: chart.options.decimal
497
+ decimal: chart.options.decimal,
498
+ precision: chart.options.precision,
499
+ round: chart.options.round,
500
+ zeros: chart.options.zeros
424
501
  };
425
502
 
503
+ if (chart.options.bytes) {
504
+ var series = chart.data;
505
+ if (chartType === "pie") {
506
+ series = [{data: series}];
507
+ }
508
+
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
+ // set step size
528
+ formatOptions.byteScale = scale;
529
+ }
530
+
426
531
  if (chartType !== "pie") {
427
532
  var myAxes = options.scales.yAxes;
428
533
  if (chartType === "bar") {
429
534
  myAxes = options.scales.xAxes;
430
535
  }
431
536
 
537
+ if (formatOptions.byteScale) {
538
+ if (!myAxes[0].ticks.stepSize) {
539
+ myAxes[0].ticks.stepSize = formatOptions.byteScale / 2;
540
+ }
541
+ if (!myAxes[0].ticks.maxTicksLimit) {
542
+ myAxes[0].ticks.maxTicksLimit = 4;
543
+ }
544
+ }
545
+
432
546
  if (!myAxes[0].ticks.callback) {
433
547
  myAxes[0].ticks.callback = function (value) {
434
- return formatValue("", value, formatOptions);
548
+ return formatValue("", value, formatOptions, true);
435
549
  };
436
550
  }
437
551
  }
@@ -486,7 +600,7 @@
486
600
 
487
601
  var jsOptions = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle);
488
602
 
489
- var createDataTable = function (chart, options, chartType) {
603
+ var createDataTable = function (chart, options, chartType, library) {
490
604
  var datasets = [];
491
605
  var labels = [];
492
606
 
@@ -631,11 +745,15 @@
631
745
  var xmax = chart.options.xmax;
632
746
 
633
747
  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";
634
752
  if (notnull(xmin)) {
635
- options.scales.xAxes[0].time.min = toDate(xmin).getTime();
753
+ options.scales.xAxes[0][ticksKey].min = toDate(xmin).getTime();
636
754
  }
637
755
  if (notnull(xmax)) {
638
- options.scales.xAxes[0].time.max = toDate(xmax).getTime();
756
+ options.scales.xAxes[0][ticksKey].max = toDate(xmax).getTime();
639
757
  }
640
758
  } else if (chart.xtype === "number") {
641
759
  if (notnull(xmin)) {
@@ -646,6 +764,22 @@
646
764
  }
647
765
  }
648
766
 
767
+ // for empty datetime chart
768
+ if (chart.xtype === "datetime" && labels.length === 0) {
769
+ if (notnull(xmin)) {
770
+ labels.push(toDate(xmin));
771
+ }
772
+ if (notnull(xmax)) {
773
+ labels.push(toDate(xmax));
774
+ }
775
+ day = false;
776
+ week = false;
777
+ month = false;
778
+ year = false;
779
+ hour = false;
780
+ minute = false;
781
+ }
782
+
649
783
  if (chart.xtype === "datetime" && labels.length > 0) {
650
784
  var minTime = (notnull(xmin) ? toDate(xmin) : labels[0]).getTime();
651
785
  var maxTime = (notnull(xmax) ? toDate(xmax) : labels[0]).getTime();
@@ -726,7 +860,7 @@
726
860
  var options = jsOptions(chart, merge(chartOptions, chart.options));
727
861
  setFormatOptions(chart, options, chartType);
728
862
 
729
- var data = createDataTable(chart, options, chartType || "line");
863
+ var data = createDataTable(chart, options, chartType || "line", this.library);
730
864
 
731
865
  if (chart.xtype === "number") {
732
866
  options.scales.xAxes[0].type = "linear";
@@ -787,7 +921,7 @@
787
921
  options = jsOptions(chart, chart.options);
788
922
  }
789
923
  setFormatOptions(chart, options, chartType);
790
- var data = createDataTable(chart, options, "column");
924
+ var data = createDataTable(chart, options, "column", this.library);
791
925
  if (chartType !== "bar") {
792
926
  setLabelSize(chart, data, options);
793
927
  }
@@ -812,7 +946,7 @@
812
946
  options.showLines = false;
813
947
  }
814
948
 
815
- var data = createDataTable(chart, options, chartType);
949
+ var data = createDataTable(chart, options, chartType, this.library);
816
950
 
817
951
  options.scales.xAxes[0].type = "linear";
818
952
  options.scales.xAxes[0].position = "bottom";
@@ -886,6 +1020,7 @@
886
1020
  },
887
1021
  plotOptions: {
888
1022
  areaspline: {},
1023
+ area: {},
889
1024
  series: {
890
1025
  marker: {}
891
1026
  }
@@ -922,7 +1057,10 @@
922
1057
  };
923
1058
 
924
1059
  var setStacked$1 = function (options, stacked) {
925
- options.plotOptions.series.stacking = stacked ? (stacked === true ? "normal" : stacked) : null;
1060
+ var stackedValue = stacked ? (stacked === true ? "normal" : stacked) : null;
1061
+ options.plotOptions.series.stacking = stackedValue;
1062
+ options.plotOptions.area.stacking = stackedValue;
1063
+ options.plotOptions.areaspline.stacking = stackedValue;
926
1064
  };
927
1065
 
928
1066
  var setXtitle$1 = function (options, title) {
@@ -940,7 +1078,10 @@
940
1078
  prefix: chart.options.prefix,
941
1079
  suffix: chart.options.suffix,
942
1080
  thousands: chart.options.thousands,
943
- decimal: chart.options.decimal
1081
+ decimal: chart.options.decimal,
1082
+ precision: chart.options.precision,
1083
+ round: chart.options.round,
1084
+ zeros: chart.options.zeros
944
1085
  };
945
1086
 
946
1087
  if (chartType !== "pie" && !options.yAxis.labels.formatter) {
@@ -1341,7 +1482,7 @@
1341
1482
  defaultExport$2.prototype.renderGeoChart = function renderGeoChart (chart) {
1342
1483
  var this$1 = this;
1343
1484
 
1344
- this.waitForLoaded(chart, function () {
1485
+ this.waitForLoaded(chart, "geochart", function () {
1345
1486
  var chartOptions = {
1346
1487
  legend: "none",
1347
1488
  colorAxis: {
@@ -1457,7 +1598,7 @@
1457
1598
  if (config.language) {
1458
1599
  loadOptions.language = config.language;
1459
1600
  }
1460
- if (pack === "corechart" && config.mapsApiKey) {
1601
+ if (pack === "geochart" && config.mapsApiKey) {
1461
1602
  loadOptions.mapsApiKey = config.mapsApiKey;
1462
1603
  }
1463
1604
 
@@ -1469,7 +1610,7 @@
1469
1610
  var cb, call;
1470
1611
  for (var i = 0; i < callbacks.length; i++) {
1471
1612
  cb = callbacks[i];
1472
- call = this.library.visualization && ((cb.pack === "corechart" && this.library.visualization.LineChart) || (cb.pack === "timeline" && this.library.visualization.Timeline));
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));
1473
1614
  if (call) {
1474
1615
  cb.callback();
1475
1616
  callbacks.splice(i, 1);
@@ -1568,7 +1709,7 @@
1568
1709
  function ajaxCall(url, success, error) {
1569
1710
  var $ = window.jQuery || window.Zepto || window.$;
1570
1711
 
1571
- if ($) {
1712
+ if ($ && $.ajax) {
1572
1713
  $.ajax({
1573
1714
  dataType: "json",
1574
1715
  url: url,
@@ -1605,8 +1746,12 @@
1605
1746
  }
1606
1747
  }
1607
1748
 
1608
- function chartError(element, message) {
1609
- setText(element, "Error Loading Chart: " + message);
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);
1610
1755
  element.style.color = "#ff0000";
1611
1756
  }
1612
1757
 
@@ -1627,6 +1772,17 @@
1627
1772
  }, function (message) {
1628
1773
  chartError(chart.element, message);
1629
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
+ }
1630
1786
  } else {
1631
1787
  chart.rawData = dataSource;
1632
1788
  errorCatcher(chart);
@@ -1829,8 +1985,14 @@
1829
1985
  return r;
1830
1986
  };
1831
1987
 
1832
- function detectXType(series, noDatetime) {
1833
- if (detectXTypeWithFunction(series, isNumber)) {
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)) {
1834
1996
  return "number";
1835
1997
  } else if (!noDatetime && detectXTypeWithFunction(series, isDate)) {
1836
1998
  return "datetime";
@@ -1882,12 +2044,18 @@
1882
2044
  chart.hideLegend = false;
1883
2045
  }
1884
2046
 
1885
- chart.xtype = keyType ? keyType : (opts.discrete ? "string" : detectXType(series, noDatetime));
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));
1886
2055
 
1887
2056
  // right format
1888
- series = copySeries(series);
1889
2057
  for (i = 0; i < series.length; i++) {
1890
- series[i].data = formatSeriesData(toArr(series[i].data), chart.xtype);
2058
+ series[i].data = formatSeriesData(series[i].data, chart.xtype);
1891
2059
  }
1892
2060
 
1893
2061
  return series;
@@ -1972,6 +2140,8 @@
1972
2140
  var sep = this.dataSource.indexOf("?") === -1 ? "?" : "&";
1973
2141
  var url = this.dataSource + sep + "_=" + (new Date()).getTime();
1974
2142
  fetchDataSource(this, url);
2143
+ } else if (typeof this.dataSource === "function") {
2144
+ fetchDataSource(this, this.dataSource);
1975
2145
  }
1976
2146
  };
1977
2147
 
@@ -1980,8 +2150,8 @@
1980
2150
 
1981
2151
  var refresh = this.options.refresh;
1982
2152
 
1983
- if (refresh && typeof this.dataSource !== "string") {
1984
- throw new Error("Data source must be a URL for refresh");
2153
+ if (refresh && typeof this.dataSource !== "string" && typeof this.dataSource !== "function") {
2154
+ throw new Error("Data source must be a URL or callback for refresh");
1985
2155
  }
1986
2156
 
1987
2157
  if (!this.intervalId) {
@@ -2291,4 +2461,4 @@
2291
2461
 
2292
2462
  return Chartkick;
2293
2463
 
2294
- }));
2464
+ })));
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chartkick
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.1
4
+ version: 3.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-15 00:00:00.000000000 Z
11
+ date: 2020-08-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -59,7 +59,6 @@ extensions: []
59
59
  extra_rdoc_files: []
60
60
  files:
61
61
  - CHANGELOG.md
62
- - CONTRIBUTING.md
63
62
  - LICENSE.txt
64
63
  - README.md
65
64
  - lib/chartkick.rb
@@ -88,7 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
87
  - !ruby/object:Gem::Version
89
88
  version: '0'
90
89
  requirements: []
91
- rubygems_version: 3.0.4
90
+ rubygems_version: 3.1.2
92
91
  signing_key:
93
92
  specification_version: 4
94
93
  summary: Create beautiful JavaScript charts with one line of Ruby