pizza-rails 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6f24edb8d0ccb3d5eb703a1da92860069662b208
4
+ data.tar.gz: 8b249f04b5dccb472557a52f1baf620dbcb05095
5
+ SHA512:
6
+ metadata.gz: 571f09781888bd95439054d38e56b83c32ed36dac03434f7a7b55698dac05f02c84d0a6bd23540c436b1de8394aba05fb3543e4e22ecb15b4f1de5fa689645ba
7
+ data.tar.gz: 18bc7e8c8b17dc2d1950c3e4b42a6293720b6c124129b3e95274202856078feabeb81b2047f19ec7814333e8a1200a699544514ed77d3f269e433eb86f88d921
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pizza-rails.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Eric Roberts
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,46 @@
1
+ # Pizza::Rails
2
+
3
+ Pizza is a responsive pie, donut, bar, and line graph charting library based on the Snap SVG framework from Adobe. It focuses on easy integration via HTML markup and CSS instead of JavaScript objects, although you can pass JavaScript objects to Pizza as well.
4
+
5
+ http://zurb.com/playground/pizza-amore-charts-and-graphs
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'pizza-rails', github: 'ericroberts/pizza-rails'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install pizza-rails
20
+
21
+ ## Usage
22
+
23
+ Add the following to your application.js
24
+
25
+ //= require snap
26
+ //= require pizza
27
+
28
+ Add the following to your application.css
29
+
30
+ *= require pizza
31
+
32
+ Or, if using SCSS:
33
+
34
+ @import "pizza";
35
+
36
+ If you're using SASS/SCSS there are more details for styling you can checkout at the full docs below.
37
+
38
+ See full docs at http://zurb.com/playground/pizza-amore-charts-and-graphs
39
+
40
+ ## Contributing
41
+
42
+ 1. Fork it
43
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
44
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
45
+ 4. Push to the branch (`git push origin my-new-feature`)
46
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,8 @@
1
+ require "pizza/rails/version"
2
+
3
+ module Pizza
4
+ module Rails
5
+ class Engine < ::Rails::Engine
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ module Pizza
2
+ module Rails
3
+ VERSION = "0.2.1"
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pizza/rails/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "pizza-rails"
8
+ spec.version = Pizza::Rails::VERSION
9
+ spec.authors = ["Eric Roberts"]
10
+ spec.email = ["ericroberts@gmail.com"]
11
+ spec.description = %q{Pizza is a responsive pie, donut, bar, and line graph charting library based on the Snap SVG framework from Adobe. It focuses on easy integration via HTML markup and CSS instead of JavaScript objects, although you can pass JavaScript objects to Pizza as well.}
12
+ spec.summary = %q{Rails wrapper for Pizza by Zurb}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_development_dependency "bundler", "~> 1.3"
20
+ spec.add_development_dependency "rake"
21
+ spec.add_dependency "railties", ">= 3"
22
+ spec.add_dependency "snapsvg-rails", ">= 0.2.0"
23
+ end
@@ -0,0 +1,2 @@
1
+ //= require ./pizza/pizza
2
+ //= require_tree ./pizza
@@ -0,0 +1,208 @@
1
+ $.extend(Pizza, {
2
+ bar: function (legend) {
3
+ var settings = legend.data('settings'),
4
+ svg = this.svg(legend, settings),
5
+ data = legend.data('graph-data'),
6
+ current_offset = 0,
7
+ container = $(this.identifier(legend)),
8
+ base_width = container.outerWidth(),
9
+ base_height = container.outerHeight(),
10
+ max = min = 0,
11
+ total = 0,
12
+ spacer = settings.bar_spacer * (settings.bar_spacer/ base_width),
13
+ interval = (base_width - (data.length * spacer)) / data.length;
14
+
15
+ if (interval < 10) {
16
+ spacer = 1;
17
+ interval = (base_width - (data.length * spacer)) / data.length;
18
+ }
19
+
20
+ for (var i = 0; i < data.length; i++) {
21
+ if (max < data[i].value) max = data[i].value;
22
+ if (min > data[i].value) min = data[i].value;
23
+ total += data[i].value;
24
+ }
25
+
26
+ var existing_group = $('g[data-id=bars]', svg);
27
+
28
+ if (existing_group.length > 0) {
29
+ var g = existing_group[0];
30
+ } else {
31
+ var g = this.svg_obj('g');
32
+ g.setAttribute('data-id', 'bars');
33
+ }
34
+
35
+ if (settings.show_grid) {
36
+ this.assemble_grid(svg, min, max, base_width, base_height, settings);
37
+ }
38
+
39
+ g.setAttribute('transform', 'translate(0, ' + (base_height) +') scale(1, -1)');
40
+
41
+ for (var i = 0; i < data.length; i++) {
42
+ var y = (base_height) * (data[i].value / max);
43
+
44
+ var existing_rect = $('rect[data-id=r' + i +']', g);
45
+
46
+ if (existing_rect.length > 0) {
47
+ var rect = existing_rect[0];
48
+ } else {
49
+ var rect = this.svg_obj('rect');
50
+ rect.setAttribute('data-id', 'r' + i);
51
+ }
52
+
53
+ if (current_offset === 0) {
54
+ var new_offset = current_offset;
55
+ } else {
56
+ var new_offset = current_offset + spacer;
57
+ }
58
+
59
+ rect.setAttribute('data-y', y);
60
+
61
+ this.set_attr(rect, {
62
+ x : new_offset,
63
+ y : 0,
64
+ width : interval,
65
+ height : 0,
66
+ fill: data[i].color,
67
+ stroke: settings.stroke_color,
68
+ 'strokeWidth': settings.stroke_width
69
+ });
70
+
71
+ Snap(rect).animate({height: y}, 1500, mina[settings.animation_type]);
72
+
73
+ current_offset = new_offset + interval;
74
+
75
+ if (existing_group.length < 1) {
76
+ g.appendChild(rect);
77
+ this.animate_bar(Snap(rect), y, settings);
78
+ }
79
+ }
80
+
81
+ if (existing_group.length < 1) {
82
+ svg.appendChild(g);
83
+ }
84
+
85
+ return [legend, svg];
86
+ },
87
+
88
+ animate_bar : function (el, y, settings) {
89
+ var self = this,
90
+ $el = $(el),
91
+ new_y = y + 15;
92
+
93
+ el.hover(function (e) {
94
+ var path = Snap(e.target);
95
+
96
+ path.animate({
97
+ height: new_y
98
+ }, settings.animation_speed, mina[settings.animation_type]);
99
+
100
+ }, function (e) {
101
+ var path = Snap(e.target);
102
+
103
+ path.animate({
104
+ height: y
105
+ }, settings.animation_speed, mina[settings.animation_type]);
106
+ });
107
+
108
+ },
109
+
110
+ assemble_grid : function (svg, min, max, width, height, settings) {
111
+ var existing_group = $('g[data-id=bars]', svg);
112
+
113
+ if (existing_group.length > 0) {
114
+ var line_g = $('g[data-id=grid]', svg)[0],
115
+ text_g = $('g[data-id=labels]', svg)[0];
116
+ } else {
117
+ var line_g = this.svg_obj('g'),
118
+ text_g = this.svg_obj('g');
119
+
120
+ line_g.setAttribute('data-id', 'grid');
121
+ text_g.setAttribute('data-id', 'labels');
122
+ }
123
+
124
+ var ticks = this.ticks(min, max, settings.bar_intervals),
125
+ ticks_length = i = ticks.length,
126
+ interval = height/(ticks_length-1),
127
+ total_tick_height = 0;
128
+
129
+ while (i--) {
130
+ if (existing_group.length > 0) {
131
+ var line = $('line[data-id=l' + i + ']', line_g)[0],
132
+ text = $('text[data-id=t' + i + ']', text_g)[0];
133
+ } else {
134
+ var line = this.svg_obj('line'),
135
+ text = this.svg_obj('text');
136
+
137
+ line.setAttribute('data-id', 'l' + i);
138
+ text.setAttribute('data-id', 't' + i);
139
+ }
140
+
141
+ var line_height = total_tick_height + interval;
142
+
143
+ this.set_attr(line, {
144
+ x1 : 0,
145
+ x2 : width,
146
+ y1 : line_height,
147
+ y2 : line_height,
148
+ stroke : 'gray',
149
+ 'stroke-width' : 1,
150
+ 'stroke-dasharray' : '5,5'
151
+ })
152
+ .set_attr(text, {
153
+ x : -8,
154
+ y : line_height + 5,
155
+ 'text-anchor': 'end'
156
+ });
157
+
158
+ text.innerHTML = ticks[i];
159
+
160
+ if (existing_group.length < 1) {
161
+ line_g.appendChild(line);
162
+ text_g.appendChild(text);
163
+ }
164
+
165
+ total_tick_height = line_height;
166
+ }
167
+
168
+ line_g.setAttribute('transform', 'translate(0, -' + total_tick_height / ticks_length + ')');
169
+ text_g.setAttribute('transform', 'translate(0, -' + total_tick_height / ticks_length + ')');
170
+
171
+ if (existing_group.length < 1) {
172
+ svg.appendChild(line_g);
173
+ svg.appendChild(text_g);
174
+ }
175
+
176
+ },
177
+
178
+ bar_events : function () {
179
+ var self = this;
180
+
181
+ $(this.scope).on('mouseenter.pizza mouseleave.pizza touchstart.pizza', '[data-bar-id] li', function (e) {
182
+ var parent = $(this).parent(),
183
+ path = $('#' + parent.attr('data-bar-id') + ' rect[data-id=r' + $(this).index() + ']')[0],
184
+ settings = $(this).parent().data('settings'),
185
+ new_height = parseInt(path.getAttribute('data-y'), 10) + 15;
186
+
187
+ if (/start/i.test(e.type)) {
188
+ $(path).siblings('rect').each(function () {
189
+ if (this.nodeName) {
190
+ Snap(path).animate({
191
+ height: path.getAttribute('data-y')
192
+ }, settings.animation_speed, mina[settings.animation_type]);
193
+ }
194
+ });
195
+ }
196
+
197
+ if (/enter|start/i.test(e.type)) {
198
+ Snap(path).animate({
199
+ height: new_height
200
+ }, settings.animation_speed, mina[settings.animation_type]);
201
+ } else {
202
+ Snap(path).animate({
203
+ height: path.getAttribute('data-y')
204
+ }, settings.animation_speed, mina[settings.animation_type]);
205
+ }
206
+ });
207
+ }
208
+ });
@@ -0,0 +1,259 @@
1
+ $.extend(Pizza, {
2
+ line : function (legend) {
3
+ var settings = legend.data('settings'),
4
+ svg = this.svg(legend, settings),
5
+ container = $(this.identifier(legend)),
6
+ width = container.outerWidth(),
7
+ height = container.outerHeight(),
8
+ data = legend.data('graph-data'),
9
+ max_x = max_y = min_x = min_y = total_x = total_y = 0,
10
+ i = data.length,
11
+ points = '';
12
+
13
+ for (var i = 0; i < data.length; i++) {
14
+ if (data[i].x > max_x) max_x = data[i].x;
15
+ if (data[i].y > max_y) max_y = data[i].y;
16
+ if (min_x > data[i].x) min_x = data[i].x;
17
+ if (min_y > data[i].y) min_y = data[i].y;
18
+ total_x += data[i].x;
19
+ total_y += data[i].y;
20
+ }
21
+
22
+ var existing_group = $('g[data-id=line]', svg);
23
+
24
+ if (existing_group.length > 0) {
25
+ var line_g = $('g[data-id=line]', svg)[0],
26
+ circle_g = $('g[data-id=points]', svg)[0],
27
+ polyline = $('path[data-id=path]', line_g)[0];
28
+ } else {
29
+ var polyline = this.svg_obj('path'),
30
+ line_g = this.svg_obj('g'),
31
+ circle_g = this.svg_obj('g');
32
+
33
+ line_g.setAttribute('data-id', 'line');
34
+ circle_g.setAttribute('data-id', 'points');
35
+ polyline.setAttribute('data-id', 'path');
36
+ }
37
+
38
+ for (var i = 0; i < data.length; i++) {
39
+ if (existing_group.length > 0) {
40
+ var circle = $('circle[data-id=c' + i + ']', circle_g)[0];
41
+ } else {
42
+ var circle = this.svg_obj('circle');
43
+
44
+ circle.setAttribute('data-id', 'c' + i);
45
+ }
46
+
47
+ var x = (data[i].x / max_x) * width,
48
+ y = (data[i].y / max_y) * height;
49
+
50
+ points += x + ',' + y + ' ';
51
+ this.set_attr(circle, {cx: x, cy: y,r: 0,fill: data[i.color],
52
+ 'data-value': data[i].x + ', ' + data[i].y,
53
+ 'data-tooltip': '',
54
+ 'title': data[i].x + ', ' + data[i].y,
55
+ 'class': 'has-tip tip-top'});
56
+
57
+ Snap(circle).animate({
58
+ r: 4
59
+ }, 1500, mina[settings.animation_type]);
60
+
61
+ this.animate(Snap(circle), x, y, settings, 2);
62
+
63
+ if (existing_group.length < 1) {
64
+ circle_g.appendChild(circle);
65
+ }
66
+ }
67
+
68
+ this.flip(circle_g, height);
69
+ this.flip(line_g, height);
70
+
71
+ if (settings.show_grid) {
72
+ this.assemble_grid_x(svg, min_x, max_x, width, height, settings);
73
+ this.assemble_grid_y(svg, min_y, max_y, width, height, settings);
74
+ }
75
+ var v = this.points_to_path(points);
76
+
77
+ this.set_attr(polyline, {d:v, fill: 'none', stroke: 'black', 'stroke-width': 2});
78
+
79
+ if (existing_group.length < 1) {
80
+ line_g.appendChild(polyline);
81
+ svg.appendChild(line_g);
82
+ }
83
+
84
+ if (existing_group.length < 1) {
85
+ svg.appendChild(circle_g);
86
+ }
87
+
88
+ return [legend, svg];
89
+ },
90
+
91
+ assemble_grid_x : function (svg, min, max, width, height, settings) {
92
+ var existing_group = $('g[data-id=gridx]', svg);
93
+
94
+ if (existing_group.length > 0) {
95
+ var line_g = existing_group[0],
96
+ text_g = $('g[data-id=labelx]', svg)[0];
97
+ } else {
98
+ var line_g = this.svg_obj('g'),
99
+ text_g = this.svg_obj('g');
100
+
101
+ line_g.setAttribute('data-id', 'gridx');
102
+ text_g.setAttribute('data-id', 'labelx');
103
+ }
104
+
105
+ var ticks = this.ticks(min, max, settings.bar_intervals).reverse(),
106
+ ticks_length = i = ticks.length,
107
+ total_tick_width = 0,
108
+ interval = width/(ticks_length-1);
109
+
110
+ while (i--) {
111
+ if (existing_group.length > 0) {
112
+ var line = $('line[data-id=l' + i + ']', line_g)[0],
113
+ text = $('text[data-id=t' + i + ']', text_g)[0];
114
+ } else {
115
+ var line = this.svg_obj('line'),
116
+ text = this.svg_obj('text');
117
+
118
+ line.setAttribute('data-id', 'l' + i);
119
+ text.setAttribute('data-id', 't' + i);
120
+ }
121
+
122
+ var line_width = total_tick_width + interval;
123
+
124
+ this.set_attr(line, {
125
+ x1 : line_width,
126
+ x2 : line_width,
127
+ y1 : 0,
128
+ y2 : height,
129
+ stroke : 'gray',
130
+ 'stroke-width' : 1,
131
+ 'stroke-dasharray' : '5,5'
132
+ })
133
+ .set_attr(text, {
134
+ y: height + 20,
135
+ x: line_width - interval,
136
+ 'text-anchor': 'middle'
137
+ });
138
+
139
+ if (existing_group.length < 1) {
140
+ text.innerHTML = ticks[i];
141
+ text_g.appendChild(text);
142
+ line_g.appendChild(line);
143
+ }
144
+
145
+ total_tick_width = line_width;
146
+ }
147
+
148
+ line_g.setAttribute('transform', 'translate(-' + interval + ', 0)');
149
+
150
+ if (existing_group.length < 1) {
151
+ svg.appendChild(line_g);
152
+ svg.appendChild(text_g);
153
+ }
154
+ },
155
+
156
+ assemble_grid_y : function (svg, min, max, width, height, settings) {
157
+ var existing_group = $('g[data-id=gridy]', svg);
158
+
159
+ if (existing_group.length > 0) {
160
+ var line_g = existing_group[0],
161
+ text_g = $('g[data-id=labely]', svg)[0];
162
+ } else {
163
+ var line_g = this.svg_obj('g'),
164
+ text_g = this.svg_obj('g');
165
+
166
+ line_g.setAttribute('data-id', 'gridy');
167
+ text_g.setAttribute('data-id', 'labely');
168
+ }
169
+
170
+ var ticks = this.ticks(min, max, settings.bar_intervals),
171
+ ticks_length = i = ticks.length,
172
+ total_tick_height = 0;
173
+
174
+ while (i--) {
175
+ if (existing_group.length > 0) {
176
+ var line = $('line[data-id=l' + i + ']', line_g)[0],
177
+ text = $('text[data-id=t' + i + ']', text_g)[0];
178
+ } else {
179
+ var line = this.svg_obj('line'),
180
+ text = this.svg_obj('text');
181
+
182
+ line.setAttribute('data-id', 'l' + i);
183
+ text.setAttribute('data-id', 't' + i);
184
+ }
185
+
186
+ var line_height = total_tick_height + (height/(ticks_length-1));
187
+
188
+ this.set_attr(line, {
189
+ x1 : 0,
190
+ x2 : width,
191
+ y1 : line_height,
192
+ y2 : line_height,
193
+ stroke : 'gray',
194
+ 'stroke-width' : 1,
195
+ 'stroke-dasharray' : '5,5'
196
+ })
197
+ .set_attr(text, {
198
+ x : -8,
199
+ y : line_height + 5,
200
+ 'text-anchor': 'end'
201
+ });
202
+
203
+ if (existing_group.length < 1) {
204
+ text_g.appendChild(text);
205
+ line_g.appendChild(line);
206
+ text.innerHTML = ticks[i];
207
+ }
208
+
209
+ total_tick_height = line_height;
210
+ }
211
+
212
+ line_g.setAttribute('transform', 'translate(0, -' + total_tick_height / ticks_length + ')');
213
+ text_g.setAttribute('transform', 'translate(0, -' + total_tick_height / ticks_length + ')');
214
+
215
+ if (existing_group.length < 1) {
216
+ svg.appendChild(line_g);
217
+ svg.appendChild(text_g);
218
+ }
219
+
220
+ },
221
+
222
+ points_to_path : function (points) {
223
+ var points = points.split(/\s+|,/);
224
+ var x0=points.shift(), y0=points.shift();
225
+ var pathdata = 'M'+x0+','+y0+'L'+points.join(' ');
226
+ return ['M'+x0+','+y0+'L'].concat(points).join(' ');
227
+ },
228
+
229
+ line_events : function () {
230
+ $(this.scope).on('mouseenter.pizza mouseleave.pizza touchstart.pizza', '[data-line-id] li', function (e) {
231
+ var parent = $(this).parent(),
232
+ path = $('#' + parent.data('line-id') + ' circle[data-id="c' + $(this).index() + '"]')[0],
233
+ settings = $(this).parent().data('settings');
234
+
235
+ if (/start/i.test(e.type)) {
236
+ $(path).siblings('circle').each(function () {
237
+ if (this.nodeName) {
238
+ Snap(path).animate({
239
+ transform: 's1 1 ' + path.getAttribute('cx') + ' ' + path.getAttribute('cy')
240
+ }, settings.animation_speed, mina[settings.animation_type]);
241
+ }
242
+ });
243
+ }
244
+
245
+ if (/enter|start/i.test(e.type)) {
246
+ Snap(path).animate({
247
+ transform: 's2 2 ' + path.getAttribute('cx') + ' ' + path.getAttribute('cy')
248
+ }, settings.animation_speed, mina[settings.animation_type]);
249
+ $(path).trigger('mouseenter')
250
+ } else {
251
+ Snap(path).animate({
252
+ transform: 's1 1 ' + path.getAttribute('cx') + ' ' + path.getAttribute('cy')
253
+ }, settings.animation_speed, mina[settings.animation_type]);
254
+ $(path).trigger('mouseout')
255
+ }
256
+ });
257
+ }
258
+
259
+ });
@@ -0,0 +1,225 @@
1
+ $.extend(Pizza, {
2
+ pie : function (legend) {
3
+ // pie chart concept from JavaScript the
4
+ // Definitive Guide 6th edition by David Flanagan
5
+ var settings = legend.data('settings'),
6
+ svg = this.svg(legend, settings),
7
+ data = legend.data('graph-data'),
8
+ total = 0,
9
+ angles = [],
10
+ start_angle = 0,
11
+ container = $(this.identifier(legend)),
12
+ base = container.outerWidth();
13
+
14
+ for (var i = 0; i < data.length; i++) {
15
+ total += data[i].value;
16
+ }
17
+
18
+ for (var i = 0; i < data.length; i++) {
19
+ angles[i] = data[i].value / total * Math.PI * 2;
20
+ }
21
+
22
+ if(angles.length == 1) angles[0] = Math.PI * 2 - 0.0001; // if 1
23
+
24
+ for (var i = 0; i < data.length; i++) {
25
+ var end_angle = start_angle + angles[i];
26
+ var cx = (base / 2),
27
+ cy = (base / 2),
28
+ r = ((base / 2) * 0.85);
29
+
30
+ if (!settings.donut) {
31
+ // Compute the two points where our wedge intersects the circle
32
+ // These formulas are chosen so that an angle of 0 is at 12 o'clock
33
+ // and positive angles increase clockwise
34
+ var x1 = cx + r * Math.sin(start_angle);
35
+ var y1 = cy - r * Math.cos(start_angle);
36
+ var x2 = cx + r * Math.sin(end_angle);
37
+ var y2 = cy - r * Math.cos(end_angle);
38
+
39
+ // This is a flag for angles larger than than a half circle
40
+ // It is required by the SVG arc drawing component
41
+ var big = 0;
42
+ if (end_angle - start_angle > Math.PI) big = 1;
43
+
44
+ // This string holds the path details
45
+ var d = "M" + cx + "," + cy + // Start at circle center
46
+ " L" + x1 + "," + y1 + // Draw line to (x1,y1)
47
+ " A" + r + "," + r + // Draw an arc of radius r
48
+ " 0 " + big + " 1 " + // Arc details...
49
+ x2 + "," + y2 + // Arc goes to to (x2,y2)
50
+ " Z"; // Close path back to (cx,cy)
51
+ }
52
+
53
+ var existing_path = $('path[data-id="s' + i + '"]', svg);
54
+
55
+ if (existing_path.length > 0) {
56
+ var path = existing_path[0];
57
+ } else {
58
+ var path = this.svg_obj('path');
59
+ }
60
+
61
+ var percent = (data[i].value / total) * 100.0;
62
+
63
+ var existing_text = $('text[data-id="s' + i + '"]', svg);
64
+
65
+ if (existing_text.length > 0) {
66
+ var text = existing_text[0];
67
+
68
+ text.setAttribute('x', cx + (r + settings.percent_offset) * Math.sin(start_angle + (angles[i] / 2)));
69
+ text.setAttribute('y', cy - (r + settings.percent_offset) * Math.cos(start_angle + (angles[i] / 2)));
70
+
71
+ } else {
72
+
73
+ if (data[i].text) {
74
+ var visible_text = this.parse_options(data[i].text, percent, data[i].value);
75
+ } else {
76
+ var visible_text = Math.ceil(percent) + '%';
77
+ }
78
+ var text = this.svg_obj('text');
79
+
80
+ text.setAttribute('x', cx + (r + settings.percent_offset) * Math.sin(start_angle + (angles[i] / 2)));
81
+ text.setAttribute('y', cy - (r + settings.percent_offset) * Math.cos(start_angle + (angles[i] / 2)));
82
+ text.innerHTML = visible_text;
83
+ }
84
+
85
+ text.setAttribute('text-anchor', 'middle');
86
+
87
+ if (settings.always_show_text) {
88
+ Snap(text).animate({
89
+ opacity: 1
90
+ }, settings.animation_speed);
91
+ } else {
92
+ Snap(text).attr({
93
+ opacity: 0
94
+ }, settings.animation_speed);
95
+ }
96
+
97
+ text.setAttribute('data-id', 's' + i);
98
+
99
+ if (settings.donut) {
100
+ this.annular_sector(path, {
101
+ centerX:cx, centerY:cy,
102
+ startDegrees:start_angle, endDegrees:end_angle,
103
+ innerRadius: (r * settings.donut_inner_ratio), outerRadius:r
104
+ });
105
+ } else {
106
+ path.setAttribute('d', d);
107
+ }
108
+
109
+ this.set_attr(path, {
110
+ fill: data[i].color,
111
+ stroke: settings.stroke_color,
112
+ 'strokeWidth': settings.stroke_width,
113
+ 'data-cx' : cx,
114
+ 'data-cy' : cy,
115
+ 'data-id' : 's' + i
116
+ });
117
+
118
+ var existing_group = $('g[data-id=g' + i + ']', svg);
119
+
120
+ if (existing_group.length < 1) {
121
+ var g = this.svg_obj('g');
122
+
123
+ g.setAttribute('data-id', 'g' + i);
124
+ g.appendChild(path);
125
+ g.appendChild(text);
126
+ svg.appendChild(g);
127
+
128
+ this.animate(Snap(path), cx, cy, settings);
129
+ }
130
+
131
+ // The next wedge begins where this one ends
132
+ start_angle = end_angle;
133
+ }
134
+
135
+ return [legend, svg];
136
+ },
137
+
138
+ annular_sector : function (path, options) {
139
+ var opts = optionsWithDefaults(options);
140
+
141
+ var p = [ // points
142
+ [opts.cx + opts.r2*Math.sin(opts.startRadians),
143
+ opts.cy - opts.r2*Math.cos(opts.startRadians)],
144
+ [opts.cx + opts.r2*Math.sin(opts.closeRadians),
145
+ opts.cy - opts.r2*Math.cos(opts.closeRadians)],
146
+ [opts.cx + opts.r1*Math.sin(opts.closeRadians),
147
+ opts.cy - opts.r1*Math.cos(opts.closeRadians)],
148
+ [opts.cx + opts.r1*Math.sin(opts.startRadians),
149
+ opts.cy - opts.r1*Math.cos(opts.startRadians)],
150
+ ];
151
+
152
+ var angleDiff = opts.closeRadians - opts.startRadians;
153
+ var largeArc = (angleDiff % (Math.PI*2)) > Math.PI ? 1 : 0;
154
+ var cmds = [];
155
+ cmds.push("M"+p[0].join()); // Move to P0
156
+ cmds.push("A"+[opts.r2,opts.r2,0,largeArc,1,p[1]].join()); // Arc to P1
157
+ cmds.push("L"+p[2].join()); // Line to P2
158
+ cmds.push("A"+[opts.r1,opts.r1,0,largeArc,0,p[3]].join()); // Arc to P3
159
+ cmds.push("z"); // Close path (Line to P0)
160
+ path.setAttribute('d',cmds.join(' '));
161
+
162
+ function optionsWithDefaults(o){
163
+ // Create a new object so that we don't mutate the original
164
+ var o2 = {
165
+ cx : o.centerX || 0,
166
+ cy : o.centerY || 0,
167
+ startRadians : (o.startDegrees || 0),
168
+ closeRadians : (o.endDegrees || 0),
169
+ };
170
+
171
+ var t = o.thickness!==undefined ? o.thickness : 100;
172
+ if (o.innerRadius!==undefined) o2.r1 = o.innerRadius;
173
+ else if (o.outerRadius!==undefined) o2.r1 = o.outerRadius - t;
174
+ else o2.r1 = 200 - t;
175
+ if (o.outerRadius!==undefined) o2.r2 = o.outerRadius;
176
+ else o2.r2 = o2.r1 + t;
177
+
178
+ if (o2.r1<0) o2.r1 = 0;
179
+ if (o2.r2<0) o2.r2 = 0;
180
+
181
+ return o2;
182
+ }
183
+ },
184
+
185
+ pie_events : function () {
186
+ $(this.scope).on('mouseenter.pizza mouseleave.pizza touchstart.pizza', '[data-pie-id] li', function (e) {
187
+ var parent = $(this).parent(),
188
+ path = $('#' + parent.attr('data-pie-id') + ' path[data-id="s' + $(this).index() + '"]')[0],
189
+ text = path.nextSibling,
190
+ settings = $(this).parent().data('settings');
191
+
192
+ if (/start/i.test(e.type)) {
193
+ $(path).siblings('path').each(function () {
194
+ if (this.nodeName) {
195
+ Snap(path).animate({
196
+ transform: 's1 1 ' + path.getAttribute('data-cx') + ' ' + path.getAttribute('data-cy')
197
+ }, settings.animation_speed, mina[settings.animation_type]);
198
+ Snap($(this).next()[0]).animate({
199
+ opacity: 0
200
+ }, settings.animation_speed);
201
+ }
202
+ });
203
+ }
204
+
205
+ if (/enter|start/i.test(e.type)) {
206
+ Snap(path).animate({
207
+ transform: 's1.05 1.05 ' + path.getAttribute('data-cx') + ' ' + path.getAttribute('data-cy')
208
+ }, settings.animation_speed, mina[settings.animation_type]);
209
+
210
+ if (settings.show_text) {
211
+ Snap(text).animate({
212
+ opacity: 1
213
+ }, settings.animation_speed);
214
+ }
215
+ } else {
216
+ Snap(path).animate({
217
+ transform: 's1 1 ' + path.getAttribute('data-cx') + ' ' + path.getAttribute('data-cy')
218
+ }, settings.animation_speed, mina[settings.animation_type]);
219
+ Snap(text).animate({
220
+ opacity: 0
221
+ }, settings.animation_speed);
222
+ }
223
+ });
224
+ }
225
+ });
@@ -0,0 +1,256 @@
1
+ var Pizza = {
2
+ version : '0.2.1',
3
+
4
+ settings : {
5
+ donut: false,
6
+ donut_inner_ratio: 0.4, // between 0 and 1
7
+ percent_offset: 35, // relative to radius
8
+ show_text: true, // show or hide the percentage on the chart.
9
+ animation_speed: 500,
10
+ always_show_text: false,
11
+ show_grid: true,
12
+ bar_spacer: 100,
13
+ bar_intervals: 6,
14
+ animation_type: 'elastic' // options: backin, backout, bounce, easein,
15
+ // easeinout, easeout, linear
16
+ },
17
+
18
+ NS : 'http://www.w3.org/2000/svg',
19
+
20
+ init : function (scope, options) {
21
+ var self = this;
22
+ this.scope = scope || document.body;
23
+
24
+ var charts = $('[data-pie-id], [data-line-id], [data-bar-id]', this.scope);
25
+
26
+ $.extend(true, this.settings, options);
27
+
28
+ if (charts.length > 0) {
29
+ charts.each(function () {
30
+ return self.build($(this), options);
31
+ });
32
+ } else if ($(this.scope).is('[data-pie-id]')
33
+ || $(this.scope).is('[data-line-id]')
34
+ || $(this.scope).is('[data-bar-id]')) {
35
+ this.build($(this.scope), options);
36
+ }
37
+
38
+ this.events();
39
+ },
40
+
41
+ events : function () {
42
+ var self = this;
43
+
44
+ $(window).off('.pizza').on('resize.pizza', self.throttle(function () {
45
+ self.init();
46
+ }, 500));
47
+
48
+ $(this.scope).off('.pizza');
49
+
50
+ this.pie_events();
51
+ this.line_events();
52
+ this.bar_events();
53
+ },
54
+
55
+ build : function(legend, options) {
56
+ legend.data('settings', $.extend({}, this.settings, options, legend.data('options')));
57
+
58
+ this.data(legend, options || {});
59
+
60
+ if (legend.data('pie-id')) {
61
+ this.update_DOM(this.pie(legend));
62
+ } else if (legend.data('line-id')) {
63
+ this.update_DOM(this.line(legend));
64
+ } else if (legend.data('bar-id')) {
65
+ this.update_DOM(this.bar(legend));
66
+ }
67
+ },
68
+
69
+ data : function (legend, options) {
70
+ var data = [],
71
+ count = 0;
72
+
73
+ $('li', legend).each(function () {
74
+ var segment = $(this);
75
+
76
+ if (options.data) {
77
+ data.push({
78
+ value: options.data[segment.index()],
79
+ text: segment.data('text'),
80
+ color: segment.css('color'),
81
+ segment: segment
82
+ });
83
+ } else {
84
+ data.push({
85
+ x : segment.data('x'),
86
+ y : segment.data('y'),
87
+ value: segment.data('value'),
88
+ text: segment.data('text'),
89
+ color: segment.css('color'),
90
+ segment: segment
91
+ });
92
+ }
93
+ });
94
+
95
+ return legend.data('graph-data', data);
96
+ },
97
+
98
+ update_DOM : function (parts) {
99
+ var legend = parts[0],
100
+ graph = parts[1];
101
+
102
+ return $(this.identifier(legend)).html(graph);
103
+ },
104
+
105
+ animate : function (el, cx, cy, settings, scale) {
106
+ var self = this,
107
+ scale = scale || 1.05;
108
+
109
+ el.hover(function (e) {
110
+ var path = Snap(e.target),
111
+ text = Snap(path.node.nextSibling);
112
+
113
+ path.animate({
114
+ transform: 's' + scale + ' ' + scale + ' ' + cx + ' ' + cy
115
+ }, settings.animation_speed, mina[settings.animation_type]);
116
+
117
+ if (!/text/.test(text.node.nodeName)) return;
118
+
119
+ text.touchend(function () {
120
+ Snap(path).animate({
121
+ transform: 's' + scale + ' ' + scale + ' ' + cx + ' ' + cy
122
+ }, settings.animation_speed, mina[settings.animation_type]);
123
+ });
124
+
125
+ if (settings.show_text) {
126
+ text.animate({
127
+ opacity: 1
128
+ }, settings.animation_speed);
129
+ text.touchend(function () {
130
+ text.animate({
131
+ opacity: 1
132
+ }, settings.animation_speed);
133
+ });
134
+ }
135
+
136
+ }, function (e) {
137
+ var path = Snap(e.target),
138
+ text = Snap(path.node.nextSibling);
139
+
140
+ path.animate({
141
+ transform: 's1 1 ' + cx + ' ' + cy
142
+ }, settings.animation_speed, mina[settings.animation_type]);
143
+
144
+ if (!/text/.test(text.node.nodeName)) return;
145
+
146
+ text.animate({
147
+ opacity: 0
148
+ }, settings.animation_speed);
149
+ });
150
+
151
+ },
152
+
153
+ parse_options : function (string, percent, value) {
154
+ var matches = string.match(/{{(percent|value)}}/g),
155
+ output = '';
156
+
157
+ for (var i = 0; i < matches.length; i++) {
158
+
159
+ if (/percent/i.test(matches[i])) {
160
+ output = string.replace(matches[i], [Math.ceil(percent), '%'].join(''));
161
+ }
162
+
163
+ if (/value/i.test(matches[i])) {
164
+ output = output.replace(matches[i], value);
165
+ }
166
+ }
167
+
168
+ return output;
169
+ },
170
+
171
+ svg : function (legend, settings) {
172
+ var container = $(this.identifier(legend)),
173
+ svg = $('svg', container),
174
+ width = container.width(),
175
+ pie = legend.attr('data-pie-id'),
176
+ height = container.height();
177
+
178
+ if (svg.length > 0) {
179
+ svg = svg[0];
180
+ } else {
181
+ var svg = this.svg_obj('svg');
182
+ svg.width = width;
183
+ svg.height = height;
184
+ }
185
+
186
+ if (pie) {
187
+ var view_box = '-' + settings.percent_offset + ' -' + settings.percent_offset + ' ' +
188
+ (width + (settings.percent_offset * 1.5)) + ' ' +
189
+ (width + (settings.percent_offset * 1.5));
190
+ } else {
191
+ var view_box = '-' + settings.percent_offset + ' -' + settings.percent_offset + ' ' +
192
+ (width + (settings.percent_offset * 1.6)) + ' ' +
193
+ (height + (settings.percent_offset * 1.6));
194
+ }
195
+
196
+ this.set_attr(svg, {width: '100%', height: '100%', viewBox: view_box});
197
+
198
+ return svg;
199
+ },
200
+
201
+ identifier : function (legend) {
202
+ id = legend.data('pie-id') || legend.data('bar-id') || legend.data('line-id');
203
+ return '#' + id;
204
+ },
205
+
206
+ throttle : function(fun, delay) {
207
+ var timer = null;
208
+ return function () {
209
+ var context = this, args = arguments;
210
+ clearTimeout(timer);
211
+ timer = setTimeout(function () {
212
+ fun.apply(context, args);
213
+ }, delay);
214
+ };
215
+ },
216
+
217
+ svg_obj : function (type) {
218
+ return document.createElementNS(this.NS, type);
219
+ },
220
+
221
+ ticks: function (min, max, count) {
222
+ var span = max - min,
223
+ step = Math.pow(10, Math.floor(Math.log(span / count) / Math.LN10)),
224
+ err = count / span * step;
225
+
226
+ // Filter ticks to get closer to the desired count.
227
+ if (err <= .15) step *= 10;
228
+ else if (err <= .35) step *= 5;
229
+ else if (err <= .75) step *= 2;
230
+
231
+ // Round start and stop values to step interval.
232
+ var tstart = Math.ceil(min / step) * step,
233
+ tstop = Math.floor(max / step) * step + step * .5,
234
+ ticks = [],
235
+ x;
236
+
237
+ // now generate ticks
238
+ for (i=tstart; i < tstop; i += step) {
239
+ ticks.push(i);
240
+ }
241
+ return ticks;
242
+ },
243
+
244
+ set_attr : function (node, attrs) {
245
+
246
+ for (attr in attrs) {
247
+ node.setAttribute(attr, attrs[attr]);
248
+ }
249
+
250
+ return this;
251
+ },
252
+
253
+ flip : function (node, h) {
254
+ node.setAttribute('transform', 'translate(0, ' + h +') scale(1, -1)');
255
+ }
256
+ };
@@ -0,0 +1,53 @@
1
+ // Pie Chart Variables
2
+ $pie-color: #f45c5c !default;
3
+
4
+ @mixin darkened-children($num-children, $color) {
5
+ @for $i from 1 through $num-children {
6
+ $color: scale-color($color, $lightness: -10%);
7
+ & > *:nth-child(#{$num-children}n+#{$i}) { // This causes looping to occur after there are $num-children child elements.
8
+ color: $color;
9
+ }
10
+ }
11
+ }
12
+
13
+ @mixin lightened-children($num-children, $color) {
14
+ @for $i from 1 through $num-children {
15
+ $color: scale-color($color, $lightness: 10%);
16
+ & > *:nth-child(#{$num-children}n+#{$i}) { // This causes looping to occur after there are $num-children child elements.
17
+ color: $color;
18
+ }
19
+ }
20
+ }
21
+
22
+ [data-pie-id], [data-bar-id], [data-line-id] {
23
+ @include darkened-children(7, $pie-color);
24
+ }
25
+
26
+ // Set the SVG to 100% width to smooth responsive resizing (optional)
27
+ #pie, #donut, #bar {
28
+ max-height: 450px;
29
+ }
30
+
31
+ #bar {
32
+ // height: 300px;
33
+ }
34
+
35
+ svg {
36
+ width: 100%;
37
+ height: auto;
38
+ }
39
+
40
+ .tooltip {
41
+ margin-left: -5px;
42
+ border-radius: 3px;
43
+ padding: 5px;
44
+ }
45
+
46
+ // svg text {
47
+ // fill: $label-color;
48
+ // }
49
+
50
+ ul[data-pie-id] {
51
+ list-style: none;
52
+ padding: 10px;
53
+ }
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pizza-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Eric Roberts
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: railties
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '3'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: snapsvg-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.2.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: 0.2.0
69
+ description: Pizza is a responsive pie, donut, bar, and line graph charting library
70
+ based on the Snap SVG framework from Adobe. It focuses on easy integration via HTML
71
+ markup and CSS instead of JavaScript objects, although you can pass JavaScript objects
72
+ to Pizza as well.
73
+ email:
74
+ - ericroberts@gmail.com
75
+ executables: []
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - .gitignore
80
+ - Gemfile
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - lib/pizza/rails.rb
85
+ - lib/pizza/rails/version.rb
86
+ - pizza-rails.gemspec
87
+ - vendor/assets/javascripts/pizza.js
88
+ - vendor/assets/javascripts/pizza/bar.js
89
+ - vendor/assets/javascripts/pizza/line.js
90
+ - vendor/assets/javascripts/pizza/pie.js
91
+ - vendor/assets/javascripts/pizza/pizza.js
92
+ - vendor/assets/stylesheets/pizza.scss
93
+ homepage: ''
94
+ licenses:
95
+ - MIT
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.1.10
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: Rails wrapper for Pizza by Zurb
117
+ test_files: []