highcharts-js-rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## v0.0.1 (2011-10-13) ##
2
+
3
+ * First release
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in highcharts-rails.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (C) 2011 Alex Robbin
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,85 @@
1
+ Highcharts JS Rails
2
+ ===================
3
+
4
+ Easily configure a Highcharts JS chart for use in a Rails application.
5
+
6
+ Currently you are able to do the following:
7
+
8
+ * Configure multiple axes and series on a single chart
9
+ * Set a specific graph type for each individual series of data (available options include area, areaspline, bar, column, line, pie, scatter or spline) - default is line
10
+ * Customize the tooltip for the graph
11
+ * Configure the location and design of the legend
12
+ * Set the tick interval for a specific axis
13
+
14
+ Options
15
+ -------
16
+
17
+ To instantiate a new chart:
18
+
19
+ `chart = Highcharts::Chart.new`
20
+
21
+ The Highcharts::Chart class can receive a hash of the following options:
22
+
23
+ * container: string (required)
24
+ * title: string (required)
25
+ * subtitle: string
26
+ * xAxes: hash or array of hashes (required)
27
+ * yAxes: hash or array of hashes (required)
28
+ * legend: hash
29
+ * series: hash or array of hashes (required)
30
+ * tooltip: string
31
+
32
+ Both the `xAxes` and `yAxes` options can be either a single hash (meaning there is only one axis) or an array of hashes (for multiple-axis charts). Each hash can include the following options:
33
+
34
+ * title: string
35
+ * categories: array
36
+ * tickInterval: integer
37
+ * min: integer
38
+ * max: integer
39
+ * labels: hash
40
+ * opposite: boolean
41
+
42
+ The `categories` option's array of values will be automatically formatted based on its class.
43
+ The `tickInterval` option is only necessary if you want to override automatic tickInterval creation. If the option is not passed, the tickInterval will be calculated based on the amount of data passed to the attached `series`.
44
+ The `labels` option can receive a set of options that includes `prepend` and `append`.
45
+
46
+ The `legend` option for the Highcharts::Chart class can receive a hash of the following options:
47
+
48
+ * layout: string (possible values can be horizontal or vertical - default is horizontal)
49
+ * align: string
50
+ * verticalAlign: string
51
+ * x: integer
52
+ * y: integer
53
+ * borderWidth: integer
54
+
55
+ The `series` option for the Highcharts::Chart class can receive a hash of the following options:
56
+
57
+ * name: string
58
+ * type: string (possible values can be area, areaspline, bar, column, line, pie, scatter or spline - default is line)
59
+ * xAxis: integer (the index of the xAxis this series should be attached to)
60
+ * yAxis: integer (the index of the yAxis this series should be attached to)
61
+ * data: array
62
+
63
+ The `data` option's array of values will be automatically formatted as a float, if necessary.
64
+
65
+ Example
66
+ --------
67
+
68
+ Ruby:
69
+
70
+ ```
71
+ chart = Highcharts::Chart.new(
72
+ :container => 'graph',
73
+ :title => "Highcharts Example",
74
+ :xAxes => {:categories => ['October 12', 'October 13', 'October 14']},
75
+ :yAxes => [{:title => 'Impressions', :min => 0}],
76
+ :series => [{:name => 'Impressions', :yAxis => 0, :type => 'line', :data => [100000, 122000, 127000]}],
77
+ :legend => {:layout => 'vertical', :align => 'right', :verticalAlign => 'top', :x => -10, :y => 100, :borderWidth => 0},
78
+ :tooltip => "'<b>' + this.series.name + '</b><br/>' + this.x + ': ' + this.y"
79
+ )
80
+ <%= chart %>
81
+ ```
82
+
83
+ HTML/ERB:
84
+
85
+ `<div id="graph" style="width: 400px; height: 200px;"></div>`
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require 'highcharts/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'highcharts-js-rails'
7
+ s.version = Highcharts::VERSION
8
+ s.authors = ['Alex Robbin']
9
+ s.email = ['agrobbin@gmail.com']
10
+ s.homepage = 'http://github.com/agrobbin/highcharts-js-rails'
11
+ s.summary = %q{Easily configure a Highcharts JS chart for use in a Rails application}
12
+ s.description = s.summary
13
+
14
+ s.rubyforge_project = 'highcharts-js-rails'
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ['lib']
20
+
21
+ s.add_development_dependency 'bundler'
22
+ s.add_development_dependency 'simplecov'
23
+ s.add_development_dependency 'rake'
24
+ s.add_development_dependency 'rspec'
25
+ s.add_dependency 'actionpack', '~> 3.0'
26
+ end
data/lib/highcharts.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'highcharts/axis'
2
+ require 'highcharts/base'
3
+ require 'highcharts/chart'
4
+ require 'highcharts/legend'
5
+ require 'highcharts/plot_options'
6
+ require 'highcharts/rails'
7
+ require 'highcharts/series'
8
+ require 'highcharts/version'
@@ -0,0 +1,48 @@
1
+ module Highcharts
2
+ class Axis < Base
3
+
4
+ attr_accessor :title, :categories, :tickInterval, :min, :max, :labels, :opposite
5
+
6
+ def to_s
7
+ "{" +
8
+ [render_title, render_categories, render_tickInterval, render_options(:objects => 'min max opposite'), render_labels].flatten.compact.join(',') +
9
+ "}"
10
+ end
11
+
12
+ def render_title
13
+ title.present? ? "title: {text: '#{title}'}" : nil
14
+ end
15
+
16
+ def render_categories
17
+ return nil unless categories.present?
18
+ "categories: [#{categories.collect {|c| "'#{format_category(c)}'"}.join(', ')}]" # need to encapsulate each category in quotes and format it before joining them with a comma
19
+ end
20
+
21
+ def render_tickInterval
22
+ return tickInterval if tickInterval.present?
23
+ return nil unless categories.present?
24
+ "tickInterval: #{(Math.sqrt(categories.length) < 5 ? 1 : Math.sqrt(categories.length)).floor}"
25
+ end
26
+
27
+ def format_category(category)
28
+ case category
29
+ when Date
30
+ category.strftime("%b. %d")
31
+ when Time
32
+ category.strftime("%H:%M")
33
+ when DateTime
34
+ category.strftime("%b. %d, %H:%M")
35
+ else
36
+ category.to_s
37
+ end
38
+ end
39
+
40
+ def render_labels
41
+ return nil unless labels.present?
42
+ "labels: {" +
43
+ "formatter: function(){ return '#{labels[:prepend]}' + this.value + '#{labels[:append]}'; }" +
44
+ "}"
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,19 @@
1
+ module Highcharts
2
+ class Base < ActionView::Base
3
+
4
+ def initialize(*args)
5
+ args.extract_options!.each {|arg, value| self.send("#{arg}=", value)}
6
+ end
7
+
8
+ def render_options(args)
9
+ attrs = []
10
+ args.each do |t, a|
11
+ a.split.each do |option|
12
+ attrs << "#{option}: #{t == :objects ? send(option) : "'#{send(option)}'"}" if send(option).present?
13
+ end
14
+ end
15
+ attrs
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,80 @@
1
+ module Highcharts
2
+ class Chart < Base
3
+ include ActionView::Helpers
4
+
5
+ attr_accessor :container, :title, :subtitle, :plot_options, :xAxes, :yAxes, :legend, :series, :tooltip
6
+
7
+ def to_s
8
+ check_required_fields
9
+
10
+ javascript_tag do
11
+ safe_concat("$(function(){" +
12
+ "new Highcharts.Chart({" +
13
+ render_all +
14
+ "})" +
15
+ "});")
16
+ end
17
+ end
18
+
19
+ def render_all
20
+ content = [render_container, render_titles, render_plot_options, render_axes, render_series, render_legend, render_tooltip].flatten.compact.join(",")
21
+ if Rails.env.production?
22
+ content
23
+ else
24
+ content.gsub(/(,|{|\[)/, "\\1\n").gsub(/(}|])/, "\n\\1")
25
+ end
26
+ end
27
+
28
+ def check_required_fields
29
+ %w(container title series).each do |required_argument|
30
+ raise ArgumentError, ":#{required_argument} must be passed" if send(required_argument).blank? || (send(required_argument).is_a?(Array) && send(required_argument).length == 0)
31
+ end
32
+ end
33
+
34
+ def render_container
35
+ "chart: {renderTo: '#{container}'}"
36
+ end
37
+
38
+ def render_titles
39
+ %w(title subtitle).collect do |t|
40
+ "#{t}: {text: '#{send(t)}'}" if send(t).present?
41
+ end
42
+ end
43
+
44
+ def render_plot_options
45
+ return nil unless plot_options.present?
46
+ "plotOptions: {" +
47
+ Array.wrap(plot_options).collect {|po| PlotOptions.new(po)}.join(',') +
48
+ "}"
49
+ end
50
+
51
+ def render_axes
52
+ result = %w(x y).collect do |axis|
53
+ return nil unless send("#{axis}Axes").present?
54
+ "#{axis}Axis: [" +
55
+ Array.wrap(send("#{axis}Axes")).collect {|a| Axis.new(a)}.join(',') +
56
+ "]"
57
+ end
58
+ result.compact.join(",")
59
+ end
60
+
61
+ def render_legend
62
+ return nil unless legend.present?
63
+ Legend.new(legend)
64
+ end
65
+
66
+ def render_series
67
+ "series: [" +
68
+ Array.wrap(series).collect {|s| Series.new(s)}.join(',') +
69
+ "]"
70
+ end
71
+
72
+ def render_tooltip
73
+ return nil unless tooltip.present?
74
+ "tooltip: {" +
75
+ "formatter: function(){ return #{tooltip}; }" +
76
+ "}"
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,15 @@
1
+ module Highcharts
2
+ class Legend < Base
3
+
4
+ attr_accessor :layout, :align, :verticalAlign, :x, :y, :borderWidth
5
+
6
+ def to_s
7
+ rendered_options = render_options(:strings => 'layout align verticalAlign', :objects => 'x y borderWidth')
8
+ return nil if rendered_options.length == 0
9
+ "legend: {" +
10
+ rendered_options.join(',') +
11
+ "}"
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ module Highcharts
2
+ class PlotOptions < Base
3
+
4
+ attr_accessor :type, :data_labels, :legend
5
+
6
+ def to_s
7
+ "#{type}: {" +
8
+ [render_data_labels, render_legend].flatten.compact.join(',') +
9
+ "}"
10
+ end
11
+
12
+ def render_data_labels
13
+ "dataLabels: {" +
14
+ "enabled: #{data_labels == false ? 'false' : 'true'}" +
15
+ "}"
16
+ end
17
+
18
+ def render_legend
19
+ "showInLegend: #{legend ? 'true' : 'false'}"
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,6 @@
1
+ module Highcharts
2
+ module Rails
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,19 @@
1
+ module Highcharts
2
+ class Series < Base
3
+
4
+ attr_accessor :name, :type, :xAxis, :yAxis, :data
5
+
6
+ def to_s
7
+ rendered_options = render_options(:strings => 'name type', :objects => 'xAxis yAxis')
8
+ return nil if rendered_options.length == 0
9
+ "{" +
10
+ [rendered_options, render_data].flatten.compact.join(',') +
11
+ "}"
12
+ end
13
+
14
+ def render_data
15
+ "data: #{data.first.is_a?(Array) ? data : data.collect(&:to_f)}"
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module Highcharts
2
+ VERSION = "0.0.1"
3
+ end
data/vendor/.DS_Store ADDED
Binary file
Binary file
@@ -0,0 +1,298 @@
1
+ /**
2
+ * @license Highcharts JS v2.2.0 (2012-02-16)
3
+ * MooTools adapter
4
+ *
5
+ * (c) 2010-2011 Torstein Hønsi
6
+ *
7
+ * License: www.highcharts.com/license
8
+ */
9
+
10
+ // JSLint options:
11
+ /*global Fx, $, $extend, $each, $merge, Events, Event, DOMEvent */
12
+
13
+ (function () {
14
+
15
+ var win = window,
16
+ doc = document,
17
+ mooVersion = win.MooTools.version.substring(0, 3), // Get the first three characters of the version number
18
+ legacy = mooVersion === '1.2' || mooVersion === '1.1', // 1.1 && 1.2 considered legacy, 1.3 is not.
19
+ legacyEvent = legacy || mooVersion === '1.3', // In versions 1.1 - 1.3 the event class is named Event, in newer versions it is named DOMEvent.
20
+ $extend = win.$extend || function () {
21
+ return Object.append.apply(Object, arguments);
22
+ };
23
+
24
+ win.HighchartsAdapter = {
25
+ /**
26
+ * Initialize the adapter. This is run once as Highcharts is first run.
27
+ * @param {Object} pathAnim The helper object to do animations across adapters.
28
+ */
29
+ init: function (pathAnim) {
30
+ var fxProto = Fx.prototype,
31
+ fxStart = fxProto.start,
32
+ morphProto = Fx.Morph.prototype,
33
+ morphCompute = morphProto.compute;
34
+
35
+ // override Fx.start to allow animation of SVG element wrappers
36
+ /*jslint unparam: true*//* allow unused parameters in fx functions */
37
+ fxProto.start = function (from, to) {
38
+ var fx = this,
39
+ elem = fx.element;
40
+
41
+ // special for animating paths
42
+ if (from.d) {
43
+ //this.fromD = this.element.d.split(' ');
44
+ fx.paths = pathAnim.init(
45
+ elem,
46
+ elem.d,
47
+ fx.toD
48
+ );
49
+ }
50
+ fxStart.apply(fx, arguments);
51
+
52
+ return this; // chainable
53
+ };
54
+
55
+ // override Fx.step to allow animation of SVG element wrappers
56
+ morphProto.compute = function (from, to, delta) {
57
+ var fx = this,
58
+ paths = fx.paths;
59
+
60
+ if (paths) {
61
+ fx.element.attr(
62
+ 'd',
63
+ pathAnim.step(paths[0], paths[1], delta, fx.toD)
64
+ );
65
+ } else {
66
+ return morphCompute.apply(fx, arguments);
67
+ }
68
+ };
69
+ /*jslint unparam: false*/
70
+ },
71
+
72
+ /**
73
+ * Downloads a script and executes a callback when done.
74
+ * @param {String} scriptLocation
75
+ * @param {Function} callback
76
+ */
77
+ getScript: function (scriptLocation, callback) {
78
+ // We cannot assume that Assets class from mootools-more is available so instead insert a script tag to download script.
79
+ var head = doc.getElementsByTagName('head')[0];
80
+ var script = doc.createElement('script');
81
+
82
+ script.type = 'text/javascript';
83
+ script.src = scriptLocation;
84
+ script.onload = callback;
85
+
86
+ head.appendChild(script);
87
+ },
88
+
89
+ /**
90
+ * Animate a HTML element or SVG element wrapper
91
+ * @param {Object} el
92
+ * @param {Object} params
93
+ * @param {Object} options jQuery-like animation options: duration, easing, callback
94
+ */
95
+ animate: function (el, params, options) {
96
+ var isSVGElement = el.attr,
97
+ effect,
98
+ complete = options && options.complete;
99
+
100
+ if (isSVGElement && !el.setStyle) {
101
+ // add setStyle and getStyle methods for internal use in Moo
102
+ el.getStyle = el.attr;
103
+ el.setStyle = function () { // property value is given as array in Moo - break it down
104
+ var args = arguments;
105
+ el.attr.call(el, args[0], args[1][0]);
106
+ };
107
+ // dirty hack to trick Moo into handling el as an element wrapper
108
+ el.$family = function () { return true; };
109
+ }
110
+
111
+ // stop running animations
112
+ win.HighchartsAdapter.stop(el);
113
+
114
+ // define and run the effect
115
+ effect = new Fx.Morph(
116
+ isSVGElement ? el : $(el),
117
+ $extend({
118
+ transition: Fx.Transitions.Quad.easeInOut
119
+ }, options)
120
+ );
121
+
122
+ // Make sure that the element reference is set when animating svg elements
123
+ if (isSVGElement) {
124
+ effect.element = el;
125
+ }
126
+
127
+ // special treatment for paths
128
+ if (params.d) {
129
+ effect.toD = params.d;
130
+ }
131
+
132
+ // jQuery-like events
133
+ if (complete) {
134
+ effect.addEvent('complete', complete);
135
+ }
136
+
137
+ // run
138
+ effect.start(params);
139
+
140
+ // record for use in stop method
141
+ el.fx = effect;
142
+ },
143
+
144
+ /**
145
+ * MooTool's each function
146
+ *
147
+ */
148
+ each: function (arr, fn) {
149
+ return legacy ?
150
+ $each(arr, fn) :
151
+ Array.each(arr, fn);
152
+ },
153
+
154
+ /**
155
+ * Map an array
156
+ * @param {Array} arr
157
+ * @param {Function} fn
158
+ */
159
+ map: function (arr, fn) {
160
+ return arr.map(fn);
161
+ },
162
+
163
+ /**
164
+ * Grep or filter an array
165
+ * @param {Array} arr
166
+ * @param {Function} fn
167
+ */
168
+ grep: function (arr, fn) {
169
+ return arr.filter(fn);
170
+ },
171
+
172
+ /**
173
+ * Deep merge two objects and return a third
174
+ */
175
+ merge: function () {
176
+ var args = arguments,
177
+ args13 = [{}], // MooTools 1.3+
178
+ i = args.length,
179
+ ret;
180
+
181
+ if (legacy) {
182
+ ret = $merge.apply(null, args);
183
+ } else {
184
+ while (i--) {
185
+ // Boolean argumens should not be merged.
186
+ // JQuery explicitly skips this, so we do it here as well.
187
+ if (typeof args[i] !== 'boolean') {
188
+ args13[i + 1] = args[i];
189
+ }
190
+ }
191
+ ret = Object.merge.apply(Object, args13);
192
+ }
193
+
194
+ return ret;
195
+ },
196
+
197
+ /**
198
+ * Get the offset of an element relative to the top left corner of the web page
199
+ */
200
+ offset: function (el) {
201
+ var offsets = $(el).getOffsets();
202
+ return {
203
+ left: offsets.x,
204
+ top: offsets.y
205
+ };
206
+ },
207
+
208
+ /**
209
+ * Extends an object with Events, if its not done
210
+ */
211
+ extendWithEvents: function (el) {
212
+ // if the addEvent method is not defined, el is a custom Highcharts object
213
+ // like series or point
214
+ if (!el.addEvent) {
215
+ if (el.nodeName) {
216
+ el = $(el); // a dynamically generated node
217
+ } else {
218
+ $extend(el, new Events()); // a custom object
219
+ }
220
+ }
221
+ },
222
+
223
+ /**
224
+ * Add an event listener
225
+ * @param {Object} el HTML element or custom object
226
+ * @param {String} type Event type
227
+ * @param {Function} fn Event handler
228
+ */
229
+ addEvent: function (el, type, fn) {
230
+ if (typeof type === 'string') { // chart broke due to el being string, type function
231
+
232
+ if (type === 'unload') { // Moo self destructs before custom unload events
233
+ type = 'beforeunload';
234
+ }
235
+
236
+ win.HighchartsAdapter.extendWithEvents(el);
237
+
238
+ el.addEvent(type, fn);
239
+ }
240
+ },
241
+
242
+ removeEvent: function (el, type, fn) {
243
+ if (typeof el === 'string') {
244
+ // el.removeEvents below apperantly calls this method again. Do not quite understand why, so for now just bail out.
245
+ return;
246
+ }
247
+ win.HighchartsAdapter.extendWithEvents(el);
248
+ if (type) {
249
+ if (type === 'unload') { // Moo self destructs before custom unload events
250
+ type = 'beforeunload';
251
+ }
252
+
253
+ if (fn) {
254
+ el.removeEvent(type, fn);
255
+ } else {
256
+ el.removeEvents(type);
257
+ }
258
+ } else {
259
+ el.removeEvents();
260
+ }
261
+ },
262
+
263
+ fireEvent: function (el, event, eventArguments, defaultFunction) {
264
+ var eventArgs = {
265
+ type: event,
266
+ target: el
267
+ };
268
+ // create an event object that keeps all functions
269
+ event = legacyEvent ? new Event(eventArgs) : new DOMEvent(eventArgs);
270
+ event = $extend(event, eventArguments);
271
+ // override the preventDefault function to be able to use
272
+ // this for custom events
273
+ event.preventDefault = function () {
274
+ defaultFunction = null;
275
+ };
276
+ // if fireEvent is not available on the object, there hasn't been added
277
+ // any events to it above
278
+ if (el.fireEvent) {
279
+ el.fireEvent(event.type, event);
280
+ }
281
+
282
+ // fire the default if it is passed and it is not prevented above
283
+ if (defaultFunction) {
284
+ defaultFunction(event);
285
+ }
286
+ },
287
+
288
+ /**
289
+ * Stop running animations on the object
290
+ */
291
+ stop: function (el) {
292
+ if (el.fx) {
293
+ el.fx.cancel();
294
+ }
295
+ }
296
+ };
297
+
298
+ }());