chartkick 3.2.1 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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