1 /**
  2  * Mapstraction implementation for Geo Mashup maps.
  3  * @fileOverview
  4  */
  5 
  6 /**
  7  * @name AjaxRequestOptions
  8  * @class This type represents options used for an AJAX request.
  9  * It has no constructor, but is instantiated as an object literal.
 10  *
 11  * @property {String} url The AJAX request URL.
 12  */
 13 
 14 /**
 15  * @name ContentFilter
 16  * @class This type represents objects used to filter content.
 17  * It has no constructor, but is instantiated as an object literal.
 18  * 
 19  * @name ContentFilter#content
 20  * @property {String} content HTML content to filter.
 21  */
 22 
 23 /*global GeoMashup */
 24 /*global customizeGeoMashup, customizeGeoMashupMap, customGeoMashupColorIcon, customGeoMashupCategoryIcon */
 25 /*global customGeoMashupSinglePostIcon, customGeoMashupMultiplePostImage */
 26 /*global jQuery, mxn */
 27 /*jslint browser: true, white: true, sloppy: true */
 28 
 29 GeoMashup.loadFullPost = function( point ) {
 30 	var i, request, cache, objects, object_ids;
 31 
 32 	objects = this.getObjectsAtLocation( point );
 33 	object_ids = this.getOnObjectIDs( objects );
 34 	cache = this.locationCache( point, 'full-post-' + object_ids.join(',') );
 35 	if ( cache.html ) {
 36 
 37 		this.getShowPostElement().innerHTML = cache.html;
 38 
 39 	} else {
 40 
 41 		this.getShowPostElement().innerHTML = '<div align="center"><img src="' +
 42 			this.opts.url_path + '/images/busy_icon.gif" alt="Loading..." /></div>';
 43 		request = {
 44 			url: this.geo_query_url + '&object_name=' + this.opts.object_name +
 45 				'&object_ids=' + object_ids.join( ',' ) + '&template=full-post'
 46 		};
 47 		/**
 48 		 * Requesting full post content.
 49 		 * @name GeoMashup#fullPostRequest
 50 		 * @event
 51 		 * @param {Array} objects Objects included in the request
 52 		 * @param {AjaxRequestOptions} options
 53 		 */
 54 		this.doAction( 'fullPostRequest', objects, request );
 55 		jQuery.get( request.url, function( content ) {
 56 			var filter = {content: content};
 57 			/**
 58 			 * Loading full post content.
 59 			 * @name GeoMashup#fullPostLoad
 60 			 * @event
 61 			 * @param {Array} objects Objects included in the request
 62 			 * @param {ContentFilter} filter
 63 			 */
 64 			GeoMashup.doAction( 'fullPostLoad', objects, filter );
 65 			cache.html = filter.content;
 66 			jQuery( GeoMashup.getShowPostElement() ).html( filter.content );
 67 			/**
 68 			 * The full post display has changed.
 69 			 * @name GeoMashup#fullPostChanged
 70 			 * @event
 71 			 */
 72 			GeoMashup.doAction( 'fullPostChanged' );
 73 		} );
 74 	}
 75 };
 76 
 77 GeoMashup.createTermLine = function ( term_data ) {
 78 
 79 	// Polylines are close, but the openlayers implementation at least cannot hide or remove a polyline
 80 	var options = {color: term_data.color, width: 5, opacity: 0.5};
 81 
 82 	term_data.line = new mxn.Polyline(term_data.points);
 83 	/**
 84 	 * A term line was created.
 85 	 * @name GeoMashup#termLine
 86 	 * @event
 87 	 * @param {Polyline} line
 88 	 */
 89 	this.doAction( 'termLine', term_data.line );
 90 	/**
 91 	 * A category line was created.
 92 	 * @name GeoMashup#categoryLine
 93 	 * @event
 94 	 * @deprecated Use GeoMashup#termLine
 95 	 * @param {GeoMashupOptions} properties Geo Mashup configuration data
 96 	 * @param {Polyline} line
 97 	 */
 98 	this.doAction( 'categoryLine', this.opts, term_data.line );
 99 
100 
101 	/**
102 	 * A term line will be added with the given options.
103 	 * @name GeoMashup#termLineOptions
104 	 * @event
105 	 * @param {Object} options Modifiable <a href="http://mapstraction.github.com/mxn/build/latest/docs/symbols/mxn.Polyline.html#addData">Mapstraction</a>
106 	 *   or <a href="http://code.google.com/apis/maps/documentation/javascript/v2/reference.html#GPolylineOptions">Google</a> Polyline options
107 	 */
108 	this.doAction( 'termLineOptions', options );
109 	/**
110 	 * A term line will be added with the given options.
111 	 * @name GeoMashup#termLineOptions
112 	 * @event
113 	 * @deprecated Use GeoMashup#termLineOptions
114 	 * @param {GeoMashupOptions} properties Geo Mashup configuration data
115 	 * @param {Object} options Modifiable <a href="http://mapstraction.github.com/mxn/build/latest/docs/symbols/mxn.Polyline.html#addData">Mapstraction</a>
116 	 *   or <a href="http://code.google.com/apis/maps/documentation/javascript/v2/reference.html#GPolylineOptions">Google</a> Polyline options
117 	 */
118 	this.doAction( 'termLineOptions', this.opts, options );
119 
120 	this.map.addPolylineWithData( term_data.line, options );
121 
122 	if (this.map.getZoom() > term_data.max_line_zoom) {
123 		try {
124 			term_data.line.hide();
125 		} catch( e ) {
126 			// Not implemented?
127 			this.map.removePolyline( term_data.line );
128 		}
129 	}
130 };
131 
132 GeoMashup.openInfoWindow = function( marker ) {
133 	var request, cache, object_ids, i, object_element, point = marker.location;
134 
135 	if ( this.open_window_marker && !this.opts.multiple_info_windows ) {
136 		this.open_window_marker.closeBubble();
137 	}
138 	object_ids = this.getOnObjectIDs( this.getObjectsAtLocation( point ) );
139 	cache = this.locationCache( point, 'info-window-' + object_ids.join(',') );
140 	if ( cache.html ) {
141 		marker.setInfoBubble( cache.html );
142 		marker.openBubble();
143 	} else {
144 		marker.setInfoBubble( '<div align="center"><img src="' + this.opts.url_path + 
145 			'/images/busy_icon.gif" alt="Loading..." /></div>' );
146 		marker.openBubble();
147 		this.open_window_marker = marker;
148 		// Collect object ids
149 		// Do an AJAX query to get content for these objects
150 		request = {
151 			url: this.geo_query_url + '&object_name=' + this.opts.object_name +
152 				'&object_ids=' + object_ids.join( ',' ) 
153 		};
154 		/** 
155 		 * A marker's info window content is being requested.
156 		 * @name GeoMashup#markerInfoWindowRequest
157 		 * @event
158 		 * @param {Marker} marker
159 		 * @param {AjaxRequestOptions} request Modifiable property: url
160 		 */
161 		this.doAction( 'markerInfoWindowRequest', marker, request );
162 		jQuery.get( 
163 			request.url,
164 			function( content ) {
165 				var filter = {content: content};
166 				marker.closeBubble();
167 				/**
168 				 * A marker info window content is being loaded.
169 				 * @name GeoMashup#markerInfoWindowLoad
170 				 * @event
171 				 * @param {Marker} marker
172 				 * @param {ContentFilter} filter Modifiable property: content
173 				 */
174 				GeoMashup.doAction( 'markerInfoWindowLoad', marker, filter );
175 				cache.html = GeoMashup.parentizeLinksMarkup( filter.content );
176 				marker.setInfoBubble( cache.html );
177 				marker.openBubble();
178 			}
179 		); 
180 	}
181 };
182 
183 GeoMashup.closeInfoWindow = function( marker ) {
184 	marker.closeBubble();
185 };
186 
187 GeoMashup.addGlowMarker = function( marker ) {
188 	var point = marker.location, 
189 		glow_options = {
190 			clickable : true,
191 			icon : this.opts.url_path + '/images/mm_20_glow.png',
192 			iconSize : [ 22, 30 ],
193 			iconAnchor : [ 11, 27 ] 
194 		};
195 
196 	if ( this.glow_marker ) {
197 		this.removeGlowMarker();
198 	} 
199 	/** 
200 	 * A highlight "glow" marker is being created.
201 	 * @name GeoMashup#glowMarkerIcon
202 	 * @event
203 	 * @param {GeoMashupOptions} properties Geo Mashup configuration data
204 	 * @param {Object} glow_options Modifiable <a href="http://mapstraction.github.com/mxn/build/latest/docs/symbols/mxn.Marker.html#addData">Mapstraction</a> 
205 	 *   or <a href="http://code.google.com/apis/maps/documentation/javascript/v2/reference.html#GMarkerOptions">Google</a> marker options
206 	 */
207 	this.doAction( 'glowMarkerIcon', this.opts, glow_options );
208 	this.glow_marker = new mxn.Marker( point );
209 	this.glow_marker.addData( glow_options );
210 	this.glow_marker.click.addHandler( function() {
211 		GeoMashup.deselectMarker();
212 	} );
213 	this.map.addMarker( this.glow_marker );
214 };
215 
216 GeoMashup.removeGlowMarker = function() {
217 	if ( this.glow_marker ) {
218 		this.glow_marker.hide();
219 		this.map.removeMarker( this.glow_marker );
220 		this.glow_marker = null;
221 	}
222 };
223 
224 GeoMashup.hideAttachments = function() {
225 	var i, j, obj;
226 
227 	/* No removeOverlay (yet)
228 	for ( i = 0; i < this.open_attachments.length; i += 1 ) {
229 		this.map.removeOverlay( this.open_attachments[i] );
230 	} 
231 	this.open_attachments = [];
232 	*/
233 };
234 
235 GeoMashup.showMarkerAttachments = function( marker ) {
236 	var object_ids, uncached_ids = [];
237 
238 	this.hideAttachments(); // check support
239 	object_ids = this.getOnObjectIDs( this.getMarkerObjects( marker ) );
240 	jQuery.each( object_ids, function( i, id ) {
241 		var cached_attachments = GeoMashup.locationCache( marker.location, 'attachments-' + id );
242 		if ( cached_attachments.urls ) {
243 			jQuery.each( cached_attachments.urls, function( j, url ) {
244 				GeoMashup.open_attachments.push( url );
245 				GeoMashup.map.addOverlay( url );
246 			} );
247 		} else {
248 			uncached_ids.push( id );
249 		}
250 	} );
251 	// Request any uncached attachments
252 	jQuery.each( uncached_ids, function( i, id ) {
253 		var ajax_params = {action: 'geo_mashup_kml_attachments'};
254 		ajax_params.post_ids = id;
255 		jQuery.getJSON( GeoMashup.opts.ajaxurl + '?callback=?', ajax_params, function( data ) {
256 			var cached_attachments = GeoMashup.locationCache( marker.location, 'attachments-' + id );
257 			if ( !cached_attachments.urls ) {
258 				cached_attachments.urls = [];
259 			}
260 			jQuery.each( data, function( j, url ) {
261 				cached_attachments.urls.push( url );
262 				GeoMashup.open_attachments.push( url );
263 				GeoMashup.map.addOverlay( url );
264 			} );
265 		} );
266 	} );
267 };
268 
269 GeoMashup.addObjectIcon = function( obj ) {
270 
271 	// Back compat
272 	if ( typeof customGeoMashupCategoryIcon === 'function' && obj.terms && obj.terms.hasOwnProperty( 'category' ) ) {
273 		obj.icon = customGeoMashupCategoryIcon( GeoMashup.opts, obj.terms.category );
274 	} 
275 
276 	if ( !obj.icon ) {
277 
278 		jQuery.each( obj.terms, function( taxonomy, terms ) {
279 			var single_icon;
280 
281 			if ( terms.length > 1) {
282 
283 				obj.icon = GeoMashup.clone( GeoMashup.multiple_term_icon );
284 				return false;
285 
286 			} else if ( terms.length === 1 ) {
287 
288 				single_icon = GeoMashup.term_manager.getTermData( taxonomy, terms[0], 'icon' );
289 
290 				if ( obj.icon && obj.icon.image !== single_icon.image ) {
291 
292 					// We have two different icons in different taxonomies
293 					obj.icon = GeoMashup.clone( GeoMashup.multiple_term_icon );
294 					return false;
295 
296 				} else {
297 
298 					obj.icon = GeoMashup.clone( single_icon );
299 
300 				}
301 
302 			} 
303 			
304 		} );
305 
306 		if ( !obj.icon ) {
307 			obj.icon = GeoMashup.colorIcon( 'red' );
308 		}
309 
310 		/**
311 		 * An icon is being assigned to an object.
312 		 * @name GeoMashup#objectIcon
313 		 * @event
314 		 * @param {GeoMashupOptions} properties Geo Mashup configuration data
315 		 * @param {GeoMashupObject} object Object whose icon property was set.
316 		 */
317 		this.doAction( 'objectIcon', GeoMashup.opts, obj );
318 	}
319 };
320 
321 GeoMashup.createMarker = function(point,obj) {
322 	var marker, marker_opts;
323 
324 	if ( !obj.icon ) {
325 		this.addObjectIcon( obj );
326 	}
327 	marker_opts = {
328 		label: obj.title, 
329 		icon: obj.icon.image,
330 		iconSize: obj.icon.iconSize,
331 		iconShadow: obj.icon.iconShadow,
332 		iconAnchor: obj.icon.iconAnchor,
333 		iconShadowSize: obj.icon.shadowSize,
334 		visible: true
335 	};
336 	/**
337 	 * A marker is being created for an object. Use this to change marker 
338 	 * options, but if you just want to assign an icon to an object, use the 
339 	 * objectIcon action.
340 	 * 
341 	 * @name GeoMashup#objectMarkerOptions
342 	 * @event
343 	 * @param {GeoMashupOptions} properties Geo Mashup configuration data
344 	 * @param {Object} glow_options Modifiable <a href="http://mapstraction.github.com/mxn/build/latest/docs/symbols/mxn.Marker.html#addData">Mapstraction</a> 
345 	 *   or <a href="http://code.google.com/apis/maps/documentation/javascript/v2/reference.html#GMarkerOptions">Google</a> marker options
346 	 * @param {GeoMashupObject} object
347 	 */
348 	this.doAction( 'objectMarkerOptions', this.opts, marker_opts, obj );
349 	marker = new mxn.Marker( point );
350 	marker.addData( marker_opts );
351 
352 	marker.click.addHandler( function() {
353 		// Toggle marker selection
354 		if ( marker === GeoMashup.selected_marker ) {
355 			GeoMashup.deselectMarker();
356 		} else {
357 			GeoMashup.selectMarker( marker );
358 		}
359 	} ); 
360 
361 	/**
362 	 * A marker was created.
363 	 * @name GeoMashup#marker
364 	 * @event
365 	 * @param {GeoMashupOptions} properties Geo Mashup configuration data
366 	 * @param {Marker} marker
367 	 */
368 	this.doAction( 'marker', this.opts, marker );
369 
370 	return marker;
371 };
372 
373 GeoMashup.clickObjectMarker = function( object_id, try_count ) {
374 	var obj = this.objects[object_id];
375 
376 	if ( !GeoMashup.isObjectOn( obj ) ) {
377 		return false;
378 	}
379 
380 	if (typeof try_count === 'undefined') {
381 		try_count = 1;
382 	}
383 	if ( obj && obj.marker && try_count < 4 ) {
384 		// openlayers/mxn seems to have trouble displaying an infobubble right away
385 		if ( try_count < 2 ) {
386 			try_count += 1;
387 			setTimeout(function () {GeoMashup.clickObjectMarker(object_id, try_count);}, 1000);
388 		} else {
389 			obj.marker.click.fire();
390 		}
391 	}
392 };
393 
394 GeoMashup.colorIcon = function( color_name ) {
395 	var icon = this.clone( this.base_color_icon );
396 	icon.image = this.opts.url_path + '/images/mm_20_' + color_name + '.png';
397 	return icon;
398 };
399 
400 GeoMashup.getMarkerLatLng = function( marker ) {
401 	return marker.location;
402 };
403 
404 GeoMashup.hideMarker = function( marker ) {
405 	if ( marker === this.selected_marker ) {
406 		this.deselectMarker();
407 	}
408 	marker.hide();
409 };
410 
411 GeoMashup.showMarker = function( marker ) {
412 	marker.show();
413 };
414 
415 GeoMashup.hideLine = function( line ) {
416 	try {
417 		line.hide();
418 	} catch( e ) {
419 		this.map.removePolyline( line );
420 	}
421 };
422 
423 GeoMashup.showLine = function( line ) {
424 	try {
425 		line.show();
426 	} catch( e ) {
427 		this.map.addPolyline( line );
428 	}
429 };
430 
431 GeoMashup.newLatLng = function( lat, lng ) {
432 	return new mxn.LatLonPoint( lat, lng );
433 };
434 
435 GeoMashup.extendLocationBounds = function( latlng ) {
436 	if ( this.location_bounds ) {
437 		this.location_bounds.extend( latlng );
438 	} else {
439 		this.location_bounds = new mxn.BoundingBox( latlng, latlng );
440 	}
441 };
442 
443 GeoMashup.addMarkers = function( markers ) {
444 	this.forEach( markers, function( i, marker ) {
445 		this.map.addMarker( marker );
446 	} );
447 };
448 
449 GeoMashup.makeMarkerMultiple = function( marker ) {
450 	var plus_image, original_image;
451 	if (typeof customGeoMashupMultiplePostImage === 'function') {
452 		plus_image = customGeoMashupMultiplePostImage(this.opts, marker);
453 	}
454 	if (!plus_image) {
455 		plus_image = this.opts.url_path + '/images/mm_20_plus.png';
456 	}
457 	original_image = marker.iconUrl;
458 	marker.setIcon( plus_image );
459 	/** 
460 	 * A marker representing multiple objects was created.
461 	 * @name GeoMashup#multiObjectMarker
462 	 * @event
463 	 * @param {GeoMashupOptions} properties Geo Mashup configuration data
464 	 * @param {Marker} marker
465 	 */
466 	this.doAction( 'multiObjectMarker', this.opts, marker );
467 	/** 
468 	 * A marker representing multiple objects was created with this icon.
469 	 * @name GeoMashup#multiObjectIcon
470 	 * @event
471 	 * @param {GeoMashupOptions} properties Geo Mashup configuration data
472 	 * @param {String} plus_image Icon URL
473 	 */
474 	this.doAction( 'multiObjectIcon', this.opts, plus_image );
475 	if ( marker.onmap && marker.iconUrl !== original_image ) {
476 		this.map.removeMarker( marker );
477 		this.map.addMarker( marker );
478 	}
479 };
480 
481 GeoMashup.setMarkerImage = function( marker, image_url ) {
482 	if ( marker.iconUrl !== image_url ) {
483 		marker.setIcon( image_url );
484 		if ( marker.onmap ) {
485 			this.map.removeMarker( marker );
486 			this.map.addMarker( marker );
487 		}
488 	}
489 };
490 
491 GeoMashup.autoZoom = function() {
492     var map = this.map;
493     var limitZoom = function() {
494         var max_zoom = parseInt( GeoMashup.opts.auto_zoom_max, 10 );
495 
496         if ( map.getZoom() > max_zoom ) {
497             map.setZoom( max_zoom );
498         }
499         map.changeZoom.removeHandler( limitZoom );
500     };
501     if ( typeof this.opts.auto_zoom_max !== 'undefined' ) {
502         this.map.changeZoom.addHandler( limitZoom );
503     }
504     this.map.autoCenterAndZoom();
505 };
506 
507 GeoMashup.isMarkerVisible = function( marker ) {
508 	var map_bounds;
509 	try {
510 		map_bounds = this.map.getBounds();
511 	} catch( e ) {
512 		// No bounds available yet, no markers are visible
513 		return false;
514 	}
515 	return ( marker.getAttribute( 'visible' ) && map_bounds && map_bounds.contains( marker.location ) ); 
516 };
517 
518 GeoMashup.centerMarker = function( marker, zoom ) {
519 	if ( typeof zoom === 'number' ) {
520 		this.map.setCenterAndZoom( marker.location, zoom );
521 	} else {
522 		this.map.setCenter( marker.location, {}, true );
523 	}
524 };
525 
526 GeoMashup.createMap = function(container, opts) {
527 	var i, type_num, center_latlng, map_opts, map_types, request, url, objects, point, marker_opts, 
528 		clusterer_opts, single_marker, ov, credit_div, initial_zoom = 1, controls = {}, filter = {};
529 
530 	this.container = container;
531 	this.base_color_icon = {};
532 	this.base_color_icon.image = opts.url_path + '/images/mm_20_black.png';
533 	this.base_color_icon.iconShadow = opts.url_path + '/images/mm_20_shadow.png';
534 	this.base_color_icon.iconSize = [12, 20];
535 	this.base_color_icon.shadowSize =  [22, 20];
536 	this.base_color_icon.iconAnchor = [6, 20];
537 	this.base_color_icon.infoWindowAnchor = [5, 1];
538 	this.multiple_term_icon = this.clone( this.base_color_icon );
539 	this.multiple_term_icon.image = opts.url_path + '/images/mm_20_mixed.png';
540 
541 	// Falsify options to make tests simpler
542 	this.forEach( opts, function( key, value ) {
543 		if ( 'false' === value || 'FALSE' === value ) {
544 			opts[key] = false;
545 		}
546 	} );
547 
548 	// See if we have access to a parent frame
549 	this.have_parent_access = false;
550 	try {
551 		if ( typeof parent === 'object' ) {
552 			// Try access, throws an exception if prohibited
553 			parent.document.getElementById( 'bogus-test' );
554 			// Access worked
555 			this.have_parent_access = true;
556 		}
557 	} catch ( parent_exception ) { }
558 
559 	// For now, siteurl is the home url
560 	opts.home_url = opts.siteurl;
561 
562 	map_types = {
563 		'G_NORMAL_MAP' : mxn.Mapstraction.ROAD,
564 		'G_SATELLITE_MAP' : mxn.Mapstraction.SATELLITE,
565 		'G_HYBRID_MAP' : mxn.Mapstraction.HYBRID,
566 		'G_PHYSICAL_MAP' : mxn.Mapstraction.PHYSICAL
567 	};
568 
569 	if (typeof opts.map_type === 'string') {
570 		if ( map_types[opts.map_type] ) {
571 			opts.map_type = map_types[opts.map_type] ;
572 		} else {
573 			type_num = parseInt(opts.map_type, 10);
574 			if ( isNaN(type_num) || type_num > 2 ) {
575 				opts.map_type = map_types.G_NORMAL_MAP;
576 			} else {
577 				opts.map_type = type_num;
578 			}
579 		}
580 	} else if (typeof opts.map_type === 'undefined') {
581 		opts.map_type = map_types.G_NORMAL_MAP;
582 	}
583 	this.map = new mxn.Mapstraction( this.container, opts.map_api );
584 	map_opts = {enableDragging: true};
585 	map_opts.enableScrollWheelZoom = ( opts.enable_scroll_wheel_zoom ? true : false );
586 	
587 	if ( typeof this.map.enableGeoMashupExtras === 'function' ) {
588 		this.map.enableGeoMashupExtras();
589 	}
590 	/**
591 	 * The map options are being set.
592 	 * @name GeoMashup#mapOptions
593 	 * @event
594 	 * @param {GeoMashupOptions} properties Geo Mashup configuration data
595 	 * @param {Object} map_opts Modifiable <a href="http://mapstraction.github.com/mxn/build/latest/docs/symbols/mxn.Mapstraction.html#options">Mapstraction</a> 
596 	 *   or <a href="http://code.google.com/apis/maps/documentation/javascript/v2/reference.html#GMapOptions">Google</a> map options
597 	 */
598 	this.doAction( 'mapOptions', opts, map_opts );
599 	this.map.setOptions( map_opts );
600 	this.map.setCenterAndZoom(new mxn.LatLonPoint(0,0), 0);
601 
602 	/**
603 	 * The map was created.
604 	 * @name GeoMashup#newMap
605 	 * @event
606 	 * @param {GeoMashupOptions} properties Geo Mashup configuration data
607 	 * @param {Map} map
608 	 */
609 	this.doAction( 'newMap', opts, this.map );
610 
611 	// Create the loading spinner icon and show it
612 	this.spinner_div = document.createElement( 'div' );
613 	this.spinner_div.innerHTML = '<div id="gm-loading-icon" style="-moz-user-select: none; z-index: 100; position: absolute; left: ' +
614 		( jQuery(this.container).width() / 2 ) + 'px; top: ' + ( jQuery(this.container).height() / 2 ) + 'px;">' +
615 		'<img style="border: 0px none ; margin: 0px; padding: 0px; width: 16px; height: 16px; -moz-user-select: none;" src="' +
616 		opts.url_path + '/images/busy_icon.gif"/></a></div>';
617 	this.showLoadingIcon();
618 	this.map.load.addHandler( function() {GeoMashup.hideLoadingIcon();} );
619 
620 	if (!opts.object_name) {
621 		opts.object_name = 'post';
622 	}
623 	this.opts = opts;
624 	filter.url = opts.siteurl +
625 		( opts.siteurl.indexOf( '?' ) > 0 ? '&' : '?' ) +
626 		'geo_mashup_content=geo-query&map_name=' + encodeURIComponent( opts.name );
627 	if ( opts.lang && filter.url.indexOf( 'lang=' ) === -1 ) {
628 		filter.url += '&lang=' + encodeURIComponent( opts.lang );
629 	}
630 
631 	/**
632 	 * The base URL used for geo queries is being set.
633 	 * @name GeoMashup#geoQueryUrl
634 	 * @event
635 	 * @param {GeoMashupOptions} properties Geo Mashup configuration data
636 	 * @param {Object} filter Mofiable property: url
637 	 */
638 	this.doAction( 'geoQueryUrl', this.opts, filter );
639 	this.geo_query_url = filter.url;
640 
641 	// TODO: Try to deleselect markers with clicks? Need to make sure we don't get other object's clicks.
642 	this.map.changeZoom.addHandler( function( old_zoom, new_zoom ) {
643 		GeoMashup.adjustZoom( old_zoom, new_zoom );
644 		GeoMashup.adjustViewport();
645 	} );
646 	this.map.endPan.addHandler( function() {GeoMashup.adjustViewport();} );
647 
648 	// No clustering available
649 
650 	if ( opts.zoom !== 'auto' && typeof opts.zoom === 'string' ) {
651 		initial_zoom = parseInt(opts.zoom, 10);
652 	}else {
653 		initial_zoom = opts.zoom;
654 	}
655 
656 	if (opts.load_kml) {
657 		try {
658             // Some servers (Google) don't like HTML entities in URLs
659             opts.load_kml = jQuery( '<div/>').html( opts.load_kml ).text();
660 			if ( initial_zoom === 'auto' ) {
661 				this.map.addOverlay( opts.load_kml, true );
662 			} else {
663 				this.map.addOverlay( opts.load_kml );
664 			}
665 		} catch (e) {
666 			// Probably not implemented
667 		}
668 	}
669 
670 	if ( this.term_manager ) {
671 		this.term_manager.load();
672 	}
673 
674 	try {
675 		this.map.setMapType( opts.map_type );
676 	} catch ( map_type_ex) {
677 		// Probably not implemented
678 	}
679 	if ( initial_zoom !== 'auto' ) {
680 		if (opts.center_lat && opts.center_lng) {
681 			// Use the center from options
682 			this.map.setCenterAndZoom(new mxn.LatLonPoint( parseFloat( opts.center_lat ), parseFloat( opts.center_lng ) ), initial_zoom );
683 		} else if (opts.object_data && opts.object_data.objects[0]) {
684 			center_latlng = new mxn.LatLonPoint( parseFloat( opts.object_data.objects[0].lat ), parseFloat( opts.object_data.objects[0].lng ) );
685 			this.map.setCenterAndZoom( center_latlng, initial_zoom );
686 		} else {
687 			// Center on the most recent located object
688 			url = this.geo_query_url + '&limit=1';
689 			if (opts.map_cat) {
690 				url += '&map_cat='+opts.map_cat;
691 			}
692 			jQuery.getJSON( url, function( objects ) {
693 				if (objects.length>0) {
694 					center_latlng = new mxn.LatLonPoint( parseFloat( objects[0].lat ), parseFloat( objects[0].lng ) );
695 					this.map.setCenterAndZoom( center_latlng, initial_zoom );
696 				}
697 			} );
698 		}
699 	}
700 
701 	this.location_bounds = null;
702 
703 	if (opts.map_content === 'single')
704 	{
705 		if (opts.object_data && opts.object_data.objects.length && !opts.load_kml)
706 		{
707 			marker_opts = {visible: true};
708 			if (typeof customGeoMashupSinglePostIcon === 'function') {
709 				marker_opts = customGeoMashupSinglePostIcon(this.opts);
710 			}
711 			if ( !marker_opts.image ) {
712 				marker_opts = this.colorIcon( 'red' );
713 				marker_opts.icon = marker_opts.image;
714 			}
715 			/**
716 			 * A single map marker is being created with these options
717 			 * @name GeoMashup#singleMarkerOptions
718 			 * @event
719 			 * @param {GeoMashupOptions} properties Geo Mashup configuration data
720 			 * @param {Object} marker_opts Mofifiable Mapstraction or Google marker options
721 			 */
722 			this.doAction( 'singleMarkerOptions', this.opts, marker_opts );
723 			single_marker = new mxn.Marker(
724 				new mxn.LatLonPoint( parseFloat( opts.object_data.objects[0].lat ), parseFloat( opts.object_data.objects[0].lng ) )
725 			);
726 			this.map.addMarkerWithData( single_marker, marker_opts );
727 			/**
728 			 * A single map marker was added to the map.
729 			 * @name GeoMashup#singleMarker
730 			 * @event
731 			 * @param {GeoMashupOptions} properties Geo Mashup configuration data
732 			 * @param {Marker} single_marker
733 			 */
734 			this.doAction( 'singleMarker', this.opts, single_marker );
735 		}
736 	} else if (opts.object_data) {
737 		this.addObjects(opts.object_data.objects,true);
738 	} else {
739 		// Request objects near visible range first
740 		this.requestObjects(true);
741 
742 		// Request all objects
743 		this.requestObjects(false);
744 	}
745 
746 	if ('GSmallZoomControl' === opts.map_control || 'GSmallZoomControl3D' === opts.map_control) {
747 		controls.zoom = 'small';
748 	} else if ('GSmallMapControl' === opts.map_control) {
749 		controls.zoom = 'small';
750 		controls.pan = true;
751 	} else if ('GLargeMapControl' === opts.map_control || 'GLargeMapControl3D' === opts.map_control) {
752 		controls.zoom = 'large';
753 		controls.pan = true;
754 	}
755 
756 	if (opts.add_map_type_control ) {
757 		controls.map_type = true;
758 	}
759 
760 	if (opts.add_overview_control) {
761 		controls.overview = true;
762 	}
763 	this.map.addControls( controls );
764 
765 	if (opts.add_map_type_control && typeof this.map.setMapTypes === 'function' ) {
766 		if ( typeof opts.add_map_type_control === 'string' ) {
767 			opts.add_map_type_control = opts.add_map_type_control.split(/\s*,\s*/);
768 			if ( typeof map_types[opts.add_map_type_control[0]] === 'undefined' ) {
769 				// Convert the old boolean value to a default array
770 				opts.add_map_type_control = [ 'G_NORMAL_MAP', 'G_SATELLITE_MAP', 'G_PHYSICAL_MAP' ];
771 			}
772 		}
773 		// Convert to mapstraction types
774 		opts.mxn_map_type_control = [];
775 		for ( i = 0; i < opts.add_map_type_control.length; i += 1 ) {
776 			opts.mxn_map_type_control.push( map_types[ opts.add_map_type_control[i] ] );
777 		}
778 		this.map.setMapTypes( opts.mxn_map_type_control );
779 	}
780 
781 	this.map.load.addHandler( function() {GeoMashup.updateVisibleList();} );
782 	if (typeof customizeGeoMashupMap === 'function') {
783 		customizeGeoMashupMap(this.opts, this.map);
784 	}
785 	if (typeof customizeGeoMashup === 'function') {
786 		customizeGeoMashup(this);
787 	}
788 	/**
789 	 * The map has loaded.
790 	 * @name GeoMashup#loadedMap
791 	 * @event
792 	 * @param {GeoMashupOptions} properties Geo Mashup configuration data
793 	 * @param {Map} map
794 	 */
795 	this.doAction( 'loadedMap', this.opts, this.map );
796 
797 };
798