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