ghazel-ghazel-googlecharts 1.4.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +48 -0
- data/License.txt +20 -0
- data/Manifest.txt +18 -0
- data/README.txt +9 -0
- data/Rakefile +32 -0
- data/config/hoe.rb +71 -0
- data/config/requirements.rb +16 -0
- data/lib/gchart.rb +583 -0
- data/lib/gchart/aliases.rb +14 -0
- data/lib/gchart/theme.rb +46 -0
- data/lib/gchart/version.rb +10 -0
- data/lib/themes.yml +45 -0
- data/setup.rb +1585 -0
- data/spec/fixtures/test_theme.yml +8 -0
- data/spec/gchart_spec.rb +567 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +7 -0
- data/tasks/environment.rake +7 -0
- data/tasks/rspec.rake +21 -0
- data/website/index.txt +570 -0
- metadata +77 -0
data/History.txt
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
== 1.3.6
|
2
|
+
* support nil values. The Google Charts API specifies that a single underscore (_) can be used to omit a value from a line chart with 'simple' data encoding, and a double underscore (__) can do the same for a chart with 'extended' data encoding. (Matt Moyer)
|
3
|
+
* allow a label to appear on a google-o-meter via the :legend option. (hallettj)
|
4
|
+
|
5
|
+
== 1.3.5
|
6
|
+
* added code to properly escape image tag URLs (mokolabs)
|
7
|
+
* added themes support + 4 default themes (keynote, thirty7signals, pastel, greyscale) chart.line(:theme=>:keynote) (jakehow)
|
8
|
+
|
9
|
+
== 1.3.4
|
10
|
+
* updated documentation and cleaned it up (mokolabs)
|
11
|
+
* added support for custom class, id and alt tags when using the image_tag format (i.e Gchart.line(:data => [0, 26], :format => 'image_tag')) (mokolabs)
|
12
|
+
|
13
|
+
== 1.3.2 - 1.3.3
|
14
|
+
* changes required by github
|
15
|
+
|
16
|
+
== 1.3.1
|
17
|
+
* added width and spacing options
|
18
|
+
|
19
|
+
== 1.3.0
|
20
|
+
* added support for google-o-meter
|
21
|
+
* fixed a bug when the max value of a data set was 0
|
22
|
+
|
23
|
+
== 1.2.0
|
24
|
+
* added support for sparklines
|
25
|
+
|
26
|
+
== 1.1.0
|
27
|
+
* fixed another bug fix related to the uri escaping required to download the file properly.
|
28
|
+
|
29
|
+
== 1.0.0
|
30
|
+
* fixed the (URI::InvalidURIError) issue
|
31
|
+
|
32
|
+
== 0.2.0
|
33
|
+
* added export options (file and image tag)
|
34
|
+
* added support for all arguments to be passed as a string or an array
|
35
|
+
|
36
|
+
== 0.1.0 2007-12-11
|
37
|
+
* fixed the axis labels
|
38
|
+
|
39
|
+
== 0.0.3 2007-12-11
|
40
|
+
* added :chart_background alias and fixed a bug related to the background colors.
|
41
|
+
|
42
|
+
== 0.0.2 2007-12-11
|
43
|
+
* added support for more features and aliases
|
44
|
+
|
45
|
+
== 0.0.1 2007-12-08
|
46
|
+
|
47
|
+
* 1 major enhancement:
|
48
|
+
* Initial release
|
data/License.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2007 Matt Aimonetti
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
History.txt
|
2
|
+
License.txt
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
config/hoe.rb
|
7
|
+
config/requirements.rb
|
8
|
+
lib/gchart.rb
|
9
|
+
lib/gchart/aliases.rb
|
10
|
+
lib/gchart/theme.rb
|
11
|
+
lib/gchart/version.rb
|
12
|
+
setup.rb
|
13
|
+
spec/gchart_spec.rb
|
14
|
+
spec/theme_spec.rb
|
15
|
+
spec/spec.opts
|
16
|
+
spec/spec_helper.rb
|
17
|
+
tasks/environment.rake
|
18
|
+
tasks/rspec.rake
|
data/README.txt
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'config/requirements'
|
2
|
+
require 'config/hoe' # setup Hoe + all gem configuration
|
3
|
+
|
4
|
+
Dir['tasks/**/*.rake'].each { |rake| load rake }
|
5
|
+
|
6
|
+
desc %{Update ".manifest" with the latest list of project filenames. Respect\
|
7
|
+
.gitignore by excluding everything that git ignores. Update `files` and\
|
8
|
+
`test_files` arrays in "*.gemspec" file if it's present.}
|
9
|
+
task :manifest do
|
10
|
+
list = Dir['**/*'].sort
|
11
|
+
spec_file = Dir['*.gemspec'].first
|
12
|
+
list -= [spec_file] if spec_file
|
13
|
+
|
14
|
+
File.read('.gitignore').each_line do |glob|
|
15
|
+
glob = glob.chomp.sub(/^\//, '')
|
16
|
+
list -= Dir[glob]
|
17
|
+
list -= Dir["#{glob}/**/*"] if File.directory?(glob) and !File.symlink?(glob)
|
18
|
+
puts "excluding #{glob}"
|
19
|
+
end
|
20
|
+
|
21
|
+
if spec_file
|
22
|
+
spec = File.read spec_file
|
23
|
+
spec.gsub! /^(\s* s.(test_)?files \s* = \s* )( \[ [^\]]* \] | %w\( [^)]* \) )/mx do
|
24
|
+
assignment = $1
|
25
|
+
bunch = $2 ? list.grep(/^test\//) : list
|
26
|
+
'%s%%w(%s)' % [assignment, bunch.join(' ')]
|
27
|
+
end
|
28
|
+
|
29
|
+
File.open(spec_file, 'w') {|f| f << spec }
|
30
|
+
end
|
31
|
+
File.open('.manifest', 'w') {|f| f << list.join("\n") }
|
32
|
+
end
|
data/config/hoe.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'gchart/version'
|
2
|
+
|
3
|
+
AUTHOR = 'Matt Aimonetti' # can also be an array of Authors
|
4
|
+
EMAIL = "mattaimonetti@gmail.com"
|
5
|
+
DESCRIPTION = "description of gem"
|
6
|
+
GEM_NAME = 'googlecharts' # what ppl will type to install your gem
|
7
|
+
RUBYFORGE_PROJECT = 'googlecharts' # The unix name for your project
|
8
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
9
|
+
DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
|
10
|
+
|
11
|
+
@config_file = "~/.rubyforge/user-config.yml"
|
12
|
+
@config = nil
|
13
|
+
RUBYFORGE_USERNAME = "matt_a"
|
14
|
+
def rubyforge_username
|
15
|
+
unless @config
|
16
|
+
begin
|
17
|
+
@config = YAML.load(File.read(File.expand_path(@config_file)))
|
18
|
+
rescue
|
19
|
+
puts <<-EOS
|
20
|
+
ERROR: No rubyforge config file found: #{@config_file}
|
21
|
+
Run 'rubyforge setup' to prepare your env for access to Rubyforge
|
22
|
+
- See http://newgem.rubyforge.org/rubyforge.html for more details
|
23
|
+
EOS
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
end
|
27
|
+
RUBYFORGE_USERNAME.replace @config["username"]
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
REV = nil
|
32
|
+
# UNCOMMENT IF REQUIRED:
|
33
|
+
# REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
|
34
|
+
VERS = GchartInfo::VERSION::STRING + (REV ? ".#{REV}" : "")
|
35
|
+
RDOC_OPTS = ['--quiet', '--title', 'gchart documentation',
|
36
|
+
"--opname", "index.html",
|
37
|
+
"--line-numbers",
|
38
|
+
"--main", "README",
|
39
|
+
"--inline-source"]
|
40
|
+
|
41
|
+
class Hoe
|
42
|
+
def extra_deps
|
43
|
+
@extra_deps.reject! { |x| Array(x).first == 'hoe' }
|
44
|
+
@extra_deps
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Generate all the Rake tasks
|
49
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
50
|
+
hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
51
|
+
p.author = AUTHOR
|
52
|
+
p.description = DESCRIPTION
|
53
|
+
p.email = EMAIL
|
54
|
+
p.summary = DESCRIPTION
|
55
|
+
p.url = HOMEPATH
|
56
|
+
p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
|
57
|
+
p.test_globs = ["test/**/test_*.rb"]
|
58
|
+
p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
|
59
|
+
|
60
|
+
# == Optional
|
61
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
62
|
+
#p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
|
63
|
+
|
64
|
+
#p.spec_extras = {} # A hash of extra values to set in the gemspec.
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
|
69
|
+
PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
|
70
|
+
hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
|
71
|
+
hoe.rsync_args = '-av --delete --ignore-errors'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
include FileUtils
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
%w[rake hoe newgem rubigen].each do |req_gem|
|
6
|
+
begin
|
7
|
+
require req_gem
|
8
|
+
rescue LoadError
|
9
|
+
puts "This Rakefile could use '#{req_gem}' RubyGem."
|
10
|
+
puts "Installation: gem install #{req_gem} -y"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
$:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
|
15
|
+
|
16
|
+
require 'gchart'
|
data/lib/gchart.rb
ADDED
@@ -0,0 +1,583 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
require 'gchart/version'
|
3
|
+
require 'gchart/theme'
|
4
|
+
require "open-uri"
|
5
|
+
require "uri"
|
6
|
+
require "cgi"
|
7
|
+
require 'enumerator'
|
8
|
+
|
9
|
+
|
10
|
+
class Gchart
|
11
|
+
|
12
|
+
include GchartInfo
|
13
|
+
|
14
|
+
@@url = "http://chart.apis.google.com/chart?"
|
15
|
+
@@types = ['line', 'line_xy', 'scatter', 'bar', 'venn', 'pie', 'pie_3d', 'jstize', 'sparkline', 'meter', 'map']
|
16
|
+
@@simple_chars = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a
|
17
|
+
@@chars = @@simple_chars + ['-', '.']
|
18
|
+
@@ext_pairs = @@chars.map { |char_1| @@chars.map { |char_2| char_1 + char_2 } }.flatten
|
19
|
+
@@file_name = 'chart.png'
|
20
|
+
|
21
|
+
attr_accessor :title, :type, :width, :height, :horizontal, :grouped, :legend,
|
22
|
+
:data, :encoding, :bar_colors, :title_color,
|
23
|
+
:title_size, :custom, :axis_with_labels, :axis_labels,
|
24
|
+
:bar_width_and_spacing, :id, :alt, :class, :range_markers,
|
25
|
+
:geographical_area, :map_colors, :country_codes, :axis_range
|
26
|
+
|
27
|
+
# Support for Gchart.line(:title => 'my title', :size => '400x600')
|
28
|
+
def self.method_missing(m, options={})
|
29
|
+
# Start with theme defaults if a theme is set
|
30
|
+
theme = options[:theme]
|
31
|
+
options = theme ? Chart::Theme.load(theme).to_options.merge(options) : options
|
32
|
+
# Extract the format and optional filename, then clean the hash
|
33
|
+
format = options[:format] || 'url'
|
34
|
+
@@file_name = options[:filename] unless options[:filename].nil?
|
35
|
+
options.delete(:format)
|
36
|
+
options.delete(:filename)
|
37
|
+
#update map_colors to be bar_colors
|
38
|
+
options.update(:bar_colors => options[:map_colors]) if options.has_key?(:map_colors)
|
39
|
+
# create the chart and return it in the format asked for
|
40
|
+
if @@types.include?(m.to_s)
|
41
|
+
chart = new(options.merge!({:type => m}))
|
42
|
+
chart.send(format)
|
43
|
+
elsif m.to_s == 'version'
|
44
|
+
Gchart::VERSION::STRING
|
45
|
+
else
|
46
|
+
"#{m} is not a supported chart format, please use one of the following: #{supported_types}."
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialize(options={})
|
51
|
+
@type = :line
|
52
|
+
@width = 300
|
53
|
+
@height = 200
|
54
|
+
@horizontal = false
|
55
|
+
@grouped = false
|
56
|
+
@encoding = 'simple'
|
57
|
+
# Sets the alt tag when chart is exported as image tag
|
58
|
+
@alt = 'Google Chart'
|
59
|
+
# Sets the CSS id selector when chart is exported as image tag
|
60
|
+
@id = false
|
61
|
+
# Sets the CSS class selector when chart is exported as image tag
|
62
|
+
@class = false
|
63
|
+
|
64
|
+
# set the options value if definable
|
65
|
+
options.each do |attribute, value|
|
66
|
+
send("#{attribute.to_s}=", value) if self.respond_to?("#{attribute}=")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.supported_types
|
71
|
+
@@types.join(' ')
|
72
|
+
end
|
73
|
+
|
74
|
+
# Defines the Graph size using the following format:
|
75
|
+
# width X height
|
76
|
+
def size=(size='300x200')
|
77
|
+
@width, @height = size.split("x").map { |dimension| dimension.to_i }
|
78
|
+
end
|
79
|
+
|
80
|
+
def size
|
81
|
+
"#{@width}x#{@height}"
|
82
|
+
end
|
83
|
+
|
84
|
+
def dimensions
|
85
|
+
# TODO: maybe others?
|
86
|
+
[:line_xy, :scatter].include?(@type) ? 2 : 1
|
87
|
+
end
|
88
|
+
|
89
|
+
# Sets the orientation of a bar graph
|
90
|
+
def orientation=(orientation='h')
|
91
|
+
if orientation == 'h' || orientation == 'horizontal'
|
92
|
+
self.horizontal = true
|
93
|
+
elsif orientation == 'v' || orientation == 'vertical'
|
94
|
+
self.horizontal = false
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Sets the bar graph presentation (stacked or grouped)
|
99
|
+
def stacked=(option=true)
|
100
|
+
@grouped = option ? false : true
|
101
|
+
end
|
102
|
+
|
103
|
+
def bg=(options)
|
104
|
+
if options.is_a?(String)
|
105
|
+
@bg_color = options
|
106
|
+
elsif options.is_a?(Hash)
|
107
|
+
@bg_color = options[:color]
|
108
|
+
@bg_type = options[:type]
|
109
|
+
@bg_angle = options[:angle]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def graph_bg=(options)
|
114
|
+
if options.is_a?(String)
|
115
|
+
@chart_color = options
|
116
|
+
elsif options.is_a?(Hash)
|
117
|
+
@chart_color = options[:color]
|
118
|
+
@chart_type = options[:type]
|
119
|
+
@chart_angle = options[:angle]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def max_value=(max_value)
|
124
|
+
@max_value = max_value
|
125
|
+
@max_value = nil if ['auto', :auto].include? @max_value
|
126
|
+
@max_value = false if ['false', :false].include? @max_value
|
127
|
+
end
|
128
|
+
|
129
|
+
def min_value=(min_value)
|
130
|
+
@min_value = min_value
|
131
|
+
@min_value = nil if ['auto', :auto].include? @min_value
|
132
|
+
@min_value = false if ['false', :false].include? @min_value
|
133
|
+
end
|
134
|
+
|
135
|
+
# auto sets the range if required
|
136
|
+
# it also sets the axis_range if not defined
|
137
|
+
def full_data_range(ds)
|
138
|
+
return if @max_value == false
|
139
|
+
|
140
|
+
ds.each_with_index do |mds, mds_index|
|
141
|
+
# global limits override individuals. is this preferred?
|
142
|
+
mds[:min_value] = @min_value if not @min_value.nil?
|
143
|
+
mds[:max_value] = @max_value if not @max_value.nil?
|
144
|
+
|
145
|
+
if mds_index == 0 and @type == :bar
|
146
|
+
# TODO: unless you specify a zero line (using chp or chds),
|
147
|
+
# the min_value of a bar chart is always 0.
|
148
|
+
#mds[:min_value] ||= mds[:data].first.to_a.compact.min
|
149
|
+
mds[:min_value] ||= 0
|
150
|
+
end
|
151
|
+
if (mds_index == 0 and @type == :bar and
|
152
|
+
not grouped and mds[:data].first.is_a?(Array))
|
153
|
+
totals = []
|
154
|
+
mds[:data].each do |l|
|
155
|
+
l.each_with_index do |v, index|
|
156
|
+
next if v.nil?
|
157
|
+
totals[index] ||= 0
|
158
|
+
totals[index] += v
|
159
|
+
end
|
160
|
+
end
|
161
|
+
mds[:max_value] ||= totals.compact.max
|
162
|
+
else
|
163
|
+
all = mds[:data].flatten.compact
|
164
|
+
mds[:min_value] ||= all.min
|
165
|
+
mds[:max_value] ||= all.max
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
if not @axis_range
|
170
|
+
@axis_range = ds.map{|mds| [mds[:min_value], mds[:max_value]]}
|
171
|
+
if dimensions == 1 and (@type != :bar or not @horizontal)
|
172
|
+
tmp = @axis_range.fetch(0, [])
|
173
|
+
@axis_range[0] = @axis_range.fetch(1, [])
|
174
|
+
@axis_range[1] = tmp
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def number_visible
|
180
|
+
n = 0
|
181
|
+
axis_set.each do |mds|
|
182
|
+
return n.to_s if mds[:invisible] == true
|
183
|
+
if mds[:data].first.is_a?(Array)
|
184
|
+
n += mds[:data].length
|
185
|
+
else
|
186
|
+
n += 1
|
187
|
+
end
|
188
|
+
end
|
189
|
+
""
|
190
|
+
end
|
191
|
+
|
192
|
+
# Turns input into an array of axis hashes, dependent on the chart type
|
193
|
+
def convert_dataset(ds)
|
194
|
+
if dimensions == 2
|
195
|
+
# valid inputs include:
|
196
|
+
# an array of >=2 arrays, or an array of >=2 hashes
|
197
|
+
ds = ds.map do |d|
|
198
|
+
d.is_a?(Hash) ? d : {:data => d}
|
199
|
+
end
|
200
|
+
elsif dimensions == 1
|
201
|
+
# valid inputs include:
|
202
|
+
# a hash, an array of data, an array of >=1 array, or an array of >=1 hash
|
203
|
+
if ds.is_a?(Hash)
|
204
|
+
ds = [ds]
|
205
|
+
elsif not ds.first.is_a?(Hash)
|
206
|
+
ds = [{:data => ds}]
|
207
|
+
end
|
208
|
+
end
|
209
|
+
ds
|
210
|
+
end
|
211
|
+
|
212
|
+
def prepare_dataset
|
213
|
+
@dataset = convert_dataset(data || [])
|
214
|
+
full_data_range(@dataset)
|
215
|
+
end
|
216
|
+
|
217
|
+
def axis_set
|
218
|
+
@dataset
|
219
|
+
end
|
220
|
+
|
221
|
+
def dataset
|
222
|
+
datasets = []
|
223
|
+
@dataset.each do |d|
|
224
|
+
if d[:data].first.is_a?(Array)
|
225
|
+
datasets += d[:data]
|
226
|
+
else
|
227
|
+
datasets << d[:data]
|
228
|
+
end
|
229
|
+
end
|
230
|
+
datasets
|
231
|
+
end
|
232
|
+
|
233
|
+
def self.jstize(string)
|
234
|
+
string.gsub(' ', '+').gsub(/\[|\{|\}|\||\\|\^|\[|\]|\`|\]/) {|c| "%#{c[0].to_s(16).upcase}"}
|
235
|
+
end
|
236
|
+
# load all the custom aliases
|
237
|
+
require 'gchart/aliases'
|
238
|
+
|
239
|
+
protected
|
240
|
+
|
241
|
+
# Returns the chart's generated PNG as a blob. (borrowed from John's gchart.rubyforge.org)
|
242
|
+
def fetch
|
243
|
+
open(query_builder) { |io| io.read }
|
244
|
+
end
|
245
|
+
|
246
|
+
# Writes the chart's generated PNG to a file. (borrowed from John's gchart.rubyforge.org)
|
247
|
+
def write(io_or_file=@@file_name)
|
248
|
+
return io_or_file.write(fetch) if io_or_file.respond_to?(:write)
|
249
|
+
open(io_or_file, "w+") { |io| io.write(fetch) }
|
250
|
+
end
|
251
|
+
|
252
|
+
# Format
|
253
|
+
|
254
|
+
def image_tag
|
255
|
+
image = "<img"
|
256
|
+
image += " id=\"#{@id}\"" if @id
|
257
|
+
image += " class=\"#{@class}\"" if @class
|
258
|
+
image += " src=\"#{query_builder(:html)}\""
|
259
|
+
image += " width=\"#{@width}\""
|
260
|
+
image += " height=\"#{@height}\""
|
261
|
+
image += " alt=\"#{@alt}\""
|
262
|
+
image += " title=\"#{@title}\"" if @title
|
263
|
+
image += " />"
|
264
|
+
end
|
265
|
+
|
266
|
+
alias_method :img_tag, :image_tag
|
267
|
+
|
268
|
+
def url
|
269
|
+
query_builder
|
270
|
+
end
|
271
|
+
|
272
|
+
def file
|
273
|
+
write
|
274
|
+
end
|
275
|
+
|
276
|
+
#
|
277
|
+
def jstize(string)
|
278
|
+
Gchart.jstize(string)
|
279
|
+
end
|
280
|
+
|
281
|
+
private
|
282
|
+
|
283
|
+
# The title size cannot be set without specifying a color.
|
284
|
+
# A dark key will be used for the title color if no color is specified
|
285
|
+
def set_title
|
286
|
+
title_params = "chtt=#{title}"
|
287
|
+
unless (title_color.nil? && title_size.nil? )
|
288
|
+
title_params << "&chts=" + (color, size = (@title_color || '454545'), @title_size).compact.join(',')
|
289
|
+
end
|
290
|
+
title_params
|
291
|
+
end
|
292
|
+
|
293
|
+
def set_size
|
294
|
+
"chs=#{size}"
|
295
|
+
end
|
296
|
+
|
297
|
+
def set_data
|
298
|
+
data = send("#{@encoding}_encoding")
|
299
|
+
"chd=#{data}"
|
300
|
+
end
|
301
|
+
|
302
|
+
def set_colors
|
303
|
+
bg_type = fill_type(@bg_type) || 's' if @bg_color
|
304
|
+
chart_type = fill_type(@chart_type) || 's' if @chart_color
|
305
|
+
|
306
|
+
"chf=" + {'bg' => fill_for(bg_type, @bg_color, @bg_angle), 'c' => fill_for(chart_type, @chart_color, @chart_angle)}.map{|k,v| "#{k},#{v}" unless v.nil?}.compact.join('|')
|
307
|
+
end
|
308
|
+
|
309
|
+
# set bar, line colors
|
310
|
+
def set_bar_colors
|
311
|
+
@bar_colors = @bar_colors.join(',') if @bar_colors.is_a?(Array)
|
312
|
+
"chco=#{@bar_colors}"
|
313
|
+
end
|
314
|
+
|
315
|
+
def set_country_codes
|
316
|
+
@country_codes = @country_codes.join() if @country_codes.is_a?(Array)
|
317
|
+
"chld=#{@country_codes}"
|
318
|
+
end
|
319
|
+
|
320
|
+
# set bar spacing
|
321
|
+
# chbh=
|
322
|
+
# <bar width in pixels>,
|
323
|
+
# <optional space between bars in a group>,
|
324
|
+
# <optional space between groups>
|
325
|
+
def set_bar_width_and_spacing
|
326
|
+
width_and_spacing_values = case @bar_width_and_spacing
|
327
|
+
when String
|
328
|
+
@bar_width_and_spacing
|
329
|
+
when Array
|
330
|
+
@bar_width_and_spacing.join(',')
|
331
|
+
when Hash
|
332
|
+
width = @bar_width_and_spacing[:width] || 23
|
333
|
+
spacing = @bar_width_and_spacing[:spacing] || 4
|
334
|
+
group_spacing = @bar_width_and_spacing[:group_spacing] || 8
|
335
|
+
[width,spacing,group_spacing].join(',')
|
336
|
+
else
|
337
|
+
@bar_width_and_spacing.to_s
|
338
|
+
end
|
339
|
+
"chbh=#{width_and_spacing_values}"
|
340
|
+
end
|
341
|
+
|
342
|
+
def set_range_markers
|
343
|
+
markers = case @range_markers
|
344
|
+
when Hash
|
345
|
+
set_range_marker(@range_markers)
|
346
|
+
when Array
|
347
|
+
range_markers.collect{|marker| set_range_marker(marker)}.join('|')
|
348
|
+
end
|
349
|
+
"chm=#{markers}"
|
350
|
+
end
|
351
|
+
|
352
|
+
def set_range_marker(options)
|
353
|
+
orientation = ['vertical', 'Vertical', 'V', 'v', 'R'].include?(options[:orientation]) ? 'R' : 'r'
|
354
|
+
"#{orientation},#{options[:color]},0,#{options[:start_position]},#{options[:stop_position]}#{',1' if options[:overlaid?]}"
|
355
|
+
end
|
356
|
+
|
357
|
+
def fill_for(type=nil, color='', angle=nil)
|
358
|
+
unless type.nil?
|
359
|
+
case type
|
360
|
+
when 'lg'
|
361
|
+
angle ||= 0
|
362
|
+
color = "#{color},0,ffffff,1" if color.split(',').size == 1
|
363
|
+
"#{type},#{angle},#{color}"
|
364
|
+
when 'ls'
|
365
|
+
angle ||= 90
|
366
|
+
color = "#{color},0.2,ffffff,0.2" if color.split(',').size == 1
|
367
|
+
"#{type},#{angle},#{color}"
|
368
|
+
else
|
369
|
+
"#{type},#{color}"
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
# A chart can have one or many legends.
|
375
|
+
# Gchart.line(:legend => 'label')
|
376
|
+
# or
|
377
|
+
# Gchart.line(:legend => ['first label', 'last label'])
|
378
|
+
def set_legend
|
379
|
+
return set_labels if @type == :pie || @type == :pie_3d || @type == :meter
|
380
|
+
|
381
|
+
if @legend.is_a?(Array)
|
382
|
+
"chdl=#{@legend.map{|label| "#{CGI::escape(label)}"}.join('|')}"
|
383
|
+
else
|
384
|
+
"chdl=#{@legend}"
|
385
|
+
end
|
386
|
+
|
387
|
+
end
|
388
|
+
|
389
|
+
def set_labels
|
390
|
+
if @legend.is_a?(Array)
|
391
|
+
"chl=#{@legend.map{|label| "#{CGI::escape(label)}"}.join('|')}"
|
392
|
+
else
|
393
|
+
"chl=#{@legend}"
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
def set_axis_with_labels
|
398
|
+
@axis_with_labels = @axis_with_labels.join(',') if @axis_with_labels.is_a?(Array)
|
399
|
+
"chxt=#{@axis_with_labels}"
|
400
|
+
end
|
401
|
+
|
402
|
+
def set_axis_labels
|
403
|
+
if axis_labels.is_a?(Array)
|
404
|
+
labels_arr = axis_labels.enum_with_index.map{|labels,index| [index,labels]}
|
405
|
+
elsif axis_labels.is_a?(Hash)
|
406
|
+
labels_arr = axis_labels.to_a
|
407
|
+
end
|
408
|
+
labels_arr.map! do |index,labels|
|
409
|
+
if labels.is_a?(Array)
|
410
|
+
"#{index}:|#{labels.to_a.join('|')}"
|
411
|
+
else
|
412
|
+
"#{index}:|#{labels}"
|
413
|
+
end
|
414
|
+
end
|
415
|
+
"chxl=#{labels_arr.join('|')}"
|
416
|
+
end
|
417
|
+
|
418
|
+
# http://code.google.com/apis/chart/labels.html#axis_range
|
419
|
+
# Specify a range for axis labels
|
420
|
+
def set_axis_range
|
421
|
+
# a passed axis_range should look like:
|
422
|
+
# [[10,100]] or [[10,100,4]] or [[10,100], [20,300]]
|
423
|
+
# in the second example, 4 is the interval
|
424
|
+
if axis_range && axis_range.respond_to?(:each) && axis_range.first.respond_to?(:each)
|
425
|
+
'chxr=' + axis_range.enum_for(:each_with_index).map{|range, index| [index, range[0], range[1], range[2]].compact.join(',')}.join("|")
|
426
|
+
else
|
427
|
+
nil
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
def set_geographical_area
|
432
|
+
"chtm=#{@geographical_area}"
|
433
|
+
end
|
434
|
+
|
435
|
+
def set_type
|
436
|
+
case @type
|
437
|
+
when :line
|
438
|
+
"cht=lc"
|
439
|
+
when :line_xy
|
440
|
+
"cht=lxy"
|
441
|
+
when :bar
|
442
|
+
"cht=b" + (horizontal? ? "h" : "v") + (grouped? ? "g" : "s")
|
443
|
+
when :pie_3d
|
444
|
+
"cht=p3"
|
445
|
+
when :pie
|
446
|
+
"cht=p"
|
447
|
+
when :venn
|
448
|
+
"cht=v"
|
449
|
+
when :scatter
|
450
|
+
"cht=s"
|
451
|
+
when :sparkline
|
452
|
+
"cht=ls"
|
453
|
+
when :meter
|
454
|
+
"cht=gom"
|
455
|
+
when :map
|
456
|
+
"cht=t"
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
def fill_type(type)
|
461
|
+
case type
|
462
|
+
when 'solid'
|
463
|
+
's'
|
464
|
+
when 'gradient'
|
465
|
+
'lg'
|
466
|
+
when 'stripes'
|
467
|
+
'ls'
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
def encode_scaled_dataset chars, nil_char
|
472
|
+
dsets = []
|
473
|
+
axis_set.each do |ds|
|
474
|
+
if @max_value != false
|
475
|
+
range = ds[:max_value] - ds[:min_value]
|
476
|
+
range = 1 if range == 0
|
477
|
+
end
|
478
|
+
if not ds[:data].first.is_a?(Array)
|
479
|
+
datasets = [ds[:data]]
|
480
|
+
else
|
481
|
+
datasets = ds[:data]
|
482
|
+
end
|
483
|
+
datasets.each do |l|
|
484
|
+
dsets << l.map do |number|
|
485
|
+
if number.nil?
|
486
|
+
nil_char
|
487
|
+
else
|
488
|
+
if not range.nil?
|
489
|
+
number = chars.size * (number - ds[:min_value]) / range.to_f
|
490
|
+
number = [number, chars.size - 1].min
|
491
|
+
end
|
492
|
+
chars[number.to_i]
|
493
|
+
end
|
494
|
+
end.join
|
495
|
+
end
|
496
|
+
end
|
497
|
+
dsets.join(',')
|
498
|
+
end
|
499
|
+
|
500
|
+
# http://code.google.com/apis/chart/#simple
|
501
|
+
# Simple encoding has a resolution of 62 different values.
|
502
|
+
# Allowing five pixels per data point, this is sufficient for line and bar charts up
|
503
|
+
# to about 300 pixels. Simple encoding is suitable for all other types of chart regardless of size.
|
504
|
+
def simple_encoding
|
505
|
+
"s" + number_visible + ":" + encode_scaled_dataset(@@simple_chars, '_')
|
506
|
+
end
|
507
|
+
|
508
|
+
# http://code.google.com/apis/chart/#text
|
509
|
+
# Text encoding with data scaling lets you specify arbitrary positive or
|
510
|
+
# negative floating point numbers, in combination with a scaling parameter
|
511
|
+
# that lets you specify a custom range for your chart. This chart is useful
|
512
|
+
# when you don't want to worry about limiting your data to a specific range,
|
513
|
+
# or do the calculations to scale your data down or up to fit nicely inside
|
514
|
+
# a chart.
|
515
|
+
#
|
516
|
+
# Valid values range from (+/-)9.999e(+/-)100, and only four non-zero digits are supported (that is, 123400, 1234, 12.34, and 0.1234 are valid, but 12345, 123.45 and 123400.5 are not).
|
517
|
+
#
|
518
|
+
# This encoding is not available for maps.
|
519
|
+
#
|
520
|
+
def text_encoding
|
521
|
+
chds = axis_set.map{ |ds| "#{ds[:min_value]},#{ds[:max_value]}" }.join(",")
|
522
|
+
"t" + number_visible + ":" + dataset.map{ |ds| ds.join(',') }.join('|') + "&chds=" + chds
|
523
|
+
end
|
524
|
+
|
525
|
+
# http://code.google.com/apis/chart/#extended
|
526
|
+
# Extended encoding has a resolution of 4,096 different values
|
527
|
+
# and is best used for large charts where a large data range is required.
|
528
|
+
def extended_encoding
|
529
|
+
"e" + number_visible + ":" + encode_scaled_dataset(@@ext_pairs, '__')
|
530
|
+
end
|
531
|
+
|
532
|
+
def query_builder(options="")
|
533
|
+
prepare_dataset
|
534
|
+
query_params = instance_variables.map do |var|
|
535
|
+
case var
|
536
|
+
when '@data'
|
537
|
+
set_data unless @data == []
|
538
|
+
# Set the graph size
|
539
|
+
when '@width'
|
540
|
+
set_size unless @width.nil? || @height.nil?
|
541
|
+
when '@type'
|
542
|
+
set_type
|
543
|
+
when '@title'
|
544
|
+
set_title unless @title.nil?
|
545
|
+
when '@legend'
|
546
|
+
set_legend unless @legend.nil?
|
547
|
+
when '@bg_color'
|
548
|
+
set_colors
|
549
|
+
when '@chart_color'
|
550
|
+
set_colors if @bg_color.nil?
|
551
|
+
when '@bar_colors'
|
552
|
+
set_bar_colors
|
553
|
+
when '@bar_width_and_spacing'
|
554
|
+
set_bar_width_and_spacing
|
555
|
+
when '@axis_with_labels'
|
556
|
+
set_axis_with_labels
|
557
|
+
when '@axis_range'
|
558
|
+
set_axis_range if dataset
|
559
|
+
when '@axis_labels'
|
560
|
+
set_axis_labels
|
561
|
+
when '@range_markers'
|
562
|
+
set_range_markers
|
563
|
+
when '@geographical_area'
|
564
|
+
set_geographical_area
|
565
|
+
when '@country_codes'
|
566
|
+
set_country_codes
|
567
|
+
when '@custom'
|
568
|
+
@custom
|
569
|
+
end
|
570
|
+
end.compact
|
571
|
+
|
572
|
+
# Use ampersand as default delimiter
|
573
|
+
unless options == :html
|
574
|
+
delimiter = '&'
|
575
|
+
# Escape ampersand for html image tags
|
576
|
+
else
|
577
|
+
delimiter = '&'
|
578
|
+
end
|
579
|
+
|
580
|
+
jstize(@@url + query_params.join(delimiter))
|
581
|
+
end
|
582
|
+
|
583
|
+
end
|