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