blazer 0.0.8 → 1.0.0

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.

Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +261 -45
  4. data/app/assets/javascripts/blazer/Sortable.js +1144 -0
  5. data/app/assets/javascripts/blazer/application.js +2 -1
  6. data/app/assets/javascripts/blazer/chartkick.js +935 -0
  7. data/app/assets/javascripts/blazer/selectize.js +391 -201
  8. data/app/assets/stylesheets/blazer/application.css +17 -2
  9. data/app/assets/stylesheets/blazer/selectize.default.css +3 -2
  10. data/app/controllers/blazer/base_controller.rb +48 -0
  11. data/app/controllers/blazer/checks_controller.rb +51 -0
  12. data/app/controllers/blazer/dashboards_controller.rb +94 -0
  13. data/app/controllers/blazer/queries_controller.rb +29 -101
  14. data/app/helpers/blazer/{queries_helper.rb → base_helper.rb} +1 -1
  15. data/app/mailers/blazer/check_mailer.rb +21 -0
  16. data/app/models/blazer/check.rb +28 -0
  17. data/app/models/blazer/connection.rb +0 -1
  18. data/app/models/blazer/dashboard.rb +12 -0
  19. data/app/models/blazer/dashboard_query.rb +9 -0
  20. data/app/models/blazer/query.rb +5 -0
  21. data/app/views/blazer/check_mailer/failing_checks.html.erb +6 -0
  22. data/app/views/blazer/check_mailer/state_change.html.erb +6 -0
  23. data/app/views/blazer/checks/_form.html.erb +28 -0
  24. data/app/views/blazer/checks/edit.html.erb +1 -0
  25. data/app/views/blazer/checks/index.html.erb +41 -0
  26. data/app/views/blazer/checks/new.html.erb +1 -0
  27. data/app/views/blazer/checks/run.html.erb +9 -0
  28. data/app/views/blazer/dashboards/_form.html.erb +86 -0
  29. data/app/views/blazer/dashboards/edit.html.erb +1 -0
  30. data/app/views/blazer/dashboards/index.html.erb +21 -0
  31. data/app/views/blazer/dashboards/new.html.erb +1 -0
  32. data/app/views/blazer/dashboards/show.html.erb +148 -0
  33. data/app/views/blazer/queries/_form.html.erb +16 -5
  34. data/app/views/blazer/queries/_tables.html +5 -0
  35. data/app/views/blazer/queries/index.html.erb +6 -0
  36. data/app/views/blazer/queries/run.html.erb +59 -44
  37. data/app/views/blazer/queries/show.html.erb +20 -16
  38. data/config/routes.rb +5 -0
  39. data/lib/blazer.rb +46 -2
  40. data/lib/blazer/data_source.rb +70 -0
  41. data/lib/blazer/engine.rb +6 -2
  42. data/lib/blazer/tasks.rb +12 -0
  43. data/lib/blazer/version.rb +1 -1
  44. data/lib/generators/blazer/templates/config.yml +26 -6
  45. data/lib/generators/blazer/templates/install.rb +21 -0
  46. metadata +27 -3
@@ -8,10 +8,11 @@
8
8
  //= require ./moment
9
9
  //= require ./moment-timezone
10
10
  //= require ./daterangepicker
11
- //= require chartkick
11
+ //= require ./chartkick
12
12
  //= require ./ace/ace
13
13
  //= require ./ace/ext-language_tools
14
14
  //= require ./ace/theme-twilight
15
15
  //= require ./ace/mode-sql
16
16
  //= require ./ace/snippets/text
17
17
  //= require ./ace/snippets/sql
18
+ //= require ./Sortable
@@ -0,0 +1,935 @@
1
+ /*
2
+ * Chartkick.js
3
+ * Create beautiful Javascript charts with minimal code
4
+ * https://github.com/ankane/chartkick.js
5
+ * v1.4.1
6
+ * MIT License
7
+ */
8
+
9
+ /*jslint browser: true, indent: 2, plusplus: true, vars: true */
10
+
11
+ (function (window) {
12
+ 'use strict';
13
+
14
+ var config = window.Chartkick || {};
15
+ var Chartkick, DATE_PATTERN, ISO8601_PATTERN, DECIMAL_SEPARATOR, adapters = [];
16
+ DATE_PATTERN = /^(\d\d\d\d)(\-)?(\d\d)(\-)?(\d\d)$/i;
17
+
18
+ // helpers
19
+
20
+ function isArray(variable) {
21
+ return Object.prototype.toString.call(variable) === "[object Array]";
22
+ }
23
+
24
+ function isFunction(variable) {
25
+ return variable instanceof Function;
26
+ }
27
+
28
+ function isPlainObject(variable) {
29
+ return !isFunction(variable) && variable instanceof Object;
30
+ }
31
+
32
+ // https://github.com/madrobby/zepto/blob/master/src/zepto.js
33
+ function extend(target, source) {
34
+ var key;
35
+ for (key in source) {
36
+ if (isPlainObject(source[key]) || isArray(source[key])) {
37
+ if (isPlainObject(source[key]) && !isPlainObject(target[key])) {
38
+ target[key] = {};
39
+ }
40
+ if (isArray(source[key]) && !isArray(target[key])) {
41
+ target[key] = [];
42
+ }
43
+ extend(target[key], source[key]);
44
+ } else if (source[key] !== undefined) {
45
+ target[key] = source[key];
46
+ }
47
+ }
48
+ }
49
+
50
+ function merge(obj1, obj2) {
51
+ var target = {};
52
+ extend(target, obj1);
53
+ extend(target, obj2);
54
+ return target;
55
+ }
56
+
57
+ // https://github.com/Do/iso8601.js
58
+ ISO8601_PATTERN = /(\d\d\d\d)(\-)?(\d\d)(\-)?(\d\d)(T)?(\d\d)(:)?(\d\d)?(:)?(\d\d)?([\.,]\d+)?($|Z|([\+\-])(\d\d)(:)?(\d\d)?)/i;
59
+ DECIMAL_SEPARATOR = String(1.5).charAt(1);
60
+
61
+ function parseISO8601(input) {
62
+ var day, hour, matches, milliseconds, minutes, month, offset, result, seconds, type, year;
63
+ type = Object.prototype.toString.call(input);
64
+ if (type === '[object Date]') {
65
+ return input;
66
+ }
67
+ if (type !== '[object String]') {
68
+ return;
69
+ }
70
+ if (matches = input.match(ISO8601_PATTERN)) {
71
+ year = parseInt(matches[1], 10);
72
+ month = parseInt(matches[3], 10) - 1;
73
+ day = parseInt(matches[5], 10);
74
+ hour = parseInt(matches[7], 10);
75
+ minutes = matches[9] ? parseInt(matches[9], 10) : 0;
76
+ seconds = matches[11] ? parseInt(matches[11], 10) : 0;
77
+ milliseconds = matches[12] ? parseFloat(DECIMAL_SEPARATOR + matches[12].slice(1)) * 1000 : 0;
78
+ result = Date.UTC(year, month, day, hour, minutes, seconds, milliseconds);
79
+ if (matches[13] && matches[14]) {
80
+ offset = matches[15] * 60;
81
+ if (matches[17]) {
82
+ offset += parseInt(matches[17], 10);
83
+ }
84
+ offset *= matches[14] === '-' ? -1 : 1;
85
+ result -= offset * 60 * 1000;
86
+ }
87
+ return new Date(result);
88
+ }
89
+ }
90
+ // end iso8601.js
91
+
92
+ function negativeValues(series) {
93
+ var i, j, data;
94
+ for (i = 0; i < series.length; i++) {
95
+ data = series[i].data;
96
+ for (j = 0; j < data.length; j++) {
97
+ if (data[j][1] < 0) {
98
+ return true;
99
+ }
100
+ }
101
+ }
102
+ return false;
103
+ }
104
+
105
+ function jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle) {
106
+ return function (series, opts, chartOptions) {
107
+ var options = merge({}, defaultOptions);
108
+ options = merge(options, chartOptions || {});
109
+
110
+ // hide legend
111
+ // this is *not* an external option!
112
+ if (opts.hideLegend) {
113
+ hideLegend(options);
114
+ }
115
+
116
+ // min
117
+ if ("min" in opts) {
118
+ setMin(options, opts.min);
119
+ } else if (!negativeValues(series)) {
120
+ setMin(options, 0);
121
+ }
122
+
123
+ // max
124
+ if (opts.max) {
125
+ setMax(options, opts.max);
126
+ }
127
+
128
+ if (opts.stacked) {
129
+ setStacked(options);
130
+ }
131
+
132
+ if (opts.colors) {
133
+ options.colors = opts.colors;
134
+ }
135
+
136
+ if (opts.xtitle) {
137
+ setXtitle(options, opts.xtitle);
138
+ }
139
+
140
+ if (opts.ytitle) {
141
+ setYtitle(options, opts.ytitle);
142
+ }
143
+
144
+ // merge library last
145
+ options = merge(options, opts.library || {});
146
+
147
+ return options;
148
+ };
149
+ }
150
+
151
+ function setText(element, text) {
152
+ if (document.body.innerText) {
153
+ element.innerText = text;
154
+ } else {
155
+ element.textContent = text;
156
+ }
157
+ }
158
+
159
+ function chartError(element, message) {
160
+ setText(element, "Error Loading Chart: " + message);
161
+ element.style.color = "#ff0000";
162
+ }
163
+
164
+ function getJSON(element, url, success) {
165
+ var $ = window.jQuery || window.Zepto || window.$;
166
+ $.ajax({
167
+ dataType: "json",
168
+ url: url,
169
+ success: success,
170
+ error: function (jqXHR, textStatus, errorThrown) {
171
+ var message = (typeof errorThrown === "string") ? errorThrown : errorThrown.message;
172
+ chartError(element, message);
173
+ }
174
+ });
175
+ }
176
+
177
+ function errorCatcher(chart, callback) {
178
+ try {
179
+ callback(chart);
180
+ } catch (err) {
181
+ chartError(chart.element, err.message);
182
+ throw err;
183
+ }
184
+ }
185
+
186
+ function fetchDataSource(chart, callback) {
187
+ if (typeof chart.dataSource === "string") {
188
+ getJSON(chart.element, chart.dataSource, function (data, textStatus, jqXHR) {
189
+ chart.data = data;
190
+ errorCatcher(chart, callback);
191
+ });
192
+ } else {
193
+ chart.data = chart.dataSource;
194
+ errorCatcher(chart, callback);
195
+ }
196
+ }
197
+
198
+ // type conversions
199
+
200
+ function toStr(n) {
201
+ return "" + n;
202
+ }
203
+
204
+ function toFloat(n) {
205
+ return parseFloat(n);
206
+ }
207
+
208
+ function toDate(n) {
209
+ var matches, year, month, day;
210
+ if (typeof n !== "object") {
211
+ if (typeof n === "number") {
212
+ n = new Date(n * 1000); // ms
213
+ } else if (matches = n.match(DATE_PATTERN)) {
214
+ year = parseInt(matches[1], 10);
215
+ month = parseInt(matches[3], 10) - 1;
216
+ day = parseInt(matches[5], 10);
217
+ return new Date(year, month, day);
218
+ } else { // str
219
+ // try our best to get the str into iso8601
220
+ // TODO be smarter about this
221
+ var str = n.replace(/ /, "T").replace(" ", "").replace("UTC", "Z");
222
+ n = parseISO8601(str) || new Date(n);
223
+ }
224
+ }
225
+ return n;
226
+ }
227
+
228
+ function toArr(n) {
229
+ if (!isArray(n)) {
230
+ var arr = [], i;
231
+ for (i in n) {
232
+ if (n.hasOwnProperty(i)) {
233
+ arr.push([i, n[i]]);
234
+ }
235
+ }
236
+ n = arr;
237
+ }
238
+ return n;
239
+ }
240
+
241
+ function sortByTime(a, b) {
242
+ return a[0].getTime() - b[0].getTime();
243
+ }
244
+
245
+ if ("Highcharts" in window) {
246
+ var HighchartsAdapter = new function () {
247
+ var Highcharts = window.Highcharts;
248
+
249
+ this.name = "highcharts";
250
+
251
+ var defaultOptions = {
252
+ chart: {},
253
+ xAxis: {
254
+ title: {
255
+ text: null
256
+ },
257
+ labels: {
258
+ style: {
259
+ fontSize: "12px"
260
+ }
261
+ }
262
+ },
263
+ yAxis: {
264
+ title: {
265
+ text: null
266
+ },
267
+ labels: {
268
+ style: {
269
+ fontSize: "12px"
270
+ }
271
+ }
272
+ },
273
+ title: {
274
+ text: null
275
+ },
276
+ credits: {
277
+ enabled: false
278
+ },
279
+ legend: {
280
+ borderWidth: 0
281
+ },
282
+ tooltip: {
283
+ style: {
284
+ fontSize: "12px"
285
+ }
286
+ },
287
+ plotOptions: {
288
+ areaspline: {},
289
+ series: {
290
+ marker: {}
291
+ }
292
+ }
293
+ };
294
+
295
+ var hideLegend = function (options) {
296
+ options.legend.enabled = false;
297
+ };
298
+
299
+ var setMin = function (options, min) {
300
+ options.yAxis.min = min;
301
+ };
302
+
303
+ var setMax = function (options, max) {
304
+ options.yAxis.max = max;
305
+ };
306
+
307
+ var setStacked = function (options) {
308
+ options.plotOptions.series.stacking = "normal";
309
+ };
310
+
311
+ var setXtitle = function (options, title) {
312
+ options.xAxis.title.text = title;
313
+ };
314
+
315
+ var setYtitle = function (options, title) {
316
+ options.yAxis.title.text = title;
317
+ };
318
+
319
+ var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle);
320
+
321
+ this.renderLineChart = function (chart, chartType) {
322
+ chartType = chartType || "spline";
323
+ var chartOptions = {};
324
+ if (chartType === "areaspline") {
325
+ chartOptions = {
326
+ plotOptions: {
327
+ areaspline: {
328
+ stacking: "normal"
329
+ },
330
+ series: {
331
+ marker: {
332
+ enabled: false
333
+ }
334
+ }
335
+ }
336
+ };
337
+ }
338
+ var options = jsOptions(chart.data, chart.options, chartOptions), data, i, j;
339
+ options.xAxis.type = chart.options.discrete ? "category" : "datetime";
340
+ options.chart.type = chartType;
341
+ options.chart.renderTo = chart.element.id;
342
+
343
+ var series = chart.data;
344
+ for (i = 0; i < series.length; i++) {
345
+ data = series[i].data;
346
+ if (!chart.options.discrete) {
347
+ for (j = 0; j < data.length; j++) {
348
+ data[j][0] = data[j][0].getTime();
349
+ }
350
+ }
351
+ series[i].marker = {symbol: "circle"};
352
+ }
353
+ options.series = series;
354
+ new Highcharts.Chart(options);
355
+ };
356
+
357
+ this.renderScatterChart = function (chart) {
358
+ var chartOptions = {};
359
+ var options = jsOptions(chart.data, chart.options, chartOptions);
360
+ options.chart.type = 'scatter';
361
+ options.chart.renderTo = chart.element.id;
362
+ options.series = chart.data;
363
+ new Highcharts.Chart(options);
364
+ };
365
+
366
+ this.renderPieChart = function (chart) {
367
+ var chartOptions = {};
368
+ if (chart.options.colors) {
369
+ chartOptions.colors = chart.options.colors;
370
+ }
371
+ var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
372
+ options.chart.renderTo = chart.element.id;
373
+ options.series = [{
374
+ type: "pie",
375
+ name: "Value",
376
+ data: chart.data
377
+ }];
378
+ new Highcharts.Chart(options);
379
+ };
380
+
381
+ this.renderColumnChart = function (chart, chartType) {
382
+ var chartType = chartType || "column";
383
+ var series = chart.data;
384
+ var options = jsOptions(series, chart.options), i, j, s, d, rows = [];
385
+ options.chart.type = chartType;
386
+ options.chart.renderTo = chart.element.id;
387
+
388
+ for (i = 0; i < series.length; i++) {
389
+ s = series[i];
390
+
391
+ for (j = 0; j < s.data.length; j++) {
392
+ d = s.data[j];
393
+ if (!rows[d[0]]) {
394
+ rows[d[0]] = new Array(series.length);
395
+ }
396
+ rows[d[0]][i] = d[1];
397
+ }
398
+ }
399
+
400
+ var categories = [];
401
+ for (i in rows) {
402
+ if (rows.hasOwnProperty(i)) {
403
+ categories.push(i);
404
+ }
405
+ }
406
+ options.xAxis.categories = categories;
407
+
408
+ var newSeries = [];
409
+ for (i = 0; i < series.length; i++) {
410
+ d = [];
411
+ for (j = 0; j < categories.length; j++) {
412
+ d.push(rows[categories[j]][i] || 0);
413
+ }
414
+
415
+ newSeries.push({
416
+ name: series[i].name,
417
+ data: d
418
+ });
419
+ }
420
+ options.series = newSeries;
421
+
422
+ new Highcharts.Chart(options);
423
+ };
424
+
425
+ var self = this;
426
+
427
+ this.renderBarChart = function (chart) {
428
+ self.renderColumnChart(chart, "bar");
429
+ };
430
+
431
+ this.renderAreaChart = function (chart) {
432
+ self.renderLineChart(chart, "areaspline");
433
+ };
434
+ };
435
+ adapters.push(HighchartsAdapter);
436
+ }
437
+ if (window.google && window.google.setOnLoadCallback) {
438
+ var GoogleChartsAdapter = new function () {
439
+ var google = window.google;
440
+
441
+ this.name = "google";
442
+
443
+ var loaded = {};
444
+ var callbacks = [];
445
+
446
+ var runCallbacks = function () {
447
+ var cb, call;
448
+ for (var i = 0; i < callbacks.length; i++) {
449
+ cb = callbacks[i];
450
+ call = google.visualization && ((cb.pack === "corechart" && google.visualization.LineChart) || (cb.pack === "timeline" && google.visualization.Timeline))
451
+ if (call) {
452
+ cb.callback();
453
+ callbacks.splice(i, 1);
454
+ i--;
455
+ }
456
+ }
457
+ };
458
+
459
+ var waitForLoaded = function (pack, callback) {
460
+ if (!callback) {
461
+ callback = pack;
462
+ pack = "corechart";
463
+ }
464
+
465
+ callbacks.push({pack: pack, callback: callback});
466
+
467
+ if (loaded[pack]) {
468
+ runCallbacks();
469
+ } else {
470
+ loaded[pack] = true;
471
+
472
+ // https://groups.google.com/forum/#!topic/google-visualization-api/fMKJcyA2yyI
473
+ var loadOptions = {
474
+ packages: [pack],
475
+ callback: runCallbacks
476
+ };
477
+ if (config.language) {
478
+ loadOptions.language = config.language;
479
+ }
480
+ google.load("visualization", "1", loadOptions);
481
+ }
482
+ };
483
+
484
+ // Set chart options
485
+ var defaultOptions = {
486
+ chartArea: {},
487
+ fontName: "'Lucida Grande', 'Lucida Sans Unicode', Verdana, Arial, Helvetica, sans-serif",
488
+ pointSize: 6,
489
+ legend: {
490
+ textStyle: {
491
+ fontSize: 12,
492
+ color: "#444"
493
+ },
494
+ alignment: "center",
495
+ position: "right"
496
+ },
497
+ curveType: "function",
498
+ hAxis: {
499
+ textStyle: {
500
+ color: "#666",
501
+ fontSize: 12
502
+ },
503
+ titleTextStyle: {},
504
+ gridlines: {
505
+ color: "transparent"
506
+ },
507
+ baselineColor: "#ccc",
508
+ viewWindow: {}
509
+ },
510
+ vAxis: {
511
+ textStyle: {
512
+ color: "#666",
513
+ fontSize: 12
514
+ },
515
+ titleTextStyle: {},
516
+ baselineColor: "#ccc",
517
+ viewWindow: {}
518
+ },
519
+ tooltip: {
520
+ textStyle: {
521
+ color: "#666",
522
+ fontSize: 12
523
+ }
524
+ }
525
+ };
526
+
527
+ var hideLegend = function (options) {
528
+ options.legend.position = "none";
529
+ };
530
+
531
+ var setMin = function (options, min) {
532
+ options.vAxis.viewWindow.min = min;
533
+ };
534
+
535
+ var setMax = function (options, max) {
536
+ options.vAxis.viewWindow.max = max;
537
+ };
538
+
539
+ var setBarMin = function (options, min) {
540
+ options.hAxis.viewWindow.min = min;
541
+ };
542
+
543
+ var setBarMax = function (options, max) {
544
+ options.hAxis.viewWindow.max = max;
545
+ };
546
+
547
+ var setStacked = function (options) {
548
+ options.isStacked = true;
549
+ };
550
+
551
+ var setXtitle = function (options, title) {
552
+ options.hAxis.title = title;
553
+ options.hAxis.titleTextStyle.italic = false;
554
+ }
555
+
556
+ var setYtitle = function (options, title) {
557
+ options.vAxis.title = title;
558
+ options.vAxis.titleTextStyle.italic = false;
559
+ };
560
+
561
+ var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle);
562
+
563
+ // cant use object as key
564
+ var createDataTable = function (series, columnType) {
565
+ var data = new google.visualization.DataTable();
566
+ data.addColumn(columnType, "");
567
+
568
+ var i, j, s, d, key, rows = [];
569
+ for (i = 0; i < series.length; i++) {
570
+ s = series[i];
571
+ data.addColumn("number", s.name);
572
+
573
+ for (j = 0; j < s.data.length; j++) {
574
+ d = s.data[j];
575
+ key = (columnType === "datetime") ? d[0].getTime() : d[0];
576
+ if (!rows[key]) {
577
+ rows[key] = new Array(series.length);
578
+ }
579
+ rows[key][i] = toFloat(d[1]);
580
+ }
581
+ }
582
+
583
+ var rows2 = [];
584
+ var day = true;
585
+ var value;
586
+ for (i in rows) {
587
+ if (rows.hasOwnProperty(i)) {
588
+ if (columnType === "datetime") {
589
+ value = new Date(toFloat(i));
590
+ day = day && isDay(value);
591
+ } else if (columnType === "number") {
592
+ value = toFloat(i);
593
+ } else {
594
+ value = i;
595
+ }
596
+ rows2.push([value].concat(rows[i]));
597
+ }
598
+ }
599
+ if (columnType === "datetime") {
600
+ rows2.sort(sortByTime);
601
+ }
602
+ data.addRows(rows2);
603
+
604
+ if (columnType === "datetime" && day) {
605
+ var formatter = new google.visualization.DateFormat({
606
+ pattern: "MMM d, yyyy"
607
+ });
608
+ formatter.format(data, 0);
609
+ }
610
+
611
+ return data;
612
+ };
613
+
614
+ var resize = function (callback) {
615
+ if (window.attachEvent) {
616
+ window.attachEvent("onresize", callback);
617
+ } else if (window.addEventListener) {
618
+ window.addEventListener("resize", callback, true);
619
+ }
620
+ callback();
621
+ };
622
+
623
+ this.renderLineChart = function (chart) {
624
+ waitForLoaded(function () {
625
+ var options = jsOptions(chart.data, chart.options);
626
+ var data = createDataTable(chart.data, chart.options.discrete ? "string" : "datetime");
627
+ chart.chart = new google.visualization.LineChart(chart.element);
628
+ resize(function () {
629
+ chart.chart.draw(data, options);
630
+ });
631
+ });
632
+ };
633
+
634
+ this.renderPieChart = function (chart) {
635
+ waitForLoaded(function () {
636
+ var chartOptions = {
637
+ chartArea: {
638
+ top: "10%",
639
+ height: "80%"
640
+ }
641
+ };
642
+ if (chart.options.colors) {
643
+ chartOptions.colors = chart.options.colors;
644
+ }
645
+ var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
646
+
647
+ var data = new google.visualization.DataTable();
648
+ data.addColumn("string", "");
649
+ data.addColumn("number", "Value");
650
+ data.addRows(chart.data);
651
+
652
+ chart.chart = new google.visualization.PieChart(chart.element);
653
+ resize(function () {
654
+ chart.chart.draw(data, options);
655
+ });
656
+ });
657
+ };
658
+
659
+ this.renderColumnChart = function (chart) {
660
+ waitForLoaded(function () {
661
+ var options = jsOptions(chart.data, chart.options);
662
+ var data = createDataTable(chart.data, "string");
663
+ chart.chart = new google.visualization.ColumnChart(chart.element);
664
+ resize(function () {
665
+ chart.chart.draw(data, options);
666
+ });
667
+ });
668
+ };
669
+
670
+ this.renderBarChart = function (chart) {
671
+ waitForLoaded(function () {
672
+ var chartOptions = {
673
+ hAxis: {
674
+ gridlines: {
675
+ color: "#ccc"
676
+ }
677
+ }
678
+ };
679
+ var options = jsOptionsFunc(defaultOptions, hideLegend, setBarMin, setBarMax, setStacked)(chart.data, chart.options, chartOptions);
680
+ var data = createDataTable(chart.data, "string");
681
+ chart.chart = new google.visualization.BarChart(chart.element);
682
+ resize(function () {
683
+ chart.chart.draw(data, options);
684
+ });
685
+ });
686
+ };
687
+
688
+ this.renderAreaChart = function (chart) {
689
+ waitForLoaded(function () {
690
+ var chartOptions = {
691
+ isStacked: true,
692
+ pointSize: 0,
693
+ areaOpacity: 0.5
694
+ };
695
+ var options = jsOptions(chart.data, chart.options, chartOptions);
696
+ var data = createDataTable(chart.data, chart.options.discrete ? "string" : "datetime");
697
+ chart.chart = new google.visualization.AreaChart(chart.element);
698
+ resize(function () {
699
+ chart.chart.draw(data, options);
700
+ });
701
+ });
702
+ };
703
+
704
+ this.renderGeoChart = function (chart) {
705
+ waitForLoaded(function () {
706
+ var chartOptions = {
707
+ legend: "none",
708
+ colorAxis: {
709
+ colors: chart.options.colors || ["#f6c7b6", "#ce502d"]
710
+ }
711
+ };
712
+ var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
713
+
714
+ var data = new google.visualization.DataTable();
715
+ data.addColumn("string", "");
716
+ data.addColumn("number", "Value");
717
+ data.addRows(chart.data);
718
+
719
+ chart.chart = new google.visualization.GeoChart(chart.element);
720
+ resize(function () {
721
+ chart.chart.draw(data, options);
722
+ });
723
+ });
724
+ };
725
+
726
+ this.renderScatterChart = function (chart) {
727
+ waitForLoaded(function () {
728
+ var chartOptions = {};
729
+ var options = jsOptions(chart.data, chart.options, chartOptions);
730
+ var data = createDataTable(chart.data, "number");
731
+
732
+ chart.chart = new google.visualization.ScatterChart(chart.element);
733
+ resize(function () {
734
+ chart.chart.draw(data, options);
735
+ });
736
+ });
737
+ };
738
+
739
+ this.renderTimeline = function (chart) {
740
+ waitForLoaded("timeline", function () {
741
+ var chartOptions = {
742
+ legend: "none"
743
+ };
744
+
745
+ if (chart.options.colors) {
746
+ chartOptions.colors = chart.options.colors;
747
+ }
748
+ var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
749
+
750
+ var data = new google.visualization.DataTable();
751
+ data.addColumn({type: "string", id: "Name"});
752
+ data.addColumn({type: "date", id: "Start"});
753
+ data.addColumn({type: "date", id: "End"});
754
+ data.addRows(chart.data);
755
+
756
+ chart.chart = new google.visualization.Timeline(chart.element);
757
+
758
+ resize(function () {
759
+ chart.chart.draw(data, options);
760
+ });
761
+ });
762
+ };
763
+ };
764
+
765
+ adapters.push(GoogleChartsAdapter);
766
+ }
767
+
768
+ // TODO remove chartType if cross-browser way
769
+ // to get the name of the chart class
770
+ function renderChart(chartType, chart) {
771
+ var i, adapter, fnName, adapterName;
772
+ fnName = "render" + chartType;
773
+ adapterName = chart.options.adapter;
774
+
775
+ for (i = 0; i < adapters.length; i++) {
776
+ adapter = adapters[i];
777
+ if ((!adapterName || adapterName === adapter.name) && isFunction(adapter[fnName])) {
778
+ return adapter[fnName](chart);
779
+ }
780
+ }
781
+ throw new Error("No adapter found");
782
+ }
783
+
784
+ // process data
785
+
786
+ var toFormattedKey = function (key, keyType) {
787
+ if (keyType === "number") {
788
+ key = toFloat(key);
789
+ } else if (keyType === "datetime") {
790
+ key = toDate(key);
791
+ } else {
792
+ key = toStr(key);
793
+ }
794
+ return key;
795
+ };
796
+
797
+ var formatSeriesData = function (data, keyType) {
798
+ var r = [], key, j;
799
+ for (j = 0; j < data.length; j++) {
800
+ key = toFormattedKey(data[j][0], keyType);
801
+ r.push([key, toFloat(data[j][1])]);
802
+ }
803
+ if (keyType === "datetime") {
804
+ r.sort(sortByTime);
805
+ }
806
+ return r;
807
+ };
808
+
809
+ function isDay(d) {
810
+ return d.getMilliseconds() + d.getSeconds() + d.getMinutes() + d.getHours() === 0;
811
+ }
812
+
813
+ function processSeries(series, opts, keyType) {
814
+ var i;
815
+
816
+ // see if one series or multiple
817
+ if (!isArray(series) || typeof series[0] !== "object" || isArray(series[0])) {
818
+ series = [{name: "Value", data: series}];
819
+ opts.hideLegend = true;
820
+ } else {
821
+ opts.hideLegend = false;
822
+ }
823
+ if (opts.discrete) {
824
+ keyType = "string";
825
+ }
826
+
827
+ // right format
828
+ for (i = 0; i < series.length; i++) {
829
+ series[i].data = formatSeriesData(toArr(series[i].data), keyType);
830
+ }
831
+
832
+ return series;
833
+ }
834
+
835
+ function processSimple(data) {
836
+ var perfectData = toArr(data), i;
837
+ for (i = 0; i < perfectData.length; i++) {
838
+ perfectData[i] = [toStr(perfectData[i][0]), toFloat(perfectData[i][1])];
839
+ }
840
+ return perfectData;
841
+ }
842
+
843
+ function processTime(data)
844
+ {
845
+ var i;
846
+ for (i = 0; i < data.length; i++) {
847
+ data[i][1] = toDate(data[i][1]);
848
+ data[i][2] = toDate(data[i][2]);
849
+ }
850
+ return data;
851
+ }
852
+
853
+ function processLineData(chart) {
854
+ chart.data = processSeries(chart.data, chart.options, "datetime");
855
+ renderChart("LineChart", chart);
856
+ }
857
+
858
+ function processColumnData(chart) {
859
+ chart.data = processSeries(chart.data, chart.options, "string");
860
+ renderChart("ColumnChart", chart);
861
+ }
862
+
863
+ function processPieData(chart) {
864
+ chart.data = processSimple(chart.data);
865
+ renderChart("PieChart", chart);
866
+ }
867
+
868
+ function processBarData(chart) {
869
+ chart.data = processSeries(chart.data, chart.options, "string");
870
+ renderChart("BarChart", chart);
871
+ }
872
+
873
+ function processAreaData(chart) {
874
+ chart.data = processSeries(chart.data, chart.options, "datetime");
875
+ renderChart("AreaChart", chart);
876
+ }
877
+
878
+ function processGeoData(chart) {
879
+ chart.data = processSimple(chart.data);
880
+ renderChart("GeoChart", chart);
881
+ }
882
+
883
+ function processScatterData(chart) {
884
+ chart.data = processSeries(chart.data, chart.options, "number");
885
+ renderChart("ScatterChart", chart);
886
+ }
887
+
888
+ function processTimelineData(chart) {
889
+ chart.data = processTime(chart.data);
890
+ renderChart("Timeline", chart);
891
+ }
892
+
893
+ function setElement(chart, element, dataSource, opts, callback) {
894
+ if (typeof element === "string") {
895
+ element = document.getElementById(element);
896
+ }
897
+ chart.element = element;
898
+ chart.options = opts || {};
899
+ chart.dataSource = dataSource;
900
+ Chartkick.charts[element.id] = chart;
901
+ fetchDataSource(chart, callback);
902
+ }
903
+
904
+ // define classes
905
+
906
+ Chartkick = {
907
+ LineChart: function (element, dataSource, opts) {
908
+ setElement(this, element, dataSource, opts, processLineData);
909
+ },
910
+ PieChart: function (element, dataSource, opts) {
911
+ setElement(this, element, dataSource, opts, processPieData);
912
+ },
913
+ ColumnChart: function (element, dataSource, opts) {
914
+ setElement(this, element, dataSource, opts, processColumnData);
915
+ },
916
+ BarChart: function (element, dataSource, opts) {
917
+ setElement(this, element, dataSource, opts, processBarData);
918
+ },
919
+ AreaChart: function (element, dataSource, opts) {
920
+ setElement(this, element, dataSource, opts, processAreaData);
921
+ },
922
+ GeoChart: function (element, dataSource, opts) {
923
+ setElement(this, element, dataSource, opts, processGeoData);
924
+ },
925
+ ScatterChart: function (element, dataSource, opts) {
926
+ setElement(this, element, dataSource, opts, processScatterData);
927
+ },
928
+ Timeline: function (element, dataSource, opts) {
929
+ setElement(this, element, dataSource, opts, processTimelineData);
930
+ },
931
+ charts: {}
932
+ };
933
+
934
+ window.Chartkick = Chartkick;
935
+ }(window));