chartkick 0.0.1 → 0.0.2

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: 17460bd45d49b250595d30520c3fcaa24bdbbd37
4
- data.tar.gz: 8b02dd2a160331f62fd65e5ecf10e263ee49e6e6
3
+ metadata.gz: 7cff1f5410c612571c17f1d8e2f4dd0fef8a2a3f
4
+ data.tar.gz: a4b70704b7e4b5c233382dda51bb9a45a2b65f5c
5
5
  SHA512:
6
- metadata.gz: 1a671fb0e0386dbb8d4987729735740ba66400f4f127e6710fa79b8e232f2de654d37ffa36997b9229be8d02322cc8498cec936c68bb31388c25f85c401e9e36
7
- data.tar.gz: 9da602eeabb3002b50c63d48faae4c22894115be6ec10735b597ea62309f06b9852f9b0540f2c4e122aa875564c1b84e571e90c31794206f0697d87089dc831d
6
+ metadata.gz: a2b0e9e917045bcafc1069ef5fc3743de290c4ae42b95adb3b96b403450f334336769144a638390e4b28f3587a734edc292792ad756b879312596ffb3a308177
7
+ data.tar.gz: 9a40777c914f017f637da7af79c4da86d87ba94de53dacf271a852d7097ac7c5ee92bf06cbf919db01033499f9f89b9b624ed9ad196d1a84e9c9c225494dd096
data/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # Chartkick
2
2
 
3
- Create beautiful Javascript charts with one line of Ruby
3
+ Create beautiful Javascript charts with one line of Ruby. No more fighting with charting libraries!
4
4
 
5
- [Demo](http://ankane.github.io/chartkick/)
5
+ [See it in action](http://ankane.github.io/chartkick/)
6
6
 
7
- :two_hearts: A perfect companion to [groupdate](http://ankane.github.io/groupdate/)
7
+ Works with Rails 3.1+ and most browsers (including IE 6)
8
8
 
9
- Works with Rails 3.0+
9
+ :two_hearts: A perfect companion to [groupdate](http://ankane.github.io/groupdate/)
10
10
 
11
11
  ## Usage
12
12
 
@@ -19,7 +19,7 @@ Line chart
19
19
  Pie chart
20
20
 
21
21
  ```erb
22
- <%= pie_chart Goal.group("goals.name").count %>
22
+ <%= pie_chart Goal.group("name").count %>
23
23
  ```
24
24
 
25
25
  Column chart
@@ -28,18 +28,71 @@ Column chart
28
28
  <%= column_chart Task.group_by_hour_of_day(:created_at).count %>
29
29
  ```
30
30
 
31
- Multiple series (line chart only)
31
+ Multiple series (except pie chart)
32
32
 
33
33
  ```erb
34
34
  <%= line_chart Goal.all.map{|goal| {:name => goal.name, :data => goal.feats.group_by_week(:created_at).count } } %>
35
35
  ```
36
36
 
37
- Customize (id and height)
37
+ ### Say Goodbye To Timeouts
38
+
39
+ Make your pages load super fast and stop worrying about timeouts. Give each chart its own endpoint.
40
+
41
+ ```erb
42
+ <%= line_chart completed_tasks_charts_path %>
43
+ ```
44
+
45
+ And in your controller, pass the data as JSON.
46
+
47
+ ```ruby
48
+ class ChartsController < ApplicationController
49
+ def completed_tasks
50
+ render :json => Task.group_by_day(:completed_at).count
51
+ end
52
+ end
53
+ ```
54
+
55
+ **Note:** This feature requires jQuery at the moment.
56
+
57
+ ### Options
58
+
59
+ id and height
38
60
 
39
61
  ```erb
40
62
  <%= line_chart User.group_by_day(:created_at).count, :id => "users-chart", :height => "500px" %>
41
63
  ```
42
64
 
65
+ min and max values (except pie chart)
66
+
67
+ ```erb
68
+ <%= line_chart User.group_by_day(:created_at).count, :min => 1000, :max => 5000 %>
69
+ ```
70
+
71
+ **Note:** min defaults to 0 - use `:min => nil` to allow the chart to choose the minimum.
72
+
73
+ ### Data
74
+
75
+ Pass data as a Hash or Array
76
+
77
+ ```erb
78
+ <%= pie_chart({"Football" => 10, "Basketball" => 5}) %>
79
+ <%= pie_chart [["Football", 10], ["Basketball", 5]] %>
80
+ ```
81
+
82
+ For multiple series, use the format
83
+
84
+ ```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}] %>
88
+ ```
89
+
90
+ Times can be a time, a timestamp, or a string (strings are parsed)
91
+
92
+ ```erb
93
+ <% line_chart({20.day.ago => 5, 1368174456 => 4, "2013-05-07 00:00:00 UTC" => 7}) %>
94
+ ```
95
+
43
96
  ## Installation
44
97
 
45
98
  Add this line to your application's Gemfile:
@@ -56,6 +109,29 @@ And add the javascript files to your views.
56
109
  <%= javascript_include_tag "//www.google.com/jsapi", "chartkick" %>
57
110
  ```
58
111
 
112
+ If you prefer Highcharts, use:
113
+
114
+ ```erb
115
+ <%= javascript_include_tag "path/to/highcharts.js", "chartkick" %>
116
+ ```
117
+
118
+ ## No Ruby? No Problem
119
+
120
+ Chartkick doesn’t require Ruby.
121
+
122
+ ```html
123
+ <script src="/path/to/chartkick.js"></script>
124
+ <div id="chart-1" style="height: 300px;"></div>
125
+ <script>
126
+ var chart = document.getElementById("chart-1");
127
+ new Chartkick.PieChart(chart, {"Football": 45, "Soccer": 56, "Basketball": 98});
128
+ // or remote
129
+ new Chartkick.LineChart(chart, "/charts/stocks");
130
+ </script>
131
+ ```
132
+
133
+ Download [chartkick.js](https://raw.github.com/ankane/chartkick/master/app/assets/javascripts/chartkick.js)
134
+
59
135
  ## Contributing
60
136
 
61
137
  1. Fork it
@@ -1,125 +1,482 @@
1
- /*jslint browser: true, indent: 2 */
1
+ /*jslint browser: true, indent: 2, plusplus: true */
2
2
  /*global google*/
3
3
 
4
4
  (function() {
5
5
  'use strict';
6
6
 
7
- google.load("visualization", "1.0", {"packages": ["corechart"]});
7
+ // vendor
8
8
 
9
- // Set chart options
10
- var defaultOptions = {
11
- fontName: "'Lucida Grande', 'Lucida Sans Unicode', Verdana, Arial, Helvetica, sans-serif",
12
- pointSize: 6,
13
- legend: {
14
- textStyle: {
15
- fontSize: 12,
16
- color: "#444"
9
+ // http://stackoverflow.com/questions/728360/most-elegant-way-to-clone-a-javascript-object
10
+ var clone = function(obj) {
11
+ var copy, i, attr, len;
12
+
13
+ // Handle the 3 simple types, and null or undefined
14
+ if (null === obj || "object" !== typeof obj) {
15
+ return obj;
16
+ }
17
+
18
+ // Handle Date
19
+ if (obj instanceof Date) {
20
+ copy = new Date();
21
+ copy.setTime(obj.getTime());
22
+ return copy;
23
+ }
24
+
25
+ // Handle Array
26
+ if (obj instanceof Array) {
27
+ copy = [];
28
+ for (i = 0, len = obj.length; i < len; i++) {
29
+ copy[i] = clone(obj[i]);
30
+ }
31
+ return copy;
32
+ }
33
+
34
+ // Handle Object
35
+ if (obj instanceof Object) {
36
+ copy = {};
37
+ for (attr in obj) {
38
+ if (obj.hasOwnProperty(attr)) {
39
+ copy[attr] = clone(obj[attr]);
40
+ }
41
+ }
42
+ return copy;
43
+ }
44
+
45
+ throw new Error("Unable to copy obj! Its type isn't supported.");
46
+ };
47
+
48
+ // 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);
52
+
53
+ var parseISO8601 = function(input) {
54
+ var day, hour, matches, milliseconds, minutes, month, offset, result, seconds, type, year;
55
+ type = Object.prototype.toString.call(input);
56
+ if (type === '[object Date]') return input;
57
+ if (type !== '[object String]') return;
58
+ if (matches = input.match(ISO8601_PATTERN)) {
59
+ year = parseInt(matches[1], 10);
60
+ month = parseInt(matches[3], 10) - 1;
61
+ day = parseInt(matches[5], 10);
62
+ hour = parseInt(matches[7], 10);
63
+ minutes = matches[9] ? parseInt(matches[9], 10) : 0;
64
+ seconds = matches[11] ? parseInt(matches[11], 10) : 0;
65
+ milliseconds = matches[12] ? parseFloat(DECIMAL_SEPARATOR + matches[12].slice(1)) * 1000 : 0;
66
+ result = Date.UTC(year, month, day, hour, minutes, seconds, milliseconds);
67
+ if (matches[13] && matches[14]) {
68
+ offset = matches[15] * 60;
69
+ if (matches[17]) offset += parseInt(matches[17], 10);
70
+ offset *= matches[14] === '-' ? -1 : 1;
71
+ result -= offset * 60 * 1000;
72
+ }
73
+ return new Date(result);
74
+ }
75
+ };
76
+
77
+ // source
78
+
79
+ if ("Highcharts" in window) {
80
+
81
+ var defaultOptions = {
82
+ xAxis: {
83
+ labels: {
84
+ style: {
85
+ fontSize: "12px"
86
+ }
87
+ }
17
88
  },
18
- alignment: "center"
19
- },
20
- curveType: "function",
21
- hAxis: {
22
- textStyle: {
23
- color: "#666",
24
- fontSize: 12
89
+ yAxis: {
90
+ title: {
91
+ text: null
92
+ },
93
+ labels: {
94
+ style: {
95
+ fontSize: "12px"
96
+ }
97
+ },
98
+ min: 0
25
99
  },
26
- gridlines: {
27
- color: "transparent"
100
+ title: {
101
+ text: null
28
102
  },
29
- baselineColor: "#ccc"
30
- },
31
- vAxis: {
32
- textStyle: {
33
- color: "#666",
34
- fontSize: 12
103
+ credits: {
104
+ enabled: false
35
105
  },
36
- baselineColor: "#ccc",
37
- viewWindow: {
38
- min: 0
106
+ legend: {
107
+ borderWidth: 0
108
+ },
109
+ tooltip: {
110
+ style: {
111
+ fontSize: "12px"
112
+ }
39
113
  }
40
- },
41
- tooltip: {
42
- textStyle: {
43
- color: "#666",
44
- fontSize: 12
45
- }
46
- }
47
- }, Chartkick = {
48
- LineChart: function(elementId, series) {
49
- google.setOnLoadCallback(function() {
50
- // Create the data table.
51
- var data = new google.visualization.DataTable(), rows = {}, i, j, k, s, d, rows2 = [], options, chart;
52
-
53
- data.addColumn("datetime", "");
54
- for (i = 0; i < series.length; i += 1) {
55
- s = series[i];
56
- data.addColumn("number", s.name);
57
-
58
- for (j = 0; j < s.data.length; j += 1) {
59
- d = s.data[j];
60
- if (!rows[d[0]]) {
61
- rows[d[0]] = new Array(series.length);
62
- }
63
- rows[d[0]][i] = d[1];
114
+ };
115
+
116
+ var jsOptions = function(opts) {
117
+ var options = clone(defaultOptions);
118
+ if ("min" in opts) {
119
+ options.yAxis.min = opts.min;
120
+ }
121
+ if ("max" in opts) {
122
+ options.yAxis.max = opts.max;
123
+ }
124
+ return options;
125
+ }
126
+
127
+ var renderLineChart = function(element, series, opts) {
128
+ var options = jsOptions(opts), data, i, j;
129
+ options.xAxis.type = "datetime";
130
+ options.chart = {type: "spline"};
131
+
132
+ for (i = 0; i < series.length; i++) {
133
+ data = series[i].data;
134
+ for (j = 0; j < data.length; j++) {
135
+ data[j][0] = data[j][0].getTime();
136
+ }
137
+ series[i].marker = {symbol: "circle"};
138
+ }
139
+ options.series = series;
140
+
141
+ if (series.length == 1) {
142
+ options.legend = {enabled: false};
143
+ }
144
+ $(element).highcharts(options);
145
+ };
146
+
147
+ var renderPieChart = function(element, series, opts) {
148
+ var options = jsOptions(opts);
149
+ options.series = [{
150
+ type: "pie",
151
+ name: "Value",
152
+ data: series
153
+ }];
154
+ $(element).highcharts(options);
155
+ };
156
+
157
+ var renderColumnChart = function(element, series, opts) {
158
+ var options = jsOptions(opts), data, i, j;
159
+ options.chart = {type: "column"};
160
+
161
+ var i, j, s, d, rows = [];
162
+ for (i = 0; i < series.length; i++) {
163
+ s = series[i];
164
+
165
+ for (j = 0; j < s.data.length; j++) {
166
+ d = s.data[j];
167
+ if (!rows[d[0]]) {
168
+ rows[d[0]] = new Array(series.length);
64
169
  }
170
+ rows[d[0]][i] = d[1];
65
171
  }
172
+ }
173
+
174
+ var categories = [];
175
+ for (i in rows) {
176
+ categories.push(i);
177
+ }
178
+ options.xAxis.categories = categories;
66
179
 
67
- // columns
68
- rows2 = [];
69
- for (k in rows) {
70
- rows2.push([new Date(k * 1000)].concat(rows[k]));
180
+ var newSeries = [];
181
+ for (i = 0; i < series.length; i++) {
182
+ d = [];
183
+ for (j = 0; j < categories.length; j++) {
184
+ d.push(rows[categories[j]][i]);
71
185
  }
72
- data.addRows(rows2);
73
186
 
74
- options = defaultOptions; // TODO clone
75
- if (series.length > 1) {
76
- options.legend.position = "right";
77
- } else {
187
+ newSeries.push({
188
+ name: series[i].name,
189
+ data: d
190
+ });
191
+ }
192
+ options.series = newSeries;
193
+
194
+ if (series.length == 1) {
195
+ options.legend.enabled = false;
196
+ }
197
+ $(element).highcharts(options);
198
+ };
199
+ }
200
+ else { // Google charts
201
+
202
+ var loaded = false;
203
+ google.setOnLoadCallback( function() {
204
+ loaded = true;
205
+ });
206
+ google.load("visualization", "1.0", {"packages": ["corechart"]});
207
+
208
+ var waitForLoaded = function(callback) {
209
+ google.setOnLoadCallback(callback); // always do this to prevent race conditions (watch out for other issues due to this)
210
+ if (loaded) {
211
+ callback();
212
+ }
213
+ }
214
+
215
+ // Set chart options
216
+ var defaultOptions = {
217
+ fontName: "'Lucida Grande', 'Lucida Sans Unicode', Verdana, Arial, Helvetica, sans-serif",
218
+ pointSize: 6,
219
+ legend: {
220
+ textStyle: {
221
+ fontSize: 12,
222
+ color: "#444"
223
+ },
224
+ alignment: "center",
225
+ position: "right"
226
+ },
227
+ curveType: "function",
228
+ hAxis: {
229
+ textStyle: {
230
+ color: "#666",
231
+ fontSize: 12
232
+ },
233
+ gridlines: {
234
+ color: "transparent"
235
+ },
236
+ baselineColor: "#ccc"
237
+ },
238
+ vAxis: {
239
+ textStyle: {
240
+ color: "#666",
241
+ fontSize: 12
242
+ },
243
+ baselineColor: "#ccc",
244
+ viewWindow: {
245
+ min: 0
246
+ }
247
+ },
248
+ tooltip: {
249
+ textStyle: {
250
+ color: "#666",
251
+ fontSize: 12
252
+ }
253
+ }
254
+ }
255
+
256
+ // cant use object as key
257
+ var createDataTable = function(series, columnType) {
258
+ var data = new google.visualization.DataTable();
259
+ data.addColumn(columnType, "");
260
+
261
+ var i, j, s, d, key, rows = [];
262
+ for (i = 0; i < series.length; i++) {
263
+ s = series[i];
264
+ data.addColumn("number", s.name);
265
+
266
+ for (j = 0; j < s.data.length; j++) {
267
+ d = s.data[j];
268
+ key = (columnType === "datetime") ? d[0].getTime() : d[0];
269
+ if (!rows[key]) {
270
+ rows[key] = new Array(series.length);
271
+ }
272
+ rows[key][i] = toFloat(d[1]);
273
+ }
274
+ }
275
+
276
+ var rows2 = [];
277
+ for (i in rows) {
278
+ rows2.push([(columnType === "datetime") ? new Date(toFloat(i)) : i].concat(rows[i]));
279
+ }
280
+ data.addRows(rows2);
281
+
282
+ return data;
283
+ };
284
+
285
+ var jsOptions = function(opts) {
286
+ var options = clone(defaultOptions);
287
+ if ("min" in opts) {
288
+ options.vAxis.viewWindow.min = opts.min;
289
+ }
290
+ if ("max" in opts) {
291
+ options.vAxis.viewWindow.max = opts.max;
292
+ }
293
+ return options;
294
+ }
295
+
296
+ var renderLineChart = function(element, series, opts) {
297
+ waitForLoaded(function() {
298
+ var data = createDataTable(series, "datetime");
299
+
300
+ var options = jsOptions(opts);
301
+ if (series.length == 1) {
78
302
  options.legend.position = "none";
79
303
  }
80
- options.chartArea = null;
81
304
 
82
- chart = new google.visualization.LineChart(document.getElementById(elementId));
305
+ var chart = new google.visualization.LineChart(element);
83
306
  chart.draw(data, options);
84
- });
85
- },
86
- PieChart: function(elementId, series) {
87
- google.setOnLoadCallback(function() {
88
- // Create the data table.
89
- var data = new google.visualization.DataTable();
307
+ })
308
+ };
90
309
 
91
- // columns
310
+ var renderPieChart = function(element, series, opts) {
311
+ waitForLoaded(function() {
312
+ var data = new google.visualization.DataTable();
92
313
  data.addColumn("string", "");
93
314
  data.addColumn("number", "Value");
94
315
  data.addRows(series);
95
316
 
96
- var options = defaultOptions; // TODO clone
97
- options.legend.position = "right";
317
+ var options = jsOptions(opts);
98
318
  options.chartArea = {
99
319
  top: "10%",
100
320
  height: "80%"
101
321
  };
102
322
 
103
- var chart = new google.visualization.PieChart(document.getElementById(elementId));
323
+ var chart = new google.visualization.PieChart(element);
104
324
  chart.draw(data, options);
105
325
  });
106
- },
107
- ColumnChart: function(elementId, series) {
108
- google.setOnLoadCallback(function() {
109
- var data = new google.visualization.DataTable();
326
+ };
110
327
 
111
- // columns
112
- data.addColumn("string", "");
113
- data.addColumn("number", "Value");
114
- data.addRows(series);
328
+ var renderColumnChart = function(element, series, opts) {
329
+ waitForLoaded(function() {
330
+ var data = createDataTable(series, "string");
115
331
 
116
- var options = defaultOptions; // TODO clone
117
- options.legend.position = "none";
118
- options.chartArea = null;
332
+ var options = jsOptions(opts);
333
+ if (series.length == 1) {
334
+ options.legend.position = "none";
335
+ }
119
336
 
120
- var chart = new google.visualization.ColumnChart(document.getElementById(elementId));
337
+ var chart = new google.visualization.ColumnChart(element);
121
338
  chart.draw(data, options);
122
339
  });
340
+ };
341
+ }
342
+
343
+ var chartError = function(element) {
344
+ element.innerHTML = "Error Loading Chart";
345
+ element.style.color = "red";
346
+ };
347
+
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
352
+ $.ajax({
353
+ dataType: "json",
354
+ url: url,
355
+ success: success,
356
+ error: function() {
357
+ chartError(element);
358
+ }
359
+ });
360
+ };
361
+
362
+ // not working all the time
363
+ var errorCatcher = function(element, data, opts, callback) {
364
+ try {
365
+ callback(element, data, opts);
366
+ } catch (err) {
367
+ chartError(element);
368
+ throw err;
369
+ }
370
+ };
371
+
372
+ // TODO catch errors for callback
373
+ var fetchDataSource = function(element, dataSource, opts, callback) {
374
+ if (typeof dataSource === "string") {
375
+ getJSON(element, dataSource, function(data, textStatus, jqXHR) {
376
+ errorCatcher(element, data, opts, callback);
377
+ });
378
+ }
379
+ else {
380
+ errorCatcher(element, dataSource, opts, callback);
381
+ }
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
+ }
395
+
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
+ }
415
+
416
+ return series;
417
+ }
418
+
419
+ var toStr = function(n) {
420
+ return "" + n;
421
+ }
422
+
423
+ var toFloat = function(n) {
424
+ return parseFloat(n);
425
+ };
426
+
427
+ var toDate = function(n) {
428
+ if (typeof n !== "object") {
429
+ if (typeof n === "number") {
430
+ n = new Date(n * 1000); // ms
431
+ }
432
+ else { // str
433
+ // try our best to get the str into iso8601
434
+ // TODO be smarter about this
435
+ var str = n.replace(/ /, "T").replace(" ", "").replace("UTC", "Z");
436
+ n = parseISO8601(str) || new Date(n);
437
+ }
438
+ }
439
+ return n;
440
+ };
441
+
442
+ var toArr = function(n) {
443
+ if (!isArray(n)) {
444
+ var arr = [], i;
445
+ for (i in n) {
446
+ if (n.hasOwnProperty(i)) {
447
+ arr.push([i, n[i]]);
448
+ }
449
+ }
450
+ n = arr;
451
+ }
452
+ return n;
453
+ }
454
+
455
+ var processLineData = function(element, data, opts) {
456
+ renderLineChart(element, standardSeries(data, true), opts);
457
+ }
458
+
459
+ var processColumnData = function(element, data, opts) {
460
+ renderColumnChart(element, standardSeries(data, false), opts);
461
+ }
462
+
463
+ var processPieData = function(element, data, opts) {
464
+ var perfectData = toArr(data), i;
465
+ for (i = 0; i < perfectData.length; i++) {
466
+ perfectData[i] = [toStr(perfectData[i][0]), toFloat(perfectData[i][1])];
467
+ }
468
+ renderPieChart(element, perfectData, opts);
469
+ }
470
+
471
+ var Chartkick = {
472
+ LineChart: function(element, dataSource, opts) {
473
+ fetchDataSource(element, dataSource, opts || {}, processLineData);
474
+ },
475
+ ColumnChart: function(element, dataSource, opts) {
476
+ fetchDataSource(element, dataSource, opts || {}, processColumnData);
477
+ },
478
+ PieChart: function(element, dataSource, opts) {
479
+ fetchDataSource(element, dataSource, opts || {}, processPieData);
123
480
  }
124
481
  };
125
482
 
@@ -1,46 +1,33 @@
1
1
  module Chartkick
2
2
  module Helper
3
3
 
4
- def line_chart(series, options = {})
5
- unless series.is_a?(Array) and series[0].is_a?(Hash)
6
- series = [{:name => "Value", :data => series}]
7
- end
8
- series.each do |s|
9
- s[:data] = s[:data].map{|k, v| [k.is_a?(Time) ? k : Time.parse(k), v] }.sort_by{|k, v| k }.map{|k, v| [k.to_i, v.to_f] }
10
- end
11
-
12
- html, element_id = chartkick_div(options)
13
- html << content_tag(:script) do
14
- concat "new Chartkick.LineChart(#{element_id.to_json}, #{series.to_json});".html_safe
15
- end
16
- html
4
+ def line_chart(data_source, options = {})
5
+ chartkick_chart "LineChart", data_source, options
17
6
  end
18
7
 
19
- def pie_chart(series, options = {})
20
- series = series.to_a
21
- html, element_id = chartkick_div(options)
22
- html << content_tag(:script) do
23
- concat "new Chartkick.PieChart(#{element_id.to_json}, #{series.to_json});".html_safe
24
- end
25
- html
8
+ def pie_chart(data_source, options = {})
9
+ chartkick_chart "PieChart", data_source, options
26
10
  end
27
11
 
28
- def column_chart(series, options = {})
29
- series = series.map{|k,v| [k.to_s, v.to_f] }
30
- html, element_id = chartkick_div(options)
31
- html << content_tag(:script) do
32
- concat "new Chartkick.ColumnChart(#{element_id.to_json}, #{series.to_json});".html_safe
33
- end
34
- html
12
+ def column_chart(data_source, options = {})
13
+ chartkick_chart "ColumnChart", data_source, options
35
14
  end
36
15
 
37
16
  private
38
17
 
39
- def chartkick_div(options)
18
+ def chartkick_chart(klass, data_source, options, &block)
40
19
  @chartkick_chart_id ||= 0
41
- element_id = options[:id] || "chart-#{@chartkick_chart_id += 1}"
42
- height = options[:height] || "300px"
43
- [content_tag(:div, :id => element_id, :style => "height: #{height};") {}, element_id]
20
+ options = options.dup
21
+ element_id = options.delete(:id) || "chart-#{@chartkick_chart_id += 1}"
22
+ height = options.delete(:height) || "300px"
23
+
24
+ # don't quote font-family names due to rails escaping
25
+ html = content_tag :div, :id => element_id, :style => "height: #{height}; text-align: center; color: #999; line-height: #{height}; font-size: 14px; font-family: Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif;" do
26
+ concat "Loading..."
27
+ end
28
+ html += javascript_tag do
29
+ concat "new Chartkick.#{klass}(document.getElementById(#{element_id.to_json}), #{data_source.to_json}, #{options.to_json});".html_safe
30
+ end
44
31
  end
45
32
 
46
33
  end
@@ -1,3 +1,3 @@
1
1
  module Chartkick
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
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.1
4
+ version: 0.0.2
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-07 00:00:00.000000000 Z
11
+ date: 2013-05-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties