Source: maps/limbs/LimbMarkerFactory.js


/**
 * @class
 * Manages {lucid.maps.limbs.Limb}s for a set of markers.
 * This class will create the google.maps.Marker instance on the map as well as the associated LIMB.
 * An info-box can optionally be associated with the factory, which displays information about the marker.
 *
 * @constructor
 * @param {lucid.maps.limbs.LimbMarkerFactoryOptions} options  The factory options.
 */
lucid.maps.limbs.LimbMarkerFactory = function( options )
{
	var markers;
	var limbs;
	
	
	// This initialise function is called at the end of the constructor.
	function init()
	{
		var defaultLimbOptions = (typeof options.limbOptions !== "undefined") ? options.limbOptions : {};
		
		markers = [];
		limbs = new lucid.maps.limbs.LimbFactory( options.map, defaultLimbOptions );
	}
	
	
	/**
	 * Remove all markers and LIMBs created by this manager.
	 */
	this.removeAll = function()
	{
		for (var i=0; i<markers.length; i++)
		{
			if (typeof options.infoBox !== "undefined")
				options.infoBox.remove( markers[i] );
			
			markers[i].setMap( null );
			markers[i] = null;
		}
		markers = [];
		
		limbs.destroyAll();
	};
	
	/**
	 * Add a new marker and a LIMB to the map.
	 * The map instance and label styling are set in the options passed into the manager's constructor.
	 * 
	 * @param {google.maps.LatLng} position  The location to be marked.
	 * @param {string} title  The name/title of the location. Used as the title of the marker created on the map.
	 * @return {lucid.maps.limbs.Limb}  The limb that has been created.
	 */
	this.add = function( position, title )
	{
		var customMarkerOptions = { position: position,
		                            title: title };
		
		var customLimbOptions = {};
		
		return this.addCustomised( customMarkerOptions, customLimbOptions );
	};
	
	/**
	 * Add a new marker and a LIMB to the map.
	 * NB: The map and label styling are set in the options passed into the manager's constructor.
	 * 
	 * @param {google.maps.MarkerOptions} customisedMarkerOptions  Selected settings of the marker to be overriden from the options passed into the constructor.
	 * @param {lucid.maps.limbs.LimbOptions} customisedLimbOptions  Selected settings of the LIMB to be overriden from the options passed into the constructor.
	 * @return {lucid.maps.limbs.Limb}  The limb that has been created.
	 */
	this.addCustomised = function( customisedMarkerOptions, customisedLimbOptions )
	{
		// Create the marker, taking into account the default marker options set into this instance
		// and the custom marker options in the function call.
		var markerOptions = {};
		lucid.maps.limbs.applyMarkerOptions( markerOptions, options.markerOptions );
		lucid.maps.limbs.applyMarkerOptions( markerOptions, customisedMarkerOptions );
		markerOptions.map = options.map;
		
		var marker = new google.maps.Marker( markerOptions );
		
		if (typeof options.infoBox !== "undefined")
		{
			options.infoBox.add( marker );
		}
		
		if (typeof markerOptions.clickHandlerFactory === "function")
		{
			google.maps.event.addListener( marker, "click", markerOptions.clickHandlerFactory( marker ) );
		}
		
		markers[markers.length] = marker;
		
		
		customisedLimbOptions.target = marker;
		
		return limbs.create( customisedLimbOptions );
	};
	
	/**
	 * Zoom and pan the map so that all markers are in view.
	 * You can optionally specify additional locations to be included in the map view by setting LatLng in the options parameter.
	 *
	 * @param {object} zoomOptions
	 * @param {google.maps.LatLng[]} [zoomOptions.additionalLocations]  Locations, in addition to the markers, which should be kept in view when the map view is changed.
	 */
	this.zoomToAll = function( zoomOptions )
	{
		var locations = [];
		
		for (var i=0; i<markers.length; i++)
		{
			locations[locations.length] = markers[i].getPosition();
		}
		
		if (zoomOptions.additionalLocations)
		{
			for (var i=0; i<zoomOptions.additionalLocations.length; i++)
			{
				locations[locations.length] = zoomOptions.additionalLocations[i];
			}
		}
		
		if (locations.length > 0)
		{
			var bounds = new google.maps.LatLngBounds( locations[0], locations[0] );
			for (var i=0; i<locations.length; i++)
			{
				bounds = bounds.extend( locations[i] );
			}
			
			options.map.fitBounds( bounds );
		}
	};
	
	/**
	 * Temporarily hide the labels.
	 * This does not remove the LIMBs and markers, it just takes them off display.
	 * Call this if you hide the map element.
	 */
	this.hide = function()
	{
		limbs.hide();
		// NB: The markers are part of the map and should be hidden by hiding the map element.
	};
	
	/**
	 * Re-display the labels after being hidden with a call to 'hide'.
	 */
	this.show = function()
	{
		limbs.show();
		// NB: The markers are part of the map and should be shown by showing the map element.
	};
	
	/**
	 * Destroy this instance and any associated resources.
	 * This method should be called when the instance is no longer required.
	 */
	this.destroy = function()
	{
		this.removeAll();
	};
	
	/**
	 * @return {number}  The total number of places being labelled (includes any labels currently being hidden).
	 */
	this.getNumberOfMarkers = function()
	{
		return markers.length;
	};
	
	
	init();
};


/**
 * @type {object}
 * @property {google.maps.Map} map  The map the markers and LIMBs are to be associated with.
 * @property {google.maps.MarkerOptions} markerOptions  The settings for the marker shown on the map for these labels.
 *                                                      The LimbMarkerFactory will ignore the position property. This will be set when the places are added with a call to addPlaces.
 * @property {lucid.maps.limbs.LimbOptions} [limbOptions] The settings for the LIMB shown on the map for these labels.
 *                                                        The LimbMarkerFactory will ignore the marker property. This will be set to the place's marker.
 *                                                        The LimbMarkerFactory will ignore the independent property. This will always be false.
 *                                                        If undefined the LIMB will take its styling from the marker.
 * @property {lucid.maps.limbs.InfoBox} [infoBox]  The class that displays an info box when the user clicks on the label.
 *                                                 Leave undefined if no info box should appear.
 */
lucid.maps.limbs.LimbMarkerFactoryOptions = {};

/**
 * Copy google.maps.MarkerOptions from one instance to another.
 * Only the settings defined in the 'optionsToApply' object will be copied onto the 'options' object.
 * 
 * @param {google.maps.MarkerOptions} options  The target instance.
 * @param {google.maps.MarkerOptions} optionsToApply  Options that take precedence and should be copied into the target object.
 */
lucid.maps.limbs.applyMarkerOptions = function( options, optionsToApply )
{
	if (typeof optionsToApply.anchorPoint !== "undefined")
		options.anchorPoint = optionsToApply.anchorPoint;
	
	if (typeof optionsToApply.animation !== "undefined")
		options.animation = optionsToApply.animation;
	
	if (typeof optionsToApply.clickable !== "undefined")
		options.clickable = optionsToApply.clickable;
	
	if (typeof optionsToApply.crossOnDrag !== "undefined")
		options.crossOnDrag = optionsToApply.crossOnDrag;
	
	if (typeof optionsToApply.cursor !== "undefined")
		options.cursor = optionsToApply.cursor;
	
	if (typeof optionsToApply.draggable !== "undefined")
		options.draggable = optionsToApply.draggable;
	
	if (typeof optionsToApply.flat !== "undefined")
		options.flat = optionsToApply.flat;
	
	if (typeof optionsToApply.icon !== "undefined")
		options.icon = optionsToApply.icon;
	
	if (typeof optionsToApply.map !== "undefined")
		options.map = optionsToApply.map;
	
	if (typeof optionsToApply.optimized !== "undefined")
		options.optimized = optionsToApply.optimized;
	
	if (typeof optionsToApply.position !== "undefined")
		options.position = optionsToApply.position;
	
	if (typeof optionsToApply.raiseOnDrag !== "undefined")
		options.raiseOnDrag = optionsToApply.raiseOnDrag;
	
	if (typeof optionsToApply.shadow !== "undefined")
		options.shadow = optionsToApply.shadow;
	
	if (typeof optionsToApply.shape !== "undefined")
		options.shape = optionsToApply.shape;
	
	if (typeof optionsToApply.title !== "undefined")
		options.title = optionsToApply.title;
	
	if (typeof optionsToApply.visible !== "undefined")
		options.visible = optionsToApply.visible;
	
	if (typeof optionsToApply.zIndex !== "undefined")
		options.zIndex = optionsToApply.zIndex;
	
	if (typeof optionsToApply.clickHandlerFactory !== "undefined")
		options.clickHandlerFactory = optionsToApply.clickHandlerFactory;
};

/* TODO Move these 'apply' functions into a separate JS file. */

/**
 * Copy google.maps.DirectionsRequest from one instance to another.
 * Only the settings defined in the 'optionsToApply' object will be copied onto the 'options' object.
 * 
 * @param {google.maps.DirectionsRequest} options  The target instance.
 * @param {google.maps.DirectionsRequest} optionsToApply  Options that take precedence and should be copied into the target object.
 */
lucid.maps.limbs.applyDirectionsOptions = function( options, optionsToApply )
{
	if (typeof optionsToApply.avoidHighways !== "undefined")
		options.avoidHighways = optionsToApply.avoidHighways;

	if (typeof optionsToApply.avoidTolls !== "undefined")
		options.avoidTolls = optionsToApply.avoidTolls;

	if (typeof optionsToApply.destination !== "undefined")
		options.destination = optionsToApply.destination;

	if (typeof optionsToApply.durationInTraffic !== "undefined")
		options.durationInTraffic = optionsToApply.durationInTraffic;

	if (typeof optionsToApply.optimizeWaypoints !== "undefined")
		options.optimizeWaypoints = optionsToApply.optimizeWaypoints;

	if (typeof optionsToApply.origin !== "undefined")
		options.origin = optionsToApply.origin;

	if (typeof optionsToApply.provideRouteAlternatives !== "undefined")
		options.provideRouteAlternatives = optionsToApply.provideRouteAlternatives;

	if (typeof optionsToApply.region !== "undefined")
		options.region = optionsToApply.region;

	if (typeof optionsToApply.transitOptions !== "undefined")
		options.transitOptions = optionsToApply.transitOptions;

	if (typeof optionsToApply.travelMode !== "undefined")
		options.travelMode = optionsToApply.travelMode;

	if (typeof optionsToApply.unitSystem !== "undefined")
		options.unitSystem = optionsToApply.unitSystem;

	if (typeof optionsToApply.waypoints !== "undefined")
		options.waypoints = optionsToApply.waypoints;
};