	// ***** Require External Dependencies *****
requireNamespace("Phoenix.Widgets", "Phoenix.Widgets.Map");


/*
	Phoenix.Widgets.Map()
	
*/

Phoenix.Widgets.Map = function(container, insertionmethod, mapname, config)
{
	this.nsIdentifier = "Phoenix.Widgets.Map";														// What sort of widget are we?

	// ***** Require External Dependencies *****
	requireNamespace("Phoenix.Util.Event", "Phoenix.Widgets.Map");
	requireNamespace("Phoenix.DOM", "Phoenix.Widgets.Map");
	
	// ***** Sanity-Check Passed Parameters *****
	
	if(!container) throw new Phoenix.Exception("Phoenix.Widgets.Map.Constructor: First parameter must be a valid HTML element reference.");	// Can't survive without an element to bed ourselves down in
	insertionmethod = (insertionmethod == null || insertionmethod > 3 || insertionmethod < 0) ? 3: insertionmethod;			// If insertionmethod not specified or invalid, assume "unrender fallback elements and insert ourselves *before* them in the div"
	mapname = mapname || Phoenix.DOM.generateUniqueId("PWMap");		// If no mapname specified, generate one guaranteed unique within the page, with the specified prefix for recognisability
	

	// ***** State Variables *****
	
	// Set up identifying properties of this widget
	this.globalWidgetIndex = Phoenix.Widgets.registerThisWidget(this.nsIdentifier, this);	// Grab a reference to the index of this particular instance of the control in the Phoenix.Widgets._InstantiatedControls array (in case we ever need to refer to ourself in the global scope)
	
	this.renderMethod = insertionmethod;	// How are we to render ourselves?  See Render() for explanation of values.
	this.rendered = false;								// Has this control been rendered yet?
	this.useIEAsynchContextHack = false;	// IE doesn't support passing parameters to asynchronous callback functions
	this.highlightStyle = config.highlightStyle || 'move-y';
	this.animationDelay = config.animationDelay || 10;							// time between animation frames (milliseconds)
	this.animationStep = config.animationStep || 1;								// how many pixels to move per animation step (1=smoothest/slowest)
	this.minHeight = config.minHeight || 0;										// Min height to move elements to when un-highlighted
	this.maxHeight = config.maxHeight || 5;										// Max height to move elements when highlighted
	this.UseSecLayer = config.UseSecLayer || 0;
	this.IsSecLayerActive = config.IsSecLayerActive || 0;
	

	// ** "Child HTML Element" references **
	// Root element of this control:
	this.rootElement = container;						// Reference to the root element that this widget inhabits
	this.rootElement.owningControl = this;

	// "Transparent Image Overlay" element (transparent image to attach actual imagemap to):
	this.image = document.createElement("img");	// Reference to the map tag which defines the imagemap
	this.image.src = config.transparentImageSrc || "/include/widgets/map/mapoverlay.gif";
	this.image.alt = this.image.title = "";
	this.image.useMap = "#"+mapname;
	this.image.className = "mapoverlay";
	this.image.owningControl = this;
	
	// "Imagemap" element (defines imagemap to apply to image overlay):
	this.imageMap = document.createElement("map");	// Reference to the map tag which defines the imagemap
	this.imageMap.id = this.imageMap.name = mapname;
	this.imageMap.owningControl = this;

	// "Map Layer" elements (this.mapLayersRoot points to containing div.  Children are div elements which will be stacked to form eventual composite map image):
	
	// Map layers
	this.mapLayersRoot = document.createElement("div");	// Reference to the div which contains the layer divs
	this.mapLayersRoot.className = "maplayersroot";
	this.mapLayersRoot.owningControl = this;
	this.mapLayersRoot.savedImagesList = new Array();	// Array of image URLs to apply to map layers once they're rendered (style object is apprently reinitialised when the element is added to the page)
	this.mapLayersRoot.movementDirectionList = new Array();
	this.mapLayersRoot.movementTimers = new Array();
	
	if(this.UseSecLayer)
	{
		// Map layers Sec
		this.mapLayersSecRoot = document.createElement("div");	// Reference to the div which contains the layer divs
		this.mapLayersSecRoot.className = "maplayersroot";
		this.mapLayersSecRoot.owningControl = this;
		this.mapLayersSecRoot.savedImagesList = new Array();	// Array of image URLs to apply to map layers once they're rendered (style object is apprently reinitialised when the element is added to the page)
		this.mapLayersSecRoot.movementDirectionList = new Array();
		this.mapLayersSecRoot.movementTimers = new Array();
	}

	// Map layers "active" images
	this.mapLayersActiveRoot = document.createElement("div");	// Reference to the div which contains the layer divs
	this.mapLayersActiveRoot.className = "maplayersactiveroot";
	this.mapLayersActiveRoot.owningControl = this;
	this.mapLayersActiveRoot.savedImagesList = new Array();	// Array of image URLs to apply to map layers once they're rendered (style object is apprently reinitialised when the element is added to the page)
	this.mapLayersActiveRoot.opacityDirectionList = new Array();
	this.mapLayersActiveRoot.opacityTimers = new Array();
	
	// Map labels (sit on top of the map layers and map "active" layers
	this.mapLabelsRoot = document.createElement("div");	// Reference to the div which contains the layer divs
	this.mapLabelsRoot.className = "maplabelsroot";
	this.mapLabelsRoot.owningControl = this;
	this.mapLabelsRoot.savedImagesList = new Array();	// Array of image URLs to apply to map layers once they're rendered (style object is apprently reinitialised when the element is added to the page)

	
	// Set up event-handler callbacks (these are events that the slider control exposes *to other scripts*.  Its constituent elements will have their own (private) event handlers below that call any function attached to these properties after they do their jobs).
	this.onclick = null;							// Fired when the mouse button is raised 
	this.onselectarea = null;							// Fired when mouseup after the thumb slider has changed position
	this.ondeselectarea = null;
	this.onmousedown = null;
	this.onmouseup = null;


	// ***** Do Initial "Pre-Rendering" Setup *****

	// Mark out this element's root container as a mapwidget (for styling purposes)
	Phoenix.DOM.addCSSClass(this.rootElement, "mapwidget");
	
	// If appropriate, hide or unrender any DHTML fallback elements in the div
	// We do this here so that it'll be done while the DOM is still loading, rather than having them flicker by waiting for the entire page to load (exposing the fallback elements the whole time) and then blanking them out after a short delay
	if(this.renderMethod == 2 || this.renderMethod == 3)	// If hide/undisplay all fallback elements
	{
		if(container.hasChildNodes())
		{
			var removemethod = (this.renderMethod == 2) ? "hidden": "unrendered";	// Remove (display:none) the control or just hide (visibility:hidden) it?
	
			// Hide all "fallback" HTML elements (anything inside the div this control has been passed as its container)
			//var children = container.getElementsByTagName("*");
			var children = Phoenix.DOM.getElementsByClassName("dhtmlfallback", container);
			for(var i=0; i<children.length; i++)	// remove/hide old HTML elements
				Phoenix.DOM.addCSSClass(children[i], removemethod);
		}
	}

	// Initialise areas and map layers from the passed-in config (if any)
	for(var i=0; i<config.regions.length; i++)
	{
		// Set up map overlay for this area
		var newlayerelement = document.createElement("div");
		newlayerelement.className = "maplayer";
		newlayerelement.owningControl = this;
		this.mapLayersRoot.appendChild(newlayerelement);
		
		if(this.UseSecLayer)
		{
			// Set up map overlay Sec for this area
			var newlayerSecelement = document.createElement("div");
			newlayerSecelement.className = "maplayer";
			newlayerSecelement.owningControl = this;
			this.mapLayersSecRoot.appendChild(newlayerSecelement);
		}
		
		var newactiveelement = document.createElement("div");
		newactiveelement.className = "maplayeractive";
		newactiveelement.owningControl = this;
		this.mapLayersActiveRoot.appendChild(newactiveelement);


		var newlabelelement = document.createElement("div");
		newlabelelement.className = "maplayerlabel";
		newlabelelement.owningControl = this;
		this.mapLabelsRoot.appendChild(newlabelelement);

		// Save the style info for attachment on Render()
		this.mapLayersRoot.savedImagesList.push(config.regions[i].mapoverlay);	// Style info gets wiped when element is added to page on Render(), so instead save it and attach the style info in the Render() function
		if(this.UseSecLayer)
		{
			this.mapLayersSecRoot.savedImagesList.push(config.regions[i].mapoverlaySec);	// Style info gets wiped when element is added to page on Render(), so instead save it and attach the style info in the Render() function
		}
		this.mapLayersActiveRoot.savedImagesList.push(config.regions[i].mapoverlay_active);
		this.mapLabelsRoot.savedImagesList.push(config.regions[i].textoverlay);

		// set up area tag
		var newareaelement = document.createElement("area");
		newareaelement.className = "maparea";
		newareaelement.owningControl = this;
		newareaelement.href = "#"+i;
		newareaelement.alt = config.regions[i].alt || config.regions[i].title || "";		// If either alt or title not supplied, fill in with the other
		newareaelement.title = config.regions[i].title || config.regions[i].alt || "";
		newareaelement.shape = config.regions[i].shape;
		newareaelement.coords = config.regions[i].coords;
		newareaelement.selected = false;
		this.imageMap.appendChild(newareaelement);
	}
	
	// IE Asynchronous Function Parameters Hack
	// Check to see if we're running under some version of IE with a broken setTimeout/setInterval implementation that doesn't support additional parameters to the callback function.  If non-broken implementation, the asynch function will set the useIEAsynchContextHack to false.  Otherwise (for broken implementations) it'll stay true.
	this.useIEAsynchContextHack = true;
	setTimeout(function(context) { if(context) context.useIEAsynchContextHack = false; }, 0, this);
	

	// ***** Internal Functions *****
	
	this._render = function()
	{
		/*
			Add new controls to parent rootElement in one of four ways:
			1a. Leave previous contents alone and append to end of div (this.renderMethod == 0)
			1b. Leave all previous content alone and prepend to beginning of div (this.renderMethod == 1)
			2a. Set all previous contents of the div to visibility:hidden and prepend to beginning of the div (this.renderMethod == 2)
			2b. Set all previous contents of the div to display:none and prepend to beginning of the div (this.renderMethod == 3)
		*/

		if(!this.rendered)
		{
			var IEversion = navigator.userAgent.substr(navigator.userAgent.indexOf("MSIE")+5, 1);	// Browser-version sniffing is hideous, but it's the only way to target IE6 (but not IE7 or later) for the alpha-PNG hack below
			
			var referenceelement;
			if(this.renderMethod == 0)		// Add the new elements to the end of the div
				referenceelement = null;		// parent.insertBefore(element, null) appends to the end of the parent
			else													// Add the new elements to the beginning of the div (insertionmethod == 1-3)
				referenceelement = container.firstChild;
			
			// Add "map layer" divs
			container.insertBefore(this.mapLayersRoot, referenceelement);
			
			if(this.UseSecLayer)
			{
				// Add "map layer" divs
				container.insertBefore(this.mapLayersSecRoot, referenceelement);
			}
			
			// Add "map active layer" divs
			container.insertBefore(this.mapLayersActiveRoot, referenceelement);
			// Add "map label" divs
			container.insertBefore(this.mapLabelsRoot, referenceelement);
			// Add image overlay
			container.insertBefore(this.image, referenceelement);
			// Add imagemap
			container.insertBefore(this.imageMap, referenceelement);
			
			if(IEversion == 6)
			{
				// Check to see if they've set the background image to a PNG file in IE6, and do the alpha-PNG hack if so:
				var bgimage = this.mapLayersRoot.currentStyle.backgroundImage.toString();
				var pngpattern = /url\("?(.*\.png)"?\);?/i;	// IE6 reformats the CSS fields and does things like inserting quotes around the url in url() values, but don't trust it
	
				var matches = bgimage.match(pngpattern);
				if(typeof(this.mapLayersRoot.style.filter) != "undefined" && matches && matches[1])								// Has filters, is IE version 6 and backgroundImage filename ends with .png.
				{
					this.mapLayersRoot.style.backgroundImage = "url("+this.image.src+")";											// Set old background to a transparent overlay image (for efficiency, just reuse the transparent map overlay we already use for the imagemap)
					this.mapLayersRoot.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+matches[1]+"', sizingMethod='scale')";	// And set up the DXImageTransform filter
				}
				
				if(this.UseSecLayer)
				{
					// Check to see if they've set the background image to a PNG file in IE6, and do the alpha-PNG hack if so:
					var bgimageSec = this.mapLayersSecRoot.currentStyle.backgroundImage.toString();
					var pngpatternSec = /url\("?(.*\.png)"?\);?/i;	// IE6 reformats the CSS fields and does things like inserting quotes around the url in url() values, but don't trust it
	
					var matchesSec = bgimageSec.match(pngpatternSec);
					if(typeof(this.mapLayersSecRoot.style.filter) != "undefined" && matchesSec && matchesSec[1])								// Has filters, is IE version 6 and backgroundImage filename ends with .png.
					{
						this.mapLayersSecRoot.style.backgroundImage = "url("+this.image.src+")";											// Set old background to a transparent overlay image (for efficiency, just reuse the transparent map overlay we already use for the imagemap)
						this.mapLayersSecRoot.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+matchesSec[1]+"', sizingMethod='scale')";	// And set up the DXImageTransform filter
					}
				}
			}
			
			// Set styles of "map layer" divs (styles are undefined until the element is inserted into the document, and may be wiped on insertion).
			var maplayers = Phoenix.DOM.getElementsByClassName("maplayer", this.mapLayersRoot);			
			var maplayersSec = null;
			
			if(this.UseSecLayer)
			{
				maplayersSec = Phoenix.DOM.getElementsByClassName("maplayer", this.mapLayersSecRoot);				
			}
			
			var maplayersactive = Phoenix.DOM.getElementsByClassName("maplayeractive", this.mapLayersActiveRoot);
			var maplayerlabels = Phoenix.DOM.getElementsByClassName("maplayerlabel", this.mapLabelsRoot);
	
			for(var i=0; i<maplayers.length; i++)
			{
				maplayers[i].style.backgroundImage = "url(" + this.mapLayersRoot.savedImagesList[i] + ")";	// Set background image in CSS
				
				if(this.UseSecLayer)
				{					
					maplayersSec[i].style.backgroundImage = "url(" + this.mapLayersSecRoot.savedImagesList[i] + ")";	// Set background image in CSS
				
					if(!this.IsSecLayerActive)
					{
						maplayersSec[i].style.opacity = "0";
						maplayersSec[i].style.display = "none";
					}
					else 
					{
						maplayers[i].style.opacity = "0";
						maplayers[i].style.display = "none";
					}
				}
				
				maplayersactive[i].style.backgroundImage = "url(" + this.mapLayersActiveRoot.savedImagesList[i] + ")";	// Set background image in CSS
				maplayersactive[i].style.opacity= "0";	// Items start out inactive (active layer opacity == 0)
				
				maplayerlabels[i].style.backgroundImage = "url(" + this.mapLabelsRoot.savedImagesList[i] + ")";	// And do label
				
				if(typeof(maplayers[i].style.filter) != "undefined" && IEversion < 9)								// IE Alpha-PNG Hack:
				{
					maplayers[i].style.backgroundImage = "url("+this.image.src+")";											// Set old background to a transparent overlay image (for efficiency, just reuse the transparent map overlay we already use for the imagemap)
					maplayers[i].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+this.mapLayersRoot.savedImagesList[i]+"', sizingMethod='scale')";	// And set up the DXImageTransform filter
					
					if(this.UseSecLayer)
					{
						maplayersSec[i].style.backgroundImage = "url("+this.image.src+")";											// Set old background to a transparent overlay image (for efficiency, just reuse the transparent map overlay we already use for the imagemap)
						maplayersSec[i].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+this.mapLayersSecRoot.savedImagesList[i]+"', sizingMethod='scale')";	// And set up the DXImageTransform filter
					}
					
					maplayersactive[i].style.backgroundImage = "url("+this.image.src+")";											// Set old background to a transparent overlay image (for efficiency, just reuse the transparent map overlay we already use for the imagemap)
					maplayersactive[i].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+this.mapLayersActiveRoot.savedImagesList[i]+"', sizingMethod='scale'); progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";	// And set up the DXImageTransform filter
	
					maplayerlabels[i].style.backgroundImage = "url("+this.image.src+")";											// Set old background to a transparent overlay image (for efficiency, just reuse the transparent map overlay we already use for the imagemap)
					maplayerlabels[i].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+this.mapLabelsRoot.savedImagesList[i]+"', sizingMethod='scale')";	// And set up the DXImageTransform filter
				}
					
				this.mapLayersRoot.movementDirectionList[i] = 0;	// And initialise all layers to be static (ie, not in the process of animating up or down)
				if(this.UseSecLayer)
				{
					this.mapLayersSecRoot.movementDirectionList[i] = 0;	// And initialise all layers to be static (ie, not in the process of animating up or down)
				}
				this.mapLayersActiveRoot.opacityDirectionList[i] = 0;	// And initialise all layers to be static (ie, not in the process of animating up or down)
			}
	
			// Add event handlers to area tags in imagemap (similar to styles, event handlers are undefined before insertion into the document)
			var areas = this.imageMap.areas;
			for(var i=0; i<areas.length; i++)
			{	// Set up internal event handlers.
				Phoenix.Util.Event.addEventHandler("mouseover", areas[i], this._highlightarea);
				Phoenix.Util.Event.addEventHandler("mouseout", areas[i], this._lowlightarea);
				Phoenix.Util.Event.addEventHandler("click", areas[i], this._selectarea);
				Phoenix.Util.Event.addEventHandler("mousedown", areas[i], this._mousedown);
				Phoenix.Util.Event.addEventHandler("mouseup", areas[i], this._mouseup);
			}
	
			// And finally, remember that we've been rendered
			this.rendered = true;
		}
	};

	// Internal functions:

	this.animateLayer = function (index, context, globalwidgetindex)
	{
	
		// EITHER: the function was called synchronously (with the index value passed to it)
		// OR it was called as Phoenix.Widgets._InstantiatedControls[globalwidgetindex] by our "IE6/7's broken setTimeout" workaround
		// EITHER WAY: "this" points to the correct location already, so just copy it into "context"
		if(index != null && context == null && this != window)
			context = this;
		else if(index === undefined)	// God knows what's happened here - someone called it synchronously without knowing anything about the function, or tried to call it asynchronously through setTimeout in IE without using our setTimeout hack?
			throw new Phoenix.Exception("State lost during asynchronous call to Phoenix.Widgets.Map.animateLayer()");
		// ELSE Firefox, called asynchronously, and index/context are already passed-in correctly
		
		var thelayer = Phoenix.DOM.getElementsByClassName("maplayer", context.mapLayersRoot, "div")[index];
		var thelayeractive = Phoenix.DOM.getElementsByClassName("maplayeractive", context.mapLayersActiveRoot, "div")[index];
		var thelabel = Phoenix.DOM.getElementsByClassName("maplayerlabel", context.mapLabelsRoot, "div")[index];

		if(context.highlightStyle == 'move-y') {
		
			var currentheight = Math.abs(parseInt(thelayer.style.top)) || 0;	// On initial start, no in-line styles defined (but layers default to zero height, so if no defined inline style then height == 0)
			var thearea = context.imageMap.areas[index];
			
			if(context.mapLayersRoot.movementDirectionList[index] > 0 && currentheight < context.maxHeight)		// Going up, and not hit max height yet...
			{
				currentheight += context.animationStep;
				thelayer.style.top = 0-currentheight + "px";																					// ...so move layer up a notch (layer moving up => style.top DEcreasing, and vice-versa)...
				thelabel.style.top = 0-currentheight + "px";
				
				if(context.useIEAsynchContextHack)
					context.mapLayersRoot.movementTimers[index] = window.setTimeout("Phoenix.Widgets._InstantiatedControls["+globalwidgetindex+"].animateLayer("+index+", null, "+globalwidgetindex+");", context.animationDelay);
				else
					context.mapLayersRoot.movementTimers[index] = window.setTimeout(function(index, context) { context.animateLayer(index, context); }, context.animationDelay, index, context);
	
			}
			else if(context.mapLayersRoot.movementDirectionList[index] < 0 && currentheight > context.minHeight)	// Going down, and not hit minumum height yet and this area isn't selected...
			{
				currentheight -= context.animationStep;
				thelayer.style.top = (0-currentheight) + "px";																					// ...so move layer down a notch (layer moving up => style.top DEcreasing, and vice-versa)...
				thelabel.style.top = (0-currentheight) + "px";
	
				if(context.useIEAsynchContextHack)
					context.mapLayersRoot.movementTimers[index] = window.setTimeout("Phoenix.Widgets._InstantiatedControls["+globalwidgetindex+"].animateLayer("+index+", null, "+globalwidgetindex+");", context.animationDelay);
				else
					context.mapLayersRoot.movementTimers[index] = window.setTimeout(function(index, context) { context.animateLayer(index, context); }, context.animationDelay, index, context);
			}
			else		// Not going anywhere...
			{
				context.mapLayersRoot.movementTimers[index] = null;	// ...so blank the timer so it's obvious we aren't moving
			}
			
		}
		else if(context.highlightStyle == 'swap-image') {
		
			var currentopacity = 0;	// On initial start, no in-line styles defined (but layers default to full opacity, so if no defined inline style then opacity == 1)
			if(typeof thelayeractive.style.opacity != 'undefined') {
				currentopacity = parseFloat(thelayeractive.style.opacity) || 0;
			}
			else {
				//filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"
				var matches = thelayeractive.style.filter.match('/(.*)progid:DXImageTransform\.Microsoft\.Alpha\(Opacity=(\d+)\)(.*)/i');
				if(matches && matches.length > 2) {
					currentopacity = parseFloat(matches[2]) || 0;
				}
			}
			
			// Round opacity to 2dp, to negate IE's little problem with simple addition and subtraction (e.g., "i=1; i-=0.2;" iterated gives 1, 0.8, 0.6000000000000001, 0.4000000000000001, 0.20000000000000007, 5.551115123125783e-17), even in IE8. 0_o
			currentopacity = Math.round(currentopacity*100)/100;
			
			var thearea = context.imageMap.areas[index];
			
			if(context.mapLayersActiveRoot.opacityDirectionList[index] > 0 && currentopacity < 1)		// Going up, and not hit max height yet...
			{
				currentopacity += context.animationStep;
				thelayeractive.style.opacity = currentopacity;
				
				if(typeof(thelayeractive.style.filter) != "undefined") {
					thelayeractive.style.filter = thelayeractive.style.filter.replace(/progid:DXImageTransform\.Microsoft\.Alpha\(Opacity=\d+\)/i, 'progid:DXImageTransform.Microsoft.Alpha(Opacity='+Math.round(currentopacity*100)+')');
				}
				
				if(context.useIEAsynchContextHack)
					context.mapLayersActiveRoot.opacityTimers[index] = window.setTimeout("Phoenix.Widgets._InstantiatedControls["+globalwidgetindex+"].animateLayer("+index+", null, "+globalwidgetindex+");", context.animationDelay);
				else
					context.mapLayersActiveRoot.opacityTimers[index] = window.setTimeout(function(index, context) { context.animateLayer(index, context); }, context.animationDelay, index, context);
	
			}
			else if(context.mapLayersActiveRoot.opacityDirectionList[index] < 0 && currentopacity > 0)	// Going down, and not hit minumum height yet and this area isn't selected...
			{
				currentopacity -= context.animationStep;
				thelayeractive.style.opacity = currentopacity;
				
				if(typeof(thelayeractive.style.filter) != "undefined") {
					thelayeractive.style.filter = thelayeractive.style.filter.replace(/progid:DXImageTransform\.Microsoft\.Alpha\(Opacity=(\d+)\)/i, 'progid:DXImageTransform.Microsoft.Alpha(Opacity='+Math.round(currentopacity*100)+')');
				}
	
				if(context.useIEAsynchContextHack)
					context.mapLayersActiveRoot.opacityTimers[index] = window.setTimeout("Phoenix.Widgets._InstantiatedControls["+globalwidgetindex+"].animateLayer("+index+", null, "+globalwidgetindex+");", context.animationDelay);
				else
					context.mapLayersActiveRoot.opacityTimers[index] = window.setTimeout(function(index, context) { context.animateLayer(index, context); }, context.animationDelay, index, context);
			}
			else		// Not going anywhere...
			{
				context.mapLayersActiveRoot.opacityTimers[index] = null;	// ...so blank the timer so it's obvious we aren't moving
			}
			
			
		}
	};
	

	// Generic event handlers for area elements
 	this._highlightarea = function (event)
	{
		event = event || window.event;
		var thearea = Phoenix.Util.Event.getTarget(event);
		var index = parseInt(thearea.href.substr(thearea.href.indexOf("#")+1), 10);
		var context = thearea.owningControl;	// Grab a handle to this Phoenix.Widgets.Map object (for brevity in following lines)
		var thelayer = Phoenix.DOM.getElementsByClassName("maplayer", context.mapLayersRoot, "div")[index];
		
		if(context.highlightStyle == 'move-y') {
			context.mapLayersRoot.movementDirectionList[index] = 1;	// when selected, no matter what stage of raising/lowering the layer's at, just change the direction to "up"
		}
		else if(context.highlightStyle == 'swap-image') {
			context.mapLayersActiveRoot.opacityDirectionList[index] = 0.2;	// when selected, no matter what stage of showing/hiding the layer's at, just change the direction to "show"
		}
		thelayer.style.zIndex = 1;
		
		if(context.useIEAsynchContextHack)
			context.mapLayersRoot.movementTimers[index] = window.setTimeout("Phoenix.Widgets._InstantiatedControls["+context.globalWidgetIndex+"].animateLayer("+index+", null, "+context.globalWidgetIndex+");", context.animationDelay);
		else
			context.mapLayersRoot.movementTimers[index] = window.setTimeout(function(index, context) { context.animateLayer(index, context); }, context.animationDelay, index, context);

		if(event.preventDefault)
			event.preventDefault();
		return(false);
	};
	
	this._lowlightarea = function (event)
	{
		event = event || window.event;
		var thearea = Phoenix.Util.Event.getTarget(event);
		var index = parseInt(thearea.href.substr(thearea.href.indexOf("#")+1), 10);
		var context = thearea.owningControl;	// Grab a handle to this Phoenix.Widgets.Map object (for brevity in following lines)
		var thelayer = Phoenix.DOM.getElementsByClassName("maplayer", context.mapLayersRoot, "div")[index];
	
		if(!thearea.selected)	// Don't unhighlight if the area's been selected
		{
			if(context.highlightStyle == 'move-y') {
				context.mapLayersRoot.movementDirectionList[index] = -1;	// when selected, no matter what stage of raising/lowering the layer's at, just change the direction to "down"
			}
			else if(context.highlightStyle == 'swap-image') {
				context.mapLayersActiveRoot.opacityDirectionList[index] = -0.2;	// when selected, no matter what stage of showing/hiding the layer's at, just change the direction to "show"
			}
			thelayer.style.zIndex = 0;

			if(context.useIEAsynchContextHack)
				context.mapLayersRoot.movementTimers[index] = window.setTimeout("Phoenix.Widgets._InstantiatedControls["+context.globalWidgetIndex+"].animateLayer("+index+", null, "+context.globalWidgetIndex+");", context.animationDelay);
			else
				context.mapLayersRoot.movementTimers[index] = window.setTimeout(function(index, context) { context.animateLayer(index, context); }, context.animationDelay, index, context);
		}

		if(event.preventDefault)
			event.preventDefault();
		return(false);
	};
	
	this._selectarea = function (event)
	{
		event = event || window.event;
		var thearea = Phoenix.Util.Event.getTarget(event);
		var index = parseInt(thearea.href.substr(thearea.href.indexOf("#")+1), 10);
		var context = thearea.owningControl;	// Grab a handle to this Phoenix.Widgets.Map object (for brevity in following lines)
		var thelayer = Phoenix.DOM.getElementsByClassName("maplayer", context.mapLayersRoot, "div")[index];

		thearea.selected = !thearea.selected;
		
		thelayer.style.zIndex = 2;	// Show highlighted areas above this one, but still keep this area raised over unhighlighted ones
		
		// Pass through events to external event-handler hooks (if defined)
		if(context.onclick)
			context.onclick(event);
		
		if(thearea.selected && context.onselectarea)
			context.onselectarea(event);
		else if(!thearea.selected && context.ondeselectarea)
			context.ondeselectarea(event);
		
		if(event.preventDefault)
			event.preventDefault();
		return(false);
	};

	this._mousedown = function (event)
	{
		event = event || window.event;
		var thearea = Phoenix.Util.Event.getTarget(event);
		var context = thearea.owningControl;	// Grab a handle to this Phoenix.Widgets.Map object (for brevity in following lines)

		// Pass through events to external event-handler hooks (if defined)
		if(context.onmousedown)
			context.onmousedown(event);
		
		if(event.preventDefault)
			event.preventDefault();
		return(false);
	};

	this._mouseup = function (event)
	{
		event = event || window.event;
		var thearea = Phoenix.Util.Event.getTarget(event);
		var context = thearea.owningControl;	// Grab a handle to this Phoenix.Widgets.Map object (for brevity in following lines)

		// Pass through events to external event-handler hooks (if defined)
		if(context.onmouseup)
			context.onmouseup(event);
		
		if(event.preventDefault)
			event.preventDefault();
		return(false);
	};
	
	
	
	
	
	
	
	this.setSecLayer = function (active)
	{
		if(this.UseSecLayer)
		{
			this.IsSecLayerActive = active;
			
			var maplayers = Phoenix.DOM.getElementsByClassName("maplayer", this.mapLayersRoot);
			var maplayersSec = Phoenix.DOM.getElementsByClassName("maplayer", this.mapLayersSecRoot);
			
			for(var i=0; i<maplayers.length; i++)
			{
				if(!this.IsSecLayerActive)
				{
					maplayersSec[i].style.opacity = "0";
					maplayersSec[i].style.display = "none";
					maplayers[i].style.opacity = "1";
					maplayers[i].style.display = "block";
				}
				else 
				{
					maplayersSec[i].style.opacity = "1";
					maplayersSec[i].style.display = "block";
					maplayers[i].style.opacity = "0";
					maplayers[i].style.display = "none";
				}		
			}
		}
		
		return(active);
	};


	
	this.coordsListToPointsArray = function (valuelist)
	{
		var onedarray = valuelist.split(",");
		var twodarray = new Array();
		var tempxcoord = null;
		
		for(i=0; i<onedarray.length; i++)
		{
			if(i % 2 == 0)	// If even element, save it (don't add until we have a x,y value pair, in case someone does something stupid like passing an *odd* number of integers)
				tempxcoord = onedarray[i];
			else						// If odd element, add this and the previous one to the array
				twodarray.push({x:parseInt(tempxcoord), y:parseInt(onedarray[i])});
		}
		return(twodarray);
	};

	this.pointsArrayToCoordsList = function (twodarray)
	{
		var onedarray = new Array();
		
		for(i=0; i<twodarray.length; i++)
		{
				onedarray.push(parseInt(twodarray[i].x));
				onedarray.push(parseInt(twodarray[i].y));
		}
		
		return(onedarray);
	};
	
};