1 /**
  2  * Google API v2 implementation for Geo Mashup maps.
  3  * @fileOverview
  4  */
  5 
  6 /*global GeoMashup */
  7 /*global customizeGeoMashup, customizeGeoMashupMap, customGeoMashupColorIcon, customGeoMashupCategoryIcon */
  8 /*global customGeoMashupSinglePostIcon, customGeoMashupMultiplePostImage */
  9 /*global jQuery, google, G_DEFAULT_ICON, mxn, ClusterMarker, MarkerClusterer */
 10 /*jslint browser:true, white: true, vars: true, sloppy: true, evil: true */
 11 
 12 GeoMashup.createTermLine = function ( term_data ) {
 13 
 14 	term_data.line = new google.maps.Polyline( term_data.points, term_data.color);
 15 
 16 	google.maps.Event.addListener( term_data.line, 'click', function () {
 17 		GeoMashup.map.zoomIn();
 18 	} );
 19 
 20 	this.doAction( 'termLine', term_data.line );
 21 	this.doAction( 'categoryLine', GeoMashup.opts, term_data.line );
 22 
 23 	this.map.addOverlay( term_data.line );
 24 
 25 	if ( this.map.getZoom() > term_data.max_line_zoom ) {
 26 		term_data.line.hide();
 27 	}
 28 
 29 };
 30 
 31 GeoMashup.openMarkerInfoWindow = function( marker, content_node, window_opts ) {
 32 	var latlng = marker.getLatLng();
 33 	this.doAction( 'markerInfoWindowOptions', this.opts, this.locations[latlng], window_opts );
 34 	marker.openInfoWindow( content_node, window_opts );
 35 };
 36 
 37 GeoMashup.loadMaxContent = function( marker, regular_node, info_window_max_url, cache ) {
 38 	var info_window_max_request = new google.maps.XmlHttp.create();
 39 	var request_options = {url: info_window_max_url};
 40 	/** 
 41 	 * A marker's maximized info window content is being requested.
 42 	 * @name GeoMashup#markerInfoWindowMaxRequest
 43 	 * @event
 44 	 * @param {Marker} marker
 45 	 * @param {AjaxRequestOptions} request_options 
 46 	 */
 47 	this.doAction( 'markerInfoWindowMaxRequest', marker, request_options );
 48 	info_window_max_request.open( 'GET', request_options.url, true );
 49 	info_window_max_request.onreadystatechange = function() {
 50 		var max_node, max_options, filter;
 51 		if (info_window_max_request.readyState === 4 && info_window_max_request.status === 200 ) {
 52 			filter = {content: info_window_max_request.responseText};
 53 			/**
 54 			 * A marker's maximized info window content is being loaded.
 55 			 * @name GeoMashup#markerInfoWindowMaxLoad
 56 			 * @event
 57 			 * @param {Marker} marker
 58 			 * @param {ContentFilter} filter 
 59 			 */
 60 			GeoMashup.doAction( 'markerInfoWindowMaxLoad', marker, filter );
 61 			max_node = document.createElement( 'div' );
 62 			max_node.innerHTML = filter.content;
 63 			GeoMashup.parentizeLinks( max_node );
 64 			cache.info_window_options = {maxContent: max_node};
 65 			GeoMashup.openMarkerInfoWindow( marker, regular_node, cache.info_window_options );
 66 		} // end max readState === 4
 67 	}; // end max onreadystatechange function
 68 	info_window_max_request.send( null );
 69 };
 70 
 71 GeoMashup.openInfoWindow = function( marker ) {
 72 	var object_ids, cache_key, cache, i, request_options, info_window_request, object_element, point = marker.getPoint();
 73 
 74 	this.map.closeInfoWindow();
 75 		
 76 	object_ids = this.getOnObjectIDs( this.getMarkerObjects( marker ) );
 77 	cache_key = 'info-window-' + object_ids.join(',');
 78 	cache = this.locationCache( point, cache_key );
 79 	if ( cache.info_node ) {
 80 		marker.openInfoWindow( cache.info_node, cache.info_window_options );
 81 	} else {
 82 		marker.openInfoWindowHtml('<div align="center"><img src="' +
 83 			this.opts.url_path + 
 84 			'/images/busy_icon.gif" alt="Loading..." /></div>');
 85 		request_options = {
 86 			url: this.geo_query_url + '&object_name=' + this.opts.object_name +
 87 				'&object_ids=' + object_ids.join( ',' )
 88 		};
 89 		this.doAction( 'markerInfoWindowRequest', marker, request_options );
 90 		info_window_request = new google.maps.XmlHttp.create();
 91 		info_window_request.open('GET', request_options.url, true);
 92 		info_window_request.onreadystatechange = function() {
 93 			var node, info_window_max_request, filter;
 94 
 95 			if (info_window_request.readyState === 4 && info_window_request.status === 200) {
 96 				filter = {content: info_window_request.responseText};
 97 				GeoMashup.doAction( 'markerInfoWindowLoad', marker, filter );
 98 				node = document.createElement('div');
 99 				node.innerHTML = filter.content;
100 				GeoMashup.parentizeLinks( node );
101 				cache.info_node = node;
102 				if ( 'post' === GeoMashup.opts.object_name ) {
103 					GeoMashup.loadMaxContent( marker, node, request_options.url + '&template=info-window-max', cache );
104 				} else {
105 					cache.info_window_options = {};
106 					GeoMashup.openMarkerInfoWindow( marker, node, cache.info_window_options );
107 					GeoMashup.doAction( 'loadedInfoWindow' );
108 				}
109 			} // end readystate === 4
110 		}; // end onreadystatechange function
111 		info_window_request.send(null);
112 	} // end object not loaded yet 
113 };
114 
115 GeoMashup.addGlowMarker = function( marker ) {
116 	var glow_icon;
117 
118 	if ( this.glow_marker ) {
119 		this.map.removeOverlay( this.glow_marker );
120 		this.glow_marker.setLatLng( marker.getLatLng() );
121 	} else {
122 		glow_icon = new google.maps.Icon( {
123 			image : this.opts.url_path + '/images/mm_20_glow.png',
124 			iconSize : new google.maps.Size( 22, 30 ),
125 			iconAnchor : new google.maps.Point( 11, 27 ) 
126 		} );
127 		this.doAction( 'glowMarkerIcon', this.opts, glow_icon );
128 		this.glow_marker = new google.maps.Marker( marker.getLatLng(), {
129 			clickable : false,
130 			icon : glow_icon
131 		} );
132 	}
133 	this.map.addOverlay( this.glow_marker );
134 };
135 
136 GeoMashup.removeGlowMarker = function() {
137 	this.map.removeOverlay( this.glow_marker );
138 };
139 
140 GeoMashup.hideAttachments = function() {
141 	var i, j, obj;
142 
143 	for ( i = 0; i < this.open_attachments.length; i += 1 ) {
144 		this.map.removeOverlay( this.open_attachments[i] );
145 	} 
146 	this.open_attachments = [];
147 };
148 
149 GeoMashup.showMarkerAttachments = function( marker ) {
150 	var objects;
151 
152 	this.hideAttachments();
153 	objects = this.getObjectsAtLocation( marker.getLatLng() );
154 	this.forEach( objects, function( i, obj ) {
155 		var ajax_params = {action: 'geo_mashup_kml_attachments'};
156 		if ( obj.attachments ) {
157 			// Attachment overlays are available
158 			this.forEach( obj.attachments, function( j, attachment ) {
159 				this.open_attachments.push( attachment );
160 				this.map.addOverlay( attachment );
161 			} );
162 		} else {
163 			// Look for attachments
164 			obj.attachments = [];
165 			ajax_params.post_ids = obj.object_id;
166 			jQuery.getJSON( this.opts.ajaxurl + '?callback=?', ajax_params, function( data ) {
167 				GeoMashup.forEach( data, function( j, url ) {
168 					var geoxml = new google.maps.GeoXml( url );
169 					obj.attachments.push( geoxml );
170 					this.open_attachments.push( geoxml );
171 					this.map.addOverlay( geoxml );
172 				} );
173 			} );
174 		}
175 	} );
176 };
177 
178 GeoMashup.loadFullPost = function( point ) {
179 	var i, url, cache, post_request, objects, object_ids, request_options;
180 
181 	objects = this.getObjectsAtLocation( point );
182 	object_ids = this.getOnObjectIDs( objects );
183 	cache = this.locationCache( point, 'full-post-' + object_ids.join(',') );
184 	if ( cache.post_html ) {
185 		this.getShowPostElement().innerHTML = cache.post_html;
186 	} else {
187 
188 		this.getShowPostElement().innerHTML = '<div align="center"><img src="' +
189 			this.opts.url_path + '/images/busy_icon.gif" alt="Loading..." /></div>';
190 		request_options = {
191 			url: this.geo_query_url + '&object_name=' + this.opts.object_name +
192 				'&object_ids=' + object_ids.join( ',' ) + '&template=full-post'
193 		};
194 		this.doAction( 'fullPostRequest', objects, request_options );
195 		post_request = new google.maps.XmlHttp.create();
196 		post_request.open('GET', request_options.url, true);
197 		post_request.onreadystatechange = function() {
198 			var filter;
199 			if (post_request.readyState === 4 && post_request.status === 200) {
200 				filter = {content: post_request.responseText};
201 				GeoMashup.doAction( 'fullPostLoad', objects, filter );
202 				GeoMashup.getShowPostElement().innerHTML = filter.content;
203 				GeoMashup.doAction( 'fullPostChanged' );
204 				cache.post_html = filter.content;
205 			} // end readystate === 4
206 		}; // end onreadystatechange function
207 		post_request.send(null);
208 	}
209 };
210 
211 GeoMashup.addObjectIcon = function( obj ) {
212 
213 	// Back compat
214 	if ( typeof customGeoMashupCategoryIcon === 'function' && obj.terms && obj.terms.hasOwnProperty( 'category' ) ) {
215 		obj.icon = customGeoMashupCategoryIcon( GeoMashup.opts, obj.terms.category );
216 	} 
217 
218 	if (!obj.icon) {
219 
220 		
221 		jQuery.each( obj.terms, function( taxonomy, terms ) {
222 			var single_icon;
223 
224 			if ( terms.length > 1 ) {
225 
226 				obj.icon = new google.maps.Icon( GeoMashup.multiple_term_icon );
227 				return false; // continue
228 
229 			} else if ( terms.length === 1 ) {
230 
231 				single_icon = GeoMashup.term_manager.getTermData( taxonomy, terms[0], 'icon' );
232 
233 				if ( obj.icon && obj.icon.image !== single_icon.image ) {
234 
235 					// We have two different icons in different taxonomies
236 					obj.icon = new google.maps.Icon( GeoMashup.multiple_term_icon );
237 					return false;
238 
239 				} else {
240 
241 					obj.icon = GeoMashup.clone( single_icon );
242 
243 				}
244 
245 			} 
246 
247 		} );
248 
249 		if ( !obj.icon ) {
250 			obj.icon = GeoMashup.colorIcon( 'red' );
251 		}
252 
253 		this.doAction( 'objectIcon', this.opts, obj );
254 
255 	}
256 };
257 
258 GeoMashup.createMarker = function( point, obj ) {
259 	var marker, 
260 		// Apersand entities have been added for validity, but look bad in titles
261 		marker_opts = {title: obj.title.replace( '&', '&' )};
262 
263 	if ( !obj.icon ) {
264 		this.addObjectIcon( obj );
265 	}
266 	marker_opts.icon = this.clone( obj.icon );
267 	this.doAction( 'objectMarkerOptions', this.opts, marker_opts, obj );
268 	marker = new google.maps.Marker(point,marker_opts);
269 
270 	google.maps.Event.addListener(marker, 'click', function() {
271 		GeoMashup.selectMarker( marker );
272 	}); 
273 
274 	google.maps.Event.addListener( marker, 'remove', function() {
275 		if ( GeoMashup.selected_marker && marker === GeoMashup.selected_marker ) {
276 			GeoMashup.deselectMarker();
277 		}
278 	} );
279 
280 	google.maps.Event.addListener( marker, 'visibilitychanged', function( is_visible ) {
281 		if ( GeoMashup.selected_marker && marker === GeoMashup.selected_marker && !is_visible ) {
282 			GeoMashup.deselectMarker();
283 		}
284 	} );
285 
286 	this.doAction( 'marker', this.opts, marker );
287 
288 	return marker;
289 };
290 
291 GeoMashup.checkDependencies = function () {
292 	if (typeof google.maps.Map === 'undefined' || !google.maps.BrowserIsCompatible()) {
293 		this.container.innerHTML = '<p class="errormessage">' +
294 			'Sorry, the Google Maps script failed to load. Have you entered your ' +
295 			'<a href="http://maps.google.com/apis/maps/signup.html">API key<\/a> ' +
296 			'in the Geo Mashup Options?';
297 		throw "The Google Maps javascript didn't load.";
298 	}
299 };
300 
301 GeoMashup.clickObjectMarker = function(object_id, try_count) {
302 	if (typeof try_count === 'undefined') {
303 		try_count = 1;
304 	}
305 	if (this.objects[object_id] && try_count < 4) {
306 		if (GeoMashup.objects[object_id].marker.isHidden()) {
307 			try_count += 1;
308 			setTimeout(function () {GeoMashup.clickObjectMarker(object_id, try_count);}, 300);
309 		} else {
310 			google.maps.Event.trigger(GeoMashup.objects[object_id].marker,"click"); 
311 		}
312 	}
313 };
314 
315 GeoMashup.colorIcon = function( color_name ) {
316 	var icon = new google.maps.Icon(this.base_color_icon);
317 	icon.image = this.opts.url_path + '/images/mm_20_' + color_name + '.png';
318 	return icon;
319 };
320 
321 GeoMashup.getMarkerLatLng = function( marker ) {
322 	return marker.getLatLng();
323 };
324 
325 GeoMashup.hideMarker = function( marker ) {
326 	if ( marker === this.selected_marker ) {
327 		this.deselectMarker();
328 	}
329 	marker.hide();
330 };
331 
332 GeoMashup.showMarker = function( marker ) {
333 	marker.show();
334 };
335 
336 GeoMashup.hideLine = function( line ) {
337 	line.hide();
338 };
339 
340 GeoMashup.showLine = function( line ) {
341 	line.show();
342 };
343 
344 GeoMashup.isLineVisible = function( line ) {
345 	return line.isHidden();
346 };
347 
348 GeoMashup.newLatLng = function( lat, lng ) {
349 	return new google.maps.LatLng( lat, lng );
350 };
351 
352 GeoMashup.extendLocationBounds = function( latlng ) {
353 	this.location_bounds.extend( latlng );
354 };
355 
356 GeoMashup.addMarkers = function( markers ) {
357 	// No clustering, or ClusterMarker need the markers added to the map
358 	this.forEach( markers, function( i, marker ) {
359 		this.map.addOverlay( marker );
360 	} );
361 	if ( this.clusterer && markers.length > 0 ) {
362 		this.clusterer.addMarkers( markers );
363 		this.recluster();
364 	}
365 };
366 
367 GeoMashup.makeMarkerMultiple = function( marker ) {
368 	var plus_image;
369 	if (typeof customGeoMashupMultiplePostImage === 'function') {
370 		plus_image = customGeoMashupMultiplePostImage(this.opts, marker.getIcon().image);
371 	}
372 	if (!plus_image) {
373 		plus_image = this.opts.url_path + '/images/mm_20_plus.png';
374 	}
375 	if ( marker.getIcon().image !== plus_image ) {
376 		// User testing gave best results when both methods of
377 		// changing the marker image are used in this order
378 		marker.setImage( plus_image );
379 		marker.getIcon().image = plus_image;
380 	}
381 	this.doAction( 'multiObjectMarker', this.opts, marker );
382 	this.doAction( 'multiObjectIcon', this.opts, marker.getIcon() );
383 };
384 
385 GeoMashup.setMarkerImage = function( marker, image_url ) {
386 	if ( marker.getIcon().image !== image_url ) {
387 		marker.setImage( image_url );
388 		marker.getIcon().image = image_url;
389 	}
390 };
391 
392 GeoMashup.setCenterUpToMaxZoom = function( latlng, zoom, callback ) {
393 	var map_type = this.map.getCurrentMapType();
394 	if ( map_type === google.maps.SATELLITE_MAP || map_type === google.maps.HYBRID_MAP ) {
395 		map_type.getMaxZoomAtLatLng( latlng, function( response ) {
396 			if ( response && response.status === google.maps.GEO_SUCCESS ) {
397 				if ( response.zoom < zoom ) {
398 					zoom = response.zoom;
399 				}
400 			}
401 			GeoMashup.map.setCenter( latlng, zoom );
402 			if ( typeof callback === 'function' ) {
403 				callback( zoom );
404 			}
405 		}, zoom );
406 	} else {
407 		// Current map type doesn't have getMaxZoomAtLatLng
408 		if ( map_type.getMaximumResolution() < zoom ) {
409 			zoom = map_type.getMaximumResolution();
410 		}
411 		this.map.setCenter( latlng, zoom );
412 		if ( typeof callback === 'function' ) {
413 			callback( zoom );
414 		}
415 	}
416 };
417 
418 GeoMashup.autoZoom = function() {
419 	var zoom = this.map.getBoundsZoomLevel( this.location_bounds );
420 	var max_zoom = parseInt( this.opts.auto_zoom_max, 10 );
421 	if ( zoom > max_zoom ) {
422 		zoom = max_zoom;
423 	}
424 	this.setCenterUpToMaxZoom( 
425 		this.location_bounds.getCenter(), 
426 		zoom,
427 		function() {GeoMashup.updateVisibleList();} 
428 	);
429 };
430 
431 GeoMashup.centerMarker = function( marker, zoom ) {
432 	if ( typeof zoom === 'number' ) {
433 		this.map.setCenter( marker.getLatLng(), zoom );
434 	} else {
435 		this.map.setCenter( marker.getLatLng() );
436 	}
437 };
438 
439 GeoMashup.requestObjects = function( use_bounds ) {
440 	var request, url, map_bounds, map_span;
441 	if (this.opts.max_posts && this.object_count >= this.opts.max_posts) {
442 		return;
443 	}
444 	request = google.maps.XmlHttp.create();
445 	url = this.geo_query_url;
446 	if (use_bounds) {
447 		map_bounds = this.map.getBounds();
448 		map_span = map_bounds.toSpan();
449 		url += '&minlat=' + (map_bounds.getSouthWest().lat() - map_span.lat()) + 
450 			'&minlon=' + (map_bounds.getSouthWest().lng() - map_span.lng()) + 
451 			'&maxlat=' + (map_bounds.getSouthWest().lat() + 3*map_span.lat()) + 
452 			'&maxlon=' + (map_bounds.getSouthWest().lng() + 3*map_span.lat());
453 	}
454 	if (this.opts.map_cat) {
455 		url += '&cat=' + GeoMashup.opts.map_cat;
456 	}
457 	if (this.opts.max_posts) {
458 		url += '&limit=' + GeoMashup.opts.max_posts;
459 	}
460 	request.open("GET", url, true);
461 	request.onreadystatechange = function() {
462 		var objects;
463 
464 		if (request.readyState === 4 && request.status === 200) {
465 			objects = window['eval']( '(' + request.responseText + ')' );
466 			GeoMashup.addObjects(objects,!use_bounds);
467 		} // end readystate === 4
468 	}; // end onreadystatechange function
469 	request.send(null);
470 };
471 
472 GeoMashup.isMarkerVisible = function( marker ) {
473 	var map_bounds = this.map.getBounds();
474 	return ( ! marker.isHidden() && map_bounds.containsLatLng( marker.getLatLng() ) );
475 };
476 
477 GeoMashup.recluster = function( ) {
478 	this.clusterer.refresh();
479 };
480 
481 GeoMashup.createMap = function(container, opts) {
482 	var i, type_num, center_latlng, map_opts, map_types, request, url, objects, point, marker_opts, 
483 		clusterer_opts, google_bar_opts, single_marker, ov, credit_div, initial_zoom = 1, filter = {};
484 
485 	this.container = container;
486 	this.checkDependencies();
487 	this.base_color_icon = new google.maps.Icon();
488 	this.base_color_icon.image = opts.url_path + '/images/mm_20_black.png';
489 	this.base_color_icon.shadow = opts.url_path + '/images/mm_20_shadow.png';
490 	this.base_color_icon.iconSize = new google.maps.Size(12, 20);
491 	this.base_color_icon.shadowSize = new google.maps.Size(22, 20);
492 	this.base_color_icon.iconAnchor = new google.maps.Point(6, 20);
493 	this.base_color_icon.infoWindowAnchor = new google.maps.Point(5, 1);
494 	this.multiple_term_icon = new google.maps.Icon( this.base_color_icon );
495 	this.multiple_term_icon.image = opts.url_path + '/images/mm_20_mixed.png';
496 
497 	// Falsify options to make tests simpler
498 	this.forEach( opts, function( key, value ) {
499 		if ( 'false' === value || 'FALSE' === value ) {
500 			opts[key] = false;
501 		}
502 	} );
503 
504 	// See if we have access to a parent frame
505 	this.have_parent_access = false;
506 	try {
507 		if ( typeof parent === 'object' ) {
508 			// Try access, throws an exception if prohibited
509 			parent.document.getElementById( 'bogus-test' );
510 			// Access worked
511 			this.have_parent_access = true;
512 		}
513 	} catch ( parent_exception ) { }
514 
515 	// For now, siteurl is the home url
516 	opts.home_url = opts.siteurl;
517 
518 	map_types = {
519 		'G_NORMAL_MAP' : google.maps.NORMAL_MAP,
520 		'G_SATELLITE_MAP' : google.maps.SATELLITE_MAP,
521 		'G_HYBRID_MAP' : google.maps.HYBRID_MAP,
522 		'G_PHYSICAL_MAP' : google.maps.PHYSICAL_MAP,
523 		'G_SATELLITE_3D_MAP' : google.maps.SATELLITE_3D_MAP
524 	};
525 
526 	if (typeof opts.map_type === 'string') {
527 		if ( map_types[opts.map_type] ) {
528 			opts.map_type = map_types[opts.map_type] ;
529 		} else {
530 			type_num = parseInt(opts.map_type, 10);
531 			if (isNaN(type_num)) {
532 				opts.map_type = google.maps.NORMAL_MAP;
533 			} else {
534 				opts.map_type = this.map.getMapTypes()[type_num];
535 			}
536 		}
537 	} else if (typeof opts.map_type === 'undefined') {
538 		opts.map_type = google.maps.NORMAL_MAP;
539 	}
540 	map_opts = {
541 		mapTypes : [ opts.map_type ],
542 		googleBarOptions : { 
543 			adsOptions : {client : opts.adsense_code || 'pub-5088093001880917'}
544 		}
545 	};
546 	if ( opts.background_color ) {
547 		map_opts.backgroundColor = opts.background_color;
548 	}
549 	this.doAction( 'mapOptions', opts, map_opts );
550 	this.map = new google.maps.Map2( this.container, map_opts );
551 	this.map.setCenter(new google.maps.LatLng(0,0), 0);
552 
553 	this.doAction( 'newMap', opts, this.map );
554 
555 	// Create the loading spinner icon and show it
556 	this.spinner_div = document.createElement( 'div' );
557 	this.spinner_div.innerHTML = '<div id="gm-loading-icon" style="-moz-user-select: none; z-index: 100; position: absolute; left: ' +
558 		( this.map.getSize().width / 2 ) + 'px; top: ' + ( this.map.getSize().height / 2 ) + 'px;">' +
559 		'<img style="border: 0px none ; margin: 0px; padding: 0px; width: 16px; height: 16px; -moz-user-select: none;" src="' +
560 		opts.url_path + '/images/busy_icon.gif"/></a></div>';
561 	this.showLoadingIcon();
562 	google.maps.Event.bind( this.map, 'tilesloaded', this, this.hideLoadingIcon );
563 
564 	if (!opts.object_name) {
565 		opts.object_name = 'post';
566 	}
567 	this.opts = opts;
568 	filter.url = opts.siteurl +
569 		( opts.siteurl.indexOf( '?' ) > 0 ? '&' : '?' ) +
570 		'geo_mashup_content=geo-query&map_name=' + encodeURIComponent( opts.name );
571 	if ( opts.lang && filter.url.indexOf( 'lang=' ) === -1 ) {
572 		filter.url += '&lang=' + encodeURIComponent( opts.lang );
573 	}
574 	this.doAction( 'geoQueryUrl', this.opts, filter );
575 	this.geo_query_url = filter.url;
576 
577 	google.maps.Event.bind(this.map, "zoomend", this, this.adjustZoom);
578 	google.maps.Event.bind(this.map, "moveend", this, this.adjustViewport);
579 
580 	if (opts.cluster_max_zoom) {
581 		clusterer_opts = {
582 			'iconOptions' : {},
583 			'fitMapMaxZoom' : opts.cluster_max_zoom,
584 			'clusterMarkerTitle' : '%count',
585 			'intersectPadding' : 3
586 		};
587 		/**
588 		 * <a href="http://googlemapsapi.martinpearman.co.uk/readarticle.php?article_id=4">ClusterMarker</a>
589 		 * options are being set on a Google v2 map.
590 		 *
591 		 * @see markerClustererOptions for Google v3.
592 		 * @name GeoMashup#clusterOptions
593 		 * @event
594 		 * @param {GeoMashupOptions} properties Geo Mashup configuration data
595 		 * @param {Object} clusterer_opts Modifiable clusterer options for
596 		 *   <a href="http://googlemapsapi.martinpearman.co.uk/readarticle.php?article_id=4">ClusterMarker</a>.
597 		 */
598 		this.doAction( 'clusterOptions', this.opts, clusterer_opts );
599 		this.clusterer = new ClusterMarker( this.map, clusterer_opts );
600 	}
601 
602 	if ( opts.zoom !== 'auto' && typeof opts.zoom === 'string' ) {
603 		initial_zoom = parseInt(opts.zoom, 10);
604 	} else {
605 		initial_zoom = opts.zoom;
606 	}
607 
608 	if (opts.load_kml) {
609 		this.kml = new google.maps.GeoXml(opts.load_kml);
610 		this.map.addOverlay(this.kml);
611 		if ( initial_zoom === 'auto' ) {
612 			this.kml.gotoDefaultViewport( this.map );
613 		}
614 	}
615 
616 	if ( this.term_manager ) {
617 		this.term_manager.load();
618 	}
619 
620 	if ( initial_zoom !== 'auto' ) {
621 		if (opts.center_lat && opts.center_lng) {
622 			// Use the center from options
623 			this.map.setCenter(new google.maps.LatLng(opts.center_lat, opts.center_lng), initial_zoom, opts.map_type);
624 		} else if (this.kml) {
625 			google.maps.Event.addListener( this.kml, 'load', function() {
626 				GeoMashup.map.setCenter( GeoMashup.kml.getDefaultCenter(), initial_zoom, opts.map_type );
627 			} );
628 		} else if (opts.object_data && opts.object_data.objects[0]) {
629 			center_latlng = new google.maps.LatLng(opts.object_data.objects[0].lat, opts.object_data.objects[0].lng);
630 			this.map.setCenter(center_latlng, initial_zoom, opts.map_type);
631 		} else {
632 			// Center on the most recent located object
633 			request = google.maps.XmlHttp.create();
634 			url = this.geo_query_url + '&limit=1';
635 			if (opts.map_cat) {
636 				url += '&cat='+opts.map_cat;
637 			}
638 			request.open("GET", url, false);
639 			request.send(null);
640 			objects = window['eval']( '(' + request.responseText + ')' );
641 			if (objects.length>0) {
642 				point = new google.maps.LatLng(objects[0].lat,objects[0].lng);
643 				this.map.setCenter(point,initial_zoom,opts.map_type);
644 			} else {
645 				this.map.setCenter(new google.maps.LatLng(0,0),initial_zoom,opts.map_type);
646 			}
647 		}
648 	}
649 
650 	this.location_bounds = new google.maps.LatLngBounds();
651 
652 	if (opts.map_content === 'single')
653 	{
654 		if (opts.object_data && opts.object_data.objects.length && !this.kml)
655 		{
656 			marker_opts = {};
657 			if (typeof customGeoMashupSinglePostIcon === 'function') {
658 				marker_opts.icon = customGeoMashupSinglePostIcon(this.opts);
659 			}
660 			if ( !marker_opts.icon ) {
661 				marker_opts.icon = G_DEFAULT_ICON;
662 			}
663 			this.doAction( 'singleMarkerOptions', this.opts, marker_opts );
664 			single_marker = new google.maps.Marker(
665 				new google.maps.LatLng( opts.object_data.objects[0].lat, opts.object_data.objects[0].lng ), marker_opts );
666 			this.map.addOverlay( single_marker );
667 			this.doAction( 'singleMarker', this.opts, single_marker );
668 		}
669 	} else if (opts.object_data) {
670 		this.addObjects(opts.object_data.objects,true);
671 	} else {
672 		// Request objects near visible range first
673 		this.requestObjects(true);
674 
675 		// Request all objects
676 		this.requestObjects(false);
677 	}
678 
679 	if ('GSmallZoomControl' === opts.map_control) {
680 		this.map.addControl(new google.maps.SmallZoomControl());
681 	} else if ('GSmallZoomControl3D' === opts.map_control) {
682 		this.map.addControl(new google.maps.SmallZoomControl3D());
683 	} else if ('GSmallMapControl' === opts.map_control) {
684 		this.map.addControl(new google.maps.SmallMapControl());
685 	} else if ('GLargeMapControl' === opts.map_control) {
686 		this.map.addControl(new google.maps.LargeMapControl());
687 	} else if ('GLargeMapControl3D' === opts.map_control) {
688 		this.map.addControl(new google.maps.LargeMapControl3D());
689 	}
690 
691 	if (opts.add_map_type_control ) {
692 		if ( typeof opts.add_map_type_control === 'string' ) {
693 			opts.add_map_type_control = opts.add_map_type_control.split(/\s*,\s*/);
694 			if ( typeof map_types[opts.add_map_type_control[0]] === 'undefined' ) {
695 				// Convert the old boolean value to a default array
696 				opts.add_map_type_control = [ 'G_NORMAL_MAP', 'G_SATELLITE_MAP', 'G_PHYSICAL_MAP' ];
697 			}
698 		}
699 		for ( i = 0; i < opts.add_map_type_control.length; i += 1 ) {
700 			this.map.addMapType( map_types[opts.add_map_type_control[i]] );
701 		}
702 		this.map.addControl(new google.maps.MapTypeControl());
703 	}
704 
705 	if (opts.add_overview_control) {
706 		this.overview_control = new google.maps.OverviewMapControl();
707 		this.overview_control.setMapType( opts.map_type );
708 		this.doAction( 'overviewControl', this.opts, this.overview_control );
709 		this.map.addControl( this.overview_control );
710 		ov = document.getElementById('gm-overview');
711 		if (ov) {
712 			ov.style.position = 'absolute';
713 			this.container.appendChild(ov);
714 		}
715 	}
716 
717 	if ( opts.add_google_bar ) {
718 		this.map.enableGoogleBar();
719 	}
720 
721 	if ( opts.enable_scroll_wheel_zoom ) {
722 		this.map.enableScrollWheelZoom();
723 	}
724 
725 	google.maps.Event.addListener( this.map, 'click', function( overlay ) {
726 		if ( GeoMashup.selected_marker && ( ! overlay ) ) {
727 			GeoMashup.deselectMarker();
728 		}
729 	} );
730 
731 	if (typeof customizeGeoMashupMap === 'function') {
732 		customizeGeoMashupMap(this.opts, this.map);
733 	}
734 	if (typeof customizeGeoMashup === 'function') {
735 		customizeGeoMashup(this);
736 	}
737 	this.doAction( 'loadedMap', this.opts, this.map );
738 
739 };
740