ym4r_gm 0.2.0

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.
@@ -0,0 +1,54 @@
1
+ # coding: utf-8
2
+ require 'rubygems'
3
+ require 'bundler'
4
+ begin
5
+ Bundler.setup(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+ require 'rake'
12
+
13
+ require 'jeweler'
14
+ Jeweler::Tasks.new do |gem|
15
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
16
+ gem.name = "ym4r_gm"
17
+ gem.homepage = "http://github.com/grzegorzblaszczyk/ym4r_gm"
18
+ gem.license = "MIT"
19
+ gem.summary = %Q{Fork of YM4R_GM for Rails 3 and Ruby 1.9.2}
20
+ gem.description = %Q{Fork of YM4R_GM for Rails 3 and Ruby 1.9.2}
21
+ gem.email = "grzegorz.blaszczyk@gmail.com"
22
+ gem.authors = ["Grzegorz Błaszczyk"]
23
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
24
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
25
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
26
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
27
+ end
28
+ Jeweler::RubygemsDotOrgTasks.new
29
+
30
+ require 'rake/testtask'
31
+ Rake::TestTask.new(:test) do |test|
32
+ test.libs << 'lib' << 'test'
33
+ test.pattern = 'test/**/test_*.rb'
34
+ test.verbose = true
35
+ end
36
+
37
+ require 'rcov/rcovtask'
38
+ Rcov::RcovTask.new do |test|
39
+ test.libs << 'test'
40
+ test.pattern = 'test/**/test_*.rb'
41
+ test.verbose = true
42
+ end
43
+
44
+ task :default => :test
45
+
46
+ require 'rake/rdoctask'
47
+ Rake::RDocTask.new do |rdoc|
48
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
49
+
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.title = "ym4r_gm #{version}"
52
+ rdoc.rdoc_files.include('README*')
53
+ rdoc.rdoc_files.include('lib/**/*.rb')
54
+ end
data/VERSION ADDED
@@ -0,0 +1,2 @@
1
+ 0.2.0
2
+
@@ -0,0 +1,14 @@
1
+ #Fill here the Google Maps API keys for your application
2
+ #In this sample:
3
+ #For development and test, we have only one possible host (localhost:3000), so there is only a single key associated with the mode.
4
+ #In production, the app can be accessed through 2 different hosts: thepochisuperstarmegashow.com and exmaple.com. There then needs a 2-key hash. If you deployed to one host, only the API key would be needed (as in development and test).
5
+
6
+ development:
7
+ ABQIAAAAzMUFFnT9uH0xq39J0Y4kbhTJQa0g3IQ9GZqIMmInSLzwtGDKaBR6j135zrztfTGVOm2QlWnkaidDIQ
8
+
9
+ test:
10
+ ABQIAAAAzMUFFnT9uH0xq39J0Y4kbhTJQa0g3IQ9GZqIMmInSLzwtGDKaBR6j135zrztfTGVOm2QlWnkaidDIQ
11
+
12
+ production:
13
+ thepochisuperstarmegashow.com: ABQIAAAAzMUFFnT9uH0Sfg76Y4kbhTJQa0g3IQ9GZqIMmInSLzwtGDmlRT6e90j135zat56yhJKQlWnkaidDIQ
14
+ example.com: ABQIAAAAzMUFFnT9uH0Sfg98Y4kbhGFJQa0g3IQ9GZqIMmInSLrthJKGDmlRT98f4j135zat56yjRKQlWnkmod3TB
data/init.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'ym4r_gm'
2
+
3
+
@@ -0,0 +1,10 @@
1
+ require 'fileutils'
2
+
3
+ #Copy the Javascript files
4
+ FileUtils.copy(Dir[File.dirname(__FILE__) + '/javascript/*.js'], File.dirname(__FILE__) + '/../../../public/javascripts/')
5
+
6
+ #copy the gmaps_api_key file
7
+ gmaps_config = File.dirname(__FILE__) + '/../../../config/gmaps_api_key.yml'
8
+ unless File.exist?(gmaps_config)
9
+ FileUtils.copy(File.dirname(__FILE__) + '/gmaps_api_key.yml.sample',gmaps_config)
10
+ end
@@ -0,0 +1,444 @@
1
+ // Clusterer.js - marker clustering routines for Google Maps apps
2
+ //
3
+ // The original version of this code is available at:
4
+ // http://www.acme.com/javascript/
5
+ //
6
+ // Copyright � 2005,2006 by Jef Poskanzer <jef@mail.acme.com>.
7
+ // All rights reserved.
8
+ //
9
+ // Modified for inclusion into the YM4R library in accordance with the
10
+ // following license:
11
+ //
12
+ // Redistribution and use in source and binary forms, with or without
13
+ // modification, are permitted provided that the following conditions
14
+ // are met:
15
+ // 1. Redistributions of source code must retain the above copyright
16
+ // notice, this list of conditions and the following disclaimer.
17
+ // 2. Redistributions in binary form must reproduce the above copyright
18
+ // notice, this list of conditions and the following disclaimer in the
19
+ // documentation and/or other materials provided with the distribution.
20
+ //
21
+ // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22
+ // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
+ // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
+ // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25
+ // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
+ // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27
+ // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28
+ // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29
+ // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30
+ // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31
+ // SUCH DAMAGE.
32
+ //
33
+ // For commentary on this license please see http://www.acme.com/license.html
34
+
35
+
36
+ // Constructor.
37
+ Clusterer = function(markers,icon,maxVisibleMarkers,gridSize,minMarkersPerCluster,maxLinesPerInfoBox) {
38
+ this.markers = [];
39
+ if(markers){
40
+ for(var i =0 ; i< markers.length ; i++){
41
+ this.addMarker(markers[i]);
42
+ }
43
+ }
44
+ this.clusters = [];
45
+ this.timeout = null;
46
+
47
+ this.maxVisibleMarkers = maxVisibleMarkers || 150;
48
+ this.gridSize = gridSize || 5;
49
+ this.minMarkersPerCluster = minMarkersPerCluster || 5;
50
+ this.maxLinesPerInfoBox = maxLinesPerInfoBox || 10;
51
+
52
+ this.icon = icon || G_DEFAULT_ICON;
53
+ }
54
+
55
+ Clusterer.prototype = new GOverlay();
56
+
57
+ Clusterer.prototype.initialize = function ( map ){
58
+ this.map = map;
59
+ this.currentZoomLevel = map.getZoom();
60
+
61
+ GEvent.addListener( map, 'zoomend', Clusterer.makeCaller( Clusterer.display, this ) );
62
+ GEvent.addListener( map, 'moveend', Clusterer.makeCaller( Clusterer.display, this ) );
63
+ GEvent.addListener( map, 'infowindowclose', Clusterer.makeCaller( Clusterer.popDown, this ) );
64
+ //Set map for each marker
65
+ for(var i = 0,len = this.markers.length ; i < len ; i++){
66
+ this.markers[i].setMap( map );
67
+ }
68
+ this.displayLater();
69
+ }
70
+
71
+ Clusterer.prototype.remove = function(){
72
+ for ( var i = 0; i < this.markers.length; ++i ){
73
+ this.removeMarker(this.markers[i]);
74
+ }
75
+ }
76
+
77
+ Clusterer.prototype.copy = function(){
78
+ return new Clusterer(this.markers,this.icon,this.maxVisibleMarkers,this.gridSize,this.minMarkersPerCluster,this.maxLinesPerInfoBox);
79
+ }
80
+
81
+ Clusterer.prototype.redraw = function(force){
82
+ this.displayLater();
83
+ }
84
+
85
+ // Call this to change the cluster icon.
86
+ Clusterer.prototype.setIcon = function ( icon ){
87
+ this.icon = icon;
88
+ }
89
+
90
+ // Call this to add a marker.
91
+ Clusterer.prototype.addMarker = function ( marker, description){
92
+ marker.onMap = false;
93
+ this.markers.push( marker );
94
+ marker.description = marker.description || description;
95
+ if(this.map != null){
96
+ marker.setMap(this.map);
97
+ this.displayLater();
98
+ }
99
+ };
100
+
101
+
102
+ // Call this to remove a marker.
103
+ Clusterer.prototype.removeMarker = function ( marker ){
104
+ for ( var i = 0; i < this.markers.length; ++i )
105
+ if ( this.markers[i] == marker ){
106
+ if ( marker.onMap )
107
+ this.map.removeOverlay( marker );
108
+ for ( var j = 0; j < this.clusters.length; ++j ){
109
+ var cluster = this.clusters[j];
110
+ if ( cluster != null ){
111
+ for ( var k = 0; k < cluster.markers.length; ++k )
112
+ if ( cluster.markers[k] == marker ){
113
+ cluster.markers[k] = null;
114
+ --cluster.markerCount;
115
+ break;
116
+ }
117
+ if ( cluster.markerCount == 0 ){
118
+ this.clearCluster( cluster );
119
+ this.clusters[j] = null;
120
+ }
121
+ else if ( cluster == this.poppedUpCluster )
122
+ Clusterer.rePop( this );
123
+ }
124
+ }
125
+ this.markers[i] = null;
126
+ break;
127
+ }
128
+ this.displayLater();
129
+ };
130
+
131
+ Clusterer.prototype.displayLater = function (){
132
+ if ( this.timeout != null )
133
+ clearTimeout( this.timeout );
134
+ this.timeout = setTimeout( Clusterer.makeCaller( Clusterer.display, this ), 50 );
135
+ };
136
+
137
+ Clusterer.display = function ( clusterer ){
138
+ var i, j, marker, cluster, len, len2;
139
+
140
+ clearTimeout( clusterer.timeout );
141
+
142
+ var newZoomLevel = clusterer.map.getZoom();
143
+ if ( newZoomLevel != clusterer.currentZoomLevel ){
144
+ // When the zoom level changes, we have to remove all the clusters.
145
+ for ( i = 0 , len = clusterer.clusters.length; i < len; ++i ){
146
+ if ( clusterer.clusters[i] != null ){
147
+ clusterer.clearCluster( clusterer.clusters[i] );
148
+ clusterer.clusters[i] = null;
149
+ }
150
+ }
151
+ clusterer.clusters.length = 0;
152
+ clusterer.currentZoomLevel = newZoomLevel;
153
+ }
154
+
155
+ // Get the current bounds of the visible area.
156
+ var bounds = clusterer.map.getBounds();
157
+
158
+ // Expand the bounds a little, so things look smoother when scrolling
159
+ // by small amounts.
160
+ var sw = bounds.getSouthWest();
161
+ var ne = bounds.getNorthEast();
162
+ var dx = ne.lng() - sw.lng();
163
+ var dy = ne.lat() - sw.lat();
164
+ dx *= 0.10;
165
+ dy *= 0.10;
166
+ bounds = new GLatLngBounds(
167
+ new GLatLng( sw.lat() - dy, sw.lng() - dx ),
168
+ new GLatLng( ne.lat() + dy, ne.lng() + dx )
169
+ );
170
+
171
+ // Partition the markers into visible and non-visible lists.
172
+ var visibleMarkers = [];
173
+ var nonvisibleMarkers = [];
174
+ for ( i = 0, len = clusterer.markers.length ; i < len; ++i ){
175
+ marker = clusterer.markers[i];
176
+ if ( marker != null )
177
+ if ( bounds.contains( marker.getPoint() ) )
178
+ visibleMarkers.push( marker );
179
+ else
180
+ nonvisibleMarkers.push( marker );
181
+ }
182
+
183
+ // Take down the non-visible markers.
184
+ for ( i = 0, len = nonvisibleMarkers.length ; i < len; ++i ){
185
+ marker = nonvisibleMarkers[i];
186
+ if ( marker.onMap ){
187
+ clusterer.map.removeOverlay( marker );
188
+ marker.onMap = false;
189
+ }
190
+ }
191
+
192
+ // Take down the non-visible clusters.
193
+ for ( i = 0, len = clusterer.clusters.length ; i < len ; ++i ){
194
+ cluster = clusterer.clusters[i];
195
+ if ( cluster != null && ! bounds.contains( cluster.marker.getPoint() ) && cluster.onMap ){
196
+ clusterer.map.removeOverlay( cluster.marker );
197
+ cluster.onMap = false;
198
+ }
199
+ }
200
+
201
+ // Clustering! This is some complicated stuff. We have three goals
202
+ // here. One, limit the number of markers & clusters displayed, so the
203
+ // maps code doesn't slow to a crawl. Two, when possible keep existing
204
+ // clusters instead of replacing them with new ones, so that the app pans
205
+ // better. And three, of course, be CPU and memory efficient.
206
+ if ( visibleMarkers.length > clusterer.maxVisibleMarkers ){
207
+ // Add to the list of clusters by splitting up the current bounds
208
+ // into a grid.
209
+ var latRange = bounds.getNorthEast().lat() - bounds.getSouthWest().lat();
210
+ var latInc = latRange / clusterer.gridSize;
211
+ var lngInc = latInc / Math.cos( ( bounds.getNorthEast().lat() + bounds.getSouthWest().lat() ) / 2.0 * Math.PI / 180.0 );
212
+ for ( var lat = bounds.getSouthWest().lat(); lat <= bounds.getNorthEast().lat(); lat += latInc )
213
+ for ( var lng = bounds.getSouthWest().lng(); lng <= bounds.getNorthEast().lng(); lng += lngInc ){
214
+ cluster = new Object();
215
+ cluster.clusterer = clusterer;
216
+ cluster.bounds = new GLatLngBounds( new GLatLng( lat, lng ), new GLatLng( lat + latInc, lng + lngInc ) );
217
+ cluster.markers = [];
218
+ cluster.markerCount = 0;
219
+ cluster.onMap = false;
220
+ cluster.marker = null;
221
+ clusterer.clusters.push( cluster );
222
+ }
223
+
224
+ // Put all the unclustered visible markers into a cluster - the first
225
+ // one it fits in, which favors pre-existing clusters.
226
+ for ( i = 0, len = visibleMarkers.length ; i < len; ++i ){
227
+ marker = visibleMarkers[i];
228
+ if ( marker != null && ! marker.inCluster ){
229
+ for ( j = 0, len2 = clusterer.clusters.length ; j < len2 ; ++j ){
230
+ cluster = clusterer.clusters[j];
231
+ if ( cluster != null && cluster.bounds.contains( marker.getPoint() ) ){
232
+ cluster.markers.push( marker );
233
+ ++cluster.markerCount;
234
+ marker.inCluster = true;
235
+ }
236
+ }
237
+ }
238
+ }
239
+
240
+ // Get rid of any clusters containing only a few markers.
241
+ for ( i = 0, len = clusterer.clusters.length ; i < len ; ++i )
242
+ if ( clusterer.clusters[i] != null && clusterer.clusters[i].markerCount < clusterer.minMarkersPerCluster ){
243
+ clusterer.clearCluster( clusterer.clusters[i] );
244
+ clusterer.clusters[i] = null;
245
+ }
246
+
247
+ // Shrink the clusters list.
248
+ for ( i = clusterer.clusters.length - 1; i >= 0; --i )
249
+ if ( clusterer.clusters[i] != null )
250
+ break;
251
+ else
252
+ --clusterer.clusters.length;
253
+
254
+ // Ok, we have our clusters. Go through the markers in each
255
+ // cluster and remove them from the map if they are currently up.
256
+ for ( i = 0, len = clusterer.clusters.length ; i < len; ++i ){
257
+ cluster = clusterer.clusters[i];
258
+ if ( cluster != null ){
259
+ for ( j = 0 , len2 = cluster.markers.length ; j < len2; ++j ){
260
+ marker = cluster.markers[j];
261
+ if ( marker != null && marker.onMap ){
262
+ clusterer.map.removeOverlay( marker );
263
+ marker.onMap = false;
264
+ }
265
+ }
266
+ }
267
+ }
268
+
269
+ // Now make cluster-markers for any clusters that need one.
270
+ for ( i = 0, len = clusterer.clusters.length; i < len; ++i ){
271
+ cluster = clusterer.clusters[i];
272
+ if ( cluster != null && cluster.marker == null ){
273
+ // Figure out the average coordinates of the markers in this
274
+ // cluster.
275
+ var xTotal = 0.0, yTotal = 0.0;
276
+ for ( j = 0, len2 = cluster.markers.length; j < len2 ; ++j ){
277
+ marker = cluster.markers[j];
278
+ if ( marker != null ){
279
+ xTotal += ( + marker.getPoint().lng() );
280
+ yTotal += ( + marker.getPoint().lat() );
281
+ }
282
+ }
283
+ var location = new GLatLng( yTotal / cluster.markerCount, xTotal / cluster.markerCount );
284
+ marker = new GMarker( location, { icon: clusterer.icon } );
285
+ cluster.marker = marker;
286
+ GEvent.addListener( marker, 'click', Clusterer.makeCaller( Clusterer.popUp, cluster ) );
287
+ }
288
+ }
289
+ }
290
+
291
+ // Display the visible markers not already up and not in clusters.
292
+ for ( i = 0, len = visibleMarkers.length; i < len; ++i ){
293
+ marker = visibleMarkers[i];
294
+ if ( marker != null && ! marker.onMap && ! marker.inCluster )
295
+ {
296
+ clusterer.map.addOverlay( marker );
297
+ marker.addedToMap();
298
+ marker.onMap = true;
299
+ }
300
+ }
301
+
302
+ // Display the visible clusters not already up.
303
+ for ( i = 0, len = clusterer.clusters.length ; i < len; ++i ){
304
+ cluster = clusterer.clusters[i];
305
+ if ( cluster != null && ! cluster.onMap && bounds.contains( cluster.marker.getPoint() )){
306
+ clusterer.map.addOverlay( cluster.marker );
307
+ cluster.onMap = true;
308
+ }
309
+ }
310
+
311
+ // In case a cluster is currently popped-up, re-pop to get any new
312
+ // markers into the infobox.
313
+ Clusterer.rePop( clusterer );
314
+ };
315
+
316
+
317
+ Clusterer.popUp = function ( cluster ){
318
+ var clusterer = cluster.clusterer;
319
+ var html = '<table width="300">';
320
+ var n = 0;
321
+ for ( var i = 0 , len = cluster.markers.length; i < len; ++i )
322
+ {
323
+ var marker = cluster.markers[i];
324
+ if ( marker != null )
325
+ {
326
+ ++n;
327
+ html += '<tr><td>';
328
+ if ( marker.getIcon().smallImage != null )
329
+ html += '<img src="' + marker.getIcon().smallImage + '">';
330
+ else
331
+ html += '<img src="' + marker.getIcon().image + '" width="' + ( marker.getIcon().iconSize.width / 2 ) + '" height="' + ( marker.getIcon().iconSize.height / 2 ) + '">';
332
+ html += '</td><td>' + marker.description + '</td></tr>';
333
+ if ( n == clusterer.maxLinesPerInfoBox - 1 && cluster.markerCount > clusterer.maxLinesPerInfoBox )
334
+ {
335
+ html += '<tr><td colspan="2">...and ' + ( cluster.markerCount - n ) + ' more</td></tr>';
336
+ break;
337
+ }
338
+ }
339
+ }
340
+ html += '</table>';
341
+ clusterer.map.closeInfoWindow();
342
+ cluster.marker.openInfoWindowHtml( html );
343
+ clusterer.poppedUpCluster = cluster;
344
+ };
345
+
346
+ Clusterer.rePop = function ( clusterer ){
347
+ if ( clusterer.poppedUpCluster != null )
348
+ Clusterer.popUp( clusterer.poppedUpCluster );
349
+ };
350
+
351
+ Clusterer.popDown = function ( clusterer ){
352
+ clusterer.poppedUpCluster = null;
353
+ };
354
+
355
+ Clusterer.prototype.clearCluster = function ( cluster ){
356
+ var i, marker;
357
+
358
+ for ( i = 0; i < cluster.markers.length; ++i ){
359
+ if ( cluster.markers[i] != null ){
360
+ cluster.markers[i].inCluster = false;
361
+ cluster.markers[i] = null;
362
+ }
363
+ }
364
+
365
+ cluster.markers.length = 0;
366
+ cluster.markerCount = 0;
367
+
368
+ if ( cluster == this.poppedUpCluster )
369
+ this.map.closeInfoWindow();
370
+
371
+ if ( cluster.onMap )
372
+ {
373
+ this.map.removeOverlay( cluster.marker );
374
+ cluster.onMap = false;
375
+ }
376
+ };
377
+
378
+ // This returns a function closure that calls the given routine with the
379
+ // specified arg.
380
+ Clusterer.makeCaller = function ( func, arg ){
381
+ return function () { func( arg ); };
382
+ };
383
+
384
+
385
+ // Augment GMarker so it handles markers that have been created but
386
+ // not yet addOverlayed.
387
+ GMarker.prototype.setMap = function ( map ){
388
+ this.map = map;
389
+ };
390
+
391
+ GMarker.prototype.getMap = function (){
392
+ return this.map;
393
+ }
394
+
395
+ GMarker.prototype.addedToMap = function (){
396
+ this.map = null;
397
+ };
398
+
399
+
400
+ GMarker.prototype.origOpenInfoWindow = GMarker.prototype.openInfoWindow;
401
+ GMarker.prototype.openInfoWindow = function ( node, opts ){
402
+ if ( this.map != null )
403
+ return this.map.openInfoWindow( this.getPoint(), node, opts );
404
+ else
405
+ return this.origOpenInfoWindow( node, opts );
406
+ };
407
+
408
+ GMarker.prototype.origOpenInfoWindowHtml = GMarker.prototype.openInfoWindowHtml;
409
+ GMarker.prototype.openInfoWindowHtml = function ( html, opts ){
410
+ if ( this.map != null )
411
+ return this.map.openInfoWindowHtml( this.getPoint(), html, opts );
412
+ else
413
+ return this.origOpenInfoWindowHtml( html, opts );
414
+ };
415
+
416
+ GMarker.prototype.origOpenInfoWindowTabs = GMarker.prototype.openInfoWindowTabs;
417
+ GMarker.prototype.openInfoWindowTabs = function ( tabNodes, opts ){
418
+ if ( this.map != null )
419
+ return this.map.openInfoWindowTabs( this.getPoint(), tabNodes, opts );
420
+ else
421
+ return this.origOpenInfoWindowTabs( tabNodes, opts );
422
+ };
423
+
424
+ GMarker.prototype.origOpenInfoWindowTabsHtml = GMarker.prototype.openInfoWindowTabsHtml;
425
+ GMarker.prototype.openInfoWindowTabsHtml = function ( tabHtmls, opts ){
426
+ if ( this.map != null )
427
+ return this.map.openInfoWindowTabsHtml( this.getPoint(), tabHtmls, opts );
428
+ else
429
+ return this.origOpenInfoWindowTabsHtml( tabHtmls, opts );
430
+ };
431
+
432
+ GMarker.prototype.origShowMapBlowup = GMarker.prototype.showMapBlowup;
433
+ GMarker.prototype.showMapBlowup = function ( opts ){
434
+ if ( this.map != null )
435
+ return this.map.showMapBlowup( this.getPoint(), opts );
436
+ else
437
+ return this.origShowMapBlowup( opts );
438
+ };
439
+
440
+
441
+ function addDescriptionToMarker(marker, description){
442
+ marker.description = description;
443
+ return marker;
444
+ }