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.newLatLng = function( lat, lng ) {
345 	return new google.maps.LatLng( lat, lng );
346 };
347 
348 GeoMashup.extendLocationBounds = function( latlng ) {
349 	this.location_bounds.extend( latlng );
350 };
351 
352 GeoMashup.addMarkers = function( markers ) {
353 	// No clustering, or ClusterMarker need the markers added to the map
354 	this.forEach( markers, function( i, marker ) {
355 		this.map.addOverlay( marker );
356 	} );
357 	if ( this.clusterer && markers.length > 0 ) {
358 		this.clusterer.addMarkers( markers );
359 		this.recluster();
360 	}
361 };
362 
363 GeoMashup.makeMarkerMultiple = function( marker ) {
364 	var plus_image;
365 	if (typeof customGeoMashupMultiplePostImage === 'function') {
366 		plus_image = customGeoMashupMultiplePostImage(this.opts, marker.getIcon().image);
367 	}
368 	if (!plus_image) {
369 		plus_image = this.opts.url_path + '/images/mm_20_plus.png';
370 	}
371 	if ( marker.getIcon().image !== plus_image ) {
372 		// User testing gave best results when both methods of
373 		// changing the marker image are used in this order
374 		marker.setImage( plus_image );
375 		marker.getIcon().image = plus_image;
376 	}
377 	this.doAction( 'multiObjectMarker', this.opts, marker );
378 	this.doAction( 'multiObjectIcon', this.opts, marker.getIcon() );
379 };
380 
381 GeoMashup.setMarkerImage = function( marker, image_url ) {
382 	if ( marker.getIcon().image !== image_url ) {
383 		marker.setImage( image_url );
384 		marker.getIcon().image = image_url;
385 	}
386 };
387 
388 GeoMashup.setCenterUpToMaxZoom = function( latlng, zoom, callback ) {
389 	var map_type = this.map.getCurrentMapType();
390 	if ( map_type === google.maps.SATELLITE_MAP || map_type === google.maps.HYBRID_MAP ) {
391 		map_type.getMaxZoomAtLatLng( latlng, function( response ) {
392 			if ( response && response.status === google.maps.GEO_SUCCESS ) {
393 				if ( response.zoom < zoom ) {
394 					zoom = response.zoom;
395 				}
396 			}
397 			GeoMashup.map.setCenter( latlng, zoom );
398 			if ( typeof callback === 'function' ) {
399 				callback( zoom );
400 			}
401 		}, zoom );
402 	} else {
403 		// Current map type doesn't have getMaxZoomAtLatLng
404 		if ( map_type.getMaximumResolution() < zoom ) {
405 			zoom = map_type.getMaximumResolution();
406 		}
407 		this.map.setCenter( latlng, zoom );
408 		if ( typeof callback === 'function' ) {
409 			callback( zoom );
410 		}
411 	}
412 };
413 
414 GeoMashup.autoZoom = function() {
415 	var zoom = this.map.getBoundsZoomLevel( this.location_bounds );
416 	var max_zoom = parseInt( this.opts.auto_zoom_max, 10 );
417 	if ( zoom > max_zoom ) {
418 		zoom = max_zoom;
419 	}
420 	this.setCenterUpToMaxZoom( 
421 		this.location_bounds.getCenter(), 
422 		zoom,
423 		function() {GeoMashup.updateVisibleList();} 
424 	);
425 };
426 
427 GeoMashup.centerMarker = function( marker, zoom ) {
428 	if ( typeof zoom === 'number' ) {
429 		this.map.setCenter( marker.getLatLng(), zoom );
430 	} else {
431 		this.map.setCenter( marker.getLatLng() );
432 	}
433 };
434 
435 GeoMashup.requestObjects = function( use_bounds ) {
436 	var request, url, map_bounds, map_span;
437 	if (this.opts.max_posts && this.object_count >= this.opts.max_posts) {
438 		return;
439 	}
440 	request = google.maps.XmlHttp.create();
441 	url = this.geo_query_url;
442 	if (use_bounds) {
443 		map_bounds = this.map.getBounds();
444 		map_span = map_bounds.toSpan();
445 		url += '&minlat=' + (map_bounds.getSouthWest().lat() - map_span.lat()) + 
446 			'&minlon=' + (map_bounds.getSouthWest().lng() - map_span.lng()) + 
447 			'&maxlat=' + (map_bounds.getSouthWest().lat() + 3*map_span.lat()) + 
448 			'&maxlon=' + (map_bounds.getSouthWest().lng() + 3*map_span.lat());
449 	}
450 	if (this.opts.map_cat) {
451 		url += '&cat=' + GeoMashup.opts.map_cat;
452 	}
453 	if (this.opts.max_posts) {
454 		url += '&limit=' + GeoMashup.opts.max_posts;
455 	}
456 	request.open("GET", url, true);
457 	request.onreadystatechange = function() {
458 		var objects;
459 
460 		if (request.readyState === 4 && request.status === 200) {
461 			objects = window['eval']( '(' + request.responseText + ')' );
462 			GeoMashup.addObjects(objects,!use_bounds);
463 		} // end readystate === 4
464 	}; // end onreadystatechange function
465 	request.send(null);
466 };
467 
468 GeoMashup.isMarkerVisible = function( marker ) {
469 	var map_bounds = this.map.getBounds();
470 	return ( ! marker.isHidden() && map_bounds.containsLatLng( marker.getLatLng() ) );
471 };
472 
473 GeoMashup.recluster = function( ) {
474 	this.clusterer.refresh();
475 };
476 
477 GeoMashup.createMap = function(container, opts) {
478 	var i, type_num, center_latlng, map_opts, map_types, request, url, objects, point, marker_opts, 
479 		clusterer_opts, google_bar_opts, single_marker, ov, credit_div, initial_zoom = 1, filter = {};
480 
481 	this.container = container;
482 	this.checkDependencies();
483 	this.base_color_icon = new google.maps.Icon();
484 	this.base_color_icon.image = opts.url_path + '/images/mm_20_black.png';
485 	this.base_color_icon.shadow = opts.url_path + '/images/mm_20_shadow.png';
486 	this.base_color_icon.iconSize = new google.maps.Size(12, 20);
487 	this.base_color_icon.shadowSize = new google.maps.Size(22, 20);
488 	this.base_color_icon.iconAnchor = new google.maps.Point(6, 20);
489 	this.base_color_icon.infoWindowAnchor = new google.maps.Point(5, 1);
490 	this.multiple_term_icon = new google.maps.Icon( this.base_color_icon );
491 	this.multiple_term_icon.image = opts.url_path + '/images/mm_20_mixed.png';
492 
493 	// Falsify options to make tests simpler
494 	this.forEach( opts, function( key, value ) {
495 		if ( 'false' === value || 'FALSE' === value ) {
496 			opts[key] = false;
497 		}
498 	} );
499 
500 	// See if we have access to a parent frame
501 	this.have_parent_access = false;
502 	try {
503 		if ( typeof parent === 'object' ) {
504 			// Try access, throws an exception if prohibited
505 			parent.document.getElementById( 'bogus-test' );
506 			// Access worked
507 			this.have_parent_access = true;
508 		}
509 	} catch ( parent_exception ) { }
510 
511 	// For now, siteurl is the home url
512 	opts.home_url = opts.siteurl;
513 
514 	map_types = {
515 		'G_NORMAL_MAP' : google.maps.NORMAL_MAP,
516 		'G_SATELLITE_MAP' : google.maps.SATELLITE_MAP,
517 		'G_HYBRID_MAP' : google.maps.HYBRID_MAP,
518 		'G_PHYSICAL_MAP' : google.maps.PHYSICAL_MAP,
519 		'G_SATELLITE_3D_MAP' : google.maps.SATELLITE_3D_MAP
520 	};
521 
522 	if (typeof opts.map_type === 'string') {
523 		if ( map_types[opts.map_type] ) {
524 			opts.map_type = map_types[opts.map_type] ;
525 		} else {
526 			type_num = parseInt(opts.map_type, 10);
527 			if (isNaN(type_num)) {
528 				opts.map_type = google.maps.NORMAL_MAP;
529 			} else {
530 				opts.map_type = this.map.getMapTypes()[type_num];
531 			}
532 		}
533 	} else if (typeof opts.map_type === 'undefined') {
534 		opts.map_type = google.maps.NORMAL_MAP;
535 	}
536 	map_opts = {
537 		mapTypes : [ opts.map_type ],
538 		googleBarOptions : { 
539 			adsOptions : {client : opts.adsense_code || 'pub-5088093001880917'}
540 		}
541 	};
542 	if ( opts.background_color ) {
543 		map_opts.backgroundColor = opts.background_color;
544 	}
545 	this.doAction( 'mapOptions', opts, map_opts );
546 	this.map = new google.maps.Map2( this.container, map_opts );
547 	this.map.setCenter(new google.maps.LatLng(0,0), 0);
548 
549 	this.doAction( 'newMap', opts, this.map );
550 
551 	// Create the loading spinner icon and show it
552 	this.spinner_div = document.createElement( 'div' );
553 	this.spinner_div.innerHTML = '<div id="gm-loading-icon" style="-moz-user-select: none; z-index: 100; position: absolute; left: ' +
554 		( this.map.getSize().width / 2 ) + 'px; top: ' + ( this.map.getSize().height / 2 ) + 'px;">' +
555 		'<img style="border: 0px none ; margin: 0px; padding: 0px; width: 16px; height: 16px; -moz-user-select: none;" src="' +
556 		opts.url_path + '/images/busy_icon.gif"/></a></div>';
557 	this.showLoadingIcon();
558 	google.maps.Event.bind( this.map, 'tilesloaded', this, this.hideLoadingIcon );
559 
560 	if (!opts.object_name) {
561 		opts.object_name = 'post';
562 	}
563 	this.opts = opts;
564 	filter.url = opts.siteurl +
565 		( opts.siteurl.indexOf( '?' ) > 0 ? '&' : '?' ) +
566 		'geo_mashup_content=geo-query&map_name=' + encodeURIComponent( opts.name );
567 	if ( opts.lang && filter.url.indexOf( 'lang=' ) === -1 ) {
568 		filter.url += '&lang=' + encodeURIComponent( opts.lang );
569 	}
570 	this.doAction( 'geoQueryUrl', this.opts, filter );
571 	this.geo_query_url = filter.url;
572 
573 	google.maps.Event.bind(this.map, "zoomend", this, this.adjustZoom);
574 	google.maps.Event.bind(this.map, "moveend", this, this.adjustViewport);
575 
576 	if (opts.cluster_max_zoom) {
577 		clusterer_opts = {
578 			'iconOptions' : {},
579 			'fitMapMaxZoom' : opts.cluster_max_zoom,
580 			'clusterMarkerTitle' : '%count',
581 			'intersectPadding' : 3
582 		};
583 		/**
584 		 * <a href="http://googlemapsapi.martinpearman.co.uk/readarticle.php?article_id=4">ClusterMarker</a>
585 		 * options are being set on a Google v2 map.
586 		 *
587 		 * @see markerClustererOptions for Google v3.
588 		 * @name GeoMashup#clusterOptions
589 		 * @event
590 		 * @param {GeoMashupOptions} properties Geo Mashup configuration data
591 		 * @param {Object} clusterer_opts Modifiable clusterer options for
592 		 *   <a href="http://googlemapsapi.martinpearman.co.uk/readarticle.php?article_id=4">ClusterMarker</a>.
593 		 */
594 		this.doAction( 'clusterOptions', this.opts, clusterer_opts );
595 		this.clusterer = new ClusterMarker( this.map, clusterer_opts );
596 	}
597 
598 	if ( opts.zoom !== 'auto' && typeof opts.zoom === 'string' ) {
599 		initial_zoom = parseInt(opts.zoom, 10);
600 	} else {
601 		initial_zoom = opts.zoom;
602 	}
603 
604 	if (opts.load_kml) {
605 		this.kml = new google.maps.GeoXml(opts.load_kml);
606 		this.map.addOverlay(this.kml);
607 		if ( initial_zoom === 'auto' ) {
608 			this.kml.gotoDefaultViewport( this.map );
609 		}
610 	}
611 
612 	if ( this.term_manager ) {
613 		this.term_manager.load();
614 	}
615 
616 	if ( initial_zoom !== 'auto' ) {
617 		if (opts.center_lat && opts.center_lng) {
618 			// Use the center from options
619 			this.map.setCenter(new google.maps.LatLng(opts.center_lat, opts.center_lng), initial_zoom, opts.map_type);
620 		} else if (this.kml) {
621 			google.maps.Event.addListener( this.kml, 'load', function() {
622 				GeoMashup.map.setCenter( GeoMashup.kml.getDefaultCenter(), initial_zoom, opts.map_type );
623 			} );
624 		} else if (opts.object_data && opts.object_data.objects[0]) {
625 			center_latlng = new google.maps.LatLng(opts.object_data.objects[0].lat, opts.object_data.objects[0].lng);
626 			this.map.setCenter(center_latlng, initial_zoom, opts.map_type);
627 		} else {
628 			// Center on the most recent located object
629 			request = google.maps.XmlHttp.create();
630 			url = this.geo_query_url + '&limit=1';
631 			if (opts.map_cat) {
632 				url += '&cat='+opts.map_cat;
633 			}
634 			request.open("GET", url, false);
635 			request.send(null);
636 			objects = window['eval']( '(' + request.responseText + ')' );
637 			if (objects.length>0) {
638 				point = new google.maps.LatLng(objects[0].lat,objects[0].lng);
639 				this.map.setCenter(point,initial_zoom,opts.map_type);
640 			} else {
641 				this.map.setCenter(new google.maps.LatLng(0,0),initial_zoom,opts.map_type);
642 			}
643 		}
644 	}
645 
646 	this.location_bounds = new google.maps.LatLngBounds();
647 
648 	if (opts.map_content === 'single')
649 	{
650 		if (opts.object_data && opts.object_data.objects.length && !this.kml)
651 		{
652 			marker_opts = {};
653 			if (typeof customGeoMashupSinglePostIcon === 'function') {
654 				marker_opts.icon = customGeoMashupSinglePostIcon(this.opts);
655 			}
656 			if ( !marker_opts.icon ) {
657 				marker_opts.icon = G_DEFAULT_ICON;
658 			}
659 			this.doAction( 'singleMarkerOptions', this.opts, marker_opts );
660 			single_marker = new google.maps.Marker(
661 				new google.maps.LatLng( opts.object_data.objects[0].lat, opts.object_data.objects[0].lng ), marker_opts );
662 			this.map.addOverlay( single_marker );
663 			this.doAction( 'singleMarker', this.opts, single_marker );
664 		}
665 	} else if (opts.object_data) {
666 		this.addObjects(opts.object_data.objects,true);
667 	} else {
668 		// Request objects near visible range first
669 		this.requestObjects(true);
670 
671 		// Request all objects
672 		this.requestObjects(false);
673 	}
674 
675 	if ('GSmallZoomControl' === opts.map_control) {
676 		this.map.addControl(new google.maps.SmallZoomControl());
677 	} else if ('GSmallZoomControl3D' === opts.map_control) {
678 		this.map.addControl(new google.maps.SmallZoomControl3D());
679 	} else if ('GSmallMapControl' === opts.map_control) {
680 		this.map.addControl(new google.maps.SmallMapControl());
681 	} else if ('GLargeMapControl' === opts.map_control) {
682 		this.map.addControl(new google.maps.LargeMapControl());
683 	} else if ('GLargeMapControl3D' === opts.map_control) {
684 		this.map.addControl(new google.maps.LargeMapControl3D());
685 	}
686 
687 	if (opts.add_map_type_control ) {
688 		if ( typeof opts.add_map_type_control === 'string' ) {
689 			opts.add_map_type_control = opts.add_map_type_control.split(/\s*,\s*/);
690 			if ( typeof map_types[opts.add_map_type_control[0]] === 'undefined' ) {
691 				// Convert the old boolean value to a default array
692 				opts.add_map_type_control = [ 'G_NORMAL_MAP', 'G_SATELLITE_MAP', 'G_PHYSICAL_MAP' ];
693 			}
694 		}
695 		for ( i = 0; i < opts.add_map_type_control.length; i += 1 ) {
696 			this.map.addMapType( map_types[opts.add_map_type_control[i]] );
697 		}
698 		this.map.addControl(new google.maps.MapTypeControl());
699 	}
700 
701 	if (opts.add_overview_control) {
702 		this.overview_control = new google.maps.OverviewMapControl();
703 		this.overview_control.setMapType( opts.map_type );
704 		this.doAction( 'overviewControl', this.opts, this.overview_control );
705 		this.map.addControl( this.overview_control );
706 		ov = document.getElementById('gm-overview');
707 		if (ov) {
708 			ov.style.position = 'absolute';
709 			this.container.appendChild(ov);
710 		}
711 	}
712 
713 	if ( opts.add_google_bar ) {
714 		this.map.enableGoogleBar();
715 	}
716 
717 	if ( opts.enable_scroll_wheel_zoom ) {
718 		this.map.enableScrollWheelZoom();
719 	}
720 
721 	google.maps.Event.addListener( this.map, 'click', function( overlay ) {
722 		if ( GeoMashup.selected_marker && ( ! overlay ) ) {
723 			GeoMashup.deselectMarker();
724 		}
725 	} );
726 
727 	if (typeof customizeGeoMashupMap === 'function') {
728 		customizeGeoMashupMap(this.opts, this.map);
729 	}
730 	if (typeof customizeGeoMashup === 'function') {
731 		customizeGeoMashup(this);
732 	}
733 	this.doAction( 'loadedMap', this.opts, this.map );
734 
735 };
736