Add Google Map of voters
[selectricity-live] / vendor / plugins / ym4r_gm / lib / gm_plugin / map.rb
1 module Ym4r\r
2   module GmPlugin \r
3     #Representing the Google Maps API class GMap2.\r
4     class GMap\r
5       include MappingObject\r
6       \r
7       #A constant containing the declaration of the VML namespace, necessary to display polylines under IE.\r
8       VML_NAMESPACE = "xmlns:v=\"urn:schemas-microsoft-com:vml\""\r
9       \r
10       #The id of the DIV that will contain the map in the HTML page. \r
11       attr_reader :container\r
12       \r
13       #By default the map in the HTML page will be globally accessible with the name +map+.\r
14       def initialize(container, variable = "map")\r
15         @container = container\r
16         @variable = variable\r
17         @init = []\r
18         @init_end = [] #for stuff that must be initialized at the end (controls)\r
19         @init_begin = [] #for stuff that must be initialized at the beginning (center + zoom)\r
20         @global_init = []\r
21       end\r
22 \r
23       #Deprecated. Use the static version instead.\r
24       def header(with_vml = true)\r
25         GMap.header(:with_vml => with_vml)\r
26       end\r
27 \r
28       #Outputs the header necessary to use the Google Maps API, by including the JS files of the API, as well as a file containing YM4R/GM helper functions. By default, it also outputs a style declaration for VML elements. This default can be overriddent by passing <tt>:with_vml => false</tt> as option to the method. You can also pass a <tt>:host</tt> option in order to select the correct API key for the location where your app is currently running, in case the current environment has multiple possible keys. Usually, in this case, you should pass it <tt>@request.host</tt>. If you have defined only one API key for the current environment, the <tt>:host</tt> option is ignored. Finally you can override all the key settings in the configuration by passing a value to the <tt>:key</tt> key. Finally, you can pass a language for the map type buttons with the <tt>:hl</tt> option (possible values are: Japanese (ja), French (fr), German (de), Italian (it), Spanish (es), Catalan (ca), Basque (eu) and Galician (gl): no values means english)\r
29       def self.header(options = {})\r
30         options[:with_vml] = true unless options.has_key?(:with_vml)\r
31         options[:hl] ||= ''\r
32         api_key = ApiKey.get(options)\r
33         a = "<script src=\"http://maps.google.com/maps?file=api&amp;v=2.x&amp;key=#{api_key}&amp;hl=#{options[:hl]}\" type=\"text/javascript\"></script>\n"\r
34         a << "<script src=\"/javascripts/ym4r-gm.js\" type=\"text/javascript\"></script>\n" unless options[:without_js]\r
35         a << "<style type=\"text/css\">\n v\:* { behavior:url(#default#VML);}\n</style>" if options[:with_vml]\r
36         a\r
37       end\r
38      \r
39       #Outputs the <div id=...></div> which has been configured to contain the map. You can pass <tt>:width</tt> and <tt>:height</tt> as options to output this in the style attribute of the DIV element (you could also achieve the same effect by putting the dimension info into a CSS or using the instance method GMap#header_width_height). You can aslo pass <tt>:class</tt> to set the classname of the div.\r
40       def div(options = {})\r
41         attributes = "id=\"#{@container}\" "\r
42         if options.has_key?(:height) && options.has_key?(:width)\r
43           attributes += "style=\"width:#{options.delete(:width)}px;height:#{options.delete(:height)}px\" "\r
44         end\r
45         if options.has_key?(:class)\r
46           attributes += options.keys.map {|opt| "#{opt}=\"#{options[opt]}\"" }.join(" ")\r
47         end\r
48         "<div #{attributes}></div>"\r
49       end\r
50 \r
51       #Outputs a style declaration setting the dimensions of the DIV container of the map. This info can also be set manually in a CSS.\r
52       def header_width_height(width,height)\r
53         "<style type=\"text/css\">\n##{@container} { height: #{height}px;\n  width: #{width}px;\n}\n</style>"\r
54       end\r
55 \r
56       #Records arbitrary JavaScript code and outputs it during initialization inside the +load+ function.\r
57       def record_init(code)\r
58         @init << code\r
59       end\r
60 \r
61       #Initializes the controls: you can pass a hash with keys <tt>:small_map</tt>, <tt>:large_map</tt>, <tt>:small_zoom</tt>, <tt>:scale</tt>, <tt>:map_type</tt> and <tt>:overview_map</tt> and a boolean value as the value (usually true, since the control is not displayed by default)\r
62       def control_init(controls = {})\r
63         @init_end << add_control(GSmallMapControl.new) if controls[:small_map]\r
64         @init_end << add_control(GLargeMapControl.new) if controls[:large_map]\r
65         @init_end << add_control(GSmallZoomControl.new) if controls[:small_zoom]\r
66         @init_end << add_control(GScaleControl.new) if controls[:scale]\r
67         @init_end << add_control(GMapTypeControl.new) if controls[:map_type]\r
68         @init_end << add_control(GOverviewMapControl.new) if controls[:overview_map]\r
69       end\r
70       \r
71       #Initializes the interface configuration: double-click zoom, dragging, continuous zoom,... You can pass a hash with keys <tt>:dragging</tt>, <tt>:info_window</tt>, <tt>:double_click_zoom</tt>, <tt>:continuous_zoom</tt> and <tt>:scroll_wheel_zoom</tt>. The values should be true or false. Check the google maps API doc to know what the default values are.\r
72       def interface_init(interface = {})\r
73         if !interface[:dragging].nil?\r
74           if interface[:dragging]\r
75              @init << enableDragging() \r
76           else\r
77             @init << disableDragging() \r
78           end\r
79         end\r
80         if !interface[:info_window].nil?\r
81           if interface[:info_window]\r
82             @init << enableInfoWindow()\r
83           else\r
84             @init << disableInfoWindow()\r
85           end\r
86         end\r
87         if !interface[:double_click_zoom].nil?\r
88           if interface[:double_click_zoom]\r
89             @init << enableDoubleClickZoom()\r
90           else\r
91             @init << disableDoubleClickZoom()\r
92           end\r
93         end\r
94         if !interface[:continuous_zoom].nil?\r
95           if interface[:continuous_zoom]\r
96             @init << enableContinuousZoom()\r
97           else\r
98             @init << disableContinuousZoom()\r
99           end\r
100         end\r
101         if !interface[:scroll_wheel_zoom].nil?\r
102           if interface[:scroll_wheel_zoom]\r
103             @init << enableScrollWheelZoom()\r
104           else\r
105             @init << disableScrollWheelZoom()\r
106           end\r
107         end\r
108       end\r
109 \r
110       #Initializes the initial center and zoom of the map. +center+ can be both a GLatLng object or a 2-float array.\r
111       def center_zoom_init(center, zoom)\r
112         if center.is_a?(GLatLng)\r
113           @init_begin << set_center(center,zoom)\r
114         else\r
115           @init_begin << set_center(GLatLng.new(center),zoom)\r
116         end\r
117       end\r
118 \r
119       #Center and zoom based on the coordinates passed as argument (either 2D arrays or GLatLng objects)\r
120       def center_zoom_on_points_init(*points)\r
121         if(points.length > 0)\r
122           if(points[0].is_a?(Array))\r
123             points = points.collect { |point| GLatLng.new(point) }\r
124           end\r
125           @init_begin << center_and_zoom_on_points(points)\r
126         end\r
127       end\r
128 \r
129       #Center and zoom based on the bbox corners. Pass a GLatLngBounds object, an array of 2D coordinates (sw and ne) or an array of GLatLng objects (sw and ne).\r
130       def center_zoom_on_bounds_init(latlngbounds)\r
131         if(latlngbounds.is_a?(Array))\r
132           if latlngbounds[0].is_a?(Array)\r
133             latlngbounds = GLatLngBounds.new(GLatLng.new(latlngbounds[0]),GLatLng.new(latlngbounds[1]))\r
134           elsif latlngbounds[0].is_a?(GLatLng)\r
135             latlngbounds = GLatLngBounds.new(*latlngbounds)\r
136           end\r
137         end\r
138         #else it is already a latlngbounds object\r
139 \r
140         @init_begin << center_and_zoom_on_bounds(latlngbounds)\r
141       end\r
142 \r
143       #Initializes the map by adding an overlay (marker or polyline).\r
144       def overlay_init(overlay)\r
145         @init << add_overlay(overlay)\r
146       end\r
147 \r
148       #Sets up a new map type. If +add+ is false, all the other map types of the map are wiped out. If you want to access the map type in other methods, you should declare the map type first (with +declare_init+).\r
149       def add_map_type_init(map_type, add = true)\r
150         unless add\r
151           @init << get_map_types.set_property(:length,0)\r
152         end\r
153         @init << add_map_type(map_type)\r
154       end\r
155       #for legacy purpose\r
156       alias :map_type_init :add_map_type_init\r
157 \r
158       #Sets the map type displayed by default after the map is loaded. It should be known from the map (ie either the default map types or a user-defined map type added with <tt>add_map_type_init</tt>). Use <tt>set_map_type_init(GMapType::G_SATELLITE_MAP)</tt> or <tt>set_map_type_init(GMapType::G_HYBRID_MAP)</tt> to initialize the map with repsecitvely the Satellite view and the hybrid view.\r
159       def set_map_type_init(map_type)\r
160         @init << set_map_type(map_type)\r
161       end\r
162 \r
163       #Locally declare a MappingObject with variable name "name"\r
164       def declare_init(variable, name)\r
165         @init << variable.declare(name)\r
166       end\r
167 \r
168       #Records arbitrary JavaScript code and outputs it during initialization outside the +load+ function (ie globally).\r
169       def record_global_init(code)\r
170         @global_init << code\r
171       end\r
172       \r
173       #Deprecated. Use icon_global_init instead.\r
174       def icon_init(icon , name)\r
175         icon_global_init(icon , name)\r
176       end\r
177       \r
178       #Initializes an icon  and makes it globally accessible through the JavaScript variable of name +variable+.\r
179       def icon_global_init(icon , name, options = {})\r
180         declare_global_init(icon,name,options)\r
181       end\r
182 \r
183       #Registers an event\r
184       def event_init(object,event,callback)\r
185         @init << "GEvent.addListener(#{object.to_javascript},\"#{MappingObject.javascriptify_method(event.to_s)}\",#{callback});"\r
186       end\r
187 \r
188       #Registers an event globally\r
189       def event_global_init(object,event,callback)\r
190         @global_init << "GEvent.addListener(#{object.to_javascript},\"#{MappingObject.javascriptify_method(event.to_s)}\",#{callback});"\r
191       end\r
192       \r
193       #Declares the overlay globally with name +name+\r
194       def overlay_global_init(overlay,name, options = {})\r
195         declare_global_init(overlay,name, options)\r
196         @init << add_overlay(overlay)\r
197       end\r
198 \r
199       #Globally declare a MappingObject with variable name "name". Option <tt>:local_construction</tt> should be passed if the construction has to be done inside the onload callback method (for exsample if it depends on the GMap to be initialized)\r
200       def declare_global_init(variable,name, options = {})\r
201         unless options[:local_construction]\r
202           @global_init << "var #{variable.assign_to(name)}"\r
203         else\r
204           @global_init << "var #{name};"\r
205           @init << variable.assign_to(name)\r
206         end\r
207       end\r
208       \r
209       #Outputs the initialization code for the map. By default, it outputs the script tags, performs the initialization in response to the onload event of the window and makes the map globally available. If you pass +true+ to the option key <tt>:full</tt>, the map will be setup in full screen, in which case it is not necessary (but not harmful) to set a size for the map div.\r
210       def to_html(options = {})\r
211         no_load = options[:no_load]\r
212         no_script_tag = options[:no_script_tag]\r
213         no_declare = options[:no_declare]\r
214         no_global = options[:no_global]\r
215         fullscreen = options[:full]\r
216         load_pr = options[:proto_load] #to prevent some problems when the onload event callback from Prototype is used\r
217         \r
218         html = ""\r
219         html << "<script type=\"text/javascript\">\n" if !no_script_tag\r
220         #put the functions in a separate javascript file to be included in the page\r
221         html << @global_init * "\n"\r
222         html << "var #{@variable};\n" if !no_declare and !no_global\r
223         if !no_load\r
224           if load_pr\r
225             html << "Event.observe(window,'load',"\r
226           else\r
227             html << "window.onload = addCodeToFunction(window.onload,"\r
228           end\r
229           html << "function() {\n"\r
230         end\r
231 \r
232         html << "if (GBrowserIsCompatible()) {\n" \r
233         \r
234         if fullscreen\r
235           #Adding the initial resizing and setting up the event handler for\r
236           #future resizes\r
237           html << "setWindowDims(document.getElementById('#{@container}'));\n"\r
238           html << "if (window.attachEvent) { window.attachEvent(\"onresize\", function() {setWindowDims(document.getElementById('#{@container}'));})} else {window.addEventListener(\"resize\", function() {setWindowDims(document.getElementById('#{@container}')); } , false);}\n"\r
239         end\r
240       \r
241         if !no_declare and no_global \r
242           html << "#{declare(@variable)}\n"\r
243         else\r
244           html << "#{assign_to(@variable)}\n"\r
245         end\r
246         html << @init_begin * "\n"\r
247         html << @init * "\n"\r
248         html << @init_end * "\n"\r
249         html << "\n}\n"\r
250         html << "});\n" if !no_load\r
251         html << "</script>" if !no_script_tag\r
252         \r
253         if fullscreen\r
254           #setting up the style in case of full screen\r
255           html << "<style>html, body {width: 100%; height: 100%} body {margin-top: 0px; margin-right: 0px; margin-left: 0px; margin-bottom: 0px} ##{@container} {margin:  0px;} </style>"\r
256         end\r
257         \r
258         html\r
259       end\r
260       \r
261       #Outputs in JavaScript the creation of a GMap2 object \r
262       def create\r
263         "new GMap2(document.getElementById(\"#{@container}\"))"\r
264       end\r
265     end\r
266   end\r
267 end\r
268 \r

Benjamin Mako Hill || Want to submit a patch?