kenhirakawa-googlecharts 1.5.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/gchart.rb ADDED
@@ -0,0 +1,698 @@
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
+ class Gchart
10
+ include GchartInfo
11
+
12
+ def self.url
13
+ "http://chart.apis.google.com/chart?"
14
+ end
15
+
16
+ def self.types
17
+ @types ||= ['line', 'line_xy', 'scatter', 'bar', 'venn', 'pie', 'pie_3d', 'jstize', 'sparkline', 'meter', 'map']
18
+ end
19
+
20
+ def self.simple_chars
21
+ @simple_chars ||= ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a
22
+ end
23
+
24
+ def self.chars
25
+ @chars ||= simple_chars + ['-', '.']
26
+ end
27
+
28
+ def self.ext_pairs
29
+ @ext_pairs ||= chars.map { |char_1| chars.map { |char_2| char_1 + char_2 } }.flatten
30
+ end
31
+
32
+ def self.default_filename
33
+ 'chart.png'
34
+ end
35
+
36
+ attr_accessor :title, :type, :width, :height, :horizontal, :grouped, :legend, :data, :encoding, :bar_colors,
37
+ :title_color, :title_size, :custom, :axis_with_labels, :axis_labels, :bar_width_and_spacing, :id, :alt, :klass,
38
+ :range_markers, :geographical_area, :map_colors, :country_codes, :axis_range, :filename, :min, :max, :colors
39
+
40
+ attr_accessor :bg_type, :bg_color, :bg_angle, :chart_type, :chart_color, :chart_angle, :axis_range, :thickness, :new_markers, :grid_lines
41
+
42
+ attr_accessor :min_value, :max_value
43
+
44
+ types.each do |type|
45
+ instance_eval <<-DYNCLASSMETH
46
+ def #{type}(options = {})
47
+ # Start with theme defaults if a theme is set
48
+ theme = options[:theme]
49
+ options = theme ? Chart::Theme.load(theme).to_options.merge(options) : options
50
+ # # Extract the format and optional filename, then clean the hash
51
+ format = options[:format] || 'url'
52
+ options[:filename] ||= Gchart.default_filename
53
+ options.delete(:format)
54
+ #update map_colors to become bar_colors
55
+ options.update(:bar_colors => options[:map_colors]) if options.has_key?(:map_colors)
56
+ chart = new(options.merge!({:type => "#{type}"}))
57
+ chart.send(format)
58
+ end
59
+ DYNCLASSMETH
60
+ end
61
+
62
+ def self.version
63
+ Gchart::VERSION::STRING
64
+ end
65
+
66
+ def self.method_missing(m, options={})
67
+ raise NoMethodError, "#{m} is not a supported chart format, please use one of the following: #{supported_types}."
68
+ end
69
+
70
+ def initialize(options={})
71
+ @type = options[:type] || 'line'
72
+ @data = []
73
+ @width = 300
74
+ @height = 200
75
+ @horizontal = false
76
+ @grouped = false
77
+ @encoding = 'simple'
78
+ # @max_value = 'auto'
79
+ # @min_value defaults to nil meaning zero
80
+ @filename = options[:filename]
81
+ # Sets the alt tag when chart is exported as image tag
82
+ @alt = 'Google Chart'
83
+ # Sets the CSS id selector when chart is exported as image tag
84
+ @id = false
85
+ # Sets the CSS class selector when chart is exported as image tag
86
+ @klass = options[:class] || false
87
+ # set the options value if definable
88
+ options.each do |attribute, value|
89
+ send("#{attribute}=", value) if self.respond_to?("#{attribute}=")
90
+ end
91
+ end
92
+
93
+
94
+ def self.supported_types
95
+ Gchart.types.join(' ')
96
+ end
97
+
98
+ # Defines the Graph size using the following format:
99
+ # width X height
100
+ def size=(size='300x200')
101
+ @width, @height = size.split("x").map { |dimension| dimension.to_i }
102
+ end
103
+
104
+ def size=(size='300x200')
105
+ @width, @height = size.split("x").map { |dimension| dimension.to_i }
106
+ end
107
+
108
+
109
+
110
+
111
+
112
+ def size
113
+ "#{width}x#{height}"
114
+ end
115
+
116
+ def dimensions
117
+ # TODO: maybe others?
118
+ [:line_xy, :scatter].include?(type) ? 2 : 1
119
+ end
120
+
121
+ # Sets the orientation of a bar graph
122
+ def orientation=(orientation='h')
123
+ if orientation == 'h' || orientation == 'horizontal'
124
+ self.horizontal = true
125
+ elsif orientation == 'v' || orientation == 'vertical'
126
+ self.horizontal = false
127
+ end
128
+ end
129
+
130
+ # Sets the bar graph presentation (stacked or grouped)
131
+ def stacked=(option=true)
132
+ @grouped = option ? false : true
133
+ end
134
+
135
+ def bg=(options)
136
+ if options.is_a?(String)
137
+ @bg_color = options
138
+ elsif options.is_a?(Hash)
139
+ @bg_color = options[:color]
140
+ @bg_type = options[:type]
141
+ @bg_angle = options[:angle]
142
+ end
143
+ end
144
+
145
+ def graph_bg=(options)
146
+ if options.is_a?(String)
147
+ @chart_color = options
148
+ elsif options.is_a?(Hash)
149
+ @chart_color = options[:color]
150
+ @chart_type = options[:type]
151
+ @chart_angle = options[:angle]
152
+ end
153
+ end
154
+
155
+ def max_value=(max_v)
156
+ if max_v =~ /false/
157
+ @max_value = false
158
+ else
159
+ @max_value = max_v
160
+ end
161
+ end
162
+
163
+ def min_value=(min_v)
164
+ if min_v =~ /false/
165
+ @min_value = false
166
+ else
167
+ @min_value = min_v
168
+ end
169
+ end
170
+
171
+ # returns the full data range as an array
172
+ # it also sets the data range if not defined
173
+ def full_data_range(ds)
174
+ return if max_value == false
175
+
176
+ ds.each_with_index do |mds, mds_index|
177
+ mds[:min_value] ||= min_value
178
+ mds[:max_value] ||= max_value
179
+
180
+ if mds_index == 0 && type.to_s == 'bar'
181
+ # TODO: unless you specify a zero line (using chp or chds),
182
+ # the min_value of a bar chart is always 0.
183
+ #mds[:min_value] ||= mds[:data].first.to_a.compact.min
184
+ mds[:min_value] ||= 0
185
+ end
186
+ if (mds_index == 0 && type.to_s == 'bar' &&
187
+ !grouped && mds[:data].first.is_a?(Array))
188
+ totals = []
189
+ mds[:data].each do |l|
190
+ l.each_with_index do |v, index|
191
+ next if v.nil?
192
+ totals[index] ||= 0
193
+ totals[index] += v
194
+ end
195
+ end
196
+ mds[:max_value] ||= totals.compact.max
197
+ else
198
+ all = mds[:data].flatten.compact
199
+ # default min value should be 0 unless set to auto
200
+ if mds[:min_value] == 'auto'
201
+ mds[:min_value] = all.min
202
+ else
203
+ min = all.min
204
+ mds[:min_value] ||= (min && min < 0 ? min : 0)
205
+ end
206
+ mds[:max_value] ||= all.max
207
+ end
208
+ end
209
+
210
+ unless axis_range
211
+ @axis_range = ds.map{|mds| [mds[:min_value], mds[:max_value]]}
212
+ if dimensions == 1 && (type.to_s != 'bar' || horizontal)
213
+ tmp = axis_range.fetch(0, [])
214
+ @axis_range[0] = axis_range.fetch(1, [])
215
+ @axis_range[1] = tmp
216
+ end
217
+ end
218
+ # return [min, max] unless (min.nil? || max.nil?)
219
+ # @max = (max_value.nil? || max_value == 'auto') ? ds.compact.map{|mds| mds.compact.max}.max : max_value
220
+ #
221
+ # if min_value.nil?
222
+ # min_ds_value = ds.compact.map{|mds| mds.compact.min}.min || 0
223
+ # @min = (min_ds_value < 0) ? min_ds_value : 0
224
+ # else
225
+ # @min = min_value == 'auto' ? ds.compact.map{|mds| mds.compact.min}.min || 0 : min_value
226
+ # end
227
+ # @axis_range = [[min,max]]
228
+ end
229
+
230
+ def dataset
231
+ if @dataset
232
+ @dataset
233
+ else
234
+ @dataset = convert_dataset(data || [])
235
+ full_data_range(@dataset) # unless axis_range
236
+ @dataset
237
+ end
238
+ end
239
+
240
+ # Sets of data to handle multiple sets
241
+ def datasets
242
+ datasets = []
243
+ dataset.each do |d|
244
+ if d[:data].first.is_a?(Array)
245
+ datasets += d[:data]
246
+ else
247
+ datasets << d[:data]
248
+ end
249
+ end
250
+ datasets
251
+ end
252
+
253
+ def self.jstize(string)
254
+ # See discussion: http://github.com/mattetti/googlecharts/commit/9b5cfb93aa51aae06611057668e631cd515ec4f3#comment_51347
255
+ # string.gsub(' ', '+').gsub(/\[|\{|\}|\\|\^|\[|\]|\`|\]/) {|c| "%#{c[0].to_s.upcase}"}
256
+ string.gsub(' ', '+').gsub(/\[|\{|\}|\||\\|\^|\[|\]|\`|\]/) {|c| "%#{c[0].to_s.upcase}"}
257
+ end
258
+ # load all the custom aliases
259
+ require 'gchart/aliases'
260
+
261
+ # Returns the chart's generated PNG as a blob. (borrowed from John's gchart.rubyforge.org)
262
+ def fetch
263
+ open(query_builder) { |io| io.read }
264
+ end
265
+
266
+ # Writes the chart's generated PNG to a file. (borrowed from John's gchart.rubyforge.org)
267
+ def write
268
+ io_or_file = filename || Gchart.default_filename
269
+ return io_or_file.write(fetch) if io_or_file.respond_to?(:write)
270
+ open(io_or_file, "wb+") { |io| io.write(fetch) }
271
+ end
272
+
273
+ # Format
274
+
275
+ def image_tag
276
+ image = "<img"
277
+ image += " id=\"#{id}\"" if id
278
+ image += " class=\"#{klass}\"" if klass
279
+ image += " src=\"#{query_builder(:html)}\""
280
+ image += " width=\"#{width}\""
281
+ image += " height=\"#{height}\""
282
+ image += " alt=\"#{alt}\""
283
+ image += " title=\"#{title}\"" if title
284
+ image += " />"
285
+ end
286
+
287
+ alias_method :img_tag, :image_tag
288
+
289
+ def url
290
+ query_builder
291
+ end
292
+
293
+ def file
294
+ write
295
+ end
296
+
297
+ #
298
+ def jstize(string)
299
+ Gchart.jstize(string)
300
+ end
301
+
302
+ private
303
+
304
+ # The title size cannot be set without specifying a color.
305
+ # A dark key will be used for the title color if no color is specified
306
+ def set_title
307
+ title_params = "chtt=#{title}"
308
+ unless (title_color.nil? && title_size.nil? )
309
+ title_params << "&chts=" + (color, size = (title_color || '454545'), title_size).compact.join(',')
310
+ end
311
+ title_params
312
+ end
313
+
314
+ def set_size
315
+ "chs=#{size}"
316
+ end
317
+
318
+ def set_data
319
+ data = send("#{@encoding}_encoding")
320
+ "chd=#{data}"
321
+ end
322
+
323
+ def set_colors
324
+ @bg_type = fill_type(bg_type) || 's' if bg_color
325
+ @chart_type = fill_type(chart_type) || 's' if chart_color
326
+
327
+ "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('|')
328
+ end
329
+
330
+ # set bar, line colors
331
+ def set_bar_colors
332
+ @bar_colors = bar_colors.join(',') if bar_colors.is_a?(Array)
333
+ "chco=#{bar_colors}"
334
+ end
335
+
336
+ def set_country_codes
337
+ @country_codes = country_codes.join() if country_codes.is_a?(Array)
338
+ "chld=#{country_codes}"
339
+ end
340
+
341
+ # set bar spacing
342
+ # chbh=
343
+ # <bar width in pixels>,
344
+ # <optional space between bars in a group>,
345
+ # <optional space between groups>
346
+ def set_bar_width_and_spacing
347
+ width_and_spacing_values = case bar_width_and_spacing
348
+ when String
349
+ bar_width_and_spacing
350
+ when Array
351
+ bar_width_and_spacing.join(',')
352
+ when Hash
353
+ width = bar_width_and_spacing[:width] || 23
354
+ spacing = bar_width_and_spacing[:spacing] || 4
355
+ group_spacing = bar_width_and_spacing[:group_spacing] || 8
356
+ [width,spacing,group_spacing].join(',')
357
+ else
358
+ bar_width_and_spacing.to_s
359
+ end
360
+ "chbh=#{width_and_spacing_values}"
361
+ end
362
+
363
+ def set_range_markers
364
+ markers = case range_markers
365
+ when Hash
366
+ set_range_marker(range_markers)
367
+ when Array
368
+ range_markers.collect{|marker| set_range_marker(marker)}.join('|')
369
+ end
370
+ "chm=#{markers}"
371
+ end
372
+
373
+ def set_range_marker(options)
374
+ orientation = ['vertical', 'Vertical', 'V', 'v', 'R'].include?(options[:orientation]) ? 'R' : 'r'
375
+ "#{orientation},#{options[:color]},0,#{options[:start_position]},#{options[:stop_position]}#{',1' if options[:overlaid?]}"
376
+ end
377
+
378
+ def fill_for(type=nil, color='', angle=nil)
379
+ unless type.nil?
380
+ case type
381
+ when 'lg'
382
+ angle ||= 0
383
+ color = "#{color},0,ffffff,1" if color.split(',').size == 1
384
+ "#{type},#{angle},#{color}"
385
+ when 'ls'
386
+ angle ||= 90
387
+ color = "#{color},0.2,ffffff,0.2" if color.split(',').size == 1
388
+ "#{type},#{angle},#{color}"
389
+ else
390
+ "#{type},#{color}"
391
+ end
392
+ end
393
+ end
394
+
395
+ # A chart can have one or many legends.
396
+ # Gchart.line(:legend => 'label')
397
+ # or
398
+ # Gchart.line(:legend => ['first label', 'last label'])
399
+ def set_legend
400
+ return set_labels if type.to_s =~ /pie|pie_3d|meter/
401
+
402
+ if legend.is_a?(Array)
403
+ "chdl=#{@legend.map{|label| "#{CGI::escape(label.to_s)}"}.join('|')}"
404
+ else
405
+ "chdl=#{legend}"
406
+ end
407
+
408
+ end
409
+
410
+ def set_line_thickness
411
+ "chls=#{thickness}"
412
+ end
413
+
414
+ def set_line_markers
415
+ "chm=#{new_markers}"
416
+ end
417
+
418
+ def set_grid_lines
419
+ "chg=#{grid_lines}"
420
+ end
421
+
422
+ def set_labels
423
+ if legend.is_a?(Array)
424
+ "chl=#{@legend.map{|label| "#{CGI::escape(label.to_s)}"}.join('|')}"
425
+ else
426
+ "chl=#{@legend}"
427
+ end
428
+ end
429
+
430
+ def set_axis_with_labels
431
+ @axis_with_labels = axis_with_labels.join(',') if @axis_with_labels.is_a?(Array)
432
+ "chxt=#{axis_with_labels}"
433
+ end
434
+
435
+ def set_axis_labels
436
+ if axis_labels.is_a?(Array)
437
+ if RUBY_VERSION.to_f < 1.9
438
+ labels_arr = axis_labels.enum_with_index.map{|labels,index| [index,labels]}
439
+ else
440
+ labels_arr = axis_labels.map.with_index.map{|labels,index| [index,labels]}
441
+ end
442
+ elsif axis_labels.is_a?(Hash)
443
+ labels_arr = axis_labels.to_a
444
+ end
445
+ labels_arr.map! do |index,labels|
446
+ if labels.is_a?(Array)
447
+ "#{index}:|#{labels.map{|label| "#{CGI::escape(label.to_s)}"}.join('|')}"
448
+ else
449
+ "#{index}:|#{labels}"
450
+ end
451
+ end
452
+ count = labels_arr.length
453
+
454
+ "chxl=#{labels_arr.join('|')}"
455
+ end
456
+
457
+ # http://code.google.com/apis/chart/labels.html#axis_range
458
+ # Specify a range for axis labels
459
+ def set_axis_range
460
+ # a passed axis_range should look like:
461
+ # [[10,100]] or [[10,100,4]] or [[10,100], [20,300]]
462
+ # in the second example, 4 is the interval
463
+ set = axis_range || datasets
464
+ # in the case of a line graph, the first axis range should 1
465
+ index_increase = type.to_s == 'line' ? 1 : 0
466
+ if set && set.respond_to?(:each) && set.first.respond_to?(:each)
467
+ 'chxr=' + datasets.enum_for(:each_with_index).map do |range, index|
468
+ [(index + index_increase), (min_value || range.first), (max_value || range.last)].compact.uniq.join(',')
469
+ end.join("|")
470
+ else
471
+ nil
472
+ end
473
+ end
474
+
475
+
476
+
477
+
478
+
479
+ def set_geographical_area
480
+ "chtm=#{geographical_area}"
481
+ end
482
+
483
+ def set_type
484
+ case type.to_s
485
+ when 'line'
486
+ "cht=lc"
487
+ when 'line_xy'
488
+ "cht=lxy"
489
+ when 'bar'
490
+ "cht=b" + (horizontal? ? "h" : "v") + (grouped? ? "g" : "s")
491
+ when 'pie_3d'
492
+ "cht=p3"
493
+ when 'pie'
494
+ "cht=p"
495
+ when 'venn'
496
+ "cht=v"
497
+ when 'scatter'
498
+ "cht=s"
499
+ when 'sparkline'
500
+ "cht=ls"
501
+ when 'meter'
502
+ "cht=gom"
503
+ when 'map'
504
+ "cht=t"
505
+ end
506
+ end
507
+
508
+ def fill_type(type)
509
+ case type
510
+ when 'solid'
511
+ 's'
512
+ when 'gradient'
513
+ 'lg'
514
+ when 'stripes'
515
+ 'ls'
516
+ end
517
+ end
518
+
519
+ def number_visible
520
+ n = 0
521
+ dataset.each do |mds|
522
+ return n.to_s if mds[:invisible] == true
523
+ if mds[:data].first.is_a?(Array)
524
+ n += mds[:data].length
525
+ else
526
+ n += 1
527
+ end
528
+ end
529
+ ""
530
+ end
531
+
532
+ # Turns input into an array of axis hashes, dependent on the chart type
533
+ def convert_dataset(ds)
534
+ if dimensions == 2
535
+ # valid inputs include:
536
+ # an array of >=2 arrays, or an array of >=2 hashes
537
+ ds = ds.map do |d|
538
+ d.is_a?(Hash) ? d : {:data => d}
539
+ end
540
+ elsif dimensions == 1
541
+ # valid inputs include:
542
+ # a hash, an array of data, an array of >=1 array, or an array of >=1 hash
543
+ if ds.is_a?(Hash)
544
+ ds = [ds]
545
+ elsif not ds.first.is_a?(Hash)
546
+ ds = [{:data => ds}]
547
+ end
548
+ end
549
+ ds
550
+ end
551
+
552
+ # just an alias
553
+ def axis_set
554
+ dataset
555
+ end
556
+
557
+ def convert_to_simple_value(number)
558
+ if number.nil?
559
+ "_"
560
+ else
561
+ value = Gchart.simple_chars[number.to_i]
562
+ value.nil? ? "_" : value
563
+ end
564
+ end
565
+
566
+ def convert_to_extended_value(number)
567
+ if number.nil?
568
+ '__'
569
+ else
570
+ value = Gchart.ext_pairs[number.to_i]
571
+ value.nil? ? "__" : value
572
+ end
573
+ end
574
+
575
+ def encode_scaled_dataset(chars, nil_char)
576
+ dsets = []
577
+ dataset.each do |ds|
578
+ if max_value != false
579
+ range = ds[:max_value] - ds[:min_value]
580
+ range = 1 if range == 0
581
+ end
582
+ unless ds[:data].first.is_a?(Array)
583
+ ldatasets = [ds[:data]]
584
+ else
585
+ ldatasets = ds[:data]
586
+ end
587
+ ldatasets.each do |l|
588
+ dsets << l.map do |number|
589
+ if number.nil?
590
+ nil_char
591
+ else
592
+ unless range.nil? || range.zero?
593
+ number = chars.size * (number - ds[:min_value]) / range.to_f
594
+ number = [number, chars.size - 1].min
595
+ end
596
+ chars[number.to_i]
597
+ end
598
+ end.join
599
+ end
600
+ end
601
+ dsets.join(',')
602
+ end
603
+
604
+ # http://code.google.com/apis/chart/#simple
605
+ # Simple encoding has a resolution of 62 different values.
606
+ # Allowing five pixels per data point, this is sufficient for line and bar charts up
607
+ # to about 300 pixels. Simple encoding is suitable for all other types of chart regardless of size.
608
+ def simple_encoding
609
+ "s" + number_visible + ":" + encode_scaled_dataset(Gchart.simple_chars, '_')
610
+ end
611
+
612
+ # http://code.google.com/apis/chart/#text
613
+ # Text encoding with data scaling lets you specify arbitrary positive or
614
+ # negative floating point numbers, in combination with a scaling parameter
615
+ # that lets you specify a custom range for your chart. This chart is useful
616
+ # when you don't want to worry about limiting your data to a specific range,
617
+ # or do the calculations to scale your data down or up to fit nicely inside
618
+ # a chart.
619
+ #
620
+ # 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).
621
+ #
622
+ # This encoding is not available for maps.
623
+ #
624
+ def text_encoding
625
+ chds = dataset.map{|ds| "#{ds[:min_value]},#{ds[:max_value]}" }.join(",")
626
+ "t" + number_visible + ":" + datasets.map{ |ds| ds.join(',') }.join('|') + "&chds=" + chds
627
+ end
628
+
629
+ #Text encoding, without custom scaling
630
+ def basic_encoding
631
+ chds = dataset.map{|ds| "#{ds[:min_value]},#{ds[:max_value]}" }.join(",")
632
+ "t" + number_visible + ":" + datasets.map{ |ds| ds.join(',') }.join('|')
633
+ end
634
+
635
+ # http://code.google.com/apis/chart/#extended
636
+ # Extended encoding has a resolution of 4,096 different values
637
+ # and is best used for large charts where a large data range is required.
638
+ def extended_encoding
639
+ "e" + number_visible + ":" + encode_scaled_dataset(Gchart.ext_pairs, '__')
640
+ end
641
+
642
+ def query_builder(options="")
643
+ query_params = instance_variables.sort.map do |var|
644
+ case var.to_s
645
+ when '@data'
646
+ set_data unless data == []
647
+ # Set the graph size
648
+ when '@width'
649
+ set_size unless width.nil? || height.nil?
650
+ when '@type'
651
+ set_type
652
+ when '@title'
653
+ set_title unless title.nil?
654
+ when '@legend'
655
+ set_legend unless legend.nil?
656
+ when '@thickness'
657
+ set_line_thickness
658
+ when '@new_markers'
659
+ set_line_markers
660
+ when '@bg_color'
661
+ set_colors
662
+ when '@chart_color'
663
+ set_colors if bg_color.nil?
664
+ when '@bar_colors'
665
+ set_bar_colors
666
+ when '@bar_width_and_spacing'
667
+ set_bar_width_and_spacing
668
+ when '@axis_with_labels'
669
+ set_axis_with_labels
670
+ when '@axis_labels'
671
+ set_axis_labels
672
+ when '@range_markers'
673
+ set_range_markers
674
+ when '@grid_lines'
675
+ set_grid_lines
676
+ when '@geographical_area'
677
+ set_geographical_area
678
+ when '@country_codes'
679
+ set_country_codes
680
+ when '@custom'
681
+ custom
682
+ end
683
+ end.compact
684
+
685
+ query_params << set_axis_range
686
+
687
+ # Use ampersand as default delimiter
688
+ unless options == :html
689
+ delimiter = '&'
690
+ # Escape ampersand for html image tags
691
+ else
692
+ delimiter = '&amp;'
693
+ end
694
+
695
+ jstize(Gchart.url + query_params.join(delimiter))
696
+ end
697
+
698
+ end
@@ -0,0 +1,2 @@
1
+ require 'gchart'
2
+ Googlecharts = Gchart unless Object.const_defined? 'Googlechart'