kube_auto_analyzer 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1817 @@
1
+ /*
2
+ * Chartkick.js
3
+ * Create beautiful charts with one line of JavaScript
4
+ * https://github.com/ankane/chartkick.js
5
+ * v2.2.4
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, ISO8601_PATTERN, DECIMAL_SEPARATOR, adapters = [];
16
+ var DATE_PATTERN = /^(\d\d\d\d)(\-)?(\d\d)(\-)?(\d\d)$/i;
17
+ var GoogleChartsAdapter, HighchartsAdapter, ChartjsAdapter;
18
+ var pendingRequests = [], runningRequests = 0, maxRequests = 4;
19
+
20
+ // helpers
21
+
22
+ function isArray(variable) {
23
+ return Object.prototype.toString.call(variable) === "[object Array]";
24
+ }
25
+
26
+ function isFunction(variable) {
27
+ return variable instanceof Function;
28
+ }
29
+
30
+ function isPlainObject(variable) {
31
+ return !isFunction(variable) && variable instanceof Object;
32
+ }
33
+
34
+ // https://github.com/madrobby/zepto/blob/master/src/zepto.js
35
+ function extend(target, source) {
36
+ var key;
37
+ for (key in source) {
38
+ if (isPlainObject(source[key]) || isArray(source[key])) {
39
+ if (isPlainObject(source[key]) && !isPlainObject(target[key])) {
40
+ target[key] = {};
41
+ }
42
+ if (isArray(source[key]) && !isArray(target[key])) {
43
+ target[key] = [];
44
+ }
45
+ extend(target[key], source[key]);
46
+ } else if (source[key] !== undefined) {
47
+ target[key] = source[key];
48
+ }
49
+ }
50
+ }
51
+
52
+ function merge(obj1, obj2) {
53
+ var target = {};
54
+ extend(target, obj1);
55
+ extend(target, obj2);
56
+ return target;
57
+ }
58
+
59
+ // https://github.com/Do/iso8601.js
60
+ ISO8601_PATTERN = /(\d\d\d\d)(\-)?(\d\d)(\-)?(\d\d)(T)?(\d\d)(:)?(\d\d)?(:)?(\d\d)?([\.,]\d+)?($|Z|([\+\-])(\d\d)(:)?(\d\d)?)/i;
61
+ DECIMAL_SEPARATOR = String(1.5).charAt(1);
62
+
63
+ function parseISO8601(input) {
64
+ var day, hour, matches, milliseconds, minutes, month, offset, result, seconds, type, year;
65
+ type = Object.prototype.toString.call(input);
66
+ if (type === "[object Date]") {
67
+ return input;
68
+ }
69
+ if (type !== "[object String]") {
70
+ return;
71
+ }
72
+ matches = input.match(ISO8601_PATTERN);
73
+ if (matches) {
74
+ year = parseInt(matches[1], 10);
75
+ month = parseInt(matches[3], 10) - 1;
76
+ day = parseInt(matches[5], 10);
77
+ hour = parseInt(matches[7], 10);
78
+ minutes = matches[9] ? parseInt(matches[9], 10) : 0;
79
+ seconds = matches[11] ? parseInt(matches[11], 10) : 0;
80
+ milliseconds = matches[12] ? parseFloat(DECIMAL_SEPARATOR + matches[12].slice(1)) * 1000 : 0;
81
+ result = Date.UTC(year, month, day, hour, minutes, seconds, milliseconds);
82
+ if (matches[13] && matches[14]) {
83
+ offset = matches[15] * 60;
84
+ if (matches[17]) {
85
+ offset += parseInt(matches[17], 10);
86
+ }
87
+ offset *= matches[14] === "-" ? -1 : 1;
88
+ result -= offset * 60 * 1000;
89
+ }
90
+ return new Date(result);
91
+ }
92
+ }
93
+ // end iso8601.js
94
+
95
+ function negativeValues(series) {
96
+ var i, j, data;
97
+ for (i = 0; i < series.length; i++) {
98
+ data = series[i].data;
99
+ for (j = 0; j < data.length; j++) {
100
+ if (data[j][1] < 0) {
101
+ return true;
102
+ }
103
+ }
104
+ }
105
+ return false;
106
+ }
107
+
108
+ function jsOptionsFunc(defaultOptions, hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle) {
109
+ return function (chart, opts, chartOptions) {
110
+ var series = chart.data;
111
+ var options = merge({}, defaultOptions);
112
+ options = merge(options, chartOptions || {});
113
+
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);
120
+ }
121
+
122
+ // min
123
+ if ("min" in opts) {
124
+ setMin(options, opts.min);
125
+ } else if (!negativeValues(series)) {
126
+ setMin(options, 0);
127
+ }
128
+
129
+ // max
130
+ if (opts.max) {
131
+ setMax(options, opts.max);
132
+ }
133
+
134
+ if ("stacked" in opts) {
135
+ setStacked(options, opts.stacked);
136
+ }
137
+
138
+ if (opts.colors) {
139
+ options.colors = opts.colors;
140
+ }
141
+
142
+ if (opts.xtitle) {
143
+ setXtitle(options, opts.xtitle);
144
+ }
145
+
146
+ if (opts.ytitle) {
147
+ setYtitle(options, opts.ytitle);
148
+ }
149
+
150
+ // merge library last
151
+ options = merge(options, opts.library || {});
152
+
153
+ return options;
154
+ };
155
+ }
156
+
157
+ function setText(element, text) {
158
+ if (document.body.innerText) {
159
+ element.innerText = text;
160
+ } else {
161
+ element.textContent = text;
162
+ }
163
+ }
164
+
165
+ function chartError(element, message) {
166
+ setText(element, "Error Loading Chart: " + message);
167
+ element.style.color = "#ff0000";
168
+ }
169
+
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();
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);
195
+ });
196
+ }
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
+
225
+ function errorCatcher(chart, callback) {
226
+ try {
227
+ callback(chart);
228
+ } catch (err) {
229
+ chartError(chart.element, err.message);
230
+ throw err;
231
+ }
232
+ }
233
+
234
+ function fetchDataSource(chart, callback, dataSource) {
235
+ if (typeof dataSource === "string") {
236
+ pushRequest(chart.element, dataSource, function (data, textStatus, jqXHR) {
237
+ chart.rawData = data;
238
+ errorCatcher(chart, callback);
239
+ });
240
+ } else {
241
+ chart.rawData = dataSource;
242
+ errorCatcher(chart, callback);
243
+ }
244
+ }
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
+
307
+ // type conversions
308
+
309
+ function toStr(n) {
310
+ return "" + n;
311
+ }
312
+
313
+ function toFloat(n) {
314
+ return parseFloat(n);
315
+ }
316
+
317
+ function toDate(n) {
318
+ var matches, year, month, day;
319
+ if (typeof n !== "object") {
320
+ if (typeof n === "number") {
321
+ n = new Date(n * 1000); // ms
322
+ } else if ((matches = n.match(DATE_PATTERN))) {
323
+ year = parseInt(matches[1], 10);
324
+ month = parseInt(matches[3], 10) - 1;
325
+ day = parseInt(matches[5], 10);
326
+ return new Date(year, month, day);
327
+ } else { // str
328
+ // try our best to get the str into iso8601
329
+ // TODO be smarter about this
330
+ var str = n.replace(/ /, "T").replace(" ", "").replace("UTC", "Z");
331
+ n = parseISO8601(str) || new Date(n);
332
+ }
333
+ }
334
+ return n;
335
+ }
336
+
337
+ function toArr(n) {
338
+ if (!isArray(n)) {
339
+ var arr = [], i;
340
+ for (i in n) {
341
+ if (n.hasOwnProperty(i)) {
342
+ arr.push([i, n[i]]);
343
+ }
344
+ }
345
+ n = arr;
346
+ }
347
+ return n;
348
+ }
349
+
350
+ function sortByTime(a, b) {
351
+ return a[0].getTime() - b[0].getTime();
352
+ }
353
+
354
+ function sortByNumberSeries(a, b) {
355
+ return a[0] - b[0];
356
+ }
357
+
358
+ function sortByNumber(a, b) {
359
+ return a - b;
360
+ }
361
+
362
+ function loadAdapters() {
363
+ if (!HighchartsAdapter && "Highcharts" in window) {
364
+ HighchartsAdapter = new function () {
365
+ var Highcharts = window.Highcharts;
366
+
367
+ this.name = "highcharts";
368
+
369
+ var defaultOptions = {
370
+ chart: {},
371
+ xAxis: {
372
+ title: {
373
+ text: null
374
+ },
375
+ labels: {
376
+ style: {
377
+ fontSize: "12px"
378
+ }
379
+ }
380
+ },
381
+ yAxis: {
382
+ title: {
383
+ text: null
384
+ },
385
+ labels: {
386
+ style: {
387
+ fontSize: "12px"
388
+ }
389
+ }
390
+ },
391
+ title: {
392
+ text: null
393
+ },
394
+ credits: {
395
+ enabled: false
396
+ },
397
+ legend: {
398
+ borderWidth: 0
399
+ },
400
+ tooltip: {
401
+ style: {
402
+ fontSize: "12px"
403
+ }
404
+ },
405
+ plotOptions: {
406
+ areaspline: {},
407
+ series: {
408
+ marker: {}
409
+ }
410
+ }
411
+ };
412
+
413
+ var hideLegend = function (options, legend, hideLegend) {
414
+ if (legend !== undefined) {
415
+ options.legend.enabled = !!legend;
416
+ if (legend && legend !== true) {
417
+ if (legend === "top" || legend === "bottom") {
418
+ options.legend.verticalAlign = legend;
419
+ } else {
420
+ options.legend.layout = "vertical";
421
+ options.legend.verticalAlign = "middle";
422
+ options.legend.align = legend;
423
+ }
424
+ }
425
+ } else if (hideLegend) {
426
+ options.legend.enabled = false;
427
+ }
428
+ };
429
+
430
+ var setTitle = function (options, title) {
431
+ options.title.text = title;
432
+ };
433
+
434
+ var setMin = function (options, min) {
435
+ options.yAxis.min = min;
436
+ };
437
+
438
+ var setMax = function (options, max) {
439
+ options.yAxis.max = max;
440
+ };
441
+
442
+ var setStacked = function (options, stacked) {
443
+ options.plotOptions.series.stacking = stacked ? "normal" : null;
444
+ };
445
+
446
+ var setXtitle = function (options, title) {
447
+ options.xAxis.title.text = title;
448
+ };
449
+
450
+ var setYtitle = function (options, title) {
451
+ options.yAxis.title.text = title;
452
+ };
453
+
454
+ var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle);
455
+
456
+ this.renderLineChart = function (chart, chartType) {
457
+ chartType = chartType || "spline";
458
+ var chartOptions = {};
459
+ if (chartType === "areaspline") {
460
+ chartOptions = {
461
+ plotOptions: {
462
+ areaspline: {
463
+ stacking: "normal"
464
+ },
465
+ area: {
466
+ stacking: "normal"
467
+ },
468
+ series: {
469
+ marker: {
470
+ enabled: false
471
+ }
472
+ }
473
+ }
474
+ };
475
+ }
476
+
477
+ if (chart.options.curve === false) {
478
+ if (chartType === "areaspline") {
479
+ chartType = "area";
480
+ } else if (chartType === "spline") {
481
+ chartType = "line";
482
+ }
483
+ }
484
+
485
+ var options = jsOptions(chart, chart.options, chartOptions), data, i, j;
486
+ options.xAxis.type = chart.discrete ? "category" : "datetime";
487
+ if (!options.chart.type) {
488
+ options.chart.type = chartType;
489
+ }
490
+ options.chart.renderTo = chart.element.id;
491
+
492
+ var series = chart.data;
493
+ for (i = 0; i < series.length; i++) {
494
+ data = series[i].data;
495
+ if (!chart.discrete) {
496
+ for (j = 0; j < data.length; j++) {
497
+ data[j][0] = data[j][0].getTime();
498
+ }
499
+ }
500
+ series[i].marker = {symbol: "circle"};
501
+ if (chart.options.points === false) {
502
+ series[i].marker.enabled = false;
503
+ }
504
+ }
505
+ options.series = series;
506
+ chart.chart = new Highcharts.Chart(options);
507
+ };
508
+
509
+ this.renderScatterChart = function (chart) {
510
+ var chartOptions = {};
511
+ var options = jsOptions(chart, chart.options, chartOptions);
512
+ options.chart.type = "scatter";
513
+ options.chart.renderTo = chart.element.id;
514
+ options.series = chart.data;
515
+ chart.chart = new Highcharts.Chart(options);
516
+ };
517
+
518
+ this.renderPieChart = function (chart) {
519
+ var chartOptions = merge(defaultOptions, {});
520
+
521
+ if (chart.options.colors) {
522
+ chartOptions.colors = chart.options.colors;
523
+ }
524
+ if (chart.options.donut) {
525
+ chartOptions.plotOptions = {pie: {innerSize: "50%"}};
526
+ }
527
+
528
+ if ("legend" in chart.options) {
529
+ hideLegend(chartOptions, chart.options.legend);
530
+ }
531
+
532
+ if (chart.options.title) {
533
+ setTitle(chartOptions, chart.options.title);
534
+ }
535
+
536
+ var options = merge(chartOptions, chart.options.library || {});
537
+ options.chart.renderTo = chart.element.id;
538
+ options.series = [{
539
+ type: "pie",
540
+ name: chart.options.label || "Value",
541
+ data: chart.data
542
+ }];
543
+ chart.chart = new Highcharts.Chart(options);
544
+ };
545
+
546
+ this.renderColumnChart = function (chart, chartType) {
547
+ chartType = chartType || "column";
548
+ var series = chart.data;
549
+ var options = jsOptions(chart, chart.options), i, j, s, d, rows = [], categories = [];
550
+ options.chart.type = chartType;
551
+ options.chart.renderTo = chart.element.id;
552
+
553
+ for (i = 0; i < series.length; i++) {
554
+ s = series[i];
555
+
556
+ for (j = 0; j < s.data.length; j++) {
557
+ d = s.data[j];
558
+ if (!rows[d[0]]) {
559
+ rows[d[0]] = new Array(series.length);
560
+ categories.push(d[0]);
561
+ }
562
+ rows[d[0]][i] = d[1];
563
+ }
564
+ }
565
+
566
+ if (chart.options.xtype === "number") {
567
+ categories.sort(sortByNumber);
568
+ }
569
+
570
+ options.xAxis.categories = categories;
571
+
572
+ var newSeries = [], d2;
573
+ for (i = 0; i < series.length; i++) {
574
+ d = [];
575
+ for (j = 0; j < categories.length; j++) {
576
+ d.push(rows[categories[j]][i] || 0);
577
+ }
578
+
579
+ d2 = {
580
+ name: series[i].name,
581
+ data: d
582
+ }
583
+ if (series[i].stack) {
584
+ d2.stack = series[i].stack;
585
+ }
586
+
587
+ newSeries.push(d2);
588
+ }
589
+ options.series = newSeries;
590
+
591
+ chart.chart = new Highcharts.Chart(options);
592
+ };
593
+
594
+ var self = this;
595
+
596
+ this.renderBarChart = function (chart) {
597
+ self.renderColumnChart(chart, "bar");
598
+ };
599
+
600
+ this.renderAreaChart = function (chart) {
601
+ self.renderLineChart(chart, "areaspline");
602
+ };
603
+ };
604
+ adapters.push(HighchartsAdapter);
605
+ }
606
+ if (!GoogleChartsAdapter && window.google && (window.google.setOnLoadCallback || window.google.charts)) {
607
+ GoogleChartsAdapter = new function () {
608
+ var google = window.google;
609
+
610
+ this.name = "google";
611
+
612
+ var loaded = {};
613
+ var callbacks = [];
614
+
615
+ var runCallbacks = function () {
616
+ var cb, call;
617
+ for (var i = 0; i < callbacks.length; i++) {
618
+ cb = callbacks[i];
619
+ call = google.visualization && ((cb.pack === "corechart" && google.visualization.LineChart) || (cb.pack === "timeline" && google.visualization.Timeline));
620
+ if (call) {
621
+ cb.callback();
622
+ callbacks.splice(i, 1);
623
+ i--;
624
+ }
625
+ }
626
+ };
627
+
628
+ var waitForLoaded = function (pack, callback) {
629
+ if (!callback) {
630
+ callback = pack;
631
+ pack = "corechart";
632
+ }
633
+
634
+ callbacks.push({pack: pack, callback: callback});
635
+
636
+ if (loaded[pack]) {
637
+ runCallbacks();
638
+ } else {
639
+ loaded[pack] = true;
640
+
641
+ // https://groups.google.com/forum/#!topic/google-visualization-api/fMKJcyA2yyI
642
+ var loadOptions = {
643
+ packages: [pack],
644
+ callback: runCallbacks
645
+ };
646
+ if (config.language) {
647
+ loadOptions.language = config.language;
648
+ }
649
+ if (pack === "corechart" && config.mapsApiKey) {
650
+ loadOptions.mapsApiKey = config.mapsApiKey;
651
+ }
652
+
653
+ if (window.google.setOnLoadCallback) {
654
+ google.load("visualization", "1", loadOptions);
655
+ } else {
656
+ google.charts.load("current", loadOptions);
657
+ }
658
+ }
659
+ };
660
+
661
+ // Set chart options
662
+ var defaultOptions = {
663
+ chartArea: {},
664
+ fontName: "'Lucida Grande', 'Lucida Sans Unicode', Verdana, Arial, Helvetica, sans-serif",
665
+ pointSize: 6,
666
+ legend: {
667
+ textStyle: {
668
+ fontSize: 12,
669
+ color: "#444"
670
+ },
671
+ alignment: "center",
672
+ position: "right"
673
+ },
674
+ curveType: "function",
675
+ hAxis: {
676
+ textStyle: {
677
+ color: "#666",
678
+ fontSize: 12
679
+ },
680
+ titleTextStyle: {},
681
+ gridlines: {
682
+ color: "transparent"
683
+ },
684
+ baselineColor: "#ccc",
685
+ viewWindow: {}
686
+ },
687
+ vAxis: {
688
+ textStyle: {
689
+ color: "#666",
690
+ fontSize: 12
691
+ },
692
+ titleTextStyle: {},
693
+ baselineColor: "#ccc",
694
+ viewWindow: {}
695
+ },
696
+ tooltip: {
697
+ textStyle: {
698
+ color: "#666",
699
+ fontSize: 12
700
+ }
701
+ }
702
+ };
703
+
704
+ var hideLegend = function (options, legend, hideLegend) {
705
+ if (legend !== undefined) {
706
+ var position;
707
+ if (!legend) {
708
+ position = "none";
709
+ } else if (legend === true) {
710
+ position = "right";
711
+ } else {
712
+ position = legend;
713
+ }
714
+ options.legend.position = position;
715
+ } else if (hideLegend) {
716
+ options.legend.position = "none";
717
+ }
718
+ };
719
+
720
+ var setTitle = function (options, title) {
721
+ options.title = title;
722
+ options.titleTextStyle = {color: "#333", fontSize: "20px"};
723
+ };
724
+
725
+ var setMin = function (options, min) {
726
+ options.vAxis.viewWindow.min = min;
727
+ };
728
+
729
+ var setMax = function (options, max) {
730
+ options.vAxis.viewWindow.max = max;
731
+ };
732
+
733
+ var setBarMin = function (options, min) {
734
+ options.hAxis.viewWindow.min = min;
735
+ };
736
+
737
+ var setBarMax = function (options, max) {
738
+ options.hAxis.viewWindow.max = max;
739
+ };
740
+
741
+ var setStacked = function (options, stacked) {
742
+ options.isStacked = !!stacked;
743
+ };
744
+
745
+ var setXtitle = function (options, title) {
746
+ options.hAxis.title = title;
747
+ options.hAxis.titleTextStyle.italic = false;
748
+ };
749
+
750
+ var setYtitle = function (options, title) {
751
+ options.vAxis.title = title;
752
+ options.vAxis.titleTextStyle.italic = false;
753
+ };
754
+
755
+ var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle);
756
+
757
+ // cant use object as key
758
+ var createDataTable = function (series, columnType, xtype) {
759
+ var i, j, s, d, key, rows = [], sortedLabels = [];
760
+ for (i = 0; i < series.length; i++) {
761
+ s = series[i];
762
+
763
+ for (j = 0; j < s.data.length; j++) {
764
+ d = s.data[j];
765
+ key = (columnType === "datetime") ? d[0].getTime() : d[0];
766
+ if (!rows[key]) {
767
+ rows[key] = new Array(series.length);
768
+ sortedLabels.push(key);
769
+ }
770
+ rows[key][i] = toFloat(d[1]);
771
+ }
772
+ }
773
+
774
+ var rows2 = [];
775
+ var day = true;
776
+ var value;
777
+ for (var j = 0; j < sortedLabels.length; j++) {
778
+ var i = sortedLabels[j];
779
+ if (columnType === "datetime") {
780
+ value = new Date(toFloat(i));
781
+ day = day && isDay(value);
782
+ } else if (columnType === "number") {
783
+ value = toFloat(i);
784
+ } else {
785
+ value = i;
786
+ }
787
+ rows2.push([value].concat(rows[i]));
788
+ }
789
+ if (columnType === "datetime") {
790
+ rows2.sort(sortByTime);
791
+ } else if (columnType === "number") {
792
+ rows2.sort(sortByNumberSeries);
793
+ }
794
+
795
+ if (xtype === "number") {
796
+ rows2.sort(sortByNumberSeries);
797
+
798
+ for (var i = 0; i < rows2.length; i++) {
799
+ rows2[i][0] = toStr(rows2[i][0]);
800
+ }
801
+ }
802
+
803
+ // create datatable
804
+ var data = new google.visualization.DataTable();
805
+ columnType = columnType === "datetime" && day ? "date" : columnType;
806
+ data.addColumn(columnType, "");
807
+ for (i = 0; i < series.length; i++) {
808
+ data.addColumn("number", series[i].name);
809
+ }
810
+ data.addRows(rows2);
811
+
812
+ return data;
813
+ };
814
+
815
+ var resize = function (callback) {
816
+ if (window.attachEvent) {
817
+ window.attachEvent("onresize", callback);
818
+ } else if (window.addEventListener) {
819
+ window.addEventListener("resize", callback, true);
820
+ }
821
+ callback();
822
+ };
823
+
824
+ this.renderLineChart = function (chart) {
825
+ waitForLoaded(function () {
826
+ var chartOptions = {};
827
+
828
+ if (chart.options.curve === false) {
829
+ chartOptions.curveType = "none";
830
+ }
831
+
832
+ if (chart.options.points === false) {
833
+ chartOptions.pointSize = 0;
834
+ }
835
+
836
+ var options = jsOptions(chart, chart.options, chartOptions);
837
+ var columnType = chart.discrete ? "string" : "datetime";
838
+ if (chart.options.xtype === "number") {
839
+ columnType = "number";
840
+ }
841
+ var data = createDataTable(chart.data, columnType);
842
+ chart.chart = new google.visualization.LineChart(chart.element);
843
+ resize(function () {
844
+ chart.chart.draw(data, options);
845
+ });
846
+ });
847
+ };
848
+
849
+ this.renderPieChart = function (chart) {
850
+ waitForLoaded(function () {
851
+ var chartOptions = {
852
+ chartArea: {
853
+ top: "10%",
854
+ height: "80%"
855
+ },
856
+ legend: {}
857
+ };
858
+ if (chart.options.colors) {
859
+ chartOptions.colors = chart.options.colors;
860
+ }
861
+ if (chart.options.donut) {
862
+ chartOptions.pieHole = 0.5;
863
+ }
864
+ if ("legend" in chart.options) {
865
+ hideLegend(chartOptions, chart.options.legend);
866
+ }
867
+ if (chart.options.title) {
868
+ setTitle(chartOptions, chart.options.title);
869
+ }
870
+ var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
871
+
872
+ var data = new google.visualization.DataTable();
873
+ data.addColumn("string", "");
874
+ data.addColumn("number", "Value");
875
+ data.addRows(chart.data);
876
+
877
+ chart.chart = new google.visualization.PieChart(chart.element);
878
+ resize(function () {
879
+ chart.chart.draw(data, options);
880
+ });
881
+ });
882
+ };
883
+
884
+ this.renderColumnChart = function (chart) {
885
+ waitForLoaded(function () {
886
+ var options = jsOptions(chart, chart.options);
887
+ var data = createDataTable(chart.data, "string", chart.options.xtype);
888
+ chart.chart = new google.visualization.ColumnChart(chart.element);
889
+ resize(function () {
890
+ chart.chart.draw(data, options);
891
+ });
892
+ });
893
+ };
894
+
895
+ this.renderBarChart = function (chart) {
896
+ waitForLoaded(function () {
897
+ var chartOptions = {
898
+ hAxis: {
899
+ gridlines: {
900
+ color: "#ccc"
901
+ }
902
+ }
903
+ };
904
+ var options = jsOptionsFunc(defaultOptions, hideLegend, setTitle, setBarMin, setBarMax, setStacked, setXtitle, setYtitle)(chart, chart.options, chartOptions);
905
+ var data = createDataTable(chart.data, "string", chart.options.xtype);
906
+ chart.chart = new google.visualization.BarChart(chart.element);
907
+ resize(function () {
908
+ chart.chart.draw(data, options);
909
+ });
910
+ });
911
+ };
912
+
913
+ this.renderAreaChart = function (chart) {
914
+ waitForLoaded(function () {
915
+ var chartOptions = {
916
+ isStacked: true,
917
+ pointSize: 0,
918
+ areaOpacity: 0.5
919
+ };
920
+
921
+ var options = jsOptions(chart, chart.options, chartOptions);
922
+ var columnType = chart.discrete ? "string" : "datetime";
923
+ if (chart.options.xtype === "number") {
924
+ columnType = "number";
925
+ }
926
+ var data = createDataTable(chart.data, columnType);
927
+ chart.chart = new google.visualization.AreaChart(chart.element);
928
+ resize(function () {
929
+ chart.chart.draw(data, options);
930
+ });
931
+ });
932
+ };
933
+
934
+ this.renderGeoChart = function (chart) {
935
+ waitForLoaded(function () {
936
+ var chartOptions = {
937
+ legend: "none",
938
+ colorAxis: {
939
+ colors: chart.options.colors || ["#f6c7b6", "#ce502d"]
940
+ }
941
+ };
942
+ var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
943
+
944
+ var data = new google.visualization.DataTable();
945
+ data.addColumn("string", "");
946
+ data.addColumn("number", chart.options.label || "Value");
947
+ data.addRows(chart.data);
948
+
949
+ chart.chart = new google.visualization.GeoChart(chart.element);
950
+ resize(function () {
951
+ chart.chart.draw(data, options);
952
+ });
953
+ });
954
+ };
955
+
956
+ this.renderScatterChart = function (chart) {
957
+ waitForLoaded(function () {
958
+ var chartOptions = {};
959
+ var options = jsOptions(chart, chart.options, chartOptions);
960
+
961
+ var series = chart.data, rows2 = [], i, j, data, d;
962
+ for (i = 0; i < series.length; i++) {
963
+ d = series[i].data;
964
+ for (j = 0; j < d.length; j++) {
965
+ var row = new Array(series.length + 1);
966
+ row[0] = d[j][0];
967
+ row[i + 1] = d[j][1];
968
+ rows2.push(row);
969
+ }
970
+ }
971
+
972
+ var data = new google.visualization.DataTable();
973
+ data.addColumn("number", "");
974
+ for (i = 0; i < series.length; i++) {
975
+ data.addColumn("number", series[i].name);
976
+ }
977
+ data.addRows(rows2);
978
+
979
+ chart.chart = new google.visualization.ScatterChart(chart.element);
980
+ resize(function () {
981
+ chart.chart.draw(data, options);
982
+ });
983
+ });
984
+ };
985
+
986
+ this.renderTimeline = function (chart) {
987
+ waitForLoaded("timeline", function () {
988
+ var chartOptions = {
989
+ legend: "none"
990
+ };
991
+
992
+ if (chart.options.colors) {
993
+ chartOptions.colors = chart.options.colors;
994
+ }
995
+ var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
996
+
997
+ var data = new google.visualization.DataTable();
998
+ data.addColumn({type: "string", id: "Name"});
999
+ data.addColumn({type: "date", id: "Start"});
1000
+ data.addColumn({type: "date", id: "End"});
1001
+ data.addRows(chart.data);
1002
+
1003
+ chart.element.style.lineHeight = "normal";
1004
+ chart.chart = new google.visualization.Timeline(chart.element);
1005
+
1006
+ resize(function () {
1007
+ chart.chart.draw(data, options);
1008
+ });
1009
+ });
1010
+ };
1011
+ };
1012
+
1013
+ adapters.push(GoogleChartsAdapter);
1014
+ }
1015
+ if (!ChartjsAdapter && "Chart" in window) {
1016
+ ChartjsAdapter = new function () {
1017
+ var Chart = window.Chart;
1018
+
1019
+ this.name = "chartjs";
1020
+
1021
+ var baseOptions = {
1022
+ maintainAspectRatio: false,
1023
+ animation: false,
1024
+ tooltips: {
1025
+ displayColors: false
1026
+ },
1027
+ legend: {},
1028
+ title: {fontSize: 20, fontColor: "#333"}
1029
+ };
1030
+
1031
+ var defaultOptions = {
1032
+ scales: {
1033
+ yAxes: [
1034
+ {
1035
+ ticks: {
1036
+ maxTicksLimit: 4
1037
+ },
1038
+ scaleLabel: {
1039
+ fontSize: 16,
1040
+ // fontStyle: "bold",
1041
+ fontColor: "#333"
1042
+ }
1043
+ }
1044
+ ],
1045
+ xAxes: [
1046
+ {
1047
+ gridLines: {
1048
+ drawOnChartArea: false
1049
+ },
1050
+ scaleLabel: {
1051
+ fontSize: 16,
1052
+ // fontStyle: "bold",
1053
+ fontColor: "#333"
1054
+ },
1055
+ time: {},
1056
+ ticks: {}
1057
+ }
1058
+ ]
1059
+ }
1060
+ };
1061
+
1062
+ // http://there4.io/2012/05/02/google-chart-color-list/
1063
+ var defaultColors = [
1064
+ "#3366CC", "#DC3912", "#FF9900", "#109618", "#990099", "#3B3EAC", "#0099C6",
1065
+ "#DD4477", "#66AA00", "#B82E2E", "#316395", "#994499", "#22AA99", "#AAAA11",
1066
+ "#6633CC", "#E67300", "#8B0707", "#329262", "#5574A6", "#651067"
1067
+ ];
1068
+
1069
+ var hideLegend = function (options, legend, hideLegend) {
1070
+ if (legend !== undefined) {
1071
+ options.legend.display = !!legend;
1072
+ if (legend && legend !== true) {
1073
+ options.legend.position = legend;
1074
+ }
1075
+ } else if (hideLegend) {
1076
+ options.legend.display = false;
1077
+ }
1078
+ };
1079
+
1080
+ var setTitle = function (options, title) {
1081
+ options.title.display = true;
1082
+ options.title.text = title;
1083
+ };
1084
+
1085
+ var setMin = function (options, min) {
1086
+ if (min !== null) {
1087
+ options.scales.yAxes[0].ticks.min = toFloat(min);
1088
+ }
1089
+ };
1090
+
1091
+ var setMax = function (options, max) {
1092
+ options.scales.yAxes[0].ticks.max = toFloat(max);
1093
+ };
1094
+
1095
+ var setBarMin = function (options, min) {
1096
+ if (min !== null) {
1097
+ options.scales.xAxes[0].ticks.min = toFloat(min);
1098
+ }
1099
+ };
1100
+
1101
+ var setBarMax = function (options, max) {
1102
+ options.scales.xAxes[0].ticks.max = toFloat(max);
1103
+ };
1104
+
1105
+ var setStacked = function (options, stacked) {
1106
+ options.scales.xAxes[0].stacked = !!stacked;
1107
+ options.scales.yAxes[0].stacked = !!stacked;
1108
+ };
1109
+
1110
+ var setXtitle = function (options, title) {
1111
+ options.scales.xAxes[0].scaleLabel.display = true;
1112
+ options.scales.xAxes[0].scaleLabel.labelString = title;
1113
+ };
1114
+
1115
+ var setYtitle = function (options, title) {
1116
+ options.scales.yAxes[0].scaleLabel.display = true;
1117
+ options.scales.yAxes[0].scaleLabel.labelString = title;
1118
+ };
1119
+
1120
+ var drawChart = function(chart, type, data, options) {
1121
+ if (chart.chart) {
1122
+ chart.chart.destroy();
1123
+ } else {
1124
+ chart.element.innerHTML = "<canvas></canvas>";
1125
+ }
1126
+
1127
+ var ctx = chart.element.getElementsByTagName("CANVAS")[0];
1128
+ chart.chart = new Chart(ctx, {
1129
+ type: type,
1130
+ data: data,
1131
+ options: options
1132
+ });
1133
+ };
1134
+
1135
+ // http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
1136
+ var addOpacity = function(hex, opacity) {
1137
+ var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
1138
+ return result ? "rgba(" + parseInt(result[1], 16) + ", " + parseInt(result[2], 16) + ", " + parseInt(result[3], 16) + ", " + opacity + ")" : hex;
1139
+ };
1140
+
1141
+ var setLabelSize = function (chart, data, options) {
1142
+ var maxLabelSize = Math.ceil(chart.element.offsetWidth / 4.0 / data.labels.length);
1143
+ if (maxLabelSize > 25) {
1144
+ maxLabelSize = 25;
1145
+ }
1146
+ options.scales.xAxes[0].ticks.callback = function (value) {
1147
+ value = toStr(value);
1148
+ if (value.length > maxLabelSize) {
1149
+ return value.substring(0, maxLabelSize - 2) + "...";
1150
+ } else {
1151
+ return value;
1152
+ }
1153
+ };
1154
+ };
1155
+
1156
+ var jsOptions = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle);
1157
+
1158
+ var createDataTable = function (chart, options, chartType) {
1159
+ var datasets = [];
1160
+ var labels = [];
1161
+
1162
+ var colors = chart.options.colors || defaultColors;
1163
+
1164
+ var day = true;
1165
+ var week = true;
1166
+ var dayOfWeek;
1167
+ var month = true;
1168
+ var year = true;
1169
+ var hour = true;
1170
+ var minute = true;
1171
+ var detectType = (chartType === "line" || chartType === "area") && !chart.discrete;
1172
+
1173
+ var series = chart.data;
1174
+
1175
+ var sortedLabels = [];
1176
+
1177
+ var i, j, s, d, key, rows = [];
1178
+ for (i = 0; i < series.length; i++) {
1179
+ s = series[i];
1180
+
1181
+ for (j = 0; j < s.data.length; j++) {
1182
+ d = s.data[j];
1183
+ key = detectType ? d[0].getTime() : d[0];
1184
+ if (!rows[key]) {
1185
+ rows[key] = new Array(series.length);
1186
+ }
1187
+ rows[key][i] = toFloat(d[1]);
1188
+ if (sortedLabels.indexOf(key) === -1) {
1189
+ sortedLabels.push(key);
1190
+ }
1191
+ }
1192
+ }
1193
+
1194
+ if (detectType || chart.options.xtype === "number") {
1195
+ sortedLabels.sort(sortByNumber);
1196
+ }
1197
+
1198
+ var rows2 = [];
1199
+ for (j = 0; j < series.length; j++) {
1200
+ rows2.push([]);
1201
+ }
1202
+
1203
+ var value;
1204
+ var k;
1205
+ for (k = 0; k < sortedLabels.length; k++) {
1206
+ i = sortedLabels[k];
1207
+ if (detectType) {
1208
+ value = new Date(toFloat(i));
1209
+ // TODO make this efficient
1210
+ day = day && isDay(value);
1211
+ if (!dayOfWeek) {
1212
+ dayOfWeek = value.getDay();
1213
+ }
1214
+ week = week && isWeek(value, dayOfWeek);
1215
+ month = month && isMonth(value);
1216
+ year = year && isYear(value);
1217
+ hour = hour && isHour(value);
1218
+ minute = minute && isMinute(value);
1219
+ } else {
1220
+ value = i;
1221
+ }
1222
+ labels.push(value);
1223
+ for (j = 0; j < series.length; j++) {
1224
+ // Chart.js doesn't like undefined
1225
+ rows2[j].push(rows[i][j] === undefined ? null : rows[i][j]);
1226
+ }
1227
+ }
1228
+
1229
+ for (i = 0; i < series.length; i++) {
1230
+ s = series[i];
1231
+
1232
+ var color = s.color || colors[i];
1233
+ var backgroundColor = chartType !== "line" ? addOpacity(color, 0.5) : color;
1234
+
1235
+ var dataset = {
1236
+ label: s.name,
1237
+ data: rows2[i],
1238
+ fill: chartType === "area",
1239
+ borderColor: color,
1240
+ backgroundColor: backgroundColor,
1241
+ pointBackgroundColor: color,
1242
+ borderWidth: 2
1243
+ };
1244
+
1245
+ if (s.stack) {
1246
+ dataset.stack = s.stack;
1247
+ }
1248
+
1249
+ if (chart.options.curve === false) {
1250
+ dataset.lineTension = 0;
1251
+ }
1252
+
1253
+ if (chart.options.points === false) {
1254
+ dataset.pointRadius = 0;
1255
+ dataset.pointHitRadius = 5;
1256
+ }
1257
+
1258
+ datasets.push(merge(dataset, s.library || {}));
1259
+ }
1260
+
1261
+ if (detectType && labels.length > 0) {
1262
+ var minTime = labels[0].getTime();
1263
+ var maxTime = labels[0].getTime();
1264
+ for (i = 1; i < labels.length; i++) {
1265
+ value = labels[i].getTime();
1266
+ if (value < minTime) {
1267
+ minTime = value;
1268
+ }
1269
+ if (value > maxTime) {
1270
+ maxTime = value;
1271
+ }
1272
+ }
1273
+
1274
+ var timeDiff = (maxTime - minTime) / (86400 * 1000.0);
1275
+
1276
+ if (!options.scales.xAxes[0].time.unit) {
1277
+ var step;
1278
+ if (year || timeDiff > 365 * 10) {
1279
+ options.scales.xAxes[0].time.unit = "year";
1280
+ step = 365;
1281
+ } else if (month || timeDiff > 30 * 10) {
1282
+ options.scales.xAxes[0].time.unit = "month";
1283
+ step = 30;
1284
+ } else if (day || timeDiff > 10) {
1285
+ options.scales.xAxes[0].time.unit = "day";
1286
+ step = 1;
1287
+ } else if (hour || timeDiff > 0.5) {
1288
+ options.scales.xAxes[0].time.displayFormats = {hour: "MMM D, h a"};
1289
+ options.scales.xAxes[0].time.unit = "hour";
1290
+ step = 1 / 24.0;
1291
+ } else if (minute) {
1292
+ options.scales.xAxes[0].time.displayFormats = {minute: "h:mm a"};
1293
+ options.scales.xAxes[0].time.unit = "minute";
1294
+ step = 1 / 24.0 / 60.0;
1295
+ }
1296
+
1297
+ if (step && timeDiff > 0) {
1298
+ var unitStepSize = Math.ceil(timeDiff / step / (chart.element.offsetWidth / 100.0));
1299
+ if (week && step === 1) {
1300
+ unitStepSize = Math.ceil(unitStepSize / 7.0) * 7;
1301
+ }
1302
+ options.scales.xAxes[0].time.unitStepSize = unitStepSize;
1303
+ }
1304
+ }
1305
+
1306
+ if (!options.scales.xAxes[0].time.tooltipFormat) {
1307
+ if (day) {
1308
+ options.scales.xAxes[0].time.tooltipFormat = "ll";
1309
+ } else if (hour) {
1310
+ options.scales.xAxes[0].time.tooltipFormat = "MMM D, h a";
1311
+ } else if (minute) {
1312
+ options.scales.xAxes[0].time.tooltipFormat = "h:mm a";
1313
+ }
1314
+ }
1315
+ }
1316
+
1317
+ var data = {
1318
+ labels: labels,
1319
+ datasets: datasets
1320
+ };
1321
+
1322
+ return data;
1323
+ };
1324
+
1325
+ this.renderLineChart = function (chart, chartType) {
1326
+ if (chart.options.xtype === "number") {
1327
+ return self.renderScatterChart(chart, chartType, true);
1328
+ }
1329
+
1330
+ var chartOptions = {};
1331
+ if (chartType === "area") {
1332
+ // TODO fix area stacked
1333
+ // chartOptions.stacked = true;
1334
+ }
1335
+ // fix for https://github.com/chartjs/Chart.js/issues/2441
1336
+ if (!chart.options.max && allZeros(chart.data)) {
1337
+ chartOptions.max = 1;
1338
+ }
1339
+
1340
+ var options = jsOptions(chart, merge(chartOptions, chart.options));
1341
+
1342
+ var data = createDataTable(chart, options, chartType || "line");
1343
+
1344
+ options.scales.xAxes[0].type = chart.discrete ? "category" : "time";
1345
+
1346
+ drawChart(chart, "line", data, options);
1347
+ };
1348
+
1349
+ this.renderPieChart = function (chart) {
1350
+ var options = merge({}, baseOptions);
1351
+ if (chart.options.donut) {
1352
+ options.cutoutPercentage = 50;
1353
+ }
1354
+
1355
+ if ("legend" in chart.options) {
1356
+ hideLegend(options, chart.options.legend);
1357
+ }
1358
+
1359
+ if (chart.options.title) {
1360
+ setTitle(options, chart.options.title);
1361
+ }
1362
+
1363
+ options = merge(options, chart.options.library || {});
1364
+
1365
+ var labels = [];
1366
+ var values = [];
1367
+ for (var i = 0; i < chart.data.length; i++) {
1368
+ var point = chart.data[i];
1369
+ labels.push(point[0]);
1370
+ values.push(point[1]);
1371
+ }
1372
+
1373
+ var data = {
1374
+ labels: labels,
1375
+ datasets: [
1376
+ {
1377
+ data: values,
1378
+ backgroundColor: chart.options.colors || defaultColors
1379
+ }
1380
+ ]
1381
+ };
1382
+
1383
+ drawChart(chart, "pie", data, options);
1384
+ };
1385
+
1386
+ this.renderColumnChart = function (chart, chartType) {
1387
+ var options;
1388
+ if (chartType === "bar") {
1389
+ options = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setTitle, setBarMin, setBarMax, setStacked, setXtitle, setYtitle)(chart, chart.options);
1390
+ } else {
1391
+ options = jsOptions(chart, chart.options);
1392
+ }
1393
+ var data = createDataTable(chart, options, "column");
1394
+ setLabelSize(chart, data, options);
1395
+ drawChart(chart, (chartType === "bar" ? "horizontalBar" : "bar"), data, options);
1396
+ };
1397
+
1398
+ var self = this;
1399
+
1400
+ this.renderAreaChart = function (chart) {
1401
+ self.renderLineChart(chart, "area");
1402
+ };
1403
+
1404
+ this.renderBarChart = function (chart) {
1405
+ self.renderColumnChart(chart, "bar");
1406
+ };
1407
+
1408
+ this.renderScatterChart = function (chart, chartType, lineChart) {
1409
+ chartType = chartType || "line";
1410
+
1411
+ var options = jsOptions(chart, chart.options);
1412
+
1413
+ var colors = chart.options.colors || defaultColors;
1414
+
1415
+ var datasets = [];
1416
+ var series = chart.data;
1417
+ for (var i = 0; i < series.length; i++) {
1418
+ var s = series[i];
1419
+ var d = [];
1420
+ for (var j = 0; j < s.data.length; j++) {
1421
+ var point = {
1422
+ x: toFloat(s.data[j][0]),
1423
+ y: toFloat(s.data[j][1])
1424
+ };
1425
+ if (chartType === "bubble") {
1426
+ point.r = toFloat(s.data[j][2]);
1427
+ }
1428
+ d.push(point);
1429
+ }
1430
+
1431
+ var color = s.color || colors[i];
1432
+ var backgroundColor = chartType === "area" ? addOpacity(color, 0.5) : color;
1433
+
1434
+ datasets.push({
1435
+ label: s.name,
1436
+ showLine: lineChart || false,
1437
+ data: d,
1438
+ borderColor: color,
1439
+ backgroundColor: backgroundColor,
1440
+ pointBackgroundColor: color,
1441
+ fill: chartType === "area"
1442
+ })
1443
+ }
1444
+
1445
+ if (chartType === "area") {
1446
+ chartType = "line";
1447
+ }
1448
+
1449
+ var data = {datasets: datasets};
1450
+
1451
+ options.scales.xAxes[0].type = "linear";
1452
+ options.scales.xAxes[0].position = "bottom";
1453
+
1454
+ drawChart(chart, chartType, data, options);
1455
+ };
1456
+
1457
+ this.renderBubbleChart = function (chart) {
1458
+ this.renderScatterChart(chart, "bubble");
1459
+ };
1460
+ };
1461
+
1462
+ adapters.unshift(ChartjsAdapter);
1463
+ }
1464
+ }
1465
+
1466
+ function renderChart(chartType, chart) {
1467
+ callAdapter(chartType, chart);
1468
+ if (chart.options.download && !chart.downloadAttached && chart.adapter === "chartjs") {
1469
+ addDownloadButton(chart);
1470
+ }
1471
+ }
1472
+
1473
+ // TODO remove chartType if cross-browser way
1474
+ // to get the name of the chart class
1475
+ function callAdapter(chartType, chart) {
1476
+ var i, adapter, fnName, adapterName;
1477
+ fnName = "render" + chartType;
1478
+ adapterName = chart.options.adapter;
1479
+
1480
+ loadAdapters();
1481
+
1482
+ for (i = 0; i < adapters.length; i++) {
1483
+ adapter = adapters[i];
1484
+ if ((!adapterName || adapterName === adapter.name) && isFunction(adapter[fnName])) {
1485
+ chart.adapter = adapter.name;
1486
+ return adapter[fnName](chart);
1487
+ }
1488
+ }
1489
+ throw new Error("No adapter found");
1490
+ }
1491
+
1492
+ // process data
1493
+
1494
+ var toFormattedKey = function (key, keyType) {
1495
+ if (keyType === "number") {
1496
+ key = toFloat(key);
1497
+ } else if (keyType === "datetime") {
1498
+ key = toDate(key);
1499
+ } else {
1500
+ key = toStr(key);
1501
+ }
1502
+ return key;
1503
+ };
1504
+
1505
+ var formatSeriesData = function (data, keyType) {
1506
+ var r = [], key, j;
1507
+ for (j = 0; j < data.length; j++) {
1508
+ if (keyType === "bubble") {
1509
+ r.push([toFloat(data[j][0]), toFloat(data[j][1]), toFloat(data[j][2])]);
1510
+ } else {
1511
+ key = toFormattedKey(data[j][0], keyType);
1512
+ r.push([key, toFloat(data[j][1])]);
1513
+ }
1514
+ }
1515
+ if (keyType === "datetime") {
1516
+ r.sort(sortByTime);
1517
+ } else if (keyType === "number") {
1518
+ r.sort(sortByNumberSeries);
1519
+ }
1520
+ return r;
1521
+ };
1522
+
1523
+ function isMinute(d) {
1524
+ return d.getMilliseconds() === 0 && d.getSeconds() === 0;
1525
+ }
1526
+
1527
+ function isHour(d) {
1528
+ return isMinute(d) && d.getMinutes() === 0;
1529
+ }
1530
+
1531
+ function isDay(d) {
1532
+ return isHour(d) && d.getHours() === 0;
1533
+ }
1534
+
1535
+ function isWeek(d, dayOfWeek) {
1536
+ return isDay(d) && d.getDay() === dayOfWeek;
1537
+ }
1538
+
1539
+ function isMonth(d) {
1540
+ return isDay(d) && d.getDate() === 1;
1541
+ }
1542
+
1543
+ function isYear(d) {
1544
+ return isMonth(d) && d.getMonth() === 0;
1545
+ }
1546
+
1547
+ function isDate(obj) {
1548
+ return !isNaN(toDate(obj)) && toStr(obj).length >= 6;
1549
+ }
1550
+
1551
+ function allZeros(data) {
1552
+ var i, j, d;
1553
+ for (i = 0; i < data.length; i++) {
1554
+ d = data[i].data;
1555
+ for (j = 0; j < d.length; j++) {
1556
+ if (d[j][1] != 0) {
1557
+ return false;
1558
+ }
1559
+ }
1560
+ }
1561
+ return true;
1562
+ }
1563
+
1564
+ function detectDiscrete(series) {
1565
+ var i, j, data;
1566
+ for (i = 0; i < series.length; i++) {
1567
+ data = toArr(series[i].data);
1568
+ for (j = 0; j < data.length; j++) {
1569
+ if (!isDate(data[j][0])) {
1570
+ return true;
1571
+ }
1572
+ }
1573
+ }
1574
+ return false;
1575
+ }
1576
+
1577
+ // creates a shallow copy of each element of the array
1578
+ // elements are expected to be objects
1579
+ function copySeries(series) {
1580
+ var newSeries = [], i, j;
1581
+ for (i = 0; i < series.length; i++) {
1582
+ var copy = {}
1583
+ for (j in series[i]) {
1584
+ if (series[i].hasOwnProperty(j)) {
1585
+ copy[j] = series[i][j];
1586
+ }
1587
+ }
1588
+ newSeries.push(copy)
1589
+ }
1590
+ return newSeries;
1591
+ }
1592
+
1593
+ function processSeries(chart, keyType) {
1594
+ var i;
1595
+
1596
+ var opts = chart.options;
1597
+ var series = chart.rawData;
1598
+
1599
+ // see if one series or multiple
1600
+ if (!isArray(series) || typeof series[0] !== "object" || isArray(series[0])) {
1601
+ series = [{name: opts.label || "Value", data: series}];
1602
+ chart.hideLegend = true;
1603
+ } else {
1604
+ chart.hideLegend = false;
1605
+ }
1606
+ if ((opts.discrete === null || opts.discrete === undefined) && keyType !== "bubble" && keyType !== "number") {
1607
+ chart.discrete = detectDiscrete(series);
1608
+ } else {
1609
+ chart.discrete = opts.discrete;
1610
+ }
1611
+ if (chart.discrete) {
1612
+ keyType = "string";
1613
+ }
1614
+ if (chart.options.xtype) {
1615
+ keyType = chart.options.xtype;
1616
+ }
1617
+
1618
+ // right format
1619
+ series = copySeries(series);
1620
+ for (i = 0; i < series.length; i++) {
1621
+ series[i].data = formatSeriesData(toArr(series[i].data), keyType);
1622
+ }
1623
+
1624
+ return series;
1625
+ }
1626
+
1627
+ function processSimple(chart) {
1628
+ var perfectData = toArr(chart.rawData), i;
1629
+ for (i = 0; i < perfectData.length; i++) {
1630
+ perfectData[i] = [toStr(perfectData[i][0]), toFloat(perfectData[i][1])];
1631
+ }
1632
+ return perfectData;
1633
+ }
1634
+
1635
+ function processTime(chart)
1636
+ {
1637
+ var i, data = chart.rawData;
1638
+ for (i = 0; i < data.length; i++) {
1639
+ data[i][1] = toDate(data[i][1]);
1640
+ data[i][2] = toDate(data[i][2]);
1641
+ }
1642
+ return data;
1643
+ }
1644
+
1645
+ function processLineData(chart) {
1646
+ return processSeries(chart, "datetime");
1647
+ }
1648
+
1649
+ function processColumnData(chart) {
1650
+ return processSeries(chart, "string");
1651
+ }
1652
+
1653
+ function processBarData(chart) {
1654
+ return processSeries(chart, "string");
1655
+ }
1656
+
1657
+ function processAreaData(chart) {
1658
+ return processSeries(chart, "datetime");
1659
+ }
1660
+
1661
+ function processScatterData(chart) {
1662
+ return processSeries(chart, "number");
1663
+ }
1664
+
1665
+ function processBubbleData(chart) {
1666
+ return processSeries(chart, "bubble");
1667
+ }
1668
+
1669
+ function createChart(chartType, chart, element, dataSource, opts, processData) {
1670
+ var elementId;
1671
+ if (typeof element === "string") {
1672
+ elementId = element;
1673
+ element = document.getElementById(element);
1674
+ if (!element) {
1675
+ throw new Error("No element with id " + elementId);
1676
+ }
1677
+ }
1678
+
1679
+ chart.element = element;
1680
+ opts = merge(Chartkick.options, opts || {});
1681
+ chart.options = opts;
1682
+ chart.dataSource = dataSource;
1683
+
1684
+ if (!processData) {
1685
+ processData = function (chart) {
1686
+ return chart.rawData;
1687
+ }
1688
+ }
1689
+
1690
+ // getters
1691
+ chart.getElement = function () {
1692
+ return element;
1693
+ };
1694
+ chart.getDataSource = function () {
1695
+ return chart.dataSource;
1696
+ };
1697
+ chart.getData = function () {
1698
+ return chart.data;
1699
+ };
1700
+ chart.getOptions = function () {
1701
+ return chart.options;
1702
+ };
1703
+ chart.getChartObject = function () {
1704
+ return chart.chart;
1705
+ };
1706
+ chart.getAdapter = function () {
1707
+ return chart.adapter;
1708
+ };
1709
+
1710
+ var callback = function () {
1711
+ chart.data = processData(chart);
1712
+ renderChart(chartType, chart);
1713
+ };
1714
+
1715
+ // functions
1716
+ chart.updateData = function (dataSource, options) {
1717
+ chart.dataSource = dataSource;
1718
+ if (options) {
1719
+ chart.options = merge(Chartkick.options, options);
1720
+ }
1721
+ fetchDataSource(chart, callback, dataSource);
1722
+ };
1723
+ chart.setOptions = function (options) {
1724
+ chart.options = merge(Chartkick.options, options);
1725
+ chart.redraw();
1726
+ };
1727
+ chart.redraw = function() {
1728
+ fetchDataSource(chart, callback, chart.rawData);
1729
+ };
1730
+ chart.refreshData = function () {
1731
+ if (typeof chart.dataSource === "string") {
1732
+ // prevent browser from caching
1733
+ var sep = chart.dataSource.indexOf("?") === -1 ? "?" : "&";
1734
+ var url = chart.dataSource + sep + "_=" + (new Date()).getTime();
1735
+ fetchDataSource(chart, callback, url);
1736
+ }
1737
+ };
1738
+ chart.stopRefresh = function () {
1739
+ if (chart.intervalId) {
1740
+ clearInterval(chart.intervalId);
1741
+ }
1742
+ };
1743
+ chart.toImage = function () {
1744
+ if (chart.adapter === "chartjs") {
1745
+ return chart.chart.toBase64Image();
1746
+ } else {
1747
+ return null;
1748
+ }
1749
+ }
1750
+
1751
+ Chartkick.charts[element.id] = chart;
1752
+
1753
+ fetchDataSource(chart, callback, dataSource);
1754
+
1755
+ if (opts.refresh) {
1756
+ chart.intervalId = setInterval( function () {
1757
+ chart.refreshData();
1758
+ }, opts.refresh * 1000);
1759
+ }
1760
+ }
1761
+
1762
+ // define classes
1763
+
1764
+ Chartkick = {
1765
+ LineChart: function (element, dataSource, options) {
1766
+ createChart("LineChart", this, element, dataSource, options, processLineData);
1767
+ },
1768
+ PieChart: function (element, dataSource, options) {
1769
+ createChart("PieChart", this, element, dataSource, options, processSimple);
1770
+ },
1771
+ ColumnChart: function (element, dataSource, options) {
1772
+ createChart("ColumnChart", this, element, dataSource, options, processColumnData);
1773
+ },
1774
+ BarChart: function (element, dataSource, options) {
1775
+ createChart("BarChart", this, element, dataSource, options, processBarData);
1776
+ },
1777
+ AreaChart: function (element, dataSource, options) {
1778
+ createChart("AreaChart", this, element, dataSource, options, processAreaData);
1779
+ },
1780
+ GeoChart: function (element, dataSource, options) {
1781
+ createChart("GeoChart", this, element, dataSource, options, processSimple);
1782
+ },
1783
+ ScatterChart: function (element, dataSource, options) {
1784
+ createChart("ScatterChart", this, element, dataSource, options, processScatterData);
1785
+ },
1786
+ BubbleChart: function (element, dataSource, options) {
1787
+ createChart("BubbleChart", this, element, dataSource, options, processBubbleData);
1788
+ },
1789
+ Timeline: function (element, dataSource, options) {
1790
+ createChart("Timeline", this, element, dataSource, options, processTime);
1791
+ },
1792
+ charts: {},
1793
+ configure: function (options) {
1794
+ for (var key in options) {
1795
+ if (options.hasOwnProperty(key)) {
1796
+ config[key] = options[key];
1797
+ }
1798
+ }
1799
+ },
1800
+ eachChart: function (callback) {
1801
+ for (var chartId in Chartkick.charts) {
1802
+ if (Chartkick.charts.hasOwnProperty(chartId)) {
1803
+ callback(Chartkick.charts[chartId]);
1804
+ }
1805
+ }
1806
+ },
1807
+ options: {},
1808
+ adapters: adapters,
1809
+ createChart: createChart
1810
+ };
1811
+
1812
+ if (typeof module === "object" && typeof module.exports === "object") {
1813
+ module.exports = Chartkick;
1814
+ } else {
1815
+ window.Chartkick = Chartkick;
1816
+ }
1817
+ }(window));