chartkick 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of chartkick might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +11 -4
- data/app/assets/javascripts/chartkick.js +123 -102
- data/lib/chartkick/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e5bfc65a915cb616197a9dbd513eabecc1329c77
|
4
|
+
data.tar.gz: 86d3cec3916edf351936d5db2255b55d4e9950f5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 543d3fe3a54d083c5964d3e6a48b86e6ef206ce1e24c472b84aec713ce16860c1b7d025761d0f8c2e7dfa3f927573e0160331fe9ac53f062f57815d98408c4cd
|
7
|
+
data.tar.gz: 686b706c7bdc764a3919698a1b59a37ad41248ab265b813e88cd65d9f4719964e2070faac03815b7102e3075de1b91c819517d1aa8d199bda647bad5b52bf00a
|
data/README.md
CHANGED
@@ -31,7 +31,9 @@ Column chart
|
|
31
31
|
Multiple series (except pie chart)
|
32
32
|
|
33
33
|
```erb
|
34
|
-
<%= line_chart
|
34
|
+
<%= line_chart @goals.map{|goal|
|
35
|
+
{:name => goal.name, :data => goal.feats.group_by_week(:created_at).count }
|
36
|
+
} %>
|
35
37
|
```
|
36
38
|
|
37
39
|
### Say Goodbye To Timeouts
|
@@ -82,9 +84,10 @@ Pass data as a Hash or Array
|
|
82
84
|
For multiple series, use the format
|
83
85
|
|
84
86
|
```erb
|
85
|
-
|
86
|
-
|
87
|
-
|
87
|
+
<%= line_chart [
|
88
|
+
{:name => "Series A", :data => series_a},
|
89
|
+
{:name => "Series B", :data => series_b}
|
90
|
+
] %>
|
88
91
|
```
|
89
92
|
|
90
93
|
Times can be a time, a timestamp, or a string (strings are parsed)
|
@@ -132,6 +135,10 @@ Chartkick doesn’t require Ruby.
|
|
132
135
|
|
133
136
|
Download [chartkick.js](https://raw.github.com/ankane/chartkick/master/app/assets/javascripts/chartkick.js)
|
134
137
|
|
138
|
+
## Credits
|
139
|
+
|
140
|
+
Chartkick uses [iso8601.js](https://github.com/Do/iso8601.js) to parse dates and times.
|
141
|
+
|
135
142
|
## Contributing
|
136
143
|
|
137
144
|
1. Fork it
|
@@ -1,13 +1,11 @@
|
|
1
1
|
/*jslint browser: true, indent: 2, plusplus: true */
|
2
|
-
/*global google
|
2
|
+
/*global google, $*/
|
3
3
|
|
4
4
|
(function() {
|
5
5
|
'use strict';
|
6
6
|
|
7
|
-
// vendor
|
8
|
-
|
9
7
|
// http://stackoverflow.com/questions/728360/most-elegant-way-to-clone-a-javascript-object
|
10
|
-
|
8
|
+
function clone(obj) {
|
11
9
|
var copy, i, attr, len;
|
12
10
|
|
13
11
|
// Handle the 3 simple types, and null or undefined
|
@@ -43,18 +41,21 @@
|
|
43
41
|
}
|
44
42
|
|
45
43
|
throw new Error("Unable to copy obj! Its type isn't supported.");
|
46
|
-
}
|
44
|
+
}
|
47
45
|
|
48
46
|
// https://github.com/Do/iso8601.js
|
49
|
-
var
|
50
|
-
|
51
|
-
DECIMAL_SEPARATOR = String(1.5).charAt(1);
|
47
|
+
var ISO8601_PATTERN = /(\d\d\d\d)(\-)?(\d\d)(\-)?(\d\d)(T)?(\d\d)(:)?(\d\d)?(:)?(\d\d)?([\.,]\d+)?($|Z|([\+\-])(\d\d)(:)?(\d\d)?)/i;
|
48
|
+
var DECIMAL_SEPARATOR = String(1.5).charAt(1);
|
52
49
|
|
53
|
-
|
50
|
+
function parseISO8601(input) {
|
54
51
|
var day, hour, matches, milliseconds, minutes, month, offset, result, seconds, type, year;
|
55
52
|
type = Object.prototype.toString.call(input);
|
56
|
-
if (type === '[object Date]')
|
57
|
-
|
53
|
+
if (type === '[object Date]') {
|
54
|
+
return input;
|
55
|
+
}
|
56
|
+
if (type !== '[object String]') {
|
57
|
+
return;
|
58
|
+
}
|
58
59
|
if (matches = input.match(ISO8601_PATTERN)) {
|
59
60
|
year = parseInt(matches[1], 10);
|
60
61
|
month = parseInt(matches[3], 10) - 1;
|
@@ -66,15 +67,19 @@
|
|
66
67
|
result = Date.UTC(year, month, day, hour, minutes, seconds, milliseconds);
|
67
68
|
if (matches[13] && matches[14]) {
|
68
69
|
offset = matches[15] * 60;
|
69
|
-
if (matches[17])
|
70
|
+
if (matches[17]) {
|
71
|
+
offset += parseInt(matches[17], 10);
|
72
|
+
}
|
70
73
|
offset *= matches[14] === '-' ? -1 : 1;
|
71
74
|
result -= offset * 60 * 1000;
|
72
75
|
}
|
73
76
|
return new Date(result);
|
74
77
|
}
|
75
|
-
}
|
78
|
+
}
|
79
|
+
// end iso8601.js
|
76
80
|
|
77
|
-
//
|
81
|
+
// only functions that need defined specific to charting library
|
82
|
+
var renderLineChart, renderPieChart, renderColumnChart;
|
78
83
|
|
79
84
|
if ("Highcharts" in window) {
|
80
85
|
|
@@ -122,9 +127,9 @@
|
|
122
127
|
options.yAxis.max = opts.max;
|
123
128
|
}
|
124
129
|
return options;
|
125
|
-
}
|
130
|
+
};
|
126
131
|
|
127
|
-
|
132
|
+
renderLineChart = function(element, series, opts) {
|
128
133
|
var options = jsOptions(opts), data, i, j;
|
129
134
|
options.xAxis.type = "datetime";
|
130
135
|
options.chart = {type: "spline"};
|
@@ -138,13 +143,13 @@
|
|
138
143
|
}
|
139
144
|
options.series = series;
|
140
145
|
|
141
|
-
if (series.length
|
146
|
+
if (series.length === 1) {
|
142
147
|
options.legend = {enabled: false};
|
143
148
|
}
|
144
149
|
$(element).highcharts(options);
|
145
150
|
};
|
146
151
|
|
147
|
-
|
152
|
+
renderPieChart = function(element, series, opts) {
|
148
153
|
var options = jsOptions(opts);
|
149
154
|
options.series = [{
|
150
155
|
type: "pie",
|
@@ -154,11 +159,10 @@
|
|
154
159
|
$(element).highcharts(options);
|
155
160
|
};
|
156
161
|
|
157
|
-
|
158
|
-
var options = jsOptions(opts),
|
162
|
+
renderColumnChart = function(element, series, opts) {
|
163
|
+
var options = jsOptions(opts), i, j, s, d, rows = [];
|
159
164
|
options.chart = {type: "column"};
|
160
165
|
|
161
|
-
var i, j, s, d, rows = [];
|
162
166
|
for (i = 0; i < series.length; i++) {
|
163
167
|
s = series[i];
|
164
168
|
|
@@ -173,7 +177,9 @@
|
|
173
177
|
|
174
178
|
var categories = [];
|
175
179
|
for (i in rows) {
|
176
|
-
|
180
|
+
if (rows.hasOwnProperty(i)) {
|
181
|
+
categories.push(i);
|
182
|
+
}
|
177
183
|
}
|
178
184
|
options.xAxis.categories = categories;
|
179
185
|
|
@@ -191,16 +197,15 @@
|
|
191
197
|
}
|
192
198
|
options.series = newSeries;
|
193
199
|
|
194
|
-
if (series.length
|
200
|
+
if (series.length === 1) {
|
195
201
|
options.legend.enabled = false;
|
196
202
|
}
|
197
203
|
$(element).highcharts(options);
|
198
204
|
};
|
199
|
-
}
|
200
|
-
|
201
|
-
|
205
|
+
} else if ("google" in window) { // Google charts
|
206
|
+
// load from google
|
202
207
|
var loaded = false;
|
203
|
-
google.setOnLoadCallback(
|
208
|
+
google.setOnLoadCallback(function() {
|
204
209
|
loaded = true;
|
205
210
|
});
|
206
211
|
google.load("visualization", "1.0", {"packages": ["corechart"]});
|
@@ -210,7 +215,7 @@
|
|
210
215
|
if (loaded) {
|
211
216
|
callback();
|
212
217
|
}
|
213
|
-
}
|
218
|
+
};
|
214
219
|
|
215
220
|
// Set chart options
|
216
221
|
var defaultOptions = {
|
@@ -251,7 +256,7 @@
|
|
251
256
|
fontSize: 12
|
252
257
|
}
|
253
258
|
}
|
254
|
-
}
|
259
|
+
};
|
255
260
|
|
256
261
|
// cant use object as key
|
257
262
|
var createDataTable = function(series, columnType) {
|
@@ -275,7 +280,9 @@
|
|
275
280
|
|
276
281
|
var rows2 = [];
|
277
282
|
for (i in rows) {
|
278
|
-
|
283
|
+
if (rows.hasOwnProperty(i)) {
|
284
|
+
rows2.push([(columnType === "datetime") ? new Date(toFloat(i)) : i].concat(rows[i]));
|
285
|
+
}
|
279
286
|
}
|
280
287
|
data.addRows(rows2);
|
281
288
|
|
@@ -291,23 +298,23 @@
|
|
291
298
|
options.vAxis.viewWindow.max = opts.max;
|
292
299
|
}
|
293
300
|
return options;
|
294
|
-
}
|
301
|
+
};
|
295
302
|
|
296
|
-
|
303
|
+
renderLineChart = function(element, series, opts) {
|
297
304
|
waitForLoaded(function() {
|
298
305
|
var data = createDataTable(series, "datetime");
|
299
306
|
|
300
307
|
var options = jsOptions(opts);
|
301
|
-
if (series.length
|
308
|
+
if (series.length === 1) {
|
302
309
|
options.legend.position = "none";
|
303
310
|
}
|
304
311
|
|
305
312
|
var chart = new google.visualization.LineChart(element);
|
306
313
|
chart.draw(data, options);
|
307
|
-
})
|
314
|
+
});
|
308
315
|
};
|
309
316
|
|
310
|
-
|
317
|
+
renderPieChart = function(element, series, opts) {
|
311
318
|
waitForLoaded(function() {
|
312
319
|
var data = new google.visualization.DataTable();
|
313
320
|
data.addColumn("string", "");
|
@@ -325,12 +332,12 @@
|
|
325
332
|
});
|
326
333
|
};
|
327
334
|
|
328
|
-
|
335
|
+
renderColumnChart = function(element, series, opts) {
|
329
336
|
waitForLoaded(function() {
|
330
337
|
var data = createDataTable(series, "string");
|
331
338
|
|
332
339
|
var options = jsOptions(opts);
|
333
|
-
if (series.length
|
340
|
+
if (series.length === 1) {
|
334
341
|
options.legend.position = "none";
|
335
342
|
}
|
336
343
|
|
@@ -338,98 +345,78 @@
|
|
338
345
|
chart.draw(data, options);
|
339
346
|
});
|
340
347
|
};
|
348
|
+
} else { // no chart library installed
|
349
|
+
renderLineChart = renderPieChart = renderColumnChart = function() {
|
350
|
+
throw new Error("Please install Google Charts or Highcharts");
|
351
|
+
};
|
341
352
|
}
|
342
353
|
|
343
|
-
var
|
344
|
-
|
345
|
-
|
346
|
-
|
354
|
+
var hasInnerText = (document.getElementsByTagName("body")[0].innerText !== undefined) ? true : false;
|
355
|
+
function setText(element, text) {
|
356
|
+
if (hasInnerText) {
|
357
|
+
element.innerText = text;
|
358
|
+
} else {
|
359
|
+
element.textContent = text;
|
360
|
+
}
|
361
|
+
}
|
347
362
|
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
363
|
+
function chartError(element, message) {
|
364
|
+
setText(element, "Error Loading Chart: " + message);
|
365
|
+
element.style.color = "#ff0000";
|
366
|
+
}
|
367
|
+
|
368
|
+
function getJSON(element, url, success) {
|
352
369
|
$.ajax({
|
353
370
|
dataType: "json",
|
354
371
|
url: url,
|
355
372
|
success: success,
|
356
|
-
error: function() {
|
357
|
-
|
373
|
+
error: function(jqXHR, textStatus, errorThrown) {
|
374
|
+
var message = (typeof errorThrown === "string") ? errorThrown : errorThrown.message;
|
375
|
+
chartError(element, message);
|
358
376
|
}
|
359
377
|
});
|
360
|
-
}
|
378
|
+
}
|
361
379
|
|
362
|
-
|
363
|
-
var errorCatcher = function(element, data, opts, callback) {
|
380
|
+
function errorCatcher(element, data, opts, callback) {
|
364
381
|
try {
|
365
382
|
callback(element, data, opts);
|
366
383
|
} catch (err) {
|
367
|
-
chartError(element);
|
384
|
+
chartError(element, err.message);
|
368
385
|
throw err;
|
369
386
|
}
|
370
|
-
}
|
387
|
+
}
|
371
388
|
|
372
|
-
|
373
|
-
var fetchDataSource = function(element, dataSource, opts, callback) {
|
389
|
+
function fetchDataSource(element, dataSource, opts, callback) {
|
374
390
|
if (typeof dataSource === "string") {
|
375
391
|
getJSON(element, dataSource, function(data, textStatus, jqXHR) {
|
376
392
|
errorCatcher(element, data, opts, callback);
|
377
393
|
});
|
378
|
-
}
|
379
|
-
else {
|
394
|
+
} else {
|
380
395
|
errorCatcher(element, dataSource, opts, callback);
|
381
396
|
}
|
382
|
-
}
|
383
|
-
|
384
|
-
var isArray = function(variable) {
|
385
|
-
return Object.prototype.toString.call(variable) === "[object Array]"
|
386
|
-
};
|
387
|
-
|
388
|
-
var standardSeries = function(series, time) {
|
389
|
-
var i, j, data, r, key;
|
390
|
-
|
391
|
-
// clean data
|
392
|
-
if (!isArray(series) || typeof series[0] !== "object" || isArray(series[0])) {
|
393
|
-
series = [{name: "Value", data: series}];
|
394
|
-
}
|
397
|
+
}
|
395
398
|
|
396
|
-
|
397
|
-
for (i = 0; i < series.length; i++) {
|
398
|
-
data = toArr(series[i].data);
|
399
|
-
r = [];
|
400
|
-
for (j = 0; j < data.length; j++) {
|
401
|
-
key = data[j][0];
|
402
|
-
if (time) {
|
403
|
-
key = toDate(key);
|
404
|
-
}
|
405
|
-
else {
|
406
|
-
key = toStr(key);
|
407
|
-
}
|
408
|
-
r.push([key, toFloat(data[j][1])]);
|
409
|
-
}
|
410
|
-
if (time) {
|
411
|
-
r.sort(function(a,b){ return a[0].getTime() - b[0].getTime() });
|
412
|
-
}
|
413
|
-
series[i].data = r;
|
414
|
-
}
|
399
|
+
// helpers
|
415
400
|
|
416
|
-
|
401
|
+
function isArray(variable) {
|
402
|
+
return Object.prototype.toString.call(variable) === "[object Array]";
|
417
403
|
}
|
418
404
|
|
419
|
-
|
405
|
+
// type conversions
|
406
|
+
|
407
|
+
function toStr(n) {
|
420
408
|
return "" + n;
|
421
409
|
}
|
422
410
|
|
423
|
-
|
411
|
+
function toFloat(n) {
|
424
412
|
return parseFloat(n);
|
425
|
-
}
|
413
|
+
}
|
426
414
|
|
427
|
-
|
415
|
+
function toDate(n) {
|
428
416
|
if (typeof n !== "object") {
|
429
417
|
if (typeof n === "number") {
|
430
418
|
n = new Date(n * 1000); // ms
|
431
|
-
}
|
432
|
-
else { // str
|
419
|
+
} else { // str
|
433
420
|
// try our best to get the str into iso8601
|
434
421
|
// TODO be smarter about this
|
435
422
|
var str = n.replace(/ /, "T").replace(" ", "").replace("UTC", "Z");
|
@@ -437,9 +424,9 @@
|
|
437
424
|
}
|
438
425
|
}
|
439
426
|
return n;
|
440
|
-
}
|
427
|
+
}
|
441
428
|
|
442
|
-
|
429
|
+
function toArr(n) {
|
443
430
|
if (!isArray(n)) {
|
444
431
|
var arr = [], i;
|
445
432
|
for (i in n) {
|
@@ -452,15 +439,47 @@
|
|
452
439
|
return n;
|
453
440
|
}
|
454
441
|
|
455
|
-
|
456
|
-
|
442
|
+
// process data
|
443
|
+
|
444
|
+
function sortByTime(a, b) {
|
445
|
+
return a[0].getTime() - b[0].getTime();
|
457
446
|
}
|
458
447
|
|
459
|
-
|
460
|
-
|
448
|
+
function processSeries(series, time) {
|
449
|
+
var i, j, data, r, key;
|
450
|
+
|
451
|
+
// see if one series or multiple
|
452
|
+
if (!isArray(series) || typeof series[0] !== "object" || isArray(series[0])) {
|
453
|
+
series = [{name: "Value", data: series}];
|
454
|
+
}
|
455
|
+
|
456
|
+
// right format
|
457
|
+
for (i = 0; i < series.length; i++) {
|
458
|
+
data = toArr(series[i].data);
|
459
|
+
r = [];
|
460
|
+
for (j = 0; j < data.length; j++) {
|
461
|
+
key = data[j][0];
|
462
|
+
key = time ? toDate(key) : toStr(key);
|
463
|
+
r.push([key, toFloat(data[j][1])]);
|
464
|
+
}
|
465
|
+
if (time) {
|
466
|
+
r.sort(sortByTime);
|
467
|
+
}
|
468
|
+
series[i].data = r;
|
469
|
+
}
|
470
|
+
|
471
|
+
return series;
|
461
472
|
}
|
462
473
|
|
463
|
-
|
474
|
+
function processLineData(element, data, opts) {
|
475
|
+
renderLineChart(element, processSeries(data, true), opts);
|
476
|
+
}
|
477
|
+
|
478
|
+
function processColumnData(element, data, opts) {
|
479
|
+
renderColumnChart(element, processSeries(data, false), opts);
|
480
|
+
}
|
481
|
+
|
482
|
+
function processPieData(element, data, opts) {
|
464
483
|
var perfectData = toArr(data), i;
|
465
484
|
for (i = 0; i < perfectData.length; i++) {
|
466
485
|
perfectData[i] = [toStr(perfectData[i][0]), toFloat(perfectData[i][1])];
|
@@ -468,6 +487,8 @@
|
|
468
487
|
renderPieChart(element, perfectData, opts);
|
469
488
|
}
|
470
489
|
|
490
|
+
// define classes
|
491
|
+
|
471
492
|
var Chartkick = {
|
472
493
|
LineChart: function(element, dataSource, opts) {
|
473
494
|
fetchDataSource(element, dataSource, opts || {}, processLineData);
|
data/lib/chartkick/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chartkick
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-05-
|
11
|
+
date: 2013-05-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|