chartkick 3.2.0 → 3.3.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of chartkick might be problematic. Click here for more details.

@@ -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.0
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";
@@ -780,12 +914,14 @@
780
914
  defaultExport.prototype.renderColumnChart = function renderColumnChart (chart, chartType) {
781
915
  var options;
782
916
  if (chartType === "bar") {
783
- options = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setTitle, setBarMin, setBarMax, setStacked, setXtitle, setYtitle)(chart, chart.options);
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);
784
920
  } else {
785
921
  options = jsOptions(chart, chart.options);
786
922
  }
787
923
  setFormatOptions(chart, options, chartType);
788
- var data = createDataTable(chart, options, "column");
924
+ var data = createDataTable(chart, options, "column", this.library);
789
925
  if (chartType !== "bar") {
790
926
  setLabelSize(chart, data, options);
791
927
  }
@@ -810,7 +946,7 @@
810
946
  options.showLines = false;
811
947
  }
812
948
 
813
- var data = createDataTable(chart, options, chartType);
949
+ var data = createDataTable(chart, options, chartType, this.library);
814
950
 
815
951
  options.scales.xAxes[0].type = "linear";
816
952
  options.scales.xAxes[0].position = "bottom";
@@ -884,6 +1020,7 @@
884
1020
  },
885
1021
  plotOptions: {
886
1022
  areaspline: {},
1023
+ area: {},
887
1024
  series: {
888
1025
  marker: {}
889
1026
  }
@@ -920,7 +1057,10 @@
920
1057
  };
921
1058
 
922
1059
  var setStacked$1 = function (options, stacked) {
923
- 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;
924
1064
  };
925
1065
 
926
1066
  var setXtitle$1 = function (options, title) {
@@ -938,7 +1078,10 @@
938
1078
  prefix: chart.options.prefix,
939
1079
  suffix: chart.options.suffix,
940
1080
  thousands: chart.options.thousands,
941
- decimal: chart.options.decimal
1081
+ decimal: chart.options.decimal,
1082
+ precision: chart.options.precision,
1083
+ round: chart.options.round,
1084
+ zeros: chart.options.zeros
942
1085
  };
943
1086
 
944
1087
  if (chartType !== "pie" && !options.yAxis.labels.formatter) {
@@ -1339,7 +1482,7 @@
1339
1482
  defaultExport$2.prototype.renderGeoChart = function renderGeoChart (chart) {
1340
1483
  var this$1 = this;
1341
1484
 
1342
- this.waitForLoaded(chart, function () {
1485
+ this.waitForLoaded(chart, "geochart", function () {
1343
1486
  var chartOptions = {
1344
1487
  legend: "none",
1345
1488
  colorAxis: {
@@ -1455,7 +1598,7 @@
1455
1598
  if (config.language) {
1456
1599
  loadOptions.language = config.language;
1457
1600
  }
1458
- if (pack === "corechart" && config.mapsApiKey) {
1601
+ if (pack === "geochart" && config.mapsApiKey) {
1459
1602
  loadOptions.mapsApiKey = config.mapsApiKey;
1460
1603
  }
1461
1604
 
@@ -1467,7 +1610,7 @@
1467
1610
  var cb, call;
1468
1611
  for (var i = 0; i < callbacks.length; i++) {
1469
1612
  cb = callbacks[i];
1470
- 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));
1471
1614
  if (call) {
1472
1615
  cb.callback();
1473
1616
  callbacks.splice(i, 1);
@@ -1566,7 +1709,7 @@
1566
1709
  function ajaxCall(url, success, error) {
1567
1710
  var $ = window.jQuery || window.Zepto || window.$;
1568
1711
 
1569
- if ($) {
1712
+ if ($ && $.ajax) {
1570
1713
  $.ajax({
1571
1714
  dataType: "json",
1572
1715
  url: url,
@@ -1603,8 +1746,12 @@
1603
1746
  }
1604
1747
  }
1605
1748
 
1606
- function chartError(element, message) {
1607
- 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);
1608
1755
  element.style.color = "#ff0000";
1609
1756
  }
1610
1757
 
@@ -1625,6 +1772,17 @@
1625
1772
  }, function (message) {
1626
1773
  chartError(chart.element, message);
1627
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
+ }
1628
1786
  } else {
1629
1787
  chart.rawData = dataSource;
1630
1788
  errorCatcher(chart);
@@ -1827,8 +1985,14 @@
1827
1985
  return r;
1828
1986
  };
1829
1987
 
1830
- function detectXType(series, noDatetime) {
1831
- 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)) {
1832
1996
  return "number";
1833
1997
  } else if (!noDatetime && detectXTypeWithFunction(series, isDate)) {
1834
1998
  return "datetime";
@@ -1880,12 +2044,18 @@
1880
2044
  chart.hideLegend = false;
1881
2045
  }
1882
2046
 
1883
- 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));
1884
2055
 
1885
2056
  // right format
1886
- series = copySeries(series);
1887
2057
  for (i = 0; i < series.length; i++) {
1888
- series[i].data = formatSeriesData(toArr(series[i].data), chart.xtype);
2058
+ series[i].data = formatSeriesData(series[i].data, chart.xtype);
1889
2059
  }
1890
2060
 
1891
2061
  return series;
@@ -1970,6 +2140,8 @@
1970
2140
  var sep = this.dataSource.indexOf("?") === -1 ? "?" : "&";
1971
2141
  var url = this.dataSource + sep + "_=" + (new Date()).getTime();
1972
2142
  fetchDataSource(this, url);
2143
+ } else if (typeof this.dataSource === "function") {
2144
+ fetchDataSource(this, this.dataSource);
1973
2145
  }
1974
2146
  };
1975
2147
 
@@ -1978,8 +2150,8 @@
1978
2150
 
1979
2151
  var refresh = this.options.refresh;
1980
2152
 
1981
- if (refresh && typeof this.dataSource !== "string") {
1982
- 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");
1983
2155
  }
1984
2156
 
1985
2157
  if (!this.intervalId) {
@@ -2289,4 +2461,4 @@
2289
2461
 
2290
2462
  return Chartkick;
2291
2463
 
2292
- }));
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.0
4
+ version: 3.3.2
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-06-04 00:00:00.000000000 Z
11
+ date: 2020-07-24 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.3
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