blazer 1.7.5 → 1.7.6

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

Potentially problematic release.


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

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0265c97c2a0672f33553de90d304f7cfe6b329fd
4
- data.tar.gz: 8b75dd5df6a79ca85ea0dc57fb9cce0c57041f2a
3
+ metadata.gz: f8f87990be2cd7082b9e63283da48848a6815c38
4
+ data.tar.gz: cdaacc750e40af335e7ce7d32e208f60b5ac9dc5
5
5
  SHA512:
6
- metadata.gz: 71cf44972395be5cbc664c6d37c9e85d8b2a4f33a70fec4d96da7f4fddff2e9fe4a89e99db42f2f150496c8ee2fe3f5c361d672bd3cbd3ef7fdf7aec653d9cd6
7
- data.tar.gz: 0bd5ed95b4f97474e336e07cba17dc4b75188b22456c42a8aa8f85160866c58a2b83a6e27df644d25d756cd3b3da42d6324121853392e4ccf4ba24b23e65f00c
6
+ metadata.gz: edb80ba4ba8372ac7118ff28b1b3025dcefb13f4ab5a816ce1c6039720b6911bebe4666ea5e7f5e9b190b4cee1ffd00af9f755723cf6c1becaf2e83718f15a1e
7
+ data.tar.gz: 9625b2373ebcef0a78556a4bbb8613b77e879c4dab9618175e3aadd41c138873e716b26a12df8d3ee4a14ffff0c65269455b9831ec7d8555a154b8a0b43f7997
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 1.7.6
2
+
3
+ - Added scatter chart
4
+ - Fixed issue with false values showing up blank
5
+ - Fixed preview for table names with certain characters
6
+
1
7
  ## 1.7.5
2
8
 
3
9
  - Fixed issue with check emails sometimes failing for default Rails 5 ActiveJob adapter
data/README.md CHANGED
@@ -141,7 +141,7 @@ ENV["BLAZER_PASSWORD"] = "secret"
141
141
  ### Devise
142
142
 
143
143
  ```ruby
144
- authenticate :user, lambda { |user| user.admin? } do
144
+ authenticate :user, -> (user) { user.admin? } do
145
145
  mount Blazer::Engine, at: "blazer"
146
146
  end
147
147
  ```
@@ -260,6 +260,8 @@ Of course, you can force a refresh at any time.
260
260
 
261
261
  Blazer will automatically generate charts based on the types of the columns returned in your query.
262
262
 
263
+ **Note:** The order of columns matters.
264
+
263
265
  ### Line Chart
264
266
 
265
267
  There are two ways to generate line charts.
@@ -293,6 +295,14 @@ SELECT gender, COUNT(*) FROM users GROUP BY 1
293
295
  SELECT gender, zip_code, COUNT(*) FROM users GROUP BY 1, 2
294
296
  ```
295
297
 
298
+ ### Scatter Chart
299
+
300
+ 2 columns - both numeric
301
+
302
+ ```sql
303
+ SELECT x, y FROM table
304
+ ```
305
+
296
306
  ### Maps
297
307
 
298
308
  Columns named `latitude` and `longitude` or `lat` and `lon` or `lat` and `lng` - [Example](https://blazerme.herokuapp.com/queries/15-map)
@@ -1,8 +1,8 @@
1
1
  /*
2
2
  * Chartkick.js
3
- * Create beautiful JavaScript charts with minimal code
3
+ * Create beautiful charts with one line of JavaScript
4
4
  * https://github.com/ankane/chartkick.js
5
- * v2.0.0
5
+ * v2.2.1
6
6
  * MIT License
7
7
  */
8
8
 
@@ -15,6 +15,7 @@
15
15
  var Chartkick, ISO8601_PATTERN, DECIMAL_SEPARATOR, adapters = [];
16
16
  var DATE_PATTERN = /^(\d\d\d\d)(\-)?(\d\d)(\-)?(\d\d)$/i;
17
17
  var GoogleChartsAdapter, HighchartsAdapter, ChartjsAdapter;
18
+ var pendingRequests = [], runningRequests = 0, maxRequests = 4;
18
19
 
19
20
  // helpers
20
21
 
@@ -62,10 +63,10 @@
62
63
  function parseISO8601(input) {
63
64
  var day, hour, matches, milliseconds, minutes, month, offset, result, seconds, type, year;
64
65
  type = Object.prototype.toString.call(input);
65
- if (type === '[object Date]') {
66
+ if (type === "[object Date]") {
66
67
  return input;
67
68
  }
68
- if (type !== '[object String]') {
69
+ if (type !== "[object String]") {
69
70
  return;
70
71
  }
71
72
  matches = input.match(ISO8601_PATTERN);
@@ -83,7 +84,7 @@
83
84
  if (matches[17]) {
84
85
  offset += parseInt(matches[17], 10);
85
86
  }
86
- offset *= matches[14] === '-' ? -1 : 1;
87
+ offset *= matches[14] === "-" ? -1 : 1;
87
88
  result -= offset * 60 * 1000;
88
89
  }
89
90
  return new Date(result);
@@ -104,15 +105,18 @@
104
105
  return false;
105
106
  }
106
107
 
107
- function jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle) {
108
- return function (series, opts, chartOptions) {
108
+ function jsOptionsFunc(defaultOptions, hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle) {
109
+ return function (chart, opts, chartOptions) {
110
+ var series = chart.data;
109
111
  var options = merge({}, defaultOptions);
110
112
  options = merge(options, chartOptions || {});
111
113
 
112
- // hide legend
113
- // this is *not* an external option!
114
- if (opts.hideLegend) {
115
- hideLegend(options);
114
+ if (chart.hideLegend || "legend" in opts) {
115
+ hideLegend(options, opts.legend, chart.hideLegend);
116
+ }
117
+
118
+ if (opts.title) {
119
+ setTitle(options, opts.title);
116
120
  }
117
121
 
118
122
  // min
@@ -163,19 +167,61 @@
163
167
  element.style.color = "#ff0000";
164
168
  }
165
169
 
166
- function getJSON(element, url, success) {
167
- var $ = window.jQuery || window.Zepto || window.$;
168
- $.ajax({
169
- dataType: "json",
170
- url: url,
171
- success: success,
172
- error: function (jqXHR, textStatus, errorThrown) {
173
- var message = (typeof errorThrown === "string") ? errorThrown : errorThrown.message;
174
- chartError(element, message);
170
+ function pushRequest(element, url, success) {
171
+ pendingRequests.push([element, url, success]);
172
+ runNext();
173
+ }
174
+
175
+ function runNext() {
176
+ if (runningRequests < maxRequests) {
177
+ var request = pendingRequests.shift()
178
+ if (request) {
179
+ runningRequests++;
180
+ getJSON(request[0], request[1], request[2]);
181
+ runNext();
175
182
  }
183
+ }
184
+ }
185
+
186
+ function requestComplete() {
187
+ runningRequests--;
188
+ runNext();
189
+ }
190
+
191
+ function getJSON(element, url, success) {
192
+ ajaxCall(url, success, function (jqXHR, textStatus, errorThrown) {
193
+ var message = (typeof errorThrown === "string") ? errorThrown : errorThrown.message;
194
+ chartError(element, message);
176
195
  });
177
196
  }
178
197
 
198
+ function ajaxCall(url, success, error) {
199
+ var $ = window.jQuery || window.Zepto || window.$;
200
+
201
+ if ($) {
202
+ $.ajax({
203
+ dataType: "json",
204
+ url: url,
205
+ success: success,
206
+ error: error,
207
+ complete: requestComplete
208
+ });
209
+ } else {
210
+ var xhr = new XMLHttpRequest();
211
+ xhr.open("GET", url, true);
212
+ xhr.setRequestHeader("Content-Type", "application/json");
213
+ xhr.onload = function () {
214
+ requestComplete();
215
+ if (xhr.status === 200) {
216
+ success(JSON.parse(xhr.responseText), xhr.statusText, xhr);
217
+ } else {
218
+ error(xhr, "error", xhr.statusText);
219
+ }
220
+ };
221
+ xhr.send();
222
+ }
223
+ }
224
+
179
225
  function errorCatcher(chart, callback) {
180
226
  try {
181
227
  callback(chart);
@@ -185,18 +231,79 @@
185
231
  }
186
232
  }
187
233
 
188
- function fetchDataSource(chart, callback) {
189
- if (typeof chart.dataSource === "string") {
190
- getJSON(chart.element, chart.dataSource, function (data, textStatus, jqXHR) {
191
- chart.data = data;
234
+ function fetchDataSource(chart, callback, dataSource) {
235
+ if (typeof dataSource === "string") {
236
+ pushRequest(chart.element, dataSource, function (data, textStatus, jqXHR) {
237
+ chart.rawData = data;
192
238
  errorCatcher(chart, callback);
193
239
  });
194
240
  } else {
195
- chart.data = chart.dataSource;
241
+ chart.rawData = dataSource;
196
242
  errorCatcher(chart, callback);
197
243
  }
198
244
  }
199
245
 
246
+ function addDownloadButton(chart) {
247
+ var element = chart.element;
248
+ var link = document.createElement("a");
249
+ link.download = chart.options.download === true ? "chart.png" : chart.options.download; // http://caniuse.com/download
250
+ link.style.position = "absolute";
251
+ link.style.top = "20px";
252
+ link.style.right = "20px";
253
+ link.style.zIndex = 1000;
254
+ link.style.lineHeight = "20px";
255
+ link.target = "_blank"; // for safari
256
+ var image = document.createElement("img");
257
+ image.alt = "Download";
258
+ image.style.border = "none";
259
+ // icon from font-awesome
260
+ // http://fa2png.io/
261
+ image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAMAAAC6V+0/AAABCFBMVEUAAADMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMywEsqxAAAAV3RSTlMAAQIDBggJCgsMDQ4PERQaHB0eISIjJCouLzE0OTo/QUJHSUpLTU5PUllhYmltcHh5foWLjI+SlaCio6atr7S1t7m6vsHHyM7R2tze5Obo7fHz9ff5+/1hlxK2AAAA30lEQVQYGUXBhVYCQQBA0TdYWAt2d3d3YWAHyur7/z9xgD16Lw0DW+XKx+1GgX+FRzM3HWQWrHl5N/oapW5RPe0PkBu+UYeICvozTWZVK23Ao04B79oJrOsJDOoxkZoQPWgX29pHpCZEk7rEvQYiNSFq1UMqvlCjJkRBS1R8hb00Vb/TajtBL7nTHE1X1vyMQF732dQhyF2o6SAwrzP06iUQzvwsArlnzcOdrgBhJyHa1QOgO9U1GsKuvjUTjavliZYQ8nNPapG6sap/3nrIdJ6bOWzmX/fy0XVpfzZP3S8OJT3g9EEiJwAAAABJRU5ErkJggg==";
262
+ link.appendChild(image);
263
+ element.style.position = "relative";
264
+
265
+ chart.downloadAttached = true;
266
+
267
+ // mouseenter
268
+ addEvent(element, "mouseover", function(e) {
269
+ var related = e.relatedTarget;
270
+ // check download option again to ensure it wasn't changed
271
+ if (!related || (related !== this && !childOf(this, related)) && chart.options.download) {
272
+ link.href = chart.toImage();
273
+ element.appendChild(link);
274
+ }
275
+ });
276
+
277
+ // mouseleave
278
+ addEvent(element, "mouseout", function(e) {
279
+ var related = e.relatedTarget;
280
+ if (!related || (related !== this && !childOf(this, related))) {
281
+ if (link.parentNode) {
282
+ link.parentNode.removeChild(link);
283
+ }
284
+ }
285
+ });
286
+ }
287
+
288
+ // http://stackoverflow.com/questions/10149963/adding-event-listener-cross-browser
289
+ function addEvent(elem, event, fn) {
290
+ if (elem.addEventListener) {
291
+ elem.addEventListener(event, fn, false);
292
+ } else {
293
+ elem.attachEvent("on" + event, function() {
294
+ // set the this pointer same as addEventListener when fn is called
295
+ return(fn.call(elem, window.event));
296
+ });
297
+ }
298
+ }
299
+
300
+ // https://gist.github.com/shawnbot/4166283
301
+ function childOf(p, c) {
302
+ if (p === c) return false;
303
+ while (c && c !== p) c = c.parentNode;
304
+ return c === p;
305
+ }
306
+
200
307
  // type conversions
201
308
 
202
309
  function toStr(n) {
@@ -299,8 +406,25 @@
299
406
  }
300
407
  };
301
408
 
302
- var hideLegend = function (options) {
303
- options.legend.enabled = false;
409
+ var hideLegend = function (options, legend, hideLegend) {
410
+ if (legend !== undefined) {
411
+ options.legend.enabled = !!legend;
412
+ if (legend && legend !== true) {
413
+ if (legend === "top" || legend === "bottom") {
414
+ options.legend.verticalAlign = legend;
415
+ } else {
416
+ options.legend.layout = "vertical";
417
+ options.legend.verticalAlign = "middle";
418
+ options.legend.align = legend;
419
+ }
420
+ }
421
+ } else if (hideLegend) {
422
+ options.legend.enabled = false;
423
+ }
424
+ };
425
+
426
+ var setTitle = function (options, title) {
427
+ options.title.text = title;
304
428
  };
305
429
 
306
430
  var setMin = function (options, min) {
@@ -323,7 +447,7 @@
323
447
  options.yAxis.title.text = title;
324
448
  };
325
449
 
326
- var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle);
450
+ var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle);
327
451
 
328
452
  this.renderLineChart = function (chart, chartType) {
329
453
  chartType = chartType || "spline";
@@ -334,6 +458,9 @@
334
458
  areaspline: {
335
459
  stacking: "normal"
336
460
  },
461
+ area: {
462
+ stacking: "normal"
463
+ },
337
464
  series: {
338
465
  marker: {
339
466
  enabled: false
@@ -342,15 +469,26 @@
342
469
  }
343
470
  };
344
471
  }
345
- var options = jsOptions(chart.data, chart.options, chartOptions), data, i, j;
346
- options.xAxis.type = chart.options.discrete ? "category" : "datetime";
347
- options.chart.type = chartType;
472
+
473
+ if (chart.options.curve === false) {
474
+ if (chartType === "areaspline") {
475
+ chartType = "area";
476
+ } else if (chartType === "spline") {
477
+ chartType = "line";
478
+ }
479
+ }
480
+
481
+ var options = jsOptions(chart, chart.options, chartOptions), data, i, j;
482
+ options.xAxis.type = chart.discrete ? "category" : "datetime";
483
+ if (!options.chart.type) {
484
+ options.chart.type = chartType;
485
+ }
348
486
  options.chart.renderTo = chart.element.id;
349
487
 
350
488
  var series = chart.data;
351
489
  for (i = 0; i < series.length; i++) {
352
490
  data = series[i].data;
353
- if (!chart.options.discrete) {
491
+ if (!chart.discrete) {
354
492
  for (j = 0; j < data.length; j++) {
355
493
  data[j][0] = data[j][0].getTime();
356
494
  }
@@ -358,37 +496,50 @@
358
496
  series[i].marker = {symbol: "circle"};
359
497
  }
360
498
  options.series = series;
361
- new Highcharts.Chart(options);
499
+ chart.chart = new Highcharts.Chart(options);
362
500
  };
363
501
 
364
502
  this.renderScatterChart = function (chart) {
365
503
  var chartOptions = {};
366
- var options = jsOptions(chart.data, chart.options, chartOptions);
367
- options.chart.type = 'scatter';
504
+ var options = jsOptions(chart, chart.options, chartOptions);
505
+ options.chart.type = "scatter";
368
506
  options.chart.renderTo = chart.element.id;
369
507
  options.series = chart.data;
370
- new Highcharts.Chart(options);
508
+ chart.chart = new Highcharts.Chart(options);
371
509
  };
372
510
 
373
511
  this.renderPieChart = function (chart) {
374
- var chartOptions = {};
512
+ var chartOptions = merge(defaultOptions, {});
513
+
375
514
  if (chart.options.colors) {
376
515
  chartOptions.colors = chart.options.colors;
377
516
  }
378
- var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
517
+ if (chart.options.donut) {
518
+ chartOptions.plotOptions = {pie: {innerSize: "50%"}};
519
+ }
520
+
521
+ if ("legend" in chart.options) {
522
+ hideLegend(chartOptions, chart.options.legend);
523
+ }
524
+
525
+ if (chart.options.title) {
526
+ setTitle(chartOptions, chart.options.title);
527
+ }
528
+
529
+ var options = merge(chartOptions, chart.options.library || {});
379
530
  options.chart.renderTo = chart.element.id;
380
531
  options.series = [{
381
532
  type: "pie",
382
533
  name: chart.options.label || "Value",
383
534
  data: chart.data
384
535
  }];
385
- new Highcharts.Chart(options);
536
+ chart.chart = new Highcharts.Chart(options);
386
537
  };
387
538
 
388
539
  this.renderColumnChart = function (chart, chartType) {
389
540
  chartType = chartType || "column";
390
541
  var series = chart.data;
391
- var options = jsOptions(series, chart.options), i, j, s, d, rows = [];
542
+ var options = jsOptions(chart, chart.options), i, j, s, d, rows = [], categories = [];
392
543
  options.chart.type = chartType;
393
544
  options.chart.renderTo = chart.element.id;
394
545
 
@@ -399,17 +550,12 @@
399
550
  d = s.data[j];
400
551
  if (!rows[d[0]]) {
401
552
  rows[d[0]] = new Array(series.length);
553
+ categories.push(d[0]);
402
554
  }
403
555
  rows[d[0]][i] = d[1];
404
556
  }
405
557
  }
406
558
 
407
- var categories = [];
408
- for (i in rows) {
409
- if (rows.hasOwnProperty(i)) {
410
- categories.push(i);
411
- }
412
- }
413
559
  options.xAxis.categories = categories;
414
560
 
415
561
  var newSeries = [];
@@ -426,7 +572,7 @@
426
572
  }
427
573
  options.series = newSeries;
428
574
 
429
- new Highcharts.Chart(options);
575
+ chart.chart = new Highcharts.Chart(options);
430
576
  };
431
577
 
432
578
  var self = this;
@@ -441,7 +587,7 @@
441
587
  };
442
588
  adapters.push(HighchartsAdapter);
443
589
  }
444
- if (!GoogleChartsAdapter && window.google && window.google.setOnLoadCallback) {
590
+ if (!GoogleChartsAdapter && window.google && (window.google.setOnLoadCallback || window.google.charts)) {
445
591
  GoogleChartsAdapter = new function () {
446
592
  var google = window.google;
447
593
 
@@ -484,7 +630,12 @@
484
630
  if (config.language) {
485
631
  loadOptions.language = config.language;
486
632
  }
487
- google.load("visualization", "1", loadOptions);
633
+
634
+ if (window.google.setOnLoadCallback) {
635
+ google.load("visualization", "1", loadOptions);
636
+ } else {
637
+ google.charts.load("current", loadOptions);
638
+ }
488
639
  }
489
640
  };
490
641
 
@@ -531,8 +682,25 @@
531
682
  }
532
683
  };
533
684
 
534
- var hideLegend = function (options) {
535
- options.legend.position = "none";
685
+ var hideLegend = function (options, legend, hideLegend) {
686
+ if (legend !== undefined) {
687
+ var position;
688
+ if (!legend) {
689
+ position = "none";
690
+ } else if (legend === true) {
691
+ position = "right";
692
+ } else {
693
+ position = legend;
694
+ }
695
+ options.legend.position = position;
696
+ } else if (hideLegend) {
697
+ options.legend.position = "none";
698
+ }
699
+ };
700
+
701
+ var setTitle = function (options, title) {
702
+ options.title = title;
703
+ options.titleTextStyle = {color: "#333", fontSize: "20px"};
536
704
  };
537
705
 
538
706
  var setMin = function (options, min) {
@@ -565,11 +733,11 @@
565
733
  options.vAxis.titleTextStyle.italic = false;
566
734
  };
567
735
 
568
- var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle);
736
+ var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle);
569
737
 
570
738
  // cant use object as key
571
739
  var createDataTable = function (series, columnType) {
572
- var i, j, s, d, key, rows = [];
740
+ var i, j, s, d, key, rows = [], sortedLabels = [];
573
741
  for (i = 0; i < series.length; i++) {
574
742
  s = series[i];
575
743
 
@@ -578,6 +746,7 @@
578
746
  key = (columnType === "datetime") ? d[0].getTime() : d[0];
579
747
  if (!rows[key]) {
580
748
  rows[key] = new Array(series.length);
749
+ sortedLabels.push(key);
581
750
  }
582
751
  rows[key][i] = toFloat(d[1]);
583
752
  }
@@ -586,18 +755,17 @@
586
755
  var rows2 = [];
587
756
  var day = true;
588
757
  var value;
589
- for (i in rows) {
590
- if (rows.hasOwnProperty(i)) {
591
- if (columnType === "datetime") {
592
- value = new Date(toFloat(i));
593
- day = day && isDay(value);
594
- } else if (columnType === "number") {
595
- value = toFloat(i);
596
- } else {
597
- value = i;
598
- }
599
- rows2.push([value].concat(rows[i]));
758
+ for (var j = 0; j < sortedLabels.length; j++) {
759
+ var i = sortedLabels[j];
760
+ if (columnType === "datetime") {
761
+ value = new Date(toFloat(i));
762
+ day = day && isDay(value);
763
+ } else if (columnType === "number") {
764
+ value = toFloat(i);
765
+ } else {
766
+ value = i;
600
767
  }
768
+ rows2.push([value].concat(rows[i]));
601
769
  }
602
770
  if (columnType === "datetime") {
603
771
  rows2.sort(sortByTime);
@@ -626,8 +794,14 @@
626
794
 
627
795
  this.renderLineChart = function (chart) {
628
796
  waitForLoaded(function () {
629
- var options = jsOptions(chart.data, chart.options);
630
- var data = createDataTable(chart.data, chart.options.discrete ? "string" : "datetime");
797
+ var chartOptions = {};
798
+
799
+ if (chart.options.curve === false) {
800
+ chartOptions.curveType = "none";
801
+ }
802
+
803
+ var options = jsOptions(chart, chart.options, chartOptions);
804
+ var data = createDataTable(chart.data, chart.discrete ? "string" : "datetime");
631
805
  chart.chart = new google.visualization.LineChart(chart.element);
632
806
  resize(function () {
633
807
  chart.chart.draw(data, options);
@@ -641,11 +815,21 @@
641
815
  chartArea: {
642
816
  top: "10%",
643
817
  height: "80%"
644
- }
818
+ },
819
+ legend: {}
645
820
  };
646
821
  if (chart.options.colors) {
647
822
  chartOptions.colors = chart.options.colors;
648
823
  }
824
+ if (chart.options.donut) {
825
+ chartOptions.pieHole = 0.5;
826
+ }
827
+ if ("legend" in chart.options) {
828
+ hideLegend(chartOptions, chart.options.legend);
829
+ }
830
+ if (chart.options.title) {
831
+ setTitle(chartOptions, chart.options.title);
832
+ }
649
833
  var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
650
834
 
651
835
  var data = new google.visualization.DataTable();
@@ -662,7 +846,7 @@
662
846
 
663
847
  this.renderColumnChart = function (chart) {
664
848
  waitForLoaded(function () {
665
- var options = jsOptions(chart.data, chart.options);
849
+ var options = jsOptions(chart, chart.options);
666
850
  var data = createDataTable(chart.data, "string");
667
851
  chart.chart = new google.visualization.ColumnChart(chart.element);
668
852
  resize(function () {
@@ -680,7 +864,7 @@
680
864
  }
681
865
  }
682
866
  };
683
- var options = jsOptionsFunc(defaultOptions, hideLegend, setBarMin, setBarMax, setStacked)(chart.data, chart.options, chartOptions);
867
+ var options = jsOptionsFunc(defaultOptions, hideLegend, setTitle, setBarMin, setBarMax, setStacked, setXtitle, setYtitle)(chart, chart.options, chartOptions);
684
868
  var data = createDataTable(chart.data, "string");
685
869
  chart.chart = new google.visualization.BarChart(chart.element);
686
870
  resize(function () {
@@ -696,8 +880,8 @@
696
880
  pointSize: 0,
697
881
  areaOpacity: 0.5
698
882
  };
699
- var options = jsOptions(chart.data, chart.options, chartOptions);
700
- var data = createDataTable(chart.data, chart.options.discrete ? "string" : "datetime");
883
+ var options = jsOptions(chart, chart.options, chartOptions);
884
+ var data = createDataTable(chart.data, chart.discrete ? "string" : "datetime");
701
885
  chart.chart = new google.visualization.AreaChart(chart.element);
702
886
  resize(function () {
703
887
  chart.chart.draw(data, options);
@@ -730,7 +914,7 @@
730
914
  this.renderScatterChart = function (chart) {
731
915
  waitForLoaded(function () {
732
916
  var chartOptions = {};
733
- var options = jsOptions(chart.data, chart.options, chartOptions);
917
+ var options = jsOptions(chart, chart.options, chartOptions);
734
918
  var data = createDataTable(chart.data, "number");
735
919
 
736
920
  chart.chart = new google.visualization.ScatterChart(chart.element);
@@ -777,7 +961,12 @@
777
961
 
778
962
  var baseOptions = {
779
963
  maintainAspectRatio: false,
780
- animation: false
964
+ animation: false,
965
+ tooltips: {
966
+ displayColors: false
967
+ },
968
+ legend: {},
969
+ title: {fontSize: 20, fontColor: "#333"}
781
970
  };
782
971
 
783
972
  var defaultOptions = {
@@ -808,8 +997,7 @@
808
997
  ticks: {}
809
998
  }
810
999
  ]
811
- },
812
- legend: {}
1000
+ }
813
1001
  };
814
1002
 
815
1003
  // http://there4.io/2012/05/02/google-chart-color-list/
@@ -819,28 +1007,40 @@
819
1007
  "#6633CC", "#E67300", "#8B0707", "#329262", "#5574A6", "#3B3EAC"
820
1008
  ];
821
1009
 
822
- var hideLegend = function (options) {
823
- options.legend.display = false;
1010
+ var hideLegend = function (options, legend, hideLegend) {
1011
+ if (legend !== undefined) {
1012
+ options.legend.display = !!legend;
1013
+ if (legend && legend !== true) {
1014
+ options.legend.position = legend;
1015
+ }
1016
+ } else if (hideLegend) {
1017
+ options.legend.display = false;
1018
+ }
1019
+ };
1020
+
1021
+ var setTitle = function (options, title) {
1022
+ options.title.display = true;
1023
+ options.title.text = title;
824
1024
  };
825
1025
 
826
1026
  var setMin = function (options, min) {
827
1027
  if (min !== null) {
828
- options.scales.yAxes[0].ticks.min = min;
1028
+ options.scales.yAxes[0].ticks.min = toFloat(min);
829
1029
  }
830
1030
  };
831
1031
 
832
1032
  var setMax = function (options, max) {
833
- options.scales.yAxes[0].ticks.max = max;
1033
+ options.scales.yAxes[0].ticks.max = toFloat(max);
834
1034
  };
835
1035
 
836
1036
  var setBarMin = function (options, min) {
837
1037
  if (min !== null) {
838
- options.scales.xAxes[0].ticks.min = min;
1038
+ options.scales.xAxes[0].ticks.min = toFloat(min);
839
1039
  }
840
1040
  };
841
1041
 
842
1042
  var setBarMax = function (options, max) {
843
- options.scales.xAxes[0].ticks.max = max;
1043
+ options.scales.xAxes[0].ticks.max = toFloat(max);
844
1044
  };
845
1045
 
846
1046
  var setStacked = function (options, stacked) {
@@ -859,9 +1059,13 @@
859
1059
  };
860
1060
 
861
1061
  var drawChart = function(chart, type, data, options) {
862
- chart.element.innerHTML = "<canvas></canvas>";
863
- var ctx = chart.element.getElementsByTagName("CANVAS")[0];
1062
+ if (chart.chart) {
1063
+ chart.chart.destroy();
1064
+ } else {
1065
+ chart.element.innerHTML = "<canvas></canvas>";
1066
+ }
864
1067
 
1068
+ var ctx = chart.element.getElementsByTagName("CANVAS")[0];
865
1069
  chart.chart = new Chart(ctx, {
866
1070
  type: type,
867
1071
  data: data,
@@ -890,7 +1094,7 @@
890
1094
  };
891
1095
  };
892
1096
 
893
- var jsOptions = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle);
1097
+ var jsOptions = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle);
894
1098
 
895
1099
  var createDataTable = function (chart, options, chartType) {
896
1100
  var datasets = [];
@@ -905,7 +1109,7 @@
905
1109
  var year = true;
906
1110
  var hour = true;
907
1111
  var minute = true;
908
- var detectType = (chartType === "line" || chartType === "area") && !chart.options.discrete;
1112
+ var detectType = (chartType === "line" || chartType === "area") && !chart.discrete;
909
1113
 
910
1114
  var series = chart.data;
911
1115
 
@@ -958,7 +1162,8 @@
958
1162
  }
959
1163
  labels.push(value);
960
1164
  for (j = 0; j < series.length; j++) {
961
- rows2[j].push(rows[i][j]);
1165
+ // Chart.js doesn't like undefined
1166
+ rows2[j].push(rows[i][j] === undefined ? null : rows[i][j]);
962
1167
  }
963
1168
  }
964
1169
 
@@ -977,6 +1182,10 @@
977
1182
  borderWidth: 2
978
1183
  };
979
1184
 
1185
+ if (chart.options.curve === false) {
1186
+ dataset.lineTension = 0;
1187
+ }
1188
+
980
1189
  datasets.push(merge(dataset, s.library || {}));
981
1190
  }
982
1191
 
@@ -1006,7 +1215,8 @@
1006
1215
  } else if (day || timeDiff > 10) {
1007
1216
  options.scales.xAxes[0].time.unit = "day";
1008
1217
  step = 1;
1009
- } else if (hour) {
1218
+ } else if (hour || timeDiff > 0.5) {
1219
+ options.scales.xAxes[0].time.displayFormats = {hour: "MMM D, h a"};
1010
1220
  options.scales.xAxes[0].time.unit = "hour";
1011
1221
  step = 1 / 24.0;
1012
1222
  } else if (minute) {
@@ -1015,7 +1225,6 @@
1015
1225
  step = 1 / 24.0 / 60.0;
1016
1226
  }
1017
1227
 
1018
-
1019
1228
  if (step && timeDiff > 0) {
1020
1229
  var unitStepSize = Math.ceil(timeDiff / step / (chart.element.offsetWidth / 100.0));
1021
1230
  if (week && step === 1) {
@@ -1045,27 +1254,40 @@
1045
1254
  };
1046
1255
 
1047
1256
  this.renderLineChart = function (chart, chartType) {
1048
- var areaOptions = {};
1257
+ var chartOptions = {};
1049
1258
  if (chartType === "area") {
1050
1259
  // TODO fix area stacked
1051
- // areaOptions.stacked = true;
1260
+ // chartOptions.stacked = true;
1052
1261
  }
1053
1262
  // fix for https://github.com/chartjs/Chart.js/issues/2441
1054
1263
  if (!chart.options.max && allZeros(chart.data)) {
1055
- chart.options.max = 1;
1264
+ chartOptions.max = 1;
1056
1265
  }
1057
1266
 
1058
- var options = jsOptions(chart.data, merge(areaOptions, chart.options));
1267
+ var options = jsOptions(chart, merge(chartOptions, chart.options));
1059
1268
 
1060
1269
  var data = createDataTable(chart, options, chartType || "line");
1061
1270
 
1062
- options.scales.xAxes[0].type = chart.options.discrete ? "category" : "time";
1271
+ options.scales.xAxes[0].type = chart.discrete ? "category" : "time";
1063
1272
 
1064
1273
  drawChart(chart, "line", data, options);
1065
1274
  };
1066
1275
 
1067
1276
  this.renderPieChart = function (chart) {
1068
- var options = merge(baseOptions, chart.options.library || {});
1277
+ var options = merge({}, baseOptions);
1278
+ if (chart.options.donut) {
1279
+ options.cutoutPercentage = 50;
1280
+ }
1281
+
1282
+ if ("legend" in chart.options) {
1283
+ hideLegend(options, chart.options.legend);
1284
+ }
1285
+
1286
+ if (chart.options.title) {
1287
+ setTitle(options, chart.options.title);
1288
+ }
1289
+
1290
+ options = merge(options, chart.options.library || {});
1069
1291
 
1070
1292
  var labels = [];
1071
1293
  var values = [];
@@ -1091,9 +1313,9 @@
1091
1313
  this.renderColumnChart = function (chart, chartType) {
1092
1314
  var options;
1093
1315
  if (chartType === "bar") {
1094
- options = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setBarMin, setBarMax, setStacked)(chart.data, chart.options);
1316
+ options = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setTitle, setBarMin, setBarMax, setStacked, setXtitle, setYtitle)(chart, chart.options);
1095
1317
  } else {
1096
- options = jsOptions(chart.data, chart.options);
1318
+ options = jsOptions(chart, chart.options);
1097
1319
  }
1098
1320
  var data = createDataTable(chart, options, "column");
1099
1321
  setLabelSize(chart, data, options);
@@ -1109,15 +1331,57 @@
1109
1331
  this.renderBarChart = function (chart) {
1110
1332
  self.renderColumnChart(chart, "bar");
1111
1333
  };
1334
+
1335
+ this.renderScatterChart = function (chart) {
1336
+ var options = jsOptions(chart, chart.options);
1337
+
1338
+ var colors = chart.options.colors || defaultColors;
1339
+
1340
+ var datasets = [];
1341
+ var series = chart.data;
1342
+ for (var i = 0; i < series.length; i++) {
1343
+ var s = series[i];
1344
+ var d = [];
1345
+ for (var j = 0; j < s.data.length; j++) {
1346
+ d.push({
1347
+ x: toFloat(s.data[j][0]),
1348
+ y: toFloat(s.data[j][1])
1349
+ });
1350
+ }
1351
+
1352
+ datasets.push({
1353
+ label: s.name,
1354
+ showLine: false,
1355
+ data: d,
1356
+ borderColor: colors[i],
1357
+ backgroundColor: colors[i],
1358
+ pointBackgroundColor: colors[i]
1359
+ })
1360
+ }
1361
+
1362
+ var data = {datasets: datasets};
1363
+
1364
+ options.scales.xAxes[0].type = "linear";
1365
+ options.scales.xAxes[0].position = "bottom";
1366
+
1367
+ drawChart(chart, "line", data, options);
1368
+ };
1112
1369
  };
1113
1370
 
1114
1371
  adapters.unshift(ChartjsAdapter);
1115
1372
  }
1116
1373
  }
1117
1374
 
1375
+ function renderChart(chartType, chart) {
1376
+ callAdapter(chartType, chart);
1377
+ if (chart.options.download && !chart.downloadAttached && chart.adapter === "chartjs") {
1378
+ addDownloadButton(chart);
1379
+ }
1380
+ }
1381
+
1118
1382
  // TODO remove chartType if cross-browser way
1119
1383
  // to get the name of the chart class
1120
- function renderChart(chartType, chart) {
1384
+ function callAdapter(chartType, chart) {
1121
1385
  var i, adapter, fnName, adapterName;
1122
1386
  fnName = "render" + chartType;
1123
1387
  adapterName = chart.options.adapter;
@@ -1127,6 +1391,7 @@
1127
1391
  for (i = 0; i < adapters.length; i++) {
1128
1392
  adapter = adapters[i];
1129
1393
  if ((!adapterName || adapterName === adapter.name) && isFunction(adapter[fnName])) {
1394
+ chart.adapter = adapter.name;
1130
1395
  return adapter[fnName](chart);
1131
1396
  }
1132
1397
  }
@@ -1212,20 +1477,25 @@
1212
1477
  return false;
1213
1478
  }
1214
1479
 
1215
- function processSeries(series, opts, keyType) {
1480
+ function processSeries(chart, keyType) {
1216
1481
  var i;
1217
1482
 
1483
+ var opts = chart.options;
1484
+ var series = chart.rawData;
1485
+
1218
1486
  // see if one series or multiple
1219
1487
  if (!isArray(series) || typeof series[0] !== "object" || isArray(series[0])) {
1220
1488
  series = [{name: opts.label || "Value", data: series}];
1221
- opts.hideLegend = true;
1489
+ chart.hideLegend = true;
1222
1490
  } else {
1223
- opts.hideLegend = false;
1491
+ chart.hideLegend = false;
1224
1492
  }
1225
1493
  if ((opts.discrete === null || opts.discrete === undefined)) {
1226
- opts.discrete = detectDiscrete(series);
1494
+ chart.discrete = detectDiscrete(series);
1495
+ } else {
1496
+ chart.discrete = opts.discrete;
1227
1497
  }
1228
- if (opts.discrete) {
1498
+ if (chart.discrete) {
1229
1499
  keyType = "string";
1230
1500
  }
1231
1501
 
@@ -1237,17 +1507,17 @@
1237
1507
  return series;
1238
1508
  }
1239
1509
 
1240
- function processSimple(data) {
1241
- var perfectData = toArr(data), i;
1510
+ function processSimple(chart) {
1511
+ var perfectData = toArr(chart.rawData), i;
1242
1512
  for (i = 0; i < perfectData.length; i++) {
1243
1513
  perfectData[i] = [toStr(perfectData[i][0]), toFloat(perfectData[i][1])];
1244
1514
  }
1245
1515
  return perfectData;
1246
1516
  }
1247
1517
 
1248
- function processTime(data)
1518
+ function processTime(chart)
1249
1519
  {
1250
- var i;
1520
+ var i, data = chart.rawData;
1251
1521
  for (i = 0; i < data.length; i++) {
1252
1522
  data[i][1] = toDate(data[i][1]);
1253
1523
  data[i][2] = toDate(data[i][2]);
@@ -1256,46 +1526,26 @@
1256
1526
  }
1257
1527
 
1258
1528
  function processLineData(chart) {
1259
- chart.data = processSeries(chart.data, chart.options, "datetime");
1260
- renderChart("LineChart", chart);
1529
+ return processSeries(chart, "datetime");
1261
1530
  }
1262
1531
 
1263
1532
  function processColumnData(chart) {
1264
- chart.data = processSeries(chart.data, chart.options, "string");
1265
- renderChart("ColumnChart", chart);
1266
- }
1267
-
1268
- function processPieData(chart) {
1269
- chart.data = processSimple(chart.data);
1270
- renderChart("PieChart", chart);
1533
+ return processSeries(chart, "string");
1271
1534
  }
1272
1535
 
1273
1536
  function processBarData(chart) {
1274
- chart.data = processSeries(chart.data, chart.options, "string");
1275
- renderChart("BarChart", chart);
1537
+ return processSeries(chart, "string");
1276
1538
  }
1277
1539
 
1278
1540
  function processAreaData(chart) {
1279
- chart.data = processSeries(chart.data, chart.options, "datetime");
1280
- renderChart("AreaChart", chart);
1281
- }
1282
-
1283
- function processGeoData(chart) {
1284
- chart.data = processSimple(chart.data);
1285
- renderChart("GeoChart", chart);
1541
+ return processSeries(chart, "datetime");
1286
1542
  }
1287
1543
 
1288
1544
  function processScatterData(chart) {
1289
- chart.data = processSeries(chart.data, chart.options, "number");
1290
- renderChart("ScatterChart", chart);
1291
- }
1292
-
1293
- function processTimelineData(chart) {
1294
- chart.data = processTime(chart.data);
1295
- renderChart("Timeline", chart);
1545
+ return processSeries(chart, "number");
1296
1546
  }
1297
1547
 
1298
- function setElement(chart, element, dataSource, opts, callback) {
1548
+ function createChart(chartType, chart, element, dataSource, opts, processData) {
1299
1549
  var elementId;
1300
1550
  if (typeof element === "string") {
1301
1551
  elementId = element;
@@ -1304,53 +1554,135 @@
1304
1554
  throw new Error("No element with id " + elementId);
1305
1555
  }
1306
1556
  }
1557
+
1307
1558
  chart.element = element;
1308
- chart.options = opts || {};
1559
+ opts = merge(Chartkick.options, opts || {});
1560
+ chart.options = opts;
1309
1561
  chart.dataSource = dataSource;
1562
+
1563
+ if (!processData) {
1564
+ processData = function (chart) {
1565
+ return chart.rawData;
1566
+ }
1567
+ }
1568
+
1569
+ // getters
1310
1570
  chart.getElement = function () {
1311
1571
  return element;
1312
1572
  };
1573
+ chart.getDataSource = function () {
1574
+ return chart.dataSource;
1575
+ };
1313
1576
  chart.getData = function () {
1314
1577
  return chart.data;
1315
1578
  };
1316
1579
  chart.getOptions = function () {
1317
- return opts || {};
1580
+ return chart.options;
1318
1581
  };
1319
1582
  chart.getChartObject = function () {
1320
1583
  return chart.chart;
1321
1584
  };
1585
+ chart.getAdapter = function () {
1586
+ return chart.adapter;
1587
+ };
1588
+
1589
+ var callback = function () {
1590
+ chart.data = processData(chart);
1591
+ renderChart(chartType, chart);
1592
+ };
1593
+
1594
+ // functions
1595
+ chart.updateData = function (dataSource, options) {
1596
+ chart.dataSource = dataSource;
1597
+ if (options) {
1598
+ chart.options = merge(Chartkick.options, options);
1599
+ }
1600
+ fetchDataSource(chart, callback, dataSource);
1601
+ };
1602
+ chart.setOptions = function (options) {
1603
+ chart.options = merge(Chartkick.options, options);
1604
+ chart.redraw();
1605
+ };
1606
+ chart.redraw = function() {
1607
+ fetchDataSource(chart, callback, chart.rawData);
1608
+ };
1609
+ chart.refreshData = function () {
1610
+ if (typeof dataSource === "string") {
1611
+ // prevent browser from caching
1612
+ var sep = dataSource.indexOf("?") === -1 ? "?" : "&";
1613
+ var url = dataSource + sep + "_=" + (new Date()).getTime();
1614
+ fetchDataSource(chart, callback, url);
1615
+ }
1616
+ };
1617
+ chart.stopRefresh = function () {
1618
+ if (chart.intervalId) {
1619
+ clearInterval(chart.intervalId);
1620
+ }
1621
+ };
1622
+ chart.toImage = function () {
1623
+ if (chart.adapter === "chartjs") {
1624
+ return chart.chart.toBase64Image();
1625
+ } else {
1626
+ return null;
1627
+ }
1628
+ }
1629
+
1322
1630
  Chartkick.charts[element.id] = chart;
1323
- fetchDataSource(chart, callback);
1631
+
1632
+ fetchDataSource(chart, callback, dataSource);
1633
+
1634
+ if (opts.refresh) {
1635
+ chart.intervalId = setInterval( function () {
1636
+ chart.refreshData();
1637
+ }, opts.refresh * 1000);
1638
+ }
1324
1639
  }
1325
1640
 
1326
1641
  // define classes
1327
1642
 
1328
1643
  Chartkick = {
1329
- LineChart: function (element, dataSource, opts) {
1330
- setElement(this, element, dataSource, opts, processLineData);
1644
+ LineChart: function (element, dataSource, options) {
1645
+ createChart("LineChart", this, element, dataSource, options, processLineData);
1646
+ },
1647
+ PieChart: function (element, dataSource, options) {
1648
+ createChart("PieChart", this, element, dataSource, options, processSimple);
1331
1649
  },
1332
- PieChart: function (element, dataSource, opts) {
1333
- setElement(this, element, dataSource, opts, processPieData);
1650
+ ColumnChart: function (element, dataSource, options) {
1651
+ createChart("ColumnChart", this, element, dataSource, options, processColumnData);
1334
1652
  },
1335
- ColumnChart: function (element, dataSource, opts) {
1336
- setElement(this, element, dataSource, opts, processColumnData);
1653
+ BarChart: function (element, dataSource, options) {
1654
+ createChart("BarChart", this, element, dataSource, options, processBarData);
1337
1655
  },
1338
- BarChart: function (element, dataSource, opts) {
1339
- setElement(this, element, dataSource, opts, processBarData);
1656
+ AreaChart: function (element, dataSource, options) {
1657
+ createChart("AreaChart", this, element, dataSource, options, processAreaData);
1340
1658
  },
1341
- AreaChart: function (element, dataSource, opts) {
1342
- setElement(this, element, dataSource, opts, processAreaData);
1659
+ GeoChart: function (element, dataSource, options) {
1660
+ createChart("GeoChart", this, element, dataSource, options, processSimple);
1343
1661
  },
1344
- GeoChart: function (element, dataSource, opts) {
1345
- setElement(this, element, dataSource, opts, processGeoData);
1662
+ ScatterChart: function (element, dataSource, options) {
1663
+ createChart("ScatterChart", this, element, dataSource, options, processScatterData);
1346
1664
  },
1347
- ScatterChart: function (element, dataSource, opts) {
1348
- setElement(this, element, dataSource, opts, processScatterData);
1665
+ Timeline: function (element, dataSource, options) {
1666
+ createChart("Timeline", this, element, dataSource, options, processTime);
1349
1667
  },
1350
- Timeline: function (element, dataSource, opts) {
1351
- setElement(this, element, dataSource, opts, processTimelineData);
1668
+ charts: {},
1669
+ configure: function (options) {
1670
+ for (var key in options) {
1671
+ if (options.hasOwnProperty(key)) {
1672
+ config[key] = options[key];
1673
+ }
1674
+ }
1675
+ },
1676
+ eachChart: function (callback) {
1677
+ for (var chartId in Chartkick.charts) {
1678
+ if (Chartkick.charts.hasOwnProperty(chartId)) {
1679
+ callback(Chartkick.charts[chartId]);
1680
+ }
1681
+ }
1352
1682
  },
1353
- charts: {}
1683
+ options: {},
1684
+ adapters: adapters,
1685
+ createChart: createChart
1354
1686
  };
1355
1687
 
1356
1688
  if (typeof module === "object" && typeof module.exports === "object") {
@@ -218,7 +218,7 @@
218
218
  var $select = $("#table_names").selectize({})
219
219
  var selectize = $select[0].selectize
220
220
  selectize.on("change", function(val) {
221
- editor.setValue(previewStatement[_this.dataSource].replace("{table}", val), 1)
221
+ editor.setValue(previewStatement[_this.dataSource].replace("{table}", '"' + val + '"'), 1)
222
222
  _this.run()
223
223
  selectize.clear(true)
224
224
  selectize.blur()
@@ -1,5 +1,5 @@
1
1
  <% if @error %>
2
- <div class="alert alert-danger"><%= @error %></div>
2
+ <div class="alert alert-danger"><%= @error.first(200) %></div>
3
3
  <% elsif !@success %>
4
4
  <% if @only_chart %>
5
5
  <p class="text-muted">Select variables</p>
@@ -91,6 +91,8 @@
91
91
  <% end %>
92
92
  <% end %>
93
93
  <%= column_chart first_20.group_by { |r| v = r[1]; (@boom[@columns[1]] || {})[v.to_s] || v }.each_with_index.map { |(name, v), i| {name: name, data: v.sort_by { |r2| labels.index(r2[0]) }.map { |v2| v3 = v2[0]; [(@boom[@columns[0]] || {})[v3.to_s] || v3, v2[2]] }} }, id: chart_id %>
94
+ <% elsif chart_type == "scatter" %>
95
+ <%= scatter_chart @rows, xtitle: @columns[0], ytitle: @columns[1] %>
94
96
  <% elsif @only_chart %>
95
97
  <% if @rows.size == 1 && @rows.first.size == 1 %>
96
98
  <% v = @rows.first.first %>
@@ -28,7 +28,7 @@ module Blazer
28
28
  columns = result.columns
29
29
  cast_method = Rails::VERSION::MAJOR < 5 ? :type_cast : :cast_value
30
30
  result.rows.each do |untyped_row|
31
- rows << (result.column_types.empty? ? untyped_row : columns.each_with_index.map { |c, i| untyped_row[i] ? result.column_types[c].send(cast_method, untyped_row[i]) : nil })
31
+ rows << (result.column_types.empty? ? untyped_row : columns.each_with_index.map { |c, i| untyped_row[i] ? result.column_types[c].send(cast_method, untyped_row[i]) : untyped_row[i] })
32
32
  end
33
33
  end
34
34
  rescue => e
data/lib/blazer/result.rb CHANGED
@@ -73,6 +73,8 @@ module Blazer
73
73
  "bar"
74
74
  elsif column_types == ["string", "string", "numeric"]
75
75
  "bar2"
76
+ elsif column_types == ["numeric", "numeric"]
77
+ "scatter"
76
78
  end
77
79
  end
78
80
  end
@@ -1,3 +1,3 @@
1
1
  module Blazer
2
- VERSION = "1.7.5"
2
+ VERSION = "1.7.6"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blazer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.5
4
+ version: 1.7.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-22 00:00:00.000000000 Z
11
+ date: 2016-12-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails