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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7cff1f5410c612571c17f1d8e2f4dd0fef8a2a3f
4
- data.tar.gz: a4b70704b7e4b5c233382dda51bb9a45a2b65f5c
3
+ metadata.gz: e5bfc65a915cb616197a9dbd513eabecc1329c77
4
+ data.tar.gz: 86d3cec3916edf351936d5db2255b55d4e9950f5
5
5
  SHA512:
6
- metadata.gz: a2b0e9e917045bcafc1069ef5fc3743de290c4ae42b95adb3b96b403450f334336769144a638390e4b28f3587a734edc292792ad756b879312596ffb3a308177
7
- data.tar.gz: 9a40777c914f017f637da7af79c4da86d87ba94de53dacf271a852d7097ac7c5ee92bf06cbf919db01033499f9f89b9b624ed9ad196d1a84e9c9c225494dd096
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 Goal.all.map{|goal| {:name => goal.name, :data => goal.feats.group_by_week(:created_at).count } } %>
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
- <% series_a = {time_0 => 5, time_1 => 7} # Hash %>
86
- <% series_b = [[time_0, 8], [time_1, 9]] # or Array %>
87
- <%= line_chart [{:name => "Series A", :data => series_a, {:name => "Series B", :data => series_b}] %>
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
- var clone = function(obj) {
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 DECIMAL_SEPARATOR, ISO8601_PATTERN;
50
- ISO8601_PATTERN = /(\d\d\d\d)(\-)?(\d\d)(\-)?(\d\d)(T)?(\d\d)(:)?(\d\d)?(:)?(\d\d)?([\.,]\d+)?($|Z|([\+\-])(\d\d)(:)?(\d\d)?)/i;
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
- var parseISO8601 = function(input) {
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]') return input;
57
- if (type !== '[object String]') return;
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]) offset += parseInt(matches[17], 10);
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
- // source
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
- var renderLineChart = function(element, series, opts) {
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 == 1) {
146
+ if (series.length === 1) {
142
147
  options.legend = {enabled: false};
143
148
  }
144
149
  $(element).highcharts(options);
145
150
  };
146
151
 
147
- var renderPieChart = function(element, series, opts) {
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
- var renderColumnChart = function(element, series, opts) {
158
- var options = jsOptions(opts), data, i, j;
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
- categories.push(i);
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 == 1) {
200
+ if (series.length === 1) {
195
201
  options.legend.enabled = false;
196
202
  }
197
203
  $(element).highcharts(options);
198
204
  };
199
- }
200
- else { // Google charts
201
-
205
+ } else if ("google" in window) { // Google charts
206
+ // load from google
202
207
  var loaded = false;
203
- google.setOnLoadCallback( function() {
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
- rows2.push([(columnType === "datetime") ? new Date(toFloat(i)) : i].concat(rows[i]));
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
- var renderLineChart = function(element, series, opts) {
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 == 1) {
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
- var renderPieChart = function(element, series, opts) {
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
- var renderColumnChart = function(element, series, opts) {
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 == 1) {
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 chartError = function(element) {
344
- element.innerHTML = "Error Loading Chart";
345
- element.style.color = "red";
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
- var getJSON = function(element, url, success) {
349
- // TODO no jquery
350
- // TODO parse JSON in older browsers
351
- // https://raw.github.com/douglascrockford/JSON-js/master/json2.js
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
- chartError(element);
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
- // not working all the time
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
- // TODO catch errors for callback
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
- // right format
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
- return series;
401
+ function isArray(variable) {
402
+ return Object.prototype.toString.call(variable) === "[object Array]";
417
403
  }
418
404
 
419
- var toStr = function(n) {
405
+ // type conversions
406
+
407
+ function toStr(n) {
420
408
  return "" + n;
421
409
  }
422
410
 
423
- var toFloat = function(n) {
411
+ function toFloat(n) {
424
412
  return parseFloat(n);
425
- };
413
+ }
426
414
 
427
- var toDate = function(n) {
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
- var toArr = function(n) {
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
- var processLineData = function(element, data, opts) {
456
- renderLineChart(element, standardSeries(data, true), opts);
442
+ // process data
443
+
444
+ function sortByTime(a, b) {
445
+ return a[0].getTime() - b[0].getTime();
457
446
  }
458
447
 
459
- var processColumnData = function(element, data, opts) {
460
- renderColumnChart(element, standardSeries(data, false), opts);
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
- var processPieData = function(element, data, opts) {
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);
@@ -1,3 +1,3 @@
1
1
  module Chartkick
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
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.2
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-10 00:00:00.000000000 Z
11
+ date: 2013-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties