/**
 *	Copyright 2002-2006 by Earth Resource Mapping Ltd.  All rights reserved.
 *  Image Integration Framework generated pages may only be served by organizations that have
 *	a license for the Image Integration Framework product. Image Integration Framework may only be used by
 *	Application Service Providers (ASPs) with a license for the Image Integration Framework ASP Edition product.
 *
 *  @fileoverview Integration service
 */

var SIX = {};

/*
** If the Node object does not exist (Internet Explorer), 
** define it with the XML DOM NodeType Named Constants. 
*/
if (!window.Node) 
{
    var Node = {            
        ELEMENT_NODE: 1,    
        ATTRIBUTE_NODE: 2,  
        TEXT_NODE: 3,
        CDATA_SECTION_NODE: 4,
        ENTITY_REFERENCE_NODE: 5,
        ENTITY_NODE: 6,
        PROCESSING_INSTRUCTION_NODE: 7,
        COMMENT_NODE: 8,
        DOCUMENT_NODE: 9,
        DOCUMENT_TYPE_NODE: 10,
        DOCUMENT_FRAGMENT_NODE: 11, 
        NOTATION_NODE: 12    
    }
}

SIX.aUidToObject = new Object();
SIX.nRefIndex = 0;
SIX.aRefArray = new Array();
SIX.addRef = function (obj) {
	SIX.nRefIndex++;
	SIX.aRefArray[SIX.nRefIndex] = obj;
	obj.myself = "SIX.aRefArray[" + SIX.nRefIndex + "]";
}

SIX.Uid = new function (sPrefix)
{
	this.aPrefixes = new Object();//Associative array of Uid prefixes.
}

SIX.Uid.getUid = function (sPrefix)
{
	var sIndex = (sPrefix && sPrefix != "") ? sPrefix : "default";
	if (!this.aPrefixes[sIndex])
		this.aPrefixes[sIndex] = 1;
	else 
		this.aPrefixes[sIndex] += 1;
		
	return (sIndex + "Uid" + this.aPrefixes[sIndex]);
}

var timestamp = 0;
var DEBUG = 1;
function debug(flag, label, value) {
   if (flag) {
      var funcName = debug.caller.toString();
      funcName = funcName.substring(10, funcName.indexOf(")") + 1);
      var msg = "In " + funcName + ": " + label + "=" + value;
      var now = new Date();
      var elapsed = now - timestamp;
      if (elapsed < 10000) {
         msg += " (" + elapsed + " msec)";
      }
      timestamp = now;
      java.lang.System.out.println(msg);
   }
}

SIX.LastError = new function ()
{
	this.sTitle = "";
	this.sMessage = "";
	this.sErrorText = "";
}

SIX.LastError.setLastError = function (sTitle, sMessage, sErrorText)
{
	this.sTitle = sTitle;
	this.sMessage = sMessage;
	this.sErrorText = sErrorText;
}

SIX.Util = {};

SIX.Util.createElement = function (tagName, className, id)
{
	var elem = document.createElement(tagName);
	elem.setAttribute("class", className);
	elem.setAttribute("className", className);
	if (arguments.length >= 3 && id != "")
		elem.setAttribute("id", id);
	return elem;
}

SIX.Util.decToHex = function (nDec) 
{
	var sHexValues = "0123456789abcdef";
	var sHex = sHexValues.substr(nDec&15,1);
	while (nDec > 15) 
	{
		nDec >>= 4;
		sHex = sHexValues.substr(nDec&15,1) + sHex;
	}
	return sHex;
}

SIX.Util.hexToDec = function (sHex) 
{
	return parseInt(sHex,16);
} 

SIX.Util.hexNoPrefix = function (sHex)
{
	var sHexNoPrefix = sHex;
	//Turn "#FFFFFF" or "0xFFFFFF" into "FFFFFF"
	if (sHex.substring(0,1) == "#") 
		sHexNoPrefix = sHex.substring(1,7);
	else if ((sHex.substring(0,2) == "0x") || (sHex.substring(0,2) == "0X"))
		sHexNoPrefix = sHex.substring(2,8);
	return sHexNoPrefix;
}

SIX.Util.rgbToHex = function (aRGBColor)
{
	var sHexColor = "";
	if (aRGBColor.length >= 3)
	{
		var sHexR = SIX.Util.decToHex(aRGBColor[0]);
		if (sHexR.length == 1) 
			sHexR = "0" + sHexR;
			
		var sHexG = SIX.Util.decToHex(aRGBColor[1]);
		if (sHexG.length == 1) 
			sHexG = "0" + sHexG;
					
		var sHexB = SIX.Util.decToHex(aRGBColor[2]);
		if (sHexB.length == 1) 
			sHexB = "0" + sHexB;

		sHexColor = "#" + sHexR + sHexG + sHexB;		
	}
	return sHexColor;
}

SIX.Util.toHex = function (sColor)
{
	var sHex = "";
	sColor = sColor.toLowerCase();
	if ((sColor.indexOf("#") != -1) || (sColor.indexOf("transparent") != -1))
	{
		sHex = sColor;
	}
	else
	{
		sColor = sColor.replace(/\s+/g,"");//Replace spaces
		var nIndex1 = sColor.indexOf("rgb(");
		var nIndex2 = sColor.indexOf(")");
		if ((nIndex1 != -1) && (nIndex2 != -1))
		{
			sColor = sColor.substring(nIndex1 + 4, nIndex2);
			var aRGB = sColor.split(",");
			sHex = SIX.Util.rgbToHex(aRGB);
		}
	}
	return sHex;
}

SIX.Util.hexToRgb = function (sHexColor)
{
	//Turn "#000000" into [0,0,0] or "#FFFFFF into [255,255,255]

	if (sHexColor.substring(0,1) == "#") 
		sHexColor = sHexColor.substring(1,7);
		
	var aRGB = new Array();

	var nR = SIX.Util.hexToDec(sHexColor.substring(0,2));
	var nG = SIX.Util.hexToDec(sHexColor.substring(2,4));
	var nB = SIX.Util.hexToDec(sHexColor.substring(4,6));

	if (isNaN(nR) || isNaN(nG) || isNaN(nB))
		return null;
		
	aRGB[0] = nR;
	aRGB[1] = nG;
	aRGB[2] = nB;
	
	return aRGB;
}

SIX.Util.clone = function (src)
{
	var clone = src;
	if (src && (typeof(src) == "object"))
	{
		clone = new Object();
		for (var i in src)
			clone[i] = this.clone(src[i]);
	}
	return clone;
}

SIX.Util.trim = function (str)
{
    var sTrimmed = str.replace(/^\s+/g,"");//Replace white space at start
    sTrimmed = sTrimmed.replace(/\s+$/g,"");//Replace white space at end
    return sTrimmed;
}

SIX.Util.compareStrings = function (s1,s2) 
{
	var bEqual = false;
	if (s1 == s2)
		bEqual = true;
	else if ((typeof s1 == "string") && (typeof s2 == "string") &&
			(s1.toLowerCase() == s2.toLowerCase()))
		bEqual = true;
	return bEqual;
}

SIX.Util.cookiesEnabled = function () 
{
	var oldCookie = document.cookie;
	var bIsEnabled = true;
    document.cookie = "cookies_enabled=1";
    var pos = document.cookie.indexOf("cookies_enabled=");
    if (pos == -1)
        bIsEnabled = false;
    document.cookie = oldCookie;
    return bIsEnabled;
}

SIX.Util.setCookie = function (name, value, expires, path, domain, secure) 
{
	if (!this.cookiesEnabled())
		return;
	//Set time in milliseconds.
	var dateToday = new Date();
	dateToday.setTime(dateToday.getTime());

	//If the expires variable is set, make the correct 
	//expires time, convert it from days to milliseconds. 

	if (expires) 
		expires = expires * 1000 * 60 * 60 * 24;

	var dateExpires = new Date(dateToday.getTime() + expires);

	document.cookie = name + "=" + escape(value) +
	(expires ? ";expires=" + dateExpires.toGMTString() : "" ) + 
	(path ? ";path=" + path : "" ) + 
	(domain ? ";domain=" + domain : "" ) +
	(secure ? ";secure" : "" );
}

// this function gets the cookie, if it exists
SIX.Util.getCookie = function (name) 
{
	if (!this.cookiesEnabled())
		return null;
	var start = document.cookie.indexOf(name + "=");
	var len = start + name.length + 1;
	if (!start && (name != document.cookie.substring(0, name.length)))
		return null;
	if (start == -1) 
		return null;
	var end = document.cookie.indexOf(";", len);
	if (end == -1) end = document.cookie.length;
	return unescape(document.cookie.substring(len, end));
}

// this deletes the cookie when called
SIX.Util.deleteCookie = function (name, path, domain) 
{
	if (!this.cookiesEnabled())
		return;
	if (this.getCookie(name)) 
	{
		document.cookie = name + "=" +
		(path ? ";path=" + path : "") +
		(domain ? ";domain=" + domain : "") +
		";expires=Thu, 01-Jan-1970 00:00:01 GMT";
	}
}

SIX.Util.getQueryStringParamValue = function (sParamName)
{
	var sQueryString = window.location.search.substring(1);
	sQueryString = unescape(sQueryString);
	var aParams = sQueryString.split("&");
	var sParamValue = "";
	for (var i=0;i<aParams.length;i++) 
	{
		var aParamNameValuePair = aParams[i].split("=");
		if (aParamNameValuePair[0].toLowerCase() == sParamName.toLowerCase())
		{
			sParamValue = aParamNameValuePair[1];
			break;	 
		}
	}
	return sParamValue;
}

SIX.Util.getQueryStringParamValueEx = function (sQueryString, sParamName, sDelimiter)
{
	var aParams = sQueryString.split(sDelimiter);
	var sParamValue = "";
	for (var i=0;i<aParams.length;i++)
	{
		var aParamNameValuePair = aParams[i].split("=");
		if (aParamNameValuePair[0].toLowerCase() == sParamName.toLowerCase())
		{
			sParamValue = aParamNameValuePair[1];
			break;
		}
	}
	return sParamValue;
}

SIX.Util.stringToDegrees = function (s) 
{
	var s2 = s.replace("&deg;" ,":");
	s2 = s2.replace("'" ,":");
	s2 = s2.replace('"' ,":");
	s2 = s2.replace(/\s+/g,":");//Replace spaces
	
	var bNegative = false;
	if (s2.length && s2.substr(0,1) == "-") {
		bNegative = true;
		s2 = s2.substr(1,s2.length-1);
	}
		
	var dms = s2.split(":");
	var dd = 0;
	var ratio = 1.0;
	for( var i=0; i<dms.length; i++ ) {
		var part = parseFloat(dms[i]);
		if( isNaN(part) )
			break;
		dd += (part/ratio);
		ratio = ratio*60;
	}
	if ( bNegative || s.substr(s.length-1,1) == "S" || s.substr(s.length-1,1) == "W" )
		dd *= -1;
	return dd;
}

SIX.Util.latitudeToString = function (dd) 
{
	var dms = this.DDToDMS(dd);
	dms += (dd >= 0 ? "N" : "S");
	return dms;
}

SIX.Util.longitudeToString = function (dd) 
{
	var dms = this.DDToDMS(dd);
	dms += (dd >= 0 ? "E" : "W");
	return dms;
}

SIX.Util.DDToDMS = function (dd) 
{
	var d = Math.abs(dd);
	var nDeg = Math.floor(d);
						
	d = (d - nDeg)*60;	
	nMin = Math.floor(d);
	
	d = (d - nMin)*60;	
	nSec = d.toFixed(2);
	
	if (nSec == 60.00)
	{
		nSec = 0;
		nMin += 1;
		if (nMin == 60)
		{
			nMin = 0;
			nDeg += 1;
		}
	}
	
	var dms = nDeg + "&deg;" + (nMin < 10 ? "0" + nMin : nMin) + "'" + (nSec < 10 ? "0" + nSec : nSec) + '"';	
	return dms;
}

SIX.Util.pause = function (nMilliseconds) 
{
	var tNow = new Date();
	var tExit = tNow.getTime() + nMilliseconds;
	while (true) 
	{
		tNow = new Date();
		if (tNow.getTime() > tExit)
		return;
	}
} 

SIX.Util.formatDate = function (date, sFormat)
{
	var sDate = sFormat;
	var sDay = SIX.Util.getZeroPadded(date.getDate(),2);//Day
	//Note: Date.getMonth() returns 0 to 11 for Jan to Dec, 
	//so need to add 1 for formatting as DD/MM/YYYY.
	var sMonth = SIX.Util.getZeroPadded(date.getMonth()+1,2);//Month 
	var sYear = SIX.Util.getZeroPadded(date.getFullYear(),2);//Year
	var sHours = SIX.Util.getZeroPadded(date.getHours(),2);//Hours
	var sMinutes = SIX.Util.getZeroPadded(date.getMinutes(),2);//Minutes
	var sSeconds = SIX.Util.getZeroPadded(date.getSeconds(),2);//Seconds
	
	sDate = sDate.replace("dd", sDay);
	sDate = sDate.replace("mm", sMonth);
	sDate = sDate.replace("yyyy", sYear);
	sDate = sDate.replace("yy", sYear.substring(2,4));
	sDate = sDate.replace("hh", sHours);
	sDate = sDate.replace("mm", sMinutes);
	sDate = sDate.replace("ss", sSeconds);
	return sDate;
}

SIX.Util.timeStampToDate = function (sTimeStamp, sFormat)
{
	var sDate = "Invalid time stamp";
	sTimeStamp = sTimeStamp.replace(/ /g,"");//Trim white space
	
	if ((sTimeStamp.length == 10) || (sTimeStamp.length == 12) || (sTimeStamp.length == 13))
	{
		//Time stamps are based on the number of seconds since the beginning epoch 
		//of January 1, 1970 at midnight 00:00:00 GMT.
		//Time stamps usually have 10 or 13 digits, although 12-digit time stamps have also been encountered.
		//13-digit time stamps (such as 1121453661023) have an additional 3 digits for microseconds.
		var nTimeStamp = parseInt(sTimeStamp);
		if (!isNaN(nTimeStamp))
		{
			var date = new Date(nTimeStamp);
			sDate = SIX.Util.formatDate(date, sFormat);
		}
	}
	return sDate;
}

SIX.Util.getZeroPadded = function (nNum, nWidth)
{
	var sNum = "" + nNum;
  	while (sNum.length < nWidth)
  		sNum = "0" + nNum;
  	return sNum;
}

SIX.Util.getSpaces = function (nNumSpaces)
{
	var sSpaces = "";
  	for (var i=0; i<nNumSpaces; i++)
  		sSpaces += " ";
  	return sSpaces;
}

SIX.Util.getStyle = function (el, styleProp)
{
	if (el.currentStyle)
		var style = el.currentStyle[styleProp];
	else if (window.getComputedStyle)
		var style = document.defaultView.getComputedStyle(el,null).getPropertyValue(styleProp);
	return style;
}

SIX.Hash = function ()
{
	this.length = 0;
	this.items = new Object();
	for (var i=0; i<arguments.length; i+=2) 
	{
		if (typeof(arguments[i + 1]) != 'undefined') 
		{
			this.items[arguments[i]] = arguments[i + 1];
			this.length++;
		}
	}
}

SIX.Hash.prototype.removeItem = function (key)
{
	var value;
	if (typeof(this.items[key]) != 'undefined') 
	{
		this.length--;
		var value = this.items[key];
		delete this.items[key];
	}
	return value;
}

SIX.Hash.prototype.removeAllItems = function() {
    for (var i in this.items) {
        if (this.items.hasOwnProperty(i))
            this.removeItem(i);
    }
}

SIX.Hash.prototype.getItem = function (key)
{
    if (!isNaN(parseInt(key)))
        key = "_" + key;
	return this.items[key];
}

SIX.Hash.prototype.setItem = function (key, value) {
    if (!isNaN(parseInt(key)))
        key = "_" + key;
	if (typeof(value) != 'undefined') 
	{
		if (typeof(this.items[key]) == 'undefined') 
		{
			this.length++;
		}

		this.items[key] = value;
	}

	return value;
}

SIX.Hash.prototype.hasItem = function (key) {
    if (!isNaN(parseInt(key)))
        key = "_" + key;
	return (typeof(this.items[key]) != 'undefined');
}

SIX.Point = function (x,y)
{
	this.x = x ? x : 0.0;
	this.y = y ? y : 0.0;
}

SIX.Envelope = function (dMinX, dMinY, dMaxX, dMaxY)
{
	this.dMinX = dMinX;
	this.dMinY = dMinY;
	this.dMaxX = dMaxX;
	this.dMaxY = dMaxY;
}

SIX.Envelope.prototype.setMinX = function (dMinX)
{
	this.dMinX = dMinX;
}

SIX.Envelope.prototype.setMinY = function (dMinY)
{
	this.dMinY = dMinY;
}

SIX.Envelope.prototype.setMaxX = function (dMaxX)
{
	this.dMaxX = dMaxX;
}

SIX.Envelope.prototype.setMaxY = function (dMaxY)
{
	this.dMaxY = dMaxY;
}

SIX.Envelope.prototype.containsPoint = function (point)
{
	var bContainsPoint = false;
	if ((point.x >= this.dMinX) && (point.x <= this.dMaxX) && 
		(point.y >= this.dMinY) && (point.y <= this.dMaxY))
		bContainsPoint = true;
	return bContainsPoint;
}

SIX.Envelope.prototype.containsPoints = function (aPoints)
{
	var bContainsPoints = true;
	for (var i=0; i<aPoints.length; i++)
	{
		if (!this.containsPoint(aPoints[i]))
		{
			bContainsPoints = false;
			break;
		}
	}
	return bContainsPoints;
}				

SIX.Envelope.prototype.contains = function (envelope)
{
	var bContains = false;
	if ((envelope.dMinX > this.dMinX) && (envelope.dMaxX < this.dMaxX) &&
		(envelope.dMinY > this.dMinY) && (envelope.dMaxY < this.dMaxY)) 
		bContains = true;
	return bContains;
}

SIX.Envelope.prototype.intersects = function (envelope)
{
	var bIntersects = false;
	
	if ((((envelope.dMinX <= this.dMinX) && (envelope.dMaxX >= this.dMinX)) || 
		((envelope.dMinX >= this.dMinX) && (envelope.dMinX <= this.dMaxX))) && 
		(((envelope.dMaxY >= this.dMaxY) && (envelope.dMinY <= this.dMaxY)) || 
		((envelope.dMaxY <= this.dMaxY) && (envelope.dMaxY >= this.dMinY))))	
		bIntersects = true;

	return bIntersects;
}

SIX.Polygon = function (aRings, envelope)
{
	this.envelope = (envelope ? envelope : null);
	this.aRings = aRings;
}

SIX.Polygon.prototype.containsPoint = function (point)
{
	if (this.envelope && !this.envelope.containsPoint(point))
		return false; 
		
	var bInPolygon = false;
	if (this.aRings)
	{
		var aRings = this.aRings;
		for (var i=0; i<aRings.length; i++)
		{
			var ring = aRings[i];
			if (SIX.Util.inRegion(ring.aPoints, point))
			{
				bInPolygon = true;
				var aHoles = ring.aHoles;
				for (var j=0; j<aHoles.length; j++)
				{
					if (SIX.Util.inRegion(aHoles[j].aPoints, point))
					{
						bInPolygon = false;
						break;
					}
				}
				if (bInPolygon) break;
			}
		}
	}
	return bInPolygon;
}

SIX.PolygonRing = function (aPoints, aHoles)
{
	this.aPoints = aPoints;
	this.aHoles = aHoles;
}

SIX.PolygonHole = function (aPoints)
{
	this.aPoints = aPoints;
}

SIX.Util.pointsInRegion = function (aImageRegion, aPoints)
{
	var bPointsInRegion = false;
	if (aPoints.length)
	{
		bPointsInRegion = true;
		for (var i=0; i<aPoints.length; i++)
		{
			if (!this.inRegion(aImageRegion, aPoints[i]))
			{
				bPointsInRegion = false;
				break;
			}
		}
	}
	return bPointsInRegion;
}

SIX.Util.inRegion = function (aImageRegion, point)
{
	var bInRegion = false;
	//Polygon may or may not be defined with identical first and last points. 
	var bFirstLastPointsIdentical = ((aImageRegion[0].x == aImageRegion[aImageRegion.length - 1].x) && (aImageRegion[0].y == aImageRegion[aImageRegion.length - 1].y));
	var nNumUniquePoints =  (bFirstLastPointsIdentical ? aImageRegion.length - 1 : aImageRegion.length);
	if (nNumUniquePoints >= 3)
	{
		var i=0;
		var j=0;
		
		for (i=0; i<nNumUniquePoints; i++) 
		{
			j++; 
			if (j == nNumUniquePoints) j=0;
			if (aImageRegion[i].y < point.y && aImageRegion[j].y >= point.y ||
				aImageRegion[j].y < point.y && aImageRegion[i].y >= point.y) 
			{
				if (aImageRegion[i].x + (point.y - aImageRegion[i].y) / (aImageRegion[j].y - aImageRegion[i].y) * (aImageRegion[j].x - aImageRegion[i].x) < point.x) 
					bInRegion = !bInRegion; 
			}
		}
	}
	return bInRegion;
}

/**
*	PURPOSE: Performs a binary search on a sorted array of objects 
*
*	ARGUMENTS: 
*	aArray	- Array of objects.
*	sKey	- Name of attribute which objects are sorted by.
*	nValue	- Value to search for.
*
*	NOTES:
*	1. First instance in the array with matching value is returned. 
*	2. Returns index of first matching element, OR 
*	if not found, a negative index of where the element should be minus 1
*/

SIX.Util.searchObjectArray = function (aArray, sKey, nValue)
{
	var nRetVal = -1;
  
  	if (aArray.length && (typeof aArray[0] == "object") && (sKey != ""))
  	{
		var nLeft = -1; 
		var nRight = aArray.length; 
		var nMid;  
		
		while (nRight - nLeft > 1)
		{
			nMid = (nLeft + nRight) >>> 1;
			if(aArray[nMid][sKey] < nValue)
				nLeft = nMid;
			else
				nRight = nMid;
		}

		if ((nRight < aArray.length) && (aArray[nRight][sKey] == nValue))
			nRetVal = nRight;
		else
			nRetVal = -(nRight + 1);
	}

	return nRetVal;
}

SIX.Util.escapeMarkup = function (sMarkup)
{
	//Replace single quotes
	var sEscaped = sMarkup.replace(/\\'/g, "&#92;&#39;");
	sEscaped = sEscaped.replace(/'/g, "&#39;");
	//Replace double quotes
	sEscaped = sEscaped.replace(/\\"/g, "&#92;&#34;");
	sEscaped = sEscaped.replace(/"/g, "&#34;");			
	
	sEscaped = sEscaped.replace(/</g,"&lt;");//Replace '<'
	sEscaped = sEscaped.replace(/>/g,"&gt;");//Replace '>'
	return sEscaped;
}

SIX.Util.convertTextUrlsToLinks = function (sText)
{
	//Replace any text that represents a link with an anchor tag.

	//Match links that begin at the start of a line.
	var sFormattedText = sText.replace(/^(http|https|ftp)(:\/\/\S+)/gim, "<a href='$1$2' target='_blank'>$1$2</a>");			
	sFormattedText = sFormattedText.replace(/^(www\.\S+)/gim, "<a href='http://$1' target='_blank'>$1</a>");

	//Match links that begin after a single white space character, including space, tab, form feed, line feed.
				
	sFormattedText = sFormattedText.replace(/(\s)(http|https|ftp)(:\/\/\S+)/gim, "$1<a href='$2$3' target='_blank'>$2$3</a>");	
	sFormattedText = sFormattedText.replace(/(\s)(www\.\S+)/gim, "$1<a href='http://$2' target='_blank'>$2</a>");	
	
	sFormattedText = sFormattedText.replace(/\r\n/g, "<br>");
	sFormattedText = sFormattedText.replace(/\r/g, "<br>");
	sFormattedText = sFormattedText.replace(/\n/g, "<br>");

	//Convert spaces before tags to nbsp;
	//Need to ensure one space (not nbsp;) occurs before and after anchor so text and anchors wrap correctly.
	sFormattedText = sFormattedText.replace(/  </gi, "&nbsp; <");
	if (sFormattedText != "")
	{
		var sTextTemp = "";
		while(sTextTemp != sFormattedText)
		{
			sTextTemp = sFormattedText;
			sFormattedText = sTextTemp.replace(/  &nbsp;/gi, " &nbsp;&nbsp;");
		}
	}
	
	//Convert spaces after tags to nbsp;
	//Need to ensure one space (not nbsp;) occurs before and after anchor so text and anchors wrap correctly.
	sFormattedText = sFormattedText.replace(/>  /gi, "> &nbsp;");
	if (sFormattedText != "")
	{
		var sTextTemp = "";
		while(sTextTemp != sFormattedText)
		{
			sTextTemp = sFormattedText;
			sFormattedText = sTextTemp.replace(/&nbsp;  /gi, "&nbsp;&nbsp; ");
		}
	}	
	
	//Need to insert space between break tags to see multiple line breaks.
	sFormattedText = sFormattedText.replace(/<br><br>/g, "<br>&nbsp;<br>");		
	sFormattedText = sFormattedText.replace(/<br><br>/g, "<br>&nbsp;<br>");
	return sFormattedText;
}

/*
**  Given an array of points that define a closed polygon, calculate the area.
*/
SIX.Util.calculateArea = function (aPoints)
{
	var dArea = 0.0;

	for (var i=0; i<aPoints.length; i++) 
	{
		var point1 = aPoints[i];
		if (point1 instanceof SIX.Point)
		{
			var x1 = point1.x;
			var y1 = point1.y;
		}
		else
		{
			var x1 = point1[0];
			var y1 = point1[1];		
		}
		
		var j = (i + 1) % aPoints.length;
		var point2 = aPoints[j];
		if (point2 instanceof SIX.Point)
		{
			var x2 = point2.x;
			var y2 = point2.y;
		}
		else
		{
			var x2 = point2[0];
			var y2 = point2[1];		
		}
		
		dArea += x1 * y2;
		dArea -= y1 * x2;
	}

	dArea = Math.abs(dArea / 2);
	return dArea;
}

SIX.Util.calculateAreaCentroid = function (aPoints) 
{
    var vertices = aPoints.slice(0);
    if (vertices.length < 3)
        return ([0, 0]);
    var cx = 0;
    var cy = 0;
    var i, j;
    var factor = 0;
    if (vertices[0].y != vertices[vertices.length - 1].y ||
        vertices[0].x != vertices[vertices.length - 1].x) {
        // We must close the polygon
        vertices[vertices.length] = vertices[0];
    }
    var area = this.calculateArea(vertices);
    var n = vertices.length - 1;
    for (i=0; i<n; i++) {
        j = (i + 1);
        factor = (vertices[j].y * vertices[i].x -
                vertices[i].y * vertices[j].x);
           
        cx += (vertices[i].x + vertices[j].x) * factor;
        cy += (vertices[i].y + vertices[j].y) * factor;
    }
    factor = 1 / (6 * area);
    cx *= factor;
    cy *= factor;
    return ([Math.abs(cx), Math.abs(cy)]);
}
    
SIX.Util.findPosX = function (obj)
{
	var curleft = 0;
	if(obj.offsetParent)
		while(1) 
		{
			curleft += obj.offsetLeft;
			if(!obj.offsetParent)
			break;
			obj = obj.offsetParent;
		}
	else if(obj.x)
		curleft += obj.x;
	return curleft;
}

SIX.Util.findPosY = function (obj)
{
	var curtop = 0;
	if(obj.offsetParent)
		while(1)
		{
			curtop += obj.offsetTop;
			if(!obj.offsetParent)
			break;
			obj = obj.offsetParent;
		}
	else if(obj.y)
		curtop += obj.y;
	return curtop;
}

// -------------------------------------------------------------------
//   Pass this function a SELECT object and the options will be sorted
//   by their text (display) values
// -------------------------------------------------------------------
SIX.Util.sortSelect = function (obj) 
{
	var o = new Array();
	
	if (obj == null || obj.options == null) { return; }
	//if (!hasOptions(obj)) { return; }
	for (var i=0; i<obj.options.length; i++) 
	{
		o[o.length] = new Option( obj.options[i].text, obj.options[i].value, obj.options[i].defaultSelected, obj.options[i].selected) ;
	}
	if (o.length==0) { return; }
	o = o.sort( 
			function(a,b) 
			{ 
				if ((a.text+"") < (b.text+"")) { return -1; }
				if ((a.text+"") > (b.text+"")) { return 1; }
				return 0;
			} 
		);

	for (var i=0; i<o.length; i++) 
	{
		obj.options[i] = new Option(o[i].text, o[i].value, o[i].defaultSelected, o[i].selected);
	}
}

SIX.Util.getScreenXDPI = function ()
{
	var nXDPI = 96;
	if (SIX.App.isIE)
		nXDPI = screen.deviceXDPI;
	else 
	{
		var screenDPICalculatorDiv = document.getElementById("ScreenDPICalculator");
		if (screenDPICalculatorDiv) 
			nXDPI = screenDPICalculatorDiv.offsetWidth;
	}
	return nXDPI;
}

SIX.Util.getScreenYDPI = function ()
{
	var nYDPI = 96;
	if (SIX.App.isIE)
		nYDPI = screen.deviceYDPI;
	else 
	{	
		var screenDPICalculatorDiv = document.getElementById("ScreenDPICalculator");
		if (screenDPICalculatorDiv) 	
			nYDPI = screenDPICalculatorDiv.offsetHeight;
	}
	return nYDPI;
}

SIX.Util.getRandomNum = function (lbound, ubound) 
{
	return (Math.floor(Math.random() * (ubound - lbound)) + lbound);
}

SIX.Util.getRandomChar = function(number, lower, upper, other, extra) 
{
	var numberChars = "0123456789";
	var lowerChars = "abcdefghijklmnopqrstuvwxyz";
	var upperChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	var otherChars = "`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/? ";
	var charSet = extra;
	if (number == true)
	charSet += numberChars;
	if (lower == true)
	charSet += lowerChars;
	if (upper == true)
	charSet += upperChars;
	if (other == true)
	charSet += otherChars;
	return charSet.charAt(this.getRandomNum(0, charSet.length));
}

SIX.Util.getParentNode = function (el, tagName) 
{
	if (el == null) 
		return null;
	else if (el.nodeType == Node.ELEMENT_NODE && el.tagName.toLowerCase() == tagName.toLowerCase())
		return el;
	else
		return this.getParentNode(el.parentNode, tagName);
}

SIX.Util.isParentNode = function (node, childNode) 
{
	var bIsParentNode = false;
	if (node && childNode && node != childNode)
	{
		var parentNode = childNode.parentNode;
		while (parentNode)
		{
			if (node == parentNode)
			{
				 bIsParentNode = true;
				 break;
			}
			parentNode = parentNode.parentNode;
		}
	}
	return bIsParentNode;
}

SIX.Util.extend = function(targetClass, sourceClass)
{
	//Copy prototypes from base access class
	for (var i in sourceClass.prototype) 
	{
		if (sourceClass.prototype.hasOwnProperty(i) && typeof targetClass.prototype[i] == 'undefined')
			targetClass.prototype[i] = sourceClass.prototype[i];
	}
	
	//Copy properties from base access class
	for (var i in sourceClass)
	{
		if (sourceClass.hasOwnProperty(i) && typeof sourceClass[i] != 'function' && typeof targetClass[i] == 'undefined')
			targetClass[i] = sourceClass[i];
	}
}

SIX.Util.getParameter = function (params, name, defaultValue)
{
	var value = params && typeof params[name] != "undefined" ? params[name] : defaultValue;
	return value;
}

SIX.Util.interpolateRGBColors = function (aStartColor, aEndColor, nSteps)
{
	var aColors = new Array();

	var nStartR = aStartColor[0];
	var nStartG = aStartColor[1];
	var nStartB = aStartColor[2];

	var nEndR = aEndColor[0];
	var nEndG = aEndColor[1];
	var nEndB = aEndColor[2];

	var nDeltaR = nEndR - nStartR;
	var nDeltaG = nEndG - nStartG;
	var nDeltaB = nEndB - nStartB;

	for (var i=0; i<nSteps+1; i++)
	{
		var nR = nStartR + (nDeltaR * i / nSteps);
		var nG = nStartG + (nDeltaG * i / nSteps);
		var nB = nStartB + (nDeltaB * i / nSteps);

		var sHexR = SIX.Util.decToHex(nR);
		if (sHexR.length == 1) 
			sHexR = "0" + sHexR;
			
		var sHexG = SIX.Util.decToHex(nG);
		if (sHexG.length == 1) 
			sHexG = "0" + sHexG;
					
		var sHexB = SIX.Util.decToHex(nB);
		if (sHexB.length == 1) 
			sHexB = "0" + sHexB;	

		var sColor = "#" + sHexR + sHexG + sHexB;
		aColors[i] = sColor.toLowerCase();
	}

	return aColors;
}

SIX.Util.interpolateHexColors = function (sHexStartColor, sHexEndColor, nSteps)
{
	var aStartColor = SIX.Util.hexToRgb(sHexStartColor);
	var aEndColor = SIX.Util.hexToRgb(sHexEndColor);
	return SIX.Util.interpolateRGBColors(aStartColor, aEndColor, nSteps);
}

SIX.Util.interpolateRGBColor = function (aStartColor, aEndColor, dPercent)
{
	var nStartR = aStartColor[0];
	var nStartG = aStartColor[1];
	var nStartB = aStartColor[2];

	var nEndR = aEndColor[0];
	var nEndG = aEndColor[1];
	var nEndB = aEndColor[2];

	var nDeltaR = nEndR - nStartR;
	var nDeltaG = nEndG - nStartG;
	var nDeltaB = nEndB - nStartB;

	var nR = nStartR + (nDeltaR * dPercent / 100);
	var nG = nStartG + (nDeltaG * dPercent / 100);
	var nB = nStartB + (nDeltaB * dPercent / 100);

	var sHexR = SIX.Util.decToHex(nR);
	if (sHexR.length == 1) 
		sHexR = "0" + sHexR;
		
	var sHexG = SIX.Util.decToHex(nG);
	if (sHexG.length == 1) 
		sHexG = "0" + sHexG;
				
	var sHexB = SIX.Util.decToHex(nB);
	if (sHexB.length == 1) 
		sHexB = "0" + sHexB;	

	var sColor = "#" + sHexR + sHexG + sHexB;
	sColor = sColor.toLowerCase();

	return sColor;
}

SIX.Util.interpolateHexColor = function (sHexStartColor, sHexEndColor, dPercent)
{
	var aStartColor = SIX.Util.hexToRgb(sHexStartColor);
	var aEndColor = SIX.Util.hexToRgb(sHexEndColor);
	return SIX.Util.interpolateRGBColor(aStartColor, aEndColor, dPercent);
}

SIX.Util.interpolateHexColorsArray = function (aHexColors, nSteps)
{
	var aInterpolatedColors = new Array();
	for (var i=0; i<nSteps; i++)
	{
		var dBin = (aHexColors.length - 1) * i / nSteps;
		var nBin = Math.floor(dBin); 
		var sStartColor = aHexColors[nBin];
		var sEndColor = aHexColors[nBin+1];
		var dPercent = 100 * (dBin - nBin);
		aInterpolatedColors[i] = SIX.Util.interpolateHexColor(sStartColor, sEndColor, dPercent);
	}

	return aInterpolatedColors;
}

//	---------------------------------------------------------------------------
//  SIX.Parameters - Class for parsing parameter values from a string of the form
//				"<param1 name>=<param1 value>;<param2 name>=<param2 value>;..."
//				Purpose: Mainly used to simplify class instantiation by allowing
//				only those parameters which differ from their default values to 
//				be specified in the call to the class constructor. 
//
//				Example: 
//				function MyClass(param1, param2, param3, param4, param5)
//				{
//					this.param1 = arguments.length < 1 ? 10 : param1;
//					this.param2 = arguments.length < 2 ? 20 : param2;
//					this.param3 = arguments.length < 3 ? 30 : param3;
//					this.param4 = arguments.length < 4 ? 40 : param4;
//					this.param5 = arguments.length < 5 ? 50 : param5;
//				}
//
//				If only param5 differs from the default value, then default values
//				must still be supplied for param1, param2, param3 and param4 as follows:
//
//				var myClass = new MyClass(10,20,30,40,27);
//
//				Now using SIX.Parameters:
//				function MyClass(sParams)
//				{
//					this.parameters = new SIX.Parameters(sParams);
//					this.param1 = this.parameters.getParameter("param1","number",10);
//					this.param2 = this.parameters.getParameter("param2","number",20);
//					this.param3 = this.parameters.getParameter("param3","number",30);
//					this.param4 = this.parameters.getParameter("param4","number",40);
//					this.param5 = this.parameters.getParameter("param5","number",50);
//				}	
//
//				Now, if only param5 differs from the default value, then only param5
//				is supplied as follows:
//
//				var myClass = new MyClass("param5=27");
//	---------------------------------------------------------------------------
SIX.Parameters = function (params)
{
	var aParts = params.split(";");
	this.parameters = new Object();//Associative array
	
	for(var i=0; i<aParts.length; i++) 
	{
		var iIndex = aParts[i].indexOf("=");
		this.parameters[aParts[i].substr(0, iIndex).toLowerCase()] = aParts[i].substr(iIndex + 1);
	}
}

SIX.Parameters.prototype.getParameter = function (name, type, defaultValue)
{
	type = type.toLowerCase();
	var value = defaultValue; 
	
	if (this.parameters && typeof this.parameters[name] != "undefined")
	{
		switch (type)
		{
			case "boolean": 
				value = this.parameters[name].toLowerCase() == "true";
			break;
			case "number": 
				value = parseFloat(this.parameters[name]);
			break;
			case "string": 
				value = this.parameters[name];
			break;
		}
	}

	return value;
} 

SIX.eMapLoadState = {
	LOAD_ERROR : -1,
	NOT_LOADED : 0,
	LOADING : 1,
	LOADED : 2
}

SIX.eLayersLoadState = {
	LOAD_ERROR : -1,
	NOT_LOADED : 0,
	LOADING : 1,
	LOADED : 2
}


/**
* @class SIX.Access methods used by other interface classes that don't have specific needs.
* Different types of map servers (for example ArcIMS using ArcXML or MapXtreme or MapPoint .NET) 
* have different ways to access the map server. The SIX.Access object provides a generic interface 
* to map servers. Each SIX.Service object contains an SIX.Access object to access that service's 
* particular map server. When a new SIX.Access style object is created, it actually returns a 
* sub-class object specific to the map server being used. These subclasses implement the same 
* methods as the SIX.Access class in ways specific to each map server being accessed. 
* @constructor
*/
SIX.Access = function (service, sInterface) 
{
	SIX.addRef(this);
	this.service = service;
	this.name = "";
}

SIX.Access.prototype.updateMap = function (map) 
{
	if (this.service.aServices.length)
	{
		for (var i=0; i<this.service.aServices.length; i++)
		{
			this.service.aServices[i].access.updateMap(map);
		}
		this.service.loaded = true;
		return;
	}
	
	var control = document[map.uid];
	
	if( this.service.loaded && !this.service.active ) 
	{
		this.service.busy(false);
	}
	else if( !this.service.loaded ) 
	{
		if (this.service.queries) 
		{
			for (var i=0; i<this.service.queries.aQueries.length; i++)
			{
				var nIndex = control.GetLayerIndex(this.service.queries.aQueries[i].uid);
				if (nIndex == -1)
					nIndex = map.addLayer("GISOverlay","",this.service.queries.aQueries[i].uid,"");
				if (nIndex == -1) 
				{
					alert("Unable to open GIS layer\n" + this.service.queries.aQueries[i].uid);
					this.service.online(false);
					break;
				}
			}		
		}
		
		var nIndex = control.GetLayerIndex(this.service.uid);
		if (nIndex == -1)
			nIndex = map.addLayer("GISOverlay","",this.service.uid,"");
		if (nIndex == -1)
		{
			alert("Unable to open GIS layer\n" + this.service.uid);
			this.service.online(false);
		}
		else 
		{	
			this.service.loaded = true;
			control.SetLayerTransparency(this.service.uid, "#", this.service.transparency);
			if( this.service.colorMask ) {
				var mask = "transmask=#" + this.service.colorMask.toString(16);
				map.setLayerParameter( this.service.uid, mask );
			}
			var color;
			for( color in this.service.colors)
			{
				if (this.service.colors.hasOwnProperty(color))
					control.SetLayerTransparency(this.service.uid,color,this.service.colors[color].percent/100);
			}
			this.orderLayers(map);
		}
	}
	
	var objGISOverlayMapsLoadStats = map.getGISOverlayMapsLoadStats();
	if (!objGISOverlayMapsLoadStats.nNumGISOverlayMapsLoading)
	{
		map.resetGISOverlayMapsLoadState();
		map.setGISOverlayProgress(0);	
	}
	this.service.loadMap(map);
}

SIX.Access.prototype.loadMap = function (map) 
{
}

SIX.Access.prototype.loadLayers = function (map) 
{
	this.service.nLayersLoadState = SIX.eLayersLoadState.LOADED;
	return true;
}

SIX.Access.prototype.queryLayers = function (map) 
{
}

SIX.Access.prototype.responseMap = function (map, layerName, url, body, action, tlx, tly, brx, bry, response) 
{
	return true;
}

SIX.Access.prototype.queryResponse = function (map)
{
}

SIX.Access.prototype.searchResponse = function (map)
{
}

SIX.Access.prototype.searchMap = function (map, search)
{
}

SIX.Access.prototype.orderLayers = function (map)
{
	var control = document[map.uid];
	var order = (control.GetLayerIndex(map.sDefaultECWJP2LayerName) > -1 ? 1 : 0);
	var catalog = this.service.catalog;
	for( var i=0; i<catalog.services.length; i++ ) 
	{
		var service = catalog.services[catalog.servicesOrder[i]];
		if (service.access instanceof SIX.Access.ECWP) 
		{
			for (var j=service.layers.length-1; j>=0; j--) 
			{
				var layer = service.layers[service.layersOrder[j]];
				
				if (layer.loaded)
				{
					var oldOrder = control.GetLayerIndex(layer.uid);
					if (oldOrder != -1) 
					{
						if (oldOrder != order)
							map.moveLayer(oldOrder,order);
						order += 1;
					}
					
					for (var k=layer.aLayers.length-1; k>=0; k--)
					{
						var layerChild = layer.aLayers[k];
						var nIndexChild = control.GetLayerIndex(layerChild.uid);
						if (nIndexChild != -1) 
						{
							if (nIndexChild != order)
								map.moveLayer(nIndexChild, order);
							order += 1;
						}
					}
				}
			}		
		}
		else
		{
			if (service.loaded)
			{
				var oldOrder = control.GetLayerIndex(service.uid);
				if (oldOrder != -1)
				{
					if (oldOrder != order)
						map.moveLayer(oldOrder, order);
					order += 1;
				}
				
				for (var j=service.aServices.length-1;j>=0; j--) 
				{
					var serviceChild = service.aServices[j];
					var oldOrder = control.GetLayerIndex(serviceChild.uid);
					if (oldOrder != -1)
					{
						if (oldOrder != order)
							map.moveLayer(oldOrder, order);
						order += 1;
					} 
				}
			}
		}
	}
}

SIX.Access.prototype.getScale = function (map)
{
	return map.scale;
}

SIX.Access.prototype.getBestScale = function (map)
{
	return 0;
}

/**
* @class Interfaces to ECWP (Image Web Server) web servers that provide streaming imagery support.
* @constructor
* @base SIX.Access
*/
SIX.Access.ECWP = function (service) 
{
	SIX.Access.call(this, service);
	this.name = "Image Web Server (ECWP protocol)";
	this.hasMetadata = true;
	this.hasLegend = false;//true;
	this.hasQuery = false;//true;
	this.hasSearch = false;//true;
	this.canReorder = true;
	this.hasPrint = true;	
}
SIX.Util.extend(SIX.Access.ECWP, SIX.Access);

SIX.Access.ECWP.prototype.updateMap = function (map) 
{	
	if (this.service.aServices.length)
	{
		for (var i=0; i<this.service.aServices.length; i++)
			this.service.aServices[i].access.updateMap(map);
		this.service.loaded = true;
		return;
	}

	var bUpdateMap = false;
	map.bAllowExtentsUpdate = false;
	this.service.loaded = true;
	var control = document[map.uid];
	if (!control)
	return;
	var nLayersLoaded = 0;
	var legend = "<table>";
	var coordSysNew = new SIX.CoordSys();
	var bCoordSysChanged = false;
	
	var channelCurrent = SIX.App.getCurrentChannel();
		
	// delete layers first to ensure no projections collision
	var bEcwpLayerDeleted = false;
	for( var i=0; i<this.service.layers.length; i++ ) 
	{
		var layer = this.service.layers[this.service.layersOrder[i]];
		
		if (layer.loaded && (!layer.bInList || !layer.bSelected || !layer.bInScale || !this.service.active)) 
		{
			if (this.service.deleteEcwpLayer(map, layer))
				bEcwpLayerDeleted = true;
		}
		else if (layer.bSelected && layer.bInScale && this.service.active)
		{		
			for (var j=0; j<layer.aLayers.length; j++)
			{
				var layerChild = layer.aLayers[j];
				if (layerChild.loaded && (!layerChild.bSelected || !layerChild.bInScale))
				{
					if (this.service.deleteEcwpLayer(map, layerChild))
						bEcwpLayerDeleted = true;
				}
				
				if (!layerChild.loaded && layerChild.bSelected && layerChild.bInScale)
					layer.loaded = false;	
			}		
			
			if (!bCoordSysChanged)
			{
				if (!map.coordSys.equals(layer.coordSys))
				{
					bCoordSysChanged = true;
					map.coordSys = layer.coordSys.clone(map.coordSys);
				}
			}	
			coordSysNew = layer.coordSys.clone(coordSysNew);
		}	
	}
		
	// need to check that there is a blank image
	var resetExtents = false;
	
	// used for searching outside a projection
	if (bEcwpLayerDeleted && !bCoordSysChanged)
		bCoordSysChanged = this.checkECWJP2Exists(map);
	
	// ----------------------- Trap case where we are manually switching in a non-compatible image ------------------
	if ((map.coordSys.initialized() && coordSysNew.initialized() && !map.coordSys.equals(coordSysNew)) || bCoordSysChanged) 
	{	
		resetExtents = true;
		for(var i=0; i<map.catalog.services.length; i++)
		{
			var aservice = map.catalog.services[i];
			if( aservice.access instanceof SIX.Access.ECWP) 
			{
				for (var j=0; j<aservice.layers.length; j++)
				{
					var layer = aservice.layers[j];
					aservice.deleteEcwpLayer(map, layer);
				}
			}
		}
	}
	
	for (var i=this.service.layers.length-1; i>=0; i--) 
	{
		var layer = this.service.layers[this.service.layersOrder[i]];	
		
		if ( !layer.loaded && layer.bInList && layer.bSelected && layer.bInScale && this.service.active ) 
		{		
			if (control.GetLayerIndex(map.sDefaultECWJP2LayerName) != -1)
			{					
				map.deleteAllLayers();
				for(var j=0; j<map.catalog.services.length; j++) 
				{
					var svc = map.catalog.services[j];
					if (svc != this.service)
					{							
						svc.loaded = false;
						for (var k=0; k<svc.aServices.length; k++)
							svc.aServices[k].loaded = false;
					}
				}
			}	
			
			var requestResponse;
			var error;
		
			if (layer.url && layer.url != "")
			{
				if (layer.bCalculateExtents && !(layer.tlLatitude || layer.tlLongitude || layer.brLatitude || layer.brLongitude))
				{
					error = control.GetLayerIndex(layer.uid);
					if (error == -1) 
					{
						//Add layer to control to obtain extents.  Set freecache=true and visibility=false to prevent caching
						//as this layer is delete after obtaining extents, and only added again with correct freecache and 
						//visibility parameters if the layer extents intersect the view extents.
						error = map.addLayer("ecwjp2",layer.url,layer.uid,"freecache=true;visibility=false" + (layer.bOTDF ? ";otdf=true" : ""));
					}	
					if (error != -1)
					{				
						map.setLayerExtents(layer);	
						if (!map.layerInExtents(layer))
						{
							layer.bInScale = false;
							bUpdateMap = true;	
						}
						map.deleteLayer(layer.uid);
					}
				}
					
				if (layer.bInScale)
				{		
					error = control.GetLayerIndex(layer.uid);
					if (error == -1) 
					{
						var sLayerParams = "freecache=" + SIXConfig.FREECACHE;
						if (SIX.App.bDHTMLControl)
						{	
							if (layer.bOTDF)
							{
								//Default url is an otdf path.
								sLayerParams += ";otdf=true";
							}
							else
							{
								var sClipRegion = "";
								if (layer.aClipRegion.length && !map.inLayerClipRegion(layer))
								{
									//If we're not inside the clip region, then we may be on the border of the 
									//clip region, so need to request png to hide data outside clip region.
									sClipRegion = layer.getClipRegionStr();
									if (sClipRegion != "")
										sLayerParams += ";imagetype=png;otdf=false;clipregion=" + sClipRegion;
								}
								
								if (layer.sOTDFPath != "")
								{
									sLayerParams += ";otdfpath=" + layer.sOTDFPath;					
									if (sClipRegion == "")
										sLayerParams += ";otdf=true;imagetype=jpg";
								}
							}
						}
						error = map.addLayer("ecwjp2",layer.url,layer.uid, sLayerParams);	
					}
					if (error != -1)
					{				
						map.uncacheEcwpLayer(layer);
						map.setLayerClipRegion(layer);
					}
				}						
			}
		
			if (error != -1)
			{				
				for (var j=0; j<layer.aLayers.length; j++)
				{
					var layerChild = layer.aLayers[j];
					if (layerChild.bInScale && layerChild.bSelected)
					{
						var nChildLayerIndex = control.GetLayerIndex(layerChild.uid);
						if (nChildLayerIndex == -1) 					
						{			
							var fnDisplayError = function (layer) 
							{
								var sLastErrorText = control.GetLastErrorText();
								var sErrorMessage = "Unable to open image layer " + layer.url + "\n" + sLastErrorText;
								alert(sErrorMessage);		
							}
									
							if (layerChild.bCalculateExtents && !(layerChild.tlLatitude || layerChild.tlLongitude || layerChild.brLatitude || layerChild.brLongitude))
							{
								//Add layer to control to obtain extents.  Set freecache=true and visibility=false to prevent caching
								//as this layer is deleted after obtaining extents, and only added again with correct freecache and 
								//visibility parameters if the layer extents intersect the view extents.
								error = map.addLayer("ecwjp2",layerChild.url,layerChild.uid,"freecache=true;visibility=false" + (layerChild.bOTDF ? ";otdf=true" : ""));	
								if (error == -1)
								{
									fnDisplayError(layerChild);
									break;							
								}
								map.setLayerExtents(layerChild);	
								if (!map.layerInExtents(layerChild))
								{
									layerChild.bInScale = false;
									bUpdateMap = true;
								}
								map.deleteLayer(layerChild.uid);
							}
								
							if (layerChild.bInScale)
							{	
								var sLayerParams = "freecache=" + SIXConfig.FREECACHE;
								if (SIX.App.bDHTMLControl)
								{									
									if (layerChild.bOTDF)
									{
										//OTDF currently does not support PNG or inregion.
										//Default url is an otdf path.
										sLayerParams += ";otdf=true";
									}
									else
									{
										var sClipRegion = "";
										if (layerChild.aClipRegion.length && !map.inLayerClipRegion(layerChild))
										{
											//If we're not inside the clip region, then we may be on the border of the 
											//clip region, so need to request png to hide data outside clip region.
											sClipRegion = layerChild.getClipRegionStr();
											if (sClipRegion != "")
												sLayerParams += ";imagetype=png;otdf=false;clipregion=" + sClipRegion;
										}
										
										if (layerChild.sOTDFPath != "")
										{
											sLayerParams += ";otdfpath=" + layerChild.sOTDFPath;					
											if (sClipRegion == "")
												sLayerParams += ";otdf=true";
										}
									}
								}
								
								error = map.addLayer("ecwjp2",layerChild.url,layerChild.uid,sLayerParams);	
								if (error == -1)
								{
									fnDisplayError(layerChild);
									break;								
								}
								layerChild.loaded = true;			
								map.uncacheEcwpLayer(layerChild);
								map.setLayerClipRegion(layerChild);
							}										
						}			
					}			
				}
			}

			if( error == -1 ) 
			{
				var sLastErrorText = control.GetLastErrorText();
				var sErrorMessage = "Unable to open image layer\n " + sLastErrorText;	

				if (sLastErrorText.indexOf("licensed client limit has been reached") > -1)
				{
					sErrorMessage = "Due to unprecedented demand for image data from the " +
										"Department of Land's Spatial Information eXchange the " +
										"servers are overloaded and we are unable to fulfill " +
										"your image request at this time.\n\n Please try again " +
										"at a later time when the servers are less congested.";								
				}
				
				requestResponse = new SIX.RequestResponse(layer.uid, layer.url, "", "", 0, 0, 0, 0, 0, SIXConfig.eRequestResponseStatus.FAILURE);
									
				alert(sErrorMessage);
				this.service.layerVisibleNoUpdate(layer);			
			}
			else 
			{
				requestResponse = new SIX.RequestResponse(layer.uid, layer.url, "", "", 0, 0, 0, 0, 0, SIXConfig.eRequestResponseStatus.SUCCESS);	

				layer.loaded = true;
				map.coordSys = layer.coordSys.clone(map.coordSys);//Remember the projection we are in
				this.orderLayers(map);
			}	
			
			this.service.statistics.addRequestResponse(requestResponse);	
			if (channelCurrent.console) 
				channelCurrent.console.updateHealth(this.service);
		}							

		if( layer.loaded && this.service.active ) 
		{
			nLayersLoaded++;		
			for( var color in this.service.colors)
			{
				if (this.service.colors.hasOwnProperty(color))
					control.SetLayerTransparency(layer.uid,color,this.service.colors[color].percent/100);
			}
			var host = layer.url.split("/");
			legend += "<tr><td>" + layer.name + "</td><td>";
			legend += "<img src='http://" + host[2] + "/ecwp/ecw_img.dll?thumbnail?ecw=" + layer.url + "&sizex=64&sizey=64'>";
			legend += "</td></tr>";
		}

	}
	legend += "</table>";
	this.service.setLegendUrl(legend);
	
	var dPercent = 100 * (nLayersLoaded > 1 ? this.service.transparency : 1.0);
	this.service.setSliderPosition(dPercent);
	this.service.setTransparency(dPercent);
	
	// check to make sure that there is at least one ECW image present
	if (this.checkECWJP2Exists(map))
		resetExtents = true;
	
	// reset extents due to projection change and reload all layers
	if (resetExtents) 
	{	
		if ((map.tlLatitude == 0) || 
			(map.tlLongitude == 0) || 
			(map.brLatitude == 0) || 
			(map.brLongitude == 0))
		{
			map.setExtentsAll();
		}
		else
		{
			map.setExtents(map.tly, map.tlx, map.bry, map.brx);
		}
			
		for(var i=0; i<map.catalog.services.length; i++)
		{
			var service = map.catalog.services[i];
			if (service != this.service)
				service.access.updateMap(map);
		}
	}
	
	if (map.bRoamClip)
		map.initRoamClip();
	
	map.bAllowExtentsUpdate = true;
	
	if (!map.loaded)
	{
		//Force OnExtentChange callback to fire with current control extents.
		var dTopLeftX = control.GetTopLeftWorldCoordinateX();
		var dTopLeftY = control.GetTopLeftWorldCoordinateY();
		var dBottomRightX = control.GetBottomRightWorldCoordinateX();
		var dBottomRightY = control.GetBottomRightWorldCoordinateY();	
		control.SetExtents(dTopLeftX, dTopLeftY, dBottomRightX, dBottomRightY); 
	}
	else
	{
		map.bAllowExtentsUpdate = false;
		control.SetExtents(map.tlx, map.tly, map.brx, map.bry); 
		map.bAllowExtentsUpdate = true;
	}

	if (channelCurrent.console)
		channelCurrent.console.updateHealth(this.service);			

	if (bUpdateMap)
	{
		map.hiddenLayers(this.service);
		this.updateMap(map);
		this.service.build();
	}
}


SIX.Access.ECWP.prototype.getBestScaleForLayer = function (map, layer)
{
	var control = document[map.uid];
	var scale = 0;
	var nLayerIndex = control.GetLayerIndex(layer.uid);
	if (nLayerIndex >= 0)
	{
		var nCellSizeX = control.GetLayerCellSizeX(layer.uid);
		var nCellSizeY = control.GetLayerCellSizeY(layer.uid);
		
		var dCenterWorldX = map.tlx + (map.brx - map.tlx) / 2;
		var dCenterWorldY = this.tly + (map.bry - map.tly) / 2;
		var tlx = dCenterWorldX - Math.abs(nCellSizeX * map.width / 2); 
		var brx = dCenterWorldX + Math.abs(nCellSizeX * map.width / 2); 
		var tly = dCenterWorldY + Math.abs(nCellSizeY * map.height / 2); 
		var bry = dCenterWorldY - Math.abs(nCellSizeY * map.height / 2); 
		scale = map.getScaleFromExtents(tlx, tly, brx, bry);
		if (scale < layer.minScale)
			scale = layer.minScale;
	}
	return scale;
}

SIX.Access.ECWP.prototype.getBestScale = function (map)
{
	if (this.service.aServices.length)
	{
		var scale = 0;
		for (var i=0; i<this.service.aServices.length; i++)
		{
			var tempScale = this.service.aServices[i].access.getBestScale(map);
			if (tempScale > 0 && (scale == 0 || tempScale < scale))
				scale = tempScale;
		}
		return scale;
	}

	var scale = 0;
	
	for (var i=0; i<this.service.layers.length; i++)
	{
		var layer = this.service.layers[i];
		if (layer.bInScale && layer.bSelected)
		{
			var layerScale = this.getBestScaleForLayer(map, layer);
			if (layerScale > 0 && (scale == 0 || layerScale < scale))
				scale = layerScale;
		}
		for (var j=0; j<layer.aLayers.length; j++)
		{
			var childLayer = layer.aLayers[j];
			if (childLayer.bInScale && childLayer.bSelected)
			{
				var layerScale = this.getBestScaleForLayer(map, childLayer);
				if (layerScale > 0 && (scale == 0 || layerScale < scale))
					scale = layerScale;			
			}
		}		
	}
	
	return scale;
}

/**
*	need to put in a blank ECW quickly - all image have been turned off
*/
SIX.Access.ECWP.prototype.checkECWJP2Exists = function (map)
{	
	var control = document[map.uid];

	var bECWJP2Exists = false;
	var bResetExents = false;	
	var nLayers = control.GetNumberLayers();
			
	for (var i = 0; i < nLayers; i++)
	{
		var sLayerName = control.GetLayerName(i);
		var sLayerType = control.GetLayerType(sLayerName);
		sLayerType = sLayerType.toLowerCase();
		if ((sLayerType == "ecw") || (sLayerType == "jp2"))
		{
			bECWJP2Exists = true;
			break;
		}
	}
		
	if (!bECWJP2Exists)	// there is no ecw layer
	{
		map.deleteAllLayers();
		
		if (!map.bReconnecting)
		{
			for(var j=0; j<map.catalog.services.length; j++)
			{
				var svc = map.catalog.services[j];
				if (svc != this.service)
				{
					svc.loaded = false;		
					for (var k=0; k<svc.aServices.length; k++)
						svc.aServices[k].loaded = false;
				}
			}		
		
			var nIndex = map.addLayer("ecwjp2",map.sDefaultECWJP2Url,map.sDefaultECWJP2LayerName,"freecache=" + SIXConfig.FREECACHE + ";visibility=FALSE;");
			if (nIndex == -1)
			{
				var msg = "Unable to open image layer:\n" + map.sDefaultECWJP2Url + " " + control.GetLastErrorText();
			}
			else
			{
				map.setDefaultECWJP2CoordSys(map.datum(), map.projection());
				
				var msg = "Loaded default layer:\n" + map.sDefaultECWJP2Url;
				
				map.coordSys = map.coordSysDefaultECWJP2.clone(map.coordSys);	
				if (nIndex) map.moveLayer(nIndex,0);//Make default ecwp the bottom layer.
				bResetExents = true;
			}	
			this.service.deleteLayers(map);
		}
	}	
	return bResetExents;
}

SIX.Access.ECWP.prototype.searchMap = function (map, search) {
	if( search.name != "Location" || search.fields.length != 2 )
		return;
	var latitude 	= SIX.Util.stringToDegrees(search.fields[0].value);
	var longitude 	= SIX.Util.stringToDegrees(search.fields[1].value);
	map.setCenter(latitude,longitude);
}

SIX.Util.addPoints = function (aPoints, dX1, dY1, dX2, dY2)
{
	var objPoint = new Object();
	objPoint.x = dX1;
	objPoint.y = dY1;
	aPoints[aPoints.length] = objPoint;

	objPoint = new Object();
	objPoint.x = dX2;
	objPoint.y = dY2;	
	aPoints[aPoints.length] = objPoint;
}

SIX.Util.circleToPolyline = function (xc, yc, radius, Sdeg, Edeg, Aspect, nAr)
{
	var xRad = 0.0;
	var yRad = 0.0;
	var Srdn = 0.0;
	var Erdn = 0.0;
	var Rdn = 0.0;
	var xO = 0.0;
	var yO = 0.0;
	var x1 = 0.0;
	var y1 = 0.0;
	var X2 = 0.0;
	var Y2 = 0.0;
	var xA = 0.0;
	var yA = 0.0;
	var xB = 0.0;
	var yB = 0.0;

	var aPoints = new Array();

	if (!Aspect) Aspect = 1;

	xO = xc - radius;
	yO = yc - radius;
	x1 = xc + radius;
	y1 = yc + radius;

	if (Aspect < 0)
	{
		xRad = Math.abs(Aspect) * radius;
		yRad = radius;
	}
	else
	{
		xRad = radius;
		yRad = Aspect * radius;
	}

	//Convert arc extents to radians
	Srdn = Math.abs(Sdeg) * Math.PI / 180;
	Erdn = Math.abs(Edeg) * Math.PI / 180;

	if (Erdn >= Srdn)
		Srdn = Srdn + Math.PI * 2;

	xO = -1;
	yO = -1;

	var dInc = (Srdn - Erdn)/nAr;

	//Cycle through the circle
	i = 0;
	Rdn = Srdn;
	while(Rdn > Erdn)
	{
		x1 = xRad * Math.cos(Rdn);
		y1 = yRad * Math.sin(Rdn);
		if ((x1 != xO) || (y1 != yO))
		{
			i = i + 1;
			xO = x1;
			yO = y1;
			x1 = xc + x1;
			y1 = yc - y1;

			if (i%2)
			{
				xB = x1;
				yB = y1;
				if (i > 1)
					SIX.Util.addPoints(aPoints, xA, yA, xB, yB);
			}
			else
			{
				xA = x1;
				yA = y1;
				SIX.Util.addPoints(aPoints, xA, yA, xB, yB);
			}

		}
		Rdn -= dInc;
	}

	//closing last arc degree 

	i = i + 1;
	x1 = xRad * Math.cos(Erdn);
	y1 = yRad * Math.sin(Erdn);
	xO = x1;
	yO = y1;
	x1 = xc + x1;
	y1 = yc - y1;

	if (i % 2)
	{
		xB = x1;
		yB = y1;
		if (i > 1)
			SIX.Util.addPoints(aPoints, xA, yA, xB, yB);
	}
	else
	{
		xA = x1;
		yA = y1;
		SIX.Util.addPoints(aPoints, xA, yA, xB, yB);
	}
	return aPoints;
}

SIX.Util.getNearestPoint = function(dX, dY, aPoints)
{
	var dSmallestDistance = Number.MAX_VALUE;
	var nIndex = -1;

	for (var i=0; i<aPoints.length; i++) 
	{
		var point = aPoints[i];
		var dXDist = point.x - dX;
		var dYDist = point.y - dY;

		var dDistance = Math.pow(dXDist * dXDist + dYDist * dYDist, 0.5);

		if (dDistance < dSmallestDistance) 
		{
			dSmallestDistance = dDistance;
			nIndex = i;
		}
	}
	var nearestPoint = null;
	if (nIndex >= 0)
		nearestPoint = new Array(nIndex, dSmallestDistance);
	return nearestPoint;	
}

SIX.Util.getNearestLine = function (dX, dY, aPoints)
{
	//Loop through line segments, calculating equation of line segment and
	//measure perpendicular dDistance from dX, dY to the line segment.
	
	var dSmallestDistance = Number.MAX_VALUE;
	var nIndex = -1;

	var nNumPoints = aPoints.length;

	for (var i=0; i<aPoints.length-1; i++) 
	{
		var point1 = aPoints[i];
		var point2 = aPoints[i+1];

		var xDist = point2.x - point1.x;
		var yDist = point2.y - point1.y;

		var dLineMag = Math.pow(xDist * xDist + yDist * yDist, 0.5);

		var dU = (((dX - point1.x) * (point2.x - point1.x)) +
			  ((dY - point1.y) * (point2.y - point1.y))) / (dLineMag * dLineMag);

		if (dU >= 0.0 && dU <= 1.0) 
		{
			// closest point falls within the line segment
			var dIntersectionX = point1.x + dU * (point2.x - point1.x);
			var dIntersectionY = point1.y + dU * (point2.y - point1.y);
			xDist = dIntersectionX - dX;
			yDist = dIntersectionY - dY;

			var dDistance = Math.pow(xDist * xDist + yDist * yDist, 0.5);

			if (dDistance < dSmallestDistance) 
			{
				dSmallestDistance = dDistance;
				nIndex = i;
			}
		}
	}

	var nearestLine = null;
	if (nIndex >= 0)
		nearestLine = new Array(nIndex, dSmallestDistance);
	return nearestLine;
}

SIX.ServiceInfo = function (map, service, sParams, sUrl, sBody, sAction, sResponse)
{
	this.map = map;
	this.service = service;
	this.sParams = sParams;
	this.sUrl = sUrl;
	this.sBody = sBody;
	this.sAction = sAction;
	this.sResponse = sResponse;
	this.aResponseMapCallbacks = new Array();
}

SIX.ServiceInfoCache = function ()
{
	this.aServiceInfoCollection = new Array();
}

SIX.ServiceInfoCache.prototype.getServiceInfo = function (map, service, sParams, sUrl, sBody, sAction)
{
	var serviceInfo = null;
	for (var i=0; i<this.aServiceInfoCollection.length; i++)
	{
		if (this.aServiceInfoCollection[i].sUrl == sUrl)
		{
			serviceInfo = this.aServiceInfoCollection[i];
			break;
		}
	}
	if (serviceInfo)
	{
		service.nLayersLoadState = SIX.eLayersLoadState.LOADING;
		
		if (serviceInfo.service.uid != service.uid)
		{
			if (serviceInfo.sResponse)
			{
				service.access.responseMap(map, service.uid, sUrl, sBody, sAction, map.tlx, map.tly, map.brx, map.bry, serviceInfo.sResponse);
			}
			else
			{
				serviceInfo.aResponseMapCallbacks[serviceInfo.aResponseMapCallbacks.length] = function (response)
				{
					service.access.responseMap(map, service.uid, sUrl, sBody, sAction, map.tlx, map.tly, map.brx, map.bry, response);
				}
			}
		}
	}
	else
	{	 
		this.aServiceInfoCollection[this.aServiceInfoCollection.length] = new SIX.ServiceInfo(map, service, sParams, sUrl, sBody, sAction, null);
		var result = map.setLayerParameter(service.uid, sParams);
		service.nLayersLoadState = SIX.eLayersLoadState.LOADING;
	}
}

SIX.ServiceInfoCache.prototype.fireResponseMapCallbacks = function (sUrl, sResponse)
{
	for (var i=0; i<this.aServiceInfoCollection.length; i++)
	{
		var serviceInfo = this.aServiceInfoCollection[i];
		if (serviceInfo.sUrl == sUrl)
		{
			if (serviceInfo.sResponse == null)
				serviceInfo.sResponse = sResponse;
			var aResponseMapCallbacks = serviceInfo.aResponseMapCallbacks;
			serviceInfo.aResponseMapCallbacks = new Array();
			for (var j=0; j<aResponseMapCallbacks.length; j++)
			{
				aResponseMapCallbacks[j](sResponse);
			}
			break;
		}
	}
}

//	--------------------------------------------------------------------------- 
//  SIX.CoordSys Class 
//	---------------------------------------------------------------------------

SIX.CoordSys = function (sDatum, sProjection)
{
	this.sDatum = (arguments.length < 2 ? "" : sDatum.toLowerCase());
	this.sProjection = (arguments.length < 2 ? "" : sProjection.toLowerCase());
}

/**
 * Clones an existing coordinate system.
 * @param {Object} coordSys Datum/projection of coordSys are set to the datum/projection of 'this'.
 * @return The SIX.CoordSys clone
 * @type Object 
 */
SIX.CoordSys.prototype.clone = function (coordSys)
{
	if (coordSys)
	{
		//Set datum/projection of coordSys to datum/projection of 'this'.
		coordSys.setDatum(this.sDatum);
		coordSys.setProjection(this.sProjection);
	}
	else
	{
		//Create new SIX.CoordSys object with datum/projection set to datum/projection of 'this'.
		var coordSys = new SIX.CoordSys(this.sDatum, this.sProjection);	
	}
	return coordSys;
}

SIX.CoordSys.prototype.equals = function (coordSys)
{
	var bEquals = false;
	if ((typeof coordSys == 'object') && 
		(coordSys.constructor == SIX.CoordSys) && 
		(this.sDatum == coordSys.sDatum) && 
		(this.sProjection == coordSys.sProjection))
		bEquals = true;
	return bEquals;
}

SIX.CoordSys.prototype.setDatum = function (sDatum)
{
	this.sDatum = sDatum.toLowerCase();
}

SIX.CoordSys.prototype.setProjection = function (sProjection)
{
	this.sProjection = sProjection.toLowerCase();
}

SIX.CoordSys.prototype.initialized = function ()
{
	var bInitialized = true;
	if ((this.sDatum == "") && (this.sProjection == ""))
		bInitialized = false;
	return bInitialized;
}

SIX.MarkerSymbol = function (sId, sImage, sImageOn, nTiePointPixelX, nTiePointPixelY)
{
	this.sId = sId;
	this.sImage = sImage;
	this.sImageOn = sImageOn;
	this.imageOff = new Image();
	this.imageOff.src = sImage;
	this.imageOn = new Image();
	this.imageOn.src = sImageOn;
	this.nTiePointPixelX = nTiePointPixelX;
	this.nTiePointPixelY = nTiePointPixelY;
}

SIX.Marker = function (sTitle, sDescription, sSymbol, x, y)
{
	SIX.addRef(this);
	this.uid = SIX.Uid.getUid("marker");
	this.imageUid = SIX.Uid.getUid("markerImage");
	this.listItemUid = SIX.Uid.getUid("markerListItem");
	this.sTitle = sTitle;
	this.sDescription = sDescription;	
	this.sSymbol = sSymbol;
	this.x = x;
	this.y = y;	
}

SIX.MarkerCollection = function (map)
{
	SIX.addRef(this);
	this.map = map;
	this.aMarkers = new Array();
	this.nSelectedMarker = -1;
	this.markerListUid = SIX.Uid.getUid("markerList");  
	this.markerListContainerUid = SIX.Uid.getUid("markerListContainer");
	this.markerSymbols = new SIX.Hash();
	this.sDefaultMarkerSymbolId = ""; 
	this.sMarkerSymbolId = "";	
}

SIX.MarkerCollection.prototype.addMarkerSymbol = function (markerSymbol) 
{
	if (!this.markerSymbols.length)
	{
		this.sDefaultMarkerSymbolId = this.sMarkerSymbolId = markerSymbol.sId;
    }
	this.markerSymbols.setItem(markerSymbol.sId, markerSymbol);
}

SIX.MarkerCollection.prototype.setMarkerSymbolId = function (sId)
{
	if (this.markerSymbols && this.markerSymbols.hasItem(sId))
	    this.sMarkerSymbolId = sId;
}

SIX.MarkerCollection.prototype.getMarkerSymbol = function (sId) 
{
	var markerSymbol;
	var channelCurrent = SIX.App.getCurrentChannel();
	if (this.markerSymbols.hasItem(sId))
		markerSymbol = this.markerSymbols.getItem(sId);
	else if (channelCurrent.getMarkerSymbol(sId))
		markerSymbol = channelCurrent.markerSymbols.getItem(sId);		
	else if (SIX.App.markerSymbols.hasItem(sId))
		markerSymbol = SIX.App.markerSymbols.getItem(sId);
	return markerSymbol;
}

SIX.MarkerCollection.prototype.getCurrentMarkerSymbolId = function ()
{
    return this.sMarkerSymbolId;
}

SIX.MarkerCollection.prototype.getDefaultMarkerSymbolId = function ()
{
    return this.sDefaultMarkerSymbolId;
}

SIX.MarkerCollection.prototype.setDefaultMarkerSymbolId = function (sId)
{
	if (this.markerSymbols && this.markerSymbols.hasItem(sId))
	{
		this.sDefaultMarkerSymbolId = sId;
	    this.sMarkerSymbolId = sId;
    }
}

SIX.MarkerCollection.prototype.getDefaultMarkerSymbol = function () 
{
    var channelCurrent = SIX.App.getCurrentChannel();
	var sDefaultMarkerSymbolId = "";
	if (this.sDefaultMarkerSymbolId != "")
	    sDefaultMarkerSymbolId = this.sDefaultMarkerSymbolId;
	else if (channelCurrent.sDefaultMarkerSymbolId != "")
	    sDefaultMarkerSymbolId = channelCurrent.sDefaultMarkerSymbolId;
	else if (SIX.App.sDefaultMarkerSymbolId != "")
	    sDefaultMarkerSymbolId = SIX.App.sDefaultMarkerSymbolId;  
	                                
	var markerSymbol = this.getMarkerSymbol(sDefaultMarkerSymbolId);
	return markerSymbol;
}


SIX.MarkerCollection.prototype.deleteAllMarkers = function ()
{
	for (var i=this.aMarkers.length-1; i>=0; i--)
		this.deleteMarker(i);
	this.nSelectedMarker = -1;
}

SIX.MarkerCollection.prototype.fromGeoJSONFeatureCollection = function (featureCollection)
{
	if (featureCollection.type == "FeatureCollection")
	{
		this.aMarkers = new Array();
		
		for (var i=0; i<featureCollection.features.length; i++)
		{
			var feature = featureCollection.features[i];
			if (feature.type == "Feature")
			{
				var geometry = feature.geometry;
				if (geometry.type == "Point")
				{					
					var properties = feature.properties;
					var marker = this.addMarker(new SIX.Marker(properties.title, 
											properties.description, 
											properties.symbol, 
											geometry.coordinates[0],
											geometry.coordinates[1]));
					SIX.App.addMouseAction(marker.imageUid,this,0,true);
				}
			}
		}
	}	
}

SIX.MarkerCollection.prototype.toGeoJSONFeatureCollection = function ()
{
	var featureCollection = new Object();
	featureCollection.type = "FeatureCollection";
	
	featureCollection.features = new Array();
	for (var i=0; i<this.aMarkers.length; i++)
	{
		var marker = this.aMarkers[i];
		var feature = new Object();
		feature.type = "Feature";
		
		var geometry = new Object();
		geometry.type = "Point";
		geometry.coordinates = new Array();
		geometry.coordinates[0] = marker.x;
		geometry.coordinates[1] = marker.y;
		feature.geometry = geometry;
		
		var properties = new Object();
		properties.title = marker.sTitle;
		properties.description = marker.sDescription;
		properties.symbol = marker.sSymbol;
		
		feature.properties = properties;
		featureCollection.features[i] = feature;
	}
	return featureCollection;	
}

SIX.MarkerCollection.prototype.getMarkerIndex = function (sMarkerId)
{
	var nMarkerIndex = -1;
	for (var i=0; i<this.aMarkers.length; i++)
	{
		var marker = this.aMarkers[i];
		if (marker.uid == sMarkerId)
		{
			nMarkerIndex = i;
			break;
		}
	}
	return nMarkerIndex;	
}

SIX.MarkerCollection.prototype.getNearestMarkerIndex = function (dLon, dLat)
{
	var nMarkerNearest = -1;
	var dDistanceToNearest = -1;
	for (var i=0; i<this.aMarkers.length; i++)
	{
		var marker = this.aMarkers[i];
		if (!nMarkerNearest) nMarkerNearest = i;
		
		var dDistance = this.map.getDistanceBetweenLLCoords(dLon, dLat, marker.x, marker.y);
		if (dDistance != -1)
		{
			if ((dDistanceToNearest == -1) || (dDistance < dDistanceToNearest)) 
			{
				dDistanceToNearest = dDistance;
				nMarkerNearest = i;
			}
		} 
	}
	return nMarkerNearest;
}

SIX.MarkerCollection.prototype.addMarker = function (marker)
{
	this.aMarkers[this.aMarkers.length] = marker;
	return marker;
}

SIX.MarkerCollection.prototype.deleteMarker = function (nIndex)
{
	if (nIndex < this.aMarkers.length)
	{
		var elemFeatureLayer = document.getElementById(this.map.featureLayerUid);
		if (elemFeatureLayer)
		{
			var marker = this.aMarkers[nIndex];
			var elemMarker = document.getElementById(marker.uid);
			if (elemMarker)
				elemFeatureLayer.removeChild(elemMarker);
		}
		this.aMarkers.splice(nIndex, 1);
		if (this.nSelectedMarker == nIndex)
			this.nSelectedMarker = -1;
	}
}

SIX.MarkerCollection.prototype.hideFeatures = function ()
{
	var elemFeatureLayer = document.getElementById(this.map.featureLayerUid);
	if (elemFeatureLayer)
		elemFeatureLayer.style.visibility = "hidden";
}

SIX.MarkerCollection.prototype.showFeatures = function ()
{
	var elemFeatureLayer = document.getElementById(this.map.featureLayerUid);
	if (elemFeatureLayer)
		elemFeatureLayer.style.visibility = "visible";
}

SIX.MarkerCollection.prototype.update = function ()
{
	var elemFeatureLayer = document.getElementById(this.map.featureLayerUid);
	if (elemFeatureLayer)
	{	
		var control = document[this.map.uid];
		var markerCollection = this;
		var nWidth = control.GetViewWidth();
		var nHeight = control.GetViewHeight();
		for (var i=0; i<this.aMarkers.length; i++)
		{
			var marker = this.aMarkers[i];
			var elemDiv = document.getElementById(marker.uid);
			if (elemDiv == null)
			{
				elemDiv = this.buildMarker(marker);
				elemFeatureLayer.appendChild(elemDiv);				
			}
			var aPoint = this.map.getPixelFromLL(marker.x, marker.y);
			if (aPoint)
			{			
				elemDiv.style.top = aPoint[1] + "px";
				elemDiv.style.left = aPoint[0] + "px";
				elemDiv.style.visibility = "visible";
			}
			else
			{
				elemDiv.style.visibility = "hidden";
			}
		}
		this.showFeatures();
	}
}

SIX.MarkerCollection.prototype.buildMarker = function (marker) 
{		
	var markerCollection = this;
	var elemDiv = SIX.Util.createElement("div", "markerSymbolContainer", marker.uid);
	elemDiv.ondblclick = function()
	{
		with(markerCollection)
		{
			markerCollection.onMouseDown(this.id);
		}
	}
	
	var elemImage = this.buildMarkerImage(marker);
	elemDiv.appendChild(elemImage);
	return elemDiv;		
}

SIX.MarkerCollection.prototype.buildMarkerImage = function (marker)
{
	var markerSymbol = this.getMarkerSymbol(marker.sSymbol);
	var elemImage = SIX.Util.createElement("img", "markerSymbol", marker.imageUid);
	elemImage.setAttribute("alt", marker.sTitle);
	elemImage.setAttribute("title", marker.sTitle);
	elemImage.setAttribute("src", markerSymbol.sImage);
	
	var sMouseOutSrc = markerSymbol.sImage;
	var sMouseOverSrc = markerSymbol.sImageOn;
	
	if (sMouseOverSrc != "")
	{
		elemImage.onmouseover = function () 
								{
									with ({sMouseOverSrc : sMouseOverSrc})
									{
										this.src = sMouseOverSrc;
									}
								}

		elemImage.onmouseout = function () 
								{
									with ({sMouseOutSrc : sMouseOutSrc})
									{
										this.src = sMouseOutSrc;
									}
								}
	}
	
	elemImage.style.left = -markerSymbol.nTiePointPixelX + "px";
	elemImage.style.top = -markerSymbol.nTiePointPixelY + "px";
	return elemImage;
}

SIX.MarkerCollection.prototype.action = function (action,moving,x,y) 
{			
	this.moveMarker(SIX.App.mouseCurrentAction.uid,moving,x,y);
}

SIX.MarkerCollection.prototype.moveMarker = function (sMarkerImageUid, moving, nPixelX, nPixelY) 
{
	var elemMarkerImage = document.getElementById(sMarkerImageUid);
	var elemMarker = getParent(elemMarkerImage, "div");
	if (elemMarker)
	{
		var sMarkerUid = elemMarker.id;
		if (moving)
		{
			var nLeft = nPixelX + this.nPixelX;
			var nTop = nPixelY + this.nPixelY;
			elemMarker.style.left = nLeft + "px";
			elemMarker.style.top = nTop + "px";
			var nMarkerIndex = this.getMarkerIndex(sMarkerUid);
			if (nMarkerIndex != -1)
			{
				var marker = this.aMarkers[nMarkerIndex];
				var aCoords = this.map.getLLFromPixel(nLeft, nTop);
				if (aCoords)
				{
					marker.x = aCoords[0];
					marker.y = aCoords[1];
				}		
			}			
		}
		else 
		{
			var nLeft = parseInt(elemMarker.style.left);
			var nTop = parseInt(elemMarker.style.top);
			this.nPixelX = nLeft - nPixelX;
			this.nPixelY = nTop - nPixelY;
		}
	}
}		

SIX.MarkerCollection.prototype.onMouseDown = function (sMarkerId)
{
	var elemImage = document.getElementById(sMarkerId);
	if (elemImage)
	{
		var nMarkerIndex = this.getMarkerIndex(sMarkerId);
		if (nMarkerIndex != -1)
		{
			this.map.showMarkerForm(nMarkerIndex);
		}
	}
	return false;	
}

SIX.MarkerCollection.prototype.buildMarkerList = function ()
{
	var elemMarkerListContainer = document.createElement("div");
	elemMarkerListContainer.setAttribute("id", this.markerListContainerUid);
	elemMarkerListContainer.style.display = "none";

	var elemMarkerListTitle = SIX.Util.createElement("div", "markerListTitle");
	var elemImageExpandCollapse = this.buildExpandCollapseMarkerListButton();
	elemMarkerListTitle.appendChild(elemImageExpandCollapse);	
	var textNodeMarkerListTitle = document.createTextNode("Place Markers");
	elemMarkerListTitle.appendChild(textNodeMarkerListTitle);
	elemMarkerListContainer.appendChild(elemMarkerListTitle);	
	
	var elemMarkerList = SIX.Util.createElement("ul", "markerList", this.markerListUid);
	elemMarkerList.style.display = "none";
	elemMarkerListContainer.appendChild(elemMarkerList);	
	return elemMarkerListContainer;	
}

SIX.MarkerCollection.prototype.updateMarkerList = function ()
{
	var elemMarkerListContainer = document.getElementById(this.markerListContainerUid);
	if (elemMarkerListContainer)
	{
		if (this.aMarkers.length)
			elemMarkerListContainer.style.display = "block";
		else
			elemMarkerListContainer.style.display = "none";
	}
	
	var elemMarkerList = document.getElementById(this.markerListUid);
	if (elemMarkerList)
	{
		elemMarkerList.innerHTML = "";
		for (var i=0; i<this.aMarkers.length; i++)
		{
			var marker = this.aMarkers[i];
			var elemListItem = SIX.Util.createElement("li", "markerListItem", marker.listItemUid);
			
			var elemImageSymbol = this.buildMarkerListItemSymbol(marker);
			elemListItem.appendChild(elemImageSymbol);
				
			var elemSpanText = SIX.Util.createElement("div", "markerListItemText");			
			var elemListItemTextNode = document.createTextNode(marker.sTitle);
			elemSpanText.appendChild(elemListItemTextNode);
			elemListItem.appendChild(elemSpanText);

			var elemImageDelete = this.buildMarkerListItemDeleteButton(marker);
			elemListItem.appendChild(elemImageDelete);

			elemMarkerList.appendChild(elemListItem);			
		}
	}
}

SIX.MarkerCollection.prototype.buildExpandCollapseMarkerListButton = function ()
{
	var nodeImage = SIX.Util.createElement("img", "markerListExpandCollapseButton");
	nodeImage.setAttribute("alt", "Show/hide markers");
	nodeImage.setAttribute("title", "Show/hide markers");
	nodeImage.setAttribute("src", "images/expandPlus.gif");	

	var markerCollection = this;
	nodeImage.onclick = function()
	{
		with ({markerCollection : markerCollection})
		{
			var elemMarkerList = document.getElementById(markerCollection.markerListUid);
			if (elemMarkerList)
			{
				if (elemMarkerList.style.display == "none")
				{
					elemMarkerList.style.display = "block";
					this.src = "images/collapseMinus.gif";
				}
				else
				{
					elemMarkerList.style.display = "none";
					this.src = "images/expandPlus.gif"; 
				}
			}
		}
	}
	return nodeImage;
}

SIX.MarkerCollection.prototype.buildMarkerListItemSymbol = function (marker)
{
	var nodeDiv =  SIX.Util.createElement("div", "markerListItemSymbolButtonContainer");
	var markerSymbol = this.getMarkerSymbol(marker.sSymbol)
	var sImagePath = markerSymbol.sImage;
	var nodeImage = SIX.Util.createElement("img", "markerListItemSymbolButton");
	nodeImage.setAttribute("alt", "");
	nodeImage.setAttribute("title", "");
	nodeImage.setAttribute("src", sImagePath);
	
	nodeDiv.appendChild(nodeImage);
	
	return nodeDiv;
}

SIX.MarkerCollection.prototype.buildMarkerListItemDeleteButton = function (marker)
{
	var nodeImageDelete = SIX.Util.createElement("img", "markerListItemDeleteButton");
	nodeImageDelete.setAttribute("alt", "Delete marker");
	nodeImageDelete.setAttribute("title", "Delete marker");
	nodeImageDelete.setAttribute("src", "images/delete.gif");

	var markerCollection = this;
	nodeImageDelete.onclick = function()
	{
		with ({markerCollection : markerCollection, marker : marker})
		{
			if (!confirm("Are you sure you want to delete this marker?"))
				return;
			var nMarkerIndex = markerCollection.getMarkerIndex(marker.uid);
			if (nMarkerIndex >= 0)
			{
				markerCollection.deleteMarker(nMarkerIndex);
				markerCollection.updateMarkerList();
			}
		}
	}
	
	return nodeImageDelete;
}

SIX.eValidContextExtents = {
	MINX : 150, 
	MAXX : 151, 
	MINY : -35, 
	MAXY : -34
}

SIX.ContextCollection = function (sId, sTitle, sDescription, sParams)
{
	this.uid = SIX.Uid.getUid("ContextCollection");
	this.containerUid = SIX.Uid.getUid("ContextCollectionContainer");
	this.allUid = SIX.Uid.getUid("ContextCollection");
	this.sId = sId;
	this.sTitle = sTitle;
	this.sDescription = sDescription;
	this.aContextIds = new Array();
	this.aContextListItemUids = new Array();
	this.parameters = new SIX.Parameters(sParams ? sParams : "");
	this.bCanDelete = this.parameters.getParameter("delete", "boolean", false);
	this.bCanCopy = this.parameters.getParameter("copy", "boolean", false);
	this.bCanEmail = this.parameters.getParameter("email", "boolean", true);
	this.bAllMaps = this.parameters.getParameter("allmaps", "boolean", false);
	this.nContextCollectionCookieExpiryDays = 365 * 100;
}

SIX.ContextCollection.prototype.getContext = function (sContextId)
{
	var contextCollection = this;
	var fnGetContextResponse = function (data)
	{
		with ({contextCollection: contextCollection, sContextId : sContextId})
		{
			if (data)
			{	
				var sContextJSON = data.geoJSON;
				var context = new SIX.Context(sContextId, "", "");
				context.fromGeoJSONObject(sContextJSON.parseJSON());
					
				var nodeListItem = contextCollection.buildContextListItem(context);
				if (nodeListItem)
				{
					var nodeSpanButtonGroup = SIX.Util.createElement("span", "contextButtonGroup");
					nodeListItem.appendChild(nodeSpanButtonGroup);

					if (contextCollection.bCanCopy)
					{
						var nodeImageCopy = contextCollection.buildCopyContextButton(context);
						nodeSpanButtonGroup.appendChild(nodeImageCopy);
					}
					
					if (contextCollection.bCanEmail)
					{
						var nodeAnchorEmail = contextCollection.buildEmailContextButton(context);
						nodeSpanButtonGroup.appendChild(nodeAnchorEmail);
					}
					
					if (contextCollection.bCanDelete)
					{
						var nodeImageDelete = contextCollection.buildDeleteContextButton(context);
						nodeSpanButtonGroup.appendChild(nodeImageDelete);
					}					

					var nodeImageExpandCollapse = contextCollection.buildExpandCollapseContextDetailButton(context);
					nodeListItem.appendChild(nodeImageExpandCollapse);

					var nodeAnchor = contextCollection.buildContextUrlLink(context);
					nodeListItem.appendChild(nodeAnchor);

					var nodeChildList = contextCollection.buildContextDetail(context);
					nodeListItem.appendChild(nodeChildList);
				}
			}
		}
	}	
	MapContextWS.getMapContext(sContextId, fnGetContextResponse);	
}

SIX.ContextCollection.prototype.buildExpandCollapseContextListButton = function (bExpanded)
{
	var nodeImageExpandCollapse = SIX.Util.createElement("img", "contextListExpandCollapseButton");
	var sAltText = "Show/hide " + this.sTitle;
	nodeImageExpandCollapse.setAttribute("alt", sAltText);
	nodeImageExpandCollapse.setAttribute("title", sAltText);
	nodeImageExpandCollapse.setAttribute("src", (bExpanded ? "images/collapseMinus.gif" : "images/expandPlus.gif"));	

	var contextCollection = this;
	nodeImageExpandCollapse.onclick = function()
	{
		with ({contextCollection : contextCollection})
		{
			var elemContextList = document.getElementById(contextCollection.uid);
			if (elemContextList)
			{
				if (elemContextList.style.display == "none")
				{
					elemContextList.style.display = "block";
					this.src = "images/collapseMinus.gif";
				}
				else
				{
					elemContextList.style.display = "none";
					this.src = "images/expandPlus.gif"; 
				}
			}
		}
	}
	return nodeImageExpandCollapse;
}

SIX.ContextCollection.prototype.buildContextListItem = function (context)
{
	var nodeListItem = null;
	var elemContextList = document.getElementById(this.uid);
	if (elemContextList)
	{
		var nodeListItemToInsertBefore = null;
		var nIndexOfContext = -1;
		for (var i=0; i<this.aContextIds.length; i++)
		{
			if (nIndexOfContext >= 0)
			{
				var listItem = document.getElementById(this.aContextListItemUids[i]);
				if (listItem)
				{
					nodeListItemToInsertBefore = listItem;
					break;
				}
			}
			else if (this.aContextIds[i] == context.sId)
			{
				nIndexOfContext = i;
			}
		}		

		nodeListItem = document.createElement("li");
		if (nIndexOfContext >= 0 && nIndexOfContext < this.aContextListItemUids.length)
			nodeListItem.setAttribute("id", this.aContextListItemUids[nIndexOfContext]);
		nodeListItem.setAttribute("class", "contextListItem");
		nodeListItem.setAttribute("className", "contextListItem");
		
		if (nodeListItemToInsertBefore)
			elemContextList.insertBefore(nodeListItem, nodeListItemToInsertBefore);
		else
			elemContextList.appendChild(nodeListItem);		
	}
	return nodeListItem;	
}

SIX.ContextCollection.prototype.buildContextDetail = function (context)
{
	var nodeChildList = SIX.Util.createElement("ul", "contextChildList");
	nodeChildList.style.display = "none";	
	
	var nodeChildListItemCreationDate = document.createElement("li");
	var sCreationDate = SIX.Util.timeStampToDate(context.sTimeStamp, "dd/mm/yy hh:mm:ss");
	var textNodeChildListItemCreationDate = document.createTextNode("Created: " + sCreationDate);	
	nodeChildListItemCreationDate.appendChild(textNodeChildListItemCreationDate);						
	nodeChildList.appendChild(nodeChildListItemCreationDate);												
											
	var nodeChildListItem = document.createElement("li");
	var textNodeChildListItem = document.createTextNode(context.sDescription);	
	nodeChildListItem.appendChild(textNodeChildListItem);						
	nodeChildList.appendChild(nodeChildListItem);
	return nodeChildList;
}

SIX.ContextCollection.prototype.buildContextUrlLink = function (context)
{
	var sUrl = this.buildContextUrl(context);
	var nodeAnchor = SIX.Util.createElement("a", "contextTitle");
	nodeAnchor.setAttribute("href", sUrl);
	nodeAnchor.setAttribute("target", "_blank");
	var sCreationDate = SIX.Util.timeStampToDate(context.sTimeStamp, "dd/mm/yy hh:mm:ss");
	var sAltText = "Created: " + sCreationDate + (context.sDescription == "" ? "" : ", " + context.sDescription);
	nodeAnchor.setAttribute("alt", sAltText);
	nodeAnchor.setAttribute("title", sAltText);
	var sTitle = context.sTitle;
	var aTitle = new Array();
	var sMaxCharsPerLine = 8;
	for (var i=0; i<sTitle.length; i+=sMaxCharsPerLine)
		aTitle[aTitle.length] = sTitle.substring(i, Math.min(i+sMaxCharsPerLine, sTitle.length))
	sTitle = aTitle.join("<br>");
	nodeAnchor.innerHTML = sTitle;
	return nodeAnchor;
}

SIX.ContextCollection.prototype.buildExpandCollapseContextDetailButton = function (context)
{
	var nodeImageExpandCollapse = SIX.Util.createElement("img", "contextExpandCollapseButton");
	nodeImageExpandCollapse.setAttribute("alt", "Show/hide map details");
	nodeImageExpandCollapse.setAttribute("title", "Show/hide map details");
	nodeImageExpandCollapse.setAttribute("src", "images/expandPlus.gif");	

	nodeImageExpandCollapse.onclick = function()
	{
		listItem = getParent(this, "li");
		for (var i=0;i<listItem.childNodes.length;i++) 
		{
			var childNode = listItem.childNodes[i];
			if (childNode.tagName && childNode.tagName.toLowerCase() == "ul")
			{ 
				if (childNode.style.display == "none")
				{
					childNode.style.display = "block";
					this.src = "images/collapseMinus.gif";
				}
				else
				{
					childNode.style.display = "none";
					this.src = "images/expandPlus.gif"; 
				}
			}
		}
	}
	return nodeImageExpandCollapse;
}					

SIX.ContextCollection.prototype.buildDeleteContextButton = function (context)
{
	var nodeImageDelete = SIX.Util.createElement("img", "contextButton");
	nodeImageDelete.setAttribute("alt", "Delete map");
	nodeImageDelete.setAttribute("title", "Delete map");
	nodeImageDelete.setAttribute("src", "images/delete.gif");	

	var contextCollection = this;
	nodeImageDelete.onclick = function()
	{
		with ({contextCollection : contextCollection, context : context})
		{
			if (!confirm("Are you sure you want to delete this map?"))
				return;
			contextCollection.deleteContext(context.sId);
		}
	}
	return nodeImageDelete;
}

SIX.ContextCollection.prototype.buildCopyContextButton = function (context)
{
	var nodeImageCopy = SIX.Util.createElement("img", "contextButton");
	nodeImageCopy.setAttribute("alt", "Copy to My Saved Maps");
	nodeImageCopy.setAttribute("title", "Copy to My Saved Maps");
	nodeImageCopy.setAttribute("src", "images/copy.gif");	

	var contextCollection = this;
	var sContextId = context.sId;
	
	nodeImageCopy.onclick = function()
	{
		with ({contextCollection : contextCollection, sContextId : sContextId})
		{
			if (!SIX.App.account.bLoggedIn)
			{
				var fnLoginCallback = function () 
				{
					with ({contextCollection : contextCollection, sContextId : sContextId})
					{	
						var fnGetMapCollectionCallback = function()
						{
							with ({contextCollection : contextCollection, sContextId : sContextId})
							{	
								contextCollection.copyContext(sContextId);
							}
						}			
						SIX.App.getMySavedMaps(fnGetMapCollectionCallback);
					}	
				}				
				SIX.App.account.fnLoginCallback = fnLoginCallback;
				SIX.App.account.showLogin();
			}
			else
			{
				contextCollection.copyContext(sContextId);
			}		
		}
	}

	return nodeImageCopy;
}

SIX.ContextCollection.prototype.copyContext = function (sContextId)
{
	var contextCollection = this;
	var fnCopyContextResponse = function (data)
	{
		with ({contextCollection: contextCollection, sContextId : sContextId})
		{
			if (data)
			{	
				var sContextJSON = data.geoJSON;
				var context = new SIX.Context(sContextId, "", "");
				context.fromGeoJSONObject(sContextJSON.parseJSON());
				if (SIX.App.aContextCollections.length)
				{
					var myContextCollection = SIX.App.aContextCollections[0];
					if (contextCollection != myContextCollection)
						myContextCollection.saveContext(context);
				}
			}
		}
	}	
	MapContextWS.getMapContext(sContextId, fnCopyContextResponse);	
}

SIX.ContextCollection.prototype.buildEmailContextButton = function (context)
{
	var sUrl = this.buildContextUrl(context);
	var sCreationDate = SIX.Util.timeStampToDate(context.sTimeStamp, "dd/mm/yy hh:mm:ss");
	var sSubject = "SIX Viewer Map: " + context.sTitle;
	var sBody = "Title: " + context.sTitle + "%0A%0A" + 
				"Created: " + sCreationDate + "%0A%0A" +  
				"Description: " + context.sDescription + "%0A%0A" +
				encodeURIComponent(sUrl);

	var nodeAnchorEmail = SIX.Util.createElement("a", "contextButton");
	nodeAnchorEmail.setAttribute("href", "mailto:?subject=" + sSubject + "&body=" + sBody);
	nodeAnchorEmail.style.border = 0;

	var nodeImageEmail = document.createElement("img");
	nodeImageEmail.setAttribute("alt", "Email map");
	nodeImageEmail.setAttribute("title", "Email map");
	nodeImageEmail.setAttribute("src", "images/email.gif");	
	nodeImageEmail.style.border = 0;
	nodeAnchorEmail.appendChild(nodeImageEmail);
	return nodeAnchorEmail;
}

SIX.ContextCollection.prototype.buildContextUrl = function (context)
{
	var sSrcUrl = decodeURIComponent(context.sSrcUrl);
	var nIndex = sSrcUrl.indexOf("?");
	var sQueryStringArgs = "context=" + context.sId;
	var sBaseUrl = "";

	if (nIndex == -1)
	{
		sBaseUrl = sSrcUrl;
	}
	else
	{
		sBaseUrl = sSrcUrl.substring(0, nIndex);
		var sArgs = sSrcUrl.substring(nIndex + 1, sSrcUrl.length);
		var aParams = sArgs.split("&");
		for (var i=0;i<aParams.length;i++) 
		{
			if (aParams[i].indexOf("=") != -1)
			{
				var aParamNameValuePair = aParams[i].split("=");
				if ((aParamNameValuePair[0].toLowerCase() == "context") ||
					(aParamNameValuePair[0].toLowerCase() == "contextcollection"))
					continue;
				sQueryStringArgs += "&" + aParamNameValuePair[0] + "=" + aParamNameValuePair[1];
			}	
		}
	}
	
	var sUrl = sBaseUrl + "?" + sQueryStringArgs;							
	sUrl = sUrl.replace("#", "");	
	return sUrl;
}

SIX.ContextCollection.prototype.saveContext = function (context)
{
	var context = context.toGeoJSONObject();
	var sJSONContext = context.toJSONString();
	
	var contextCollection = this;
	var fnAddContextResponse = function(data) 
	{
		with ({contextCollection : contextCollection})
		{
			contextCollection.addContext(data);
		}
	}
	MapContextWS.addMapContext(sJSONContext, SIX.eValidContextExtents.MINX, SIX.eValidContextExtents.MAXX, SIX.eValidContextExtents.MINY, SIX.eValidContextExtents.MAXY, fnAddContextResponse);
}

SIX.ContextCollection.prototype.deleteContext = function (sContextId)
{
	var contextCollection = this;
	
	var fnGetContextCollectionResponse = function(data) 
	{
		with ({contextCollection : contextCollection, sContextId : sContextId})
		{
			if (data)
			{
				var sContextArray = data.geoJSON;
				var aContextIds = new Array();
				var fnUpdateContextCollection = function(data)
				{
					with ({contextCollection : contextCollection})
					{
						contextCollection.updateContextList();
					}
				}
				aContextIds = sContextArray.parseJSON();
				for (var i=0; i<aContextIds.length; i++)
				{	
					if (aContextIds[i] == sContextId)
					{
						aContextIds.splice(i, 1);
						break;
					}
				}
				var sJSONContextArray = aContextIds.toJSONString();
				MapContextWS.updateMapContextCollection(contextCollection.sId, sJSONContextArray, SIX.eValidContextExtents.MINX, SIX.eValidContextExtents.MAXX, SIX.eValidContextExtents.MINY, SIX.eValidContextExtents.MAXY, fnUpdateContextCollection);
			}
		}		
	}	
	
	if (this.sId != "")
		MapContextWS.getMapContextCollection(this.sId, fnGetContextCollectionResponse);
}

SIX.ContextCollection.prototype.updateContextList = function ()
{
	var contextCollection = this;
	
	var fnUpdateContextList = function()
	{
		with ({contextCollection : contextCollection})
		{		
			contextCollection.aContextListItemUids = new Array();
			for (var i=0; i<contextCollection.aContextIds.length; i++)
				contextCollection.aContextListItemUids[i] = SIX.Uid.getUid("SavedMapListItem");
			
			var elemContextList = document.getElementById(contextCollection.uid);
			if (elemContextList)
				elemContextList.innerHTML = "";

			for (var i=0; i<contextCollection.aContextIds.length; i++)
				contextCollection.getContext(contextCollection.aContextIds[i]);

			var elemContextCollectionContainer = document.getElementById(contextCollection.containerUid);
			var elemAllMaps = document.getElementById(contextCollection.allUid);
			if (elemContextCollectionContainer)
			{
				if (contextCollection.aContextIds.length && contextCollection.bAllMaps)
				{		
					if (!elemAllMaps)
					{
						elemAllMaps = contextCollection.buildContextCollectionUrlLink();
						if (elemAllMaps)
							elemContextCollectionContainer.appendChild(elemAllMaps);
					}
				}
				else
				{
					if (elemAllMaps)
						elemContextCollectionContainer.removeChild(elemAllMaps);
				}
			}
		}	
	}
	
	var fnGetContextCollectionResponse = function(data) 
	{
		with ({contextCollection : contextCollection, fnUpdateContextList : fnUpdateContextList})
		{
			if (data)
			{
				var sContextArray = data.geoJSON;
				contextCollection.aContextIds = sContextArray.parseJSON();	
				fnUpdateContextList();
			}
		}
	}
	
	if (this.sId != "")
		MapContextWS.getMapContextCollection(this.sId, fnGetContextCollectionResponse);
	else 
		fnUpdateContextList();
}

SIX.ContextCollection.prototype.buildContextCollectionUrlLink = function ()
{
	var elemAllSavedMaps = SIX.Util.createElement("div", "allMaps", this.allUid);

	if (this.sId != "")
	{
		var sLocation = decodeURI(window.location.href);
		sLocation = sLocation.replace("#", "");

		var nIndex = sLocation.indexOf("?");
		var sQueryStringArgs = "contextcollection=" + this.sId;
		var sBaseUrl = "";

		if (nIndex == -1)
		{
			sBaseUrl = sLocation;
		}
		else
		{
			sBaseUrl = sLocation.substring(0, nIndex);
			var sArgs = sLocation.substring(nIndex + 1, sLocation.length);
			var aParams = sArgs.split("&");
			for (var i=0;i<aParams.length;i++) 
			{
				if (aParams[i].indexOf("=") != -1)
				{
					var aParamNameValuePair = aParams[i].split("=");
					if ((aParamNameValuePair[0].toLowerCase() == "context") ||
						(aParamNameValuePair[0].toLowerCase() == "contextcollection"))
						continue;
					sQueryStringArgs += "&" + aParamNameValuePair[0] + "=" + aParamNameValuePair[1];
				}	
			}
		}

		var sUrl = sBaseUrl + "?" + sQueryStringArgs;
		var sSubject = "All Saved Maps";
		var sBody = sUrl;
		
		var textNodeAllMaps = document.createTextNode("Email All Maps");
		elemAllSavedMaps.appendChild(textNodeAllMaps);

		var nodeAnchorEmail = SIX.Util.createElement("a", "allMapsButton");
		nodeAnchorEmail.setAttribute("href", "mailto:?subject=" + sSubject + "&body=" + sBody);
		nodeAnchorEmail.style.border = 0;

		var nodeImageEmail = document.createElement("img");
		nodeImageEmail.setAttribute("alt", "Email All Maps");
		nodeImageEmail.setAttribute("title", "Email All Maps");
		nodeImageEmail.setAttribute("src", "images/email.gif");	
		nodeImageEmail.style.border = 0;
		nodeAnchorEmail.appendChild(nodeImageEmail);
		elemAllSavedMaps.appendChild(nodeAnchorEmail);	
}	
	return elemAllSavedMaps;
}

SIX.ContextCollection.prototype.addContext = function (sContextId)
{
	var contextCollection = this;
	
	var fnAddContextCollectionResponse = function(data) 
	{
		with ({contextCollection : contextCollection, sContextId : sContextId})
		{
			if (data) 
			{
				var sContextCollectionId = data;
				for (var i=0; i<SIX.App.aContextCollections.length; i++)
				{
					if (SIX.App.aContextCollections[i] == contextCollection && contextCollection.sId == "")
						contextCollection.sId = sContextCollectionId;
				}
				contextCollection.updateContextList();
			}	
		}	
	}	
	
	var fnGetContextCollectionResponse = function(data) 
	{
		with ({contextCollection : contextCollection, sContextId : sContextId})
		{
			if (data)
			{
				var sContextArray = data.geoJSON;
				var aContextIds = new Array();
				var fnUpdateContextCollection = function(data)
				{
					with ({contextCollection : contextCollection})
					{
						contextCollection.updateContextList();
					}
				}
				aContextIds = sContextArray.parseJSON();	
				aContextIds[aContextIds.length] = sContextId;
				var sJSONContextArray = aContextIds.toJSONString();
				MapContextWS.updateMapContextCollection(contextCollection.sId, sJSONContextArray, SIX.eValidContextExtents.MINX, SIX.eValidContextExtents.MAXX, SIX.eValidContextExtents.MINY, SIX.eValidContextExtents.MAXY, fnUpdateContextCollection);
			}
		}		
	}	
	
	if (this.sId != "")
	{
		MapContextWS.getMapContextCollection(this.sId, fnGetContextCollectionResponse);
	}
	else
	{	
		var aContextIds = new Array();
		aContextIds[aContextIds.length] = sContextId;
		var sJSONContextArray = aContextIds.toJSONString();	
		MapContextWS.addMapContextCollection(sJSONContextArray, SIX.eValidContextExtents.MINX, SIX.eValidContextExtents.MAXX, SIX.eValidContextExtents.MINY, SIX.eValidContextExtents.MAXY, fnAddContextCollectionResponse);
	}	
}

SIX.Context = function (sId, sTitle, sDescription)
{
	this.sTimeStamp = "" + (new Date()).getTime();
	this.sId = sId;
	this.sVersion = "1.0.0";
	this.sTitle = sTitle;
	this.sDescription = sDescription;
	var channelCurrent = SIX.App.getCurrentChannel();
	this.sChannelId = channelCurrent.sId;
	var sSrcUrl = encodeURIComponent(decodeURI(window.location.href));
	this.sSrcUrl = sSrcUrl.replace("#", "");
	this.featureCollection = null;
	
	this.tlLat = 0;
	this.tlLon = 0;	
	this.brLat = 0;
	this.brLon = 0;
}

SIX.Context.prototype.setExtents = function (tlLat, tlLon, brLat, brLon)
{
	this.tlLat = tlLat;
	this.tlLon = tlLon;	
	this.brLat = brLat;
	this.brLon = brLon;
}

SIX.Context.prototype.toGeoJSONObject = function ()
{
	var geoJSONObject = new Object();
	geoJSONObject.timestamp = this.sTimeStamp;
	geoJSONObject.version = this.sVersion;
	geoJSONObject.title = this.sTitle;
	geoJSONObject.description = this.sDescription;
	geoJSONObject.srcurl = this.sSrcUrl;
	geoJSONObject.channel = this.sChannelId;
	geoJSONObject.bbox = new Array();
	geoJSONObject.bbox[0] = this.tlLon;
	geoJSONObject.bbox[1] = this.brLat;
	geoJSONObject.bbox[2] = this.brLon;
	geoJSONObject.bbox[3] = this.tlLat;
	geoJSONObject.featureCollection = this.featureCollection;
	return geoJSONObject;
}

SIX.Context.prototype.fromGeoJSONObject = function (geoJSONObject)
{
	this.sTimeStamp = geoJSONObject.timestamp;
	this.sVersion = geoJSONObject.version;
	this.sTitle = geoJSONObject.title;
	this.sDescription = geoJSONObject.description;
	this.sSrcUrl = geoJSONObject.srcurl;
	this.sChannelId = geoJSONObject.channel;
	this.tlLon = geoJSONObject.bbox[0];
	this.brLat = geoJSONObject.bbox[1];
	this.brLon = geoJSONObject.bbox[2];
	this.tlLat = geoJSONObject.bbox[3];
	this.featureCollection = geoJSONObject.featureCollection;
}

SIX.QueryPolygonCollection = function ()
{
	this.aPolygons = new Object();
	this.aPolygonsSelectionState = new Object();
	this.aPolygonsMetadata = new Object();
}

SIX.eMapCallbacks = {
	ONMOUSEDOWN : 1,
	ONMOUSEUP : 2,
	ONMOUSEMOVE : 3,
	ONEXTENTCHANGE : 4,
	ONLOAD : 5
}

/**
 * @class SIX.Map objects used to display maps within windows. So there will be one SIX.Map object per SIX.Window object displaying a map. 
 * Put another way, if there are two windows being used to display maps, then there will be two SIX.Map objects, one for each window. 
 * SIX.Map objects are attached to SIX.Catalog objects which contain details about the services used to display maps. 
 */
SIX.Map = function () 
{
	SIX.addRef(this);
	for (var i=0; i<SIX.App.channels.length; i++)
	{
		var channel = SIX.App.channels[i];
		channel.maps[channel.maps.length] = this;
	}

	this.div = null;

	this.name = "Map";
	this.loaded = false;
	this.width = 100;
	this.height= 100;
	this.width1K = this.width;		// will never be > 1023
	this.height1K = this.height;
	
	this.aHistory = new Array();		// stores extents in history
	this.bHistoryMove = false;
	this.nHistoryIndex = 0;
	this.nOldHistoryIndex = 0;
	
	this.uid = SIX.Uid.getUid("map");
	this.mapDivUid = SIX.Uid.getUid("mapDiv");
	this.featureLayerUid = SIX.Uid.getUid("featureLayer");
	this.containerUid = SIX.Uid.getUid("container");
	this.crosshairsUid = SIX.Uid.getUid("crosshairs");
	this.markerFormContainerUid = SIX.Uid.getUid("markerFormContainer");
	this.markerFormUid = SIX.Uid.getUid("markerForm");
	this.markerFormSymbolUid = SIX.Uid.getUid("markerFormSymbol");
	this.markerSymbolsContainerUid = SIX.Uid.getUid("markerSymbolsContainer");
	this.markerSymbolsUid = SIX.Uid.getUid("markerSymbols");
	this.markerFormDescriptionUid = SIX.Uid.getUid("markerFormDescription");
	this.markerFormDescriptionRichTextUid = SIX.Uid.getUid("markerFormDescription");
	this.sMarkerDescription = "";
	
	this.saveMapFormContainerUid = SIX.Uid.getUid("saveMapFormContainer");
	this.saveMapFormUid = SIX.Uid.getUid("saveMapForm");
	this.saveMapFormDescriptionUid = SIX.Uid.getUid("saveMapFormDescription");
	this.saveMapFormDescriptionRichTextUid = SIX.Uid.getUid("saveMapFormDescription"); 
	this.sMapDescription = "";	
	
	this.savedMapsUid = SIX.Uid.getUid("savedMaps"); 
	this.savedMapsContainerUid = SIX.Uid.getUid("savedMapsContainer"); 
	this.savedMapsTitleBarUid = SIX.Uid.getUid("savedMapsTitleBar"); 
	this.savedMapsBackgroundUid = SIX.Uid.getUid("savedMapsBackground"); 
	
	this.catalog = 0;
	this.status = 0;
	
	this.markerCollection = new SIX.MarkerCollection(this);
	
	this.sDefaultECWJP2Url = "";
	this.sDefaultECWJP2LayerName = SIX.Uid.getUid("default");
	this.coordSysDefaultECWJP2 = new SIX.CoordSys();
	this.coordSys = new SIX.CoordSys();

	this.nECWProgressPercent = 0;
	this.nGISOverlayProgressPercent = 0;
	this.tGISOverlayProgress = null;

	this.tlx = 0;				// world coordinates; might be Eastings/Northings
	this.tly = 0;
	this.brx = 0;
	this.bry = 0;
	this.envelope = new SIX.Envelope(this.tlx, this.bry, this.brx, this.tly);

	this.hLayersToUpdate = new SIX.Hash();
	
	//Each layer needs its topleft and bottom right coordinates converted. To avoid 
	//exceeding GET request length limitations (2083 characters under IE), we 
	//limit the number of layers to 20 (40 conversions).
	this.MAX_LAYERS_TO_UPDATE = 20;
	
	this.tlLatitude  = 90;
	this.tlLongitude = -180;
	this.brLatitude = -90;
	this.brLongitude = 180;
	
	this.bGeodeticExtentsUpdated = false;
	
	this.tlMaxLat = 0;
	this.tlMaxLon = 0;
	this.brMaxLat = 0;
	this.brMaxLon = 0;

	this.tlMaxX = 0;
	this.tlMaxY = 0;
	this.brMaxX = 0;
	this.brMaxY = 0;
	
	this.bMaxWorldExtentsUpdated = false;

	this.dInitialTopLeftLat = 0;
	this.dInitialTopLeftLon = 0;
	this.dInitialBottomRightLat = 0;
	this.dInitialBottomRightLon = 0;
	
	this.tlMaxOverlayLat = 0;
	this.tlMaxOverlayLon = 0;
	this.brMaxOverlayLat = 0;
	this.brMaxOverlayLon = 0;
	
	this.bDisplayBest = false;
	this.tlMaxDisplayBestLat = 0;
	this.tlMaxDisplayBestLon = 0;
	this.brMaxDisplayBestLat = 0;
	this.brMaxDisplayBestLon = 0;	
	
	this.dMaxOverlayScaleChangeFactor = 2.0;
	
	this.scale = 0;
	this.dBestScale = 0;
	this.nMouseMask = null;
	this.dMouseScreenX = null;
	this.dMouseScreenY = null;			
	this.dMouseWorldX = null;
	this.dMouseWorldY = null;
	this.mouseDown = false;

	this.redrawTimer = null;
	
	this.nQueryType = SIX.eQueryTypes.NONE;
	this.queryPoints = new Array();		// points forming the query area

	//Hot Spot SIX.Tool Parameters
	this.pointQueryZonesCenter = null;
	this.bQueryZonesVisible = true;
	this.sQueryZonesDistanceDisplayUnits = "m";
	this.dQueryZonesEndDistance = 1000.0;
	this.dQueryZonesIntervalDistance = 100.0;
	this.nMaxQueryZones = 50;
	this.aQueryZonesBoundaryColors = ["#FF0000"/*Red*/,"#0000FF"/*Blue*/,"#FFFF00"/*Yellow*/,"#00FF00"/*Green*/];
	this.aQueryZones = new Array();
		
	this.editLayer = false;
	this.editLayerName = SIX.Uid.getUid("VectorEditLayer");
	
	this.realtimeRoam = true;			// set to false to override realtime panning from a service
	this.bAllowExtentsUpdate = true;
	
	this.bRoamClip = false;				// boolean value, are we using the roaming window
	this.nRoamSize = 250;				// value for clipping region
	
	this.nNumReconnectAttempts = 0;
	this.tReconnect = null;
	this.bReconnecting = false;
	
	this.aQueryPolygonCollections = new Object();
	
	this.bChangingChannel = false;
	this.aLayerParams = new Object();
	this.childMap = null;
	this.parentMap = null;
	this.opacity = 1.0;
	this.tCancelSetExtentsTimeout = null;
	this.tSetExtentsInterval = null;
	
	this.bDoubleClick = false;
	this.tMouseDown = (new Date()).getTime();
	
	this.coordsCache = new SIX.Hash();
	this.MAX_COORDS_CACHE_SIZE = 1000;
	
	this.tStatusUpdate = null;
	this.hCallbackManagers = new SIX.Hash();
	this.aAttachedItems = new Array();
	this.sBackgroundColor = "#ffffff";
}

SIX.Map.prototype.executeCallbacks = function (nType)
{
	if (this.hCallbackManagers.hasItem(nType))
	{
		var callbackManager = this.hCallbackManagers.getItem(nType);
		var aArguments = arguments.length > 1 ? Array.prototype.slice.call(arguments, 1, arguments.length) : new Array();
		callbackManager.executeCallbacks.apply(callbackManager, aArguments);
	}
}

SIX.Map.prototype.addCallback = function (nType, method, data)
{
	var callbackManager = this.hCallbackManagers.hasItem(nType) ?  this.hCallbackManagers.getItem(nType) : new SIX.CallbackManager();
	callbackManager.addCallback(new SIX.Callback(this, method, (arguments.length >= 3 ? data : null)));
	this.hCallbackManagers.setItem(nType, callbackManager);
}

SIX.Map.prototype.getTopLeftMaxX = function ()
{
	if (!this.bMaxWorldExtentsUpdated)
		this.getMaxWorldExtents();
	return this.tlMaxX;
}

SIX.Map.prototype.getTopLeftMaxY = function ()
{
	if (!this.bMaxWorldExtentsUpdated)
		this.getMaxWorldExtents();
	return this.tlMaxY;
}

SIX.Map.prototype.getBottomRightMaxX = function ()
{
	if (!this.bMaxWorldExtentsUpdated)
		this.getMaxWorldExtents();
	return this.brMaxX;
}

SIX.Map.prototype.getBottomRightMaxY = function ()
{
	if (!this.bMaxWorldExtentsUpdated)
		this.getMaxWorldExtents();
	return this.brMaxY;
}

SIX.Map.prototype.getMaxWorldExtents = function ()
{
	//Top left, Top right, Bottom right, Bottom left
	var aExtentsLL = [[this.tlMaxLon, this.tlMaxLat],
					[this.brMaxLon, this.brMaxLat]];
	
	var aExtentsEN = this.convertCoords(this.datum(), "GEODETIC", aExtentsLL, this.datum(), this.projection());
	
	if (aExtentsEN)
	{		
		this.tlMaxX = aExtentsEN[0][0];
		this.tlMaxY  = aExtentsEN[0][1];
		
		this.brMaxX = aExtentsEN[1][0];
		this.brMaxY  = aExtentsEN[1][1];	
	}
	this.bMaxWorldExtentsUpdated = true;		
}

SIX.Map.prototype.getGeodeticExtents = function ()
{
	//Top left, Top right, Bottom right, Bottom left
	var aExtentsEN = [[this.tlx, this.tly],
					[this.brx, this.tly],
					[this.brx, this.bry],
					[this.tlx, this.bry]];
	
	var aExtentsLL = this.convertCoords(this.datum(), this.projection(), aExtentsEN, this.datum(), "GEODETIC");
	
	if (aExtentsLL)
	{
		this.tlLatitude  = aExtentsLL[0][1];
		this.tlLongitude = aExtentsLL[0][0];
		
		this.trLatitude  = aExtentsLL[1][1];
		this.trLongitude = aExtentsLL[1][0];
		
		this.brLatitude  = aExtentsLL[2][1];
		this.brLongitude = aExtentsLL[2][0];
		
		this.blLatitude  = aExtentsLL[3][1];
		this.blLongitude = aExtentsLL[3][0];
	}
	this.bGeodeticExtentsUpdated = true;
}

SIX.Map.prototype.getTopLeftLatitude = function ()
{
	if (!this.bGeodeticExtentsUpdated)
		this.getGeodeticExtents();
	return this.tlLatitude;
}

SIX.Map.prototype.getTopLeftLongitude = function ()
{
	if (!this.bGeodeticExtentsUpdated)
		this.getGeodeticExtents();
	return this.tlLongitude;
}

SIX.Map.prototype.getTopRightLatitude = function ()
{
	if (!this.bGeodeticExtentsUpdated)
		this.getGeodeticExtents();
	return this.trLatitude;
}

SIX.Map.prototype.getTopRightLongitude = function ()
{
	if (!this.bGeodeticExtentsUpdated)
		this.getGeodeticExtents();
	return this.trLongitude;
}

SIX.Map.prototype.getBottomLeftLatitude = function ()
{
	if (!this.bGeodeticExtentsUpdated)
		this.getGeodeticExtents();
	return this.blLatitude;
}

SIX.Map.prototype.getBottomLeftLongitude = function ()
{
	if (!this.bGeodeticExtentsUpdated)
		this.getGeodeticExtents();
	return this.blLongitude;
}

SIX.Map.prototype.getBottomRightLatitude = function ()
{
	if (!this.bGeodeticExtentsUpdated)
		this.getGeodeticExtents();
	return this.brLatitude;
}

SIX.Map.prototype.getBottomRightLongitude = function ()
{
	if (!this.bGeodeticExtentsUpdated)
		this.getGeodeticExtents();
	return this.brLongitude;
}

SIX.Map.prototype.updateStatus = function ()
{
	if (this.status)
		this.status.update();
}

SIX.Map.prototype.onMouseMove = function (mask, screenX, screenY, worldX, worldY)
{
	if (isNaN(screenX) || isNaN(screenY) || isNaN(worldX) || isNaN(worldY))
		return;	
	
	this.nMouseMask = mask;
	this.dMouseScreenX = screenX;
	this.dMouseScreenY = screenY;		
	this.dMouseWorldX = worldX;
	this.dMouseWorldY = worldY;	
			
	this.updateStatus();
	
	var mapToClip = this.getBottomMap();
	if (mapToClip.bRoamClip)
	    mapToClip.roamClip(mask, screenX, screenY, worldX, worldY);
	
	var channel = SIX.App.getCurrentChannel();
	channel.executeToolGroupCallbacks(SIX.eToolCallbacks.ONMAPMOUSEMOVE, this);
	this.executeCallbacks(SIX.eMapCallbacks.ONMOUSEMOVE);
}

SIX.Map.prototype.onMouseDown = function (mask, screenX, screenY, worldX, worldY)
{	
	if (isNaN(screenX) || isNaN(screenY) || isNaN(worldX) || isNaN(worldY))
		return;	
		
	var tMouseDown = (new Date()).getTime();
	this.bDoubleClick = (tMouseDown - this.tMouseDown) < 500 ? true : false;
	this.tMouseDown = tMouseDown;

	var control = document[this.uid];
	
	var mapToClip = this.getBottomMap();
	if (mapToClip.bRoamClip)
	    mapToClip.roamClip(mask, screenX, screenY, worldX, worldY);

	this.mouseDown = true;

	this.nMouseMask = mask;
	this.dMouseScreenX = screenX;
	this.dMouseScreenY = screenY;
	this.dMouseWorldX = worldX;
	this.dMouseWorldY = worldY;
	
	this.updateStatus();

	var channel = SIX.App.getCurrentChannel();
	channel.executeToolGroupCallbacks(SIX.eToolCallbacks.ONMAPMOUSEDOWN, this);
	this.executeCallbacks(SIX.eMapCallbacks.ONMOUSEDOWN);
}

SIX.Map.prototype.onMouseUp = function (mask, screenX, screenY, worldX, worldY)
{
	//The document.onmouseup (SIX.App.onMouseUp()) event does not fire under firefox when the mouse is over the control.
	//So we need to cancel any outstanding mouse actions in the control's OnMouseUp() callback.
	SIX.App.mouseCurrentAction = 0;
	if (SIX.App.nPreMouseActionPointerMode != -1)
	{
		this.setPointerMode(SIX.App.nPreMouseActionPointerMode);
		SIX.App.nPreMouseActionPointerMode = -1;
	}		
	
	if (isNaN(screenX) || isNaN(screenY) || isNaN(worldX) || isNaN(worldY))
		return;

	var control = document[this.uid];
	
	this.mouseDown = false;

	this.nMouseMask = mask;
	this.dMouseScreenX = screenX;
	this.dMouseScreenY = screenY;
	this.dMouseWorldX = worldX;
	this.dMouseWorldY = worldY;
	
	this.updateStatus();

	var channel = SIX.App.getCurrentChannel();
	channel.executeToolGroupCallbacks(SIX.eToolCallbacks.ONMAPMOUSEUP, this);
	this.executeCallbacks(SIX.eMapCallbacks.ONMOUSEUP);
}

SIX.Map.prototype.saveLayerParams = function (sName, sParams)
{
	var aParams = this.aLayerParams[sName];
	var aParts = sParams.split(";");
	
	for(var i=0; i<aParts.length; i++) 
	{
		var iIndex = aParts[i].indexOf("=");
		aParams[aParts[i].substr(0, iIndex).toLowerCase()] = aParts[i].substr(iIndex + 1);
	}		
}

SIX.Map.prototype.setLayerParameter = function (sName, sParams)
{
	var control = document[this.uid];
	var bRetVal = false;
	var nIndex = control.GetLayerIndex(sName);
	if (nIndex != -1)
	{
		//NCSJSView.SetLayerParameter() returns false for some parameters
		//so just return true if layer exists.
		bRetVal = true;
		control.SetLayerParameter(sName,sParams);
		var sLayerType = control.GetLayerType(sName);
		sLayerType = sLayerType.toLowerCase();
		if (sLayerType == "simplevector")
			this.saveLayerParams(sName, sParams);
	}
	return bRetVal;
}

SIX.Map.prototype.deleteLayer = function (sName)
{
	var control = document[this.uid];
	//map.bAllowExtentsUpdate may be set to false in the calling function
	//so we need to restore it's original value.
	var bAllowExtentsUpdate = this.bAllowExtentsUpdate;
	this.bAllowExtentsUpdate = false;
	var retVal = control.DeleteLayer(sName);
	if (retVal && this.aLayerParams[sName])
	{
		delete this.aLayerParams[sName];
		this.aLayerParams[sName] = null;
	}
	this.bAllowExtentsUpdate = bAllowExtentsUpdate;
	return retVal;
}

SIX.Map.prototype.addLayer = function (sType, sUrl, sName, sParams)
{
	var control = document[this.uid];
	//map.bAllowExtentsUpdate may be set to false in the calling function
	//so we need to restore it's original value.
	sType = ((SIX.App.bDHTMLControl && sType.toLowerCase() == "ecwjp2") ? "ECW" : sType);
	var bAllowExtentsUpdate = this.bAllowExtentsUpdate;
	this.bAllowExtentsUpdate = false;
	var nIndex = control.AddLayer(sType,sUrl,sName,sParams);
	this.bAllowExtentsUpdate = bAllowExtentsUpdate;
	if (nIndex != -1 && sType.toLowerCase() == "simplevector")
	{
		if (!this.aLayerParams[sName])
			this.aLayerParams[sName] = new Object();
		this.saveLayerParams(sName, sParams);
	}
	return nIndex;
}

SIX.Map.prototype.moveLayer = function (nSrcIndex, nDestIndex)
{
	var control = document[this.uid];
	//map.bAllowExtentsUpdate may be set to false in the calling function
	//so we need to restore it's original value.
	var bAllowExtentsUpdate = this.bAllowExtentsUpdate;
	this.bAllowExtentsUpdate = false;
	var bRetVal = control.MoveLayer(nSrcIndex, nDestIndex);
	this.bAllowExtentsUpdate = bAllowExtentsUpdate;
	return bRetVal;
}

SIX.Map.prototype.deleteAllLayers = function ()
{
	var control = document[this.uid];
	//map.bAllowExtentsUpdate may be set to false in the calling function
	//so we need to restore it's original value.
	var bAllowExtentsUpdate = this.bAllowExtentsUpdate;
	this.bAllowExtentsUpdate = false;
	control.DeleteAllLayers();
	if (this.aLayerParams)
	{
		delete this.aLayerParams;
		this.aLayerParams = new Object();
	}	
	this.bAllowExtentsUpdate = bAllowExtentsUpdate;
}

SIX.Map.prototype.cacheEcwpLayer = function (layer)
{
	if (SIX.App.bDHTMLControl)
		return;
	var control = document[this.uid];
	var nIndex = -1;
	for (var i=0; i<SIX.App.aCachedEcwpLayers.length; i++)
	{
		if (layer == SIX.App.aCachedEcwpLayers[i])
		{
			//Already cached, move it to the end of the aCachedEcwpLayers array.
			nIndex = i;
			SIX.App.aCachedEcwpLayers.splice(nIndex, 1, layer);
			break;
		}
	}
	if (nIndex == -1) //Not cached.
		SIX.App.aCachedEcwpLayers[SIX.App.aCachedEcwpLayers.length] = layer;
	
	if (SIX.App.aCachedEcwpLayers.length >= SIXConfig.MAX_CACHED_ECWP_CONNECTIONS)
	{
		var layerOldest = SIX.App.aCachedEcwpLayers.shift();//Removes first array element.

		//Make sure the layer hasn't since been turned on
		if ((SIXConfig.FREECACHE != "TRUE") && !(layerOldest.bInScale && layerOldest.bSelected))
		{      
			//When layer added with freecache parameter set to false is deleted, the ecwp connection remains
			//So to remove the ecwp connection, the layer must be deleted, then added with freecache set to true
			//and then deleted again.
			this.deleteLayer(layerOldest.uid);
			var nIndex = this.addLayer("ecwjp2",layerOldest.url,layerOldest.uid,"freecache=TRUE;transparency=0.0");
			if (nIndex >= 0)
				this.deleteLayer(layerOldest.uid);
		}											
	}
}

SIX.Map.prototype.uncacheEcwpLayer = function (layer)
{
	if (SIX.App.bDHTMLControl)
		return;
	var control = document[this.uid];
	for (var i=0; i<SIX.App.aCachedEcwpLayers.length; i++)
	{
		if (layer == SIX.App.aCachedEcwpLayers[i])
		{
			SIX.App.aCachedEcwpLayers.splice(i, 1);
			break;
		}
	}
}
	
SIX.Map.prototype.getDistanceBetweenCoords = function (dX1, dY1, dX2, dY2)
{
	var dDistance = (Math.sqrt(Math.pow(dX2 - dX1,2) + Math.pow(dY2 - dY1,2)));
	return dDistance;
}

SIX.Map.prototype.getDistanceBetweenLLCoords = function (dX1, dY1, dX2, dY2)
{
	var dDistance = -1;
	var sDatum = this.datum();
	var sProjection = this.projection();
	var aCoord1 = this.getENPoint(dX1, dY1, sDatum, sProjection);
	var aCoord2 = this.getENPoint(dX2, dY2, sDatum, sProjection);
	if (aCoord1 && aCoord1.length >= 2 && aCoord2 && aCoord2.length >= 2)
		dDistance = (Math.sqrt(Math.pow(aCoord2[0] - aCoord1[0],2) + Math.pow(aCoord2[1] - aCoord1[1],2)));
	return dDistance;
}

/**
*	PURPOSE: checks to make sure the vector edit layer exists
*/

SIX.Map.prototype.checkVectorLayerExists = function ()
{
	if (!SIX.App.bDHTMLControl)
	{
		var control = document[this.uid];
		
		if (control.GetLayerIndex(this.editLayerName) == -1)	// need to add vector edit layer
		{
			var bAdd = this.addLayer("SIMPLEVECTOR", "", this.editLayerName,"visibility=true;color=#ff0000;fillcolor=#00FF00;linewidth=1");
			control.SetLayerTransparency(this.editLayerName, "#00ff00", 0.3);
		}
	}
}

SIX.Map.prototype.addQueryPoint = function (worldX, worldY, screenX, screenY) 
{
	var point = new Object;
	point.worldX = worldX; 
	point.worldY = worldY;
	point.screenX = screenX; 
	point.screenY = screenY;
	this.queryPoints[this.queryPoints.length] = point;
	return point;
}

/**
*	PURPOSE: sets up document to watch for escape key
*/
SIX.Map.prototype.cancelOnEscape = function ()
{
	if( document.captureEvents ) 
		{
    //non IE
    	if( Event.KEYUP ) 
		{
        //NS 4, NS 6+, Mozilla 0.9+
        document.captureEvents( Event.KEYUP );
    	}
	}
	document.onkeyup = this.keyPressed;
}

/**
*	PURPOSE: listens for escape key, delete polygon drawing if heard.
*/
SIX.Map.prototype.keyPressed = function (e) {
    if( !e ) {
        //if the browser did not pass the event information to the
        //function, we will have to obtain it from the event register
        if( window.event ) {
            //DOM
            e = window.event;
        } else {
            //TOTAL FAILURE, WE HAVE NO WAY OF REFERENCING THE EVENT
            return;
        }
    }
    if( typeof( e.which ) == 'number' ) {
        //NS 4, NS 6+, Mozilla 0.9+, Opera
        e = e.which;
    } else if( typeof( e.keyCode ) == 'number'  ) {
        //IE, NS 6+, Mozilla 0.9+
        e = e.keyCode;
    } else if( typeof( e.charCode ) == 'number'  ) {
        //also NS 6+, Mozilla 0.9+
        e = e.charCode;
    } else {
        //TOTAL FAILURE, WE HAVE NO WAY OF OBTAINING THE KEY CODE
        return;
    }
    if (e == 27) // 27 is escape key
		{
		var channel = SIX.App.getCurrentChannel();
		channel.maps[0].clearDrawingObjects();
		channel.maps[0].queryPoints.length = 0;
		document.onkeyup = null;
		}
}

SIX.Map.prototype.drawQuery = function ( worldX, worldY, screenX, screenY ) {
	if (this.nQueryType != SIX.eQueryTypes.NONE)
	{
		var control = document[this.uid];
		var edit = "";
		
		this.cancelOnEscape();
		
		switch(this.nQueryType) 
		{
			case SIX.eQueryTypes.RECTANGLE :
			case SIX.eQueryTypes.POINT_OR_RECTANGLE:
				if( this.queryPoints.length == 1 ) 
				{
					var point = this.queryPoints[0];
					if (Math.abs(screenX - point.screenX) > 6 || Math.abs(screenY - point.screenY) > 6) 
					{
						edit = "filledpolygon1=" +
						point.worldX + "," + point.worldY + "|" + point.worldX + "," + worldY + "|" +
					   	worldX + "," + worldY + "|" + worldX + "," + point.worldY;							
					
						if (SIX.App.bDHTMLControl)
						{
							var elemEditLayer = document.getElementById(this.editLayerName);
							if (elemEditLayer)
							{	
								elemEditLayer.style.display = "block";
								var nWidth = Math.abs(Math.round(screenX) - Math.round(point.screenX));
								var nHeight = Math.abs(Math.round(screenY) - Math.round(point.screenY));
								var nLeft = Math.min(Math.round(screenX), Math.round(point.screenX));
								var nTop = Math.min(Math.round(screenY), Math.round(point.screenY));
								elemEditLayer.style.top = nTop + "px";
								elemEditLayer.style.left = nLeft + "px";
								elemEditLayer.style.width = nWidth + "px";
								elemEditLayer.style.height = nHeight + "px";
								elemEditLayer.style.visibility = "visible";				
							}
						}
					}
				}
			break;
			default : 
			break;
		}
		if( edit != "" && this.editLayer) {
			this.setLayerParameter(this.editLayerName,edit);
		}
	}
}

SIX.Map.prototype.initQuery = function (tool)
{
	var aServices = tool.getAttachedServices();
	if (aServices)
	{
		var channel = SIX.App.getCurrentChannel();
		for (var i=0; i<aServices.length; i++) 
		{
			var service = aServices[i];	
			if (service.queries) 
			{				
				//Turn on associated theme layers if query.bAlwaysOn set to true
				for (var j=0; j<service.queries.aQueries.length; j++)  
				{
					var query = service.queries.aQueries[j];
					var mapService = query.mapService;
					if (mapService && (mapService.nCurrentUI != eServiceUI.NONE || (mapService.parentService && mapService.parentService.nCurrentUI != eServiceUI.NONE))) 
					{
						for (var k=0; k<mapService.layers.length; k++)
						{
							var layer = mapService.layers[k];
							if ((query.sMapServiceLayer == layer.id) && !layer.bSelected && query.bAlwaysOn)
							{
								mapService.layerVisibleNoUpdate(layer);								
								mapService.access.updateMap(this);		
								this.hiddenLayers(mapService);
								mapService.build();
								break;									
							}		
						}
					}		
				}		
			}				
		}		
		SIX.App.onWindowResize();
	}
}

/**
* 	PURPOSE: clears drawing objects from the screen
*/
SIX.Map.prototype.clearDrawingObjects = function () 
{		
	var control = document[this.uid];
	this.setLayerParameter(this.editLayerName,"filledpolygon1=;filledcircle1=;polyline2=");
	var elemEditLayer = document.getElementById(this.editLayerName);
	if (elemEditLayer)
	{
		elemEditLayer.style.visibility = "hidden";
		elemEditLayer.style.display = "none";
	}
}

SIX.Map.prototype.cancelSetExtentsTimeout = function ()
{
	if (this.tCancelSetExtentsTimeout)
	{
		clearTimeout(this.tCancelSetExtentsTimeout);
		this.tCancelSetExtentsTimeout = null;
	}
	if (this.tSetExtentsInterval)
	{
		clearInterval(this.tSetExtentsInterval);
		this.tSetExtentsInterval = null;
	}
}

SIX.Map.prototype.setExtentsFromMap = function (map)
{
	var control = document[this.uid];
	control.SetExtents(map.tlx, map.tly, map.brx, map.bry);
}

SIX.Map.prototype.onExtentChange = function (tlx, tly, brx, bry)
{
	if (isNaN(tlx) || isNaN(tly) || isNaN(brx) || isNaN(bry) || !this.bAllowExtentsUpdate)
		return;

	var nPointerMode = this.getPointerMode();
	
	this.markerCollection.hideFeatures();
	
	if (this.aHistory.length)
	{
		//The following logic hides labels while they are being rendered.
		//We only want to do this when the user zooms in or out by a factor of 2.
		//This prevents labels being hidden when the view is panned. 
		//Note: The extents prior to zooming are obtained from the extents history array (this.aHistory).
		//Extents are only added to this array once zooming has stopped for a short period of time. 
		//We do NOT compare the extents passed to successive OnExtentChange() calls as OnExtentChange() 
		//is called many times during a single zoom.
		var aExtent = this.aHistory[this.bHistoryMove ? this.nOldHistoryIndex : this.nHistoryIndex];
		var dPreviousWidth = Math.abs(aExtent[3] - aExtent[1]);
		var dCurrentWidth = Math.abs(brx - tlx);
		var bHideOverlays = ((dCurrentWidth * this.dMaxOverlayScaleChangeFactor < dPreviousWidth) ||
							(dPreviousWidth * this.dMaxOverlayScaleChangeFactor < dCurrentWidth));
		if (bHideOverlays)
		{
			//Note: The pointer mode changes from ROAM to ZOOM when dragging on view with both mouse-buttons depressed. 
			this.setGISOverlayProgress(0);
	
			for(var i=0;i<this.catalog.services.length;i++) 
			{
				var svc = this.catalog.services[i];	
				if (!svc.bFeatureServer)
				{
					//Following logic hides labels while they are being rendered.
					//We only want to do this when the user zooms in or out.
					this.setLayerParameter(svc.uid,"visibility=FALSE");
					for (var j=0; j<svc.aServices.length; j++)
						this.setLayerParameter(svc.aServices[j].uid,"visibility=FALSE");
				}		
			}
		}
	}

	if (!this.bHistoryMove && !this.bChangingChannel && (tlx == this.tlx) && (tly == this.tly) && (brx == this.brx) && (bry == this.bry))
		return;
	
	this.tlx = tlx;
	this.tly = tly;
	this.brx = brx;
	this.bry = bry;
	
	this.envelope.setMinX(this.tlx);
	this.envelope.setMinY(this.bry);
	this.envelope.setMaxX(this.brx);
	this.envelope.setMaxY(this.tly);
		
	this.bGeodeticExtentsUpdated = false;		

	if (this.loaded)
	{
		this.dMouseWorldX = this.tlx + (this.brx - this.tlx) / 2;
		this.dMouseWorldY = this.tly + (this.bry - this.tly) / 2;	
		
		this.scale = this.getScaleFromExtents(this.tlx, this.tly, this.brx, this.bry);

		var channel = SIX.App.getCurrentChannel();
		channel.executeToolGroupCallbacks(SIX.eToolCallbacks.ONMAPEXTENTCHANGE, this);

		this.executeCallbacks(SIX.eMapCallbacks.ONEXTENTCHANGE);
	}

	for (var i=0; i<this.catalog.services.length; i++) 
	{
		var svc = this.catalog.services[i];	
		if (svc.access instanceof SIX.Access.ECWP)
		{
			for (var j=0; j<svc.layers.length; j++)
			{
				var layer = svc.layers[j];
				this.setLayerClipRegion(layer);

				for (var k=0; k<layer.aLayers.length; k++)
				{
					var layerChild = layer.aLayers[k];
					this.setLayerClipRegion(layerChild);
				}
			}
		}		
	}

	if (this.redrawTimer != null)
	{
		clearTimeout(this.redrawTimer);
		this.redrawTimer = null;
	}
		
	if (this.loaded)
	{
		this.redrawTimer = setTimeout(this.myself + ".loadMapEx()", 750);
	}
	else 
	{
		this.loadMap();
		if ((this.dInitialTopLeftLat == 0) && (this.dInitialTopLeftLon == 0) && (this.dInitialBottomRightLat == 0) && (this.dInitialBottomRightLon == 0))
			this.setExtentsAll();//Set map to maximum extents on first load.
		else 
			this.setExtents(this.dInitialTopLeftLat, this.dInitialTopLeftLon, this.dInitialBottomRightLat, this.dInitialBottomRightLon);

		var catalog = this.catalog;	
		if (catalog.currentSearchService && (catalog.currentSearchService.currentSearch > -1))
		{
			var search = catalog.currentSearchService.searches[catalog.currentSearchService.currentSearch];
			setTimeout(catalog.currentSearchService.myself + ".initializeCurrentSearch()", 0);
		}	
	}
}

SIX.Map.prototype.setLayerClipRegion = function (layer)
{
	if (!SIX.App.bDHTMLControl)
	{
		//Transparency mask used to hide data outside image region for plugin control.
		this.setLayerColorMask(layer);
	}
	else
	{
		if (layer.aClipRegion.length && !layer.bOTDF)
		{		
			//DHTML control allows region clipping via the ImageX inregion parameter. 
			var control = document[this.uid];
			if (control.GetLayerIndex(layer.uid) == -1)
				return;				
			//If the OTDF server is always used for this layer, then we can't use the ImageX inregion parameter, so don't do inregion check.
			if (this.inLayerClipRegion(layer))
			{	
				//Map is entirely within the image region, so do not apply the transparency mask.
				control.SetLayerParameter(layer.uid, "imagetype=jpg;otdf=" + (layer.sOTDFPath != "" ? "true" : "false") + ";clipregion=");
			}
			else
			{
				var sClipRegion = layer.getClipRegionStr();
				if (sClipRegion != "")
					control.SetLayerParameter(layer.uid, "imagetype=png;otdf=false;clipregion=" + sClipRegion);
			}
		}		
	}
}

SIX.Map.prototype.inLayerClipRegion = function (layer)
{
	var bInClipRegion = false;
	if (SIX.App.bDHTMLControl)
	{
		//DHTML control allows region clipping via the ImageX inregion parameter. 
		var control = document[this.uid];
		if (control.GetLayerIndex(layer.uid) == -1)
			return false;
		if (layer.aClipRegion.length)
		{				
			var aPoints = new Array();
			
			var aLayerTileWorldExtents = control.GetLayerTileWorldExtents(layer.uid, this.tlx, this.tly, this.brx, this.bry);
			aPoints[aPoints.length] = new SIX.Point(aLayerTileWorldExtents[0], aLayerTileWorldExtents[1]);//Top left
			aPoints[aPoints.length] = new SIX.Point(aLayerTileWorldExtents[2], aLayerTileWorldExtents[1]);//Top right
			aPoints[aPoints.length] = new SIX.Point(aLayerTileWorldExtents[2], aLayerTileWorldExtents[3]);//Bottom right
			aPoints[aPoints.length] = new SIX.Point(aLayerTileWorldExtents[0], aLayerTileWorldExtents[3]);//Bottom left					
			
			var bInClipRegion = layer.clipRegionEnvelope.containsPoints(aPoints);
			
			if (bInClipRegion)
				bInClipRegion = SIX.Util.pointsInRegion(layer.aClipRegion, aPoints);
		}		
	}
	return bInClipRegion;
}

SIX.Map.prototype.setLayerColorMask = function (layer) 
{
	var control = document[this.uid];
	if (control.GetLayerIndex(layer.uid) == -1)
		return;
	if (layer.aColorsToMask.length && (layer.sColorMask != ""))
	{
		var pointTopLeft = new SIX.Point(this.getTopLeftLongitude(), this.getTopLeftLatitude());
		var pointTopRight = new SIX.Point(this.getTopRightLongitude(), this.getTopRightLatitude());
		var pointBottomRight = new SIX.Point(this.getBottomRightLongitude(), this.getBottomRightLatitude());
		var pointBottomLeft = new SIX.Point(this.getBottomLeftLongitude(), this.getBottomLeftLatitude());
		var aPoints = new Array();
		aPoints[aPoints.length] = pointTopLeft;
		aPoints[aPoints.length] = pointTopRight;
		aPoints[aPoints.length] = pointBottomRight;
		aPoints[aPoints.length] = pointBottomLeft;		
	
		if (layer.containsPoints(aPoints))
		{	
			//Map is entirely within the image region, so do not apply the transparency mask.
			this.setLayerParameter(layer.uid, "transmask=");
			for (var i=0; i<layer.aColorsToMask.length; i++)
				control.SetLayerTransparency(layer.uid,layer.aColorsToMask[i],1.0);
		}
		else
		{
			this.setLayerParameter(layer.uid, "transmask=" + layer.sColorMask);
			for (var i=0; i<layer.aColorsToMask.length; i++)
			{
				//NOTE: Transparency must set to a non-zero number under Firefox because passing a transparency of 0.0 to the SetLayerTransparency() function will fail under Firefox.
				control.SetLayerTransparency(layer.uid,layer.aColorsToMask[i], (SIX.App.isIE ? 0.0 : 0.001));	
			}
		}
	}
}

SIX.Map.prototype.loadMapEx = function ()
{
	if (SIX.App.bDHTMLControl)
	{
		if (this.AJAXGetLLExtents)
			this.AJAXGetLLExtents.Cancel();
		
		var fnCallback = function (map, aExtentsLL) {
			if (aExtentsLL)
			{
				map.tlLatitude  = aExtentsLL[0][1];
				map.tlLongitude = aExtentsLL[0][0];
				
				map.trLatitude  = aExtentsLL[1][1];
				map.trLongitude = aExtentsLL[1][0];
				
				map.brLatitude  = aExtentsLL[2][1];
				map.brLongitude = aExtentsLL[2][0];
				
				map.blLatitude  = aExtentsLL[3][1];
				map.blLongitude = aExtentsLL[3][0];
			}
			map.bGeodeticExtentsUpdated = true;
			map.loadMap();
		}	

		//Top left, Top right, Bottom right, Bottom left
		var aExtentsEN = [[this.tlx, this.tly],
						[this.brx, this.tly],
						[this.brx, this.bry],
						[this.tlx, this.bry]];		
		this.AJAXGetLLExtents = this.convertCoordsAsync(this.datum(), this.projection(), aExtentsEN, this.datum(), "GEODETIC", fnCallback, this);
	}
	else
	{
		this.loadMap();
	}
}

SIX.Map.prototype.loadMap = function () {
	var control = document[this.uid];
	this.bChangingChannel = false;
	//control.SetParameters("bAlwaysSendMouseEvents=TRUE");

	this.width  = this.width1K 	= control.GetViewWidth();
	this.height = this.height1K	= control.GetViewHeight();
	
	//control.GetViewWidth() and control.GetViewHeight() sometimes return 0 under Firefox before the control is initialized. 
	if ((this.width <= 0) || (this.height <= 0))
		return;
	
	if( this.width1K > 1023 ) {
		this.height1K = Math.floor(this.height1K * (1023 / this.width1K));
		this.width1K = 1023;
	}
	if( this.height1K > 1023 ) {
		this.width1K = Math.floor(this.width1K * (1023 / this.height1K));
		this.height1K = 1023;
	}

	this.loaded = true;

	this.dMouseWorldX = this.tlx + (this.brx - this.tlx) / 2;
	this.dMouseWorldY = this.tly + (this.bry - this.tly) / 2;	
	
	this.scale = this.getScaleFromExtents(this.tlx, this.tly, this.brx, this.bry);
	
	this.resetGISOverlayMapsLoadState();
	
	if( !this.catalog )
		return;
		
	if ((this.nHistoryIndex < (this.aHistory.length - 1)) && (this.nHistoryIndex >= 0) && !this.bHistoryMove)
		this.clearForwardHistory();
		
	if (!this.bHistoryMove)
	{
		var bAddExtentsToHistory = true;
		if (this.aHistory.length)
		{
			var aOldExtents = this.aHistory[this.aHistory.length - 1];
			//Check to ensure two identical extents are not included as adjacent elements in history array.
			if ((aOldExtents[0] == this.tly) && (aOldExtents[1] == this.tlx) &&
				(aOldExtents[2] == this.bry) && (aOldExtents[3] == this.brx))
				bAddExtentsToHistory = false;
		}
		if (bAddExtentsToHistory)
		{
			this.aHistory[this.aHistory.length] = new Array(this.tly, this.tlx, this.bry, this.brx);
			this.nOldHistoryIndex = this.nHistoryIndex;
			this.nHistoryIndex = (this.aHistory.length - 1);
		}
	}
	else
	{
		this.bHistoryMove = false;	
	}
	
	var dBestScale = 0;

	for(var i=0;i<this.catalog.services.length;i++) 
	{
		var service = this.catalog.services[i];
		if (service.bFeatureServer)
			service.updateMap(this);
		else
		{
			if ((service.nCurrentUI == eServiceUI.NONE) && !(service.access instanceof SIX.Access.ECWP))
			{
				if (!service.layers.length)
				{
					if (service.nLayersLoadState != SIX.eLayersLoadState.LOADING)
					{	
						service.nLayersLoadState = SIX.eLayersLoadState.NOT_LOADED;					
						service.updateMap(this);
						service.build();
					}
				}
				else
				{
					if( this.hiddenLayers(service) || (service.loaded == false) ) 
					{
						service.updateMap(this);	// will also do a loadMap
						service.build();
					}
					else
					{
						service.loadMap(this);
					}	
				}
			}
			else 
			{
				if (!service.layers.length)
				{
					if (service.nLayersLoadState != SIX.eLayersLoadState.LOADING)
					{
						service.nLayersLoadState = SIX.eLayersLoadState.NOT_LOADED;							
						service.updateMap(this);
						if (service.nCurrentUI == eServiceUI.LAYERS)
							service.build();
					}
				}
				else		
				{
					if( this.hiddenLayers(service) || (service.loaded == false) ) 
					{	// something changed, so update service				
						service.updateMap(this);	// will also do a loadMap
						if (service.nCurrentUI == eServiceUI.LAYERS)
							service.build();
					}
					else
					{
						service.loadMap(this);
					}
				}
			}
			var dServiceBestScale = service.getBestScale(this);
			if (dServiceBestScale > 0 && (dBestScale == 0 || dServiceBestScale < dBestScale))
				dBestScale = dServiceBestScale;
		}
	}
	this.setBestScale(dBestScale);
		
	this.updateStatus();
	
	this.markerCollection.update();

	var channel = SIX.App.getCurrentChannel();
	channel.executeToolGroupCallbacks(SIX.eToolCallbacks.ONMAPLOAD, this);
	this.executeCallbacks(SIX.eMapCallbacks.ONLOAD);		
}

SIX.Map.prototype.layerInScale = function (layer) 
{
	var bInScale = true; 
	var dScale = layer.service.getScale(this);
	
	if (layer.minScale || layer.maxScale)
	{
		if ((layer.minScale && dScale < layer.minScale) ||
			(layer.maxScale && dScale > layer.maxScale))
			bInScale = false; 
	}	
	else
	{
		for (var i=0; i<layer.aLayers.length; i++)
		{
			bInScale = false;
			if (this.layerInScale(layer.aLayers[i]))
			{
				bInScale = true;
				break;
			}
		}	
	}		
	return bInScale;
}

SIX.Map.prototype.updateLayerWorldExtents = function ()
{
	var map = this;
	var fnCallback = function (callbackData, aExtentsEN) {	
		with ({map : map})
		{
			var aLayerUids = callbackData.aLayerUidsSubset;
			if (aExtentsEN && aLayerUids && (aExtentsEN.length == 2 * aLayerUids.length))
			{
				for (var i=0; i<aLayerUids.length; i++)
				{
					var layer = map.hLayersToUpdate.getItem(aLayerUids[i]);
					if (layer)
					{
						var nIndex = i * 2;
						layer.envelope = new SIX.Envelope(aExtentsEN[nIndex][0], aExtentsEN[nIndex+1][1], aExtentsEN[nIndex+1][0], aExtentsEN[nIndex][1]);
						map.hLayersToUpdate.removeItem(aLayerUids[i]);
					}
				}
			}
			if (callbackData.nEnd < callbackData.aLayerUids.length)
			{	
				callbackData.nEnd = (callbackData.nStart + map.MAX_LAYERS_TO_UPDATE <= callbackData.aLayerUids.length ? callbackData.nStart + map.MAX_LAYERS_TO_UPDATE : callbackData.aLayerUids.length); 
				if (callbackData.nStart < callbackData.nEnd)
				{
					callbackData.aLayerUidsSubset = callbackData.aLayerUids.slice(callbackData.nStart, callbackData.nEnd);
					var aGeoCoordsSubset = callbackData.aGeoCoords.slice(2 * callbackData.nStart, 2 * callbackData.nEnd);	
					callbackData.nStart = callbackData.nEnd;
					map.convertCoordsAsync(map.coordSys.sDatum, "geodetic", aGeoCoordsSubset, map.coordSys.sDatum, map.coordSys.sProjection, fnCallback, callbackData);
				}
			}				
		}						
	}
	
	var aGeoCoords = new Array();
	var aLayerUids = new Array();
	for (var i in this.hLayersToUpdate.items)
	{
		if (this.hLayersToUpdate.items.hasOwnProperty(i))
		{
			var layer = this.hLayersToUpdate.items[i];
			var bUpdateLayer = false;
			if (!layer.envelope && (layer.tlLatitude || layer.tlLongitude || layer.brLatitude || layer.brLongitude))
			{
				if (this.coordSys)
				{
					var coordSys = null;
					if (layer.coordSys && layer.coordSys.initialized())
						coordSys = layer.coordSys;
					else if (layer.parentLayer && layer.parentLayer.coordSys && layer.parentLayer.coordSys.initialized())
						coordSys = layer.parentLayer.coordSys;
					
					if (coordSys && coordSys.equals(this.coordSys))
					{
						aGeoCoords[aGeoCoords.length] = new Array(layer.tlLongitude, layer.tlLatitude);
						aGeoCoords[aGeoCoords.length] = new Array(layer.brLongitude, layer.brLatitude);
						aLayerUids[aLayerUids.length] = layer.uid;
						bUpdateLayer = true;
					}	
				}		
			}
			if (!bUpdateLayer)
				this.hLayersToUpdate.removeItem(i);
		}
	}			
	
	var nStart = 0;
	var nEnd = 0;

	if (nEnd < aLayerUids.length)
	{	
		nEnd = (nStart + this.MAX_LAYERS_TO_UPDATE <= aLayerUids.length ? nStart + this.MAX_LAYERS_TO_UPDATE : aLayerUids.length); 
		if (nStart < nEnd)
		{
			var callbackData = {nStart : nStart, nEnd : nEnd, aLayerUids : aLayerUids, aGeoCoords : aGeoCoords, aLayerUidsSubset : null};
			callbackData.aLayerUidsSubset = aLayerUids.slice(nStart, nEnd);
			var aGeoCoordsSubset = aGeoCoords.slice(2 * nStart, 2 * nEnd);	
			callbackData.nStart = callbackData.nEnd;
			this.convertCoordsAsync(this.coordSys.sDatum, "geodetic", aGeoCoordsSubset, this.coordSys.sDatum, this.coordSys.sProjection, fnCallback, callbackData);
		}
	}	
}

SIX.Map.prototype.layerInExtents = function (layer) 
{
	var bInExtents = true;
	if (layer.tlLatitude || layer.tlLongitude || layer.brLatitude || layer.brLongitude) 
	{
		if (!layer.envelope)
		{
			if (SIX.App.bDHTMLControl)
			{	
				if (!this.hLayersToUpdate.getItem(layer.uid) && layer.service.access instanceof SIX.Access.ECWP)
				{
					this.hLayersToUpdate.setItem(layer.uid, layer);
					if (this.tUpdateLayerWorldExtents)
						clearTimeout(this.tUpdateLayerWorldExtents)
					this.tUpdateLayerWorldExtents = setTimeout(this.myself + ".updateLayerWorldExtents()", 1000);
				}
			}	
				
			//Create a buffer around the existing lat/lon extents and test this as a first pass
			var dBufferLon = 0.5 * (layer.brLongitude - layer.tlLongitude);
			var dBufferLat = 0.5 * (layer.tlLatitude - layer.brLatitude);
			var dTopLeftLonWithBuffer = layer.tlLongitude - dBufferLon;
			var dTopLeftLatWithBuffer = layer.tlLatitude + dBufferLat;
			var dBottomRightLonWithBuffer = layer.brLongitude + dBufferLon;
			var dBottomRightLatWithBuffer = layer.brLatitude - dBufferLat;		
			
			// layer must be at least partially inside map extents area	
			if (!((((dTopLeftLonWithBuffer <= this.getTopLeftLongitude()) && (dBottomRightLonWithBuffer >= this.getTopLeftLongitude())) || 
				((dTopLeftLonWithBuffer >= this.getTopLeftLongitude()) && (dTopLeftLonWithBuffer <= this.getBottomRightLongitude()))) && 
				(((dTopLeftLatWithBuffer >= this.getTopLeftLatitude()) && (dBottomRightLatWithBuffer <= this.getTopLeftLatitude())) || 
				((dTopLeftLatWithBuffer <= this.getTopLeftLatitude()) && (dTopLeftLatWithBuffer >= this.getBottomRightLatitude())))))
			{
				bInExtents = false;
			}
			else if (this.coordSys)
			{
				var coordSys = null;
				if (layer.coordSys && layer.coordSys.initialized())
					coordSys = layer.coordSys;
				else if (layer.parentLayer && layer.parentLayer.coordSys && layer.parentLayer.coordSys.initialized())
					coordSys = layer.parentLayer.coordSys;
				
				if (coordSys && coordSys.equals(this.coordSys))
				{				
					var aExtentsLL = [[layer.tlLongitude, layer.tlLatitude],[layer.brLongitude, layer.brLatitude]];
					var aExtentsEN = this.convertCoords(coordSys.sDatum, "GEODETIC", aExtentsLL, coordSys.sDatum, coordSys.sProjection);
					var aTopLeft = (aExtentsEN && aExtentsEN.length == 2 ? aExtentsEN[0] : null);
					var aBottomRight = (aExtentsEN && aExtentsEN.length == 2 ? aExtentsEN[1] : null);
					if (aTopLeft && aTopLeft.length >= 2 && aBottomRight && aBottomRight.length >= 2)
					{
						layer.envelope = new SIX.Envelope(aTopLeft[0], aBottomRight[1], aBottomRight[0], aTopLeft[1]);
					}
				}
			}
		}
		
		if (layer.envelope && !layer.envelope.intersects(this.envelope))
			bInExtents = false;
	}
	else
	{
		for (var i=0; i<layer.aLayers.length; i++)
		{
			bInExtents = false;
			if (this.layerInExtents(layer.aLayers[i]))
			{
				bInExtents = true;
				break;
			}
		}	
	}
	
	return bInExtents;
}

SIX.Map.prototype.setChildLayersInScale = function (layer)
{
	var bChanged = false;
	for (var j=0; j<layer.aLayers.length; j++)
	{
		var layerChild = layer.aLayers[j];
		var bInScaleOld = layerChild.bInScale;
		//Set child layer's bInScale flag to same as parent layer if scale not explicitly defined for child layer.
		layerChild.bInScale = (layerChild.hasScale() ? true : layer.bInScale);
		if (!layer.service.bLayersOutOfScaleEnabled && layerChild.bInScale)
			layerChild.bInScale = this.layerInScale(layerChild);			

		if (!layer.service.bLayersOutsideExtentsEnabled && layerChild.bInScale)
			layerChild.bInScale = this.layerInExtents(layerChild);
				
		if ((bInScaleOld != layerChild.bInScale) || this.setChildLayersInScale(layerChild))
		{
			bChanged = true;
			layerChild.executeCallbacks(SIX.eLayerCallbacks.ONCHANGEISINSCALE);
		}
	}
	return bChanged;		
}

SIX.Map.prototype.hiddenLayers = function (service, bHideUnselectedLayers) 
{
	var changed = false;
	
	if (service.layers.length) 
	{
		for (var i=0; i<service.layers.length; i++) 
		{
			var layer = service.layers[i];
			var bInScale = true;
			if (this.scale && (service.loaded || service.access instanceof SIX.Access.ECWP) && this.loaded)
			{
				if (!service.bLayersOutOfScaleEnabled)
					bInScale = this.layerInScale(layer);			

				if (!service.bLayersOutsideExtentsEnabled && bInScale)
					bInScale = this.layerInExtents(layer);
			}
			
			if (bInScale != layer.bInScale)
			{
				changed = true;
				layer.bInScale = bInScale;
				layer.executeCallbacks(SIX.eLayerCallbacks.ONCHANGEISINSCALE);
			}
			
			if (this.scale && (service.loaded || service.access instanceof SIX.Access.ECWP) && this.loaded && this.setChildLayersInScale(layer))
				changed = true;	
		}
		
		if (service.bDisplayBest && service.sortLayersInBestOrder(this))
			changed = true;
		
		for (var i=0; i<service.allLayers.length; i++) 
		{
			var layer = service.allLayers[i];
			var bInList = true;
			
			if (service.bShowUnselectedLayers || layer.bSelected)
				layer.bHidden = false;
			else if (!layer.bSelected && bHideUnselectedLayers)
				layer.bHidden = true;
				
			if (layer.bHidden)
				bInList = false;
			else if (service.bHideOutOfScaleLayers)
				bInList = layer.bInScale;
			
			if (bInList != layer.bInList)
			{
				changed = true;
				layer.bInList = bInList;
			}
		}

		if (!changed)
		{
			var coordSys = new SIX.CoordSys();
			for( var i=0; i<service.layers.length; i++ ) 
			{
				var layer = service.layers[i];
				
				if(layer.bInScale && layer.bSelected) 
				{
					if (!coordSys.initialized()) 
					{
						coordSys = layer.coordSys;
					}
					else if (!coordSys.equals(layer.coordSys))
					{
						changed = true;
						break;
					}
				}
			}
		}
	}
	
	return changed;
}

/**
*	NOTE: The OnError callback is currently called only in the event of an ECWP connection lost error.  
*/
SIX.Map.prototype.onError = function (sLayerName, nNum, sCode, sDesc)
{
	if (this.bReconnecting) return;
	var channelCurrent = SIX.App.getCurrentChannel();
	if (sLayerName && (sLayerName != ""))
	{
		if (nNum == ERROR_CONNECTION_LOST) // Connection lost
		{ 
			/*
			sErrorMsg = "SIX.Layer '" + sLayerName +
					"' has lost connection to Image Web Server.\n" +
					"Only locally cached data will be viewable."
			alert(sErrorMsg);
			*/
			
			var layerInError;
			var control = document[this.uid];
			var sLayerType = control.GetLayerType(sLayerName);
			sLayerType = sLayerType.toLowerCase();
			if ((sLayerType == "ecw") || (sLayerType == "jp2"))	
			{
				for (var i=0; i<this.catalog.services.length; i++) 
				{
					var service = this.catalog.services[i];
					if (service.access instanceof SIX.Access.ECWP)
					{
						for (var j=0; j<service.layers.length; j++) 
						{
							var layer = service.layers[j];
							if (layer.uid == sLayerName)
							{
								layerInError = layer;
								var requestResponse = new SIX.RequestResponse(layer.uid, layer.url, "", "", 0, 0, 0, 0, 0, SIXConfig.eRequestResponseStatus.FAILURE);
								service.statistics.addRequestResponse(requestResponse);
								if (channelCurrent.console) 
									channelCurrent.console.updateHealth(service);
							
								if (this.tReconnect)
									clearTimeout(this.tReconnect);

								this.bReconnecting = true;
								this.tReconnect = setTimeout(this.myself + ".reconnectEcwpLayers();", SIXConfig.ECWP_RECONNECTION_INITIAL_TIMEOUT);	
								
								break;
							}
						}	
					}
					if (layerInError) break;
				}
			}
		} 
		else 
		{
			//alert(sLayerName + " - " + sCode + "(" + nNum + "), " + sDesc);
		}
	}
	else
	{
		//alert(sLayerName + " - " + sCode + "(" + nNum + "), " + sDesc);		
	}
	
}

SIX.Map.prototype.reconnectEcwpLayers = function ()
{
	if (this.nNumReconnectAttempts == SIXConfig.ECWP_RECONNECTION_MAX_ATTEMPTS)
	{
		this.bReconnecting = false;
		this.nNumReconnectAttempts = 0;
		alert("The connection to Image Web Server has been lost.\nPlease check your Internet connection and then reload the page.");		
		return;
	}
	this.nNumReconnectAttempts++;
    var control = document[this.uid];
    var channelCurrent = SIX.App.getCurrentChannel();
	for (var i=0; i<this.catalog.services.length; i++) 
	{
		var service = this.catalog.services[i];
		if (service.access instanceof SIX.Access.ECWP)
		{
			var layerTop = null;
			for (var j=0; j<service.layers.length; j++)
			{
				var layer = service.layers[service.layersOrder[j]];
				if (layer.bSelected)
				{
					layerTop = layer;
					break;
				}
			}
			
			for (var j=service.layers.length-1; j>=0; j--) 
			{   
				var layer = service.layers[service.layersOrder[j]];
				var sLayerName = layer.uid;
				var sLayerUrl = layer.url;
				
				if (layer.bSelected && sLayerUrl != "" && sLayerName != "")
				{
					var sFreecache = SIXConfig.FREECACHE;
					var sTransparency = (layer == layerTop) ? service.transparency : "1.0";
				       
					this.deleteLayer(sLayerName);        
					var sParams = "freecache=TRUE;transparency=" + sTransparency + (layer.bOTDF ? ";otdf=true" : ""); 
					var nIndex = this.addLayer("ecwjp2",sLayerUrl,sLayerName,sParams);
				 
					if ((nIndex >= 0) && (sFreecache != "TRUE"))
					{
						sParams = "freecache=" + sFreecache + ";transparency=" + sTransparency + (layer.bOTDF ? ";otdf=true" : "");
						this.deleteLayer(sLayerName);
						nIndex = this.addLayer("ecwjp2",sLayerUrl,sLayerName,sParams);
					}
					var requestResponse = new SIX.RequestResponse(sLayerName, sLayerUrl, "", "", 0, 0, 0, 0, 0, (nIndex >= 0 ? SIXConfig.eRequestResponseStatus.SUCCESS : SIXConfig.eRequestResponseStatus.FAILURE));
					service.statistics.addRequestResponse(requestResponse);
					
					if (channelCurrent.console) 
						channelCurrent.console.updateHealth(service);		
					
					if (nIndex == -1)
					{
						if (this.tReconnect)
							clearTimeout(this.tReconnect);
						this.bReconnecting = true;	
						this.tReconnect = setTimeout(this.myself + ".reconnectEcwpLayers()", SIXConfig.ECWP_RECONNECTION_REPEAT_TIMEOUT);
						return;
						//alert("SIX.Layer '" + layer.name + "' has lost connection to Image Web Server.\nPlease check your Internet connection and then reload the page.");            
					}
				}	
			}	
			this.bReconnecting = false;
			this.nNumReconnectAttempts = 0;
			service.access.orderLayers(this);	
			this.update();								
		}
	}
}

SIX.Map.prototype.onLayerResponse = function (layerName, url, body, action, tlx, tly, brx, bry, response )
{
	if (!this.catalog || isNaN(tlx) || isNaN(tly) || isNaN(brx) || isNaN(bry))
		return;				
		
	var channelCurrent = SIX.App.getCurrentChannel();						
	for(var i=0;i<this.catalog.services.length;i++) 
	{
		var catalogService = this.catalog.services[i];
		var bGetFeaturesResponse = false;
		if (catalogService.queries)
		{
			for (var j=0;j<catalogService.queries.aQueries.length;j++)
			{
				if (catalogService.queries.aQueries[j].uid == layerName)
				{
					bGetFeaturesResponse = true;
					break;
				}	
			}
		}
		
		var service = catalogService;

		for (var j=0; j<catalogService.aServices.length; j++)
		{
			if (catalogService.aServices[j].uid == layerName)
			{
				service = catalogService.aServices[j];
				break;
			}
		}
		
		if (( service.uid == layerName ) || bGetFeaturesResponse)
		{									
			if(response.substr(0, ("NCS_OVERLAY_FAILURE").length) == "NCS_OVERLAY_FAILURE") 
			{
				var bReloadingLayers = false;
				if (service.nLayersLoadState == SIX.eLayersLoadState.LOADING)
				{
					if (response.toLowerCase().indexOf("could not connect to host") != -1) 
					{ 
						if (!service.nNumConsecutiveLoadLayersFailed)
							service.tLoadLayers = setTimeout("" + service.myself + ".clearLoadLayersTimer()", SIXConfig.LOAD_LAYERS_TIMEOUT);
						
						if (service.tLoadLayers)
						{
							service.nNumConsecutiveLoadLayersFailed++;
							service.loadLayers(this);	
							bReloadingLayers = true;
						}
						else
						{
							service.nNumConsecutiveLoadLayersFailed = 0;
						}
					}
				}			
			
				if (!bReloadingLayers)
				{
					SIX.App.popupAlert(service.name + " (" + service.access.name + ") Error.",
									"The map server reported an error.",
									"Overlay Error\nURL: " + url + "\nBody: " + body + "\nResponse: " + response);
					service.nMapLoadState = SIX.eMapLoadState.LOAD_ERROR;
				}
				
				var requestResponse = new SIX.RequestResponse(layerName, url, body, action, tlx, tly, brx, bry, response, SIXConfig.eRequestResponseStatus.FAILURE);	
				service.statistics.addRequestResponse(requestResponse);	
				
				if (channelCurrent.console)
				{
					channelCurrent.console.addMessage(requestResponse.getConsoleMessage());
					channelCurrent.console.updateHealth(service);
				}				
				return;
			}
			
			if (service.nLayersLoadState == SIX.eLayersLoadState.LOADING)
			{
				service.nNumConsecutiveLoadLayersFailed = 0;
				service.clearLoadLayersTimer();
			}
			
			var nStatus = SIXConfig.eRequestResponseStatus.SUCCESS;
			var bValidResponse = service.access.responseMap(this, layerName, url, body, action, tlx, tly, brx, bry, response);	
			if (!bValidResponse) nStatus = service.getRequestResponseStatus(response);	
								
			var requestResponse = new SIX.RequestResponse(layerName, url, body, action, tlx, tly, brx, bry, response, nStatus);	
			service.statistics.addRequestResponse(requestResponse);	
				
			if (channelCurrent.console)
			{
				if (nStatus != SIXConfig.eRequestResponseStatus.SUCCESS) //Only displaying error messages.
					channelCurrent.console.addMessage(requestResponse.getConsoleMessage());
				channelCurrent.console.updateHealth(service);
			}	

			if (!service.bFeatureServer)
			{
				//After zooming in, the onLayerResponse() function may get called when an image is returned
				//from a previous GET_IMAGE request at a lower zoom-level.  Loading this image into the 
				//GISOverlay layer will result in a pixelated display.  To prevent this from happening, the 
				//following logic checks the current view extents against the extents passed in to the
				//onLayerResponse() function, and only makes the GISOverlay layer visible again if the 
				//scales represented by these extents differ by less than a factor of 2. 
				var dMapWidth = Math.abs(this.brx - this.tlx);
				var dLayerWidth = Math.abs(brx - tlx);
				if ((dMapWidth * this.dMaxOverlayScaleChangeFactor < dLayerWidth) || 
					(dLayerWidth * this.dMaxOverlayScaleChangeFactor < dMapWidth))
					return;			

				if (service.nCurrentUI != eServiceUI.NONE)
					service.build();
				else if (service.parentService)
					service.parentService.build();

				if(response == "NCS_OVERLAY_SUCCESS") 
				{
					//Actual GISOverlay image response.
					var control = document[this.uid];
	
					var sParamVisibility = this.getLayerParameter(service.uid,"visibility");
					
					if (sParamVisibility.toLowerCase() == "false")
					{	
						var sParamURL = this.getLayerParameter(service.uid,"url");	
						var sParamTransparency = this.getLayerParameter(service.uid,"transparency");
						var sParamBands = this.getLayerParameter(service.uid,"bands");
						var sParamMode = this.getLayerParameter(service.uid,"mode");
						var sParamBody = this.getLayerParameter(service.uid,"body");
						var sParamAction = this.getLayerParameter(service.uid,"action");
						var sParamWorldTLX = this.getLayerParameter(service.uid,"worldtlx");		
						var sParamWorldTLY = this.getLayerParameter(service.uid,"worldtly");	
						var sParamWorldBRX = this.getLayerParameter(service.uid,"worldbrx");	
						var sParamWorldBRY = this.getLayerParameter(service.uid,"worldbry");				  	
						
						var sParams = "mode=" + sParamMode + ";" + 
									"body=" + sParamBody + ";" + 
									"action=" + sParamAction + ";" + 
									"worldTLX=" + sParamWorldTLX + ";" + 
									"worldTLY=" + sParamWorldTLY + ";" + 
									"worldBRX=" + sParamWorldBRX + ";" +  
									"worldBRY=" + sParamWorldBRY + ";" + 									
									"url=" + sParamURL + ";" +  
									"transparency=" + sParamTransparency + ";" +
									"bands=" + sParamBands + ";"; 	
					
						var nOriginalIndex = control.GetLayerIndex(service.uid);
						if (this.deleteLayer(service.uid))
						{
							var nIndex = this.addLayer("GISOverlay", "", service.uid, sParams);
							if (nIndex != -1)
							{	
								if (nOriginalIndex != -1)
									this.moveLayer(nIndex, nOriginalIndex);
								
								control.SetLayerTransparency(service.uid, "#", service.transparency);
								if (service.colorMask) 
								{
									var mask = "transmask=#" + service.colorMask.toString(16);
									this.setLayerParameter(service.uid, mask);
								}
								var color;
								for (color in service.colors)
								{
									if (service.colors.hasOwnProperty(color))
										control.SetLayerTransparency(service.uid,color,service.colors[color].percent/100);
								}
								break;
							}	
						}
					}
				
					service.nMapLoadState = SIX.eMapLoadState.LOADED;
					var objGISOverlayMapsLoadStats = this.getGISOverlayMapsLoadStats();
					var nPercent = Math.round(100 * objGISOverlayMapsLoadStats.nNumGISOverlayMapsLoaded/objGISOverlayMapsLoadStats.nNumGISOverlayMapsRequested);

					if (this.tGISOverlayProgress != null)
					{
						clearTimeout(this.tGISOverlayProgress);
						this.tGISOverlayProgress = null;
					}	
					this.tGISOverlayProgress = setTimeout("" + this.myself + ".setGISOverlayProgress(" + nPercent + ")", 500);
				}
			}
			break;
		}
	}
}

/**
*	PURPOSE: Returns an object containing the following attributes:
*
*	nNumGISOverlayMapsLoaded		- Number of GISOverlay maps that were requested and have already loaded.
*	nNumGISOverlayMapsLoading		- Number of GISOverlay maps that were requested and are still loading.
*	nNumGISOverlayMapsErrorLoading	- Number of GISOverlay maps that were requested but failed to load.
*	nNumGISOverlayMapsRequested		- Total number of GISOverlay maps that were requested.
*
*/
SIX.Map.prototype.getGISOverlayMapsLoadStats = function ()
{
	var objGISOverlayMapsLoadStats = new Object();
	objGISOverlayMapsLoadStats.nNumGISOverlayMapsLoaded = 0;
	objGISOverlayMapsLoadStats.nNumGISOverlayMapsLoading = 0;
	objGISOverlayMapsLoadStats.nNumGISOverlayMapsErrorLoading = 0;
	objGISOverlayMapsLoadStats.nNumGISOverlayMapsRequested = 0;
	
	for(var i=0;i<this.catalog.services.length;i++) 
	{
		var svc = this.catalog.services[i];	
		if (!svc.bFeatureServer)
		{
			if (svc.aServices.length) 
			{
				for (var j=0; j<svc.aServices.length; j++)
				{
					if (svc.aServices[j].nMapLoadState == SIX.eMapLoadState.LOADED)
						objGISOverlayMapsLoadStats.nNumGISOverlayMapsLoaded++;
					if (svc.aServices[j].nMapLoadState == SIX.eMapLoadState.LOADING)
						objGISOverlayMapsLoadStats.nNumGISOverlayMapsLoading++;						
					if (svc.aServices[j].nMapLoadState == SIX.eMapLoadState.LOAD_ERROR)
						objGISOverlayMapsLoadStats.nNumGISOverlayMapsErrorLoading++;						
				}
			}
			else
			{
				if (svc.nMapLoadState == SIX.eMapLoadState.LOADED)
					objGISOverlayMapsLoadStats.nNumGISOverlayMapsLoaded++;
				if (svc.nMapLoadState == SIX.eMapLoadState.LOADING)
					objGISOverlayMapsLoadStats.nNumGISOverlayMapsLoading++;						
				if (svc.nMapLoadState == SIX.eMapLoadState.LOAD_ERROR)
					objGISOverlayMapsLoadStats.nNumGISOverlayMapsErrorLoading++;					
			}
		}		
	}

	objGISOverlayMapsLoadStats.nNumGISOverlayMapsRequested = objGISOverlayMapsLoadStats.nNumGISOverlayMapsLoaded + 
															objGISOverlayMapsLoadStats.nNumGISOverlayMapsLoading + 
															objGISOverlayMapsLoadStats.nNumGISOverlayMapsErrorLoading;
	return objGISOverlayMapsLoadStats;
}

SIX.Map.prototype.resetGISOverlayMapsLoadState = function ()
{
	for (var i=0;i<this.catalog.services.length;i++) 
	{
		var svc = this.catalog.services[i];	
		if (!svc.bFeatureServer)
		{
			if (svc.aServices.length) 
			{
				for (var j=0; j<svc.aServices.length; j++)
					svc.aServices[j].nMapLoadState = SIX.eMapLoadState.NOT_LOADED;	
			}
			else
			{
				svc.nMapLoadState = SIX.eMapLoadState.NOT_LOADED;				
			}
		}		
	}
}

SIX.Map.prototype.onPercentComplete = function (nPercent)
{
	this.nECWProgressPercent = nPercent;
	this.setECWProgress(nPercent);
}

SIX.Map.prototype.init = function() {
    this.div = document.createElement("div");
    this.div.style.width = "100%";
    this.div.style.height = "100%";

    // output during init not build to prevent timing problems

    if (this.parentMap) {
        var divViewContainer = document.createElement("div");
        divViewContainer.setAttribute("id", this.mapDivUid);
        divViewContainer.style.backgroundColor = this.sBackgroundColor;
        divViewContainer.style.position = "absolute";
        divViewContainer.style.top = "0px";
        divViewContainer.style.left = "0px";
        divViewContainer.style.width = "100%";
        divViewContainer.style.height = "100%";
        if (this.opacity < 1) {
            var dOpacity = this.opacity;
            dOpacity = dOpacity > 0.99 ? 0.99 : dOpacity;
            dOpacity = dOpacity < 0 ? 0 : dOpacity;
            divViewContainer.style.opacity = dOpacity;
            divViewContainer.style.filter = "alpha(opacity=" + (dOpacity * 100) + ")";
        }
        this.div.appendChild(divViewContainer);
    }
    else {
        var divViewContainer = this.div;
    }

    if (SIX.App.bDHTMLControl) {
        var control = new NCSJSView("100%", "100%", divViewContainer);
        document[this.uid] = control;
        control.SetCallbackName("onMouseDown", "onMouseDownHook()");
        control.SetCallbackName("onMouseMove", "onMouseMoveHook()");
        control.SetCallbackName("onMouseUp", "onMouseUpHook()");
        control.SetCallbackName("onExtentChange", "onExtentChangeHook()");
        control.SetCallbackName("onPercentComplete", "onPercentCompleteHook()");
        control.SetCallbackName("onLayerResponse", "onLayerResponseHook()");
        control.SetCallbackName("onError", "onErrorHook()");
        if (this.parentMap) {
            var parentControl = document[this.parentMap.uid];
            control.Attach(parentControl);
            control.SetGeolinkMode(GM_WINDOW);
        }
    }
    else {
        var aArgs = new Array(17);
        aArgs[0] = this.uid;
        aArgs[1] = "100%";
        aArgs[2] = "100%";

        aArgs[3] = PARAM_VIEW_ONMOUSEDOWN;
        aArgs[4] = this.myself + ".onMouseDown()";

        aArgs[5] = PARAM_VIEW_ONMOUSEMOVE;
        aArgs[6] = this.myself + ".onMouseMove()";

        aArgs[7] = PARAM_VIEW_ONMOUSEUP;
        aArgs[8] = this.myself + ".onMouseUp()";

        aArgs[9] = PARAM_VIEW_ONEXTENTCHANGE;
        aArgs[10] = this.myself + ".onExtentChange()";

        aArgs[11] = PARAM_VIEW_ONLAYERRESPONSE;
        aArgs[12] = this.myself + ".onLayerResponse()";

        aArgs[13] = PARAM_VIEW_ONPERCENTCOMPLETE;
        aArgs[14] = this.myself + ".onPercentComplete()";

        aArgs[15] = PARAM_VIEW_ONERROR;
        aArgs[16] = this.myself + ".onError()";

        var sViewHtml = NCSCreateViewReturnString(aArgs);
        divViewContainer.innerHTML = sViewHtml;
    }

    var divFrag = document.createElement("div");

    var txt = "<div id='" + this.editLayerName + "' style='position:absolute;left:0px;top:0px;width:1px;height:1px;display:none;visibility:hidden;background-color:#00ff00;filter:alpha(opacity=30);-moz-opacity:.30;opacity:.30;border:1px red solid'> </div>";

    txt += "<div class='featureLayerContainer'>";
    txt += "<div id='" + this.featureLayerUid + "' class='featureLayer'> </div>"; //Marker symbols container
    txt += "</div>";

    //Saved Maps window
    txt += "<div id='" + this.savedMapsBackgroundUid + "' class='savedMapsBackground'> </div>" +
	"<div id='" + this.savedMapsContainerUid + "' class='savedMapsContainer'>" +
	"<div id='" + this.savedMapsTitleBarUid + "' class='savedMapsTitleBar'> </div>" +
	"<div id='" + this.savedMapsUid + "' class='savedMaps'> </div>" +
	"</div>";

    txt += "<div class='featureLayerContainer'>";
    //Marker Form
    txt += "<div id='" + this.markerFormContainerUid + "' class='markerFormContainer'>" +
	"<form class='markerForm' id='" + this.markerFormUid + "' name='" + this.markerFormUid + "' onsubmit='" + this.myself + ".onSubmitMarkerForm();return false;'>" +
	"<img  class='markerFormSymbol' alt='Click to change marker symbol' title='Click to change marker symbol' id='" + this.markerFormSymbolUid + "' " + SIX.App.imageSrc("blank") + " onclick='" + this.myself + ".showMarkerSymbols()' border='0'>" +
	"<div class='markerFormRow'><span>Name:</span>&nbsp;&nbsp;<input type='text' name='Name'>" +
	"</div>" +
	"<div class='markerFormRow'><span>Description:</span></div>" +
	"<div class='markerFormRow'>" +
	"<div class='markerFormDescription' id='" + this.markerFormDescriptionUid + "'>" +
	"<div class='richTextArea' id='" + this.markerFormDescriptionRichTextUid + "'> &nbsp; </div>" +
	"<textarea wrap='hard' name='Description' class='richTextArea' rows='1'" +
	" onkeypress='" + this.myself + ".onChangeMarkerFormDescription()'" +
	" onkeyup='" + this.myself + ".onChangeMarkerFormDescription()'" +
	" onchange='" + this.myself + ".onChangeMarkerFormDescription()'" +
	"></textarea>" +
	"</div>" +
	"</div>" +
	"<div class='markerFormRow'>" +
	"<div class='markerFormOK'><input class='markerFormButton' type='button' value='OK' onclick='" + this.myself + ".onSubmitMarkerForm();'></div>" +
	"<div class='markerFormCancel'><input class='markerFormButton' type='button' value='Delete' onclick='" + this.myself + ".onCancelMarkerForm();'></div>" +
	"</div>" +
	"</form>" +
	"<div id='" + this.markerSymbolsContainerUid + "' class='markerSymbolsContainer'>" +
	"<img class='markerSymbolsCloseButton' " + SIX.App.imageSrc("images/close") + " border='0' onclick='" + this.myself + ".hideMarkerSymbols();'>" +
	"<div id='" + this.markerSymbolsUid + "' class='markerSymbols'> </div>" +
	"</div>" +
	"</div>" +
	"</div>";

    //Save Map form
    txt += "<div id='" + this.saveMapFormContainerUid + "' class='saveMapFormContainer'>" +
	"<form class='saveMapForm' id='" + this.saveMapFormUid + "' name='" + this.saveMapFormUid + "' onsubmit='" + this.myself + ".onSubmitSaveMapForm();return false;'>" +
	"<div class='saveMapFormTitleRow'>Save Map</div>" +
	"<div class='saveMapFormRow'><span>Name:</span>&nbsp;&nbsp;<input type='text' name='Name'></div>" +
	"<div class='saveMapFormRow'><span>Description:</span></div>" +
	"<div class='saveMapFormRow'>" +
	"<div class='saveMapFormDescription' id='" + this.saveMapFormDescriptionUid + "'>" +
	"<div class='richTextArea' id='" + this.saveMapFormDescriptionRichTextUid + "'> &nbsp; </div>" +
	"<textarea wrap='hard' name='Description' class='richTextArea' rows='1'" +
	" onkeypress='" + this.myself + ".onChangeSaveMapFormDescription()'" +
	" onkeyup='" + this.myself + ".onChangeSaveMapFormDescription()'" +
	" onchange='" + this.myself + ".onChangeSaveMapFormDescription()'" +
	"></textarea>" +
	"</div>" +
	"</div>" +
	"<div class='saveMapFormRow'>" +
	"<div class='saveMapFormOK'><input class='saveMapFormButton' type='button' value='OK' onclick='" + this.myself + ".onSubmitSaveMapForm();'></div>" +
	"<div class='saveMapFormCancel'><input class='saveMapFormButton' type='button' value='Cancel' onclick='" + this.myself + ".onCancelSaveMapForm();'></div>" +
	"</div>" +
	"</form>" +
	"</div>";

    divFrag.innerHTML = txt;

    if (!this.childMap) {
        this.div.appendChild(divFrag);
        var divContainer = document.createElement("div");
        divContainer.setAttribute("id", this.containerUid);

        divContainer.innerHTML = "<div class='crosshairsContainer' id='" + this.crosshairsUid + "'>" +
				"<div class='verticalCrosshairBackground'></div>" +
				"<div class='horizontalCrosshairBackground'></div>" +
				"<div class='verticalCrosshairForeground'></div>" +
				"<div class='horizontalCrosshairForeground'></div>" +
				"</div>";

        for (var i = 0; i < this.aAttachedItems.length; i++) {
            var item = this.aAttachedItems[i];
            if (item.init) {
                var elemItem = item.init();
                divContainer.appendChild(elemItem);
            }
        }

        for (var i = 0; i < SIX.App.channels.length; i++) {
            var elemDiv = SIX.App.channels[i].initMapItems(this);
            if (elemDiv)
                divContainer.appendChild(elemDiv);
        }

        if (this.parentMap) {
            for (var i = 0; i < this.parentMap.aAttachedItems.length; i++) {
                var item = this.parentMap.aAttachedItems[i];
                if (item.init) {
                    var elemItem = item.init();
                    divContainer.appendChild(elemItem);
                }
            }

            for (var i = 0; i < SIX.App.channels.length; i++) {
                var elemDiv = SIX.App.channels[i].initMapItems(this.parentMap);
                if (elemDiv)
                    divContainer.appendChild(elemDiv);
            }
        }
        this.div.appendChild(divContainer);
    }

    var childMap = this.childMap;
    if (childMap) {
        var divMap = document.createElement("div");
        divMap.innerHTML = txt;

        var divMapContainer = document.createElement("div");
        divMapContainer.style.position = "absolute";
        divMapContainer.style.top = "0px";
        divMapContainer.style.left = "0px";
        divMapContainer.style.width = "100%";
        divMapContainer.style.height = "100%";
        divMapContainer.appendChild(divMap);

        this.div.appendChild(divMapContainer);

        var divChildMapContainer = document.createElement("div");
        divChildMapContainer.style.position = "absolute";
        divChildMapContainer.style.top = "0px";
        divChildMapContainer.style.left = "0px";
        divChildMapContainer.style.width = "100%";
        divChildMapContainer.style.height = "100%";
        var divChildMap = childMap.init()
        divChildMapContainer.appendChild(divChildMap);
        this.div.appendChild(divChildMapContainer);
    }

    return this.div;
}

function getCallbackMap(callbackControl)
{
	var callbackMap;
	if (SIX.App != null)
	{
		for (var i=0; i<SIX.App.channels[SIX.App.nCurrentChannel].maps.length; i++)
		{
			var map = SIX.App.channels[SIX.App.nCurrentChannel].maps[i];
			var control = document[map.uid];
			if (control.nViewID == callbackControl.nViewID)
			{
				callbackMap = map;
				break;
			}
		}
	}
	return callbackMap;
}

function onMouseDownHook(mask, screenX, screenY, worldX, worldY)
{
	var map = getCallbackMap(this);
	if (map)
		map.onMouseDown(mask, screenX, screenY, worldX, worldY);
}

function onMouseMoveHook(mask, screenX, screenY, worldX, worldY)
{
 	var map = getCallbackMap(this);
	if (map)
		map.onMouseMove(mask, screenX, screenY, worldX, worldY);
}

function onMouseUpHook(mask, screenX, screenY, worldX, worldY)
{
	var map = getCallbackMap(this);
	if (map)
		map.onMouseUp(mask, screenX, screenY, worldX, worldY);
}

function onExtentChangeHook(tlx, tly, brx, bry)
{
	var map = getCallbackMap(this);
	if (map)
		map.onExtentChange(tlx, tly, brx, bry);
}

function onLayerResponseHook(layerName, url, body, action, tlx, tly, brx, bry, response)
{
	var map = getCallbackMap(this);
	if (map)
		map.onLayerResponse(layerName, url, body, action, tlx, tly, brx, bry, response);
}

function onPercentCompleteHook(value)
{
 	var map = getCallbackMap(this);
	if (map)
		map.onPercentComplete(value);
}

function onErrorHook(layerName, num, code, desc)
{
 	var map = getCallbackMap(this);
	if (map)
		map.onError(layerName, num, code, desc);
}

SIX.eToolCallbacks = {
	ONACTIVATE : 1,
	ONDEACTIVATE : 2, 
	ONENABLE : 3, 
	ONDISABLE : 4,
	ONMAPMOUSEDOWN : 5,
	ONMAPMOUSEUP : 6,	
	ONMAPMOUSEMOVE : 7,
	ONMAPEXTENTCHANGE : 8,
	ONMAPLOAD : 9
}

SIX.ToolGroup = function (channel)
{
	this.div = null;
	this.bToolsVisibleByDefault = true; 
	this.aTools = new Array();
}

SIX.ToolGroup.prototype.init = function ()
{
	this.div = document.createElement("div");
	for (var i=0; i<this.aTools.length; i++)
		this.div.appendChild(this.aTools[i].init());
	return this.div;
}

SIX.ToolGroup.prototype.addTool = function (tool)
{
	this.aTools[this.aTools.length] = tool;
	return tool;
}

SIX.ToolGroup.prototype.removeTool = function (tool)
{
	for (var i=0; i<this.aTools.length; i++)
	{
		if (this.aTools[i] == tool)
		{
			this.aTools.splice(i, 1);
			break;
		}
	}
}

SIX.ToolGroup.prototype.clearActiveTools = function (nToolType)
{
	var bCheckType = arguments.length >= 1;
	for (var i=0; i<this.aTools.length; i++)
	{
		var tool = this.aTools[i];
		if (tool.bActive && (!bCheckType || (tool.nType == nToolType)))
			tool.deactivate();
	}
}

SIX.ToolGroup.prototype.executeCallbacks = function (nType)
{
	for (var i=0; i<this.aTools.length; i++)
	{
		var tool = this.aTools[i];
		if (tool.bActive)
			tool.executeCallbacks.apply(tool, arguments);
	}	
}

SIX.ToolGroup.prototype.setToolsVisibleByDefault = function (bVisible)
{
	this.bToolsVisibleByDefault = bVisible; 
}

SIX.Callback = function (object, method, data)
{
	this.object = object;
	this.method = method;
	this.data = data;
}

SIX.Callback.prototype.execute = function ()
{
	var aArgs = Array.prototype.slice.call(arguments);
	aArgs[aArgs.length] = this.data;
	this.method.apply(this.object, aArgs);
}

SIX.CallbackManager = function ()
{
	this.aCallbacks = new Array();
}

SIX.CallbackManager.prototype.addCallback = function (callback)
{
	this.aCallbacks[this.aCallbacks.length] = callback;
}

SIX.CallbackManager.prototype.removeCallback = function (callback)
{
	for (var i=0; i<this.aCallbacks.length; i++)
	{
		if (this.aCallback[i] == callback)
		{
			this.aCallbacks.splice(i, 1);
			break;
		}
	}
}

SIX.CallbackManager.prototype.executeCallbacks = function ()
{
	for (var i=0; i<this.aCallbacks.length; i++)
	{
		var callback = this.aCallbacks[i];
		callback.execute.apply(callback, arguments);
	}
}

SIX.Tool = function (parent, nType, sImage, sToolTip, params)
{
	if (arguments.length < 5)
		var params = null;
	SIX.addRef(this);
	this.div = null;
	var toolGroup = parent instanceof SIX.Toolbar ? parent.toolGroup : parent;
	this.containerUid = SIX.Uid.getUid("toolContainer");
	this.uid = SIX.Uid.getUid("tool");
	this.toolGroup = toolGroup;
	this.nType = nType;
	this.sImage = sImage;
	this.imageOff = new Image();
	this.imageOff.src = ((sImage != "") ? SIX.App.imageUrl("images/" + sImage) : "");
	this.imageOn = new Image();
	this.imageOn.src = ((sImage != "") ? SIX.App.imageUrl("images/" + sImage + "_on") : "");
	this.bEnabled = SIX.Util.getParameter(params, "enabled", true);
	this.bVisible = SIX.Util.getParameter(params, "visible", this.toolGroup.bToolsVisibleByDefault);
	this.bActive = false;
	if (this.bEnabled && this.bVisible && this.nType != SIX.eToolTypes.NONE)
		this.bActive = SIX.Util.getParameter(params, "active", false);
	this.imageDisabled = null; 
	if (!this.bEnabled)
	{
		this.imageDisabled = new Image();
		this.imageDisabled.src = ((sImage != "") ? SIX.App.imageUrl("images/" + sImage + "_disabled") : "");
	}
	
	this.sToolTip = sToolTip;
	
	this.sActivationPrompt = "";
	
	this.sClass = "tool";
	this.hCallbackManagers = new SIX.Hash();
}

SIX.Tool.prototype.executeCallbacks = function (nType)
{
	if (this.hCallbackManagers.hasItem(nType))
	{
		var callbackManager = this.hCallbackManagers.getItem(nType);
		var aArguments = arguments.length > 1 ? Array.prototype.slice.call(arguments, 1, arguments.length) : new Array();
		callbackManager.executeCallbacks.apply(callbackManager, aArguments);
	}
}

SIX.Tool.prototype.addCallback = function (nType, method, data)
{
	var callbackManager = this.hCallbackManagers.hasItem(nType) ?  this.hCallbackManagers.getItem(nType) : new SIX.CallbackManager();
	callbackManager.addCallback(new SIX.Callback(this, method, (arguments.length >= 3 ? data : null)));
	this.hCallbackManagers.setItem(nType, callbackManager);
}

SIX.Tool.prototype.setVisible = function (bVisible)
{
	this.bVisible = bVisible;
	var elemToolContainer = document.getElementById(this.containerUid);
	if (elemToolContainer)
		elemToolContainer.style.display = bVisible ? "block" : "none";
}

SIX.Tool.prototype.setEnabled = function (bEnabled)
{
	this.bEnabled = bEnabled;
	if (!this.imageDisabled && !this.bEnabled)
	{
		this.imageDisabled = new Image();
		this.imageDisabled.src = ((this.sImage != "") ? SIX.App.imageUrl("images/" + this.sImage + "_disabled") : "");			
	}		
	
	var elemImg = document.getElementById(this.uid);
	if (elemImg)
	{
		elemImg.src = (this.bEnabled ? this.imageOff.src : this.imageDisabled.src);	
		elemImg.title = this.sToolTip;
		this.imageOn.title = this.sToolTip;
	}	
}

SIX.Tool.prototype.onClick = function () 
{
	if (!this.bEnabled)
		return;
	this.activate();
}

SIX.Tool.prototype.deactivate = function () 
{
	this.bActive = false;
	this.setActiveImage(false);
	var map = this._rMap;//Ensure we use the same map that was in focus when tool.activate() was called.
	this._rMap = null; 
	this.executeCallbacks(SIX.eToolCallbacks.ONDEACTIVATE, map);	
}

SIX.Tool.prototype.activate = function () 
{
	if (this.sActivationPrompt != "" && !confirm(this.sActivationPrompt))
		return;
		
	var channel = SIX.App.getCurrentChannel();
	var map = channel.getFocusedMap();
	switch(this.nType)
	{
		case SIX.eToolTypes.EXCLUSIVE:
			if (this.bActive)
			{
				this.bActive = false;	
				this.setActiveImage(false);
				this.deactivate();
				map.setPointerMode(PM_POINTER);
			}
			else
			{
				for (var i=0; i<channel.toolGroups.length; i++)
				{
					if (this.toolGroup != channel.toolGroups[i])
						channel.toolGroups[i].clearActiveTools(SIX.eToolTypes.EXCLUSIVE);
				}
				this.bActive = true;
				this.setActiveImage(true);
				for (var i=0; i<this.toolGroup.aTools.length; i++)
				{
					var toolGroupTool = this.toolGroup.aTools[i];
					if (toolGroupTool != this && toolGroupTool.nType == SIX.eToolTypes.EXCLUSIVE && toolGroupTool.bActive)
						toolGroupTool.deactivate();
				}		
				this._rMap = map;
				this.executeCallbacks(SIX.eToolCallbacks.ONACTIVATE, map);
			}
		break;
		case SIX.eToolTypes.TOGGLE:
			this.bActive = !this.bActive;	
			this.setActiveImage(this.bActive);
			if (this.bActive)
			{
				this._rMap = map;
				this.executeCallbacks(SIX.eToolCallbacks.ONACTIVATE, map);
			}
			else
			{
				this.deactivate();
			}
		break;
		case SIX.eToolTypes.NONE:
			this.executeCallbacks(SIX.eToolCallbacks.ONACTIVATE, map);
		break;
		default:
		break;
	}
}

SIX.Tool.prototype.setActiveImage = function (bActive)
{
	var elemImg = document.getElementById(this.uid);
	if (elemImg)
	{
		if (this.bEnabled)
			elemImg.src = (bActive ? this.imageOn.src : this.imageOff.src);
		else
			elemImg.src = this.imageDisabled.src;	
	}
}

SIX.Tool.prototype.setImage = function (sImage)
{
	this.sImage = sImage;
	this.imageOff.src = ((sImage != "") ? SIX.App.imageUrl("images/" + sImage) : "");
	this.imageOn.src = ((sImage != "") ? SIX.App.imageUrl("images/" + sImage + "_on") : "");
	this.imageDisabled = null; 
	if (!this.bEnabled)
	{
		this.imageDisabled = new Image();
		this.imageDisabled.src = ((sImage != "") ? SIX.App.imageUrl("images/" + sImage + "_disabled") : "");
	}
	var elemImg = document.getElementById(this.uid);
	if (elemImg)
	{
		if (this.bEnabled)
			elemImg.src = this.bActive ? this.imageOn.src : this.imageOff.src;
		else 
			elemImg.src = this.imageDisabled.src;
	}	
}

SIX.Tool.prototype.setActivationPrompt = function (sPrompt)
{
	this.sActivationPrompt = sPrompt;
}

SIX.Tool.prototype.setToolTip = function (sToolTip)
{
	this.sToolTip = sToolTip;
	var elemImg = document.getElementById(this.uid);
	if (elemImg)
		elemImg.title = sToolTip;
}

SIX.Tool.prototype.onMouseOver = function () 
{
	if (this.bEnabled)
		this.setActiveImage(true);
}

SIX.Tool.prototype.onMouseOut = function () 
{
	if (!this.bActive && this.bEnabled)
		this.setActiveImage(false);
}

SIX.Tool.prototype.init = function ()
{
	this.div = SIX.Util.createElement("div", this.sClass, this.containerUid);
	if (!this.bVisible)
		this.div.style.display = "none";
	this.div.innerHTML = "<img id='" + this.uid + "' src='" + (this.bEnabled ? (this.bActive ? this.imageOn.src : this.imageOff.src) : this.imageDisabled.src) + 
			"' border='0' title='" + this.sToolTip + "'" +
			" onclick='" + this.myself + ".onClick()' onmouseover='" + this.myself + ".onMouseOver()' onmouseout='" + this.myself + ".onMouseOut()'/>";
	return this.div;
}

SIX.Tool.prototype.setClass = function (sClass)
{
	this.sClass = sClass;
}

SIX.Tool.prototype.attachService = function (service)
{
	var channel = service.catalog.channel;
	channel.attachServiceToTool(this, service);
}

SIX.Tool.prototype.getAttachedServices = function ()
{
	var channel = SIX.App.getCurrentChannel(); 
	var aServices = channel.getServicesAttachedToTool(this);
	return aServices;
}

SIX.Map.prototype.build = function () 
{
	var control = document[this.uid];
	if (control)
	{
		if (this.sBackgroundColor != "" && this.sBackgroundColor.toLowerCase() != "tranparent")
		    control.SetBackGroundColor(this.sBackgroundColor);
		if (SIX.App.bDHTMLControl && SIXConfig.ECWP_SERVER_ALIASES.length)
			control.SetEcwServerAliases(SIXConfig.ECWP_SERVER, SIXConfig.ECWP_SERVER_ALIASES);
	}
		
	var childMap = this.childMap;
	while (childMap)
	{
		var mapChild = document[childMap.uid];
		if (mapChild && 
		    childMap.sBackgroundColor != "" && 
		    childMap.sBackgroundColor.toLowerCase() != "tranparent")
			mapChild.SetBackGroundColor(childMap.sBackgroundColor);
		childMap = childMap.childMap;
	}
}

SIX.Map.prototype.setBackgroundColor = function (sColor) 
{
	this.sBackgroundColor = sColor;
	var control = document[this.uid];
	if (control)
	{
		if (this.sBackgroundColor != "" && this.sBackgroundColor.toLowerCase() != "tranparent")
		    control.SetBackGroundColor(this.sBackgroundColor);
	}
	var divContainer = document.getElementById(this.mapDivUid);
	if (divContainer)
		divContainer.style.backgroundColor = this.sBackgroundColor;
}

SIX.Map.prototype.update = function () {
	if( !this.catalog )
		return;
		
	var control = document[this.uid];
			
	for(var i=0;i<this.catalog.services.length; i++) {
		var service = this.catalog.services[i];
		if ((this.loaded && (this.hiddenLayers(service) || (service.loaded == false))) || (service.access instanceof SIX.Access.ECWP)) 
			service.updateMap(this);
	}
	
	if (!this.editLayer) {
		if(this.addLayer("SIMPLEVECTOR", "", this.editLayerName,"visibility=true;color=#ff0000;fillcolor=#00FF00;linewidth=1") >= 0 ) {
			this.editLayer = true;
			control.SetLayerTransparency(this.editLayerName, "#00ff00", 0.3);
		}
	}
}

SIX.Map.prototype.attach = function (item, channel) {
	if (item.constructor == SIX.Status) 
	{
		this.status = item;
		item.map = this;
	} 
	else if ( item.constructor == SIX.Map) {
		this.childMap = item;
		item.parentMap = this;
	}
	else 
	{
		if (channel)
			channel.addMapItem(this, item);
		else
			this.aAttachedItems[this.aAttachedItems.length] = item;
	}
}

SIX.Map.prototype.setDefaultECWJP2Url = function (sUrl)
{
	this.sDefaultECWJP2Url = sUrl;
} 

SIX.Map.prototype.setDefaultECWJP2CoordSys = function (sDatum, sProjection)
{
	if (this.coordSysDefaultECWJP2)	
	{
		this.coordSysDefaultECWJP2.setDatum(sDatum);
		this.coordSysDefaultECWJP2.setProjection(sProjection);
	}
	else
	{
		this.coordSysDefaultECWJP2 = new SIX.CoordSys(sDatum, sProjection);
	}	
}

/**
*	PURPOSE: Assigns maximum overlay extents 
*	ACCEPTS: latlongs.
*/
SIX.Map.prototype.setMaxOverlayExtents = function (tlLat, tlLon, brLat, brLon) 
{
	this.tlMaxOverlayLat = tlLat;
	this.tlMaxOverlayLon = tlLon;
	this.brMaxOverlayLat = brLat;
	this.brMaxOverlayLon = brLon;
}

/**
*	PURPOSE: Assigns maximum extents when display best images option selected for ecwp service.
*	ACCEPTS: latlongs.
*/
SIX.Map.prototype.setMaxDisplayBestExtents = function (tlLat, tlLon, brLat, brLon) 
{
	this.tlMaxDisplayBestLat = tlLat;
	this.tlMaxDisplayBestLon = tlLon;
	this.brMaxDisplayBestLat = brLat;
	this.brMaxDisplayBestLon = brLon;
}

/**
*	PURPOSE: Assigns maximum extents 
*	ACCEPTS: latlongs.
*/
SIX.Map.prototype.setMaxExtents = function (tlLat, tlLon, brLat, brLon) 
{
	this.tlMaxLat = tlLat;
	this.tlMaxLon = tlLon;
	this.brMaxLat = brLat;
	this.brMaxLon = brLon;
	this.bMaxWorldExtentsUpdated = false;
}

/**
*	PURPOSE: Assigns initial extents 
*	ACCEPTS: latlongs.
*/
SIX.Map.prototype.setInitialExtents = function (tlLat, tlLon, brLat, brLon) 
{
	this.dInitialTopLeftLat = tlLat;
	this.dInitialTopLeftLon = tlLon;
	this.dInitialBottomRightLat = brLat;
	this.dInitialBottomRightLon = brLon;
}

/**
*	PURPOSE: set extents on the map, 
*	ACCPETS: Geodetic (decimal degrees) or eastings/northings as an array or individual values.
*/
SIX.Map.prototype.setExtents = function (tly, tlx, bry, brx) 
{
	if (isNaN(tlx) || isNaN(tly) || isNaN(brx) || isNaN(bry))
		return;
	
	if (tlx == brx || tly == bry)
		return;
	
	var control = document[this.uid];
	
	var sDatum = this.datum();
	var sProjection = this.projection();
	// need to find out if the projection is geodetic or not
	var bGeodetic =  this.isGeodetic();
	
	var bGeodeticExtents = true; 	// are we receiving geodetic extents?
	
	if (tly > 180 || tlx > 360)
		bGeodeticExtents = false;
	
	var aExtents = [[tlx, tly], [brx, bry]];	
	if (bGeodetic && !bGeodeticExtents)	// geodetic but extents in eastings / northings
		aExtents = this.convertCoords(sDatum, sProjection, [[tlx, tly], [brx, bry]], sDatum, "geodetic");
	else if (!bGeodetic && bGeodeticExtents) // need eastings and northings, extents are geodetic
		aExtents = this.convertCoords(sDatum, "geodetic", [[tlx, tly], [brx, bry]], sDatum, sProjection);
		
	if (!aExtents || !aExtents[0] || !aExtents[1] || ((aExtents[0][0] == aExtents[1][0]) && (aExtents[0][1] == aExtents[1][1])))
		return;

	control.SetExtents(aExtents[0][0], aExtents[0][1], aExtents[1][0], aExtents[1][1]);
}

SIX.Map.prototype.setLayerExtents = function (layer)
{
	var control = document[this.uid];
	var dTopLeftX = control.GetLayerImageTopLeftWorldCoordinateX(layer.uid);
	var dTopLeftY = control.GetLayerImageTopLeftWorldCoordinateY(layer.uid);
	var dBottomRightX = control.GetLayerImageBottomRightWorldCoordinateX(layer.uid);
	var dBottomRightY = control.GetLayerImageBottomRightWorldCoordinateY(layer.uid);
		
	if (this.projection() == "GEODETIC")
	{
		layer.setExtents(dTopLeftY, dTopLeftX, dBottomRightY, dBottomRightX);
	}
	else
	{
		//Top left, Top right, Bottom right, Bottom left
		var aExtentsEN = [[dTopLeftX, dTopLeftY],
						[dTopLeftX, dBottomRightY],
						[dBottomRightX, dBottomRightY],
						[dBottomRightX, dTopLeftY]];

		var aExtentsLL = this.convertCoords(this.datum(), this.projection(), aExtentsEN, this.datum(), "GEODETIC");
		
		if (aExtentsLL)
		{
			var dTopLeftLat  = aExtentsLL[0][1];
			var dTopLeftLon = aExtentsLL[0][0];
			
			var dTopRightLat  = aExtentsLL[1][1];
			var dTopRightLon = aExtentsLL[1][0];
			
			var dBottomRightLat  = aExtentsLL[2][1];
			var dBottomRightLon = aExtentsLL[2][0];
			
			var dBottomLeftLat  = aExtentsLL[3][1];
			var dBottomLeftLon = aExtentsLL[3][0];
			
			var aLatitudes = [dTopLeftLat, dTopRightLat, dBottomRightLat, dBottomLeftLat];
			var aLongitudes = [dTopLeftLon, dTopRightLon, dBottomRightLon, dBottomLeftLon];
			aLatitudes.sort(function (x, y) { return x - y; });//Sort ascending
			aLongitudes.sort(function (x, y) { return x - y; });//Sort ascending
			dLayerTopLeftLat = aLatitudes[aLatitudes.length - 1];
			dLayerTopLeftLon = aLongitudes[0];
			dLayerBottomRightLat = aLatitudes[0];
			dLayerBottomRightLon = aLongitudes[aLongitudes.length - 1];	
			
			layer.setExtents(dLayerTopLeftLat, dLayerTopLeftLon, dLayerBottomRightLat, dLayerBottomRightLon);
		}
	}
}

/**
*	PURPOSE: 	returns datum type e.g. geodetic, etc
*	returns: 	bln value, true if geodetic, false if not
*/
SIX.Map.prototype.isGeodetic = function () 
{
	var bGeodetic = false;
	
	var control = document[this.uid];
	var sLayerName = control.GetLayerName(0);
	var sLayerType = control.GetLayerProjection(sLayerName);
	
	if (sLayerType.indexOf("GEODETIC") >= 0)	// is geodetic
		{	
		bGeodetic = true;
		}
	
	return bGeodetic;
	
}

SIX.Map.prototype.getTopEcw = function ()
{
	var control = document[this.uid];
	var sEcwTop = "";	
	var nLayerCount = control.GetNumberLayers();
	for(var i=nLayerCount-1; i>=0; i--)
	{
		var nName = control.GetLayerName(i);
		var nFileName = control.GetLayerFileName(nName);
		if (nFileName.indexOf(".ecw") >= 0 || nFileName.indexOf(".jp2") >= 0)
		{
			var sVisibility = this.getLayerParameter(nName, "visibility");
			sVisibility = sVisibility.toUpperCase();
			
			if (sVisibility == "TRUE")	
			{
				sEcwTop = nName;
				break;
			}
		}
	}
	return 	sEcwTop;
}

/**
*	PURPOSE: Get top ECW image layer file name
*/
SIX.Map.prototype.getTopEcwFileName = function ()
{
	var control = document[this.uid];
	var sTopEcwLayerFileName = "";
	var sTopEcwLayerName = this.getTopEcw();
	if (sTopEcwLayerName != "")
		sTopEcwLayerFileName = control.GetLayerFileName(sTopEcwLayerName);
	return sTopEcwLayerFileName;
}

/**
*	PURPOSE: clears forward history 
*/
SIX.Map.prototype.clearForwardHistory = function ()
{
	this.aHistory.length = this.nHistoryIndex + 1;
}

/**
*	PURPOSE: moves extents backwards in "history".
*/
SIX.Map.prototype.moveBackHistory = function ()
{
	if (this.nHistoryIndex == 0)
	{
		alert('There is no backwards history');
		return;
	}
		
	if (this.nHistoryIndex > 0)
	{
		this.bHistoryMove = true;
		this.nOldHistoryIndex = this.nHistoryIndex;
		this.nHistoryIndex--;
		
		var aExtent = this.aHistory[this.nHistoryIndex];
		this.setExtents(aExtent[0], aExtent[1], aExtent[2], aExtent[3]);
	}
}

/**
*	PURPOSE: Moves extents forwards in history
*/
SIX.Map.prototype.moveForwardHistory = function ()
{	
	if (this.nHistoryIndex < (this.aHistory.length - 1))
	{
		this.bHistoryMove = true;
		this.nOldHistoryIndex = this.nHistoryIndex;
		this.nHistoryIndex++;
		var aExtent = this.aHistory[this.nHistoryIndex];
		this.setExtents(aExtent[0], aExtent[1], aExtent[2], aExtent[3]);
	}
	else
	{
		alert('There is no forward history');
	}
}

/**
*	PURPOSE: sets extents based on centre point
*/	
SIX.Map.prototype.setCenter = function (latitude, longitude) {

	var tlx = longitude;
	var tly = latitude;

	if( tly > 90 || tly < -90 || tlx > 180 || tlx < -180 )
		{
		// convert to geodetic 
		var aPoint = this.getGeoPoint(tlx, tly);
				
		tlx = aPoint[0];
		tly = aPoint[1];
		}
		
	
	var deltaLat  = parseFloat((this.getTopLeftLatitude() - this.getBottomRightLatitude())/2);
	var deltaLong = parseFloat((this.getTopLeftLongitude() - this.getBottomRightLongitude())/2);
	
	this.setExtents((parseFloat(tly) + deltaLat), (parseFloat(tlx) + deltaLong), (tly - deltaLat), (tlx - deltaLong));
}

/**
*	PURPOSE: sets extents based on centre point
*/	
SIX.Map.prototype.setCenterCoord = function (dCenterX, dCenterY, sDatum, sProjection) 
{	
	var control = document[this.uid];
	
	var dTopLeftX = control.GetTopLeftWorldCoordinateX();
	var dTopLeftY = control.GetTopLeftWorldCoordinateY();
	var dBottomRightX = control.GetBottomRightWorldCoordinateX();
	var dBottomRightY = control.GetBottomRightWorldCoordinateY();

	var dDeltaX  = (dBottomRightX - dTopLeftX)/2.0;
	var dDeltaY = (dBottomRightY - dTopLeftY)/2.0;
	
	var dLocalCenterX = dCenterX;
	var dLocalCenterY = dCenterY;
	var sLocalProjection = this.projection();
	
	if (sProjection != sLocalProjection) {
		if (sProjection != "GEODETIC") {
			var aCoordinate = this.getGeoPoint(dCenterX, dCenterY, sDatum, sProjection);
			dLocalCenterX = parseFloat(aCoordinate[0]);
			dLocalCenterY = parseFloat(aCoordinate[1]);
		}
		if (sLocalProjection != "GEODETIC") {
			var aCoordinate = this.getENPoint(dLocalCenterX, dLocalCenterY, sDatum, sLocalProjection);
			dLocalCenterX = parseFloat(aCoordinate[0]);
			dLocalCenterY = parseFloat(aCoordinate[1]);					
		}
	}	
	
	this.setExtents(dLocalCenterY - dDeltaY, 
					dLocalCenterX - dDeltaX,
					dLocalCenterY + dDeltaY,
					dLocalCenterX + dDeltaX);
}

/**
*	PURPOSE: Prompts user for center coordinate to pan to.
*/	
SIX.Map.prototype.promptCenterCoord = function (tool) 
{	
	var channel = SIX.App.getCurrentChannel();
				
	var sPopupContents = "<form name='PromptCenterCoordForm' >";
	sPopupContents += "<strong>Enter coordinates to pan to:</strong><br><br>"; 
	sPopupContents += "<strong><span style='width:80px;text-align:right;' id='CenterCoordXLabel'>Long (Dec. Deg. or Deg. Min. Sec):</span>&nbsp;&nbsp;</strong><input type='text' name='CenterCoordX'><br>";
	sPopupContents += "<strong><span style='width:80px;text-align:right;' id='CenterCoordYLabel'>Lat (Dec. Deg. or Deg. Min. Sec):</span>&nbsp;&nbsp;</strong><input type='text' name='CenterCoordY'><br><br>";
	sPopupContents += "<div class='popupRow'>";
	sPopupContents += "<select class='popupElement' style='float:none' name='CenterCoordType' id='CenterCoordType'" +
		" onChange='javascript:document.getElementById(\"CenterCoordXLabel\").innerHTML = ((this[this.selectedIndex].value == \"GEODETIC\") ? \"Long (Dec. Deg. or Deg. Min. Sec):\" : \"Easting:\");" + 
						"document.getElementById(\"CenterCoordYLabel\").innerHTML = ((this[this.selectedIndex].value == \"GEODETIC\") ? \"Lat (Dec. Deg. or Deg. Min. Sec):\" : \"Northing:\");'>" +
		"<option value='GEODETIC' selected>Geographic (Dec. Deg. or Deg. Min. Sec)" + 
		"<option value='NSWLAM94'>LAMBERT" + 
		"<option value='MGA54'>MGA54" + 
		"<option value='MGA55'>MGA55" + 
		"<option value='MGA56'>MGA56" + 
		"</select><br><br>";
	sPopupContents += "</div>";	
	sPopupContents += "<input type='button' value='Go' onclick='" + this.myself + ".promptCenterCoordSubmit();'>";
	sPopupContents += "<input style='margin-left:5px' type='button' value='Cancel' onclick='SIX.App.popupWinPanToCenter.hidePopup();'><br>";
	sPopupContents += "</form>";
		
	SIX.App.popupWinPanToCenter.clearOnHideCallback();				
	SIX.App.popupWinPanToCenter.offsetX = 10;
	SIX.App.popupWinPanToCenter.offsetY = 10;
	SIX.App.popupWinPanToCenter.autoHide();
	SIX.App.hideToolbarPopups();			
	SIX.App.popupWinPanToCenter.populate(sPopupContents);
	SIX.App.popupWinPanToCenter.showPopup(tool.containerUid);
	var map = this;
	SIX.App.popupWinPanToCenter.setOnHideCallback(function () {
			if (tool.bActive)
				tool.deactivate();
			SIX.App.popupWinPanToCenter.clearOnHideCallback();				
		}, tool.containerUid);	
}

/**
*	PURPOSE: Validate the center coordinate prompt and sets the center coordinate.
*/	
SIX.Map.prototype.promptCenterCoordSubmit = function () {	
	var popupWindow = self;
	if (popupWindow.document.PromptCenterCoordForm && 
		popupWindow.document.PromptCenterCoordForm.CenterCoordX && 
		popupWindow.document.PromptCenterCoordForm.CenterCoordY) {
		if ((popupWindow.document.PromptCenterCoordForm.CenterCoordX.value != "") && 
			(popupWindow.document.PromptCenterCoordForm.CenterCoordY.value != "")) {
			var dCenterCoordX = 0.0;
			var dCenterCoordY = 0.0;
			var sDatum = this.datum();
			var sProjection = popupWindow.document.PromptCenterCoordForm.CenterCoordType[popupWindow.document.PromptCenterCoordForm.CenterCoordType.selectedIndex].value;
			
			if (sProjection == "GEODETIC") {
				dCenterCoordX = SIX.Util.stringToDegrees(popupWindow.document.PromptCenterCoordForm.CenterCoordX.value);
				dCenterCoordY = SIX.Util.stringToDegrees(popupWindow.document.PromptCenterCoordForm.CenterCoordY.value);
				if (dCenterCoordY > 0) 
					dCenterCoordY *= -1;//Prevent users panning map center over northern hemisphere.
			} else {
				dCenterCoordX = parseFloat(popupWindow.document.PromptCenterCoordForm.CenterCoordX.value);
				dCenterCoordY = parseFloat(popupWindow.document.PromptCenterCoordForm.CenterCoordY.value);
			}
			this.setCenterCoord(dCenterCoordX, dCenterCoordY, sDatum, sProjection); 
		} else {
			alert("Invalid coordinates specified");
		}
	}
}

SIX.Map.prototype.promptQueryZonesParams = function (tool) 
{
	var map = this; 
	var dMeterFactor = (this.sQueryZonesDistanceDisplayUnits == "m" ? 1.0 : 0.001);
	var dDisplayEndDistance = this.dQueryZonesEndDistance * dMeterFactor;
	var dDisplayIntervalDistance = this.dQueryZonesIntervalDistance * dMeterFactor;

	var sContents = "<form name='PromptQueryZonesForm'>" +
					"<div class='popupRow'>Enter hot spot parameters:</div>" + 
					"<div class='popupRow'>" + 
					"<span class='popupLabel'>End distance:</span>" + 
					"<input name='QueryZonesEndDistance' type='text' value='" + dDisplayEndDistance + "' class='popupInputTextElement'>" +
					"</div>" +
					"<div class='popupRow'>" + 
					"<span class='popupLabel'>Interval distance:</span>" + 
					"<input name='QueryZonesIntervalDistance' type='text' value='" + dDisplayIntervalDistance + "' class='popupInputTextElement'>" +
					"</div>" +
					"<div class='popupRow'>" + 
					"<span class='popupLabel'>Units:</span>" + 
					"<select name='QueryZonesUnits' class='popupElement'>" +
					"<option value='m' " + (this.sQueryZonesDistanceDisplayUnits == "m" ? "selected" : "") + ">METRES</option>" +
					"<option value='km' " + (this.sQueryZonesDistanceDisplayUnits == "km" ? "selected" : "") + ">KILOMETRES</option>" +
					"</select>" +
					"</div>" +
					"<div class='popupRow'>Show hot spot zones on map" +
					"<input name='QueryZonesShowHideHotSpotZones' type='checkbox' border='0' " + (this.bQueryZonesVisible ? "checked" : "") + " onclick='" + this.myself + ".toggleQueryZonesVisibility()'>" +
					"</div>" +
					"<div class='popupRow'>" + 
					"NOTE: Hot Spot results will be approximate." + 
					"</div>" +
					"<div class='popupRow'>" +  
					"<input type='button' border='0' value='Submit' onclick='" + this.myself + ".promptQueryZonesSubmit(" + tool.myself + ")'>" +
					"<input type='button' border='0' value='Close' onclick='SIX.App.popupWinQueryZones.hidePopup()'>" +
					"</div>" +
					"</form>";

	SIX.App.popupWinQueryZones.clearOnHideCallback();
	SIX.App.popupWinQueryZones.offsetX = 10;
	SIX.App.popupWinQueryZones.offsetY = 10;
	SIX.App.popupWinQueryZones.autoHide();
	SIX.App.hideToolbarPopups();
	SIX.App.popupWinQueryZones.populate(sContents);
	SIX.App.popupWinQueryZones.showPopup(tool.uid);
	SIX.App.popupWinQueryZones.setOnHideCallback(function () {
			if (tool.bActive)
				tool.deactivate();
			SIX.App.popupWinQueryZones.clearOnHideCallback();
		}, tool.uid);	
}

SIX.Map.prototype.promptQueryZonesSubmit = function (tool)
{
	var popupWindow = self;
	var promptQueryZonesForm = popupWindow.document.PromptQueryZonesForm;
	if (promptQueryZonesForm && 
		promptQueryZonesForm.QueryZonesUnits &&
		promptQueryZonesForm.QueryZonesEndDistance && 
		promptQueryZonesForm.QueryZonesIntervalDistance &&
		(promptQueryZonesForm.QueryZonesEndDistance.value != "") && 
		(promptQueryZonesForm.QueryZonesIntervalDistance.value != ""))
	{
		var sDisplayUnits = this.sQueryZonesDistanceDisplayUnits;
		var dEndDistance = this.dQueryZonesEndDistance;
		var dIntervalDistance = this.dQueryZonesIntervalDistance;
		
		if (promptQueryZonesForm.QueryZonesUnits.selectedIndex > -1)
			sDisplayUnits = promptQueryZonesForm.QueryZonesUnits[promptQueryZonesForm.QueryZonesUnits.selectedIndex].value;
	
		var dMeterFactor = (sDisplayUnits == "m" ? 1.0 : 0.001);
		dEndDistance = parseFloat(promptQueryZonesForm.QueryZonesEndDistance.value) / dMeterFactor;
		dIntervalDistance = parseFloat(promptQueryZonesForm.QueryZonesIntervalDistance.value) / dMeterFactor;
		
		var sErrorMessage = "";
		if ((dEndDistance <= 0) || (dIntervalDistance <= 0))
			sErrorMessage += "End distance and interval distance must be greater than zero.\n";
		else if (dEndDistance < dIntervalDistance)
			sErrorMessage += "End distance must be greater than or equal to interval distance.\n";
		else if ((dEndDistance / dIntervalDistance) > this.nMaxQueryZones)
			sErrorMessage += "Number of zones must not be greater than " + this.nMaxQueryZones + ".";
			
		if (sErrorMessage != "")
		{		
			alert(sErrorMessage);
			this.promptQueryZonesParams(tool);
			return;
		}
		
		this.sQueryZonesDistanceDisplayUnits = sDisplayUnits;
		this.dQueryZonesEndDistance = dEndDistance;
		this.dQueryZonesIntervalDistance = dIntervalDistance;		
		
		var nIntervals = Math.ceil(this.dQueryZonesEndDistance/this.dQueryZonesIntervalDistance);
		var aQueryZonesColors = SIX.Util.interpolateHexColorsArray(this.aQueryZonesBoundaryColors, nIntervals);
		
		this.resetQueryZonesLayers();
		this.aQueryZones.length = 0;
		for (var i=0;i<nIntervals;i++)
		{
			var objQueryZone = new Object();
			objQueryZone.sBorderLayerName = "QueryZoneBorder" + i;
			objQueryZone.sFillLayerName = "QueryZoneFill" + i;
			objQueryZone.sColor = aQueryZonesColors[i];
			
			objQueryZone.dInnerRadius = i * this.dQueryZonesIntervalDistance;
			if (i < (nIntervals - 1))
				objQueryZone.dOuterRadius = (i + 1) * this.dQueryZonesIntervalDistance;
			else
				objQueryZone.dOuterRadius = this.dQueryZonesEndDistance;
			
			objQueryZone.aOuterBoundaryResults = new Object();
			
			this.aQueryZones[i] = objQueryZone;
		}

		var aServices = tool.getAttachedServices();
		if (aServices.length)
		{
			var queries = aServices[0].queries;
			if (queries)
			{
				queries.nCurrentQueryZone = 0;
				this.initializeQueryZoneSelector(queries);	
			}
		}
		this.initQuery(tool);
	} 
	SIX.App.popupWinQueryZones.hidePopup();
}

SIX.Map.prototype.initializeQueryZoneSelector = function (queries)
{
	var elemZoneSelect = document.getElementById(queries.zoneSelectUid);
	if (elemZoneSelect)
	{
		if (elemZoneSelect.options)
			elemZoneSelect.options.length = 0;
		
		if (this.aQueryZones.length <= 1)
		{
			elemZoneSelect.style.visibility = "hidden";	
		}		
		else
		{
			elemZoneSelect.style.visibility = "visible";
			for (var i=0; i<this.aQueryZones.length; i++)
			{
				var nodeOption = new Option("Zone " + (i+1));
				nodeOption.style.color = this.aQueryZones[i].sColor;
				nodeOption.style.fontWeight = "bold";						
				elemZoneSelect.options[i] = nodeOption;
			}				
		}
	}
}

/**
*	PURPOSE: checks to make sure the SIMPLEVECTOR layer for drawing the hot spot zones exists
*/

SIX.Map.prototype.resetQueryZonesLayers = function ()
{
	var control = document[this.uid];
	
	for (var i=this.aQueryZones.length-1; i>=0; i--)
	{
		var nLayerIndex = control.GetLayerIndex(this.aQueryZones[i].sFillLayerName);
		if (nLayerIndex == -1)	// need to add query zones SIMPLEVECTOR layer
			nLayerIndex = this.addLayer("SIMPLEVECTOR", "", this.aQueryZones[i].sFillLayerName,"visibility=" + (this.bQueryZonesVisible ? "true" : "false") + ";color=;linewidth=1");
		else
			this.setLayerParameter(this.aQueryZones[i].sFillLayerName,"visibility=" + (this.bQueryZonesVisible ? "true" : "false") + ";ellipse1=");
	}
	for (var i=this.aQueryZones.length-1; i>=0; i--)
	{
		var nLayerIndex = control.GetLayerIndex(this.aQueryZones[i].sBorderLayerName);
		if (nLayerIndex == -1)	// need to add query zones SIMPLEVECTOR layer
			nLayerIndex = this.addLayer("SIMPLEVECTOR", "", this.aQueryZones[i].sBorderLayerName,"visibility=" + (this.bQueryZonesVisible ? "true" : "false") + ";color=;linewidth=1");
		else
			this.setLayerParameter(this.aQueryZones[i].sBorderLayerName,"visibility=" + (this.bQueryZonesVisible ? "true" : "false") + ";ellipse1=");
	}	
}

SIX.Map.prototype.toggleQueryZonesVisibility = function ()
{
	this.bQueryZonesVisible = !this.bQueryZonesVisible;
	var control = document[this.uid];
	var sVisibility = "visibility=" + (this.bQueryZonesVisible ? "true" : "false");
	for (var i=0; i<this.aQueryZones.length; i++)
	{
		var nLayerIndex = control.GetLayerIndex(this.aQueryZones[i].sFillLayerName);
		if (nLayerIndex != -1)
			this.setLayerParameter(this.aQueryZones[i].sFillLayerName, sVisibility);

		var nLayerIndex = control.GetLayerIndex(this.aQueryZones[i].sBorderLayerName);
		if (nLayerIndex != -1)
			this.setLayerParameter(this.aQueryZones[i].sBorderLayerName, sVisibility);	
	}
}

SIX.Map.prototype.drawQueryZones = function ()
{	
	if (!this.pointQueryZonesCenter)
		return;
	var control = document[this.uid];
	this.resetQueryZonesLayers();
	
	var nLineWidth = Math.ceil(this.width * this.dQueryZonesIntervalDistance / (this.brx - this.tlx));
	
	for (var i=0; i<this.aQueryZones.length; i++)
	{
		var dQueryZoneCenterRadius = this.aQueryZones[i].dInnerRadius + (this.aQueryZones[i].dOuterRadius - this.aQueryZones[i].dInnerRadius) / 2;
		var dTopLeftX = this.pointQueryZonesCenter.worldX - dQueryZoneCenterRadius;
		var dTopLeftY = this.pointQueryZonesCenter.worldY + dQueryZoneCenterRadius;
		var dBottomRightX = this.pointQueryZonesCenter.worldX + dQueryZoneCenterRadius;
		var dBottomRightY = this.pointQueryZonesCenter.worldY - dQueryZoneCenterRadius;
		
		var sFillStyle = "linewidth=" + nLineWidth + ";color=" + this.aQueryZones[i].sColor + ";";
		var sFillEllipse = "ellipse1=" + dTopLeftX + "," + dTopLeftY + "|" + dBottomRightX + "," + dBottomRightY + ";";

		this.setLayerParameter(this.aQueryZones[i].sFillLayerName,sFillStyle + sFillEllipse);
		control.SetLayerTransparency(this.aQueryZones[i].sFillLayerName, "#", 0.25);
		
		var dBorderTopLeftX = this.pointQueryZonesCenter.worldX - this.aQueryZones[i].dOuterRadius;
		var dBorderTopLeftY = this.pointQueryZonesCenter.worldY + this.aQueryZones[i].dOuterRadius;
		var dBorderBottomRightX = this.pointQueryZonesCenter.worldX + this.aQueryZones[i].dOuterRadius;
		var dBorderBottomRightY = this.pointQueryZonesCenter.worldY - this.aQueryZones[i].dOuterRadius;
		
		var sBorderStyle = "linewidth=2;color=" + this.aQueryZones[i].sColor + ";";
		var sBorderEllipse = "ellipse1=" + dBorderTopLeftX + "," + dBorderTopLeftY + "|" + dBorderBottomRightX + "," + dBorderBottomRightY + ";";

		this.setLayerParameter(this.aQueryZones[i].sBorderLayerName,sBorderStyle + sBorderEllipse);	
	}	
}

/**
*	PURPOSE: sets centre with designated scale
*/
SIX.Map.prototype.setCenterWithScale = function(x, y, scale, datum, proj) {
    var sDatum = this.datum();
    var sProjection = this.projection();

    var sCoordDatum = (datum ? datum : "");
    var sCoordProjection = (proj ? proj : "");

    if (sCoordDatum != "" && sCoordProjection != "" && (sCoordDatum != sDatum || sCoordProjection != sProjection)) {
        var aEN = this.convertCoord(sCoordDatum, sCoordProjection, x, y, sDatum, sProjection);
        x = aEN[0];
        y = aEN[1];
    }
    else if (x <= 180 && y <= 90) {
        var aEN = this.convertCoord(sDatum, "GEODETIC", x, y, sDatum, sProjection);
        x = aEN[0];
        y = aEN[1];
    }
    this.zoom(scale);
    this.setCenterCoord(x, y, sDatum, sProjection);
}

SIX.Map.prototype.getCoordsCacheKey = function (sSrcDatum, sSrcProjection, aCoord, sDestDatum, sDestProjection)
{
	var sKey = sSrcDatum.toLowerCase() + "_" + sSrcProjection.toLowerCase() + "_" + aCoord[0] + "_" + aCoord[1] + "_" + sDestDatum.toLowerCase() + "_" + sDestProjection.toLowerCase();
	sKey = sKey.replace(/-/g, "neg");
	sKey = sKey.replace(/\./g, "pt");
	return sKey;
}

SIX.Map.prototype.getCachedCoord = function (sSrcDatum, sSrcProjection, aCoord, sDestDatum, sDestProjection)
{
	var aCachedCoord = null;
	if (aCoord)
	{
		var sKey = this.getCoordsCacheKey(sSrcDatum, sSrcProjection, aCoord, sDestDatum, sDestProjection);
		if (this.coordsCache.hasItem(sKey))
			aCachedCoord = this.coordsCache.getItem(sKey);
	}
	return aCachedCoord;
}

SIX.Map.prototype.cacheCoord = function (sSrcDatum, sSrcProjection, aCoord, sDestDatum, sDestProjection, aConvertedCoord)
{
	if (!this.getCachedCoord(sSrcDatum, sSrcProjection, aCoord, sDestDatum, sDestProjection))
	{
		if (this.coordsCache.length >= this.MAX_COORDS_CACHE_SIZE)
		{
			for (var i in this.coordsCache.items)
			{
				if (this.coordsCache.items.hasOwnProperty(i))
				{
					this.coordsCache.removeItem(i);
					break;//Just remove first (oldest) item in cache
				}
			}
		}
		var sKey = this.getCoordsCacheKey(sSrcDatum, sSrcProjection, aCoord, sDestDatum, sDestProjection);
		this.coordsCache.setItem(sKey, aConvertedCoord);
	}		
}

SIX.Map.prototype.convertCoords = function (sSrcDatum, sSrcProjection, aCoords, sDestDatum, sDestProjection)
{
	if (sSrcDatum == sDestDatum && sSrcProjection == sDestProjection)
		return aCoords;		
	
	var aConvertedCoords = null;
	if (SIX.App.bDHTMLControl && !this.hasProj4jsConversion(sSrcDatum, sSrcProjection, sDestDatum, sDestProjection))
	{
		var control = document[this.uid];
		
		var aCachedCoords = new Array();

		for (var i=0; i<aCoords.length; i++)
		{
			var aCachedCoord = this.getCachedCoord(sSrcDatum, sSrcProjection, aCoords[i], sDestDatum, sDestProjection);
			if (!aCachedCoord)
				break;
			aCachedCoords[i] = aCachedCoord;
		}
		
		if (aCachedCoords.length == aCoords.length)
		{
			aConvertedCoords = aCachedCoords;
			return aConvertedCoords;
		}
		
		if(control.bHaveProxy) 
		{
			var sCoords = "";
			for (var i=0; i<aCoords.length; i++)
				sCoords += (i > 0 ? "|" : "") + aCoords[i][0] + "," + aCoords[i][1];

			control._SetWaitCursor();
			var AJAX = new NCSAJAX();
			var dom = AJAX.GetXML((map.bSSLProxy ? "https://" : "http://") + SIXConfig.JSVIEWPROXY_PATH + "?xCMD=CVT&xSD=" + sSrcDatum + "&xSP=" + sSrcProjection + "&xDD=" + sDestDatum + "&xDP=" + sDestProjection + "&xSl=" + sCoords, null, "GET");
			control._SetCursor(false);
			if(dom != null) {
				try {
					var sConvertedCoords = dom.getElementText("CVT");
					if (sConvertedCoords != "")
					{
						var aCoordsTemp = sConvertedCoords.split("|");
						aConvertedCoords = new Array();
						for (var i=0; i<aCoordsTemp.length; i++)
						{	
							aConvertedCoords[i] = null;
							var aCoordTemp = aCoordsTemp[i].split(",");
							if (aCoordTemp.length >= 2)
							{
								var dX = parseFloat(aCoordTemp[0]);
								var dY = parseFloat(aCoordTemp[1]);
								if (!isNaN(dX) && !isNaN(dY))
								{
									aConvertedCoords[i] = [dX, dY];								
									this.cacheCoord(sSrcDatum, sSrcProjection, aCoords[i], sDestDatum, sDestProjection, aConvertedCoords[i]);
								}
							}
						}
					}
				} catch( ex ) {
					aConvertedCoords = null;
				}
			}
		}
	}
	else
	{
		aConvertedCoords = new Array();
		for (var i=0; i<aCoords.length; i++)
		{
			var aConvertedCoord = this.convertCoord(sSrcDatum, sSrcProjection, aCoords[i][0], aCoords[i][1], sDestDatum, sDestProjection)
			aConvertedCoords[i] = aConvertedCoord;
		}
	}
	
	return aConvertedCoords;
}

SIX.Map.prototype.convertCoordsAsync = function (sSrcDatum, sSrcProjection, aCoords, sDestDatum, sDestProjection, fnCallback, data)
{
	var AJAX = null;
	if (sSrcDatum == sDestDatum && sSrcProjection == sDestProjection)
	{
		fnCallback(data, aCoords);	
		return null;
	}	
	
	var map = this;	
	var aConvertedCoords = null;
	if (SIX.App.bDHTMLControl)
	{
		var control = document[this.uid];
		
		var aCachedCoords = new Array();

		for (var i=0; i<aCoords.length; i++)
		{
			var aCachedCoord = this.getCachedCoord(sSrcDatum, sSrcProjection, aCoords[i], sDestDatum, sDestProjection);
			if (!aCachedCoord)
				break;
			aCachedCoords[i] = aCachedCoord;
		}
		
		if (aCachedCoords.length == aCoords.length)
		{
			aConvertedCoords = aCachedCoords;
			fnCallback(data, aConvertedCoords);
			return null;
		}
		
		if (this.hasProj4jsConversion(sSrcDatum, sSrcProjection, sDestDatum, sDestProjection))
		{
			aConvertedCoords = this.convertCoords(sSrcDatum, sSrcProjection, aCoords, sDestDatum, sDestProjection);
			fnCallback(data, aConvertedCoords);
			return null;
		}
		
		if(control.bHaveProxy) 
		{
			var sCoords = "";
			for (var i=0; i<aCoords.length; i++)
				sCoords += (i > 0 ? "|" : "") + aCoords[i][0] + "," + aCoords[i][1];

			AJAX = new NCSAJAX();
			
			var fnConvertCoordsCallback = function (dom, data) {
				with ({fnCallback : fnCallback, map : map})
				{
					if(dom != null) {
						try {
							var sConvertedCoords = dom.getElementText("CVT");
							if (sConvertedCoords != "")
							{
								var aCoordsTemp = sConvertedCoords.split("|");
								aConvertedCoords = new Array();
								for (var i=0; i<aCoordsTemp.length; i++)
								{	
									aConvertedCoords[i] = null;
									var aCoordTemp = aCoordsTemp[i].split(",");
									if (aCoordTemp.length >= 2)
									{
										var dX = parseFloat(aCoordTemp[0]);
										var dY = parseFloat(aCoordTemp[1]);
										if (!isNaN(dX) && !isNaN(dY))
										{
											aConvertedCoords[i] = [dX, dY];								
											map.cacheCoord(sSrcDatum, sSrcProjection, aCoords[i], sDestDatum, sDestProjection, aConvertedCoords[i]);
										}
									}
								}
							}
						} catch( ex ) {
							aConvertedCoords = null;
						}
						fnCallback(data, aConvertedCoords);
					}
				}
			}
		
			var dom = AJAX.PostEx((map.bSSLProxy ? "https://" : "http://") + SIXConfig.JSVIEWPROXY_PATH + "?xCMD=CVT&xSD=" + sSrcDatum + "&xSP=" + sSrcProjection + "&xDD=" + sDestDatum + "&xDP=" + sDestProjection + "&xSl=" + sCoords, null, "GET", fnConvertCoordsCallback, data, true);
		}
	}
	return AJAX;
}

SIX.Map.prototype.getProj4jsDef = function (sDatum, sProjection)
{
	var def = null;
	var nEpsg = GDTEPSGMappings.getEPSGCode(sDatum, sProjection);
	if (nEpsg && Proj4js && Proj4js.defs["EPSG:" + nEpsg])
		def = "EPSG:" + nEpsg;
	return def;
}

SIX.Map.prototype.hasProj4jsConversion = function (sSrcDatum, sSrcProjection, sDestDatum, sDestProjection)
{
	return (this.getProj4jsDef(sSrcDatum, sSrcProjection) && this.getProj4jsDef(sDestDatum, sDestProjection));
}

SIX.Map.prototype.convertCoord = function (sSrcDatum, sSrcProjection, dX, dY, sDestDatum, sDestProjection)
{
	var control = document[this.uid];
	var aCoord = this.getCachedCoord(sSrcDatum, sSrcProjection, [dX, dY], sDestDatum, sDestProjection);
	if (!aCoord)
	{
		if (SIX.App.bDHTMLControl)
		{
			if (this.hasProj4jsConversion(sSrcDatum, sSrcProjection, sDestDatum, sDestProjection))
			{
				var p = new Proj4js.Point(dX, dY);
				var projSrc = new Proj4js.Proj(this.getProj4jsDef(sSrcDatum, sSrcProjection));
				var projDest = new Proj4js.Proj(this.getProj4jsDef(sDestDatum, sDestProjection));
				Proj4js.transform(projSrc, projDest, p);
				aCoord = new Array();
				aCoord[0] = p.x;
				aCoord[1] = p.y;		
			}	
			else
			{
				aCoord = control._ConvertCoords(sSrcDatum, sSrcProjection, [dX, dY], sDestDatum, sDestProjection);
			}
		}
		else
		{
			if (sDestProjection.toLowerCase() == "geodetic")
			{
				var sCoord = control.GetParameter("GDTConvertToLL(" + sSrcDatum + " " + sSrcProjection + " " + dX + " " + dY + ")");
			}
			else if (sSrcProjection.toLowerCase() == "geodetic")
			{
				var sCoord = control.GetParameter("GDTConvertToEN(" + sDestDatum + " " + sDestProjection + " " + dX + " " + dY + ")");
			}
			else
			{
				//Neither source or target projections geodetic.
				var sLLCoord = control.GetParameter("GDTConvertToLL(" + sSrcDatum + " " + sSrcProjection + " " + dX + " " + dY + ")");
				var sLLCoord = sLLCoord.replace(",", " ");
				var sCoord = control.GetParameter("GDTConvertToEN(" + sDestDatum + " " + sDestProjection + " " + sLLCoord + ")");
			}
				
			var aTempCoord = sCoord.split(",");
			if (aTempCoord.length >= 2)
			{
				aCoord = new Array();
				aCoord[0] = parseFloat(aTempCoord[0]);
				aCoord[1] = parseFloat(aTempCoord[1]);
			}		
		}	
			
		this.cacheCoord(sSrcDatum, sSrcProjection, [dX, dY], sDestDatum, sDestProjection, aCoord);
	}
	return aCoord;
}

SIX.Map.prototype.getMouseLatitude = function ()
{
	var dLatitude = null;
	if (this.dMouseWorldX != null && this.dMouseWorldY != null)
	{
		var sDatum = this.datum();
		var sProjection = this.projection();
		var aCoord = this.convertCoord(sDatum, sProjection, this.dMouseWorldX, this.dMouseWorldY, sDatum, "geodetic");
		if (aCoord && aCoord.length >= 2)
			dLatitude = aCoord[1];
	}
	return dLatitude;
}

SIX.Map.prototype.getMouseLongitude = function ()
{
	var dLongitude = null;
	if (this.dMouseWorldX != null && this.dMouseWorldY != null)
	{
		var sDatum = this.datum();
		var sProjection = this.projection();
		var aCoord = this.convertCoord(sDatum, sProjection, this.dMouseWorldX, this.dMouseWorldY, sDatum, "geodetic");
		if (aCoord && aCoord.length >= 2)
			dLongitude = aCoord[0];
	}
	return dLongitude;
}

/**
*	PURPOSE: gets a point in geodetic extents
*/
SIX.Map.prototype.getGeoPoint = function (dX, dY, sDatum, sProjection) 
{	
	var control = document[this.uid];
	
	if (arguments.length < 3)
		var sDatum = this.datum();
	
	if (arguments.length < 4)
		var sProjection = this.projection();
		
	var aGeoPoint = new Array();
	
	if ((sDatum != "") && (sProjection != ""))
	{		
		if (SIX.App.bDHTMLControl)
		{
			aGeoPoint = this.convertCoord(sDatum, sProjection, dX, dY, sDatum, "GEODETIC");
		}
		else
		{
			var sCoords = control.GetParameter("GDTConvertToLL(" + sDatum + " " + sProjection + " " + dX + " " + dY + ")");
			var aCoords = sCoords.split(",");
			if (aCoords.length >= 2)
			{
				aGeoPoint[0] = parseFloat(aCoords[0]);
				aGeoPoint[1] = parseFloat(aCoords[1]);
			}
		}
	}
	return aGeoPoint;
}

/**
*	PURPOSE: gets a point in EN coordinates given an input datum and projection
*/
SIX.Map.prototype.getENPoint = function (dX, dY, sDatum, sProjection) 
{	
	var control = document[this.uid];
	var aENPoint = new Array();
	
	if (arguments.length < 3)
		var sDatum = this.datum();
		
	if (arguments.length < 4)
		var sProjection = "geodetic";
	
	if ((sDatum != "") && (sProjection != "") && (sProjection.toLowerCase() != "geodetic"))
	{
		if (SIX.App.bDHTMLControl)
		{	
			aENPoint = this.convertCoord(sDatum, "GEODETIC", dX, dY, sDatum, sProjection);
		}
		else
		{
			var sPoint =  control.GetParameter("GDTConvertToEN(" + sDatum + " " + sProjection + " " + dX + " " + dY + ")");	
			var aCoords = sPoint.split(",");
			if (aCoords.length >= 2)
			{
				aENPoint[0] = parseFloat(aCoords[0]);
				aENPoint[1] = parseFloat(aCoords[1]);
			}
		}
	}
	return aENPoint;
}

SIX.Map.prototype.getPointerMode = function () 
{
	var control = document[this.uid];
	var nPointerMode = control.GetPointerMode();
	return nPointerMode;
}

SIX.Map.prototype.setPointerMode = function (mode) 
{
	var control = document[this.uid];
	control.SetPointerMode(mode);
	if (this.parentMap)
		this.parentMap.setPointerMode(mode);
}

/**
*	PURPOSE: Get map projection
*/
SIX.Map.prototype.projection = function ()
{
	var control = document[this.uid];
	var sLayerName = control.GetLayerName(0);
	var sProjection = control.GetLayerProjection(sLayerName);
	return sProjection;
}

/**
*	PURPOSE: Get map datum
*/
SIX.Map.prototype.datum = function ()
{
	var control = document[this.uid];
	var sLayerName = control.GetLayerName(0);
	var sDatum = control.GetLayerDatum(sLayerName);
	return sDatum;
}

/**
*	PURPOSE: sets the map exents - checks to make sure image is visible
*/
SIX.Map.prototype.setExtentsAll = function ()
{
	var control = document[this.uid];
	var bVisible = false;
	var nLayers = control.GetNumberLayers();
			
	for (var i = 0; i < nLayers; i++)	// loop through layers
	{
		var sLayerName = control.GetLayerName(i);
		var sLayerType = control.GetLayerType(sLayerName);
		sLayerType = sLayerType.toLowerCase();
		if ((sLayerType == "ecw") || (sLayerType == "jp2")) 
		{
			// is the layer visible
			var sVis = this.getLayerParameter(sLayerName, "visibility");
			sVis = sVis.toUpperCase();
			
			if (sVis == "TRUE")	
			{
				bVisible = true;
				break;
			}
		}		
	}
	// if there is an image that is visible, set extents all.
	if (bVisible)
	{	
		if (this.catalog.bDisplayBest && !((this.tlMaxDisplayBestLat == 0) && (this.tlMaxDisplayBestLon == 0) && (this.brMaxDisplayBestLat == 0) && (this.brMaxDisplayBestLon == 0)))			
		{
			this.setExtents(this.tlMaxDisplayBestLat, this.tlMaxDisplayBestLon, this.brMaxDisplayBestLat, this.brMaxDisplayBestLon);
		}
		else if ((this.tlMaxLat == 0) && (this.tlMaxLon == 0) && (this.brMaxLat == 0) && (this.brMaxLon == 0))
		{
			control.SetExtentsAll();
		}	
		else
		{
			this.setExtents(this.tlMaxLat, this.tlMaxLon, this.brMaxLat, this.brMaxLon);
		}
	}	
	else if (!((this.tlMaxOverlayLat == 0) && (this.tlMaxOverlayLon == 0) && (this.brMaxOverlayLat == 0) && (this.brMaxOverlayLon == 0)))
	{
		this.setExtents(this.tlMaxOverlayLat, this.tlMaxOverlayLon, this.brMaxOverlayLat, this.brMaxOverlayLon);
	}
}

/**
* receives the scale parameter from 
*
* Set the ground scale of the map
*   9600 is the monitor dpi, 2.54 is inches to centimeter conversion
*/

SIX.Map.prototype.zoom = function (scale)
{		
	var nDPI = SIX.Util.getScreenXDPI();
	var dScreenMetresPerPixel = (0.0254 / nDPI);
	
	var dDistanceXInMetres = dScreenMetresPerPixel * scale * this.width;
	
	if (this.isGeodetic())
	{
		var dMouseLatitude = this.getMouseLatitude();
		var dMouseLongitude = this.getMouseLongitude();
		
		var dCenterLatRad = Math.PI * dMouseLatitude / 180;
		var dCenterLonRad = Math.PI * dMouseLongitude / 180;
		
		var dEarthRadius = 6378137;
		var dSmallCircleCircumference = 2 * dEarthRadius * (Math.acos(Math.sin(dCenterLatRad) * Math.sin(dCenterLatRad) - 
												Math.cos(dCenterLatRad) * Math.cos(dCenterLatRad)));
												
		var tlx = this.getTopLeftLongitude();
		var tly = this.getTopLeftLatitude();
		var brx = this.getBottomRightLongitude();
		var bry = this.getBottomRightLatitude();
		
		var dMetresWidth = Math.abs(brx - tlx) * dSmallCircleCircumference / 360;		
		var dDistanceX = dDistanceXInMetres * 360 / dSmallCircleCircumference;
	}
	else 
	{
		var dMetresWidth = Math.abs(this.brx - this.tlx);
		var dDistanceX = dDistanceXInMetres;
		var tlx = this.tlx;
		var tly = this.tly;
		var brx = this.brx;
		var bry = this.bry;		
	}
											
	var dAspect = (brx - tlx) / (tly - bry);
	var dDistanceY = dDistanceX / dAspect;
	var aPoint = this.getCentrePoint(tlx, tly, brx, bry);

	this.setExtents(aPoint[1] + dDistanceY / 2, 
					aPoint[0] - dDistanceX / 2, 
					aPoint[1] - dDistanceY / 2, 
					aPoint[0] + dDistanceX / 2); 	
}

SIX.Map.prototype.pan = function (dPercentX, dPercentY)
{
	var dDeltaX = dPercentX * (this.brx - this.tlx) / 100;
	var dDeltaY = dPercentY * (this.tly - this.bry) / 100;
	var dTLX = this.tlx + dDeltaX;
	var dBRX = this.brx + dDeltaX;
	var dTLY = this.tly + dDeltaY
	var dBRY = this.bry + dDeltaY;	
	this.setExtents(dTLY, dTLX, dBRY, dBRX);
}

/**
*	PURPOSE: get centre point of extents
*	RETURNS: array with two value x + y
*/	
SIX.Map.prototype.getCentrePoint = function (tlx, tly, brx, bry)
{
	var aCentrePoints = new Array(2);
	
	aCentrePoints[0] = tlx + (brx - tlx) / 2;
	aCentrePoints[1] = bry + (tly - bry) / 2;
	
	return aCentrePoints;	
}

SIX.Map.prototype.getScaleFromExtents = function (tlx, tly, brx, bry)
{
	var dCenterWorldX = tlx + (brx - tlx) / 2;
	var dCenterWorldY = tly + (bry - tly) / 2;	
	
	if (this.isGeodetic())
	{	
		var dCenterLatRad = Math.PI * dCenterWorldY / 180;
		var dCenterLonRad = Math.PI * dCenterWorldX / 180;
		
		var dEarthRadius = 6378137;
		var dSmallCircleCircumference = 2 * dEarthRadius * (Math.acos(Math.sin(dCenterLatRad) * Math.sin(dCenterLatRad) - 
												Math.cos(dCenterLatRad) * Math.cos(dCenterLatRad)));
											
		var dMetresWidth = Math.abs(brx - tlx) * dSmallCircleCircumference / 360;
	}
	else 
	{
		var dMetresWidth = Math.abs(brx - tlx);
	}
											
	var nWidthPixels = this.width;
	var dMetresPerPixel = dMetresWidth / nWidthPixels;

	var nDPI = SIX.Util.getScreenXDPI();
	var dScreenMetresPerPixel = (0.0254 / nDPI);
	var scale = dMetresPerPixel / dScreenMetresPerPixel;
	return scale;
}

/**
*	PURPOSE: returns extents in Lat Longs for resizing
*	RETURNS: array for extent values
*/
SIX.Map.prototype.getExtentsFromScale = function (dX, dY, scale)
{	
	var aExtents = new Array(4);
	var nDPI = SIX.Util.getScreenXDPI();
	var dScreenMetresPerPixel = (0.0254 / nDPI);

	if(scale > 0) 
	{
		var dEarthRadius = 6378137;
		
		var dCenterLatRad = Math.PI * dY / 180;
		var dCenterLonRad = Math.PI * dX / 180;
		
		var dSmallCircleCircumference = 2 * dEarthRadius * (Math.acos(Math.sin(dCenterLatRad) * Math.sin(dCenterLatRad) - 
											Math.cos(dCenterLatRad) * Math.cos(dCenterLatRad)));
										
    	var dDistanceXInMetres = dScreenMetresPerPixel * scale * this.width;
		var dDistanceXInDegrees = dDistanceXInMetres * 360 / dSmallCircleCircumference;

		var dAspect = (this.getBottomRightLongitude() - this.getTopLeftLongitude()) / (this.getTopLeftLatitude() - this.getBottomRightLatitude());
		var dDistanceYInDegrees = dDistanceXInDegrees / dAspect;
		aExtents[0] = dX - dDistanceXInDegrees / 2;
		aExtents[1] = dY + dDistanceYInDegrees / 2;
		aExtents[2] = dX + dDistanceXInDegrees / 2;
		aExtents[3] = dY - dDistanceYInDegrees / 2;	
	}
		
	return aExtents;
}

/**
*	PURPOSE: grabs screen view
*/
SIX.Map.prototype.getView = function ()
{
	/* 
	** We need to determine whether the local image created via the control's CaptureView() function (see below)
	** has been blocked by the browser.  Unfortunately, the img onerror event does not get fired when 
	** local content is blocked.  So we do the following instead: 
	** 1. Display the error text after a timeout (say 1 second) which allows the local image time to load. 
	** 2. Call hideError() in the img onload event which hides the error text if the timeout has expired
	**	  and sets the bImageLoaded flag to true which prevents the error text being displayed if the timeout
	**	  has not expired.
	*/
	 
    var control = document[this.uid];
    var filename = control.CaptureView(".png");
    
	var sScript = "<script>" + 
				"var bImageLoaded = false;" + 
				"function hideError()" + 
				"{var elemError = document.getElementById(\"error\");" + 
				"if (elemError) elemError.style.display = \"none\";}" + 
				"function showError()" + 
				"{var elemError = document.getElementById(\"error\");" + 
				"if (elemError) elemError.style.display = \"block\";}" + 				
				"</script>";    
    var oWin = window.open();
	oWin.document.writeln("<html><head>");
	oWin.document.writeln(sScript);
	oWin.document.writeln("</head><body onload='setTimeout(\"if (!bImageLoaded) showError();\",1000)'>");
    oWin.document.writeln("<img onload='bImageLoaded=true;hideError();' src='file:///" + filename + "'>");
	oWin.document.writeln("<div id='error' style='display:none'><p>")
	oWin.document.writeln("<b>NOTE:</b> Your browser settings have prevented the map print preview from being displayed.");
	oWin.document.writeln("To display the print preview, copy and paste the below filename into your browser's address bar:<br><br>")
    oWin.document.writeln(filename);
    oWin.document.writeln("</p></div>");
 	oWin.document.writeln("<div id='copyright'>");
	oWin.document.writeln("Source - NSW Department of Lands SIX Viewer &copy;");
	oWin.document.writeln("<a href=http://six.maps.nsw.gov.au target='_blank'>six.maps.nsw.gov.au</a>");
	oWin.document.writeln("<ul style='list-style-type:circle;list-style-position:inside;margin-top:10px;margin-left:20px;'>");
	
	for(var i=0; i<this.catalog.services.length; i++) 
	{
		var service = this.catalog.services[this.catalog.servicesOrder[i]];
		if (service.access instanceof SIX.Access.ECWP) 
		{
			for (var j=0; j<service.layers.length; j++) 
			{
				var layer = service.layers[service.layersOrder[j]];
				if (layer.bSelected)
					oWin.document.writeln("<li>" + layer.name + "</li>");
			}
		}
	}

    oWin.document.writeln("</ul></div>");   
    if (this.childMap)
    {
    	childMap = document[this.childMap.uid];
    	filenameChild =  childMap.CaptureView(".png");
   	    oWin.document.writeln("<div id='childImg' style='position:absolute; top:0px; left:0px; opacity:" + this.childMap.opacity + "; filter:alpha(opacity=" + this.childMap.opacity * 100 +");'>");
   	    oWin.document.writeln("<img src='file:///" + filenameChild + "'></div>");
	}    
    oWin.document.writeln("</body></html>");
    oWin.document.close();
}

/**
*	PURPOSE: Print Preview
*/
SIX.Map.prototype.printPreview = function ()
{ 
	var map = this.getBottomMap();
	var oWin = window.open("PrintPreview.htm?map=" + encodeURIComponent(map.myself));
}

SIX.Map.prototype.onSubmitPrintPreviewPageSetup = function ()
{
	var map = this.getBottomMap();
	var sTitle = SIX.Util.trim(document.forms["PrintPreviewPageSetupForm"].Title.value);
	var sOrientation = SIX.Util.trim(document.forms["PrintPreviewPageSetupForm"].Orientation[document.forms["PrintPreviewPageSetupForm"].Orientation.selectedIndex].value);
	var sSize = SIX.Util.trim(document.forms["PrintPreviewPageSetupForm"].Size[document.forms["PrintPreviewPageSetupForm"].Size.selectedIndex].value);
	var oWin = window.open("PrintPreview.htm?map=" + encodeURIComponent(map.myself) + "&showscale=true&title=" + encodeURIComponent(sTitle) + "&orientation=" + encodeURIComponent(sOrientation) + "&size=" + encodeURIComponent(sSize));
	SIX.App.printPreviewPageSetupPopupWin.hidePopup();
}

/**
*	PURPOSE: Print Preview Page Setup
*	Prompts for page setup parameters such as orientation (portrait/landscape), title etc.
*/
SIX.Map.prototype.printPreviewPageSetup = function (tool)
{ 
	//Note: Don't use form's onsubmit and action as Firefox will prompt user with 
	//"Do you want firefox to remember this password?", which we don't want.
	var sPopupContents = "<form name='PrintPreviewPageSetupForm' onsubmit='" + this.myself + ".onSubmitPrintPreviewPageSetup();return false;' action=''>" +
		"<div class='printPreviewPageSetupTitle'>" +
		"<img class='printPreviewPageSetupLogo' src='images/SIXLogoSmall.gif'>" +
		"<div class='printPreviewPageSetupHeading'>Print Page Setup</div>" +
		"</div>" +
		"<div class='printPreviewPageSetupRow'>" +
		"<div class='printPreviewPageSetupLabel'>Title:</div>" + 
		"<div class='printPreviewPageSetupField'><input type='text' name='Title' size='30' value=''></div>" +
		"</div>" + 	
		"<div class='printPreviewPageSetupRow'>" +
		"<div class='printPreviewPageSetupLabel'>Size:</div>" + 
		"<div class='printPreviewPageSetupField'>" + 
		"<select name='Size'>" +
		"<option value='a4' selected>A4" + 
		"<option value='a3'>A3" + 
		"</select>" +			
		"</div>" +
		"</div>" +				
		"<div class='printPreviewPageSetupRow'>" +
		"<div class='printPreviewPageSetupLabel'>Orientation:</div>" + 
		"<div class='printPreviewPageSetupField'>" + 
		"<select name='Orientation'>" +
		"<option value='portrait' selected>Portrait" + 
		"<option value='landscape'>Landscape" + 
		"</select>" +			
		"</div>" +
		"</div>" + 
		"<div class='printPreviewPageSetupRow'>" +
		"<input class='printPreviewPageSetupSubmit' type='button' value='OK' onclick='" + this.myself + ".onSubmitPrintPreviewPageSetup()'>" +
		"</div>" +				
		"</form>";			
				
	SIX.App.printPreviewPageSetupPopupWin.offsetX = 10;
	SIX.App.printPreviewPageSetupPopupWin.offsetY = 30;
	
	SIX.App.printPreviewPageSetupPopupWin.autoHide();
	SIX.App.hideToolbarPopups();			
	SIX.App.printPreviewPageSetupPopupWin.populate(sPopupContents);
	SIX.App.printPreviewPageSetupPopupWin.showPopup(tool.containerUid);
}

/**
* initiates roam window
*/

SIX.Map.prototype.initRoamClip = function (nRoamSize)
{	
	var control = document[this.uid];
	
	this.bRoamClip = true;
	var nWidth = control.GetViewWidth();
	var nHeight = control.GetViewHeight();
	
	var dScreenX = parseInt(nWidth / 2, 10);
	var dScreenY = parseInt(nHeight / 2, 10);
	
	this.nRoamSize = arguments.length ? nRoamSize : this.nRoamSize;
	
	var nLayers = control.GetNumberLayers();
	for (var i=0; i<nLayers; i++)
		control.SetLayerClip(control.GetLayerName(i), false);
			
	var sTopEcw = this.getTopEcw();
	control.SetLayerClipRect(sTopEcw,(dScreenX-this.nRoamSize/2),(dScreenY-this.nRoamSize/2),(dScreenX + this.nRoamSize/2),(dScreenY + this.nRoamSize/2));
	control.SetLayerClip(sTopEcw, true);
}	
	
/**
* PURPOSE: clears old clippping windows
*/
SIX.Map.prototype.clearRoam = function ()
{
	// loop through layers, clear any clipping regions...
	this.bRoamClip = false;
	var control = document[this.uid];
	
	var nLayers = control.GetNumberLayers();
	for (var i = 0; i < nLayers; i++)
		control.SetLayerClip(control.GetLayerName(i), false);
}

SIX.Map.prototype.roamClip = function (dMask, dScreenX, dScreenY, dWorldX, dWorldY)
{
	var control = document[this.uid];
	
	var nWidth = control.GetViewWidth();
	var nHeight = control.GetViewHeight();
			
	var nLayers = control.GetNumberLayers();
	for (var i=0; i<nLayers; i++)
		control.SetLayerClip(control.GetLayerName(i), false);
					
	if(this.bRoamClip)
	{
		var sTopEcw = this.getTopEcw();
		control.SetLayerClipRect(sTopEcw,(dScreenX-this.nRoamSize/2),(dScreenY-this.nRoamSize/2),(dScreenX + this.nRoamSize/2),(dScreenY + this.nRoamSize/2));
		control.SetLayerClip(sTopEcw,true);
	}
	else
	{
		control.SetLayerClip(this.getLastVisibleLayer(),false);
	}
}

/**
*	PURPOSE: get last visible layer name
*/

SIX.Map.prototype.getLastVisibleLayer = function ()
{
	var sLayerName = "";
	var control = document[this.uid];
	for(var i=0;i<this.catalog.services.length; i++)
	{
		var service = this.catalog.services[i];
		if (service.loaded && service.active)
		{
			if (service.access instanceof SIX.Access.ECWP && service.layers.length > 0)
				sLayerName = service.layers[service.layers.length - 1].uid;
			else
				sLayerName = service.uid;			
		}
	}
	return sLayerName;
}

SIX.Map.prototype.getLayerParameter = function (sLayerName, sParameter)
{
	var sParameterValue = "";
	var control = document[this.uid];
	try
	{
		//Under Firefox and Netscape, calling GetLayerParameter() on parameters 
		//which are either not set or set to empty strings results in an exception 
		//(NS_ERROR_OUT_OF_MEMORY) being thrown.  Hence, a try catch block is wrapped
		//around the GetLayerParameter() call.  
		sParameterValue = control.GetLayerParameter(sLayerName, sParameter);
	}
	catch(e)
	{
	}	
	
	return sParameterValue;
}

SIX.Map.prototype.getECWProgress = function ()
{
	return this.nECWProgressPercent;
}

SIX.Map.prototype.setECWProgress = function (nPercent)
{
	var channelCurrent = SIX.App.getCurrentChannel();
	if (channelCurrent.console)
		channelCurrent.console.setECWJP2Progress(nPercent);
	this.nECWProgressPercent = nPercent;
	this.status.setProgress(Math.round((this.nECWProgressPercent + this.nGISOverlayProgressPercent) / 2));	
}

SIX.Map.prototype.getGISOverlayProgress = function ()
{
	return this.nGISOverlayProgressPercent;
}

SIX.Map.prototype.setGISOverlayProgress = function (nPercent)
{
	var channelCurrent = SIX.App.getCurrentChannel();
	if (channelCurrent.console)
		channelCurrent.console.setGISOverlayProgress(nPercent);
	this.nGISOverlayProgressPercent = nPercent;
	this.status.setProgress(Math.round((this.nECWProgressPercent + this.nGISOverlayProgressPercent) / 2));	
}

SIX.Map.prototype.getContainer = function ()
{
	var map = this.getTopMap();
	var container = document.getElementById(map.containerUid);
	return container;
}

SIX.Map.prototype.getCanvas = function ()
{
	var canvas = null;
	if (SIX.App.bDHTMLControl)
	{
		var map = this.getTopMap();
		var control = document[map.uid];
		canvas = document.getElementById("NCSJSViewCanvas" + control.nViewID);
	}
	return canvas;
}

SIX.Map.prototype.onChangeSaveMapFormDescription = function ()
{
	var elemForm = document.getElementById(this.saveMapFormUid);
	if (elemForm && (this.sMapDescription == "" || elemForm.Description.value != this.sMapDescription))
	{
		this.sMapDescription = elemForm.Description.value;
		var elemDivPreContainer = document.getElementById(this.saveMapFormDescriptionRichTextUid);
		var elemDivContainer = document.getElementById(this.saveMapFormDescriptionUid);
		
		if (elemDivPreContainer && elemDivContainer)
			this.onChangeRichText(elemForm.Description, elemDivPreContainer, elemDivContainer);
	}
}

SIX.Map.prototype.onChangeMarkerFormDescription = function ()
{
	var elemForm = document.getElementById(this.markerFormUid);
	if (elemForm && (this.sMarkerDescription == "" || elemForm.Description.value != this.sMarkerDescription))
	{
		this.sMarkerDescription = elemForm.Description.value;
		var elemDivPreContainer = document.getElementById(this.markerFormDescriptionRichTextUid);
		var elemDivContainer = document.getElementById(this.markerFormDescriptionUid);
		
		if (elemDivPreContainer && elemDivContainer)
			this.onChangeRichText(elemForm.Description, elemDivPreContainer, elemDivContainer);
	}
}

SIX.Map.prototype.onChangeRichText = function (elemTextArea, elemDivPreContainer, elemDivContainer)
{
	var sDescription = elemTextArea.value ? elemTextArea.value : "";
	var sFormattedDescription = SIX.Util.convertTextUrlsToLinks(sDescription);
	
	var elemPre = SIX.Util.createElement("pre", "richTextArea");
	elemPre.innerHTML = sFormattedDescription;
	
	while (elemDivPreContainer.hasChildNodes())
		elemDivPreContainer.removeChild(elemDivPreContainer.lastChild);
	
	elemDivPreContainer.appendChild(elemPre);

	var nHeight = elemTextArea.scrollHeight - 2;

	elemPre.style.height = 0 + "px";//Important - removes scroll bar
	elemPre.style.height = nHeight + "px";
			
	elemTextArea.style.height = 0 + "px";//Important - removes scroll bar
	elemTextArea.style.height = nHeight + "px";

	elemTextArea.style.top = -(elemDivPreContainer.offsetHeight) + "px";	

	for (var i=elemDivContainer.childNodes.length-1; i>=0; i--)
	{
		var nodeChild = elemDivContainer.childNodes[i];
		if ((nodeChild.tagName.toLowerCase() == "a") || 
			(nodeChild.tagName.toLowerCase() == "br") ||
			((nodeChild.tagName.toLowerCase() == "pre") && (nodeChild != elemPre)))
			elemDivContainer.removeChild(nodeChild);
	} 

	var nPosX = SIX.Util.findPosX(elemPre);
	var nPosY = SIX.Util.findPosY(elemPre);
	
	for (var i=0; i<elemPre.childNodes.length; i++)
	{
		if ((elemPre.childNodes[i].nodeType != Node.ELEMENT_NODE) || elemPre.childNodes[i].tagName.toLowerCase() != "a")
			continue;
		var bAddBreakBeforeAnchor = (i && !(elemPre.childNodes[i-1].nodeType == Node.ELEMENT_NODE && elemPre.childNodes[i-1].tagName.toLowerCase() == "br"));
		var nodeAnchor = elemPre.childNodes[i];
		var nAnchorPosX = SIX.Util.findPosX(nodeAnchor);
		var nAnchorPosY = SIX.Util.findPosY(nodeAnchor);
		var nX = nAnchorPosX - nPosX;
		var nY = nAnchorPosY - nPosY;
		var nodeAnchorClone = nodeAnchor.cloneNode(true);
		nodeAnchorClone.style.backgroundColor = "#ffffff";
		nodeAnchorClone.style.visibility = "inherit";
		
		if (SIX.App.isIE && (!nX || (nX + nodeAnchor.offsetWidth > elemPre.offsetWidth)))
		{
			//Works around bug in IE, where SIX.Util.findPosY returns wrong position when 
			//element has been wrapped to next line.
			var elemPreClone = elemPre.cloneNode(false);
			elemPreClone.style.position = "relative";	
			elemPreClone.style.left = 0;
			elemPreClone.style.visibility = "inherit";	
			if (bAddBreakBeforeAnchor)
			{	
				var nodeBreak = document.createElement("br");
				elemPreClone.appendChild(nodeBreak);
			}
			elemPreClone.appendChild(nodeAnchorClone);
			elemDivContainer.appendChild(elemPreClone);
			elemPreClone.style.top = (nAnchorPosY - SIX.Util.findPosY(elemPreClone)) + "px";	
		}
		else
		{
			nodeAnchorClone.style.position = "absolute";
			nodeAnchorClone.style.top = nY + "px";
			nodeAnchorClone.style.left = nX + "px";
			elemDivContainer.appendChild(nodeAnchorClone);
		}
	}
}

SIX.Map.prototype.onSubmitMarkerForm = function ()
{
	var channelCurrent = SIX.App.getCurrentChannel();
	var marker = this.markerCollection.aMarkers[this.markerCollection.nSelectedMarker];
	var elemMarkerForm = document.getElementById(this.markerFormUid);
	if (elemMarkerForm)
	{
		if ((marker.sSymbol != channelCurrent.sMarkerSymbolId) || 
			(marker.sTitle != elemMarkerForm.Name.value) ||
			(marker.sDescription != elemMarkerForm.Description.value))
		{
			marker.sSymbol = channelCurrent.sMarkerSymbolId;
			marker.sTitle = elemMarkerForm.Name.value;
			marker.sDescription = elemMarkerForm.Description.value;			
			var elemDiv = document.getElementById(marker.uid);
			var elemImageOld = document.getElementById(marker.imageUid);
			if (elemDiv && elemImageOld)
			{
				elemDiv.removeChild(elemImageOld);
				var elemImageNew = this.markerCollection.buildMarkerImage(marker);
				if (elemImageNew)
					elemDiv.appendChild(elemImageNew);
			}
		}
	}
	this.hideMarkerForm();
	this.markerCollection.updateMarkerList();
}

SIX.Map.prototype.onCancelMarkerForm = function ()
{
	this.markerCollection.deleteMarker(this.markerCollection.nSelectedMarker);
	this.markerCollection.update();
	this.hideMarkerForm();
	this.markerCollection.updateMarkerList();
}

SIX.Map.prototype.onSubmitSaveMapForm = function ()
{
	var elemSaveMapForm = document.getElementById(this.saveMapFormUid);
	if (elemSaveMapForm)
	{
		var context = new SIX.Context("", elemSaveMapForm.Name.value, elemSaveMapForm.Description.value);
		context.setExtents(this.getTopLeftLatitude(), this.getTopLeftLongitude(), this.getBottomRightLatitude(), this.getBottomRightLongitude());
		context.featureCollection = this.markerCollection.toGeoJSONFeatureCollection();	
		var contextCollection = SIX.App.aContextCollections[0];
		contextCollection.saveContext(context);
	}
	this.hideSaveMapForm();
}

SIX.Map.prototype.onCancelSaveMapForm = function ()
{
	this.hideSaveMapForm();
}

SIX.Map.prototype.showSaveMapForm = function (nMarkerIndex)
{
	var elemSaveMapFormContainer = document.getElementById(this.saveMapFormContainerUid);
	if (elemSaveMapFormContainer)
	{
		elemSaveMapFormContainer.style.display = "block";
		this.onChangeSaveMapFormDescription();
	}
}

SIX.Map.prototype.hideSaveMapForm = function ()
{
	var elemSaveMapFormContainer = document.getElementById(this.saveMapFormContainerUid);
	if (elemSaveMapFormContainer)
	{
		elemSaveMapFormContainer.style.display = "none";
	}
}

SIX.Map.prototype.hideMarkerSymbols = function ()
{
	var elemMarkerSymbols = document.getElementById(this.markerSymbolsContainerUid);
	if (elemMarkerSymbols)
		elemMarkerSymbols.style.visibility = "hidden";	
}

SIX.Map.prototype.showMarkerSymbols = function ()
{
		var elemMarkerSymbolsContainer = document.getElementById(this.markerSymbolsContainerUid);
		if (elemMarkerSymbolsContainer)
		{
			var channel = SIX.App.getCurrentChannel();
			if (!document.getElementById(channel.markerSymbolsUid))
				this.buildMarkerSymbols();
			elemMarkerSymbolsContainer.style.visibility = "visible";
		}
}

SIX.Map.prototype.buildMarkerSymbols = function ()
{
	var sInnerHTML = "";
	var channel = SIX.App.getCurrentChannel();
	var markerSymbols = channel.markerSymbols.length ? channel.markerSymbols : SIX.App.markerSymbols;
	sInnerHTML += "<div id='" + channel.markerSymbolsUid + "'>";
	for (var i in markerSymbols.items)
	{
		if (markerSymbols.items.hasOwnProperty(i))
			sInnerHTML += "<img src='" + markerSymbols.items[i].sImage + "' onclick='" + this.myself + ".onClickMarkerFormSymbol(\"" + i + "\")' border='0'>";
	}	
	sInnerHTML += "</div>";
	var elemMarkerSymbols = document.getElementById(this.markerSymbolsUid);
	if (elemMarkerSymbols)
		elemMarkerSymbols.innerHTML = sInnerHTML;
}

SIX.Map.prototype.onClickMarkerFormSymbol = function (sSymbol)
{
	this.setMarkerFormSymbol(sSymbol);
	this.hideMarkerSymbols();	
}

SIX.Map.prototype.setMarkerFormSymbol = function (sSymbolId)
{
	var channelCurrent = SIX.App.getCurrentChannel();
	var markerSymbol = channelCurrent.getMarkerSymbol(sSymbolId);
	var sImagePath = markerSymbol.sImage;
	var elemMarkerFormSymbol = document.getElementById(this.markerFormSymbolUid);
	if (elemMarkerFormSymbol)
	{
		elemMarkerFormSymbol.src = sImagePath;
		channelCurrent.sMarkerSymbolId = sSymbolId;
	}
}

SIX.Map.prototype.showMarkerForm = function (nMarkerIndex)
{
	if (this.tMarkerFormPan)
	{
		clearTimeout(this.tMarkerFormPan);
		this.tMarkerFormPan = null;
	}		
	var marker = this.markerCollection.aMarkers[nMarkerIndex];
	if (marker)
	{
		this.markerCollection.nSelectedMarker = nMarkerIndex;
		var elemMarkerForm = document.getElementById(this.markerFormUid);
		if (elemMarkerForm)
		{
			elemMarkerForm.Name.value = marker.sTitle;
			elemMarkerForm.Description.value = marker.sDescription;
			this.setMarkerFormSymbol(marker.sSymbol);
		}
		
		var elemMarkerFormContainer = document.getElementById(this.markerFormContainerUid);
		if (elemMarkerFormContainer)
		{
			elemMarkerFormContainer.style.visibility = "hidden";
			elemMarkerFormContainer.style.display = "block";
			var aPixel = this.getPixelFromLL(marker.x, marker.y);
			if (aPixel)
			{
				var elemMarker = document.getElementById(marker.uid);
				var elemMarkerImage = document.getElementById(marker.imageUid);
				
				var nLeft = aPixel[0] - Math.round(elemMarkerFormContainer.offsetWidth/2);
				var nTop = aPixel[1] - elemMarkerFormContainer.offsetHeight + (elemMarkerImage ? elemMarkerImage.offsetTop : 0);
				var dTopLeftY = this.tly;
				var dBottomRightY = this.bry;				
				var dTopLeftX = this.tlx;
				var dBottomRightX = this.brx;
					
				if (nTop < 0)
				{
					var dY = (this.tly - this.bry) * Math.abs(nTop / this.height);
					dTopLeftY = this.tly + dY;
					dBottomRightY = this.bry + dY;
				}
				if (nLeft < 0)
				{
					var dX = (this.brx - this.tlx) * Math.abs(nLeft / this.width);
					dTopLeftX = this.tlx - dX;
					dBottomRightX = this.brx - dX;
				}	
	
				elemMarkerFormContainer.style.visibility = "visible";
				elemMarkerFormContainer.style.left = nLeft + "px";
				elemMarkerFormContainer.style.top = nTop + "px";				

				this.onChangeMarkerFormDescription();
				
				if (nTop < 0 || nLeft < 0)
				{
					var dMaxPanPercent = 5;
					
					var dTotalPercentX = 100 * (dTopLeftX - this.tlx) / (this.brx - this.tlx);
					var dTotalPercentY = 100 * (dTopLeftY - this.tly) / (this.tly - this.bry);
					
					var dPanPercentX = (Math.abs(dTotalPercentY) > Math.abs(dTotalPercentX) ? dMaxPanPercent * dTotalPercentX / Math.abs(dTotalPercentY) : (dTotalPercentX < 0 ? -dMaxPanPercent : dMaxPanPercent));
					var dPanPercentY = (Math.abs(dTotalPercentX) > Math.abs(dTotalPercentY) ? dMaxPanPercent * dTotalPercentY / Math.abs(dTotalPercentX) : (dTotalPercentY < 0 ? -dMaxPanPercent : dMaxPanPercent));
					
					var map = this;
					var fnPan = function()
					{
						map.pan(dPanPercentX, dPanPercentY);
					}	
					this.tMarkerFormPan = setTimeout(fnPan, 50);							
				}
			}
		}
	}
}

SIX.Map.prototype.hideMarkerForm = function ()
{
	var elemMarkerFormContainer = document.getElementById(this.markerFormContainerUid);
	if (elemMarkerFormContainer)
	{
		elemMarkerFormContainer.style.visibility = "hidden";
		elemMarkerFormContainer.style.display = "none";
	}
}

SIX.Map.prototype.isMarkerFormVisible = function ()
{
	var elemMarkerFormContainer = document.getElementById(this.markerFormContainerUid);
	return (elemMarkerFormContainer && elemMarkerFormContainer.style.display != "none" && elemMarkerFormContainer.style.visibility != "hidden");
}	

SIX.Map.prototype.showSavedMaps = function (bRefresh)
{
	var elemSavedMapsContainer = document.getElementById(this.savedMapsContainerUid);
	var elemSavedMapsTitleBar = document.getElementById(this.savedMapsTitleBarUid);
	var elemSavedMaps = document.getElementById(this.savedMapsUid);
	var elemSavedMapsBackground = document.getElementById(this.savedMapsBackgroundUid);
	if (elemSavedMapsContainer && elemSavedMapsTitleBar && elemSavedMaps && elemSavedMapsBackground)
	{
		if (bRefresh)
		{
			var map = this;
			elemSavedMaps.innerHTML = "";
			elemSavedMapsTitleBar.innerHTML = "";
			
			var elemTitleBarBackground = SIX.Util.createElement("div", "savedMapsTitleBarBackground");
			elemSavedMapsTitleBar.appendChild(elemTitleBarBackground);
			
			var elemImageClose = SIX.Util.createElement("img", "savedMapsCloseButton");
			elemImageClose.setAttribute("src", "images/close.gif");
			
			elemImageClose.onclick = function()
			{
				with ({map : map})
				{
					map.hideSavedMaps();
				}
			}
			elemSavedMapsTitleBar.appendChild(elemImageClose);
			
			var elemImageSave = SIX.Util.createElement("img", "saveMapButton");
			elemImageSave.setAttribute("alt", "Save map");
			elemImageSave.setAttribute("title", "Save map");
			elemImageSave.setAttribute("src", "images/save_map.gif");	

			elemImageSave.onmouseover = function () 
									{
										this.src = "images/save_map_on.gif";
									}

			elemImageSave.onmouseout = function () 
									{
										this.src = "images/save_map.gif";
									}	
											
			elemImageSave.onclick = function()
			{
				with ({map : map})
				{				
					if (!SIX.App.account.bLoggedIn)
					{
						var fnLoginCallback = function () 
						{
							with ({map : map})
							{	
								var fnGetMapCollectionCallback = function()
								{
									with ({map : map})
									{	
										map.showSaveMapForm();	
									}
								}			
								SIX.App.getMySavedMaps(fnGetMapCollectionCallback);
							}	
						}				
						SIX.App.account.fnLoginCallback = fnLoginCallback;
						SIX.App.account.showLogin();
					}
					else
					{
						map.showSaveMapForm();
					}
				}
			}
		
			elemSavedMapsTitleBar.appendChild(elemImageSave);
		
			var elemMarkerList = this.markerCollection.buildMarkerList();
			elemSavedMaps.appendChild(elemMarkerList);
			
			this.markerCollection.updateMarkerList();
			
			for (var i=0; i<SIX.App.aContextCollections.length; i++)
			{
				var contextCollection = SIX.App.aContextCollections[i];
				var elemContextCollectionContainer = SIX.Util.createElement("div", "contextCollectionContainer", contextCollection.containerUid);
			
				var elemTitle = SIX.Util.createElement("div", "contextListTitle");
				if (i > 0)
				{
					var elemExpandCollapseContextListButton = contextCollection.buildExpandCollapseContextListButton(true);
					elemTitle.appendChild(elemExpandCollapseContextListButton);
					var textNodeTitle = document.createTextNode(contextCollection.sTitle);
					elemTitle.appendChild(textNodeTitle);
				}
				else
				{
					var nodeAnchor = document.createElement("a");
					
					nodeAnchor.setAttribute("href", "#");
					var sAltText = "Click to display " + contextCollection.sTitle;
					nodeAnchor.setAttribute("alt", sAltText);
					nodeAnchor.setAttribute("title", sAltText);		
					nodeAnchor.onclick = function (event)
										{
											with ({map : map})
											{				
												if (!SIX.App.account.bLoggedIn)
												{
													var fnLoginCallback = function () 
													{
														with ({map : map})
														{			
															SIX.App.getMySavedMaps(null);
														}	
													}				
													SIX.App.account.fnLoginCallback = fnLoginCallback;
													SIX.App.account.showLogin();
												}
											}
											return false;
										}
					var textNodeTitle = document.createTextNode(contextCollection.sTitle);
					nodeAnchor.appendChild(textNodeTitle);
					elemTitle.appendChild(nodeAnchor);
				}
				elemContextCollectionContainer.appendChild(elemTitle);
				
				var elemContextList = SIX.Util.createElement("ol", "contextList", contextCollection.uid);
				elemContextCollectionContainer.appendChild(elemContextList);
				elemSavedMaps.appendChild(elemContextCollectionContainer);			
				contextCollection.updateContextList();
			}

			var elemSavedMapsFooter = SIX.Util.createElement("div", "savedMapsFooter");
			var textNodeSavedMapsFooter = document.createTextNode("Footer");
			elemSavedMapsFooter.appendChild(textNodeSavedMapsFooter);
			elemSavedMaps.appendChild(elemSavedMapsFooter);
		}
		
		elemSavedMapsContainer.style.display = "block";
		elemSavedMapsBackground.style.display = "block";		
	}
}

SIX.Map.prototype.hideSavedMaps = function ()
{
	var elemSavedMapsContainer = document.getElementById(this.savedMapsContainerUid);
	var elemSavedMapsBackground = document.getElementById(this.savedMapsBackgroundUid);
	if (elemSavedMapsContainer && elemSavedMapsBackground)
	{
		elemSavedMapsContainer.style.display = "none";
		elemSavedMapsBackground.style.display = "none";
	}
}

SIX.Map.prototype.getPixelFromEN = function (dX, dY)
{
	var control = document[this.uid];
	var nWidth = control.GetViewWidth();
	var nHeight = control.GetViewHeight();
	var nPixelX = Math.round(nWidth * (dX - this.tlx) / (this.brx - this.tlx));
	var nPixelY = Math.round(nHeight * (this.tly - dY) / (this.tly - this.bry));
	var aCoords = new Array();
	aCoords[0] = nPixelX;
	aCoords[1] = nPixelY;
	return aCoords;
}

SIX.Map.prototype.getPixelFromLL = function (dX, dY)
{
	var aCoords = null;
	var aPoint = this.getENPoint(dX, dY, this.datum(), this.projection());
	if (aPoint)
		aCoords = this.getPixelFromEN(aPoint[0], aPoint[1]);
	return aCoords;
}

SIX.Map.prototype.getENFromPixel = function (nX, nY)
{
	var control = document[this.uid];
	var nWidth = control.GetViewWidth();
	var nHeight = control.GetViewHeight();
	var aCoords = null;

	if ((nX >= 0) && (nX < nWidth) && 
		(nY >= 0) && (nY < nHeight))
	{		
		var dX = this.tlx + (this.brx - this.tlx) * nX / nWidth;
		var dY = this.tly - (this.tly - this.bry) * nY / nHeight;
		aCoords = new Array();
		aCoords[0] = dX;
		aCoords[1] = dY;
	}	
	return aCoords;
}

SIX.Map.prototype.getLLFromPixel = function (nX, nY)
{
	var aCoords = null;
	var a
	var aPoint = this.getENFromPixel(nX, nY);
	if (aPoint)
		aCoords = this.getGeoPoint(aPoint[0], aPoint[1], this.datum(), this.projection());
	return aCoords;
}

SIX.Map.prototype.setBestScale = function (scale)
{
	this.dBestScale = scale;
}

SIX.Map.prototype.getBestScale = function ()
{
	return this.dBestScale;
}

SIX.Map.prototype.showCrosshairs = function ()
{
	var elemCrosshairs = document.getElementById(this.crosshairsUid);
	if (elemCrosshairs)
		elemCrosshairs.style.display = "block";
}

SIX.Map.prototype.hideCrosshairs = function ()
{
	var elemCrosshairs = document.getElementById(this.crosshairsUid);
	if (elemCrosshairs)
		elemCrosshairs.style.display = "none";
}

SIX.Map.prototype.getTopMap = function ()
{
	var map = this;
	while (map.childMap)
	{
	    map = map.childMap;
	}
	return map;
}

SIX.Map.prototype.getBottomMap = function ()
{
	var map = this;
	while (map.parentMap)
	{
	    map = map.parentMap;
	}
	return map;
}

SIX.eListState = {
	NONE : 0, 
	COLLAPSED : 1, 
	EXPANDED : 2
	};

SIX.eLayerCallbacks = {
	ONCHANGEISINSCALE : 1,
	ONCHANGEISSELECTED : 2
};

/**
 * @class Defines a map layer within a service.
 */
SIX.Layer = function (parent,name,id,url,sParams) 
{
	SIX.addRef(this);
	this.uid = SIX.Uid.getUid("layer");	
	SIX.aUidToObject[this.uid] = this;
	
	this.service = null;
	this.parentLayer = null;

	var bInScale = true;
	if (parent.constructor == SIX.Service)
	{
		this.service = parent;
	}
	else if (parent.constructor == SIX.Layer)
	{
		this.parentLayer = parent;
		this.service = parent.service;
		if (!this.service.bLayersOutOfScaleEnabled)
			bInScale = false;
	}

	this.name = this.service.getLayerNameAlias(id, name);
	this.listItemUid = SIX.Uid.getUid("li");
	this.listItem = null;
	this.childLayersList = null;
	this.childLayersListUid = SIX.Uid.getUid("childLayersList");
	this.childLayersListExpandCollapseUid = SIX.Uid.getUid("childLayersListExpandCollapse");
	this.nameDisabledUid = SIX.Uid.getUid("name_disabled");
	this.nameEnabledUid = SIX.Uid.getUid("name_enabled");
	this.visibilityUnselectedEnabledUid = SIX.Uid.getUid("visibility_unselected_enabled");
	this.visibilitySelectedEnabledUid = SIX.Uid.getUid("visibility_selected_enabled");
	this.visibilityUnselectedDisabledUid = SIX.Uid.getUid("visibility_unselected_disabled");
	this.visibilitySelectedDisabledUid = SIX.Uid.getUid("visibility_selected_disabled");
	this.queryUid 		= SIX.Uid.getUid("query");
	this.upUid			= SIX.Uid.getUid("up");
	this.metadataUid	= SIX.Uid.getUid("metadata");
	this.id = id;
	this.url = url;
	
	this.parameters = new SIX.Parameters(sParams ? sParams : "");
	this.doQuery = this.parameters.getParameter("doquery", "boolean", false);
	this.canQuery = this.doQuery ? true : this.parameters.getParameter("canquery", "boolean", true);
	this.bSelected = this.parameters.getParameter("selected", "boolean", false);
	this.sMetadataUrl = this.parameters.getParameter("metadataurl", "string", "");
	
	//If a best index is not specified, the layer will be turned off in best available mode. However, it will be sorted
	//above layers with a best index >= 0.  When a user turns the layer on, the best available mode gets turned off,
	//and the layer displays above whatever best available layers are currently displayed.
	this.nBestIndex = this.parameters.getParameter("bestindex", "number", -1);
	this.bCalculateExtents = this.parameters.getParameter("calculateextents", "boolean", true);
	this.sColor = "";//Color to assign to layer name in layers list.
	this.bHidden = false;
	this.bNeverInList = this.parameters.getParameter("neverinlist", "boolean", false);
	this.bInScale = bInScale;
	this.bOTDF = this.parameters.getParameter("otdf", "boolean", false);//Specifies whether default url is from OTDF server.
	this.sOTDFPath = this.parameters.getParameter("otdfpath", "string", "");//Specifies alternate path for OTDF server requests.
	
	this.bSelectedDefault = this.bSelected;
	this.fnOnSelect = null;
	this.bInList = true;
	this.featureType = "";
	this.loaded = false;
	this.minScale = null;
	this.maxScale = null;
	this.tlLatitude = 0;
	this.tlLongitude = 0;
	this.brLatitude = 0;
	this.brLongitude = 0;
	this.envelopeLL = null;
	this.envelope = null;//Used to store coordinates in coordinate system of view.
	this.coordSys = new SIX.CoordSys();
	this.aSupportedCoordinateSystems = new Array();
	this.aColorsToMask = new Array();
	this.sColorMask = "";
	this.aImageRegion = new Array();
	this.aLayers = new Array();
	this.aClipRegion = new Array();
	this.clipRegionEnvelope = null;
	this._sClipRegion = "";
	this.MAX_CLIP_REGION_LENGTH = SIX.App.isIE ? 1800 : Number.MAX_VALUE;	

	this.imgListButtonOff = null; 
	this.imgListButtonOn = null; 
	this.imgListButtonDisabledOff = null; 
	this.imgListButtonDisabledOn = null;
	
	var parentLayer = this.parentLayer;
	var nLevel = 0;
	while (parentLayer != null)
	{
		nLevel++;
		parentLayer = parentLayer.parentLayer;
	}
	
	this.nListState = this.service.bShowNestedLayers ? (nLevel < this.service.nNumLayerLevelsExpanded ? SIX.eListState.EXPANDED : SIX.eListState.COLLAPSED) : SIX.eListState.NONE;
	this.hCallbackManagers = new SIX.Hash();
	if (this.service.hLayerCallbackManagers.hasItem(id))
	{	
		this.hCallbackManagers = this.service.hLayerCallbackManagers.getItem(id);
		for (var i in this.hCallbackManagers.items)
		{
			if (this.hCallbackManagers.items.hasOwnProperty(i))
			{
				var callbackManager = this.hCallbackManagers.getItem(i);
				for (var j=0; j<callbackManager.aCallbacks.length; j++)
				{
					var callback = callbackManager.aCallbacks[j];
					callback.object = this;
				}
			}
		}
	}	
}

SIX.Layer.prototype.setParentSelection = function()
{
	if (this.bAllChildrenSelected())
		this.bSelected = true;
	else if (this.bNoChildrenSelected())
		this.bSelected = false;
		
	this.service.updateListLayer(this, false);

	if (this.parentLayer)
		this.parentLayer.setParentSelection();
}

SIX.Layer.prototype.bAllChildrenSelected = function()
{
	for (var i=0; i<this.aLayers.length; i++)
	{
		if (!this.aLayers[i].bAllChildrenSelected())
			return false;
	}
	if (this.aLayers.length == 0)
		return this.bSelected;
	else
		return true;
}

SIX.Layer.prototype.bNoChildrenSelected = function()
{
	for (var i=0; i<this.aLayers.length; i++)
	{
		if (!this.aLayers[i].bNoChildrenSelected())
			return false;
	}
	if (this.aLayers.length == 0)
		return !this.bSelected;
	else
		return true;
}

SIX.Layer.prototype.executeCallbacks = function (nType)
{
	if (this.hCallbackManagers.hasItem(nType))
	{
		var callbackManager = this.hCallbackManagers.getItem(nType);
		var aArguments = arguments.length > 1 ? Array.prototype.slice.call(arguments, 1, arguments.length) : new Array();
		callbackManager.executeCallbacks.apply(callbackManager, aArguments);
	}
}

SIX.Layer.prototype.addCallback = function (nType, method, data)
{
	var callbackManager = this.hCallbackManagers.hasItem(nType) ?  this.hCallbackManagers.getItem(nType) : new SIX.CallbackManager();
	callbackManager.addCallback(new SIX.Callback(this, method, (arguments.length >= 3 ? data : null)));
	this.hCallbackManagers.setItem(nType, callbackManager);
}

SIX.Layer.prototype.addLayer = function (layer)
{
	this.aLayers[this.aLayers.length] = layer;
	this.service.allLayers[this.service.allLayers.length] = layer;
	return layer;
}

SIX.Layer.prototype.containsPoint = function (point)
{
	var bContainsPoint = true;
	if (this.envelopeLL && !this.envelopeLL.containsPoint(point))
	{
		bContainsPoint = false;
	}
	else if (this.aImageRegion.length)
	{
		bContainsPoint = false;
		for (var i=0; i<this.aImageRegion.length; i++)
		{
			if (SIX.Util.inRegion(this.aImageRegion[i], point))
			{
				bContainsPoint = true;
				break;
			}
		}
	}
	return bContainsPoint;
}

SIX.Layer.prototype.containsPoints = function (aPoints)
{
	var bContainsPoints = true;
	if (this.envelopeLL)
		bContainsPoints = this.envelopeLL.containsPoints(aPoints);
	
	if (bContainsPoints && this.aImageRegion.length)
	{
		bContainsPoints = false;
		for (var i=0; i<this.aImageRegion.length; i++)
		{
			bContainsPoints = SIX.Util.pointsInRegion(this.aImageRegion[i], aPoints);
			if (bContainsPoints)
				break;
		}	
	}
	return bContainsPoints;
}

SIX.Layer.prototype.setColorMask = function (aColorsToMask, sColorMask, aImageRegion)
{
	this.aColorsToMask = aColorsToMask;
	this.sColorMask = sColorMask;	
	
	var nArrayDimensions = 1;
	if ((typeof aImageRegion[0] == 'object') && (aImageRegion[0].constructor == Array))
	{
		nArrayDimensions = 2;
		if ((typeof aImageRegion[0][0] == 'object') && (aImageRegion[0][0].constructor == Array))
			nArrayDimensions = 3;
	}
	
	switch (nArrayDimensions)
	{
		case 1: 
		{
			if (aImageRegion && (aImageRegion.length >= 3)) //Need at least 3 points to define the image region
			{
				this.aImageRegion[0] = new Array();
				this.aImageRegion[0][0] = aImageRegion;
			}
		}
		break;
		case 2:
		{	
			if (aImageRegion && (aImageRegion.length >= 3)) //Need at least 3 points to define the image region
			{
				this.aImageRegion[0] = new Array();
				//2D array of points of the form [[x0,y0],[x1,y1],...,[xn,yn]]
				//Need to convert to 1D array of SIX.Point objects of the form [point1,point2,...,pointn]
				for (var i=0; i<aImageRegion.length; i++)
				{
					var point = new SIX.Point(aImageRegion[i][0],aImageRegion[i][1]);
					this.aImageRegion[0][i] = point;
				}
			}			
		}
		break;
		case 3:
		{
			for (var i=0; i<aImageRegion.length; i++)
			{
				this.aImageRegion[i] = new Array();
				if (aImageRegion[i] && (aImageRegion[i].length >= 3)) //Need at least 3 points to define the image region
				{
					//2D array of points of the form [[x0,y0],[x1,y1],...,[xn,yn]]
					//Need to convert to 1D array of SIX.Point objects of the form [point1,point2,...,pointn]
					for (var j=0; j<aImageRegion[i].length; j++)
					{
						var point = new SIX.Point(aImageRegion[i][j][0],aImageRegion[i][j][1]);
						this.aImageRegion[i][j] = point;
					}
				}
			}
		}
		break;
		default:
		break;
	}
}

SIX.Layer.prototype.setExtents = function ( tlLatitude, tlLongitude, brLatitude, brLongitude ) 
{
	this.bCalculateExtents = false;
	this.tlLatitude  = tlLatitude;
	this.tlLongitude = tlLongitude;
	this.brLatitude  = brLatitude;
	this.brLongitude = brLongitude;
	this.envelopeLL = new SIX.Envelope(tlLongitude, brLatitude, brLongitude, tlLatitude);
	this.envelope = null;
}

SIX.Layer.prototype.setClipRegion = function (aRegion)
{
	var nArrayDimensions = 1;
	if ((typeof aRegion[0] == 'object') && (aRegion[0].constructor == Array))
		nArrayDimensions = 2;
	
	switch (nArrayDimensions)
	{
		case 1: 
		{
			if (aRegion && (aRegion.length >= 3)) //Need at least 3 points to define the region
			{
				this.aClipRegion = aRegion;
				var dMinX = aRegion[0].x;
				var dMinY = aRegion[0].y;
				var dMaxX = aRegion[0].x;
				var dMaxY = aRegion[0].y;
				//2D array of points of the form [[x0,y0],[x1,y1],...,[xn,yn]]
				//Need to convert to 1D array of SIX.Point objects of the form [point1,point2,...,pointn]
				for (var i=0; i<this.aClipRegion.length; i++)
				{
					var point = this.aClipRegion[i];
					dMinX = Math.min(point.x, dMinX);
					dMinY = Math.min(point.y, dMinY);
					dMaxX = Math.max(point.x, dMaxX);
					dMaxY = Math.max(point.y, dMaxY);						
				}
							
			}
		}
		break;
		case 2:
		{	
			if (aRegion && (aRegion.length >= 3)) //Need at least 3 points to define the image region
			{
				var dMinX = aRegion[0][0];
				var dMinY = aRegion[0][1];
				var dMaxX = aRegion[0][0];
				var dMaxY = aRegion[0][1];
				this.aClipRegion = new Array();
				//2D array of points of the form [[x0,y0],[x1,y1],...,[xn,yn]]
				//Need to convert to 1D array of SIX.Point objects of the form [point1,point2,...,pointn]
				for (var i=0; i<aRegion.length; i++)
				{
					var point = new SIX.Point(aRegion[i][0], aRegion[i][1]);
					this.aClipRegion[i] = point;
					dMinX = Math.min(point.x, dMinX);
					dMinY = Math.min(point.y, dMinY);
					dMaxX = Math.max(point.x, dMaxX);
					dMaxY = Math.max(point.y, dMaxY);						
				}
				this.clipRegionEnvelope = new SIX.Envelope(dMinX, dMinY, dMaxX, dMaxY);
			}			
		}
		break;
		default:
		break;
	}
	this._sClipRegion = "";
}

SIX.Layer.prototype.getClipRegionStr = function ()
{
	if (this._sClipRegion == "")
	{
		var sClipRegion = "";
		var sClipRegionRounded = "";
		for (var i=0; i<this.aClipRegion.length; i++)
		{ 
			var point = this.aClipRegion[i];
			sClipRegion += (sClipRegion != "" ? "," : "") + point.x + "," + point.y;
			sClipRegionRounded += (sClipRegionRounded != "" ? "," : "") + Math.round(point.x) + "," + Math.round(point.y);
		}
		
		if (sClipRegion.length > this.MAX_CLIP_REGION_LENGTH)
			sClipRegion = sClipRegionRounded;
		
		if (sClipRegion.length <= this.MAX_CLIP_REGION_LENGTH)
			this._sClipRegion = sClipRegion;
	}
	return this._sClipRegion;
}

SIX.Layer.prototype.setMetadataUrl = function (sMetadataUrl)
{
	this.sMetadataUrl = sMetadataUrl;
}

SIX.Layer.prototype.setScale = function (minScale, maxScale) 
{
	this.minScale = minScale;
	this.maxScale = maxScale;
}

SIX.Layer.prototype.hasScale = function () 
{
	return (this.minScale != null || this.maxScale != null);
}

/**
 * Sets the layer's coordinate system.
 * @param {Object} coordSys SIX.CoordSys object.
 * @param {String} sDatum Datum.
 * @param {String} sProjection Projection.
 * e.g. layer.setCoordSys("GDA94","GEODETIC");//Datum and projection strings as arguments
 * e.g. layer.setCoordSys(coordSys);//SIX.CoordSys object as argument.
 */
SIX.Layer.prototype.setCoordSys = function ()
{
	if ((arguments.length == 1) && (typeof arguments[0] == "object") && (arguments[0].constructor == SIX.CoordSys))
	{
		//SIX.CoordSys object is the only argument.
		var coordSys = arguments[0];
		this.coordSys = coordSys.clone(this.coordSys);
	}
	else if ((arguments.length == 2) && (typeof arguments[0] == "string") && (typeof arguments[1] == "string"))
	{
		//Datum and projection strings supplied as arguments.
		var sDatum = arguments[0];
		var sProjection = arguments[1];
		this.coordSys.setDatum(sDatum);
		this.coordSys.setProjection(sProjection);
	}
	this.envelope = null;
}

/**
 * Sets the layer's coordinate system.
 * @param {Object} coordSys SIX.CoordSys object.
 * @param {String} sDatum Datum.
 * @param {String} sProjection Projection.
 * e.g. layer.setCoordSys("GDA94","GEODETIC");//Datum and projection strings as arguments
 * e.g. layer.setCoordSys(coordSys);//SIX.CoordSys object as argument.
 */
SIX.Layer.prototype.setSupportedCoordSys = function ()
{
	var coordSys = new SIX.CoordSys();
	if ((arguments.length == 1) && (typeof arguments[0] == "object") && (arguments[0].constructor == SIX.CoordSys))
	{
		//SIX.CoordSys object is the only argument.
		coordSys = arguments[0].clone(coordSys);
	}
	else if ((arguments.length == 2) && (typeof arguments[0] == "string") && (typeof arguments[1] == "string"))
	{
		//Datum and projection strings supplied as arguments.
		var sDatum = arguments[0];
		var sProjection = arguments[1];
		coordSys.setDatum(sDatum);
		coordSys.setProjection(sProjection);
	}
	if (coordSys.initialized() && !this.supportsCoordSys(coordSys))
		this.aSupportedCoordinateSystems[this.aSupportedCoordinateSystems.length] = coordSys;
}

SIX.Layer.prototype.supportsCoordSys = function (coordSys)
{
	var bSupportsCoordSys = false;
	for (var i=0; i<this.aSupportedCoordinateSystems.length; i++)
	{
		var coordSysSupported = this.aSupportedCoordinateSystems[i];
		if (coordSysSupported.initialized() && coordSys.initialized() && coordSys.equals(coordSysSupported))
		{
			bSupportsCoordSys = true;
			break;
		}
	}
	return bSupportsCoordSys;
}

SIX.Layer.prototype.setSelected = function (bSelected)
{
	this.bSelected = bSelected;
	for (var i=0; i<this.aLayers.length; i++)
	{
		var layerChild = this.aLayers[i];
		layerChild.setSelected(bSelected);
	}
}

SIX.Layer.prototype.setListButton = function (sListButtonImgPath)
{
	this.imgListButtonOff = new Image(); 
	this.imgListButtonOff.src = sListButtonImgPath + ".gif";

	this.imgListButtonOn = new Image(); 
	this.imgListButtonOn.src = sListButtonImgPath + "_on.gif";
	
	this.imgListButtonDisabledOff = new Image(); 
	this.imgListButtonDisabledOff.src = sListButtonImgPath + "_disabled.gif";	

	this.imgListButtonDisabledOn = new Image(); 
	this.imgListButtonDisabledOn.src = sListButtonImgPath + "_disabled_on.gif";	
}

SIX.Layer.prototype.expandCollapse = function ()
{
	this.nListState = this.nListState == SIX.eListState.EXPANDED ? SIX.eListState.COLLAPSED : SIX.eListState.EXPANDED;
	
	var elemChildLayersListExpandCollapse = document.getElementById(this.childLayersListExpandCollapseUid);
	if (elemChildLayersListExpandCollapse)
		elemChildLayersListExpandCollapse.src =  SIX.App.imageUrl("images/" + (this.nListState == SIX.eListState.COLLAPSED ? "plus" : "minus"));
		
	var elemChildLayersList = document.getElementById(this.childLayersListUid);
	if (elemChildLayersList)
		elemChildLayersList.style.display = this.nListState == SIX.eListState.COLLAPSED ? "none" : "";
}

SIX.Layer.prototype.build = function ()
{
	if (this.bNeverInList)
		return null;
		
	var svc = this.service;
	var nLayerLevel = 0;
	var parentLayer = this.parentLayer;
	while (parentLayer != null)
	{
		nLayerLevel++;
		parentLayer = parentLayer.parentLayer;
	}

	var parentService = svc.parentService;
	while (parentService && parentService.parentService)
		parentService = parentService.parentService;
	
	var bCollapsed = this.nListState == SIX.eListState.COLLAPSED;
	var bExpanded = this.nListState == SIX.eListState.EXPANDED;
	var sOnClickExpandCollapse = " onclick='" + this.myself + ".expandCollapse(); return false;' ";

	var bFirstVisibleLayerInList = false;
	for (var i=0; i<svc.layersList.length; i++)
	{
		var layerTemp = svc.layersList[i];
		if (layerTemp.bInList)
		{
			if (this == layerTemp)
				bFirstVisibleLayerInList = true;
			break;
		}
	}

	if (this.aLayers.length > 0)
	{
		if (this.bAllChildrenSelected())
			this.bSelected = true;
		else if (this.bNoChildrenSelected())
			this.bSelected = false;
	}

	if (this.listItem)
	{
		svc.updateListLayer(this, true);
		return this.listItem;
	}
	else
	{
		this.listItem = SIX.Util.createElement("li", (this.parentLayer ? "childLayer" : "layer"), this.listItemUid);
		this.listItem.style.whiteSpace = "nowrap";
		if (!this.bInList)
			this.listItem.style.display = "none";
	}

	var bLayerNameEnabled = ((this.bInScale && (!svc.bDisplayBest || !svc.bDisplayBestDisableListLayers)) || (svc.bDisplayBest && this.bSelected));
	var sDisplayUnselected = "style='display:" + (this.bSelected ? "none" : "inline") + "'";
	var sDisplaySelected = "style='display:" + (!this.bSelected ? "none" : "inline") + "'";
	var sDisplaySelectedDisabled = "style='display:none;'";
	
	var bTickVisible = (this.bInScale && !this.bSelected && (!this.bDisplayBest || (this.nBestIndex < 0)));
	var bTickOnVisible = (this.bInScale && this.bSelected && (!this.bDisplayBest || (this.nBestIndex < 0)));
	var bTickDisabledVisible = ((!this.bInScale && !this.bSelected) || (this.bDisplayBest && (this.nBestIndex >= 0) && !this.bSelected));
	var bTickOnDisabledVisible = ((!this.bInScale && this.bSelected) || (this.bDisplayBest && (this.nBestIndex >= 0) && this.bSelected));	
	
	var html = "";
	if (svc.bAllLayersSelectorVisible || (parentService && parentService.bAllLayersSelectorVisible))
		html += "<img class='subPaneIndent' " + SIX.App.imageSrc("images/transparent") + ">";
	html += "<img class='layerIndent' " + SIX.App.imageSrc("images/transparent") + ">";
	for (var i=0; i<nLayerLevel; i++)
		html += "<span class='childLayerIndent'>&nbsp;</span>";

	if (this.nListState != SIX.eListState.NONE)
	{
		if (this.aLayers.length)
			html += "<img id='" + this.childLayersListExpandCollapseUid + "' " + sOnClickExpandCollapse + " alt='' title='' class='expandCollapse' " + SIX.App.imageSrc("images/" + (this.nListState == SIX.eListState.COLLAPSED ? "plus" : "minus")) + ">";
		else 
			html += "<img alt='' title='' style='visibility:hidden' class='expandCollapse' " + SIX.App.imageSrc("images/plus") + ">";//Needed for correct alignment of child layers.
	}

	var sOnClick = " onclick='" + svc.myself + ".layerVisible(" + this.myself + ")' ";
	var sOnLoad = " onload='" + svc.myself + ".updateLayersListWidth()' ";

	if (this.imgListButtonOff && this.imgListButtonOn && this.imgListButtonDisabledOff && this.imgListButtonDisabledOn)
	{
		html += "<img class='layerButton' src='" + this.imgListButtonOff.src + "' alt='' title='Visibility' id='" + this.visibilityUnselectedEnabledUid + "' " +
			sOnLoad + sOnClick + (bTickVisible ? "" : "style='display:none'") + ">" + 
			"<img class='layerButton' src='" + this.imgListButtonOn.src + "' alt='' title='Visibility' id='" + this.visibilitySelectedEnabledUid + "' " +
			sOnLoad + sOnClick + (bTickOnVisible ? "" : "style='display:none'") + ">" + 
			"<img class='layerButton' src='" + this.imgListButtonDisabledOff.src + "' alt='' title='Visibility' id='" + this.visibilityUnselectedDisabledUid + "' " +			
			sOnLoad + sOnClick + (bTickDisabledVisible ? "" : "style='display:none;'") + ">" + 
			"<img class='layerButton' src='" + this.imgListButtonDisabledOn.src + "' alt='' title='Visibility' id='" + this.visibilitySelectedDisabledUid + "' " +
			sOnLoad + sOnClick + (bTickOnDisabledVisible ? "" : "style='display:none;'") + ">";
	}				
	else
	{
		html +=
			"<img class='layerButton' " + SIX.App.imageSrc("images/" + ((this.sColor != "") ? "tick_no_border" : "tick")) + " alt='' title='Visibility' id='" + this.visibilityUnselectedEnabledUid + "' " +
			sOnLoad + sOnClick + ((this.sColor != "") ? ("border='2' style='border-color:" + this.sColor + (bTickVisible ? "" : ";display:none") + "'") : (bTickVisible ? "" : "style='display:none;'")) + ">" + 
			"<img class='layerButton' " + SIX.App.imageSrc("images/" + ((this.sColor != "") ? "tick_no_border_on" : "tick_on")) + " alt='' title='Visibility' id='" + this.visibilitySelectedEnabledUid + "' " +
			sOnLoad + sOnClick + ((this.sColor != "") ? ("border='2' style='border-color:" + this.sColor + (bTickOnVisible ? "" : ";display:none") + "'") : (bTickOnVisible ? "" : "style='display:none;'")) + ">" + 
			"<img class='layerButton' " + SIX.App.imageSrc("images/tick_disabled") + " alt='' title='Visibility' id='" + this.visibilityUnselectedDisabledUid + "' " +
			sOnLoad + sOnClick + (bTickDisabledVisible ? "" : "style='display:none;'") + ">" + 
			"<img class='layerButton' " + SIX.App.imageSrc("images/tick_on_disabled") + " alt='' title='Visibility' id='" + this.visibilitySelectedDisabledUid + "' " +
			sOnLoad + sOnClick + (bTickOnDisabledVisible ? "" : "style='display:none;'") + ">";

		if (!this.parentLayer && svc.bCanReorder) 
		{
			//Only allow top level layers to be reordered.
			html += "<img class='layerButton' " + SIX.App.imageSrc("images/" + (bFirstVisibleLayerInList ? "up" : "up_on")) +
					sOnLoad + " onclick='if(!" + svc.myself + ".bDisplayBest)" + svc.myself + ".reorderLayer(" + this.myself + ")'" +
					" alt='' title='Move layer up' name='" + this.upUid + "'>";
		}
		if (!this.parentLayer && svc.bMetadata) 
		{
			html += "<img class='layerButton' name='" + this.metadataUid + "' id='" + this.metadataUid + "' " + sOnLoad + " onclick='" + svc.myself + ".layerMeta(" + this.myself + ")' " +
					SIX.App.imageSrc("images/info") + " alt='' title='Display layer metadata' border='0'>";
		}
		
		html += "<span id='" + this.nameDisabledUid + "' class='layerTitleDisabled'" + (bLayerNameEnabled ? " style='display:none'" : "") + ">" + this.name + "</span>";			
		html += "<span id='" + this.nameEnabledUid + "' class='layerTitle'" + (bLayerNameEnabled ? "" : " style='display:none'") + ">" + this.name + "</span>";	
	}
	
	if (this.aLayers.length > 0)
	{
		if (!this.childLayersList)
		{
			this.childLayersList = SIX.Util.createElement("ul", "layersList", this.childLayersListUid);
			if (this.nListState != SIX.eListState.EXPANDED)
				this.childLayersList.style.display = "none";
		}
		
		for (var i=0; i<this.aLayers.length; i++)
		{
			var childLayerListItem = this.aLayers[i].build();
			if (childLayerListItem)
				this.childLayersList.appendChild(childLayerListItem);
		}
		
		if (this.childLayersList.parentNode == this.listItem)
			this.listItem.removeChild(this.childLayersList);
			
		this.listItem.innerHTML = html;				
						
		this.listItem.appendChild(this.childLayersList);
	}
	else
	{
		this.listItem.innerHTML = html;
	}

	return this.listItem;
}

SIX.RequestResponse = function (sLayerName, sUrl, sBody, sAction, dTopLeftX, dTopLeftY, dBottomRightX, dBottomRightY, response, nStatus)
{
	this.uid = SIX.Uid.getUid("RequestResponse");
	this.sLayerName = sLayerName;
	this.sUrl = sUrl;
	this.sBody = SIX.Util.escapeMarkup(sBody);
	this.sAction = sAction;
	this.dTopLeftX = dTopLeftX;
	this.dTopLeftY = dTopLeftY;
	this.dBottomRightX = dBottomRightX;
	this.dBottomRightY = dBottomRightY;
	this.response = (typeof response == "string") ? SIX.Util.escapeMarkup(response) : response;
	this.nStatus = nStatus;
	this.date = new Date();//Record time of response.
	this.nTimeStamp = this.date.getTime();
}

SIX.RequestResponse.prototype.getConsoleMessage = function ()
{
	var sSummary = ((this.nStatus == SIXConfig.eRequestResponseStatus.SUCCESS) ? "SUCCESS: " : "ERROR: ") + this.sUrl;
	var sDetail = "<b>Request URL</b><br>" + this.sUrl + "<br><br>";
	sDetail += "<b>Request status</b><br>" + aRequestResponseStatus[this.nStatus].sSummary + "<br><br>";  		
	if (this.sBody && this.sBody != "")
		sDetail += "<b>Request body</b><br>" + this.sBody + "<br><br>";
	if (this.response)
		sDetail += "<b>Response</b><br>" + this.response;
	
	var theConsoleMessage = new SIX.ConsoleMessage(sSummary, sDetail, this);
	
	return theConsoleMessage;
}

SIX.Statistics = function (svc)
{
	SIX.addRef(this);
	this.uid = SIX.Uid.getUid("statistics");
	this.service = svc;
	this.aAllRequests = new Array();
	this.aRequestsByType = new Object();//Associative array
}

SIX.Statistics.prototype.addRequestResponse = function (requestResponse)
{
	var sRequestsArrayIndex = "#" + requestResponse.nStatus;//Associative array index
	if (!this.aRequestsByType[sRequestsArrayIndex])
		this.aRequestsByType[sRequestsArrayIndex] = new Array();
	
	var aRequestsByType = this.aRequestsByType[sRequestsArrayIndex];
	aRequestsByType[aRequestsByType.length] = requestResponse;
	this.aAllRequests[this.aAllRequests.length] = requestResponse;
	
	if (this.service.parentService)
	{
		var parentService = this.service.parentService;
		if (!parentService.statistics.aRequestsByType[sRequestsArrayIndex])
			parentService.statistics.aRequestsByType[sRequestsArrayIndex] = new Array();
		
		var aRequestsByType = parentService.statistics.aRequestsByType[sRequestsArrayIndex];
		aRequestsByType[aRequestsByType.length] = requestResponse;
		parentService.statistics.aAllRequests[parentService.statistics.aAllRequests.length] = requestResponse;	
	}
}

var eServiceUI = {
	NONE : 0,
	LAYERS : 1,
	SEARCH : 2,
	QUERY : 3,
	LEGEND : 4
}

/**
 * @class A SIX.Service object represents a single web map server host. 
 * A service will typically have more than one layer of data that it can serve. 
 */
SIX.Service = function (name,sInterface,host,service,sParams) 
{
	this.bAutoSize = true;
	this.divContentsContainer = null;
	this.div = null;
	this.divFixed = null;
	this.divLayers = null;
	this.ulLayersList = null;
	this.liDisplayBest = null;
	this.divTitle = null;
	this.divSearch = null;
	this.divCurrentSearch = null;
	this.divQuery = null;
	this.searchFormUid = SIX.Uid.getUid("searchForm");
	this.searchListUid = SIX.Uid.getUid("searchList");	
	SIX.addRef(this);
	this.uid = SIX.Uid.getUid("service");
	SIX.aUidToObject[this.uid] = this;

	this.name = name;
	this.parentService = null;
	this.catalog = null;

	this.parameters = new SIX.Parameters(sParams ? sParams : "");
	this.transparency = this.parameters.getParameter("transparency", "number", 100)/100;
	if (this.transparency < 0 || this.transparency > 1) this.transparency = 0; 
	this.active = this.parameters.getParameter("active", "boolean", true);
	this.bHiddenSlider = this.parameters.getParameter("hiddenslider", "boolean", false); 
	this.bAlwaysOn = this.parameters.getParameter("alwayson", "boolean", false); 
	this.bCanShowHideUnselectedLayers = this.parameters.getParameter("canshowhideunselectedlayers", "boolean", false); 
	this.bFeatureServer = this.parameters.getParameter("featureserver", "boolean", false);
	this.bDisplayBestCheckbox = this.parameters.getParameter("displaybest", "boolean", false);
	this.bDisplayBestInLayersList = this.parameters.getParameter("displaybestinlayerslist", "boolean", false);
	this.bDisplayBest = this.bDisplayBestCheckbox;//Defaults to on if display best checkbox is visible
	this.bBuildSearchList = this.parameters.getParameter("buildsearchlist", "boolean", false);
	
	//bLayersOutOfScaleEnabled and bLayersOutsideExtentsEnabled parameters map whether or not layers
	//that are out of scale or outside the view extents get hidden from or disabled in the service window.
	this.bLayersOutOfScaleEnabled = this.parameters.getParameter("layersoutofscaleenabled", "boolean", false);
	this.bLayersOutsideExtentsEnabled = this.parameters.getParameter("layersoutsideextentsenabled", "boolean", false);
	this.bColoredLayers = this.parameters.getParameter("coloredlayers", "boolean", false);
	
	//The backgroundcolor parameter sets the bgcolor parameter in WMS image requests.
	this.sBackgroundColor = this.parameters.getParameter("backgroundcolor", "string", null);

	//ArcIMS servers (and possibly others) do not publish whether or not they can reproject images on the fly.
	//So setting the "canreproject" parameter to false prevents the IIF service from requesting reprojected imagery.
	this.bCanReproject = this.parameters.getParameter("canreproject", "boolean", true); 
	
	this.bShowNestedLayers = this.parameters.getParameter("shownestedlayers", "boolean", false);
	this.nNumLayerLevelsExpanded = this.parameters.getParameter("numlayerlevelsexpanded", "number", 0);
	
	//ArcIMS servers (and possibly others) do not publish which EPSG codes they support.  
	//If EPSG codes are added to the array of supported EPSG codes (below), reprojected image requests are limited to the EPSG codes in this array.
	this.aSupportedEPSGCodes = null;//Associative array
	//If EPSG codes are added to the array of unsupported EPSG codes (below), reprojected image requests cannot contain EPSG codes in this array.
	this.aUnsupportedEPSGCodes = null;//Associative array

	this.tlLatitude = 90;
	this.tlLongitude = -180;
	this.brLatitude = -90;
	this.brLongitude = 180;
	
	this.aServices = new Array();
	this.host = host;
	this.service = service;

	this.nDefaultDPI = 96;
	this.nMaxMapSize = 1048576;//1024 x 1024
	
	this.transparencyDefault = this.transparency;
	this.loaded = false;
	this.layers = new Array();
	this.allLayers = new Array();
	this.layersOrder = new Array();
	this.layersIndices = new Object();
	
	this.layersList = new Array();
	
	this.bFilterDefaultLayersInList = false;
	this.bFilterIgnoreDefaultLayerSelections = false;
	this.bFilterDefaultLayersSelected = false;
	this.aLayerExactMatchFilters = new Array();
	this.aLayerPartialMatchFilters = new Array();
	this.aExcludedLayers = new Array();
	this.aLayerNameAliases = new Array();
	
	this.isOnline = true;
	this.isBusy = false;

	this.busyUid		= SIX.Uid.getUid("busy");
	this.listUid		= SIX.Uid.getUid("list");
	this.upUid			= SIX.Uid.getUid("up");

	this.busyUid2		= SIX.Uid.getUid("busy");
	this.listUid2		= SIX.Uid.getUid("list");
	this.displayBestSelectedUid = SIX.Uid.getUid("displaybest");
	this.displayBestUnselectedUid = SIX.Uid.getUid("displaybest");

	this.bAllLayersSelectorVisible = false;
	this.bAllLayersSelectorOn = true;
	this.allLayersSelectorUid = SIX.Uid.getUid("allLayersSelector");	
		
	this.bHideOutOfScaleLayers = false;
	this.bShowUnselectedLayers = true;	
	this.sShowHiddenLayersLabel = "Show hidden layers";
	this.sHideUnselectedLayersLabel = "Hide unselected layers";
	this.sShowHideUnselectedLayersLabel = this.sHideUnselectedLayersLabel;
	
	this.showHideUnselectedLayersUid = SIX.Uid.getUid("showHideUnselectedLayers");
	this.sliderUid	= SIX.Uid.getUid("slider");
	this.ptrUid		= SIX.Uid.getUid("slider");
	this.sliderLabelUid	= SIX.Uid.getUid("sliderLabel"); 
	this.sliderLabelDisabledUid	= SIX.Uid.getUid("sliderLabelDisabled"); 
	this.bSliderEnabled = true;
	this.nSliderPointerLeftTiePoint = 5;
	this.nSliderPointerLeft = -this.nSliderPointerLeftTiePoint - 1;

	this.sliderX		= 0;
	this.tabUids = new Array(4);
	this.tabUids[0] 	= SIX.Uid.getUid("tab0");
	this.tabUids[1] 	= SIX.Uid.getUid("tab1");
	this.tabUids[2] 	= SIX.Uid.getUid("tab2");
	this.tabUids[3] 	= SIX.Uid.getUid("tab3");
	
	this.nCurrentUI = eServiceUI.NONE;

	var sUI = this.parameters.getParameter("ui", "string", "");
	switch (sUI.toLowerCase())
	{
		case "layers" :
			this.nCurrentUI = eServiceUI.LAYERS;
			break;
		case "search" :
			this.nCurrentUI = eServiceUI.SEARCH;
			break;		
		case "query" :
			this.nCurrentUI = eServiceUI.QUERY;
			break;	
		case "legend" :
			this.nCurrentUI = eServiceUI.LEGEND;
			break;
		default: 
			this.nCurrentUI = eServiceUI.NONE;
			break;												
	}
	
	this.searchUid		= SIX.Uid.getUid("search");	
	this.searches = new Array();
	this.currentSearch = -1;
	this.bSortSearch = false;
	
	this.tGISOverlaySetVisible = null;
	
	this.queries = null;

	this.nMapLoadState = SIX.eMapLoadState.NOT_LOADED;
	this.nLayersLoadState = SIX.eLayersLoadState.NOT_LOADED;

	this.colors = new Object();
	this.colorMask = 0;					// set to non-zero for masking of JPEGs
	
	this.nNumConsecutiveLoadLayersFailed = 0;
	this.tLoadLayers = null;
	
	var accessClass = SIX.Access;
	for (var name in SIX.Access)
	{
		if (SIX.Access.hasOwnProperty(name) && name.toLowerCase() == sInterface.toLowerCase())
		{
			accessClass = SIX.Access[name];
			break;
		}
	}
	this.access = new accessClass(this);
	
	this.bMetadata = this.parameters.getParameter("metadata", "boolean", this.access.hasMetadata);
	
	//Don't allow layer reordering for services containing child services.
	if (!(this.access instanceof SIX.Access.ECWP) && 
		((this.host == null) || (this.host == "")) &&
		((this.service == null) || (this.service == "")))
		this.bCanReorder = false;
	else
		this.bCanReorder = this.parameters.getParameter("canreorder", "boolean", this.access.canReorder);
	
	this.legendUrl = "";
	
	this.bChangeSearch = false;	// are we changing the search we want to see

	this.statistics = new SIX.Statistics(this);

	SIX.App.addMouseAction(this.sliderUid,this,5,true);
	SIX.App.addMouseAction(this.ptrUid,this,5,true);
	
	this.aFeatureLimitedLayers = new Array();
	this.childServicesSlider = null;
	this.tSetChildServicesTransparenciesTimeout = null;
	this.tSetChildServicesTransparenciesInterval = null;
	
	this.hCallbackManagers = new SIX.Hash();
	//Allows callbacks to be defined for layer objects created dynamically via SIX.Service.LoadLayers().
	this.hLayerCallbackManagers = new SIX.Hash();
	
	this.aAttachedServices = new Array();
	this.aServicesAttachedTo = new Array();	
}

SIX.Service.prototype.performQueries = function (map)
{
	if (this.loaded && this.queries && this.queries.aQueries.length)
		this.access.queryLayers(map);
}

SIX.Service.prototype.addFeatureLimitedLayer = function (aLayerIds, sDatabaseTable, nFeatures, nMinScale, nMaxScale)
{
	featureLimitedLayer = new SIX.FeatureLimitedLayer(this, aLayerIds, sDatabaseTable, nFeatures, nMinScale, nMaxScale);

	this.aFeatureLimitedLayers[this.aFeatureLimitedLayers.length] = featureLimitedLayer;
	return featureLimitedLayer;
}

SIX.FeatureLimitedLayer = function (svc, aLayerIds, aMaxFeatures, sDatabaseTable, nMinScale, nMaxScale)
{
	this.service = svc;
	
	if (aLayerIds.length != aMaxFeatures.length)
		alert("LayerIds array must be same size as MaxFeatures array");
		
	this.aLayerIds = aLayerIds;
	this.aMaxFeatures = aMaxFeatures;
	this.aLayers = new Array(); //Populated later by loadMap
	this.aLayerIndices = new Array(); //Populated later by loadMap
	this.databaseTable = sDatabaseTable;

	var maxFeatures = 0;
	for (var i=0; i<aMaxFeatures.length; i++)
		if (aMaxFeatures[i] > maxFeatures)
			maxFeatures = aMaxFeatures[i];
	
	this.maxFeatures = maxFeatures;
	this.minScale = nMinScale ? nMinScale : 0;
	this.maxScale = nMaxScale ? nMaxScale : 1000000;
}

var FeatureCountRendererLocal = new Object();
FeatureCountRendererLocal.cache = new Object();
FeatureCountRendererLocal.getNumFeaturesLimited = 
function (sDatabaseTable, x1, y1, x2, y2, maxFeatures, callBack)
{
	var queryHash = sDatabaseTable + x1 + x2 + y1 + y2;
	if (this.cache[queryHash] && this.cache[queryHash].maxFeatures >= maxFeatures)
		callBack(this.cache[queryHash].numFeatures)
	else
		FeatureCountRenderer.getNumFeaturesLimited(sDatabaseTable, x1, y1, x2, y2, maxFeatures, cacheFeatureCountResponse);
	
	var me = this;
	function cacheFeatureCountResponse(numFeatures)
	{
		me.cache[queryHash] = {numFeatures:numFeatures, maxFeatures:maxFeatures};
		callBack(numFeatures);
	}
}

SIX.FeatureLimitedLayer.prototype.applyFeatureCount = function (map, callBack)
{
	var svc = this.service;
	var fLayer = this;
	
	if (!this.aLayers || this.aLayers.length == 0)
		return;
	x1 = map.getTopLeftLongitude();
	y1 = map.getTopLeftLatitude();
	x2 = map.getBottomRightLongitude();
	y2 = map.getBottomRightLatitude();
	
	svc.nMapLoadState = SIX.eMapLoadState.LOADING;
	
    if (typeof FeatureCountRenderer != 'undefined')
	    FeatureCountRendererLocal.getNumFeaturesLimited(this.databaseTable, x1, y1, x2, y2, this.maxFeatures, setLayerVisibilityByFeatureCountResponse);
	else
		setLayerVisibilityByFeatureCountResponse(this.maxFeatures);

	function setLayerVisibilityByFeatureCountResponse(numFeatures)
	{
		for (var i=0; i<fLayer.aLayers.length; i++)
		{
			var layer = fLayer.aLayers[i];
			var bInScaleOld = layer.bInScale;
			layer.bInScale = numFeatures < layer.maxFeatures;
			if (layer.bInScale != bInScaleOld)
				svc.updateListLayer(layer, true);
		}
			
		callBack();
	}
}

SIX.Service.prototype.init = function () 
{
	if (!this.div)
	{
		this.div = document.createElement("div");
		this.div.style.position = "relative";
		this.div.style.top = "0px";
		this.div.style.left = "0px";
		if (!this.bAutoSize)
			this.div.style.height = "100%";		
	}
		
	return this.div;
}

SIX.Service.prototype.clearLoadLayersTimer = function ()
{
	if (this.tLoadLayers != null)
	{
		clearTimeout(this.tLoadLayers);
		this.tLoadLayers = null;
	}	
}

SIX.Service.prototype.addService = function (service)
{
	this.aServices[this.aServices.length] = service;
	service.setParentService(this);
	return service;
}

SIX.Service.prototype.setParentService = function (service)
{
	this.parentService = service;
	if (service.catalog)
		this.setCatalog(service.catalog);
}

SIX.Service.prototype.setCatalog = function (catalog)
{
	this.catalog = catalog;
	for (var i=0; i<this.aServices.length; i++)
		this.aServices[i].setCatalog(catalog);
}

SIX.Service.prototype.getChildService = function (nChildServiceIndex) 
{
	var serviceChild = null;
	if (this.aServices.length > nChildServiceIndex)
		serviceChild = this.aServices[nChildServiceIndex];
	return serviceChild;
}

/**
*	PURPOSE: add a layer to a service
*/
SIX.Service.prototype.addLayer = function (layer) 
{
	this.layersOrder[this.layersOrder.length] = this.layers.length;	
	this.layersIndices[layer.uid] = this.layers.length;
	this.layers[this.layers.length] = layer;
	this.allLayers[this.allLayers.length] = layer;
	this.nLayersLoadState = SIX.eLayersLoadState.LOADED;	
	return layer;
}

SIX.Service.prototype.addCallback = function (nType, method, data)
{
	var callbackManager = this.hCallbackManagers.hasItem(nType) ?  this.hCallbackManagers.getItem(nType) : new SIX.CallbackManager();
	callbackManager.addCallback(new SIX.Callback(this, method, (arguments.length >= 3 ? data : null)));
	this.hCallbackManagers.setItem(nType, callbackManager);
}

SIX.Service.prototype.executeCallbacks = function (nType)
{
	if (this.hCallbackManagers.hasItem(nType))
	{
		var callbackManager = this.hCallbackManagers.getItem(nType);
		var aArguments = arguments.length > 1 ? Array.prototype.slice.call(arguments, 1, arguments.length) : new Array();
		callbackManager.executeCallbacks.apply(callbackManager, aArguments);
	}
}

SIX.Service.prototype.addLayerCallback = function (layerId, nType, method, data)
{
	var hCallbackManagers = this.hLayerCallbackManagers.hasItem(layerId) ? this.hLayerCallbackManagers.getItem(layerId) : new SIX.Hash();
	var callbackManager = hCallbackManagers.hasItem(nType) ?  hCallbackManagers.getItem(nType) : new SIX.CallbackManager();
	callbackManager.addCallback(new SIX.Callback(null, method, (arguments.length >= 4 ? data : null)));
	hCallbackManagers.setItem(nType, callbackManager);
	this.hLayerCallbackManagers.setItem(layerId, hCallbackManagers);
}

/**
*	MODIFICIATION: added sGroup.  Key so that searches can be grouped depending on external variable in toolbar.
*
*/
SIX.Service.prototype.addSearch = function (search) 
{
	this.searches[this.searches.length] = search;
	return search;
}

SIX.Service.prototype.addQueries = function (queries)
{
	this.queries = queries;
	return queries;	
}

SIX.Service.prototype.setActive = function (active) {
	this.active = active;
	if (this.loaded)
	{	
		for (var i=0; i <this.catalog.maps.length; i++)
		{
			var map = this.catalog.maps[i];
			var control = document[map.uid];
			if (control.GetLayerIndex(this.uid) != -1)
				map.setLayerParameter(this.uid, "visibility=" + (active ? "TRUE" : "FALSE"));
			for (var j=0; j<this.aServices.length; j++)
			{
				if (control.GetLayerIndex(this.aServices[j].uid) != -1)
					map.setLayerParameter(this.aServices[j].uid,"visibility=" + (active ? "TRUE" : "FALSE"));
			}			
		}
		for (var i=0; i<this.aAttachedServices.length; i++)
			this.aAttachedServices[i].setActive(active);
		for (var i=0; i<this.aServices.length; i++)
		{
			var svcChild = this.aServices[i];
			for (var j=0; j<svcChild.aAttachedServices.length; j++)
				svcChild.aAttachedServices[j].setActive(active);	
		}	
	}	
	else if (this.active)
	{
		this.build();
	}
}

/**
*	for refreshes
*/

SIX.Service.prototype.update = function () {
	if (this.nCurrentUI != eServiceUI.NONE && this.nCurrentUI != eServiceUI.SEARCH)
		this.build();
}

SIX.Service.prototype.buildFixed = function () {
	if (this.bAlwaysOn && this.bHiddenSlider && !(this.bDisplayBestCheckbox && !this.bDisplayBestInLayersList))
		return null;	
	
	if (!this.div)
		return null;
	 
	var sHtml = "";	
	if (!this.bAlwaysOn || !this.bHiddenSlider || (this.bDisplayBestCheckbox && !this.bDisplayBestInLayersList)) {
		if (this.bDisplayBestCheckbox && !this.bDisplayBestInLayersList)
		{
			sHtml += "<div class='displayBestContainer'>" + 
			"<span class='displayBestLabel'>Best available</span>" +
			"<img class='displayBestCheckbox' " + SIX.App.imageSrc("images/tick") + " alt='Display best available images' title='Display best available images' id='" + this.displayBestUnselectedUid + "'" +
			" onclick='" + this.myself + ".onSelectDisplayBest()' " + (this.bDisplayBest ? "style='display:none;'" : "") + ">" + 
			"<img class='displayBestCheckbox' " + SIX.App.imageSrc("images/tick_on") + " alt='Display best available images' title='Display best available images' id='" + this.displayBestSelectedUid + "'" +
			" onclick='" + this.myself + ".onSelectDisplayBest()' " + (this.bDisplayBest ? "" : "style='display:none;'") + ">" + 
			"</div>";
		}			
			
		var bGotSelectedListLayers = false;
		for (var i=0; i<this.layers.length; i++)
		{
			var layer = this.layers[i];
			if (layer.bSelected && layer.bInScale && layer.nBestIndex < 0 && !layer.bNeverInList)
			{
				bGotSelectedListLayers = true;
				break;
			}
		}					
			
		this.bSliderEnabled = (!this.bDisplayBest || bGotSelectedListLayers);
					
		var sTransparencyToolTip = "Change Transparency. HINT: Transparency only applies when two or more images are turned on.";
		sHtml +=	"<div class='sliderContainer' style='display:" + (this.bHiddenSlider ? "none" : "block") + "'>" +
		"<span id='" + this.sliderLabelUid + "' class='sliderLabel' " + (!this.bSliderEnabled ? "style='display:none'" : "") + ">Transparency:</span>" +
		"<span id='" + this.sliderLabelDisabledUid + "' class='sliderLabelDisabled' " + (!this.bSliderEnabled ? "" : "style='display:none'") + ">Transparency:</span>" +
		"<div class='slider'>" +		
		"<img id='" + this.sliderUid + "' class='sliderBar' alt='" + sTransparencyToolTip + "' title='" + sTransparencyToolTip + "'" + SIX.App.imageSrc("images/" + (!this.bSliderEnabled ? "slider_line_disabled" : "slider_line")) + ">" +
		"<img id='" + this.ptrUid + "' alt='" + sTransparencyToolTip + "' title='" + sTransparencyToolTip + "' class='sliderPointer' " + ((this.nSliderPointerLeft + this.nSliderPointerLeftTiePoint) >= 0 ? "style='left:" + this.nSliderPointerLeft + "px'" : "") + SIX.App.imageSrc("images/" + (!this.bSliderEnabled ? "slider_ptr_disabled" : "slider_ptr")) + ">" +
		"</div></div>";
	}
	
	if (!this.divFixed)
	{
		this.divFixed = document.createElement("div");
		this.divFixed.style.position = "relative";
		this.divFixed.style.top = "0px";
		this.divFixed.style.left = "0px";
		this.divFixed.style.width = "100%";
		this.divFixed.style.height = "20px";
		this.divFixed.style.paddingTop = "2px";
				
	}
	this.divFixed.innerHTML = sHtml;
	return this.divFixed;
}

SIX.Service.prototype.buildLayers = function () 
{
	if (this.nCurrentUI == eServiceUI.NONE)
		return null;
	
	//if (this.bGroupedService && !this.aServices.length)
		//return null;

	if (!this.divLayers)
		this.divLayers = document.createElement("div");
	
	while (this.divLayers.hasChildNodes())
		this.divLayers.removeChild(this.divLayers.lastChild);
	
	if (this.layers.length) 
	{
		if (!this.ulLayersList)
			this.ulLayersList = SIX.Util.createElement("ul", "layersList");
				
		this.ulLayersList.style.position = "static";
		this.ulLayersList.style.width = "auto";	

		if (this.layers.length && this.bAllLayersSelectorVisible)
		{
			if (!this.divTitle)
			{
				this.divTitle = SIX.Util.createElement("div", "serviceTitle");
				
				this.divTitle.innerHTML = "<img" + SIX.App.imageSrc("images/service_tick" + (this.bAllLayersSelectorOn ? "_on" : "")) + 
					" alt='Turn all layers on/off' title='Turn all layers on/off'' name='" + this.allLayersSelectorUid + "' id='" + this.allLayersSelectorUid + "'" +
					"onclick='" + this.myself + ".toggleVisibleLayersSelectionState(" + i + ")'>&nbsp;&nbsp;" + this.name;
			}
			this.divLayers.appendChild(this.divTitle);
		}	
		
		this.divLayers.appendChild(this.ulLayersList);
				
		this.layersList = new Array();
		
		if (this.bDisplayBestCheckbox && this.bDisplayBestInLayersList)
		{
			if (!this.liDisplayBest)
			{
				this.liDisplayBest = SIX.Util.createElement("li", "layer");
				this.liDisplayBest.style.whiteSpace = "nowrap";
				this.liDisplayBest.style.borderTopWidth = "0px";
				var html = "";
				if (this.bAllLayersSelectorVisible)
					html += "<img class='subPaneIndent' " + SIX.App.imageSrc("images/transparent") + ">";
				html += "<img class='layerIndent' " + SIX.App.imageSrc("images/transparent") + ">";

				if (this.bShowNestedLayers)
					html += "<img alt='' title='' style='visibility:hidden' class='expandCollapse' " + SIX.App.imageSrc("images/plus") + ">";//Needed for correct alignment of child layers.

				html +="<img class='layerButton' " + SIX.App.imageSrc("images/tick") + " alt='Display best available images' title='Display best available images' id='" + this.displayBestUnselectedUid + "'" +
					" onclick='" + this.myself + ".onSelectDisplayBest()' " + (this.bDisplayBest ? "style='display:none;'" : "") + ">" + 
					"<img class='layerButton' " + SIX.App.imageSrc("images/tick_on") + " alt='Display best available images' title='Display best available images' id='" + this.displayBestSelectedUid + "'" +
					" onclick='" + this.myself + ".onSelectDisplayBest()' " + (this.bDisplayBest ? "" : "style='display:none;'") + ">";

				if (this.bCanReorder) 
					html += "<img class='layerButton' " + SIX.App.imageSrc("images/up") + " style='visibility:hidden' alt='' title='' border='0'>";

				if (this.bMetadata) 
					html += "<img class='layerButton' " + SIX.App.imageSrc("images/info") + " style='visibility:hidden' alt='' title='' border='0'>";
						
				html += "<span class='layerTitle'>Best available image layer</span>";
				this.liDisplayBest.innerHTML = html;
			}
			this.ulLayersList.appendChild(this.liDisplayBest);
		}			
		
		if (!this.bGroupedService)
		{
			for (var i=0; i<this.layers.length; i++) 
			{
				var layersI = this.layersOrder[i];
				var layer = this.layers[layersI];
				
				if (layer.bNeverInList)
					continue;
				
				this.layersList[this.layersList.length] = layer;
				var elemListItem = layer.build();
				if (elemListItem)
					this.ulLayersList.appendChild(elemListItem);
			}
		}
		
		this.updateLayersListWidth();		
	}
	else 
	{
		if (this.nLayersLoadState != SIX.eLayersLoadState.LOADED)		
			this.divLayers.innerHTML = "<span class='legend'>&nbsp;&nbsp; Requesting " + this.name + " details...</span>";
	}
	
	if (this.childServicesSlider)
	{
		this.setChildServicesTransparencies();
		var elemChildServicesSlider = this.childServicesSlider.init();
		this.divLayers.innerHTML = "";
		this.divLayers.appendChild(elemChildServicesSlider);	
	}
		
	return this.divLayers;
}

SIX.Service.prototype.deleteEcwpLayer = function (map, layer)
{
	var bEcwpLayerDeleted = false;
	if (this.access instanceof SIX.Access.ECWP)
	{
		var control = document[map.uid];
		if (layer.loaded) 
		{
			var nLayerIndex = control.GetLayerIndex(layer.uid);
			if (nLayerIndex != -1)
			{
				map.deleteLayer(layer.uid);
				bEcwpLayerDeleted = true;
				map.cacheEcwpLayer(layer);				
			}
			layer.loaded = false;
			if (layer.parentLayer)		
				layer.parentLayer.loaded = false;	
		}	
		for (var i=0; i<layer.aLayers.length; i++)
		{
			if (this.deleteEcwpLayer(map, layer.aLayers[i]))
				bEcwpLayerDeleted = true;
		}	
	}
	return bEcwpLayerDeleted;
}

SIX.Service.prototype.deleteLayers = function (map)
{
	if (this.access instanceof SIX.Access.ECWP)
	{
		for(var i=0; i<this.layers.length; i++)
			this.deleteEcwpLayer(map, this.layers[i]); 
	}
	else
	{
		for (var i=0; i<this.layers.length; i++)
		{
			delete this.layers[i];
			delete this.layersOrder[i];
		}
		delete this.layersOrder;
		delete this.layers;
		delete this.layersIndices;
		
		this.layersOrder = new Array();
		this.layers = new Array();
		this.layersIndices = new Object();
		
		if (!this.aServices.length)
			this.nLayersLoadState = SIX.eLayersLoadState.NOT_LOADED;
	}
}

SIX.Service.prototype.sortLayers = function (a,b)
{
    aa = a.name.toLowerCase();
    bb = b.name.toLowerCase();
    if (aa==bb) return 0;
    if (aa<bb) return -1;
    return 1;
}

SIX.Service.prototype.buildLegend = function () {
	return this.legendUrl;
}

/**
* lists search titles - dropdown if more than one.
*	MOD: Accounts for group searches
*/
SIX.Service.prototype.buildSearch = function () 
{
	if ( !this.searches.length || (!this.access.hasSearch && !this.bFeatureServer))
	{
		if (!this.divSearch)
			this.divSearch.createElement("div");
		this.divSearch.innerHTML = "No search available";
	}
	else 
	{		
		if (this.bBuildSearchList)
		{
			if( this.searches.length > 1 ) 
			{
				var txt = "<div class='searchListContainer'>";			
				txt += "<form id='" + this.searchFormUid + "' name='" + this.searchFormUid + "'>";
				txt += "<select name='" + this.searchUid + "' class='searchList' id='" + this.searchUid + "'" +
					" size=" + this.searches.length + " onFocus='javascript:" + this.myself + ".onFocusSearch()'" +
					" onChange='javascript:" + this.myself + ".setCurrentSearch(this[this.selectedIndex].value)'>";

				var sCompareValue = "";	// hard code 
				var sOptionDisplay = "";
				
				var bFirstTime = true;
				
				if (this.bSortSearch)
					this.searches.sort(this.sortFunction);		
				
				var txtOptions = "";
				for( var j=0; j<this.searches.length; j++) {
					
					// if grouped search, assign what item it belongs to
					if (this.access.hasGroupedSearch)
						{
						sOptionDisplay = this.searches[j].optionDisplay;
						}
					
					if ((this.access.hasGroupedSearch && (sCompareValue == sOptionDisplay)) || !this.access.hasGroupedSearch)	
						{
						// first time we are looping through || or we are using group search to regenerate the list
						if (bFirstTime && !this.bChangeSearch || bFirstTime && this.currentSearch == -1)	
							{
							this.currentSearch = j;
							this.bChangeSearch = false;
							bFirstTime = false;
							}
							
						txtOptions += "<option value='" + this.searches[j].uid + "'";
						if( j == this.currentSearch )
							{
							txtOptions += " selected";
							}
						txtOptions += ">" + this.searches[j].name + "</option>";
						}
					}
				if (txtOptions == "")
					{
					txtOptions += "<option value=''>No Searches Available for this selection</option>";
					}	
				
				txt += txtOptions;	
				txt += "</select>";
				txt += "</form>";
				txt += "</div>";	
			}
			else if (this.searches.length == 1)
			{	
				this.currentSearch = 0;
				txt += "<span class='searchTitle'>" + this.searches[this.currentSearch].name + "</span>";
			}
			
			if (!this.divSearch)
				this.divSearch = document.createElement("div");
			this.divSearch.innerHTML = txt;
			
			if (!isNaN(this.currentSearch) && this.currentSearch > -1)
			{			
				this.divCurrentSearch = this.searches[this.currentSearch].init();
				if (this.divCurrentSearch)
					this.divSearch.appendChild(this.divCurrentSearch);
			}				
		}
		else
		{
			if (!isNaN(this.currentSearch) && this.currentSearch > -1) 
			{
				if (!this.catalog || (this.catalog && (this.catalog.currentSearchService == this)))
				{
					if (!this.divSearch)
						this.divSearch = document.createElement("div");			
						
					this.divCurrentSearch = this.searches[this.currentSearch].init();
					if (this.divCurrentSearch)
						this.divSearch.appendChild(this.divCurrentSearch);
				}
			}		
		}
	}	
	return this.divSearch;	
}

SIX.Service.prototype.searchResponse = function (map)
{
	var currentSearch = this.searches[this.currentSearch];
	
	if (currentSearch.sResponseFunction)
	{
		eval(this.access.myself + "." + currentSearch.sResponseFunction + "()");
	}
	else 
	{
		this.access.searchResponse(map);
	}

	if (currentSearch.bResetHiddenFieldsAfterFirstLoad)
	{
		for (var i=0; i<currentSearch.fields.length; i++)
		{
			var field = currentSearch.fields[i];
			if (field.style == "hidden")
				field.value = "";
		}
	}

	if (currentSearch.bZoomToFirstFeature || currentSearch.bZoomToFirstFeatureOnStartup)
	{
		if (currentSearch.bGotResults)
		{
			currentSearch.selectResult(0);
			currentSearch.bZoomToFirstFeatureOnStartup = false;
		}
	}
}

/**
*	PURPOSE: sorts the array of search objects
*
*/
SIX.Service.prototype.sortFunction = function (search1, search2)
{
	if (search1["name"]<search2["name"]) 
		{
		retVal=-1;
		}
	else if (search1["name"]>search2["name"]) 
		{
		retVal=1;
		}
	else
		{ 
		retVal=0;
		}
	return retVal;
}

SIX.Service.prototype.getSearchIndex = function (search) 
{ 
	var nIndex = -1;
	for(var i=0; i<this.searches.length; i++) 
	{
		if (search == this.searches[i]) 
		{
			nIndex = i;
			break;
		}
	}
	return nIndex;
}

SIX.Service.prototype.moveSearch = function (nSrcIndex,nDestIndex) 
{ 
	var bSetIndex = false;
	
	if ((nSrcIndex >= 0) && (nSrcIndex < this.searches.length) &&
		(nDestIndex >= 0) && (nDestIndex < this.searches.length))
	{
		var search = this.searches[nSrcIndex];
		this.searches.splice(nSrcIndex, 1);//Remove search at nSrcIndex
		this.searches.splice(nDestIndex, 0, search);//Add search at nDestIndex
		bSetIndex = true;
	}
	return bSetIndex;
}

SIX.Service.prototype.setCurrentSearch = function (searchUid) 
{ 
	var currentSearchService = this.catalog.currentSearchService;
	//Reset the previous search fields.
	if (currentSearchService && currentSearchService.currentSearch > -1)
	{
		var searchCurrent = currentSearchService.searches[currentSearchService.currentSearch];
		if (searchCurrent.uid != searchUid)
			searchCurrent.resetForm();
	}

	for( var i=0; i<this.searches.length; i++ ) 
	{
		var search = this.searches[i];
		if (search.uid == searchUid) 
		{
			if (!((currentSearchService == this) && (this.currentSearch == i)))
			{
				if (currentSearchService.divCurrentSearch && currentSearchService.divCurrentSearch.parentNode)
					currentSearchService.divCurrentSearch.parentNode.removeChild(currentSearchService.divCurrentSearch);			
				this.currentSearch = i;
				this.bChangeSearch = true;
				this.initializeCurrentSearch();
			}
			break;
		}
	}
}

SIX.Service.prototype.initializeCurrentSearch = function () 
{
	if (this.currentSearch > -1)
	{
		var search = this.searches[this.currentSearch];	
		this.catalog.currentSearchService = this;
		if (search.mapService && search.mapService.catalog)			
			search.mapService.catalog.currentSearchService = this;	
			
		if (this.nCurrentUI != eServiceUI.NONE) 
		{		
			if (this.bFeatureServer || this.nCurrentUI == eServiceUI.SEARCH)
			{
				this.build();

				var elemResultsTable = document.getElementById(search.resultsField.uid);
				if (elemResultsTable)
					ts_makeSortable(elemResultsTable);
				if (search.bSubmitOnLoad)
				{
					var searchForm = document.forms[search.uid];
					if (searchForm)
						searchForm.submit();
					if (search.bSubmitOnFirstLoadOnly)
						search.bSubmitOnLoad = false;
				}	
				this.onFocusSearch();
			}
			else
				this.setUI(eServiceUI.SEARCH);
		}
	}
}

SIX.Service.prototype.onFocusSearch = function () 
{
	if (this.nCurrentUI != eServiceUI.NONE && (this.currentSearch > -1)) 
	{
		var search = this.searches[this.currentSearch];
		search.setFocusOnFirstField();
	}
}

SIX.Service.prototype.buildQuery = function () 
{
	if (this.queries && this.queries.aQueries.length)
	{
		var queries = this.queries;
		var bExportColumn = (queries.bExportColumn ? true : false);
		var bPrintButton = (queries.sPrintFunction ? true : false);
		var nWidthPercent = Math.floor(100 / queries.nNumColumns);	
			
		if (!this.divQuery)
			this.divQuery = SIX.Util.createElement("div", "queryResultsOuter");
		
		var queryText = "<div class='queryResultsBackground'>&nbsp;</div><div id='" + queries.resultsContainerUid + "' class='queryResults'>";
		queryText += "<table class='" + (queries.bColumnHeadings ? "sortable" : "queryResultsTable") + "' name='" + queries.uid + "' id='" + queries.uid + "'>";			
		if (queries.bColumnHeadings)
		{
			queryText += "<tr>" +
			"<td " + (SIX.App.isIE ? "" : "width='" + nWidthPercent + "%'") + " title='Click to sort' class='resultsHeadingsField'>" + 
			"<span class='queryResultsWindowText'>" + queries.aColumnHeadings[0] + "</span>" +
			"</td>";
			
			for (var i=1; i<=queries.nNumResultsColumns; i++)
			{
				queryText += "<td " + (SIX.App.isIE ? "" : "width='" + nWidthPercent + "%'") + " title='Click to sort' class='resultsHeadingsField'>" + 
				"<span class='queryResultsWindowText'>" + queries.aColumnHeadings[i] + "</span>";
				if (i == this.queries.nNumResultsColumns)
					queryText += "<select id='" + queries.zoneSelectUid + "' class='queryResultsWindowSelect' onchange='" + queries.myself + ".changeCurrentQueryZone(this.selectedIndex);'></select>";
				queryText += "</td>";
			}
			
			if (bExportColumn)
			{
				queryText += "<td " + (SIX.App.isIE ? "" : "width='" + nWidthPercent + "%'") + " title='Click to sort' class='resultsHeadingsField'>" + 
				"<span class='queryResultsWindowText'>" + queries.aColumnHeadings[queries.aColumnHeadings.length - 1] + "</span>" + 
				"<input type='checkbox' alt='Select all' title='Select all' checked onclick='" + this.access.myself + ".onSelectAll(event);' border='0'>";
				if (this.queries.sExportFunction)
				{
					queryText += "<a href='#'><img src='" + queries.imageExportOff.src + "' alt='" + queries.sExportDescription + "' title='" + queries.sExportDescription + "'" +
					" onclick='" + this.access.myself + "." + queries.sExportFunction + "();' border='0'></a>";
				}		
				queryText += "</td>";	
			}	
				
			/*Under Firefox, the other cells will only expand to the occupy the full table width if the following hidden cell is present.*/
			queryText += (SIX.App.isIE ? "" : "<td style='visibility:hidden;'>&nbsp;</td>");	
				
			queryText += "</tr>";
		}
		queryText += "<tr>"; //Blank row
		for (var i=0; i<queries.nNumColumns; i++)
			queryText += "<td " + (SIX.App.isIE ? "" : "width='" + nWidthPercent + "%'") + " class='" + queries.sResultClass + "'>&nbsp;</td>";	

		/*Under Firefox, the other cells will only expand to the occupy the full table width if the following hidden cell is present.*/
		queryText += (SIX.App.isIE ? "" : "<td style='visibility:hidden;'>&nbsp;</td>");	
						
		queryText += "</tr>" +				
		"</table>" +
		"</div>";
		if (bPrintButton)
		{
			queryText += "<img style='position:absolute;top:2px;right:20px' src='" + queries.imagePrintOff.src + "' alt='" + queries.sPrintDescription + "' title='" + queries.sPrintDescription + "'" +
				" onclick='" + this.access.myself + "." + queries.sPrintFunction + "();' border='0'>";
		} 		 
		this.divQuery.innerHTML = queryText;
	}
	return this.divQuery;
}

SIX.Service.prototype.buildStatistics = function ()
{
	var sText = "";
	if (this.statistics)
	{
		sText = (SIX.App.isIE ? "<div style='height:100%;overflow:auto;border-bottom:solid 20px transparent'>" : "") +
				"<table class='sortable'" + (SIX.App.isIE ? "" : " style='overflow:auto;border-bottom:solid 20px transparent'") + " cellspacing='1' cellpadding='1'" +
				" name='" + this.statistics.uid + "' id='" + this.statistics.uid + "'>" + 
				"<tr>" +
				"<td " + (SIX.App.isIE ? "" : "width='50%'") + " class='consoleTableHeading' title='Click to sort'>Response Status</td>" + 
				"<td " + (SIX.App.isIE ? "" : "width='50%'") + " class='consoleTableHeading' title='Click to sort'>Number</td>" + 
				/*Under Firefox, the other cells will only expand to the occupy the full table width if the following hidden cell is present.*/
				(SIX.App.isIE ? "" : "<td style='visibility:hidden;'>&nbsp;</td>") + 
				"</tr>" +					
				"</table>" + 
				(SIX.App.isIE ? "</div>" : "");
	}
	return sText;
}

SIX.Service.prototype.updateStatistics = function ()
{
	var elemTable = document.getElementById(this.statistics.uid);
	if (elemTable)
	{
		var nodeTableBody = elemTable.getElementsByTagName("tbody").item(0);
		for (var i in this.statistics.aRequestsByType)
		{
			if (this.statistics.aRequestsByType.hasOwnProperty(i))
			{
				var nStatus = parseInt(i.substring(1,i.length));
				var nNumRequests = this.statistics.aRequestsByType[i].length;	
				var nodeRow = document.createElement("tr"); 
				
				var nodeRequestResponseStatusCell = SIX.Util.createElement("td", "consoleTableCell");
				var textNodeRequestResponseStatus = document.createTextNode(aRequestResponseStatus[nStatus].sSummary);
				nodeRequestResponseStatusCell.appendChild(textNodeRequestResponseStatus);
				nodeRow.appendChild(nodeRequestResponseStatusCell);
				
				var nodeNumRequestsCell = SIX.Util.createElement("td", "consoleTableCell");
				
				var textNodeNumRequests = document.createTextNode("" + nNumRequests);
				nodeNumRequestsCell.appendChild(textNodeNumRequests);
				nodeRow.appendChild(nodeNumRequestsCell);
					
				nodeTableBody.appendChild(nodeRow);
			}
		}
	}
}

SIX.Service.prototype.queryResponse = function (map, response)
{
	this.access.queryResponse(map, response);	
	if (this.queries.currentQuery >= 0 &&  this.queries.currentQuery < this.queries.aQueries.length)
	{
		var query = this.queries.aQueries[this.queries.currentQuery];
		query.executeCallbacks(SIX.eQueryCallbacks.ONRESPONSE);
	}
}

SIX.Service.prototype.build = function () 
{
	var map = this.catalog.maps[0];
	
	if (this.nLayersLoadState == SIX.eLayersLoadState.NOT_LOADED)
		this.loadLayers(map);

	if (this.aServices.length && this.loaded)
	{
		this.deleteLayers(this.catalog.maps[0]);
			 		
		//If SIX.Service contains more than one service, need to merge and sort the various service layers for display.
		for (var i=0; i<this.aServices.length; i++)
		{	
			for (var j=0; j<this.aServices[i].layers.length; j++)
			{
				var childServiceLayer = this.aServices[i].layers[this.aServices[i].layersOrder[j]];
				this.layersIndices[childServiceLayer.uid] = this.layers.length;
				this.layers[this.layers.length] = childServiceLayer;
				this.allLayers[this.allLayers.length] = childServiceLayer;
			}
		}	
		//this.layers.sort(this.sortLayers);
		for (var i=0; i<this.layers.length; i++) 
			this.layersOrder[i] = i;
	}
	
	for (var i=0; i<this.layers.length; i++)
	{
		var layersI = this.layersOrder[i];
		var layer = this.layers[layersI];
	
		layer.bInList = this.bHideOutOfScaleLayers ? (layer.bInScale ? layer.bSelected : false) : true;
		layer.bInList = this.bShowUnselectedLayers ? layer.bInList : layer.bSelected;
		layer.bHidden = !layer.bInList;		
	}
	
	if (this.nCurrentUI == eServiceUI.NONE || !this.div)
		return;
		
	var divContents = null;
	if (this.bFeatureServer)
	{
		if (this.queries && this.queries.aQueries.length)
			divContents = this.buildQuery();
		else
			divContents = this.buildSearch();		
	}
	else if (this.nCurrentUI == eServiceUI.LAYERS)
		divContents = this.buildLayers();
	else if (this.nCurrentUI == eServiceUI.LEGEND)
		divContents = this.buildLegend();
	else if (this.nCurrentUI == eServiceUI.SEARCH)
		divContents = this.buildSearch();		
	else if (this.nCurrentUI == eServiceUI.QUERY)
		divContents = this.buildQuery();

	if (divContents)
	{
		if (this.nCurrentUI == eServiceUI.LAYERS)
		{
			var divFixed = this.buildFixed();
			if (divFixed && divFixed.parentNode != this.div)
				this.div.appendChild(divFixed);
		}
					
		if (!this.divContentsContainer)
		{
			this.divContentsContainer = document.createElement("div");
			this.divContentsContainer.style.position = this.bAutoSize ? "relative" : "absolute";
			this.divContentsContainer.style.top = this.divFixed && !this.bAutoSize ? "20px" : "0px";
			this.divContentsContainer.style.left = "0px";
			if (!this.bAutoSize)
			{
				this.divContentsContainer.style.bottom = "0px";
				this.divContentsContainer.style.right = "0px";	
				this.divContentsContainer.style.overflow = "auto";
			}
		}
		
		this.divContentsContainer.appendChild(divContents);
		
		if (this.divContentsContainer.parentNode != this.div)
			this.div.appendChild(this.divContentsContainer);
		
		var service = this;
		if (!this.bAutoSize && SIX.App.isIE && !SIX.App.isIE7up)
		{ 
			var fnOnResize = function () {
					with({service : service})
					{
						var div = service.div;
						if (div)
						{
							if (div.offsetHeight)
								service.divContentsContainer.style.height = (div.offsetHeight - (service.divFixed ? service.divFixed.offsetHeight : 0)) + "px";
							if (div.offsetWidth)
								service.divContentsContainer.style.width = div.offsetWidth + "px";	
						}		
					}
				}	
			this.div.onresize = fnOnResize;
			SIX.App.addBuildCallback(fnOnResize);
		}		
	}	
}

SIX.Service.prototype.busy = function (busy) 
{
	this.isBusy = busy;
	var element = document[this.busyUid];
	if( element )
		element.src  = SIX.App.imageUrl("images/busy" + (busy ? "_on" : ""));
	element = document[this.busyUid2];
	if( element )
		element.src  = SIX.App.imageUrl("images/busy" + (busy ? "_on" : ""));
}

SIX.Service.prototype.updateListLayer = function (layer, bRecurse) 
{
	if (layer.listItem)
		layer.listItem.style.display = layer.bInList ? "" : "none";

	var bFirstVisibleLayerInList = false;
	for (var i=0; i<this.layersList.length; i++)
	{
		var layerTemp = this.layersList[i];
		if (layerTemp.bInList)
		{
			if (layer == layerTemp)
				bFirstVisibleLayerInList = true;
			break;
		}
	}

	var elemUp = document.getElementById(layer.upUid);
	if (elemUp)
		elemUp.src = SIX.App.imageUrl("images/" + (bFirstVisibleLayerInList ? "up" : "up_on"));

	var bLayerNameEnabled = ((layer.bInScale && (!this.bDisplayBest || (layer.nBestIndex < 0))) || (this.bDisplayBest && layer.bSelected));
	var elemLayerNameDisabled = document.getElementById(layer.nameDisabledUid);
	if (elemLayerNameDisabled)
		elemLayerNameDisabled.style.display = bLayerNameEnabled ? "none" : "";

	var elemLayerNameEnabled = document.getElementById(layer.nameEnabledUid);
	if (elemLayerNameEnabled)
		elemLayerNameEnabled.style.display = bLayerNameEnabled ? "" : "none";	
	
	var elemVisibilityUnselectedEnabled = document.getElementById(layer.visibilityUnselectedEnabledUid);
	if (elemVisibilityUnselectedEnabled)
		elemVisibilityUnselectedEnabled.style.display = (layer.bInScale && !layer.bSelected && (!this.bDisplayBest || (layer.nBestIndex < 0))) ? "" : "none";	

	var elemVisibilitySelectedEnabled = document.getElementById(layer.visibilitySelectedEnabledUid);
	if (elemVisibilitySelectedEnabled)
		elemVisibilitySelectedEnabled.style.display = (layer.bInScale && layer.bSelected && (!this.bDisplayBest || (layer.nBestIndex < 0))) ? "" : "none";

	var elemVisibilityUnselectedDisabled = document.getElementById(layer.visibilityUnselectedDisabledUid);
	if (elemVisibilityUnselectedDisabled)
		elemVisibilityUnselectedDisabled.style.display = ((!layer.bInScale && !layer.bSelected) || (this.bDisplayBest && (layer.nBestIndex >= 0) && !layer.bSelected)) ? "" : "none";

	var elemVisibilitySelectedDisabled = document.getElementById(layer.visibilitySelectedDisabledUid);
	if (elemVisibilitySelectedDisabled)
		elemVisibilitySelectedDisabled.style.display = ((!layer.bInScale && layer.bSelected) || (this.bDisplayBest && (layer.nBestIndex >= 0) && layer.bSelected)) ? "" : "none";	

	if (bRecurse)
	{
		for (var i=0; i<layer.aLayers.length; i++)
		{
			var childLayer = layer.aLayers[i];
			this.updateListLayer(childLayer, true);
		}
		
		if (layer.aLayers.length > 0)
			layer.setParentSelection();
		else if (layer.parentLayer)
			layer.parentLayer.setParentSelection();
	}
}

SIX.Service.prototype.layerVisibleNoUpdate = function (layer) 
{
	layer.setSelected(!layer.bSelected);
	if (layer.bSelected)
		layer.bHidden = this.bHideOutOfScaleLayers ? (layer.bInScale ? false : layer.bHidden) : false;
	
	this.updateListLayer(layer, true);
}

SIX.Service.prototype.layerVisible = function (layer) 
{
	if (this.bDisplayBest)
	{
		if (layer.nBestIndex >= 0)
			this.onSelectDisplayBest();
	}
	
	this.layerVisibleNoUpdate(layer);
	
	var bEnableSlider = !this.bDisplayBest;
	if (this.bDisplayBest)
	{
		for (var j=0; j<this.layers.length; j++)
		{
			var theLayer = this.layers[j];
			if (theLayer.nBestIndex < 0 && theLayer.bInScale && theLayer.bSelected)
			{
				bEnableSlider = true;
				break;
			} 
		}
	}
	this.setSliderEnabled(bEnableSlider);
	
	//LayerVisible() is called when a layer is checked on or off.  
	//To avoid a long delay before the checkbox selection state changes,
	//UpdateMaps() must be called via the setTimeout() method.
	setTimeout(this.myself + ".updateMaps()", 250);
	if (layer.fnOnSelect)
		layer.fnOnSelect();
}

SIX.Service.prototype.setVisibleLayersSelectionState = function (bSelected) 
{
	this.bAllLayersSelectorOn = bSelected;
	var element = document.getElementById(this.allLayersSelectorUid);
	if (element)
		element.src = SIX.App.imageUrl("images/" + (this.bAllLayersSelectorOn ? "service_tick_on" : "service_tick"));
		
	for (var i=0; i<this.layers.length; i++)
	{
		if (this.layers[i].bInList)
			this.layers[i].setSelected(bSelected);
	}
	
    for (var i=0; i<this.aAttachedServices.length; i++)
    {
        var svcAttached = this.aAttachedServices[i];
        var bChangeAttachedServiceSelection = true;
        if (!bSelected)
        {
            for (var j=0; j<svcAttached.aServicesAttachedTo.length; j++)
            {
                var svcAttachedTo = svcAttached.aServicesAttachedTo[j];
                if (svcAttachedTo != this)
                {
            	    for (var k=0; k<svcAttachedTo.layers.length; k++)
                    {
	                    if (svcAttachedTo.layers[k].bSelected)
	                    {
		                    bChangeAttachedServiceSelection = false;
		                    break;
		                }
                    }
                    if (!bChangeAttachedServiceSelection) break;
                }
            }
        }
        if (bChangeAttachedServiceSelection)
            svcAttached.setVisibleLayersSelectionState(bSelected);
    }	
	
	this.build();
	
	if (this.loaded && this.active)
	{
		for (var i=0; i < this.catalog.maps.length; i++)
		{
			var map = this.catalog.maps[i];
			var control = document[map.uid];
			this.updateMap(map);
			if (control.GetLayerIndex(this.uid) != -1)
				map.setLayerParameter(this.uid,"visibility=" + (bSelected ? "true" : "false"));
			for (var j=0; j<this.aServices.length; j++)
			{
				if (control.GetLayerIndex(this.aServices[j].uid) != -1)
					map.setLayerParameter(this.aServices[j].uid,"visibility=" + (bSelected ? "true" : "false"));
			}				
		}
	}
}

SIX.Service.prototype.toggleVisibleLayersSelectionState = function () 
{
	this.bAllLayersSelectorOn = !this.bAllLayersSelectorOn;
	var element = document.getElementById(this.allLayersSelectorUid);
	if (element)
		element.src = SIX.App.imageUrl("images/" + (this.bAllLayersSelectorOn ? "service_tick_on" : "service_tick"));
	setTimeout(this.myself + ".setVisibleLayersSelectionState(" + (this.bAllLayersSelectorOn ? "true" : "false") + ")", 100);
}

SIX.Service.prototype.showHideUnselectedLayers = function ()
{
	this.setUnselectedLayersVisibility(!this.bShowUnselectedLayers);
}

SIX.Service.prototype.setUnselectedLayersVisibility = function (bVisible)
{
	if (this.setUnselectedLayersVisibilityNoUpdate(bVisible) && this.nCurrentUI != eServiceUI.NONE) 
	{
		this.build();
		if (!this.bHiddenSlider)
			this.setSliderPosition(Math.round(this.transparency * 100));
	}		
}

SIX.Service.prototype.setUnselectedLayersVisibilityNoUpdate = function (bVisible)
{
	var bChanged = false;
	this.bShowUnselectedLayers = bVisible;
		
	var element = document.getElementById(this.showHideUnselectedLayersUid);
	if (element)
	{
		this.sShowHideUnselectedLayersLabel = (this.bShowUnselectedLayers ? this.sHideUnselectedLayersLabel : this.sShowHiddenLayersLabel);
		element.value = this.sShowHideUnselectedLayersLabel;
	}
	
	var map = this.catalog.maps[0];	
	bChanged = map.hiddenLayers(this, !this.bShowUnselectedLayers);
	return bChanged;
}

SIX.Service.prototype.layerQuery = function (i) {
	var layer = this.layers[i];
	layer.doQuery = !layer.doQuery;
	var element = document[layer.queryUid];
	if( element ) {
		if( layer.doQuery )
			element.src = SIX.App.imageUrl("images/query_on");
		else
			element.src = SIX.App.imageUrl("images/query");
	}
}

SIX.Service.prototype.layerMeta = function (layer) 
{	
	if (this.bMetadata)
	{
		if (layer.sMetadataUrl != "")
		{ 
			SIX.App.metadataPopupWin.populate('<iframe src="' + layer.sMetadataUrl + '" width="' + (SIX.App.width/2 - 20) + '" height="' + (SIX.App.height/2 - 20) + '" scrolling="auto" frameborder="1"></iframe>');
			SIX.App.metadataPopupWin.setSize(SIX.App.width/2,SIX.App.height/2);
			SIX.App.metadataPopupWin.offsetX = 45;
			SIX.App.metadataPopupWin.offsetY = 15;
			SIX.App.metadataPopupWin.autoHide();
			SIX.App.metadataPopupWin.showPopup(layer.metadataUid);
		}
	}
}

SIX.Service.prototype.loadLayers = function (map) 
{	
	if (!this.loaded || !map.loaded || !this.isOnline || this.bFeatureServer)
	{
		this.nLayersLoadState = SIX.eLayersLoadState.NOT_LOADED;
		
		if (!this.loaded || !map.loaded || !this.isOnline)
		{
			var channelCurrent = SIX.App.getCurrentChannel();
			if (channelCurrent.console)
			{
				var theConsole = channelCurrent.console;
				theConsole.setHealth(this, 0);
				for (var i=0; i<this.aServices.length; i++)
					theConsole.setHealth(this.aServices[i], 0);
			}
		}
		
		return;
	}
	if (this.aServices.length)
	{
		this.nLayersLoadState = SIX.eLayersLoadState.LOADING;
		
		for (var i=0; i<this.aServices.length; i++)
		{
			this.aServices[i].access.loadLayers(map);
			if (this.aServices[i].nLayersLoadState == SIX.eLayersLoadState.NOT_LOADED)
				this.nLayersLoadState = SIX.eLayersLoadState.NOT_LOADED;
		}
	}
	else
	{		
		this.access.loadLayers(map);
	}
}

SIX.Service.prototype.applyFeatureCount = function (map)
{
	var svc = this;
	if (svc.aFeatureLimitedLayers.length)
	{
		var mapScale = map.scale;
		var limitedLayers = svc.aFeatureLimitedLayers.length;
		for (var i=0; i<svc.aFeatureLimitedLayers.length; i++)
		{
			var fLayer = svc.aFeatureLimitedLayers[i];
			if (fLayer.minScale && mapScale < fLayer.minScale)
			{
				limitedLayers--;
				continue;
			}

			fLayer.aLayers = new Array();
			fLayer.aLayerIndices = new Array();
			var bNeedToCheckLayer = false;
			for (var j=0; j<svc.layers.length; j++)
			{
				var layer = svc.layers[j];
				for (var k=0; k<fLayer.aLayerIds.length; k++)
				{
					if (layer.id == fLayer.aLayerIds[k])
					{
						if (layer.bInScale && layer.bSelected)
						{
							layer.maxFeatures = fLayer.aMaxFeatures[k];
							fLayer.aLayers[fLayer.aLayers.length] = layer;
							fLayer.aLayerIndices[fLayer.aLayerIndices.length] = j;
							bNeedToCheckLayer = true;
						}
						if (mapScale > fLayer.maxScale)
						{
							bNeedToCheckLayer = false;
							layer.bInScale = false;
						}	
					}
				}
			}
			if (bNeedToCheckLayer)
				fLayer.applyFeatureCount(map, loadMap);
			else
				limitedLayers--;
		}
		if (limitedLayers == 0)
			loadMap();
	}	else
	{
		loadMap();
	}
	
	function loadMap()
	{
		svc.access.loadMap(map);
	}
}

SIX.Service.prototype.loadMap = function (map) 
{
	var objGISOverlayMapsLoadStats = map.getGISOverlayMapsLoadStats();
	if (!objGISOverlayMapsLoadStats.nNumGISOverlayMapsLoading)
	{
		map.resetGISOverlayMapsLoadState();
		map.setGISOverlayProgress(0);	
	}
	if (this.aServices.length)
	{
		for (var i=0; i<this.aServices.length; i++)
		{
			this.aServices[i].applyFeatureCount(map);
		}
	}	
	else 
	{
		if (this.nCurrentUI == eServiceUI.NONE)
			map.hiddenLayers(this);
		this.applyFeatureCount(map);
	}
}		

SIX.Service.prototype.updateMap = function (map) 
{
	this.access.updateMap(map);
}		

SIX.Service.prototype.updateMaps = function () 
{
	if (this.loaded && this.active)
	{
		for (var i=0; i < this.catalog.maps.length; i++)
			this.updateMap(this.catalog.maps[i]);
	}
}

SIX.Service.prototype.online = function (onlineStatus) {
	this.isOnline = onlineStatus;
	if( onlineStatus && this.active )
		this.active = false;
	else
		this.active = true;
	this.activate();
}

SIX.Service.prototype.meta = function () {
	alert("Service meta data");
}

SIX.Service.prototype.hideColor = function (color,percent,scale,userColor) {
	if( this.colorMask ) {
		var maskedColor = parseInt(color.substr(1),16) & this.colorMask;
		color = "#" + maskedColor.toString(16);
	}
	if( this.loaded ) {
		for( var i=0; i < this.catalog.maps.length; i++ ) {
			var control = document[this.catalog.maps[i].uid];
			control.SetLayerTransparency(this.uid,color,percent/100);
		}
	}
	if( this.colors[color] )
		delete this.colors[color];
	if( percent != 100 ) {
		var item = new Object();
		item.color = color;
		item.percent = percent;
		item.scale = scale;
		item.userColor = userColor;		
		this.colors[color] = item;
	}
}

SIX.Service.prototype.hideColors = function (colorArray) {
	for (var i=0; i<colorArray.length; i++)
	{
		for (var j=0; j<this.aServices.length; j++)
			this.aServices[j].hideColor(colorArray[i],0,0,false);
		this.hideColor(colorArray[i],0,0,false);
	}
}

SIX.Service.prototype.setSupportedEPSGCodes = function (aEPSGCodes) 
{
	if (!this.aSupportedEPSGCodes)
		this.aSupportedEPSGCodes = new Object();
	for (var i=0; i<aEPSGCodes.length; i++)
		this.aSupportedEPSGCodes["#" + aEPSGCodes[i]] = true;
}

SIX.Service.prototype.setUnsupportedEPSGCodes = function (aEPSGCodes) 
{
	if (!this.aUnsupportedEPSGCodes)
		this.aUnsupportedEPSGCodes = new Object();
	for (var i=0; i<aEPSGCodes.length; i++)
		this.aUnsupportedEPSGCodes["#" + aEPSGCodes[i]] = true;
}

SIX.Service.prototype.supportsEPSG = function (nEPSG)
{
	var bSupportsEPSG = false;
	if (GDTEPSGMappings.getDatumProjection(nEPSG))
	{
		bSupportsEPSG = true;
		if ((this.aSupportedEPSGCodes && !this.aSupportedEPSGCodes["#" + nEPSG]) ||
			(this.aUnsupportedEPSGCodes && this.aUnsupportedEPSGCodes["#" + nEPSG]))
			bSupportsEPSG = false;
	}	
	return bSupportsEPSG;
}

SIX.Service.prototype.setLegendUrl = function (url) {
	this.legendUrl = url;
	this.build();
}

SIX.Service.prototype.needLegend = function () {
	return (this.nCurrentUI == eServiceUI.LEGEND);
}

SIX.Service.prototype.action = function (action,moving,x,y) {
	if (this.nCurrentUI == eServiceUI.NONE || action != 5 || !this.bSliderEnabled)
		return;
	var percent = 0;
	var sliderElement = document.getElementById(this.sliderUid);
	if (sliderElement) 
	{
		var nWidth = sliderElement.offsetWidth;
		nWidth = nWidth ? nWidth : 100;
		var nSliderPosX = SIX.Util.findPosX(sliderElement);
		if (nSliderPosX > 0) 
			percent = Math.round(100 * (x - nSliderPosX) / nWidth);
		if (percent > 100)
			percent = 100;
		else if (percent < 0)
			percent = 0;			
	}
			
	this.setSliderPosition(percent);
	this.setTransparency(percent);
}

SIX.Service.prototype.setSliderPosition = function (nPercent)
{
	if (nPercent < 0)
		nPercent = 0;
	else if (nPercent > 100)
		nPercent = 100;
	var elemPtr = document.getElementById(this.ptrUid);
	var elemSlider = document.getElementById(this.sliderUid);
	if (elemPtr && elemSlider && elemSlider.offsetWidth)
	{
		this.nSliderPointerLeft = Math.round(nPercent * elemSlider.offsetWidth / 100 - this.nSliderPointerLeftTiePoint);
		elemPtr.style.left = this.nSliderPointerLeft + "px";
	}
}

SIX.Service.prototype.setSliderEnabled = function (bEnabled)
{
	this.bSliderEnabled = bEnabled;

	var elemPtr = document.getElementById(this.ptrUid);
	var elemSlider = document.getElementById(this.sliderUid);
	
	if (elemPtr && elemSlider)
	{
		elemPtr.src = SIX.App.imageUrl("images/" + (bEnabled ? "slider_ptr" : "slider_ptr_disabled"));
		elemSlider.src = SIX.App.imageUrl("images/" + (bEnabled ? "slider_line" : "slider_line_disabled"));
		if (!bEnabled && elemSlider.offsetWidth)
		{
			this.nSliderPointerLeft = elemSlider.offsetWidth - this.nSliderPointerLeftTiePoint;
			elemPtr.style.left = this.nSliderPointerLeft + "px";
		}
	}

	var elemSliderLabel = document.getElementById(this.sliderLabelUid);
	var elemSliderLabelDisabled = document.getElementById(this.sliderLabelDisabledUid);
	
	if (elemSliderLabel && elemSliderLabelDisabled)
	{
		elemSliderLabel.style.display = (bEnabled ? "" : "none");
		elemSliderLabelDisabled.style.display = (bEnabled ? "none" : ""); 
	}
}

SIX.Service.prototype.setTransparency = function (percent) {
	this.transparency = percent/100;
	if( this.loaded ) {
		if( this.access instanceof SIX.Access.ECWP ) 
		{
			var layerTop;
			var nLayersLoaded = 0;
			for( var i=0; i<this.layers.length; i++ ) 
			{
				var layer = this.layers[this.layersOrder[i]];
				if (layer.loaded) 
				{
					if (!layerTop)
					{
						layerTop = layer;
					}
					else
					{
						for (var j=0; j<this.catalog.maps.length; j++) 
						{
							var control = document[this.catalog.maps[j].uid];
							control.SetLayerTransparency(layer.uid,"#", 1.0);
							for (var k=0; k<layer.aLayers.length; k++) 
								control.SetLayerTransparency(layer.aLayers[k].uid,"#",1.0);
						}
					}
					nLayersLoaded++;
				}
			}
			if (layerTop)
			{
				//Only set transparency on top layer if there is more than one layer loaded.
				this.transparency = (nLayersLoaded > 1 ? this.transparency : 1.0);
				for (var j=0; j<this.catalog.maps.length; j++) 
				{
					var control = document[this.catalog.maps[j].uid];
					control.SetLayerTransparency(layerTop.uid,"#", this.transparency);
					for (var k=0; k<layerTop.aLayers.length; k++) 
						control.SetLayerTransparency(layerTop.aLayers[k].uid,"#",this.transparency);
				}
			}
		}
		else {
			for( var j=0; j < this.catalog.maps.length; j++ ) {
				var control = document[this.catalog.maps[j].uid];
				control.SetLayerTransparency(this.uid, "#", this.transparency);
			}
		}
	}
}

SIX.Service.prototype.setUI = function (nCurrentUI) {
	var nOldUI = this.nCurrentUI;
	this.nCurrentUI = nCurrentUI;
	if (this.nCurrentUI == eServiceUI.NONE)
		return;
	if (nCurrentUI != nOldUI)
		this.build();
	if( this.legendUrl == "" && this.access.hasLegend && this.catalog.maps.length && this.nCurrentUI == eServiceUI.LEGEND)
		this.loadMap(this.catalog.maps[0]);
}

SIX.Service.prototype.reorderLayer = function (layer) 
{
	var nIndex = this.layersIndices[layer.uid];
	var i = this.getLayerOrder(nIndex);
	if (!this.bCanReorder || i < 1 || i >= this.layers.length )
		return;
	var swapi = this.layersOrder[i];
	for(j=i; j>0; j--)
		this.layersOrder[j] = this.layersOrder[j-1];
	this.layersOrder[0] = swapi;
	this.build();
	// when reordering, only one call is needed - all layers are sorted out
	for(var j=0; j<this.catalog.maps.length; j++) {
		this.access.orderLayers(this.catalog.maps[j]);
		this.updateMap(this.catalog.maps[j]);
	}
}

SIX.Service.prototype.setLayerNameAlias = function (sId, sName, sAlias)
{
	var objAlias = new Object();
	objAlias.sId = sId;
	objAlias.sName = sName;
	objAlias.sAlias = sAlias;
	this.aLayerNameAliases[this.aLayerNameAliases.length] = objAlias;
}

SIX.Service.prototype.getLayerNameAlias = function (sId, sName)
{
	var sAlias = sName;
	for (var i=0; i<this.aLayerNameAliases.length; i++)
	{
		var alias = this.aLayerNameAliases[i];
		if (alias.sId != "")
		{
			if (alias.sId.toLowerCase() == sId.toLowerCase())
			{
				sAlias = alias.sAlias;
				break;
			}	
		}
		else if (alias.sName.toLowerCase() == sName.toLowerCase())
		{
			sAlias = alias.sAlias;
			break;
		}
	}
	return sAlias;
}

SIX.Service.prototype.setLayerFilterDefaults = function (bFilterDefaultLayersInList, bFilterIgnoreDefaultLayerSelections, bFilterDefaultLayersSelected)
{
	this.bFilterDefaultLayersInList = bFilterDefaultLayersInList;
	this.bFilterIgnoreDefaultLayerSelections = (arguments.length < 2 ? false : bFilterIgnoreDefaultLayerSelections);
	this.bFilterDefaultLayersSelected = (arguments.length < 3 ? false : bFilterDefaultLayersSelected);
}

SIX.Service.prototype.addLayerFilter = function (sIdKey, sNameKey, bExactMatch, bExcludeMatch, bSelected)
{
	var objFilter = new Object();
	objFilter.sIdKey = sIdKey.toLowerCase();
	objFilter.sNameKey = (arguments.length < 2 ? "" : sNameKey.toLowerCase());
	objFilter.bExcludeMatch = (arguments.length < 4 ? false : bExcludeMatch);
	objFilter.bSelected = (arguments.length < 5 ? false : bSelected);
	if (bExactMatch)
		this.aLayerExactMatchFilters[this.aLayerExactMatchFilters.length] = objFilter;
	else
		this.aLayerPartialMatchFilters[this.aLayerPartialMatchFilters.length] = objFilter;
}

SIX.Service.prototype.filterLayer = function (sLayerId, sLayerName)
{	
	var objResult = new Object();
	//If no layer name filters include layer in list but set to unselected
	if (!this.aLayerPartialMatchFilters.length && !this.aLayerExactMatchFilters.length)
	{	
		objResult.bInList = true;
		objResult.bSelected =  false;
		return objResult;
	} 
	
	objResult.bInList = this.bFilterDefaultLayersInList;
	objResult.bSelected = this.bFilterDefaultLayersSelected;
	
	var bGotExactMatch = false;
	var sLayerIdLowerCase = sLayerId.toLowerCase();
	var sLayerNameLowerCase = sLayerName.toLowerCase();
	
	//Check exact match filters first.
	for (var i=0; i<this.aLayerExactMatchFilters.length; i++)
	{
		var objFilter = this.aLayerExactMatchFilters[i];
		if (((sLayerIdLowerCase == objFilter.sIdKey) && (objFilter.sIdKey != "")) ||
			((sLayerNameLowerCase == objFilter.sNameKey) && (objFilter.sNameKey != "")))
		{
			bGotExactMatch = true;
			objResult.bInList = !objFilter.bExcludeMatch;
			objResult.bSelected = objFilter.bSelected;
			break;
		}
	}

	if (!bGotExactMatch)
	{
		//Check partial match filters in order in which filters are added.
		for (var i=0; i<this.aLayerPartialMatchFilters.length; i++)
		{
			var objFilter = this.aLayerPartialMatchFilters[i];
			if (((sLayerIdLowerCase.indexOf(objFilter.sIdKey) > -1) && (objFilter.sIdKey != "")) ||
				((sLayerNameLowerCase.indexOf(objFilter.sNameKey) > -1) && (objFilter.sNameKey != "")))	
			{
				objResult.bInList = !objFilter.bExcludeMatch;
				objResult.bSelected = objFilter.bSelected;
			}
		}
	}
	
	return objResult;
}

/**
 * Map service layers may only display at certain scales.  However, different 
 * map services (WMS, ArcIMS etc) calculate scale in different ways.  So to 
 * ensure only layers which can be displayed are active (not disabled or hidden) 
 * within the service window's layer list, we need to determine the scale 
 * calculated by the map service.  
 * @param {Object} map SIX.Map object displaying the map server layers.
 */
SIX.Service.prototype.getScale = function (map)
{
	return (this.access.getScale(map));
}

/**
 * Calculates the map dimensions for a particular map request taking into account
 * the maximum map request size allowed for this service.  
 * @param {Object} map SIX.Map object displaying the map server layers.
 */
SIX.Service.prototype.getMapDimensions = function (map)
{
	var aDimensions = new Array();
	var nMapWidth = map.width;
	var nMapHeight = map.height;
	
	var nMapSize = map.width * map.height;
    if (nMapSize > this.nMaxMapSize)
    {
		var nScaleFactor = Math.sqrt(this.nMaxMapSize / nMapSize);
		//Following logic ensures aspect of requested image is as close as possible to map.width / map.height 
		if (map.width > map.height) 
		{
			nMapWidth = Math.floor(nScaleFactor * map.width);
			nMapHeight = Math.floor(nMapWidth * map.height / map.width); 
		}
		else 
		{
			nMapHeight = Math.floor(nScaleFactor * map.height);
			nMapWidth = Math.floor(nMapHeight * map.width / map.height); 		
		}	
	}

	aDimensions[0] = nMapWidth;
	aDimensions[1] = nMapHeight;
	return aDimensions;
}

/**
 * @class SIX.Catalog objects contain a collection of services (map servers) that can be shown in a map. 
 */
SIX.Catalog = function (channel)
{
	SIX.addRef(this);
	this.div = null;
	this.divSearch = null;
	this.name = "Catalog";
	channel.cats[channel.cats.length] = this;
	this.channel = channel;
	this.services = new Array();
	this.servicesOrder = new Array();
	this.maps = new Array();
	this.searchFormUid = SIX.Uid.getUid("searchForm");
	this.searchListUid = SIX.Uid.getUid("searchList");
	this.currentSearchService = 0;
	this.currentQueryService = 0;
	this.bDisplayBest = false;
	this.fnBuildSearch = null;
}

/*
** Builds search window which can be associated with one or more services.
*/
SIX.Catalog.prototype.buildSearch = function ()
{
	var nListSize = 0;
	var txtOptions = "";
	var bFirstTime = true;
	for (var i=0; i<this.services.length; i++) 
	{
		var service = this.services[this.servicesOrder[i]];
		if (service.active && service.searches && (service.bFeatureServer || service.access.hasSearch))
		{				
			for( var j=0; j<service.searches.length; j++) 
			{
				txtOptions += "<option value='" + service.searches[j].uid + "'";
				
				if ((this.currentSearchService == service) && (service.currentSearch == j))
				{
					this.currentSearch = nListSize;
					bFirstTime = false;
					txtOptions += " selected";
				}
						
				txtOptions += ">" + service.searches[j].name + "</option>";		
				
				nListSize++;
			}
		}				
	}
	
	if (txtOptions == "")
		txtOptions += "<option value=''>No Searches Available for this selection</option>";	
			
	var txt = "<form id='" + this.searchFormUid + "' name='" + this.searchFormUid + "'>";
	txt += "<select name='" + this.searchListUid + "' class='searchList' id='" + this.searchListUid + "'"
		+ " size=" + nListSize
		+ " onChange='javascript:" + this.myself + ".setCurrentSearch(this[this.selectedIndex].value)'>";
	txt += txtOptions;
	txt += "</select>";
	txt += "</form>";
	
	if (!this.divSearch)
		this.divSearch = SIX.Util.createElement("div", "searchListContainer");
		
	this.divSearch.innerHTML = txt;	
	return this.divSearch;
}

SIX.Catalog.prototype.setCurrentSearch = function (searchUid) 
{
	for (var i=0; i<this.services.length; i++) 
	{
		var bGotSearch = false;
		var service = this.services[i];
		for (var j=0; j<service.searches.length; j++)
		{
			if (searchUid == service.searches[j].uid)
			{
				service.setCurrentSearch(searchUid);
				bGotSearch = true;
				break;
			}
		}
		if (bGotSearch)
			break;
	}
}

SIX.Catalog.prototype.addService = function (service) {
	if (service.bDisplayBest && service.access instanceof SIX.Access.ECWP) this.bDisplayBest = true;
	this.servicesOrder[this.servicesOrder.length] = this.services.length;
	this.services[this.services.length] = service;
	service.setCatalog(this);
	return service;
}

SIX.Catalog.prototype.init = function () 
{
	if (this.fnBuildSearch)
		return this.fnBuildSearch();
	else
		return this.buildSearch();
}

SIX.Catalog.prototype.attach = function (item) 
{
	if( item.constructor == SIX.Map ) 
	{
		this.maps[this.maps.length] = item;
		item.catalog = this;
	}
}

SIX.Catalog.prototype.setMapsCatalog = function ()
{
	for (var i=0; i<this.maps.length; i++)
	{
		if (this.maps[i].catalog != this)
			this.maps[i].catalog = this;
	}		
}	

/**
 * @class Creates a simple, customizable JavaScript indicator.
 * @constructor
 * @param {String} uid				Unique ID for this HTML element
 * @param {String} containerUid		Unique ID for the HTML element containing the indicator
 * @param {Integer} nWidth			Width of the indicator
 * @param {Integer} nHeight			Height of the indicator	
 * @param {Array} aTopLabels		Array of labels to be placed at top of indicator
 * @param {Array} aBottomLabels		Array of labels to be placed at bottom of indicator
 * @param {String} sParams			Additional optional parameters
 */
SIX.Indicator = function (uid, containerUid, nIncrements, nIncrementWidth, nIncrementHeight, nIncrementSpacing, sParams)
{	
	this.div = null;
	this.uid = uid;
	this.incrementUid = uid + "_increment";
	this.containerUid = containerUid;

 	this.parameters = new SIX.Parameters(sParams ? sParams : "");
	
	this.aCompleteColors = null;
	this.sCompleteColor = this.parameters.getParameter("completecolor", "string", "#00FF00");
	this.sIncompleteColor = this.parameters.getParameter("incompletecolor", "string", "#FF0000");
	this.sBackgroundColor = this.parameters.getParameter("backgroundcolor", "string", "transparent");
	
	this.nIncrements = nIncrements;
	this.nIncrementSpacing = (this.nIncrements == 1) ? 0 : nIncrementSpacing;

	this.nBorderWidth = this.parameters.getParameter("borderwidth", "number", 0);
	this.nBorderColor = this.parameters.getParameter("bordercolor", "string", "#000000");
	
	this.nIncrementWidth = (nIncrementWidth < 2) ? 2 : nIncrementWidth;
	this.nIncrementHeight = (nIncrementHeight < 2) ? 2 : nIncrementHeight;
	
	this.nBarWidth = this.nIncrements * this.nIncrementWidth + 
					(this.nIncrements - 1) * this.nIncrementSpacing + 
					(this.nIncrementSpacing ? (2 * this.nBorderWidth * this.nIncrements) : 2 * this.nBorderWidth);
	
	this.aTopLabels = new Array();
	this.aBottomLabels = new Array();
}

SIX.Indicator.prototype.init = function ()
{	
	var text = "";
		
	var nBarWidth = this.nBarWidth;
	
	//Draw top labels
	for (var i=0; i<this.aTopLabels.length; i++)
	{	
		var nLabelsContainerWidth = Math.floor(nBarWidth + nBarWidth / this.aTopLabels[i].length);
		var nLabelWidth = Math.floor(nLabelsContainerWidth / this.aTopLabels[i].length);
		text += "<div class='indicatorLabelsContainer' style='width:" + nLabelsContainerWidth + "px;'>";	
		for (var j=0; j<this.aTopLabels[i].length; j++)
		{
			text += "<div class='indicatorLabel' style='width:" + nLabelWidth + "px;'>" + this.aTopLabels[i][j] + "</div>";	
		}
		text += "</div>";
	}

	//Draw increments
	text += "<div id='" + this.uid +
		"' class='indicatorBar' style='width:" + nBarWidth + "px;background-color:" + this.sBackgroundColor + "'>";
	
	for (var i=0; i<this.nIncrements; i++)
	{
		var nMarginLeft = 0;
		var nBorderLeft = 0;
		var nBorderRight = 0;
		var nBorderTop = this.nBorderWidth;
		var nBorderBottom = this.nBorderWidth;
		
		if (this.nIncrementSpacing || (i == 0))
			nBorderLeft = this.nBorderWidth;
		
		if (i > 0) nMarginLeft = this.nIncrementSpacing;
		
		if (this.nIncrementSpacing || (i == (this.nIncrements - 1)))
			nBorderRight = this.nBorderWidth;
		
		text += "<div id='" + this.incrementUid + i + "' class='indicatorIncrement' " +
				" style='border-left:" + this.nBorderColor + " " + nBorderLeft + "px solid" + 
				";border-right:" + this.nBorderColor + " " + nBorderRight + "px solid" + 
				";border-top:" + this.nBorderColor + " " + nBorderTop + "px solid" + 
				";border-bottom:" + this.nBorderColor + " " + nBorderBottom + "px solid" + 
				";width:" + this.nIncrementWidth + "px; height:" + this.nIncrementHeight + "px; background-color:" + this.sIncompleteColor + "'>&nbsp;</div>";
		
		if (this.nIncrementSpacing && (i < (this.nIncrements - 1)))
		{
			text += "<div class='indicatorIncrement' " +
					" style='width:" + this.nIncrementSpacing + "px; height:" + this.nIncrementHeight + "px; background-color:transparent'>&nbsp;</div>";
		}
	}

	text += "</div>";

	//Draw bottom labels
	for (var i=0; i<this.aBottomLabels.length; i++)
	{	
		var nLabelsContainerWidth = nBarWidth + nBarWidth / this.aBottomLabels[i].length;
		var nLabelWidth = Math.floor(nLabelsContainerWidth / this.aBottomLabels[i].length);
		text += "<div class='indicatorLabelsContainer' style='width:" + nLabelsContainerWidth + "px;'>";
		for (var j=0; j<this.aBottomLabels[i].length; j++)
		{
			text += "<div class='indicatorLabel' style='width:" + nLabelWidth + "px;'>" + this.aBottomLabels[i][j] + "</div>";	
		}
		text += "</div>";
	}

	if (!this.div)
		this.div = document.createElement("div");
	
	this.div.innerHTML = text;
		
	if (this.containerUid)
	{
		var elemContainer = document.getElementById(this.containerUid);
		elemContainer.appendChild(this.div);
	}	
	
	return this.div;
}

SIX.Indicator.prototype.setCompleteColors = function (aColors)
{
	if (aColors.length == this.nIncrements)
		this.aCompleteColors = aColors;
	else
		this.aCompleteColors = SIX.Util.interpolateHexColorsArray(aColors, this.nIncrements);
}

SIX.Indicator.prototype.setValue = function (value)
{
	if (value < 0) value = 0;
	if (value > 100) value = 100;
	
	var nHighestIncrement = (value * this.nIncrements / 100) - 1; 
	for (var i=0; i<this.nIncrements; i++)
	{
		var elem = document.getElementById(this.incrementUid + i);
		if (elem)
		{
			var sCompleteColor = this.aCompleteColors ? this.aCompleteColors[i] : this.sCompleteColor;
			elem.style.backgroundColor = ((nHighestIncrement >= i) ? sCompleteColor : this.sIncompleteColor);
		}
	}
}

SIX.Indicator.prototype.addTopLabels = function (aLabels)
{
	this.aTopLabels[this.aTopLabels.length] = aLabels;
}

SIX.Indicator.prototype.addBottomLabels = function (aLabels)
{
	this.aBottomLabels[this.aBottomLabels.length] = aLabels;
}

//	--------------------------------------------------------------------------- 
// 		SIX.Console Class
//	--------------------------------------------------------------------------- 

SIX.eConsoleTabs = {
	MESSAGES : 0,
	BANDWIDTH : 1,
	HEALTH : 2,
	PROGRESS : 3	
}

SIX.ConsoleMessage = function (sSummary, sDetail, requestResponse)
{
	this.uid = SIX.Uid.getUid("ConsoleMessage");
	this.sSummary = sSummary;
	this.sDetail = arguments.length >= 2 ? sDetail : "";
	this.requestResponse = arguments.length >= 3 ? requestResponse : null;
	
	this.sSummary = this.sSummary.replace(/\r/g, "");
	this.sSummary = this.sSummary.replace(/\n/g, "");
	this.sSummary = this.sSummary.replace(/\t/g, "");
	
	this.sClass = "";
	if (this.requestResponse)
		this.sClass = (this.requestResponse.nStatus == SIXConfig.eRequestResponseStatus.SUCCESS) ? "consoleMessageSuccess" : "consoleMessageFailure";	
}

SIX.Console = function (channel)
{
	SIX.addRef(this);
	this.div = null;
	this.name = "Console";
	this.channel = channel;
	this.channel.setConsole(this);
	this.aServices = new Object();//Associative array.
	
	this.messagesPaneUid = SIX.Uid.getUid("messagesPane");
	this.messagesTabUid = SIX.Uid.getUid("messagesTab");
	this.messagesTableUid = SIX.Uid.getUid("messagesTable");
	aTagExclusionsByTableId[this.messagesTableUid] = new Array();
	
	this.messagesSummaryUid = SIX.Uid.getUid("messagesSummary");
	this.messagesDetailUid = SIX.Uid.getUid("messagesDetail");
	this.messagesDetailContentsUid = SIX.Uid.getUid("messagesDetailContents");
	this.messagesDetailTitleBarUid = SIX.Uid.getUid("messagesDetailTitleBar");
	this.messagesDetailTitleBarTextUid = SIX.Uid.getUid("messagesDetailTitleBarText");
	this.aMessages = new Object();//Associative array.
	this.aMessagesOrder = new Array();//Used for determining oldest messages to remove when maximum console entries reached.
	this.nIndexLastMessageDeleted = -1;
	
	this.bandwidthPaneUid = SIX.Uid.getUid("bandwidthPane");
	this.bandwidthTabUid = SIX.Uid.getUid("bandwidthTab");
	this.bandwidthBusyTextUid = SIX.Uid.getUid("bandwidthBusyText");
	this.bandwidthResultsUid = SIX.Uid.getUid("bandwidthResults");
	this.bandwidthImageContainerUid = SIX.Uid.getUid("ImageContainer");
	this.bandwidthIndicatorContainerUid = SIX.Uid.getUid("indicatorContainer");
	this.bandwidthIndicatorUid = SIX.Uid.getUid("indicator");
	this.bandwidthSpeedUid = SIX.Uid.getUid("bandwidthSpeed");
	this.bandwidthStatusUid = SIX.Uid.getUid("bandwidthStatus");
	this.bandwidthSummaryUid = SIX.Uid.getUid("bandwidthSummary");
	
	this.healthPaneUid = SIX.Uid.getUid("healthPane");
	this.healthTabUid = SIX.Uid.getUid("healthTab");
	this.healthTableUid = SIX.Uid.getUid("healthTable");
	this.healthUid = SIX.Uid.getUid("health");
	this.statisticsTitleBarUid = SIX.Uid.getUid("statisticsTitleBar");
	this.statisticsTitleBarTextUid = SIX.Uid.getUid("statisticsTitleBarText");
	this.statisticsUid = SIX.Uid.getUid("statistics");
	this.statisticsContentsUid = SIX.Uid.getUid("statisticsContents");
	
	this.progressPaneUid = SIX.Uid.getUid("progressPane");
	this.progressTabUid = SIX.Uid.getUid("progressTab");
	
	this.aTabsVisibility = new Array();
	this.aTabsVisibility[SIX.eConsoleTabs.MESSAGES] = true;
	this.aTabsVisibility[SIX.eConsoleTabs.BANDWIDTH] = true;
	this.aTabsVisibility[SIX.eConsoleTabs.HEALTH] = true;
	this.aTabsVisibility[SIX.eConsoleTabs.PROGRESS] = true;

	this.aTabIds = new Array();
	this.aTabIds[SIX.eConsoleTabs.MESSAGES] = this.messagesTabUid;
	this.aTabIds[SIX.eConsoleTabs.BANDWIDTH] = this.bandwidthTabUid;
	this.aTabIds[SIX.eConsoleTabs.HEALTH] = this.healthTabUid;
	this.aTabIds[SIX.eConsoleTabs.PROGRESS] = this.progressTabUid;
	
	this.activeTab = SIX.eConsoleTabs.PROGRESS;

	this.aHealthIndicators = new Object();//Associative array.

	//Progress tab fields
	this.ECWJP2ProgressUid = SIX.Uid.getUid("ECWJP2Progress");
	this.GISOverlayProgressUid = SIX.Uid.getUid("GISOverlayProgress");
	
	this.ECWJP2ProgressBar = new NCSProgressbar3(this.ECWJP2ProgressUid, null, 350, 20, 20, true, "Images :", 65, 35); 
	this.ECWJP2ProgressBar.setProgressCompleteColor("#00ff00");
	this.ECWJP2ProgressBar.setProgressIncompleteColor("#ff0000");
	
	this.GISOverlayProgressBar = new NCSProgressbar3(this.GISOverlayProgressUid, null, 350, 20, 20, true, "Themes :", 65, 35); 	
	this.GISOverlayProgressBar.setProgressCompleteColor("#00ff00");
	this.GISOverlayProgressBar.setProgressIncompleteColor("#ff0000");

	//Bandwidth tab fields
	this.objDownloadSpeedIndicator = new SIX.Indicator(this.bandwidthIndicatorUid, this.bandwidthIndicatorContainerUid, 40, 4, 20, 2,
												"borderwidth=0;completecolor=#FF9900;incompletecolor=#FFFFFF");

	this.objDownloadSpeedIndicator.addBottomLabels(SIXConfig.aBandwidthSpeedLabels);
	this.objDownloadSpeedIndicator.addBottomLabels(SIXConfig.aBandwidthStatus);
	this.objDownloadSpeedIndicator.setCompleteColors(["#FF0000", "#FF0000", "#FF9900", "#FF9900", "#FFFF00", "#FFFF00", "#00FF00", "#00FF00"]);
}

SIX.Console.prototype.init = function () 
{	
	if (!this.div)
	{
		this.div = document.createElement("div");
		this.div.style.position = "absolute";
		this.div.style.width = "100%";
		this.div.style.height = "100%";
	}

	var sText = "<div id='" + this.messagesPaneUid + "' class='consoleMessages' " + (this.activeTab == SIX.eConsoleTabs.MESSAGES ? "" : "style='display:none'") + ">" + 
				"<div  id='" + this.messagesSummaryUid + "' class='consoleMessagesSummary'>" + 
				"<table class='sortable' cellspacing='0' cellpadding='2'" +
				(SIX.App.isIE ? "" : "style='width:100%'") + 
				" name='" + this.messagesTableUid + "' id='" + this.messagesTableUid + "'>" + 
				"<tr>" +
				"<td " + (SIX.App.isIE ? "" : "width='50%'") + " class='consoleTableHeading' title='Click to sort'>Message</td>" + 
				"<td " + (SIX.App.isIE ? "" : "width='50%'") + " class='consoleTableHeading' title='Click to sort'>Time</td>" + 
				/*Under Firefox, the other cells will only expand to the occupy the full table width if the following hidden cell is present.*/
				(SIX.App.isIE ? "" : "<td style='visibility:hidden;'>&nbsp;</td>") + 
				"</tr>" +					
				"</table>" + 
				"</div>" + 
				"<div id='" + this.messagesDetailUid + "' class='consoleMessagesDetail' style='display:none'>" +
				"<div id='" + this.messagesDetailTitleBarUid + "' class='consoleMessagesDetailTitleBar'>" +
				"<div id='" + this.messagesDetailTitleBarTextUid + "' class='consoleMessagesDetailTitleBarText'>Message Detail</div>" +
				"<img class='consoleMessagesDetailCloseButton' " + SIX.App.imageSrc("images/close") + " border='0' onclick='" + this.myself + ".hideMessageDetail();'>" +
				"</div>" +					
				"<div class='consoleMessagesDetailContentsOuter'>" + 
				"<div id='" + this.messagesDetailContentsUid + "' class='consoleMessagesDetailContentsInner'>&nbsp;</div>" +
				"</div>" +				
				"</div>" +				
				"</div>" + 				
				"<div id='" + this.healthPaneUid + "' class='consoleHealthPane' " + (this.activeTab == SIX.eConsoleTabs.HEALTH ? "" : "style='display:none'") + ">" + 
				"<div id='" + this.healthUid + "' class='consoleHealth'>" +
				"<table class='sortable' cellspacing='1' cellpadding='2'" +
				(SIX.App.isIE ? "" : "style='width:100%'") + 
				" name='" + this.healthTableUid + "' id='" + this.healthTableUid + "'>" + 
				"<tr>" +
				"<td " + (SIX.App.isIE ? "" : "width='25%'") + " class='consoleHealthServiceNameHeading' title='Click to sort'>Service Name</td>" + 
				"<td " + (SIX.App.isIE ? "" : "width='25%'") + " class='consoleHealthServerNameHeading' title='Click to sort'>Server Name</td>" + 
				"<td " + (SIX.App.isIE ? "" : "width='25%'") + " class='consoleTableHeading' title='Click to sort'>Health</td>" + 
				"<td " + (SIX.App.isIE ? "" : "width='25%'") + " class='consoleHealthStatisticsHeading' title='Click to sort'>Statistics</td>" + 
				/*Under Firefox, the other cells will only expand to the occupy the full table width if the following hidden cell is present.*/
				(SIX.App.isIE ? "" : "<td style='visibility:hidden;'>&nbsp;</td>") +  
				"</tr>" +							
				"</table>" + 
				"</div>" +
				"<div id='" + this.statisticsUid + "' class='consoleStatistics'  style='display:none'>" +
				"<div id='" + this.statisticsTitleBarUid + "' class='consoleStatisticsTitleBar'>" +
				"<div id='" + this.statisticsTitleBarTextUid + "' class='consoleStatisticsTitleBarText'>&nbsp;</div>" +
				"<img class='consoleStatisticsCloseButton' " + SIX.App.imageSrc("images/close") + " border='0' onclick='" + this.myself + ".hideStatistics();'>" +
				"</div>" +		
				"<div id='" + this.statisticsContentsUid + "' style='height:100%;border-bottom:solid 1px #ffffff'>&nbsp</div>" +
				"</div>" +
				"</div>" +																								
				"<div id='" + this.progressPaneUid + "' class='consoleProgress' " + (this.activeTab == SIX.eConsoleTabs.PROGRESS ? "" : "style='display:none'") + ">" + 
				"<div class='consoleProgressBarOuter'>" +
				"<div class='consoleProgressBarInner'>" +
				"<div class='consoleProgressBar'>" + this.ECWJP2ProgressBar.build() + "</div>" + 
				"<div class='consoleProgressBar'>" + this.GISOverlayProgressBar.build() + "</div>" + 
				"</div>" + 
				"</div>" + 
				"</div>" + 
				"<div id='" + this.bandwidthPaneUid + "' class='consoleBandwidth' style='" + (this.activeTab == SIX.eConsoleTabs.BANDWIDTH ? "" : "display:none;") + "'>"; 
				
				if (SIXConfig.aBandwidthTests.length == 1)
				{
					sText += "<div class='consoleBandwidthTestButtonContainer'>" +
					"<input name='Test Bandwidth' value='Test Bandwidth' type='button' class='consoleBandwidthTestButtonContainer' onclick='" + this.myself + ".testBandwidth(0)'>" +
					"</div>";
				}
				else
				{
					sText += "<table align=center>" +
					"<tr>" +
					"<td class='consoleBandwidthTableHeading'>Test Size</th>" +
					"<td class='consoleBandwidthTableHeading' style='text-align:left'>Suitable For</th>" +
					"</tr>";

					for (var i=0; i<SIXConfig.aBandwidthTests.length; i++)
					{
						var objTest = SIXConfig.aBandwidthTests[i];
						sText += "<tr><td class='consoleBandwidthTableCell'>" + 
								"<a href='#' class='consoleBandwidthText' onclick='" + this.myself + 
								".testBandwidth(" + i + ")'>" + objTest.sName +  "</a></td>" +
								"<td class='consoleBandwidthTableCell' style='text-align:left'>" + objTest.sSuitability + "</td></tr>";			
					}

					sText += "</table>";
				}
				sText += "<div id='" + this.bandwidthBusyTextUid + "' class='consoleBandwidthResults' style='display:none;'>&nbsp;</div>" +
				"<div id='" + this.bandwidthResultsUid + "' class='consoleBandwidthResults' style='text-align:center;margin-left:0px;margin-right:0px;display:none;'>" +
				"<div id='" + this.bandwidthIndicatorContainerUid + "' style='text-align:center;margin-left:auto;margin-right:auto;display:block;width:100%;height:60px;'>&nbsp;</div>" +
				"<div id='" + this.bandwidthSpeedUid + "' style='text-align:center;margin-left:auto;margin-right:auto;display:block;width:100%;'>&nbsp;</div>" +
				/*"<div id='" + this.bandwidthStatusUid + "' style='text-align:center;margin-left:auto;margin-right:auto;display:block;width:100%;'>&nbsp;</div>" +*/
				"<div id='" + this.bandwidthSummaryUid + "'  style='text-align:center;margin-left:auto;margin-right:auto;display:block;width:100%;'>&nbsp;</div>" +
				"<div id='" + this.bandwidthImageContainerUid + "' style='text-align:center;margin-left:auto;margin-right:auto;display:block;' align='center' class='consoleBandwidthImageContainer'>&nbsp;</div>" +
				"</div>" +
				"</div>";
	
	var sFixedHtml = "<div class='consoleTabRowBackground'>&nbsp;</div>" + 
			"<img class='consoleCloseButton' " + SIX.App.imageSrc("images/close") + " border='0' onclick='" + this.channel.myself + ".hideConsole();'>" +
			"<div class='consoleTabRow'>" + 
			"<div style='cursor:" + (SIX.App.isIE ? "hand" : "pointer") + (this.aTabsVisibility[SIX.eConsoleTabs.PROGRESS] ? "" : ";display:none") + "' class='consoleTab" + (this.activeTab == SIX.eConsoleTabs.PROGRESS ? "On" : "Off") + "' id='" + this.progressTabUid + "' onclick='" + this.myself + ".setTab(" + SIX.eConsoleTabs.PROGRESS + ");'><div class='consoleTabTitle'>Progress</div></div>" + 
			"<div style='cursor:" + (SIX.App.isIE ? "hand" : "pointer") + (this.aTabsVisibility[SIX.eConsoleTabs.BANDWIDTH] ? "" : ";display:none") + "' class='consoleTab" + (this.activeTab == SIX.eConsoleTabs.BANDWIDTH ? "On" : "Off") + "' id='" + this.bandwidthTabUid + "' onclick='" + this.myself + ".setTab(" + SIX.eConsoleTabs.BANDWIDTH + ");'><div class='consoleTabTitle'>Bandwidth</div></div>" +
			"<div style='cursor:" + (SIX.App.isIE ? "hand" : "pointer") + (this.aTabsVisibility[SIX.eConsoleTabs.HEALTH] ? "" : ";display:none") + "' class='consoleTab" + (this.activeTab == SIX.eConsoleTabs.HEALTH ? "On" : "Off") + "' id='" + this.healthTabUid + "' onclick='" + this.myself + ".setTab(" + SIX.eConsoleTabs.HEALTH + ");'><div class='consoleTabTitle'>Health</div></div>" +
			"<div style='cursor:" + (SIX.App.isIE ? "hand" : "pointer") + (this.aTabsVisibility[SIX.eConsoleTabs.MESSAGES] ? "" : ";display:none") + "' class='consoleTab" + (this.activeTab == SIX.eConsoleTabs.MESSAGES ? "On" : "Off") + "' id='" + this.messagesTabUid + "' onclick='" + this.myself + ".setTab(" + SIX.eConsoleTabs.MESSAGES + ");'><div class='consoleTabTitle'>Messages</div></div>" + 
			"</div>";	
	
	this.div.innerHTML = sFixedHtml + sText;
	return this.div;
}

SIX.Console.prototype.setTab = function (tab) 
{
	if (tab != this.activeTab)
	{
		var elemLastActiveTab = document.getElementById(this.aTabIds[this.activeTab]);
		if (elemLastActiveTab)
		{
			elemLastActiveTab.setAttribute("class", "consoleTabOff");
			elemLastActiveTab.setAttribute("className", "consoleTabOff");			
		}

		var elemActiveTab = document.getElementById(this.aTabIds[tab]);
		if (elemActiveTab)
		{
			elemActiveTab.setAttribute("class", "consoleTabOn");
			elemActiveTab.setAttribute("className", "consoleTabOn");
		}

		var elemLastActive = null;
		switch (this.activeTab)
		{
			case SIX.eConsoleTabs.MESSAGES:
				elemLastActive = document.getElementById(this.messagesPaneUid);
				break;
			case SIX.eConsoleTabs.HEALTH:
				elemLastActive = document.getElementById(this.healthPaneUid);
				break;	
			case SIX.eConsoleTabs.BANDWIDTH:
				elemLastActive = document.getElementById(this.bandwidthPaneUid);
				break;		
			case SIX.eConsoleTabs.PROGRESS:
				elemLastActive = document.getElementById(this.progressPaneUid);
				break;	
			default:
				break;
		}
		if (elemLastActive) 
			elemLastActive.style.display = "none";

		var elemActive = null;
		switch (tab)
		{
			case SIX.eConsoleTabs.MESSAGES:
				elemActive = document.getElementById(this.messagesPaneUid);
				break;
			case SIX.eConsoleTabs.HEALTH:
				elemActive = document.getElementById(this.healthPaneUid);
				break;	
			case SIX.eConsoleTabs.BANDWIDTH:
				elemActive = document.getElementById(this.bandwidthPaneUid);
				break;		
			case SIX.eConsoleTabs.PROGRESS:
				elemActive = document.getElementById(this.progressPaneUid);
				break;	
			default:
				break;
		}
		if (elemActive) 
			elemActive.style.display = "block";	
		this.activeTab = tab;					
	}	
}

SIX.Console.prototype.setTabVisibility = function (eTab, bVisible)
{
	this.aTabsVisibility[eTab] = bVisible;
}

SIX.Console.prototype.setActiveTab = function (eTab, bVisible)
{
	this.activeTab = eTab;
}

SIX.Console.prototype.addMessage = function (theConsoleMessage)
{
	var elemTable = document.getElementById(this.messagesTableUid);
	if (elemTable)
	{
		var nodeTableBody = elemTable.getElementsByTagName("tbody").item(0);
		var rowNodes = nodeTableBody.getElementsByTagName("tr");
		if (rowNodes.length > SIXConfig.MAX_CONSOLE_ENTRIES)
		{
			this.nIndexLastMessageDeleted++;
			var elemAnchor = document.getElementById(this.aMessagesOrder[this.nIndexLastMessageDeleted]);
			if (elemAnchor)
			{
				var nodeRow = getParent(elemAnchor, "tr");
				if (nodeRow)
					nodeTableBody.removeChild(nodeRow);
			}
		}
			
		var nodeRow = document.createElement("tr"); 
		if (theConsoleMessage.sClass != "")
		{
			nodeRow.setAttribute("class", theConsoleMessage.sClass);
			nodeRow.setAttribute("className", theConsoleMessage.sClass);
		}

		var tNow = new Date();
		this.aMessages[theConsoleMessage.uid] = theConsoleMessage;
		this.aMessagesOrder[this.aMessagesOrder.length] = theConsoleMessage.uid;
		var theConsole = this;
		
		var nodeAnchor = SIX.Util.createElement("a", "consoleText", theConsoleMessage.uid);
		nodeAnchor.setAttribute("name", theConsoleMessage.uid);
		nodeAnchor.setAttribute("href", "#");
		nodeAnchor.setAttribute("title", "Click for details");		
		nodeAnchor.onclick = function (event)
							{
								with (theConsole)
								{
									theConsole.showMessageDetail(this.name);
								}
								return false;
							}		

		var nodeSummaryCell = SIX.Util.createElement("td", "consoleText");
		
		var sSummaryServerNamesAliased = this.aliasServerNames(theConsoleMessage.sSummary);
		sSummaryServerNamesAliased = (sSummaryServerNamesAliased.length < SIXConfig.MAX_CONSOLE_ENTRY_SUMMARY_LENGTH) ? sSummaryServerNamesAliased : (sSummaryServerNamesAliased.substring(0,SIXConfig.MAX_CONSOLE_ENTRY_SUMMARY_LENGTH - 3) + "...");

		var textNodeSummaryCell = document.createTextNode(sSummaryServerNamesAliased);
		
		nodeAnchor.appendChild(textNodeSummaryCell);
		nodeSummaryCell.appendChild(nodeAnchor);

		nodeRow.appendChild(nodeSummaryCell);	  	

		var nodeTimeCell = SIX.Util.createElement("td", "consoleTableCell");
			
		var textNodeTimeCell = document.createTextNode(tNow);
		nodeTimeCell.appendChild(textNodeTimeCell);		
		
		nodeRow.appendChild(nodeTimeCell);	
		  			
		nodeTableBody.appendChild(nodeRow);	
	}	
} 

SIX.Console.prototype.hideMessageDetail = function ()
{
	var elemMessagesDetail = document.getElementById(this.messagesDetailUid);
	if (elemMessagesDetail)
		elemMessagesDetail.style.display = "none";

	var elemMessagesSummary = document.getElementById(this.messagesSummaryUid);
	if (elemMessagesSummary)
		elemMessagesSummary.style.height = "100%";
}

SIX.Console.prototype.showMessageDetail = function (sMessageId)
{
	if (this.aMessages[sMessageId])
	{
		var elemMessagesDetail = document.getElementById(this.messagesDetailUid);
		if (elemMessagesDetail)
		{
			elemMessagesDetail.style.display = "block";
			var elemContents = document.getElementById(this.messagesDetailContentsUid);
			if (elemContents)
				elemContents.innerHTML = this.aliasServerNames(this.aMessages[sMessageId].sDetail);
		}
	
		var elemMessagesSummary = document.getElementById(this.messagesSummaryUid);
		if (elemMessagesSummary)
			elemMessagesSummary.style.height = "50%";
	}
}

SIX.Console.prototype.aliasServerNames = function (sString)
{
	for (var i=0; i<SIXConfig.aServerAliases.length; i++)
	{
		var re = new RegExp(SIXConfig.aServerAliases[i].sName, "gi");
		sString = sString.replace(re, SIXConfig.aServerAliases[i].sAlias);
	}
	return sString;
}

SIX.Console.prototype.addService = function (svc)
{
	if (!this.aServices[svc.uid])
		this.aServices[svc.uid] = svc;
	this.addHealthIndicator(svc);
} 

SIX.Console.prototype.updateHealth = function (svc)
{
	//Can have a health entry for parent service, child service or both. 
	if (svc.parentService)
		this.updateHealth(svc.parentService);
		
	if (this.aServices[svc.uid])
	{
		var nSuccessfulRequests = 0;
		var nFailedRequests = 0; 
		var aRequestsByType = svc.statistics.aRequestsByType;
		var aAllRequests = svc.statistics.aAllRequests;
		
		if (aAllRequests.length)
		{
			var tLastRequest = aAllRequests[aAllRequests.length - 1].date.getTime();
			var tFirstRequest = tLastRequest - SIXConfig.STATISTICS_CALCULATION_PERIOD;
			
			for (var j in aRequestsByType) 
			{
				if (aRequestsByType.hasOwnProperty(j))
				{
					var aRequests = aRequestsByType[j];
					var nNumRequests = aRequests.length;
					var nIndex = SIX.Util.searchObjectArray(aRequests, "nTimeStamp", tFirstRequest);
					var nNumRequests = (nIndex < 0 ? aRequests.length + nIndex + 1 : aRequests.length - nIndex);

					if (j == ("#" + SIXConfig.eRequestResponseStatus.SUCCESS))
						nSuccessfulRequests += nNumRequests;
					else
						nFailedRequests += nNumRequests;
				}
			}	
			
			var nPercentHealth = 100;
			if (nSuccessfulRequests || nFailedRequests)
				nPercentHealth = Math.floor(100 * nSuccessfulRequests / (nSuccessfulRequests + nFailedRequests));
			this.setHealth(svc, nPercentHealth);
		}
	}
}

SIX.Console.prototype.setHealth = function (svc, nPercentHealth)
{
	if (this.aServices[svc.uid])
		this.aHealthIndicators[svc.uid].setValue(nPercentHealth);
}

SIX.Console.prototype.addHealthIndicator = function (svc)
{
	if (!this.aHealthIndicators[svc.uid])
	{
		var elemTable = document.getElementById(this.healthTableUid);
		if (elemTable)
		{
			var nodeTableBody = elemTable.getElementsByTagName("tbody").item(0);
				
			var nodeRow = document.createElement("tr"); 

			var nodeServiceNameCell = SIX.Util.createElement("td", "consoleHealthServiceNameCell");
			var textNodeServiceName = document.createTextNode(svc.name);
			nodeServiceNameCell.appendChild(textNodeServiceName);
			nodeRow.appendChild(nodeServiceNameCell);	  	

			var nodeServerNameCell = SIX.Util.createElement("td", "consoleHealthServerNameCell");
			var sServerName = (svc.access instanceof SIX.Access.ECWP) ? SIXConfig.ECWP_SERVER : svc.host;
			var textNodeServerName = document.createTextNode(sServerName ? this.aliasServerNames(sServerName) : "");
			nodeServerNameCell.appendChild(textNodeServerName);
			nodeRow.appendChild(nodeServerNameCell);	  	

			var healthContainerUid = SIX.Uid.getUid("healthContainer");
			var healthUid = SIX.Uid.getUid("health");
			var nodeHealthCell = SIX.Util.createElement("td", "consoleTableCell", healthContainerUid);
			
			var objHealthIndicator = new SIX.Indicator(healthUid, healthContainerUid, 10, 10, 15, 2,
													"borderwidth=0;completecolor=#00FF00;incompletecolor=#FF0000");		
			
			nodeRow.appendChild(nodeHealthCell);	  	

			var nodeStatisticsCell = SIX.Util.createElement("td", "consoleHealthStatisticsCell");
			var nodeAnchor = document.createElement("a");
			nodeAnchor.setAttribute("name", svc.uid);
			nodeAnchor.setAttribute("href", "#");
			nodeAnchor.setAttribute("title", "Statistics...");
			
			var theConsole = this;
			nodeAnchor.onclick = function (event)
								{
									with (theConsole)
									{
										theConsole.showStatistics(this.name);
									}
									return false;
								}		
			
			var textNodeStatistics = document.createTextNode("Statistics...");
			nodeAnchor.appendChild(textNodeStatistics);		
			nodeStatisticsCell.appendChild(nodeAnchor);			
			nodeRow.appendChild(nodeStatisticsCell);
			  			
			nodeTableBody.appendChild(nodeRow);	
			
			objHealthIndicator.init();
			objHealthIndicator.setValue(100);
			this.aHealthIndicators[svc.uid] = objHealthIndicator;
		}
	}
} 

SIX.Console.prototype.hideStatistics = function ()
{		
		var elemStatistics = document.getElementById(this.statisticsUid);
		if (elemStatistics)
			elemStatistics.style.display = "none";
			
		var elemHealth = document.getElementById(this.healthUid);
		if (elemHealth)
			elemHealth.style.height = "100%";			
}

SIX.Console.prototype.showStatistics = function (sServiceId)
{
	if (this.aServices[sServiceId])
	{
		var service = this.aServices[sServiceId];

		var elemStatisticsTitleBarText = document.getElementById(this.statisticsTitleBarTextUid);
		if (elemStatisticsTitleBarText)
			elemStatisticsTitleBarText.innerHTML = service.name + " Service Statistics";
		
		var elemStatistics = document.getElementById(this.statisticsUid);
		if (elemStatistics)
		{
			elemStatistics.style.display = "block";
			var elemContents = document.getElementById(this.statisticsContentsUid);
			if (elemContents)
				elemContents.innerHTML = service.buildStatistics();
		}
		
		var elemStatisticsTable = document.getElementById(service.statistics.uid);
		if (elemStatisticsTable)
			ts_makeSortable(elemStatisticsTable);
		service.updateStatistics();
		
		var elemHealth = document.getElementById(this.healthUid);
		if (elemHealth)
			elemHealth.style.height = "50%";		
	}
}

SIX.Console.prototype.setGISOverlayProgress = function (nPercent)
{
	this.GISOverlayProgressBar.setProgress(nPercent);
} 

SIX.Console.prototype.setECWJP2Progress = function (nPercent)
{
	this.ECWJP2ProgressBar.setProgress(nPercent);
} 

// Writes image tag to initiate image download.
// Arguments:
//     sUrl	Image Url
//     sFileSize	Size of the image file in bytes
//     sProperties	Img tag properties

SIX.Console.prototype.writeImageTag = function (containerUid, sUrl, nFileSizeBytes, sProperties)
{
	var nStart = (new Date()).getTime();
	//NOTE: Start time appended to image src to ensure image not in cache.
	var elemContainerUid = document.getElementById(containerUid);
	elemContainerUid.innerHTML = "<img src='" + sUrl + "?start=" + encodeURIComponent(nStart) + "' " + sProperties + " onload='" + this.myself + ".onLoadImage(" + nStart + "," + nFileSizeBytes + ")'>";
}

SIX.Console.prototype.onLoadImage = function (nStart, nFileSizeBytes)
{
	var nEnd = (new Date()).getTime();
	var dElapsedTimeSeconds = (nEnd - nStart) / 1000;

	var dFileSizeKilobytes = nFileSizeBytes / 1024;
	var dFileSizeMegabytes = dFileSizeKilobytes / 1024;

	var dSpeedBytesPerSecond = nFileSizeBytes / dElapsedTimeSeconds;
	var dSpeedKilobytesPerSecond = dFileSizeKilobytes / dElapsedTimeSeconds;
	var dSpeedMegabytesPerSecond = dFileSizeMegabytes / dElapsedTimeSeconds;

	var dSpeedBitsPerSecond = dSpeedBytesPerSecond * 8;
	var dSpeedKilobitsPerSecond = dSpeedKilobytesPerSecond * 8;
	var dSpeedMegabitsPerSecond = dSpeedMegabytesPerSecond * 8;

	var elemSpeed = document.getElementById(this.bandwidthSpeedUid);
	elemSpeed.innerHTML = "Speed: " + dSpeedKilobitsPerSecond.toFixed(0) + " kbps";

	var sStatus = "";
	var nIndicatorValue = 0;
	
	if (dSpeedBitsPerSecond <= SIXConfig.aBandwidthSpeeds[0])
		nIndicatorValue = 0;
	else if (dSpeedBitsPerSecond >= SIXConfig.aBandwidthSpeeds[SIXConfig.aBandwidthSpeeds.length - 1])
		nIndicatorValue = 100;	
	else
	{
		for (var i=0; i<SIXConfig.aBandwidthSpeeds.length-1; i++)
		{		
			if ((dSpeedBitsPerSecond >= SIXConfig.aBandwidthSpeeds[i]) &&
				(dSpeedBitsPerSecond < SIXConfig.aBandwidthSpeeds[i+1]))
			{
				var nPercentBetweenValues = 100 / (SIXConfig.aBandwidthSpeeds.length - 1);
				nIndicatorValue = Math.round(nPercentBetweenValues * (i + (dSpeedBitsPerSecond - SIXConfig.aBandwidthSpeeds[i]) / (SIXConfig.aBandwidthSpeeds[i+1] - SIXConfig.aBandwidthSpeeds[i])));
				break;
			}
		}
	}
	
	for (var i=0; i<SIXConfig.aBandwidthThresholds.length; i++)
	{		
		if (dSpeedBitsPerSecond >= SIXConfig.aBandwidthThresholds[i])
		{
			if ((i + 1) == SIXConfig.aBandwidthThresholds.length)
			{
				if (dSpeedBitsPerSecond >= SIXConfig.aBandwidthThresholds[i])
				{
					sStatus = SIXConfig.aBandwidthStatus[i];
					break;
				}
			}
			else if ((dSpeedBitsPerSecond >= SIXConfig.aBandwidthThresholds[i]) &&
					(dSpeedBitsPerSecond < SIXConfig.aBandwidthThresholds[i+1]))
			{
				sStatus = SIXConfig.aBandwidthStatus[i];
				break;
			}
		}
	}

	/*
	var elemStatus = document.getElementById(this.bandwidthStatusUid);
	if (elemStatus)
		elemStatus.innerHTML = "Status: " + sStatus;
	*/
	
	var elemSummary = document.getElementById(this.bandwidthSummaryUid);

	if (dFileSizeKilobytes < 1024)
		elemSummary.innerHTML = "" + dFileSizeKilobytes.toFixed(0) + " KB downloaded in " + dElapsedTimeSeconds + " seconds.";
	else
		elemSummary.innerHTML = "" + dFileSizeMegabytes.toFixed(2) + " MB downloaded in " + dElapsedTimeSeconds + " seconds.";

	this.objDownloadSpeedIndicator.init();
	this.objDownloadSpeedIndicator.setValue(nIndicatorValue);
	
	this.showResults();	
}

SIX.Console.prototype.showResults = function ()
{
	//Hide busy text
	var elemBusy = document.getElementById(this.bandwidthBusyTextUid);
	if (elemBusy) elemBusy.style.display = "none";	
	
	//Show results
	var elemResults = document.getElementById(this.bandwidthResultsUid);
	elemResults.style.display = "block";
}

SIX.Console.prototype.showBusyText = function (sText)
{
	//Hide results
	var elemResults = document.getElementById(this.bandwidthResultsUid);
	elemResults.style.display = "none";
	
	//Show busy text 
	var elemBusy = document.getElementById(this.bandwidthBusyTextUid);
	if (elemBusy)
	{
		elemBusy.style.display = "block";
		elemBusy.innerHTML = sText;
	}
}

SIX.Console.prototype.showImageContainer = function (imageContainerUid)
{
	var elemImageContainer = document.getElementById(this.bandwidthImageContainerUid);
		
	if (elemImageContainer && (imageContainerUid == this.bandwidthImageContainerUid))
		elemImageContainer.style.display = "block";
	else
		elemImageContainer.style.display = "none";						
}

SIX.Console.prototype.testBandwidth = function (nIndex)
{
	var objBandwidthTest = SIXConfig.aBandwidthTests[nIndex];
	this.showImageContainer(this.bandwidthImageContainerUid);
	this.showBusyText("Downloading image...");	
	this.writeImageTag(this.bandwidthImageContainerUid, objBandwidthTest.sImagePath, objBandwidthTest.nSize, "alt='" + objBandwidthTest.sImageDescription + "'");
}

SIX.Service.prototype.getRequestResponseStatus = function (response)
{
	var nStatus = SIXConfig.eRequestResponseStatus.FAILURE;
	if (response && typeof response == "string")
	{
		var sResponse = response.toLowerCase();
		for (var i=0; i<aRequestResponseStatus.length; i++)
		{
			var sErrorString = aRequestResponseStatus[i].sErrorString.toLowerCase();
			if (sResponse.indexOf(sErrorString) != -1)
			{
				nStatus = i;
				break;
			}
		}
	}
	return nStatus;
}

SIX.Service.prototype.onSelectDisplayBest = function ()
{
	this.catalog.bDisplayBest = this.bDisplayBest = !this.bDisplayBest;
	var elemDisplayBestSelected =  document.getElementById(this.displayBestSelectedUid);
	var elemDisplayBestUnselected =  document.getElementById(this.displayBestUnselectedUid);
	if (elemDisplayBestSelected) 
		elemDisplayBestSelected.style.display = this.bDisplayBest ? "" : "none";
	if (elemDisplayBestUnselected) 
		elemDisplayBestUnselected.style.display = this.bDisplayBest ? "none" : "";

	var map = this.catalog.maps[0];
	
	if (this.bDisplayBest)
	{
		if (map.hiddenLayers(this))
		{
			this.build();
			this.updateMap(map);
			this.access.orderLayers(map);
		}
		else
		{
			this.build();
		}	
	}
	else
	{
		this.build();
	}
}

SIX.Service.prototype.sortLayersInBestOrder = function (map)
{
	var aLayersWithOrder = new Array();
	for (var i=0; i<this.layers.length; i++)
	{
		var objLayerWithOrder = new Object();
		objLayerWithOrder.layer = this.layers[this.layersOrder[i]];
		objLayerWithOrder.nOrder = this.layersOrder[i];
		//Initially select all in scale layers with a best index >= 0.  If best index is less than 0, then it should be turned off
		//but sorted above layers with a best index >= 0.  This way, a user can turn the layer on and it will appear above the 
		//best available imagery.
		objLayerWithOrder.bSelected = (objLayerWithOrder.layer.nBestIndex < 0 ? this.layers[this.layersOrder[i]].bSelected : this.layers[this.layersOrder[i]].bInScale);
		aLayersWithOrder[i] = objLayerWithOrder;
	}
	
	var bChanged = false;
	
	var fnSort = function (objLayerWithOrder1, objLayerWithOrder2)
	{
		var nRetVal = 0;
			
		if (objLayerWithOrder1.layer.nBestIndex < 0 && objLayerWithOrder2.layer.nBestIndex >= 0)
			nRetVal = -1;//Move layer1 to lower index
		else if (objLayerWithOrder1.layer.nBestIndex >= 0 && objLayerWithOrder2.layer.nBestIndex < 0)
			nRetVal = 1;//Move layer1 to a higher index		
		else if (objLayerWithOrder1.layer.nBestIndex < 0 && objLayerWithOrder2.layer.nBestIndex < 0)
		{
			if (objLayerWithOrder1.nOrder < objLayerWithOrder2.nOrder)
				nRetVal = -1;//Move layer1 to lower index
			else if (objLayerWithOrder1.nOrder > objLayerWithOrder2.nOrder)
				nRetVal = 1;//Move layer1 to a higher index	
		}
		else if (!objLayerWithOrder1.bSelected && objLayerWithOrder2.bSelected)
			nRetVal = 1;//Move layer1 to higher index
		else if (objLayerWithOrder1.bSelected && !objLayerWithOrder2.bSelected)
			nRetVal = -1;//Move layer1 to lower index			
		else if (objLayerWithOrder1.layer.nBestIndex < objLayerWithOrder2.layer.nBestIndex) 
			nRetVal = 1;//Move layer1 to higher index
		else if (objLayerWithOrder1.layer.nBestIndex > objLayerWithOrder2.layer.nBestIndex) 
			nRetVal = -1;//Move layer1 to lower index
			
		return nRetVal;
	}
	
	aLayersWithOrder.sort(fnSort);

	//If a selected layer covers the entire view, turn off any layers below it.
	var nLastVisibleLayerOrder = -1;
	for (var i=0; i<this.layers.length; i++)
	{ 
		var layer = aLayersWithOrder[i].layer;
		if (nLastVisibleLayerOrder != -1)
		{
			aLayersWithOrder[i].bSelected = false;
		}
		else if (aLayersWithOrder[i].bSelected)
		{
			if (map.loaded && layer.nBestIndex >= 0)
			{
				if (layer.envelopeLL && i > 0)
				{
					for (var j=i-1; j>=0; j--)
					{
						var layerWithOrder = aLayersWithOrder[j];
						if (layerWithOrder.bSelected && 
							layerWithOrder.layer.nBestIndex >= 0 &&
							layerWithOrder.layer.envelopeLL && 
							layerWithOrder.layer.envelopeLL.contains(layer.envelopeLL))
						{
							aLayersWithOrder[i].bSelected = false;
							break;
						}		
					}
				}
			
				if (aLayersWithOrder[i].bSelected)
				{
					var pointTopLeft = new SIX.Point(map.getTopLeftLongitude(), map.getTopLeftLatitude());
					var pointTopRight = new SIX.Point(map.getTopRightLongitude(), map.getTopRightLatitude());
					var pointBottomRight = new SIX.Point(map.getBottomRightLongitude(), map.getBottomRightLatitude());
					var pointBottomLeft = new SIX.Point(map.getBottomLeftLongitude(), map.getBottomLeftLatitude());
					
					var aPoints = new Array();
					aPoints[aPoints.length] = pointTopLeft;
					aPoints[aPoints.length] = pointTopRight;
					aPoints[aPoints.length] = pointBottomRight;
					aPoints[aPoints.length] = pointBottomLeft;		
				
					if (layer.containsPoints(aPoints))
					{
						if (layer.aLayers.length)
						{
							for (var j=0; j<layer.aLayers.length; j++)
							{
								//For performance, only check the child layer's bounding box / envelope.
								//If the child layer has a region defined, don't perform the check as layers
								//beneath the child layer may be visible when region clipping is applied.
								var layerChild = layer.aLayers[j];
								var bContainsPoints = true;
								if (layerChild.envelopeLL && layerChild.envelopeLL.containsPoints(aPoints))
								{
									if ((SIX.App.bDHTMLControl && (!layerChild.aClipRegion.length || map.inLayerClipRegion(layerChild))) ||
										(!SIX.App.bDHTMLControl && !layerChild.aImageRegion.length))
									{
										nLastVisibleLayerOrder = aLayersWithOrder[i].nOrder;
										break;
									}
								}
							}
						}
						else
						{
							nLastVisibleLayerOrder = aLayersWithOrder[i].nOrder;
						}
					}
				}
			}
		}
	}

	//Need to sort layers again so layers that were deselected above (due to being hidden behind another layer) get 
	//sorted with the other unselected layers based on their best index.
	aLayersWithOrder.sort(fnSort);
	
	for (var i=0; i<this.layers.length; i++)
	{
		//Only set bChanged to true if the layers' order or selection state has changed. 
		if ((aLayersWithOrder[i].nOrder != this.layersOrder[i]) || 
			(aLayersWithOrder[i].bSelected != this.layers[this.layersOrder[i]].bSelected))
			bChanged = true;				
		this.layersOrder[i] = aLayersWithOrder[i].nOrder;
		this.layers[this.layersOrder[i]].setSelected(aLayersWithOrder[i].bSelected);
	}
		
	return bChanged;
}

SIX.Service.prototype.addChildServicesSlider = function (slider)
{
	this.childServicesSlider = slider;
	return slider;
}

SIX.Service.prototype.childServicesSliderCallback = function ()
{
	if (!this.tSetChildServicesTransparenciesInterval)
		this.tSetChildServicesTransparenciesInterval = setInterval(this.myself + ".setChildServicesTransparencies()", 100);
}

SIX.Service.prototype.cancelSetChildServicesTransparencies = function ()
{
	if (this.tSetChildServicesTransparenciesTimeout)
	{
		clearTimeout(this.tSetChildServicesTransparenciesTimeout);
		this.tSetChildServicesTransparenciesTimeout = null;
	}
	if (this.tSetChildServicesTransparenciesInterval)
	{
		clearInterval(this.tSetChildServicesTransparenciesInterval);
		this.tSetChildServicesTransparenciesInterval = null;
	}
}

SIX.Service.prototype.setChildServicesTransparencies = function ()
{
	if (this.tSetChildServicesTransparenciesTimeout)
		clearTimeout(this.tSetChildServicesTransparenciesTimeout);
	this.tSetChildServicesTransparenciesTimeout = setTimeout(this.myself + ".cancelSetChildServicesTransparencies()", 500);
	
	var dPercent = this.childServicesSlider.getValue();
	
	if (dPercent < 0) dPercent = 0;
	if (dPercent > 100) dPercent = 100;
	
	var dFirstSelectedLayerIndex = this.aServices.length * dPercent / 100;
	var nFirstSelectedLayerIndex = Math.floor(dFirstSelectedLayerIndex);

	var elemSlider = document.getElementById(this.childServicesSlider.sliderUid);
	if (elemSlider)
	{
		var nIncrementWidth = elemSlider.offsetWidth / this.aServices.length;
		var nFirstSegmentWidth = nIncrementWidth - this.childServicesSlider.nTrackX; 
		var nLastSegmentWidth = nIncrementWidth - (elemSlider.offsetWidth - this.childServicesSlider.nTrackWidth - this.childServicesSlider.nTrackX);
	
		if (nFirstSelectedLayerIndex == 0)
		{
			dFirstSelectedLayerIndex = (nIncrementWidth * dFirstSelectedLayerIndex - this.childServicesSlider.nTrackX) / (nFirstSegmentWidth * 2) + 0.5;
		}
		else if (nFirstSelectedLayerIndex == this.aServices.length - 1)
		{
			dFirstSelectedLayerIndex = nFirstSelectedLayerIndex + (nIncrementWidth * (dFirstSelectedLayerIndex - nFirstSelectedLayerIndex)) / (nLastSegmentWidth * 2);
		}
	}
	
	var dFraction = 50 - (dFirstSelectedLayerIndex - nFirstSelectedLayerIndex) * 100;
	var dFirstSelectedLayerTransparency = 100 - Math.abs(dFraction);
	var nSecondSelectedLayerIndex = -1;
	
	if (dFirstSelectedLayerTransparency < 100)
	{
		if (dFraction > 0) 
		{
			if (nFirstSelectedLayerIndex > 0)
				nSecondSelectedLayerIndex = nFirstSelectedLayerIndex - 1;
		}
		else if (dFraction < 0)
		{
			if (nFirstSelectedLayerIndex < this.aServices.length - 1)
				nSecondSelectedLayerIndex = nFirstSelectedLayerIndex + 1;
		}
	}

	for (var i=0; i<this.aServices.length; i++)
	{
		var childService = this.aServices[i];

		var bSelected = (i == nFirstSelectedLayerIndex || i == nSecondSelectedLayerIndex);

		if (bSelected)
		{
			var dTransparency = 100;
			if (i == nFirstSelectedLayerIndex) 
				dTransparency = dFirstSelectedLayerTransparency;
			else if (i == nSecondSelectedLayerIndex) 
				dTransparency = 100 - dFirstSelectedLayerTransparency;
			
			if (childService.transparency != (dTransparency / 100))
				childService.setTransparency(dTransparency);
		}
		else
		{
			if (childService.transparency > 0)
				childService.setTransparency(0);
		}
	}
}

SIX.Service.prototype.getLayerOrder = function (nIndex)
{
	var nOrder = -1;
	for (var i=0; i<this.layersOrder.length; i++)
	{
		if (nIndex == this.layersOrder[i])
		{
			nOrder = i;
			break;
		}
	}
	return nOrder;
}

SIX.Service.prototype.getBestScale = function (map)
{
	return (this.access.getBestScale(map));
}

SIX.Service.prototype.updateLayersListWidth = function ()
{
	if (this.ulLayersList && this.divLayers && this.divLayers.parentNode && this.divLayers.parentNode.scrollWidth)
	{
		this.ulLayersList.style.position = "relative";
		this.ulLayersList.style.top = "0px";
		this.ulLayersList.style.left = "0px";
		this.ulLayersList.style.width = this.divLayers.parentNode.scrollWidth + "px";
	}	
}

SIX.Service.prototype.attachService = function (svc)
{
    this.aAttachedServices[this.aAttachedServices.length] = svc;
    svc.aServicesAttachedTo[svc.aServicesAttachedTo.length] = this;
}

SIX.SliderEx = function (attachObject, attachObjectCallback, aLabels, sBarImage, sPointerImage, nWidth, nTrackX, nTrackY, nTrackWidth, sToolTip)
{
	SIX.addRef(this);
	this.div = null;
	this.attachObject = attachObject;
	this.attachObjectCallback = attachObjectCallback;
	this.barUid = SIX.Uid.getUid("LayerSliderBar");
	this.pointerUid = SIX.Uid.getUid("LayerSliderPointer");
	this.pointerContainerUid = SIX.Uid.getUid("LayerSliderPointerContainer");
	this.aLabels = aLabels;
	this.sBarImage = sBarImage;
	this.sPointerImage = sPointerImage;
	this.nPointerX = nTrackX;
	this.nWidth = nWidth;
	this.nTrackX = nTrackX;
	this.nTrackY = nTrackY;
	this.nTrackWidth = nTrackWidth;
	this.sToolTip = sToolTip;
}

SIX.SliderEx.prototype.action = function (action, moving, x, y) 
{
	var elemSlider = document.getElementById(this.sliderUid);
	var elemPointer = document.getElementById(this.pointerContainerUid);
	if (elemSlider && elemPointer)
	{
		//Need fudge factor to correct for ~5 pixel error in SIX.Util.findPosX
		var nFudgeFactorX = -5;
		var nSliderX = SIX.Util.findPosX(elemSlider);
		var nPointerX = Math.round(x) - nSliderX + nFudgeFactorX;
		if (nPointerX > this.nTrackX + this.nTrackWidth)
			nPointerX = this.nTrackX + this.nTrackWidth;
		else if (nPointerX < this.nTrackX)
			nPointerX = this.nTrackX;
		this.nPointerX = nPointerX;
		elemPointer.style.left = this.nPointerX + "px";
		elemPointer.style.top = this.nTrackY + "px";
		this.attachObject[this.attachObjectCallback]();
	}
}

SIX.SliderEx.prototype.getValue = function ()
{
	var dPercent = 0.0;
	var elemSlider = document.getElementById(this.sliderUid);
	if (elemSlider)
		dPercent = 100 * this.nPointerX / elemSlider.offsetWidth;
	return dPercent;
}

SIX.SliderEx.prototype.setValue = function (dValue)
{
	if (dValue < 0)
		dValue = 0;
	else if (dValue > 100)
		dValue = 100;
	this.nPointerX = Math.round(this.nTrackX + this.nTrackWidth * dValue / 100);
}

SIX.SliderEx.prototype.init = function ()
{
	if (!this.div)
		this.div = SIX.Util.createElement("div", "sliderExContainer");

	var sHtml = "<div class='sliderExContainer'>" +
				"<div id='" + this.sliderUid + "' class='sliderEx'>" +
				"<img id='" + this.barUid + "' class='sliderExBar' style='width:" + this.nWidth + "px' alt='" + this.sToolTip + "' title='" + this.sToolTip + "' src='" + this.sBarImage + "'>" +
				"<div id='" + this.pointerContainerUid + "' class='sliderExPointer' style='left:" + this.nPointerX + "px;top:" + this.nTrackY + "px'>" +
				"<img id='" + this.pointerUid + "' alt='" + this.sToolTip + "' title='" + this.sToolTip + "' class='sliderExPointer' src='" + this.sPointerImage + "'>" +
				"</div>";
				if (this.aLabels && this.aLabels.length)
				{  
					var nWidth = Math.floor(this.nWidth / this.aLabels.length);
						
					sHtml += "<div class='sliderExLabels' style='width:" + this.nWidth + "px'>";
					for (var i=0; i<this.aLabels.length; i++)
					{
						var nLeft = Math.floor(i * this.nWidth / this.aLabels.length);
						sHtml += "<div class='sliderExLabel' style='left:" + nLeft + "px;width:" + nWidth + "px'>" + this.aLabels[i] + "</div>";
					}
					sHtml += "</div>";
				}
				sHtml += "</div>";
				sHtml += "</div>";
	this.div.innerHTML = sHtml;
	SIX.App.addMouseAction(this.pointerUid,this,0,true);
	SIX.App.addMouseAction(this.barUid,this,0,true);				
	return this.div;
}

//	--------------------------------------------------------------------------- 
// 		SIX.Slider Class
//	--------------------------------------------------------------------------- 
SIX.Slider = function (opacity)
{
	SIX.addRef(this);
	this.div = null;
	this.opacity = (arguments.length && opacity >= 0 && opacity <= 1) ? opacity : 1;
	this.attachObject = new Array();
	
	this.sliderUid = SIX.Uid.getUid("slider");
	this.ptrUid = SIX.Uid.getUid("ptr");
	this.sToolTip = "Change transparency";
	this.sLeftLabel = "Transparency: ";
	this.sRightLabel = "";
	
	SIX.App.addMouseAction(this.sliderUid,this,1,true);
	SIX.App.addMouseAction(this.ptrUid,this,1,true);
}

SIX.Slider.prototype.build = function () 
{
	if (!this.div)
		this.div = SIX.Util.createElement("div", "sliderContainer");
	this.div.style.cssFloat = "none";

	var nPointerPos = Math.round(this.opacity * 80) - 5;
	this.div.innerHTML = "<div class='sliderContainer' style='float:none'>" +
							"<span class='sliderLabel'>" + this.sLeftLabel + "</span>" +
							"<div class='slider'>" +
							"<img id='" + this.sliderUid + "' class='sliderBar' alt='" + this.sToolTip + "' title='" + this.sToolTip + "'" + SIX.App.imageSrc("images/slider_line") + ">" +
							"<img id='" + this.ptrUid + "' alt='" + this.sToolTip + "' title='" + this.sToolTip + "' class='sliderPointer' style='left:" + nPointerPos + "px'" + SIX.App.imageSrc("images/slider_ptr") + ">" +
							"</div>" +
							"<span class='sliderLabel'>" + this.sRightLabel + "</span>" +
							"</div>";
				
	return this.div;
}

SIX.Slider.prototype.attach = function (source, targetUid, fnCallback)
{
	if (!source)
		return;
	var item = new Object();
	item.source = source;
	item.targetUid = targetUid;
	item.fnCallback = fnCallback;
	this.attachObject[this.attachObject.length] = item;
	source.attachSlider = this;
	if (fnCallback)
		fnCallback(this.opacity);
}

SIX.Slider.prototype.action = function (action,moving,x,y) 
{
	if (action != 1)
		return;
	var percent = 0;
	var sliderElement = document.getElementById(this.sliderUid);
	if (sliderElement)
	{
		var nWidth = sliderElement.offsetWidth;
		nWidth = nWidth ? nWidth : 100;
		var nSliderPosX = SIX.Util.findPosX(sliderElement);
		if (nSliderPosX > 0) 
			percent = Math.round(100 * (x - nSliderPosX - 5) / nWidth);
	}
	
	this.setSliderPosition(percent);
}

SIX.Slider.prototype.setSliderPosition = function (nPercent)
{
	if (nPercent < 0)
		nPercent = 0;
	else if (nPercent > 99)
		nPercent = 99;
	var elemPtr = document.getElementById(this.ptrUid);
	var elemSlider = document.getElementById(this.sliderUid);
	if (elemPtr && elemSlider)
		elemPtr.style.left = (Math.round(nPercent * elemSlider.offsetWidth / 100) - 5) + "px";
	
	this.opacity = nPercent/100;
	
	for (var i=0; i < this.attachObject.length; i++)
	{
		var item = this.attachObject[i];
		if (item.targetUid && (targetElem = document.getElementById(item.targetUid)))
		{
			item.source.opacity = nPercent / 100;
			targetElem.style.opacity = nPercent/100;
			if (targetElem.filters && targetElem.filters.alpha)
				targetElem.filters.alpha.opacity = nPercent;
		}
		if (item.fnCallback)
		{
			item.fnCallback(nPercent/100);
		}
	}
}

/**
 * @class SIX.Status objects define the contents of status windows. SIX.Status objects are attached to the main SIX.Map object for the page. 
 */
SIX.Status = function ()
{
	SIX.addRef(this);
	this.div = null;
	this.name = "Status";
	if (channel)
		channel.setStatus(this);
	else
		SIX.App.setStatus(this);
	this.map = 0;
	this.scale = 0;	
	this.nCurrentCoordSys = -1;//Currently selected coordinate system.
	this.aCoordSysList = new Array();
	this.coordsUid = SIX.Uid.getUid("coords");
	this.scaleUid = SIX.Uid.getUid("scale");
	this.errorUid = SIX.Uid.getUid("error");
	this.coord1Uid = SIX.Uid.getUid("coord1");
	this.coord1LabelUid = SIX.Uid.getUid("coord1Label");
	this.coord2Uid = SIX.Uid.getUid("coord2");
	this.coord2LabelUid = SIX.Uid.getUid("coord2Label");	
	
	this.tHideCoordSysMenuTimer = null;
	this.sSelectedCoordSysMenuItemColor = "#FFFFFF";
	this.sSelectedCoordSysMenuItemBackgroundColor = "#316AC5";		
	
	this.progressBarUid = SIX.Uid.getUid("progressBar");
	this.progressPercentUid = SIX.Uid.getUid("progressPercent");
	
	this.progressBar = new NCSProgressbar3(this.progressUid, null, 80, 15, 10); 
	this.progressBar.setProgressCompleteColor("#666666");
	this.progressBar.setProgressIncompleteColor("#FFFFFF");
}

SIX.Status.prototype.init = function () 
{	
	this.div = document.createElement("div");
	return this.div;
}

SIX.Status.prototype.showLastError = function () 
{
	SIX.App.popupAlert(SIX.LastError.sTitle, SIX.LastError.sMessage, SIX.LastError.sErrorText, true);
	this.hideErrorIndicator();
}

SIX.Status.prototype.hideErrorIndicator = function ()
{
	var element = document.getElementById(this.errorUid);
	if (element)
		element.style.display = "none";
}

SIX.Status.prototype.showErrorIndicator = function ()
{
	var element = document.getElementById(this.errorUid);
	if (element)
		element.style.display = "block";
}

SIX.Status.prototype.addCoordSysMenuItem = function (sName, sDatum, sProjection, bDMS)
{	
	var objCoordSysMenuItem = new Object();
	objCoordSysMenuItem.sName = sName;
	objCoordSysMenuItem.sDatum = sDatum.toUpperCase();
	objCoordSysMenuItem.sProjection = sProjection.toUpperCase();
	objCoordSysMenuItem.bDMS = bDMS ? true : false;
	objCoordSysMenuItem.uid = SIX.Uid.getUid("coordSysMenuItem");
	this.aCoordSysList[this.aCoordSysList.length] = objCoordSysMenuItem;
	if (this.nCurrentCoordSys == -1) this.nCurrentCoordSys = 0;
}

/**
*	PURPOSE: sets the status window degrees format
*/
SIX.Status.prototype.changeCoordinatesFormat = function (nSelectedCoordSys) 
{	
	if ((nSelectedCoordSys >= 0) && (nSelectedCoordSys < this.aCoordSysList.length))
		this.nCurrentCoordSys = nSelectedCoordSys;
	else
		this.nCurrentCoordSys = 0;
	
	this.update();		
}

SIX.Status.prototype.build = function () 
{	
	if (!this.div)
		return;
	var sLabel1 = "Latitude :";
	var sLabel2 = "Longitude :";
	
	if (this.aCoordSysList.length)
	{
		var objCoordSysMenuItem = this.aCoordSysList[0];
		if (objCoordSysMenuItem.sProjection != "GEODETIC")
		{
			sLabel1 = "Easting :";
			sLabel2 = "Northing :";
		}
	}
	
	var txt = "<div class='statusContainer'>" +
		"<div class='statusError' style='display:none' id='" + this.errorUid + "'><a href='#'><img alt='An error has occurred. Click here for details' title='An error has occurred. Click here for details' border=0" + SIX.App.imageSrc("images/error") + "onclick='" + this.myself + ".showLastError()'></a></div>" +
		"<div class='statusCoordinates' id='" + this.coordsUid + "'>" +
		"<span class='statusLabel' id='" + this.coord1LabelUid + "'>" + sLabel1 + "</span><span id='" + this.coord1Uid + "'></span>" + 
		"<span class='statusLabel' id='" + this.coord2LabelUid + "'>" + sLabel2 + "</span><span id='" + this.coord2Uid + "'></span>" + 
		"<img class='statusCoordinatesChooserIcon' " + SIX.App.imageSrc("images/collapse") + " border='0' onclick='" + this.myself + ".showCoordSysMenu()'>" +
		"</div>" +
		"<div class='statusButton'>" +	
		"<img " + SIX.App.imageSrc("images/status") + " border='0' onmouseover='this.src = SIX.App.imageUrl(\"images/status_on\")' onmouseout='this.src = SIX.App.imageUrl(\"images/status\")' onclick='" + this.myself + ".showHideConsole();'>" +		
		"</div>" +			
		"<div class='statusProgress'>" +	
		"<div class='statusProgressLabel'>Progress :</div>" +	
		"<div class='statusProgressBar'>" + this.progressBar.build() + "</div>" +	
		"<div class='statusProgressPercent' id='" + this.progressPercentUid + "'>0 %</div>" +	
		"</div>" + 	
		"<div class='statusScale'>" + /* To ensure the scale text is centered, this div must be added after all other divs included in the status bar */
		"<span class='statusLabel' style='margin-left:2px'>Scale :</span><span>1:</span><span id='" + this.scaleUid + "'> (approx) </span>" +
		"</div>" +			
		"</div>";
	
	this.div.innerHTML = txt;
}

SIX.Status.prototype.showHideConsole = function () 
{
	var channel = SIX.App.getCurrentChannel();
	channel.showHideConsole();
}

SIX.Status.prototype.coordSysMenuItemOnClick = function (nSelected) 
{
	this.changeCoordinatesFormat(nSelected);
	this.hideCoordSysMenu();
}

SIX.Status.prototype.coordSysMenuItemOnMouseOver = function (nSelected) 
{
	if (this.tHideCoordSysMenuTimer)
	{
		clearTimeout(this.tHideCoordSysMenuTimer);
		this.tHideCoordSysMenuTimer = null;
	}
	
	this.highlightCoordSysMenuItem(nSelected);
}

SIX.Status.prototype.coordSysMenuItemOnMouseOut = function (nSelected) 
{
	this.tHideCoordSysMenuTimer = setTimeout("SIX.App.hideCoordSysMenu()", 250);
}

SIX.Status.prototype.highlightCoordSysMenuItem = function (nIndex) 
{
	var elemCoordSysMenuItemsContainer = document.getElementById(SIX.App.coordSysMenuItemsContainerUid);
	
	if (elemCoordSysMenuItemsContainer)
	{
		for (var i=0; i<this.aCoordSysList.length; i++)
		{
			elemCoordSysMenuItemsContainer.childNodes[i].style.color = (i == nIndex) ? this.sSelectedCoordSysMenuItemColor : "";
			elemCoordSysMenuItemsContainer.childNodes[i].style.backgroundColor = (i == nIndex) ? this.sSelectedCoordSysMenuItemBackgroundColor : "";
		}
	}	
}

SIX.Status.prototype.coordSysMenuVisible = function () {
	var bVisible = false;
	var elemCoordSysMenu = document.getElementById(SIX.App.coordSysMenuUid);
	if (elemCoordSysMenu && elemCoordSysMenu.style.display != "none")
		bVisible = true;
	return bVisible;
}

SIX.Status.prototype.hideCoordSysMenu = function () {
	var elemCoordSysMenu = document.getElementById(SIX.App.coordSysMenuUid);
	elemCoordSysMenu.style.display = "none";
}

SIX.Status.prototype.showCoordSysMenu = function () {
	var elemCoordSysMenu = document.getElementById(SIX.App.coordSysMenuUid);
	if (elemCoordSysMenu)
	{
		this.updateCoordSysMenu(true);
		var nOffsetRight = 0;
		var elemCoords = document.getElementById(this.coordsUid);
		if (elemCoords)	nOffsetRight += (elemCoords.offsetWidth - 20);
		
		var elemError = document.getElementById(this.errorUid);
		if (elemError && (elemError.style.display != "none")) nOffsetRight += elemError.offsetWidth;
	
		elemCoordSysMenu.style.left = nOffsetRight;
		elemCoordSysMenu.style.display = "block";
		
		var channelCurrent = SIX.App.getCurrentChannel();
		
		var zOrder = 0;
		for (var i=0; i<channelCurrent.wins.length; i++)
		{
			var zOrderWin = channelCurrent.wins[i].zOrder;
			if (zOrderWin >= zOrder)
				zOrder = zOrderWin + 1;
		}
		elemCoordSysMenu.style.zIndex = zOrder;
	}

	this.highlightCoordSysMenuItem(this.nCurrentCoordSys);
	
	var elemCoordSysMenuItemsContainer = document.getElementById(SIX.App.coordSysMenuItemsContainerUid);
	if (elemCoordSysMenuItemsContainer)
	{
		elemCoordSysMenuItemsContainer.childNodes[this.nCurrentCoordSys].style.color = this.sSelectedCoordSysMenuItemColor;
		elemCoordSysMenuItemsContainer.childNodes[this.nCurrentCoordSys].style.backgroundColor = this.sSelectedCoordSysMenuItemBackgroundColor;	
	}
}

SIX.Status.prototype.setProgress = function (nPercent)
{
	this.progressBar.setProgress(nPercent);
	var elemProgressPercent = document.getElementById(this.progressPercentUid);
	if (elemProgressPercent) elemProgressPercent.innerHTML = "" + nPercent + " %";
}

SIX.Status.prototype.update = function () 
{
	if (!this.map || !this.div)
		return;
		
	var map = this.map;
	
	this.updateCoordSysMenu(this.coordSysMenuVisible());

	this.scale = Math.floor(map.scale);
	var elemScale = document.getElementById(this.scaleUid);
	if (elemScale) 
	{
		var input = "" + this.scale;
		var scale = "";
		var len = input.indexOf(".");
		if(len == -1)
	  	   	len = input.length;
		else
  		   	scale = input.substring(len, input.length);
		while(len > 3) 
		{
  	  		scale = input.substring(len-3, len) + scale;
			scale = "," + scale;
			len -= 3;
		}        
		scale = input.substring(0, len) + scale;
		elemScale.innerHTML = scale;
	}
}

SIX.Status.prototype.updateCoordSysMenu = function (bUpdateAll)
{
	if (!this.map || this.map.dMouseWorldX == null || this.map.dMouseWorldY == null)
		return;
		
	var map = this.map;
	var theStatus = this;
	
	var sMapDatum = map.datum();
	var sMapProjection = map.projection();
	
	var fnUpdateSelectedCoord = function(coordSys, aCoords) {
		with ({theStatus : theStatus})
		{
			if (aCoords)
			{
				var dX = aCoords[0][0];
				var dY = aCoords[0][1];
				var sCoord1Value = "";
				var sCoord1Label = "";
				var sCoord2Value = "";
				var sCoord2Label = "";
			
				if (coordSys.sProjection.toLowerCase() == "geodetic")
				{
					sCoord1Label = "Latitude :";
					sCoord2Label = "Longitude :";
					
					if (dX == null || dY == null || isNaN(dX) || isNaN(dY))
					{
						sCoord1Value = "Undefined";
						sCoord2Value = "Undefined";
					}
					else if (coordSys.bDMS)
					{
						sCoord1Value = SIX.Util.latitudeToString(dY);
						sCoord2Value = SIX.Util.longitudeToString(dX);
					}
					else
					{
						sCoord1Value = dY.toFixed(6);
						sCoord2Value = dX.toFixed(6);														
					}
				}	
				else
				{
					sCoord1Label = "Easting :";
					sCoord2Label = "Northing :";	
						
					if (isNaN(dX) || isNaN(dY))
					{
						sCoord1Value = "Undefined";
						sCoord2Value = "Undefined";
					}
					else
					{
						sCoord1Value = dX.toFixed(2);
						sCoord2Value = dY.toFixed(2);		
					}
				}
				
				var elemCoord1 = document.getElementById(theStatus.coord1Uid);
				var elemCoord1Label = document.getElementById(theStatus.coord1LabelUid);
				var elemCoord2 = document.getElementById(theStatus.coord2Uid);
				var elemCoord2Label = document.getElementById(theStatus.coord2LabelUid);
				
				if (elemCoord1) elemCoord1.innerHTML = sCoord1Value;
				if (elemCoord1Label) elemCoord1Label.innerHTML = sCoord1Label;
				if (elemCoord2) elemCoord2.innerHTML = sCoord2Value;
				if (elemCoord2Label) elemCoord2Label.innerHTML = sCoord2Label;
			}
		}
	}	
	
	var objCoordSys = this.aCoordSysList[this.nCurrentCoordSys];
	var sDatum = (objCoordSys.sDatum != "") ? objCoordSys.sDatum : sMapDatum;
	var sProjection = objCoordSys.sProjection;

	if (SIX.App.bDHTMLControl)
	{
		if (this.AJAXConvertCoords)
			this.AJAXConvertCoords.Cancel();		
		this.AJAXConvertCoords = map.convertCoordsAsync(sDatum, sMapProjection, [[map.dMouseWorldX, map.dMouseWorldY]], sDatum, sProjection, fnUpdateSelectedCoord, objCoordSys);	
	}
	else
	{
		var aCoords = map.convertCoords(sDatum, sMapProjection, [[map.dMouseWorldX, map.dMouseWorldY]], sDatum, sProjection);
		fnUpdateSelectedCoord(objCoordSys, aCoords);
	}
	
	if (bUpdateAll)
	{
		var fnUpdateCoord = function (coordSys, aCoords) {
			if (aCoords)
			{
				var elemCoord = document.getElementById(coordSys.uid);
				if (elemCoord)
				{
					var dX = aCoords[0][0];
					var dY = aCoords[0][1];
							
					if (coordSys.sProjection.toLowerCase() == "geodetic")
					{
						if (coordSys.bDMS)
							var sCoord = SIX.Util.latitudeToString(dY) + " " + SIX.Util.longitudeToString(dX);
						else
							var sCoord = Math.abs(dY).toFixed(6) + (dY >= 0 ? "N" : "S") + " " + Math.abs(dX).toFixed(6) + (dX >= 0 ? "E" : "W");
						elemCoord.innerHTML = sCoord;
					}	
					else
					{
						elemCoord.innerHTML = dX.toFixed(2) + "E " + dY.toFixed(2) + "N";
					}
				}
			}
		}
	
		var sMenuItems = "";
		for (var i=0; i<this.aCoordSysList.length; i++)
		{
			var objCoordSys = this.aCoordSysList[i];
			var sDatum = (objCoordSys.sDatum != "") ? objCoordSys.sDatum : sMapDatum;
			var sProjection = objCoordSys.sProjection;
			var sCoord = "Loading...";
			
			sMenuItems += "<div class='coordSysMenuItem'" + 
						" style='cursor:" + (SIX.App.isIE ? "hand" : "pointer") + "'" +
						" onmouseover='" + this.myself + ".coordSysMenuItemOnMouseOver(" + i + ")'" +
						" onclick='" + this.myself + ".coordSysMenuItemOnClick(" + i + ")'>" + 
						"<div class='coordSysMenuItemName'>" + objCoordSys.sName + " :" + "</div>" + 
						"<div class='coordSysMenuItemValue' id='" + objCoordSys.uid + "'>" + sCoord + "</div>" + 
						"</div>";		
		}	
		var elemCoordSysMenuItemsContainer = document.getElementById(SIX.App.coordSysMenuItemsContainerUid);
		if (elemCoordSysMenuItemsContainer) 
			elemCoordSysMenuItemsContainer.innerHTML = sMenuItems;
			
		for (var i=0; i<this.aCoordSysList.length; i++)
		{
			var objCoordSys = this.aCoordSysList[i];
			var sDatum = (objCoordSys.sDatum != "") ? objCoordSys.sDatum : sMapDatum;
			var sProjection = objCoordSys.sProjection;
			
			if (SIX.App.bDHTMLControl)
			{
				if (objCoordSys.AJAXConvertCoords)
					objCoordSys.AJAXConvertCoords.Cancel();		
				objCoordSys.AJAXConvertCoords = map.convertCoordsAsync(sDatum, sMapProjection, [[map.dMouseWorldX, map.dMouseWorldY]], sDatum, sProjection, fnUpdateCoord, objCoordSys);	
			}
			else
			{
				var aCoords = map.convertCoords(sDatum, sMapProjection, [[map.dMouseWorldX, map.dMouseWorldY]], sDatum, sProjection);
				fnUpdateCoord(objCoordSys, aCoords);
			}
		}					
			
		this.highlightCoordSysMenuItem(this.nCurrentCoordSys);
	}
}

//	--------------------------------------------------------------------------- 
// SIX.Toolbar Class
//	---------------------------------------------------------------------------
SIX.eToolTypes = {
	NONE : 0,
	EXCLUSIVE : 1,
	TOGGLE : 2
}

SIX.ToolbarSpace = function ()
{
	this.div = null;
	this.containerUid = SIX.Uid.getUid("space");
}

SIX.ToolbarSpace.prototype.init = function ()
{
	var elemSpace = SIX.Util.createElement("div", "toolbarSpace", this.containerUid);
	elemSpace.innerHTML = "&nbsp;";
	return elemSpace;
}

SIX.eToolbarCallbacks = {
	ONRESIZE : 1
}

/**
 * @class An SIX.Toolbar object defines the contents of a toolbar window. 
 * Most web maps will have a toolbar window to allow the user to interact with the map. 
 * An SIX.Toolbar object will be added to one of the windows to be used as a toolbar.
 */
SIX.Toolbar = function (channel) 
{
	SIX.addRef(this);
	this.div = null;
	this.toolGroup = new SIX.ToolGroup();
	channel.addToolGroup(this.toolGroup);
	this.items = new Array();
	this.toolbarUid = SIX.Uid.getUid("toolbar");
	this.hCallbackManagers = new SIX.Hash();
}

SIX.Toolbar.prototype.executeCallbacks = function (nType)
{
	if (this.hCallbackManagers.hasItem(nType))
	{
		var callbackManager = this.hCallbackManagers.getItem(nType);
		var aArguments = arguments.length > 1 ? Array.prototype.slice.call(arguments, 1, arguments.length) : new Array();
		callbackManager.executeCallbacks.apply(callbackManager, aArguments);
	}
}

SIX.Toolbar.prototype.addCallback = function (nType, method, data)
{
	var callbackManager = this.hCallbackManagers.hasItem(nType) ?  this.hCallbackManagers.getItem(nType) : new SIX.CallbackManager();
	callbackManager.addCallback(new SIX.Callback(this, method, (arguments.length >= 3 ? data : null)));
	this.hCallbackManagers.setItem(nType, callbackManager);
}

SIX.Toolbar.prototype.addTool = function (tool) 
{	
	if (this.getToolIndex(tool) == -1)
	{
		this.items[this.items.length] = this.toolGroup.addTool(tool);
		var elemToolbar = document.getElementById(this.toolbarUid);
		if (elemToolbar && !document.getElementById(tool.containerUid))
		{
			var elemTool = tool.init();
			elemToolbar.appendChild(elemTool);
		}
	}
	return tool;
}

SIX.Toolbar.prototype.removeTool = function (tool)
{
	var nIndex = this.getToolIndex(tool)
	if (nIndex != -1)
	{
		this.items.splice(nIndex, 1);
		this.toolGroup.removeTool(tool);
		var elemToolbar = document.getElementById(this.toolbarUid);
		var elemTool = document.getElementById(tool.containerUid);
		if (elemToolbar && elemTool)
			elemToolbar.removeChild(elemTool);
	}
}

SIX.Toolbar.prototype.getToolIndex = function (tool)
{
	var nIndex = -1;
	for (var i=0; i<this.items.length; i++)
	{
		if(this.items[i] == tool)
		{
			nIndex = i;
			break;
		}
	}
	return nIndex;
}

SIX.Toolbar.prototype.addSpace = function () 
{
	var spaceItem = new SIX.ToolbarSpace();
	this.items[this.items.length] = spaceItem;
	var elemToolbar = document.getElementById(this.toolbarUid);
	if (elemToolbar)
	{
		var elemSpace = spaceItem.init();
		elemToolbar.appendChild(elemSpace);
	}	
}

SIX.Toolbar.prototype.init = function () 
{
	this.div = SIX.Util.createElement("div", "toolbar", this.toolbarUid);
	
	for(var i=0; i < this.items.length; i++)
		this.div.appendChild(this.initTool(i));
	
	return this.div;
}

SIX.Toolbar.prototype.initTool = function (nIndex)
{
	var item = this.items[nIndex];
	return item.init();
}

//	--------------------------------------------------------------------------- 
// win	Class
// Arguments: 
// channel	If specifed, window is only visible when channel is current channel. 
// type		"pane", "map", "query", "legend", "status".
// title	Window title
// sParams	String value containing list of extra parameters for the window.
//	---------------------------------------------------------------------------

/**
 * @class Window layout class. SIX.Window objects are used to define windows. 
 * SIX.Window objects do not define the contents of each window; they simply define the existence of windows. 
 */
SIX.Window = function (channel, type, title, sParams) 
{
	SIX.addRef(this);
	this.div = null;
	this.divFixed = null;
	this.divScroll = null;
	
	if( arguments.length < 3 ) {
		this.name = "";
		this.title = "";
	} else {
		this.name = title;
		this.title = title;
	}

	this.parameters = new SIX.Parameters(sParams ? sParams : "");
	
	this.sCustomBorder1Color = this.parameters.getParameter("border1color", "string", "#000000");
	this.sCustomBorder2Color = this.parameters.getParameter("border2color", "string", "#000000");
	this.sCustomBorderColor = this.parameters.getParameter("bordercolor", "string", "#000000");
	this.nCustomBorder1Width = this.parameters.getParameter("border1width", "number", 0);
	this.nCustomBorder2Width = this.parameters.getParameter("border2width", "number", 0);
	this.nCustomBorderWidth = this.nCustomBorder1Width + this.nCustomBorder2Width;
	if (!this.nCustomBorderWidth)
		this.nCustomBorderWidth = this.parameters.getParameter("borderwidth", "number", 0);
	
	this.bCollapsible = this.parameters.getParameter("collapsible", "boolean", false);
	this.bShowHideReset = this.parameters.getParameter("showhidereset", "boolean", false); 
	this.bShowUnselectedLayers = this.parameters.getParameter("showunselectedlayers", "boolean", true); 
	this.bShowUnselectedLayersDefault = this.bShowUnselectedLayers;
	
	this.isVisible = this.parameters.getParameter("visible", "boolean", true);
	
	this.channel = channel ? channel : null;
	this.sStyle = "";
	this.type = type;
	this.uid = SIX.Uid.getUid("win");
	this.contentsUid = SIX.Uid.getUid("contents");
	this.bInitialized = false;
	
	this.borderLeftWidthCalculatorOuterDivUid = SIX.Uid.getUid("borderwidthcalculator");
	this.borderLeftWidthCalculatorInnerDivUid = SIX.Uid.getUid("borderwidthcalculator");
	this.borderRightWidthCalculatorOuterDivUid = SIX.Uid.getUid("borderwidthcalculator");
	this.borderRightWidthCalculatorInnerDivUid = SIX.Uid.getUid("borderwidthcalculator");
	this.borderTopWidthCalculatorOuterDivUid = SIX.Uid.getUid("borderwidthcalculator");
	this.borderTopWidthCalculatorInnerDivUid = SIX.Uid.getUid("borderwidthcalculator");
	this.borderBottomWidthCalculatorOuterDivUid = SIX.Uid.getUid("borderwidthcalculator");
	this.borderBottomWidthCalculatorInnerDivUid = SIX.Uid.getUid("borderwidthcalculator");
	
	this.expandCollapseUid = SIX.Uid.getUid("expandCollapse");
		
	this.imageShow = new Image();
	this.imageShow.src = SIX.App.imageUrl("images/show");
	this.imageHide = new Image();
	this.imageHide.src = SIX.App.imageUrl("images/hide");
	this.imageReset = new Image();
	this.imageReset.src = SIX.App.imageUrl("images/reset");

	this.MIN_WIDTH = 10;
	this.MIN_HEIGHT = 10;
	this.fixedHeight = this.parameters.getParameter("fixedheight", "string", "20px");
	
	this.nMinOpenHeight = 100;
	
	this.zOrder = 0;
	this.isMin = false;
	this.showTitle = true;
	this.x = 0;
	this.y = 0;
	this.framed = true;
	this.moveUid 		= SIX.Uid.getUid("move");
	this.minUid 		= SIX.Uid.getUid("min");
	this.closeUid 	= SIX.Uid.getUid("close");	
	this.resizeUid 	= SIX.Uid.getUid("resize");
	this.resizeContainerUid = SIX.Uid.getUid("resizeContainer");
	this.expandCollapseUid = SIX.Uid.getUid("expandcollapse");
	this.titleUid		= SIX.Uid.getUid("title");
	this.showHideUid = SIX.Uid.getUid("showHide");
	this.resetUid = SIX.Uid.getUid("reset");
	this.resizeMapHeightContainerUid = SIX.Uid.getUid("resizeMapHeightContainer");
	this.resizeMapHeightUid = SIX.Uid.getUid("resizeMapHeight");
	this.resizeMapWidthContainerUid = SIX.Uid.getUid("resizeMapWidthContainer");
	this.resizeMapWidthUid = SIX.Uid.getUid("resizeMapWidth");
	this.nTitleHeight = 19;
	this.attachObject = new Array();
	this.attachFixedObject = new Array();
	this.overflow		= "hidden";
	this.backgroundColor = "";
	this.transparencySliderUid = SIX.Uid.getUid("transparencySlider");
	this.transparencyPtrUid = SIX.Uid.getUid("transparencyPtr");
	this.transparencyTargetUid = this.parameters.getParameter("transparencytargetuid", "string", "");
	this.bTransparent = false;
	this.sToggleTransparencyToolTip = "Double-click to toggle background transparency";

	this.fnOnShowCallback = null;
	this.fnOnHideCallback = null;
	
	SIX.App.addMouseAction(this.moveUid,this,SIX.eWindowMouseActionTypes.MOVE,true);
	SIX.App.addMouseAction(this.resizeUid,this,SIX.eWindowMouseActionTypes.RESIZE,true);
	SIX.App.addMouseAction(this.minUid,this,SIX.eWindowMouseActionTypes.MIN,false);
	SIX.App.addMouseAction(this.closeUid,this,SIX.eWindowMouseActionTypes.TOGGLE,false);
	SIX.App.addMouseAction(this.expandCollapseUid,this,SIX.eWindowMouseActionTypes.EXPANDCOLLAPSE,false);

	this.isResizable = false;
	this.bDefaultSize = false;
	
	/**
	*	change starting height - toobar (below), .menuFrameStyle (css), SIX.Toolbar_AddTool function pane height
	*/
	
	if( type == "toolbar" ) {
		this.isResizable = false;
		this.overflow = "visible";
		this.showTitle = false;
		this.top = 580;
		this.left = 0;
		this.width = SIX.App.width-SIX.App.rightMargin;
		this.height = SIX.App.toolbarHeight;
		this.nTitleHeight = 0;
	}
	else if( type == "status" ) {
		this.top = 580;
		this.left  = 0;
		this.width = SIX.App.width-SIX.App.rightMargin;
		this.height = SIX.App.statusHeight;
		this.showTitle = false;
		this.nTitleHeight = 0;
	}
	else if( type == "legend" ) {
		this.top = 580;
		this.left = 0;
		this.width = SIX.App.width - SIX.App.paneWidth;
		this.height = SIX.App.propertiesHeight;
		this.framed = false;
		this.isResizable = true;
		this.isMin = true;
		this.overflow = "auto";
	}		
	else if( type == "query" ) {
		this.top = 580;
		this.left = 0;
		this.width = SIX.App.width - SIX.App.paneWidth;
		this.height = SIX.App.propertiesHeight;
		this.framed = false;
		this.isResizable = true;
		this.isMin = true;
	}		
	else if( type == "pane" ) 
	{
		this.isResizable = true;
		for (var i=0; i<SIX.App.channels.length; i++)
		{	
			if (!this.channel || (this.channel == SIX.App.channels[i]))
			{
				if (!SIX.App.channels[i].panes.length)
					this.isResizable = false;//Don't allow top pane to resize.
				SIX.App.channels[i].panes[SIX.App.channels[i].panes.length] = this;
			}
		}
		this.top = SIX.App.paneTop + (SIX.App.paneHeight * ((this.channel ? this.channel : SIX.App.getCurrentChannel()).panes.length-1));
		this.left = SIX.App.width - SIX.App.paneWidth;
		this.width = SIX.App.paneWidth;
		this.height = SIX.App.paneHeight;	
		this.overflow = "hidden";
	}
	else if( type == "map" ) 
	{
		this.showTitle = false;	
		this.nTitleHeight = 0;	
		this.top = SIX.App.topMargin;
		this.left = SIX.App.leftMargin;
		this.width = 500;
		this.height = 400;	
	}
	else if (type == "console") {
		this.showTitle = false;
		this.top = 580;
		this.left = 0;
		this.width = SIX.App.width;
		this.height = SIX.App.nConsoleHeight;
		this.isMin = true;
		this.isVisible = false;
		this.nTitleHeight = 0;
	}	
	else if (type == "popup")
	{
		this.top = this.parameters.getParameter("top", "number", 0);
		this.left = this.parameters.getParameter("left", "number", 0);
		this.width = this.parameters.getParameter("width", "number", 300);
		this.height = this.parameters.getParameter("height", "number", 300);
		this.framed = false;
		this.isResizable = true;
	}		
	this.nContentsTop = this.nTitleHeight + this.nCustomBorderWidth;
	this.nContentsLeft = this.nCustomBorderWidth;
	this.nContentsBottom = this.nCustomBorderWidth;
	this.nContentsRight = this.nCustomBorderWidth;
}

SIX.Window.prototype.setStyle = function (sStyle)
{
	this.sStyle = sStyle;
}	

SIX.Window.prototype.initAttachedObjects = function (attachObjects, attachDiv) 
{
	for (var i=0; i<attachObjects.length; i++)
	{
		var attachObject = attachObjects[i];
		if (attachObject.init)
		{
			var divAttachObject = attachObject.init();
			if (divAttachObject)
				attachDiv.appendChild(divAttachObject);
		}
	}
}


SIX.Window.prototype.init = function () 
{
	//Channels can share windows, so ensure windows are not initialized more than once.
	if (this.bInitialized) 
		return null;
	
	var channel = SIX.App.getCurrentChannel();

	var bVisible = (this.isVisible && (!this.channel || (this.channel == channel)));
	
	this.bInitialized = true;		
	var sChannelStyle = ((this.channel && this.type == "pane") ? this.channel.sStyle : "");
	var sWindowStyle = ((this.sStyle != "") ? this.sStyle : sChannelStyle);
	var sClass = sWindowStyle + this.type  + "Window";
	
	//The following are used for calculating window border widths so window contents can be sized accordingly.	
	var sBorderCalculatorOuterDivStyle = "visibility:hidden;top:0px;left:0px;width:100px;height:100px;";
	var sBorderCalculatorInnerDivStyle = "position:absolute;top:0px;left:0px;padding:0px;margin:0px;border-width:0px;width:100%;height:100%;overflow:hidden;visibility:hidden;";
	
	var sBorderWidthCalculators = "";
	//Left border width calculation
	sBorderWidthCalculators += "<div class='" + sClass + "' id='" + this.borderLeftWidthCalculatorOuterDivUid + "' style='" + sBorderCalculatorOuterDivStyle + "border-right-width:0px;'>";
	sBorderWidthCalculators += "<div id='" + this.borderLeftWidthCalculatorInnerDivUid + "' style='" + sBorderCalculatorInnerDivStyle + "'>";
	sBorderWidthCalculators += "</div></div>";
	
	//Right border width calculation
	sBorderWidthCalculators += "<div class='" + sClass + "' id='" + this.borderRightWidthCalculatorOuterDivUid + "' style='" + sBorderCalculatorOuterDivStyle + "border-left-width:0px;'>";
	sBorderWidthCalculators += "<div id='" + this.borderRightWidthCalculatorInnerDivUid + "' style='" + sBorderCalculatorInnerDivStyle + "'>";
	sBorderWidthCalculators += "</div></div>";

	//Top border width calculation
	sBorderWidthCalculators += "<div class='" + sClass + "' id='" + this.borderTopWidthCalculatorOuterDivUid + "' style='" + sBorderCalculatorOuterDivStyle + "border-bottom-width:0px;'>";
	sBorderWidthCalculators += "<div id='" + this.borderTopWidthCalculatorInnerDivUid + "' style='" + sBorderCalculatorInnerDivStyle + "'>";
	sBorderWidthCalculators += "</div></div>";	
	
	//Bottom border width calculation
	sBorderWidthCalculators += "<div class='" + sClass + "' id='" + this.borderBottomWidthCalculatorOuterDivUid + "' style='" + sBorderCalculatorOuterDivStyle + "border-top-width:0px;'>";
	sBorderWidthCalculators += "<div id='" + this.borderBottomWidthCalculatorInnerDivUid + "' style='" + sBorderCalculatorInnerDivStyle + "'>";
	sBorderWidthCalculators += "</div></div>";		

	var elemDivBorderWidthCalculators = document.createElement("div");
	elemDivBorderWidthCalculators.innerHTML = sBorderWidthCalculators;
	document.body.appendChild(elemDivBorderWidthCalculators);

	this.nLeftBorderWidth = this.getLeftBorderWidth();
	this.nRightBorderWidth = this.getRightBorderWidth();
	this.nTopBorderWidth = this.getTopBorderWidth();
	this.nBottomBorderWidth = this.getBottomBorderWidth();
	
	document.body.removeChild(elemDivBorderWidthCalculators);
	
	this.div = SIX.Util.createElement("div", sClass, this.uid);
	this.div.style.padding = this.nCustomBorderWidth + "px";
	this.div.style.visibility = bVisible ? "visible" : "hidden";
	this.div.style.display = bVisible ? "block" : "none";
	this.div.style.top = this.top + "px";
	this.div.style.left = this.left + "px";
	this.div.style.width = this.width + "px";
	this.div.style.height = this.height + "px";
	this.div.style.zIndex = this.zOrder;
	this.div.style.overflow = "hidden";
		
	if (this.type == "popup")
	{
		var elemPopupBackground = SIX.Util.createElement("div", "popupWindowBackground");
		this.div.appendChild(elemPopupBackground);
	}
	
	var nResizeButtonWidth = 10;
	
	if (this.type == "map")
	{
		//The NCSLayeredView throws exceptions when it is less than 2 pixels
		//from the left or top edges of the page.  The following logic ensures
		//this is never the case.
		var nContentsPageTopOffset = this.nContentsTop + this.top;
		var nContentsPageLeftOffset = this.nContentsLeft + this.left;
		if (nContentsPageTopOffset < 2)
			this.nContentsTop = 2 - this.top;
		if (nContentsPageLeftOffset < 2)
			this.nContentsLeft = 2 - this.left;	
		if (this.nContentsRight < nResizeButtonWidth)
			this.nContentsRight = nResizeButtonWidth;	
	}
	
	if (this.nCustomBorderWidth)
	{
		var elemBordersContainer = document.createElement("div");
		var sBordersHtml = "";
		//Need to generate 1 pixel high divs for inner and out borders.  
		if (this.nCustomBorder1Width && this.nCustomBorder2Width)
		{
			var nSmallestBorderWidth = (this.nCustomBorder1Width < this.nCustomBorder2Width) ? this.nCustomBorder1Width : this.nCustomBorder2Width;
			var nBorder1BlendDistance = Math.floor(0.5 * nSmallestBorderWidth);
			var nBorder2BlendDistance = nBorder1BlendDistance;

			var sBorderBoundaryColor = SIX.Util.interpolateHexColor(this.sCustomBorder1Color, this.sCustomBorder2Color, 50);			
			var aBorder1EdgeColors = SIX.Util.interpolateHexColors(this.sCustomBorder1Color, sBorderBoundaryColor, nBorder1BlendDistance + 2);
			var aBorder2EdgeColors = SIX.Util.interpolateHexColors(this.sCustomBorder2Color, sBorderBoundaryColor, nBorder2BlendDistance + 2);
			var aBorder1EdgeColorsReversed = SIX.Util.interpolateHexColors(sBorderBoundaryColor, this.sCustomBorder1Color, nBorder1BlendDistance + 2);
			var aBorder2EdgeColorsReversed = SIX.Util.interpolateHexColors(sBorderBoundaryColor, this.sCustomBorder2Color, nBorder2BlendDistance + 2);
			
			var aOffsets = new Array(); 
			aOffsets[0] = [["top", "left"], ["top", "right"]];
			aOffsets[1] = [["right", "top"], ["right", "bottom"]];
			aOffsets[2] = [["bottom", "right"], ["bottom", "left"]];
			aOffsets[3] = [["left", "bottom"], ["left", "top"]];
			//Generate top border first
			for (var i=0; i<4; i++)
			{
				var bColorsReversed = ((i == 1) || (i == 2));
				var bBlendStart = ((i == 1) || (i == 3));
				
				var sOuterBorderColor = bColorsReversed ? this.sCustomBorder2Color : this.sCustomBorder1Color;
				var nOuterBorderWidth = bColorsReversed ? this.nCustomBorder2Width : this.nCustomBorder1Width;
				var nOuterBorderBlendDistance = bColorsReversed ? nBorder2BlendDistance : nBorder1BlendDistance;		
				var nOuterBorderBlendStart = nOuterBorderWidth - nOuterBorderBlendDistance;			
				var aOuterBorderEdgeColors = bColorsReversed ? aBorder2EdgeColors : aBorder1EdgeColors;	
				
				var sInnerBorderColor = bColorsReversed ? this.sCustomBorder1Color : this.sCustomBorder2Color;
				var nInnerBorderWidth = bColorsReversed ? this.nCustomBorder1Width : this.nCustomBorder2Width;
				var nInnerBorderBlendDistance = bColorsReversed ? nBorder1BlendDistance : nBorder2BlendDistance;		
				var nInnerBorderBlendStart = nInnerBorderWidth - nInnerBorderBlendDistance;			
				var aInnerBorderEdgeColors = bColorsReversed ? aBorder1EdgeColorsReversed : aBorder2EdgeColorsReversed;
				
				var sWidth = ((i % 2) == 1) ? "1px" : "66%";
				var sHeight = ((i % 2) == 1) ? "66%" : "1px";
					
				for (var j=0; j<nInnerBorderWidth; j++)
				{
					var sColor = (j < nInnerBorderBlendDistance) ? aInnerBorderEdgeColors[j + 1] : sInnerBorderColor;	
					sBordersHtml += "<div style='" + aOffsets[i][0][0] + ":" + (j + nOuterBorderWidth) + "px;" + aOffsets[i][0][1] + ":" + (j + nOuterBorderWidth + nInnerBorderBlendDistance + 1) + "px;height:" + sHeight + ";width:" + sWidth + ";background-color:" + sColor + "' class='windowBorder'></div>";
					sBordersHtml += "<div style='" + aOffsets[i][1][0] + ":" + (j + nOuterBorderWidth) + "px;" + aOffsets[i][1][1] + ":" + (j + nOuterBorderWidth + nInnerBorderBlendDistance + 1) + "px;height:" + sHeight + ";width:" + sWidth + ";background-color:" + sColor + "' class='windowBorder'></div>";														
					
					var sCenterColor = SIX.Util.interpolateHexColor(sColor, sOuterBorderColor, 50);
					var sCornerBlendColor = (bBlendStart ? sCenterColor : sColor);
					sBordersHtml += "<div style='" + aOffsets[i][0][0] + ":" + (j + nOuterBorderWidth) + "px;" + aOffsets[i][0][1] + ":" + (j + nOuterBorderWidth) + "px;height:1px;width:1px;background-color:" + sCornerBlendColor + "' class='windowBorder'></div>";
							
					for (var k=0; k<nInnerBorderBlendDistance; k++)
					{
						var sCornerColor = SIX.Util.interpolateHexColor(sCenterColor, sColor, 100 * (k + 1)/(nInnerBorderBlendDistance + 1));
						sBordersHtml += "<div style='" + aOffsets[i][(bBlendStart ? 1 : 0)][0] + ":" + (j + nOuterBorderWidth) + "px;" + aOffsets[i][(bBlendStart ? 1 : 0)][1] + ":" + (j + nOuterBorderWidth + k + 1) + "px;height:1px;width:1px;background-color:" + sColor + "' class='windowBorder'></div>";	
						sBordersHtml += "<div style='" + aOffsets[i][(bBlendStart ? 0 : 1)][0] + ":" + (j + nOuterBorderWidth) + "px;" + aOffsets[i][(bBlendStart ? 0 : 1)][1] + ":" + (j + nOuterBorderWidth + k + 1) + "px;height:1px;width:1px;background-color:" + sCornerColor + "' class='windowBorder'></div>";							
					}	
				}
				
				for (var j=0; j<nOuterBorderWidth; j++)
				{
					var sColor = (j >= nOuterBorderBlendStart) ? aOuterBorderEdgeColors[j - nOuterBorderBlendStart + 1] : sOuterBorderColor;
					sBordersHtml += "<div style='" + aOffsets[i][0][0] + ":" + j + "px;" + aOffsets[i][0][1] + ":" + (j + nOuterBorderBlendDistance + 1) + "px;height:" + sHeight + ";width:" + sWidth + ";background-color:" + sColor + "' class='windowBorder'></div>";
					sBordersHtml += "<div style='" + aOffsets[i][1][0] + ":" + j + "px;" + aOffsets[i][1][1] + ":" + (j + nOuterBorderBlendDistance + 1) + "px;height:" + sHeight + ";width:" + sWidth + ";background-color:" + sColor + "' class='windowBorder'></div>";									
					
					var sCenterColor = SIX.Util.interpolateHexColor(sColor, sInnerBorderColor, 50);
					var sCornerBlendColor = (bBlendStart ? sCenterColor : sColor);
					sBordersHtml += "<div style='" + aOffsets[i][0][0] + ":" + j + "px;" + aOffsets[i][0][1] + ":" + j + "px;height:1px;width:1px;background-color:" + sCornerBlendColor + "' class='windowBorder'></div>";
									
					for (var k=0; k<nOuterBorderBlendDistance; k++)
					{
						var sCornerColor = SIX.Util.interpolateHexColor(sCenterColor, sColor, 100 * (k + 1)/(nOuterBorderBlendDistance + 1));
						sBordersHtml += "<div style='" + aOffsets[i][(bBlendStart ? 1 : 0)][0] + ":" + j + "px;" + aOffsets[i][(bBlendStart ? 1 : 0)][1] + ":" + (j + k + 1) + "px;height:1px;width:1px;background-color:" + sColor + "' class='windowBorder'></div>";	
						sBordersHtml += "<div style='" + aOffsets[i][(bBlendStart ? 0 : 1)][0] + ":" + j + "px;" + aOffsets[i][(bBlendStart ? 0 : 1)][1] + ":" + (j + k + 1) + "px;height:1px;width:1px;background-color:" + sCornerColor + "' class='windowBorder'></div>";	
					}	
				}	
			}
		}
		else
		{
			sBordersHtml += "<div style='top:0px;left:0px;height:" + this.nCustomBorderWidth + "px;background-color:" + this.sCustomBorderColor + "' class='windowBorder'></div>";//Top border
			sBordersHtml += "<div style='top:0px;left:0px;width:" + this.nCustomBorderWidth + "px;background-color:" + this.sCustomBorderColor + "' class='windowBorder'></div>";//Left border
			sBordersHtml += "<div style='bottom:0px;left:0px;height:" + this.nCustomBorderWidth + "px;background-color:" + this.sCustomBorderColor + "' class='windowBorder'></div>";//Bottom border
			sBordersHtml += "<div style='bottom:0px;right:0px;width:" + this.nCustomBorderWidth + "px;background-color:" + this.sCustomBorderColor + "' class='windowBorder'></div>";//Right border	
		}
		elemBordersContainer.innerHTML = sBordersHtml;
		this.div.appendChild(elemBordersContainer);
	}
	
	var elemTitleBar = document.createElement("div");
	var sTitleBarHtml = "";
	if (this.showTitle && this.nTitleHeight)
	{
		sTitleBarHtml += "<div class='windowBar'>";
		var sToolTip = "";
		var sOnDblClick = "";
		if (!this.framed)
		{
			if (!this.isMin) sToolTip = this.sToggleTransparencyToolTip;
			sOnDblClick = "ondblclick='" + this.myself + ".toggleTransparency();'";
		}	
		sTitleBarHtml += "<img " + (this.framed ? "" : "id='" + this.moveUid + "'") + " class='windowMoveBar'" +
		" src='" + SIX.App.imageUrl("images/" + (this.framed ? "windowBar" : "windowBarNoLeftBevel")) + "' width='100%' height='" + this.nTitleHeight + "' alt='" + sToolTip + "' title='" + sToolTip + "' " + sOnDblClick + ">";
	}

	if (this.framed) 
	{
		if (this.isResizable)
		{
			sTitleBarHtml += "<div class='windowPaneResize'>";
			sTitleBarHtml += "<img style='padding:0px;margin:0px;border:0px' id='" + this.resizeUid + "' border='0' width='100%' height='4px'" + SIX.App.imageSrc("images/transparent") + ">";
			sTitleBarHtml += "</div>";
		}	
	}	
	if (this.showTitle && this.nTitleHeight) 
	{
		if (!((this.type == "toolbar") && !this.framed))
			sTitleBarHtml += "<div class='windowTitle' id='" + this.titleUid + "'>" + this.title + "</div>";
		sTitleBarHtml += "</div>";
	}
	elemTitleBar.innerHTML = sTitleBarHtml;
	this.div.appendChild(elemTitleBar);
			
	var elemContentsContainer = SIX.Util.createElement("div", sWindowStyle + "windowContents", this.contentsUid);
	elemContentsContainer.style.position = "relative";
	elemContentsContainer.style.overflow = this.overflow;
	elemContentsContainer.style.width = (this.width - this.nContentsLeft - this.nContentsRight) + "px";
	elemContentsContainer.style.height = (this.height - this.nContentsTop - this.nContentsBottom) + "px";
		
		
	if (this.attachFixedObject.length > 0)
	{
		// Build the fixed part of the window
	
		this.divFixed = document.createElement("div");
		this.divFixed.style.position = "relative";
		this.divFixed.style.top = "0px";
		this.divFixed.style.left = "0px";
		this.divFixed.style.width = "100%";
		this.divFixed.style.height = this.fixedHeight;
		this.divFixed.style.paddingTop = "2px";
	
		this.initAttachedObjects(this.attachFixedObject, this.divFixed);
		elemContentsContainer.appendChild(this.divFixed);
	
		// Build the scrollable part of the window
		this.divScroll = document.createElement("div");
		this.divScroll.style.position = "absolute";
		this.divScroll.style.top = this.fixedHeight;
		this.divScroll.style.left = "0px";
		this.divScroll.style.height = "100%";
		this.divScroll.style.right = "0px";	
		this.divScroll.style.overflow = "auto";
		
		this.initAttachedObjects(this.attachObject, this.divScroll);
		elemContentsContainer.appendChild(this.divScroll);
	}
	else
	{
		this.initAttachedObjects(this.attachObject, elemContentsContainer);
	}
	
	this.div.appendChild(elemContentsContainer);
	
	if (this.type == "map")
	{
		var elemResizeWidth = SIX.Util.createElement("div", "resizeWidthOuter", this.resizeMapWidthContainerUid);
		
		elemResizeWidth.innerHTML = "<div class='resizeWidthInner'" + (SIX.App.isIE ? "" : " onmouseover='this.style.cursor = \"auto\";'") + ">" +
			"<img id='" + this.resizeMapWidthUid + "' " +  SIX.App.imageSrc("images/collapsePanes") + " title='Collapse panes' border='0'" +
			" onmouseover='this.src = (SIX.App.bMaxMapWidth ? SIX.App.imageUrl(\"images/expandPanes_on\") : SIX.App.imageUrl(\"images/collapsePanes_on\"))'" + 
			" onmouseout='this.src = (SIX.App.bMaxMapWidth ? SIX.App.imageUrl(\"images/expandPanes\") : SIX.App.imageUrl(\"images/collapsePanes\"))'" +
			" onclick='if (SIX.App.bMaxMapWidth) {SIX.App.showPanes();} else {SIX.App.hidePanes();}'" +
			">" +
			"</div>";
			
		this.div.appendChild(elemResizeWidth);

		var elemResizeHeight = SIX.Util.createElement("div", "resizeHeightOuter", this.resizeMapHeightContainerUid);
		if (SIX.App.isIE)
			elemResizeHeight.onmouseover = function () {this.style.cursor = "auto";};
		
		elemResizeHeight.innerHTML = "<div class='resizeHeightInner'>" + 
			"<img style='vertical-align:top' " +  SIX.App.imageSrc("images/maxMapHeight") + " title='Maximize map height' border='0'" +
			" onmouseover='this.src = (SIX.App.bMaxMapHeight ? SIX.App.imageUrl(\"images/minMapHeight_on\") : SIX.App.imageUrl(\"images/maxMapHeight_on\"))'" + 
			" onmouseout='this.src = (SIX.App.bMaxMapHeight ? SIX.App.imageUrl(\"images/minMapHeight\") : SIX.App.imageUrl(\"images/maxMapHeight\"))'" +
			" onclick='if (SIX.App.bMaxMapHeight) {this.src = SIX.App.imageUrl(\"images/maxMapHeight_on\"); this.title = \"Maximize map height\"; SIX.App.minMapHeight();} else {this.src = SIX.App.imageUrl(\"images/minMapHeight_on\"); this.title = \"\"; SIX.App.maxMapHeight();}'" +
			"></div>";
		this.div.appendChild(elemResizeHeight);			
	}

	if (this.bShowHideReset)
	{
		var elemShowHideReset = document.createElement("div");
		elemShowHideReset.innerHTML = "<a href='#'>" + 
				"<img id='" + this.showHideUid + "' class='windowShowHide' src='" + (this.bShowUnselectedLayers ? this.imageHide.src : this.imageShow.src) + "' alt='Show/hide unselected layers' title='Show/hide unselected layers'" +
				" onclick='" + this.myself + ".toggleUnselectedLayers();return false;' border='0'></a>" +
				"<a href='#'>" + 
				"<img id='" + this.resetUid + "' class='windowReset' src='" + this.imageReset.src+ "' alt='Reset layers' title='Reset layers'" +
				" onclick='" + this.myself + ".resetServices();return false;' border='0'></a>";
		this.div.appendChild(elemShowHideReset);
	}

	var sWindowControlsHtml = "";
	if( !this.framed) 
	{
		if (this.type == "popup")
			sWindowControlsHtml += "<img class='windowMin'" + SIX.App.imageSrc("images/close") + " onclick='" + this.myself + ".hideWin()'>";
		else
			sWindowControlsHtml += "<img id='" + this.expandCollapseUid + "' class='windowMin'" + SIX.App.imageSrc(this.isMin ? "images/expand" : "images/collapse")+ ">";

		if( this.isResizable )
		{
			sWindowControlsHtml += "<div id='" + this.resizeContainerUid + "' style='display:" + (this.isMin ? "none" : "block") + "'>";
			sWindowControlsHtml += "<img id='" + this.resizeUid + "' class='windowResize'" + SIX.App.imageSrc("images/" + (this.isMin ? "resize_off" : "resize")) + ">";
			sWindowControlsHtml += "</div>";
		}
	} 
	else
	{
		if (this.bCollapsible) 
		{
			sWindowControlsHtml += "<a href='#' alt=''>" + 
			"<img name='" + this.minUid + "' id='" + this.minUid + "' class='windowMin'" + SIX.App.imageSrc("images/collapse") + "border='0'>" +
			"</a>";
		}
	}	
	var elemWindowControls = document.createElement("div");
	elemWindowControls.innerHTML = sWindowControlsHtml;
	this.div.appendChild(elemWindowControls);
	
	return this.div;
}

SIX.Window.prototype.setTitle = function (title) {
	if (this.showTitle)
		document.getElementById(this.titleUid).innerHTML = title;
}

SIX.Window.prototype.toggleUnselectedLayers = function () 
{
	this.setUnselectedLayersVisibility(!this.bShowUnselectedLayers);
}

SIX.Window.prototype.setUnselectedLayersVisibility = function (bVisible) 
{
	var elemShowHide = document.getElementById(this.showHideUid);
	if (elemShowHide)
	{
		this.bShowUnselectedLayers = bVisible;
		elemShowHide.src = (this.bShowUnselectedLayers ? this.imageHide.src : this.imageShow.src);
		for (var i=0; i<this.attachObject.length; i++)
		{
			if ((typeof this.attachObject[i] == 'object') && 
				(this.attachObject[i].constructor == SIX.Service))	
			{
				this.attachObject[i].setUnselectedLayersVisibility(this.bShowUnselectedLayers)
			}
		}	
	}
}

SIX.Window.prototype.resetServices = function () 
{
	var bRebuild = ((this.bShowUnselectedLayers == this.bShowUnselectedLayersDefault) ? false : true);
	var bUpdateMaps = false;
	var elemShowHide = document.getElementById(this.showHideUid);
	if (elemShowHide)
	{
		this.bShowUnselectedLayers = this.bShowUnselectedLayersDefault;
		elemShowHide.src = (this.bShowUnselectedLayers ? this.imageHide.src : this.imageShow.src);
	}
	
	for (var i=0; i<this.attachObject.length; i++)
	{
		if ((typeof this.attachObject[i] == 'object') && 
			(this.attachObject[i].constructor == SIX.Service))	
		{	
			var service = this.attachObject[i];

			if (service.transparency != service.transparencyDefault)
			{
				bUpdateMaps = true;
				service.transparency = service.transparencyDefault;
			}
			
			for (var j=0; j<service.layers.length; j++) 
			{
				var layer = service.layers[j];
				if (layer.bSelected != layer.bSelectedDefault)
				{
					bUpdateMaps = true;
					layer.setSelected(layer.bSelectedDefault);
				}
				
				layer.bInList = this.bHideOutOfScaleLayers ? (layer.bInScale ? layer.bSelectedDefault : false) : layer.bSelectedDefault;
				layer.bHidden = !layer.bInList;
			}
			if (service.setUnselectedLayersVisibilityNoUpdate(this.bShowUnselectedLayers))
				bUpdateMaps = true;
			if (bRebuild || bUpdateMaps)
				service.build();
		}
	}
		
	if (bUpdateMaps)
	{
		//Update maps after window is rebuilt
		for (var i=0; i<this.attachObject.length; i++)
		{
			if ((typeof this.attachObject[i] == 'object') && 
				(this.attachObject[i].constructor == SIX.Service))	
			{	
				var service = this.attachObject[i];
				if (service.loaded && service.active)				
					for (var j=0; j<service.catalog.maps.length; j++)
						service.access.updateMap(service.catalog.maps[j]);				
			}
		}
	}
}

SIX.Window.prototype.buildAttachedObjects = function (attachedObjects, div)
{
	if (attachedObjects) 
	{
		for (var j=0; j<attachedObjects.length; j++)
		{
			if ((typeof attachedObjects[j] == 'object') && 
				(attachedObjects[j].constructor == SIX.Service) &&
				!attachedObjects[j].bFeatureServer)
			{
				attachedObjects[j].bShowUnselectedLayers = this.bShowUnselectedLayers;
				if (attachedObjects.length > 1)
					attachedObjects[j].bAllLayersSelectorVisible = true;
			}
		}
	
		for (var i=0; i<attachedObjects.length; i++)
		{
			if( attachedObjects[i].build )
			{
				var elemContents = attachedObjects[i].build();
				if (elemContents)
				{
					//Check node's parentNode attribute to see if the node has already been appended to the
					//window container via init() method.  If it hasn't, then append it.
					if (!elemContents.parentNode || elemContents.parentNode.nodeName == "#document-fragment")
					{
						div.appendChild(elemContents);
					}
				}			
			}
		}
	}
}

SIX.Window.prototype.build = function () 
{
	var elemContentsContainer = document.getElementById(this.contentsUid);

	if (this.attachFixedObject.length > 0)
	{
		this.buildAttachedObjects(this.attachFixedObject, this.divFixed);
		this.buildAttachedObjects(this.attachObject, this.divScroll);
	}
	else
	{
		this.buildAttachedObjects(this.attachObject, elemContentsContainer);
	}
}

SIX.Window.prototype.attach = function (item, bFixed) {
	var attachedObject = bFixed ? this.attachFixedObject : this.attachObject;
	var bFirstItem = attachedObject.length ? false : true;
	
	attachedObject[attachedObject.length] = item;	
	
	if (item instanceof SIX.Service)
	{
		if (item.nCurrentUI == eServiceUI.NONE)
			item.setUI(eServiceUI.LAYERS);
		item.bAutoSize = bFirstItem ? false : true;
	}
	
	if (!bFirstItem) //Other items already attached to this window.
	{
		this.overflow = "auto";
		var firstItem = attachedObject[0];
		if (firstItem instanceof SIX.Service)
			firstItem.bAutoSize = true;				
	}
	
	return item;
}

SIX.Window.prototype.action = function (action,moving,x,y) 
{
	var channelCurrent = SIX.App.getCurrentChannel();
	
	if (action != SIX.eWindowMouseActionTypes.NONE)
	{		
		if (SIX.App.nPreMouseActionPointerMode == -1)
		{
			var map = channelCurrent.maps[0];
			SIX.App.nPreMouseActionPointerMode = map.getPointerMode();
			map.setPointerMode(PM_POINTER);
		}
	}
	
	switch(action) 
	{
		case SIX.eWindowMouseActionTypes.MOVE:	
			if (!this.isMin)
			{
				//Hack for versions of IE prior to IE7 where select menus are windowed controls so sit on top of all other html elements.
				if (moving && channelCurrent.panes.length && SIX.App.isIE && !SIX.App.isIE7up)
				{
					var pane = channelCurrent.panes[0];
					if (pane.isVisible && !pane.isMin)
					{
						if (((this.y + y) < (pane.top + pane.height)) && (((this.x + x) + this.width) > pane.left))
						{
							SIX.App.mouseCurrentAction = 0;
							break;
						}
					}
				}					
			
				this.moveWin(moving,x,y);
			}
			break;
		case SIX.eWindowMouseActionTypes.RESIZE:	
			if (this.type == "pane")
			{
				channelCurrent.adjustPaneHeight(this,y-2);
			}			
			else if (!this.isMin)
			{
				//Hack for versions of IE prior to IE7 where select menus are windowed controls so sit on top of all other html elements.
				if (moving && channelCurrent.panes.length && SIX.App.isIE && !SIX.App.isIE7up)
				{
					var pane = channelCurrent.panes[0];
					if (pane.isVisible && !pane.isMin)
					{
						if (((y - this.height) < (pane.top + pane.height)) && (x > pane.left))
						{
							SIX.App.mouseCurrentAction = 0;
							break;
						}
					}
				}						
			
				this.resizeWin(moving,x,y);
			}
			break;
		case SIX.eWindowMouseActionTypes.MIN:	
			this.minWin();
			break;
		case SIX.eWindowMouseActionTypes.TOGGLE:	
			this.toggleWin();
			break;	
		case SIX.eWindowMouseActionTypes.EXPANDCOLLAPSE:	
			this.expandCollapseWin();
			break;
		case SIX.eWindowMouseActionTypes.NONE :						
		default :
			break;
	}
}

SIX.Window.prototype.moveWin = function (moving,x,y) {
	var element = document.getElementById(this.uid);
	if (moving)
	{
		this.moveTo(x + this.x, y + this.y);
	}
	else 
	{
		this.x = parseInt(SIX.Util.getStyle(element, "left")) - x;
		this.y = parseInt(SIX.Util.getStyle(element, "top"))  - y;
		this.raiseWin();
	}
}

SIX.Window.prototype.moveTo = function (x,y) {
	var element = document.getElementById(this.uid);
	this.left = x;
	this.top  = y;
	if (element)
	{
		element.style.left = x + "px";
		element.style.top = y + "px";
	}
}

SIX.Window.prototype.getLeftBorderWidth = function ()
{
	var nWidth = 0;
	var elemOuterDiv = document.getElementById(this.borderLeftWidthCalculatorOuterDivUid);
	var elemInnerDiv = document.getElementById(this.borderLeftWidthCalculatorInnerDivUid);	
		
	if (elemOuterDiv && elemInnerDiv)
		nWidth = elemOuterDiv.offsetWidth - elemInnerDiv.offsetWidth;
	return nWidth;	
}

SIX.Window.prototype.getRightBorderWidth = function ()
{
	var nWidth = 0;
	var elemOuterDiv = document.getElementById(this.borderRightWidthCalculatorOuterDivUid);
	var elemInnerDiv = document.getElementById(this.borderRightWidthCalculatorInnerDivUid);	
		
	if (elemOuterDiv && elemInnerDiv)
		nWidth = elemOuterDiv.offsetWidth - elemInnerDiv.offsetWidth;
	return nWidth;	
}

SIX.Window.prototype.getTopBorderWidth = function ()
{
	var nWidth = 0;
	var elemOuterDiv = document.getElementById(this.borderTopWidthCalculatorOuterDivUid);
	var elemInnerDiv = document.getElementById(this.borderTopWidthCalculatorInnerDivUid);	
		
	if (elemOuterDiv && elemInnerDiv)
		nWidth = elemOuterDiv.offsetHeight - elemInnerDiv.offsetHeight;
	return nWidth;	
}

SIX.Window.prototype.getBottomBorderWidth = function ()
{
	var nWidth = 0;
	var elemOuterDiv = document.getElementById(this.borderBottomWidthCalculatorOuterDivUid);
	var elemInnerDiv = document.getElementById(this.borderBottomWidthCalculatorInnerDivUid);	
		
	if (elemOuterDiv && elemInnerDiv)
		nWidth = elemOuterDiv.offsetHeight - elemInnerDiv.offsetHeight;
	return nWidth;	
}

SIX.Window.prototype.resizeWin = function (moving,x,y) 
{
	var element = document.getElementById(this.uid);
	if (moving && element) 
	{	
		var width  = x - this.left;
		var height = y - this.top;
		if( width >= this.MIN_WIDTH && height >= this.MIN_HEIGHT ) 
		{
			this.width = width;
			this.height = height;
			//Note: Under IE (Quirks mode), the width and height include padding etc, while 
			//under css standards compliant browsers, the width and height represent
			//the width and height of the content area only.			
			var contents = document.getElementById(this.contentsUid);
			if (contents)
			{	
				element.style.width = (width - this.nLeftBorderWidth - this.nRightBorderWidth - 2 * this.nCustomBorderWidth) + "px";
				element.style.height = (height - this.nTopBorderWidth - this.nBottomBorderWidth - 2 * this.nCustomBorderWidth) + "px";
						
				var nContentsWidth = width - this.nContentsLeft - this.nContentsRight - this.nLeftBorderWidth - this.nRightBorderWidth;
				if (nContentsWidth < 0) nContentsWidth = 0;
				var nContentsHeight = height - this.nContentsTop - this.nContentsBottom - this.nTopBorderWidth - this.nBottomBorderWidth;
				if (nContentsHeight < 0) nContentsHeight = 0;
				
				contents.style.width = nContentsWidth + "px";
				contents.style.height = nContentsHeight + "px";
			}
			else 
			{
				element.style.width  = (width - 2 * this.nCustomBorderWidth) + "px";
				element.style.height = (height - 2 * this.nCustomBorderWidth) + "px";	
			}
		}
	}
	else
	{
		this.raiseWin();
	}
}

SIX.Window.prototype.minWin = function () 
{
	if (this.type == "pane")
	{	
		var channelCurrent = SIX.App.getCurrentChannel();
		channelCurrent.bResetPanes = true;
	}
	if( this.isMin ) {
		document.getElementById(this.uid).style.clip = 'rect(auto auto auto auto)';
		this.raiseWin();
	}
	else
		document.getElementById(this.uid).style.clip = 'rect(auto auto 20px auto)';
	this.isMin = !this.isMin;			
	var elemMin = document.getElementById(this.minUid);
	var elemResize = document.getElementById(this.resizeUid);
	var elemResizeContainer = document.getElementById(this.resizeContainerUid);
	
	if (this.isMin)
	{
		if (elemMin)
			elemMin.src = SIX.App.imageUrl("images/expand");
			
		if (elemResize && (this.type != "pane"))
			elemResize.src = SIX.App.imageUrl("images/resize_off");
			
		if (elemResizeContainer && (this.type != "pane"))
			elemResizeContainer.style.display = "none";
	}
	else 
	{
		if (elemMin)
			elemMin.src = SIX.App.imageUrl("images/collapse");
		
		if (elemResize && (this.type != "pane"))
		{
			elemResize.src = SIX.App.imageUrl("images/resize");
		}
				
		if (elemResizeContainer && (this.type != "pane"))
		{
			elemResizeContainer.style.display = "block";
		}			
	}

	SIX.App.onWindowResize(null);
}

SIX.Window.prototype.expandCollapseWin = function () {
	this.isMin = !this.isMin;
	var elemWin = document.getElementById(this.uid);
	var elemMove = document.getElementById(this.moveUid);
	var elemExpand = document.getElementById(this.expandCollapseUid);
	var elemResize = document.getElementById(this.resizeUid);
	var elemResizeContainer = document.getElementById(this.resizeContainerUid);
	
	this.bDefaultSize = true;
	if (this.isMin)
	{
		if (elemMove) 
		{
			elemMove.alt = elemMove.title = "";
			elemMove.src = SIX.App.imageUrl("images/windowBarNoLeftBevel");
		}
			
		if (elemWin) 
			elemWin.style.backgroundColor = this.backgroundColor;
	
		if (elemExpand)
			elemExpand.src = SIX.App.imageUrl("images/expand");
			
		if (!this.framed)
		{	
			if (elemResize)
				elemResize.src = SIX.App.imageUrl("images/resize_off");
				
			if (elemResizeContainer)
				elemResizeContainer.style.display = "none";			
		}	
	}
	else 
	{
		if (elemMove) 
		{
			elemMove.alt = elemMove.title = this.sToggleTransparencyToolTip;
			elemMove.src = SIX.App.imageUrl("images/windowbar");
		}
	
		if (elemWin && this.bTransparent) 
			elemWin.style.backgroundColor = "transparent";
		
		if (elemExpand)
			elemExpand.src = SIX.App.imageUrl("images/collapse");
			
		if (!this.framed)
		{				
			if (elemResize && (this.type != "pane"))
				elemResize.src = SIX.App.imageUrl("images/resize");
				
			if (elemResizeContainer && (this.type != "pane"))
				elemResizeContainer.style.display = "block";		
		}		
	}	

	SIX.App.onWindowResize(null);
}

SIX.Window.prototype.expandWin = function () {
	this.isMin = false;	
	var elemExpand = document.getElementById(this.expandCollapseUid);
	var elemResize = document.getElementById(this.resizeUid);
	var elemResizeContainer = document.getElementById(this.resizeContainerUid);
	
	if (elemExpand)
		elemExpand.src = SIX.App.imageUrl("images/collapse");
		
	if (elemResize && (this.type != "pane"))
		elemResize.src = SIX.App.imageUrl("images/resize");
		
	if (elemResizeContainer && (this.type != "pane"))
		elemResizeContainer.style.display = "block";
}

SIX.Window.prototype.collapseWin = function () {
	this.isMin = true;
	var elemExpand = document.getElementById(this.expandCollapseUid);
	var elemResize = document.getElementById(this.resizeUid);
	var elemResizeContainer = document.getElementById(this.resizeContainerUid);
	
	if (elemExpand)
		elemExpand.src = SIX.App.imageUrl("images/expand");
		
	if (elemResize && (this.type != "pane"))
		elemResize.src = SIX.App.imageUrl("images/resize_off");
		
	if (elemResizeContainer && (this.type != "pane"))
		elemResizeContainer.style.display = "none";	
}

SIX.Window.prototype.toggleTransparency = function ()
{
	if (this.isMin) return;
	var element = document.getElementById(this.uid);
	if (element.style.backgroundColor == "transparent")
	{
		element.style.backgroundColor = this.backgroundColor;
		this.bTransparent = false;
	}
	else 
	{
		this.backgroundColor = element.style.backgroundColor;
		element.style.backgroundColor = "transparent";
		this.bTransparent = true;
	}
} 

SIX.Window.prototype.toggleWin = function () 
{
	var element = document.getElementById(this.uid);
	if (element)
	{
		if( this.isVisible ) 
		{
			element.style.visibility = "hidden";
			element.style.display = "none";
		}
		else 
		{
			element.style.visibility = "visible";
			element.style.display = "block";	
			this.raiseWin();
		}
	}
	this.isVisible = !this.isVisible;
	SIX.App.onWindowResize(null);
}

SIX.Window.prototype.hideContents = function () 
{
	var elem = document.getElementById(this.contentsUid);
	if (elem)
	{
		elem.style.visibility = "hidden";
		elem.style.display = "none";
	}
}

SIX.Window.prototype.showContents = function () 
{
	var elem = document.getElementById(this.contentsUid);
	if (elem)
	{	
		elem.style.visibility = "visible";
		elem.style.display = "block";
	}
}

SIX.Window.prototype.hideWin = function () 
{
	var elem = document.getElementById(this.uid);
	if (elem)
	{
		elem.style.visibility = "hidden";
		elem.style.display = "none";	
		
		this.isVisible = false;
		
		if (this.fnOnHideCallback)
		{
			this.fnOnHideCallback();
			this.fnOnHideCallback = null;
		}		
	}
}

SIX.Window.prototype.showWin = function () 
{
	var elem = document.getElementById(this.uid);
	if (elem)
	{
		this.isVisible = true;
		if (this.type == "pane" && SIX.App.bMaxMapWidth)
		{
			SIX.App.showPanes();
		}
		else
		{
			elem.style.visibility = "visible";
			elem.style.display = "block";
			this.raiseWin();
		}		
		
		if (this.fnOnShowCallback)
		{
			this.fnOnShowCallback();
			this.fnOnShowCallback = null;
		}		
	}
}

SIX.Window.prototype.raiseWin = function () {
	if( this.framed )
		return;
	if( this.type == "map" )
		return;
	var oldOrder = this.zOrder;
	var channel = SIX.App.getCurrentChannel();
	for(var i=0; i<channel.wins.length; i++) 
	{
		var win = channel.wins[i];
		var element = document.getElementById(win.uid);
		if (element)
		{
			if( win == this ) {
				this.zOrder = channel.wins.length;
				element.style.zIndex = win.zOrder;
			}
			else if( win.zOrder > oldOrder ) {
				win.zOrder -= 1;
				element.style.zIndex = win.zOrder;
			}
		}
	}
}

SIX.Window.prototype.setOnShowCallback = function (fnCallback) 
{
	this.fnOnShowCallback = fnCallback;
}

SIX.Window.prototype.clearOnShowCallback = function () 
{
	this.fnOnShowCallback = null;
}

SIX.Window.prototype.setOnHideCallback = function (fnCallback) 
{
	this.fnOnHideCallback = fnCallback;
}

SIX.Window.prototype.clearOnHideCallback = function () 
{
	this.fnOnHideCallback = null;
}

//	--------------------------------------------------------------------------- 
// SIX.Query Class
//	---------------------------------------------------------------------------
SIX.eQueryResultBackgroundColors = 
{
	MOUSEOUT : "transparent",
	MOUSEOVER : "#ffd700",/*gold*/
	SELECTED : "#316ac5"/*blue*/
}

SIX.eQueryTypes = {
	NONE				:	0,
	POINT				:	1,
	POINT_OR_RECTANGLE	:	2,
	POINT_LABEL			:	3,
	POLYGON				:	4,
	RECTANGLE			:	5,
	ZONES				:	6	
}

SIX.eQueryCallbacks = {
	ONRESPONSE : 1 
}

SIX.Query = function (queries, name, key, mapService, sMapServiceLayer, sParams)
{	
	SIX.addRef(this);
	this.queries = queries;
	this.name = name;
	this.key = key;
	this.fields = new Array();
	this.aOptionalFields = new Array();
	this.uid = SIX.Uid.getUid("query");
	this.mapService = mapService;
	this.sMapServiceLayer = sMapServiceLayer;

	this.bCanQuery = true;  
	this.nQueryOffset = 0;
	
	this.nFeatureIndex = 0;
	this.aOptionalFeatureDetails = new Object();//Associative array
	this.sOptionalFeatureDetailsRowId = null;
	this.sShowDetailLabel = "Show Detail";
	this.sHideDetailLabel = "Hide Detail";
	
	this.aFeatureIndexToRow = new Array();
	this.aFeatureIndices = new Object();
	this.firstResultRowId = null;
	
	this.parameters = new SIX.Parameters(sParams ? sParams : "");
	this.bAlwaysOn = this.parameters.getParameter("alwayson", "boolean", false);
	this.nQueryCount = this.parameters.getParameter("querycount", "number", 25);	
	this.bHighlightResults = this.parameters.getParameter("highlightresults", "boolean", false);
	this.sHighlightColor = this.parameters.getParameter("highlightcolor", "string", "#FF0000");	
	this.bLabelResults = this.parameters.getParameter("labelresults", "boolean", false);
	this.bNoDuplicates = this.parameters.getParameter("noduplicates", "boolean", false);
	this.bZoomToFirstFeature = this.parameters.getParameter("zoomtofirstfeature", "boolean", false);
	this.dMinZoomWidth = this.parameters.getParameter("minzoomwidth", "number", 0.015);
	this.dMinZoomHeight = this.parameters.getParameter("minzoomheight", "number", 0.015);		
	this.sHost = this.parameters.getParameter("host", "string", null);
	this.sService = this.parameters.getParameter("service", "string", null);
	this.sIdField = this.parameters.getParameter("idfield", "string", null);
	this.sMapServiceIdField = this.parameters.getParameter("mapserviceidfield", "string", null);
	this.sFieldDisplay = this.parameters.getParameter("fielddisplay", "string", "vertical");
	this.sFieldDisplay = this.sFieldDisplay.toLowerCase();
	this.bCommaDelimited = this.parameters.getParameter("commadelimited", "boolean", true);
	this.sDelimiter = this.parameters.getParameter("delimiter", "string", (this.bCommaDelimited ? "," : " "));
	this.nSpacesBeforeDelimiter = this.parameters.getParameter("spacesbeforedelimiter", "number", 0);
	this.nSpacesAfterDelimiter = this.parameters.getParameter("spacesafterdelimiter", "number", (this.bCommaDelimited ? 1 : 0));
	this.sSpacesBeforeDelimiter = SIX.Util.getSpaces(this.nSpacesBeforeDelimiter);
	this.sSpacesAfterDelimiter = SIX.Util.getSpaces(this.nSpacesAfterDelimiter);
	this.bDelimitEmptyFields = this.parameters.getParameter("delimitemptyfields", "boolean", false);
	this.sMoreInfoLabel = this.parameters.getParameter("moreinfolabel", "string", "More Information");
	this.sMoreInfoUrl = this.parameters.getParameter("moreinfourl", "string", null);
	this.fnGetMoreInfoUrl = null;
	//If sPolygonCollectionId is set, a polygon query region is constructed from the polygons associated with 
	//features returned from this query.  The polygon query region is then used in subsequent queries of type SIX.eQueryTypes.POLYGON.
	this.sPolygonCollectionId = this.parameters.getParameter("polygoncollectionid", "string", null);
	this.bQueryOutOfScale = this.parameters.getParameter("queryoutofscale", "boolean", this.queries.bQueryOutOfScale);
	this.bQueryUnselected = this.parameters.getParameter("queryunselected", "boolean", this.queries.bQueryUnselected);
	this.bGeometry = this.parameters.getParameter("geometry", "boolean", false);
	this.hCallbackManagers = new SIX.Hash();
}

SIX.Query.prototype.addCallback = function (nType, method, data)
{
	var callbackManager = this.hCallbackManagers.hasItem(nType) ?  this.hCallbackManagers.getItem(nType) : new SIX.CallbackManager();
	callbackManager.addCallback(new SIX.Callback(this, method, (arguments.length >= 3 ? data : null)));
	this.hCallbackManagers.setItem(nType, callbackManager);
}

SIX.Query.prototype.executeCallbacks = function (nType)
{
	if (this.hCallbackManagers.hasItem(nType))
	{
		var callbackManager = this.hCallbackManagers.getItem(nType);
		var aArguments = arguments.length > 1 ? Array.prototype.slice.call(arguments, 1, arguments.length) : new Array();
		callbackManager.executeCallbacks.apply(callbackManager, aArguments);
	}
}

SIX.Query.prototype.addField = function (name, key, aLookupTable, sParams) 
{
	var field = new Object();
	SIX.addRef(field);
	field.query = this;
	field.style = "query";
	field.uid = SIX.Uid.getUid("f");
	field.name = name;
	field.key = key;
	field.value = "";
	field.fnGetValue = null;
	field.parameters = new SIX.Parameters(sParams ? sParams : "");
	field.bShowLabel = field.parameters.getParameter("showlabel", "boolean", true);
	field.bShowTotal = field.parameters.getParameter("showtotal", "boolean", false);
	field.nColumnIndex = field.parameters.getParameter("columnindex", "number", (this.queries.bFieldNameColumn ? 2 : 1));
	field.bConvertTimeStampToDate = field.parameters.getParameter("converttimestamptodate", "boolean", false);
	field.sDateFormat = field.parameters.getParameter("dateformat", "string", "dd/mm/yyyy hh:mm:ss");
	
	field.sPrimaryLookupFieldKey = field.parameters.getParameter("primarylookupfieldkey", "string", null);
	field.bHidden = field.parameters.getParameter("hidden", "boolean", false);
	field.bRequest = field.parameters.getParameter("request", "boolean", true);
	field.bHideIfDuplicateFeatureId = field.parameters.getParameter("hideifduplicatefeatureid", "boolean", false);
	
	if (field.nColumnIndex < 1) field.nColumnIndex = 1;
	field.aLookupTable = (aLookupTable ? aLookupTable : null);
	var bOptional = field.parameters.getParameter("optional", "boolean", false);
	if (bOptional)
		this.aOptionalFields[this.aOptionalFields.length] = field;
	else
		this.fields[this.fields.length] = field;
	return field;
}

SIX.Query.prototype.showHideDetail = function (sFeatureId)
{
	if ((sFeatureId != "") && this.aOptionalFeatureDetails[sFeatureId])
	{
		var nodeQueryResultsTable = document.getElementById(this.queries.uid);
		var nodeTableBody = nodeQueryResultsTable.getElementsByTagName("tbody").item(0);
		var nodeRow = document.getElementById(this.sOptionalFeatureDetailsRowId);

		if (nodeRow && this.queries.aRowToFeatureMappings[this.sOptionalFeatureDetailsRowId])
		{
			var nSelectedResponseIndex = this.queries.aRowToFeatureMappings[this.sOptionalFeatureDetailsRowId][0];
			var nSelectedFeature = this.queries.aRowToFeatureMappings[this.sOptionalFeatureDetailsRowId][1];

			var cellNodes = nodeRow.getElementsByTagName("td");
			var elemQueryResult = ts_getNodeByAttribute(cellNodes.item(1), "name", "queryResult");
			
			if (elemQueryResult)
			{
				var elemOptionalDetail = ts_getNodeByAttribute(elemQueryResult, "name", "queryResultsWindowOptionalDetail");
				while (elemOptionalDetail)
				{
					elemQueryResult.removeChild(elemOptionalDetail);
					elemOptionalDetail = ts_getNodeByAttribute(elemQueryResult, "name", "queryResultsWindowOptionalDetail");
				}
				if (this.aOptionalFeatureDetails[sFeatureId].bDetailVisible)
				{	
					var aDetail = this.aOptionalFeatureDetails[sFeatureId].aDetail;
					for (var j=0; j<aDetail.length; j++)
					{
						var nodeDiv = SIX.Util.createElement("div", "queryResultsWindowText");
						nodeDiv.setAttribute("name", "queryResultsWindowOptionalDetail");
						elemQueryResult.appendChild(nodeDiv);
						
						var sNodeValue = (aDetail[j] == "") ? " " : aDetail[j];
						var textNodeCell = document.createTextNode(sNodeValue);
						
						nodeDiv.appendChild(textNodeCell);	
					}	
				}
			}
			
			var elemShowHideAnchor = ts_getNodeByAttribute(cellNodes.item(1), "name", "showHideAnchor");
			if (elemShowHideAnchor)
			{
				if (this.aOptionalFeatureDetails[sFeatureId].bDetailVisible)
					elemShowHideAnchor.innerHTML = this.sHideDetailLabel;
				else 
					elemShowHideAnchor.innerHTML = this.sShowDetailLabel;
			}				
		}
	}
}

SIX.Query.prototype.setMoreInfoUrl = function (sUrl)
{
	this.sMoreInfoUrl = sUrl;
}

SIX.Query.prototype.setMoreInfoLabel = function (sLabel)
{
	this.sMoreInfoLabel = sLabel;
}

SIX.Queries = function (service, name, aColumnHeadings, sParams)
{
	SIX.addRef(this);
	
	this.service = service;
	this.parameters = new SIX.Parameters(arguments.length < 5 ? "" : sParams);
	this.bExportColumn = this.parameters.getParameter("exportcolumn", "boolean", false);
	this.sExportFunction = this.parameters.getParameter("exportfunction", "string", null);
	this.sExportImage = this.parameters.getParameter("exportimage", "string", null);
	this.sExportDescription = this.parameters.getParameter("exportdescription", "string", null);
	this.sPrintFunction = this.parameters.getParameter("printfunction", "string", null);
	this.sPrintImage = this.parameters.getParameter("printimage", "string", null);
	this.sPrintDescription = this.parameters.getParameter("printdescription", "string", null);
	this.bFieldNameColumn = this.parameters.getParameter("fieldnamecolumn", "boolean", false);
	this.nNumResultsColumns = this.parameters.getParameter("numresultscolumns", "number", 1);
	this.sPolygonCollectionId = this.parameters.getParameter("polygoncollectionid", "string", null);
	this.sGetHeadersFunction = this.parameters.getParameter("getheadersfunction", "string", null);
	this.sHeaderClass = this.parameters.getParameter("headerclass", "string", "queriesHeader");
	this.sHeaderLabelClass = this.parameters.getParameter("headerlabelclass", "string", "queriesHeaderLabel");
	this.sResultClass = this.parameters.getParameter("resultclass", "string", "queryResult");
	this.bColumnHeadings = this.parameters.getParameter("columnheadings", "boolean", true);
	this.sAddResultFunction = this.parameters.getParameter("addresultfunction", "string", "addResult");
	this.bQueryOutOfScale = this.parameters.getParameter("queryoutofscale", "boolean", false);
	this.bQueryUnselected = this.parameters.getParameter("queryunselected", "boolean", false);
	
	this.nCurrentQueryType = SIX.eQueryTypes.NONE;
	this.aQueries = new Array();
	this.currentQuery = -1;
	this.uid = SIX.Uid.getUid("queries");
	this.resultsContainerUid = SIX.Uid.getUid("queryResultsContainer");
	this.nCurrentQueryZone = -1;
	this.zoneSelectUid = SIX.Uid.getUid("zoneSelect");
	this.name = name;
	this.sNoResults = "No results";
	this.sLoadingResults = "Loading results...";
	this.sNextSetOfResults = "Next...";
	this.sLastSetOfResults = "Previous...";		
	
	if (this.sPrintImage && (this.sPrintImage != ""))
	{
		this.imagePrintOff = new Image();
		this.imagePrintOff.src = SIX.App.imageUrl("images/" + this.sPrintImage);
		this.imagePrintOn = new Image();
		this.imagePrintOn.src = SIX.App.imageUrl("images/" + this.sPrintImage + "_on");	
	}

	if (this.sExportImage && (this.sExportImage != ""))
	{
		this.imageExportOff = new Image();
		this.imageExportOff.src = SIX.App.imageUrl("images/" + this.sExportImage);
		this.imageExportOn = new Image();
		this.imageExportOn.src = SIX.App.imageUrl("images/" + this.sExportImage + "_on");	
	}
	//One theme name column, 1 or more result columns and 0 or 1 export columns.
	this.nNumColumns = this.nNumResultsColumns + (this.bExportColumn ? 1 : 0) + (this.bFieldNameColumn ? 1 : 0) + 1;
	
	this.aColumnTotals = new Array();
	this.aColumnHeadings = new Array();
	this.aColumnHeadings[0] = "Type";
	this.aColumnHeadings[1] = "Details";
	if (this.sExportDescription)
		this.aColumnHeadings[2] = "Export";

	if (arguments.length >= 3 && aColumnHeadings)
	{
		var nNumHeadings = Math.min(aColumnHeadings.length, this.nNumColumns);
		for (var i=0; i<nNumHeadings; i++)
			this.aColumnHeadings[i] = aColumnHeadings[i];
	}
	//Associative array to obtain query and feature index from query results row id
	this.aRowToFeatureMappings = new Object();
}

SIX.Queries.prototype.setNoResultsMessage = function (sMessage)
{
	this.sNoResults = sMessage;			
}

SIX.Queries.prototype.setLoadingResultsMessage = function (sMessage)
{
	this.sLoadingResults = sMessage;			
}

SIX.Queries.prototype.setNextSetOfResultsMessage = function (sMessage)
{
	this.sNextSetOfResults = sMessage;				
}

SIX.Queries.prototype.setLastSetOfResultsMessage = function (sMessage)
{
	this.sLastSetOfResults = sMessage;					
}

SIX.Queries.prototype.changeCurrentQueryZone = function (nZoneIndex)
{
	var map = this.service.catalog.maps[0];
	this.nCurrentQueryZone = nZoneIndex;
	this.service.access.queryLayers(map, true);			
}

SIX.Queries.prototype.setSelectedQueryZone = function (nZoneIndex)
{
	this.nCurrentQueryZone = nZoneIndex;
	
	var elemZoneSelect = document.getElementById(this.zoneSelectUid);
	if (elemZoneSelect)
	{
		if (elemZoneSelect.options && (elemZoneSelect.options.length > this.nCurrentQueryZone))
		{
			for(var i=0; i<elemZoneSelect.options.length; i++)
			{
				if (this.nCurrentQueryZone == i)
				{
					elemZoneSelect.options[i].selected = true;
					break;
				}
			}
		}
	}
}

SIX.Queries.prototype.addQuery = function (name, key, mapService, sQueryLayer, sParams)
{
	var query = new SIX.Query(this, name, key, mapService, sQueryLayer, (sParams ? sParams : ""));
	this.aQueries[this.aQueries.length] =  query;
	return query;	
}

SIX.Queries.prototype.onSelectLastSetOfResults = function ()
{
	var catalog = this.service.catalog;
	var map = catalog.maps[0];
	var control = document[map.uid];

	for (var i=0; i<this.aQueries.length; i++)
	{
		var query = this.aQueries[i];
		if (query.nQueryOffset)
			query.nQueryOffset -= query.nQueryCount;
		else if ((this.nCurrentQueryZone > 0) && (i == 0))
		{
			this.setSelectedQueryZone(this.nCurrentQueryZone - 1);			
		}
	}	
	this.service.access.queryLayers(map, false);
}

SIX.Queries.prototype.onSelectNextSetOfResults = function ()
{
	var catalog = this.service.catalog;
	var map = catalog.maps[0];
	var control = document[map.uid];

	if (!this.bMoreResults && (this.nCurrentQueryZone >= 0) &&
			(this.nCurrentQueryZone < (map.aQueryZones.length - 1)))
	{
		this.setSelectedQueryZone(this.nCurrentQueryZone + 1);
	}
			
	for (var i=0; i<this.aQueries.length; i++)
	{
		var query = this.aQueries[i];
		if (this.service.access.bMoreResults)
			query.nQueryOffset += query.nQueryCount;
		else if ((this.nCurrentQueryZone >= 0) &&
				(this.nCurrentQueryZone < (map.aQueryZones.length - 1)))
			query.nQueryOffset = 0;				
	}
	this.service.access.queryLayers(map, false);
}

SIX.Queries.prototype.addNoResultsRow = function ()
{
	var queryResultsList = document.getElementById(this.uid);
	if (!queryResultsList)
		return;
	var nodeTableBody = queryResultsList.getElementsByTagName("tbody").item(0);

	var nodeRow = SIX.Util.createElement("tr", "nosort");
	nodeRow.style.backgroundColor = SIX.eQueryResultBackgroundColors.MOUSEOUT;

	var nodeCell = SIX.Util.createElement("td", "queryResult");
	nodeCell.setAttribute("colSpan", this.nNumColumns);
	
	if (!SIX.App.isIE)
	{
		var rowNodes = nodeTableBody.getElementsByTagName("tr");
		if (!rowNodes.length)
			nodeCell.setAttribute("width", "100%");
	}

	var nodeSpan = SIX.Util.createElement("span", "queryResultsWindowText");

	var textNodeCell = document.createTextNode(this.sNoResults);
	nodeSpan.appendChild(textNodeCell);
	nodeCell.appendChild(nodeSpan);		
	nodeRow.appendChild(nodeCell);
	if (!SIX.App.isIE) 
		this.addHiddenCell(nodeRow);	
	nodeTableBody.appendChild(nodeRow);
}

SIX.Queries.prototype.addLoadingResultsRow = function ()
{
	var queryResultsList = document.getElementById(this.uid);
	if (!queryResultsList)
		return;
	var nodeTableBody = queryResultsList.getElementsByTagName("tbody").item(0);

	var nodeRow = SIX.Util.createElement("tr", "nosort");
	nodeRow.style.backgroundColor = SIX.eQueryResultBackgroundColors.MOUSEOUT;

	var nodeCell = SIX.Util.createElement("td", "queryResult");
	nodeCell.setAttribute("colSpan", this.nNumColumns);

	if (!SIX.App.isIE)
	{
		var rowNodes = nodeTableBody.getElementsByTagName("tr");
		if (!rowNodes.length)
			nodeCell.setAttribute("width", "100%");
	}

	var nodeSpan = SIX.Util.createElement("span", "queryResultsWindowText");

	var textNodeCell = document.createTextNode(this.sLoadingResults);
	nodeSpan.appendChild(textNodeCell);
	nodeCell.appendChild(nodeSpan);		
	nodeRow.appendChild(nodeCell);	
	if (!SIX.App.isIE) 
		this.addHiddenCell(nodeRow);
	nodeTableBody.appendChild(nodeRow);
}

SIX.Queries.prototype.addLastSetOfResultsRow = function ()
{
	var queryResultsList = document.getElementById(this.uid);
	if (!queryResultsList)
		return;
	var nodeTableBody = queryResultsList.getElementsByTagName("tbody").item(0);

	var nodeRow = SIX.Util.createElement("tr", "nosort");
	nodeRow.style.backgroundColor = SIX.eQueryResultBackgroundColors.MOUSEOUT;

	var queries = this;

	nodeRow.onclick = function() 
						{					
							with (queries)
							{
								queries.onSelectLastSetOfResults();
							}
						}
	nodeRow.onmouseover = function()
							{
								with (queries)
								{
									queries.onMouseOverQueryResult(this);
								}
							}
	nodeRow.onmouseout = function()
							{
								with (queries)
								{
									queries.onMouseOutQueryResult(this);
								}
							}

	var nodeCell = SIX.Util.createElement("td", "queryResult");
	nodeCell.setAttribute("colSpan", this.nNumColumns);
	
	var nodeSpan = SIX.Util.createElement("span", "queryResultsWindowText");

	var textNodeCell = document.createTextNode(this.sLastSetOfResults);
	nodeSpan.appendChild(textNodeCell);
	nodeCell.appendChild(nodeSpan);		
	nodeRow.appendChild(nodeCell);	
	if (!SIX.App.isIE) 
		this.addHiddenCell(nodeRow);	
	nodeTableBody.appendChild(nodeRow);
}

SIX.Queries.prototype.addNextSetOfResultsRow = function ()
{
	var queryResultsList = document.getElementById(this.uid);
	if (!queryResultsList)
		return;
	var nodeTableBody = queryResultsList.getElementsByTagName("tbody").item(0);

	var nodeRow = SIX.Util.createElement("tr", "nosort");
	nodeRow.style.backgroundColor = SIX.eQueryResultBackgroundColors.MOUSEOUT;

	var queries = this;

	nodeRow.onclick = function() 
						{					
							with (queries)
							{
								queries.onSelectNextSetOfResults();
							}
						}
	nodeRow.onmouseover = function()
							{
								with (queries)
								{
									queries.onMouseOverQueryResult(this);
								}
							}
	nodeRow.onmouseout = function()
							{
								with (queries)
								{
									queries.onMouseOutQueryResult(this);
								}
							}

	var nodeCell = SIX.Util.createElement("td", "queryResult");
	nodeCell.setAttribute("colSpan", this.nNumColumns);

	var nodeSpan = SIX.Util.createElement("span", "queryResultsWindowText");
	
	var textNodeCell = document.createTextNode(this.sNextSetOfResults);
	nodeSpan.appendChild(textNodeCell);
	nodeCell.appendChild(nodeSpan);		
	nodeRow.appendChild(nodeCell);	
	if (!SIX.App.isIE) 
		this.addHiddenCell(nodeRow);	
	nodeTableBody.appendChild(nodeRow);
}

SIX.Queries.prototype.onMouseOverQueryResult = function (nodeRow)
{
	if (SIX.Util.toHex(nodeRow.style.backgroundColor) != SIX.eQueryResultBackgroundColors.SELECTED) 
		nodeRow.style.backgroundColor = SIX.eQueryResultBackgroundColors.MOUSEOVER;
	if (!SIX.App.isIE)
		nodeRow.style.cursor = "pointer";
}

SIX.Queries.prototype.onMouseOutQueryResult = function (nodeRow)
{
	if (SIX.Util.toHex(nodeRow.style.backgroundColor) != SIX.eQueryResultBackgroundColors.SELECTED) 
		nodeRow.style.backgroundColor = SIX.eQueryResultBackgroundColors.MOUSEOUT;
}

SIX.Queries.prototype.onSelectQueryResult = function (nodeRow)
{
	var nodeTableBody = nodeRow.parentNode;
	var rowNodes = nodeTableBody.getElementsByTagName("tr");
	for (var i=0;i<rowNodes.length;i++)
	{
		if (SIX.Util.toHex(rowNodes.item(i).style.backgroundColor) == SIX.eQueryResultBackgroundColors.SELECTED)
		{
			rowNodes.item(i).style.backgroundColor = SIX.eQueryResultBackgroundColors.MOUSEOUT;					
			break;
		}
	}			
	
	nodeRow.style.backgroundColor = SIX.eQueryResultBackgroundColors.SELECTED;
			
	if (this.aRowToFeatureMappings[nodeRow.id])
	{
		var nQueryIndex = this.aRowToFeatureMappings[nodeRow.id][0];
		var nFeatureIndex = this.aRowToFeatureMappings[nodeRow.id][1];
		//Clear take me to highlights.
		if (this.service.catalog.currentSearchService)
			this.service.catalog.currentSearchService.access.sSelectedResultId = "";
		
		this.service.access.selectQueryResult(nQueryIndex, nFeatureIndex);
	}
}

SIX.Queries.prototype.addResult = function (result, nResponseIndex, featureId, sMoreInfoUrl)
{
	var bGotResponseIndexAndFeatureId = (arguments.length >= 3);
	
	var bResultIsArray = false;
	if ((typeof result == 'object') && (result.constructor == Array))
	{
		bResultIsArray = true;
		if (!result.length)
			return;
	}
	
	var queryResultsList = document.getElementById(this.uid);
	if (!queryResultsList)
		return;
	var nodeTableBody = queryResultsList.getElementsByTagName("tbody").item(0);
	
	var sId = SIX.Uid.getUid("QueryResult");
	var nodeRow = this.createQueryResultRowNode(sId);
			
	var nodeToInsertBefore = null;						
	if (bGotResponseIndexAndFeatureId)
	{
		var queryCurrent = this.aQueries[nResponseIndex];
		
		if (!queryCurrent.firstResultRowId)
			queryCurrent.firstResultRowId = sId;
		this.aRowToFeatureMappings[sId] = [nResponseIndex, featureId];
		
		queryCurrent.aFeatureIndexToRow[featureId] = sId;
		if (nResponseIndex < (this.aQueries.length - 1))
		{
			for (var i=nResponseIndex+1; i<this.aQueries.length; i++)
			{
				var query = this.aQueries[i];
				if (query.firstResultRowId)
				{
					nodeToInsertBefore = document.getElementById(query.firstResultRowId);
					break;
				}
			}
		}
	}
	else
	{
		nodeRow.setAttribute("class", "nosort");
		nodeRow.setAttribute("className", "nosort");			
	}	
	
	if (nodeToInsertBefore)
		nodeTableBody.insertBefore(nodeRow, nodeToInsertBefore);
	else
		nodeTableBody.appendChild(nodeRow);	
	
	if (bResultIsArray == false)
	{
		var nodeCell = SIX.Util.createElement("td", this.sResultClass);
		nodeCell.setAttribute("colSpan", this.nNumColumns);
		
		var nodeSpan = SIX.Util.createElement("span", "queryResultsWindowText");
	
		var textNodeCell = document.createTextNode(result);
		nodeSpan.appendChild(textNodeCell);
		nodeCell.appendChild(nodeSpan);		
		nodeRow.appendChild(nodeCell);	  				
	}
	else
	{	
		for (var i=0; i<result.length; i++)
		{
			var nodeCell = SIX.Util.createElement("td", this.sResultClass);
		
			if (result.length == 1)
				nodeCell.style.textAlign = "left";

			var aResultParts = new Array();
			
			if ((typeof result[i] == 'object') && (result[i].constructor == Array))
				aResultParts = result[i];
			else
				aResultParts[0] = result[i];

			var nodeDivResult = SIX.Util.createElement("div", "queryResultsWindowText");
			nodeDivResult.setAttribute("name", "queryResult");
				
			for (var j=0; j<aResultParts.length; j++)
			{
				var nodeDiv = SIX.Util.createElement("div", "queryResultsWindowText");
				nodeDiv.setAttribute("name", "queryResultsWindowText");
				if (queryCurrent.sFieldDisplay == "horizontal")
					nodeDiv.style.display = "inline";
				nodeDivResult.appendChild(nodeDiv);
				
				var sNodeValue = (aResultParts[j] == "") ? " " : aResultParts[j];
				var textNodeCell = null;
				if ((this.nCurrentQueryZone >= 0) && (i == 0))
				{
					var sZoneLabel = " (Zone " + (this.nCurrentQueryZone + 1) + ")";
					textNodeCell = document.createTextNode(sNodeValue + sZoneLabel);
					var objQueryZone = this.service.catalog.maps[0].aQueryZones[this.nCurrentQueryZone];
					nodeDiv.style.color = objQueryZone.sColor;
					nodeDiv.style.fontWeight = "bold";
				}
				else
				{
					textNodeCell = document.createTextNode(sNodeValue);
				}
				
				nodeDiv.appendChild(textNodeCell);
			}
			nodeCell.appendChild(nodeDivResult);
		
			if (bGotResponseIndexAndFeatureId && queryCurrent.aOptionalFields.length && (i == 1)) //Add "Show detail" link to first result column
			{
				var nodeDiv = SIX.Util.createElement("div", "queryResultsWindowAnchor");
				var nodeAnchor = SIX.Util.createElement("a", "queryResultsWindowText");
				nodeAnchor.setAttribute("name", "showHideAnchor");
				nodeAnchor.setAttribute("href", "#");
				nodeAnchor.setAttribute("title", "Click for more detailed information");
				var textNodeAnchor = document.createTextNode(queryCurrent.sShowDetailLabel);
				nodeAnchor.appendChild(textNodeAnchor);
				var queries = this;
				
				nodeAnchor.onclick = function (event)
									{
										with (queries)
										{
											queries.service.access.showHideQueryResultDetail(this);
										}
										return false;
									}

				nodeDiv.appendChild(nodeAnchor);
				nodeCell.appendChild(nodeDiv);
			}			
	
			if (arguments.length < 4)
				var sMoreInfoUrl = queryCurrent.sMoreInfoUrl;
			else if (sMoreInfoUrl == null || sMoreInfoUrl == "")
				sMoreInfoUrl = queryCurrent.sMoreInfoUrl;

			if (sMoreInfoUrl && i == 1) //Add "More Information" link to first result column
			{
				var nodeDiv = SIX.Util.createElement("div", "queryResultsWindowAnchor");
				var nodeAnchor = SIX.Util.createElement("a", "queryResultsWindowText");
				nodeAnchor.setAttribute("name", "moreinfo");
				nodeAnchor.setAttribute("href", sMoreInfoUrl);
				nodeAnchor.setAttribute("title", sMoreInfoUrl);
				nodeAnchor.setAttribute("target", "_blank");
				var textNodeAnchor = document.createTextNode(queryCurrent.sMoreInfoLabel);
				nodeAnchor.appendChild(textNodeAnchor);
				nodeDiv.appendChild(nodeAnchor);
				nodeCell.appendChild(nodeDiv);	
			}
			
			nodeRow.appendChild(nodeCell);	  			
		}
	
		if (this.bExportColumn)
		{
			var nodeCell = SIX.Util.createElement("td", this.sResultClass);
			var nodeSpan = SIX.Util.createElement("span", "queryResultsWindowText");
			
			var nodeCheckBox = document.createElement("input");
			nodeCheckBox.setAttribute("type", "checkbox");
			nodeCheckBox.defaultChecked = nodeCheckBox.checked = true;	
			var objAccess = this.service.access;
			nodeCheckBox.onclick = function (event)
											{
												with(objAccess)
												{
													event ? objAccess.onSelect(event) : objAccess.onSelect();
												}
											}
	
			nodeSpan.appendChild(nodeCheckBox);
			nodeCell.appendChild(nodeSpan);	
			nodeRow.appendChild(nodeCell);	  			
		}		
	}
	if (!SIX.App.isIE) 
		this.addHiddenCell(nodeRow);
}

SIX.Queries.prototype.addHiddenCell = function (nodeRow)
{
	/*Under Firefox, the other cells will only expand to the occupy the full table width if the following hidden cell is present.*/
	var nodeCell = document.createElement("td");
	nodeCell.style.visibility = "hidden";
	var textNodeCell = document.createTextNode("&nbsp;");
	nodeCell.appendChild(textNodeCell);
	nodeRow.appendChild(nodeCell);
}	

SIX.Queries.prototype.createRowNode = function ()
{
	var nodeRow = document.createElement("tr");
	nodeRow.style.backgroundColor = SIX.eQueryResultBackgroundColors.MOUSEOUT;	
	return nodeRow;
}

SIX.Queries.prototype.createQueryResultRowNode = function (sId)
{
	var nodeRow = document.createElement("tr");
	nodeRow.setAttribute("id", sId);
	nodeRow.title = "Click to highlight feature on the Map";
	nodeRow.style.backgroundColor = SIX.eQueryResultBackgroundColors.MOUSEOUT;
	
	var queries = this;
	nodeRow.onclick = function() 
						{
							with (queries)
							{
								queries.onSelectQueryResult(this);
							}
						}
	nodeRow.onmouseover = function()
							{
								with (queries)
								{
									queries.onMouseOverQueryResult(this);
								}
							}
	nodeRow.onmouseout = function()
							{
								with (queries)
								{
									queries.onMouseOutQueryResult(this);
								}
							}		
	
	return nodeRow;
}

SIX.Queries.prototype.addHeaders = function ()
{
	if (this.sGetHeadersFunction)
	{
		var nodeTableQueryResults = document.getElementById(this.uid);
		if (nodeTableQueryResults)
		{
			var nodeTableBody = nodeTableQueryResults.getElementsByTagName("tbody").item(0);
			var nodeRowFirst = nodeTableBody.getElementsByTagName("tr").item(0);
			var aHeaders = (this[this.sGetHeadersFunction])();
			for (var i=0; i<aHeaders.length; i++)
			{
				var nodeRow = SIX.Util.createElement("tr", "nosort");
				if (nodeRowFirst)
					nodeTableBody.insertBefore(nodeRow, nodeRowFirst);
				else
					nodeTableBody.appendChild(nodeRow);
				
				var nodeCell = SIX.Util.createElement("td", this.sResultClass);
				nodeCell.setAttribute("colSpan", this.nNumColumns);
				
				var aHeader = aHeaders[i];
				var sHeaderLabel = aHeader[0];
				var sHeader = aHeader[1];

				var nodeDivContainer = SIX.Util.createElement("div", "queryResultContainer");
				
				var nodeDivLabel = SIX.Util.createElement("div", this.sHeaderLabelClass);

				var nodeDivHeader = SIX.Util.createElement("div", this.sHeaderClass);
	
				var textNodeLabel = document.createTextNode(sHeaderLabel);	
				nodeDivLabel.appendChild(textNodeLabel);
						
				if (SIX.App.isIE)
				{
					var textNodeHeader = document.createTextNode(sHeader);
					nodeDivHeader.appendChild(textNodeHeader);				
				}
				else
				{
					var nodePre = SIX.Util.createElement("pre", this.sHeaderClass);
					var textNodeHeader = document.createTextNode(sHeader);
					nodePre.appendChild(textNodeHeader);
					nodeDivHeader.appendChild(nodePre);
				}
				
				nodeDivContainer.appendChild(nodeDivLabel);
				nodeDivContainer.appendChild(nodeDivHeader);
				nodeCell.appendChild(nodeDivContainer);
				nodeRow.appendChild(nodeCell);
			}
		}
	}	
}

SIX.Queries.prototype.deleteResults = function ()
{
	var queryResultsList = document.getElementById(this.uid);
	if (!queryResultsList)
		return;
	
	for (var i=0; i<this.aQueries.length; i++)
		this.aQueries[i].firstResultRowId = null;

	var nodeTableBody = queryResultsList.getElementsByTagName("tbody").item(0);
	while (nodeTableBody.hasChildNodes()) 
	{
		if ((nodeTableBody.firstChild == nodeTableBody.lastChild) && this.bColumnHeadings)
		{
			var nodeFirstRow = nodeTableBody.firstChild;
			var inputs = nodeFirstRow.getElementsByTagName("input");
			for (var i=0; i<inputs.length; i++) 
			{
				if (inputs[i].type.toLowerCase() == 'checkbox') 
					inputs[i].defaultChecked = inputs[i].checked = true;//Check checkboxes in header row
			}				
			break;//Don't delete header row
		}
		nodeTableBody.removeChild(nodeTableBody.lastChild);
	}		
	
	for (var i=0; i<this.aQueries.length; i++)
	{
		var query = this.aQueries[i];
		if (query.bHighlightResults)
		{
			//Only delete previous highlights if we're displaying highlights for the query.
			for (var i in query.mapService.access.aHighlightedResults)
			{
				delete query.mapService.access.aHighlightedResults[i].aFieldValues;
				delete query.mapService.access.aHighlightedResults[i];
			}
				
			delete query.mapService.access.aHighlightedResults;
			query.mapService.access.aHighlightedResults = new Object();
		}
	}
	delete this.aRowToFeatureMappings;
	this.aRowToFeatureMappings = new Object();
}

//	--------------------------------------------------------------------------- 
// search Class
//	---------------------------------------------------------------------------

/**
*	MODIFICATIONS: 	bGroup - are we grouping the searches?
*					sToolbarKey - what toolbar value are we looking at when we change search options
*					sOptionDisplay - value of the toolbar item to be displayed when you can see this search in drop down
*/					
SIX.eSearchResultBackgroundColors = 
{
	MOUSEOUT : "transparent",
	MOUSEOVER : "#ffd700",/*gold*/
	SELECTED : "#316ac5"/*blue*/
}

/**
 * @class Services can have one or more search capabilities associated with that particular service. 
 * For example, a service might be able to search a map location based on street address, or on property folio number. 
 * An SIX.Search object is defined for each search for each service. Some services automatically build search capabilities 
 * based on information returned from the service during the service capabilities query. 
 * @constructor 
 * @param {String} name				Name of the search which appears in the drop-down menu of searches.
 * @param {String} key				Name of the layer on which search/query is being performed.
 * @param {SIX.Service} mapService		Map Service used to highlight features in search results.
 * @param {String} sMapServiceLayer Name of the Map Service layer used to highlight features in search results.
 * @param {String} sParams			String containing semicolon-delimited key/value pairs which define search parameters.<br>
 * Parameters that can be specified in sParams are as follows:<br><br>
 * - onresponse (String)		Name of access function name to call when the server responds.<br>
 * - onsubmit (String)			Name of access function name to call when search is submitted.<br>
 * - submitonload (Boolean)		If set to true, the search will initiate when the search is selected.<br>
 * - searchcount (Integer)		Specifies number of features to request.<br>	
 * - idfield (String)			Name of field which uniquely identifies a feature.<br>
 * - mapserviceidfield (String) Name of field which uniquely identifies the feature in the associated map service.<br>
 * - minzoomwidth (Float)		Minimum width in degrees to zoom into when a feature returned from a search is selected.<br>
 * - minzoomheight (Float)		Minimum height in degrees to zoom into when a feature returned from a search is selected.<br><br>
 * e.g. var search = new SIX.Search(service, "Lot/DP", "Lot_Label_1", svcHighlights, "Lot_1",
 * "idfield=delivsdm:geodb.lot.objectid;mapserviceidfield=delivsdm:geodb.lot.objectid;onresponse=searchResponse;onsubmit=searchLotDP;minzoomwidth=0.002;minzoomheight=0.002;");
 */
SIX.Search = function (service, name, key, mapService, sMapServiceLayer, sParams) 
{	
	SIX.addRef(this);
	this.div = null;
	this.service = service;
	this.name = name;
	this.key = arguments.length < 3 ? name : key;
	this.mapService = arguments.length < 4 ? null : mapService;
	this.sMapServiceLayer = arguments.length < 5 ? null : sMapServiceLayer;
	this.fields = new Array();
	this.aAllFields = new Array();//Only used for building search window.
	this.nNumHiddenFields = 0;
	this.uid = SIX.Uid.getUid("search");
	
	this.parameters = new SIX.Parameters((arguments.length < 6 ? "" : sParams));
	this.group = this.parameters.getParameter("group", "boolean", false);
	this.toolbarKey = this.parameters.getParameter("toolbarkey", "string", null);
	this.optionDisplay = this.parameters.getParameter("optiondisplay", "string", null);
	this.sResponseFunction = this.parameters.getParameter("onresponse", "string", null);
	this.sSubmitFunction = this.parameters.getParameter("onsubmit", "string", null);
	this.bSubmitOnFirstLoadOnly = this.parameters.getParameter("submitonfirstloadonly", "boolean", false);	
	this.bSubmitOnLoad = this.parameters.getParameter("submitonload", "boolean", this.bSubmitOnFirstLoadOnly);
	this.nSearchCount = this.parameters.getParameter("searchcount", "number", 25);			
	this.sIdField = this.parameters.getParameter("idfield", "string", null);
	this.sMapServiceIdField = this.parameters.getParameter("mapserviceidfield", "string", null);
	this.dMinZoomWidth = this.parameters.getParameter("minzoomwidth", "number", 0.015);
	this.dMinZoomHeight = this.parameters.getParameter("minzoomheight", "number", 0.015);	
	this.bDefault = this.parameters.getParameter("default", "boolean", false);
	this.bZoomToFirstFeature = this.parameters.getParameter("zoomtofirstfeature", "boolean", false);
	this.bZoomToFirstFeatureOnStartup = this.parameters.getParameter("zoomtofirstfeatureonstartup", "boolean", false);
	this.bResetHiddenFieldsAfterFirstLoad = this.parameters.getParameter("resethiddenfieldsafterfirstload", "boolean", false);
		
	if (this.bDefault)
	{
		this.service.currentSearch = this.service.searches.length;
		this.service.catalog.currentSearchService = this.service;
	}	
	
	this.bCanQuery = true;
	this.nSearchOffset = 0;
	this.bMoreResults = false;
	this.bGotResults = false;
	this.sNoResults = "No results";
	this.sLoadingResults = "Loading results...";
	this.sNextSetOfResults = "Next " + this.nSearchCount + "...";
	this.sLastSetOfResults = "Previous " + this.nSearchCount + "...";	
}

/**
 * Submits a search if the key pressed on a search field is the enter key.
 * @param {String} event Event.
 */
SIX.Search.prototype.submitOnEnter = function (event)
{
	var keycode;
	var evt = null;
	if (window.event) 
	{
		evt = window.event;
		keycode = evt.keyCode;
	}
	else if (event) 
	{
		evt = event;
		keycode = evt.which;
	}
	else 
	{
		return true;
	}

	if (keycode == 13)
	{
		var eventSource = (evt.target) ? evt.target : evt.srcElement;
		eventSource.form.submit();
		return false;
	}
	else
		return true;
}

SIX.Search.prototype.addHiddenField = function (name,key,value) {
	var field = new Object();
	SIX.addRef(field);
	field.style = "hidden";
	field.uid = SIX.Uid.getUid("f");
	field.name = name;
	field.sLabel = "";
	field.key = (arguments.length < 2 ? name : key);
	field.length = 0;
	field.value = (arguments.length < 3 ? "" : ("" + value));
	field.aEvents = null;
	this.nNumHiddenFields++;
	this.fields[this.fields.length] = field;
	this.aAllFields[this.aAllFields.length] = field;
	return field;
}

SIX.Search.prototype.addString = function (name,key,length,value,aEvents) {
	var field = new Object();
	SIX.addRef(field);
	field.style = "string";
	field.uid = SIX.Uid.getUid("f");
	field.name = name;
	field.sLabel = name;
	field.key = (arguments.length < 2 ? name : key);
	field.length = (arguments.length < 3 ? 20 : length);
	field.value = (arguments.length < 4 ? "" : value);
	field.aEvents = (arguments.length < 5 ? null: aEvents);
	this.fields[this.fields.length] = field;
	this.aAllFields[this.aAllFields.length] = field;
	return field;
}

SIX.Search.prototype.addNumber = function (name,key,value,aEvents) {
	var field = new Object();
	SIX.addRef(field);
	field.style = "number";
	field.uid = SIX.Uid.getUid("f");
	field.name = name;
	field.sLabel = name;
	field.key = (arguments.length < 2 ? name : key);
	field.length = 20;
	field.value = (arguments.length < 3 ? 0 : value);
	field.aEvents = (arguments.length < 4 ? null: aEvents);
	this.fields[this.fields.length] = field;
	this.aAllFields[this.aAllFields.length] = field;
	return field;
}

/**
* NB: choices can either be a single or two-dimensional array
*/

SIX.Search.prototype.addChoice = function (name,key,choices,value,nListSize,aEvents) {
	var field = new Object();
	SIX.addRef(field);
	field.style = "choice";
	field.uid = SIX.Uid.getUid("f");
	field.name = name;
	field.sLabel = name;
	field.key = (arguments.length < 2 ? name : key);
	field.length = 20;
	field.choices = (arguments.length < 3 ? ["Yes","No"] : choices);
	field.value = (arguments.length < 4 ? choices[0] : value);
	field.nListSize = (arguments.length < 5 ? 1 : nListSize);
	field.aEvents = (arguments.length < 6 ? null: aEvents);
	this.fields[this.fields.length] = field;
	this.aAllFields[this.aAllFields.length] = field;		
	return field;
}

SIX.Search.prototype.addResults = function (sName, aFieldKeys, aFieldHeadings, aFieldLookupTables, nListSize,aEvents) 
{
	var field = new Object();
	SIX.addRef(field);
	field.style = "results";
	field.uid = SIX.Uid.getUid("f");
	field.name = sName;
	field.sLabel = sName;
	field.aFieldKeys = aFieldKeys;
	field.aFieldHeadings = aFieldHeadings;
	field.aFieldLookupTables = (aFieldLookupTables ? aFieldLookupTables : null);
	field.length = 20;
	field.value = -1;
	field.nListSize = (arguments.length < 4 ? field.aFieldKeys.length : nListSize);
	field.aEvents = (arguments.length < 5 ? null: aEvents);
	this.resultsField = field; 
	this.aAllFields[this.aAllFields.length] = field;		
	return field;
}

SIX.Search.prototype.addTickbox = function (name,key,value,aEvents) {
	var field = new Object();
	SIX.addRef(field);
	field.style = "tickbox";
	field.uid = SIX.Uid.getUid("f");
	field.name = name;
	field.sLabel = name;
	field.key = (arguments.length < 2 ? name : key);
	field.length = 1;
	field.value = (arguments.length < 3 ? true : value);
	field.aEvents = (arguments.length < 4 ? null: aEvents);
	this.fields[this.fields.length] = field;
	this.aAllFields[this.aAllFields.length] = field;
	return field;
}

SIX.Search.prototype.addButton = function (name,key,value,aEvents) {
	var field = new Object();
	SIX.addRef(field);
	field.style = "button";
	field.uid = SIX.Uid.getUid("f");
	field.name = name;
	field.sLabel = "";
	field.key = (arguments.length < 2 ? name : key);
	field.length = 1;
	field.value = (arguments.length < 3 ? true : value);
	field.aEvents = (arguments.length < 4 ? null: aEvents);
	if (field.key == "submit") 
		this.submitField = field;
	else
		this.fields[this.fields.length] = field;
	this.aAllFields[this.aAllFields.length] = field;
	return field;
}

SIX.Search.prototype.init = function () 
{
	if (!this.div)
		this.div = SIX.Util.createElement("div", "searchFields");
	
	var txt = "<form name='" + this.uid + "' action='javascript:" + (this.sSubmitFunction ? (this.service.access.myself + "." + this.sSubmitFunction + "()") : (this.myself + ".doSearch()")) + ";'>";
	for(var i=0; i<this.aAllFields.length; i++ ) {
		var field = this.aAllFields[i];
		
		if (field.style == "hidden")
			continue;
		var sEvents = "";
		if (field.aEvents)
		{
			for (var j=0; j<field.aEvents.length; j++)
			{
				var sEventFunction = "";
				if (field.aEvents[j][1].indexOf(".") > -1) //Not an access function
					sEventFunction = field.aEvents[j][1];
				else
					sEventFunction = this.service.access.myself + "." + field.aEvents[j][1] + "()";
				sEvents += field.aEvents[j][0] + "='" + sEventFunction + ";' ";
			}
		}		
		
		txt += "<div class='row'>";
		if (field.sLabel != "results")
			txt += "<div class='label'>" + (field.sLabel != "" ? field.sLabel : "&nbsp;") + "</div>";

		switch(field.style) {
		default :
		case "string" :
			txt +=	"<input type='text' class='searchField' name='" + field.uid + "' id='" + field.uid + "'"
				+		" size='" + field.length + "' maxlength='" + field.length + "' value='" + field.value + "' " + sEvents + ">";
		break;
		case "number" :
			var value = (field.value ? field.value : "");
		 	txt += "<input class='searchField' name='" + field.uid + "' id='" + field.uid + "'"
				+ " size='" + field.length + "' maxlength='" + field.length + "' value='" + value + "' " + sEvents + ">";
		break;
		case "choice" :
		 	txt +=	"<select class='searchField' size='" + field.nListSize + "' name='" + field.uid + "' id='" + field.uid + "' " + sEvents + ">";
			
			if (field.choices.length > 1)
			{
				for( var j=0; j<field.choices.length; j++) 
				{
					//Check if we have value and text pairs of information
					//Under Firefox, when field.choices is a single dimensional string array,
					//field.choices[j][1] does not return null.  Instead, the second character
					//of field.choices[j] string is returned.  So, we can't use field.choices[j][1]
					//to determine whether the field.choices array is two dimensional.
					//A better way is to check the type of field.choices[j], and if the type is
					//"object", then we assume the field.choices array is two dimensional. 
					if (typeof(field.choices[j]) == "object")
					{
						txt += "<option value='" + field.choices[j][0] + "'";
						if (SIX.Util.compareStrings(field.choices[j][0], field.value))
						{
							txt += " selected";
						}
						txt += ">" + field.choices[j][1] + "</option>";
					}
					else	// single dimension array
					{	 
						txt += "<option value='" + field.choices[j] + "'";
						if (SIX.Util.compareStrings(field.choices[j],field.value))
						{
							txt += " selected";
						}
						txt += ">" + field.choices[j] + "</option>";
					}
				}
			}	
			else if (field.choices.length == 1)
			{
				txt += field.choices[0];
			}
				
			txt += "</select>";
		break;
		case "results":	
			txt += "<div class='searchResultsContainer'>"; 
			txt += "<table cellspacing='0' cellpadding='1' border='1'" +
				" class='sortable' style='" + (SIX.App.isIE ? "" : "width:auto;") + "background-color:#ffffff;filter:alpha (opacity=55);opacity:0.55;-moz-opacity:0.55;' name='" + field.uid + "' id='" + field.uid + "' " + sEvents + ">";
			txt += "<tr>";
			//Invisible column to map xml to results.
			txt += "<td style='" + (SIX.App.isIE ? "display:none;" : "visibility:hidden;") + "'>&nbsp;</td>"; 
											
			for( var j=0; j<field.aFieldHeadings.length; j++) 
			{
				var sPercentWidth = Math.floor(100 / field.aFieldHeadings.length) + "%";
				txt += "<td " + (SIX.App.isIE ? "" : ("style='width:" + sPercentWidth + "'")) + " title='Click to sort' class='resultsHeadingsField'>" + field.aFieldHeadings[j] + "</td>"; 
			}	
					
			txt += "</tr>";			
		
			//Blank row
			txt += "<tr>";
			txt += "<td style='" + (SIX.App.isIE ? "display:none;" : "visibility:hidden;") + "'>&nbsp;</td>"; 
			txt += "<td colspan='" + field.aFieldHeadings.length + "' class='searchResult'>&nbsp;</td>"; 							
			txt += "</tr>";			
			txt += "</table>";
			txt += "</div>";
		break;
		case "tickbox" :
		 	txt +=	"<input type='checkbox' class='searchField' name='" + field.uid + "' id='" + field.uid + "'"
				+		" value='" + field.value + "'" + (field.value ? "checked" : "") + " " + sEvents + ">";
		break;
		case "button" : 	
			txt += "<input type='button' class='searchButton' name='" + field.uid + "' id='" + field.uid + "'" 
				+ " value='" + field.value + "'" + " " + sEvents + ">";
		break;				
		}
		txt += "</div>";
	}
	
	txt += "</form><div id='searchCount' style='font-weight: bold;'></div>";
	this.div.innerHTML = txt;
	return this.div;
}

/**
*	PURPOSE: resets form - can't use normal reset button due to saving values during panel swap
*/
SIX.Search.prototype.resetForm = function ()
{
	var oForm = document.forms[this.uid]; 
	
	if (oForm)
	{	
		for (var i = 0; i < oForm.elements.length; i++)
		{
			if (oForm.elements[i].type == "text")
			{
				oForm.elements[i].value = "";
			}
			else if (oForm.elements[i].type == "select-one")
			{
				oForm.elements[i].selectedIndex = 0;
			}
		}
	}	

	for(var i=0; i<this.aAllFields.length; i++ ) 
	{
		var field = this.aAllFields[i];
		var id = document.getElementById(field.uid);
		if( id ) 
		{
			switch(field.style ) 
			{
				default:
				break;
				case "number" :
				case "string" :
					field.value = "";
					id.value = "";
				break;
			}
		}
	}	
}

SIX.Search.prototype.selectResult = function (nIndex) 
{
	var nRowIndex = nIndex + 1;//Results start at second row (after header row).
	var id = document.getElementById(this.resultsField.uid);
	var nodeTableBody = id.getElementsByTagName("tbody").item(0);
	var rowNodes = nodeTableBody.getElementsByTagName("tr");
	if ((nRowIndex >= 1) && (nRowIndex < rowNodes.length))
	{
		//Start at second row (after header row).
		for (var i=1; i<rowNodes.length; i++)
		{
			if (i == nRowIndex)
				rowNodes.item(i).style.backgroundColor = SIX.eSearchResultBackgroundColors.SELECTED;
			else 
				rowNodes.item(i).style.backgroundColor = SIX.eSearchResultBackgroundColors.MOUSEOUT;
		}	
	}	
	this.service.access.onSelectSearchResult();
}

SIX.Search.prototype.decodeForm = function () {
	for(var i=0; i<this.aAllFields.length; i++ ) {
		var field = this.aAllFields[i];
		var id = document.getElementById(field.uid);
		if( id ) {
			switch(field.style ) {
				default:
				break;
				case "string" :
					field.value = SIX.Util.trim(id.value);
				break;
				case "number" :
					field.value = parseFloat(id.value);
				break;
				case "choice" :
					if (id.options.length && (id.selectedIndex > -1))
						field.value = id.options[id.selectedIndex].text;
					else
						field.value = "";
				break;
				case "results" :		
					field.value = "";
					var nodeTableBody = id.getElementsByTagName("tbody").item(0);
					var rowNodes = nodeTableBody.getElementsByTagName("tr");
					for (var j=0;j<rowNodes.length;j++)
					{
						if (SIX.Util.toHex(rowNodes.item(j).style.backgroundColor) == SIX.eSearchResultBackgroundColors.SELECTED)
						{
							var cellNodes = rowNodes.item(j).getElementsByTagName("td");
							field.value = ts_getInnerText(cellNodes.item(0));						
							break;
						}
					}			
				break;
				case "tickbox" :
					field.value = id.value;
				break;
			}
		}
	}
}

SIX.Search.prototype.doSearch = function () {
	this.decodeForm();
	var catalog = this.service.catalog;	
	if (catalog.maps.length)	
		this.service.access.searchMap(catalog.maps[0],this);
}

SIX.Search.prototype.sortResults = function ()
{
	var resultsUid = this.resultsField.uid;
	var searchResultsList = document.getElementById(resultsUid);
	if (!searchResultsList)
		return;
	var nodeTableBody = searchResultsList.getElementsByTagName("tbody").item(0);
	var nodeRow = nodeTableBody.getElementsByTagName("tr")[0];//Header row.
	if (nodeRow)
	{
		var cellNodes = nodeRow.getElementsByTagName("td");
		for (var i=0; i<cellNodes.length; i++)
		{
			if (cellNodes[i].style.display != "none")
			{
				ts_resortTable(cellNodes[i], true);
				break;
			}
		}				
	}			
}

SIX.Search.prototype.addResult = function (result,featureId)
{
	var bResultIsArray = false;
	if ((typeof result == 'object') && (result.constructor == Array))
	{
		bResultIsArray = true;
		if (!result.length)
			return;
	}
	
	if (arguments.length >= 2) 
		this.bGotResults = true;
	
	var resultsUid = this.resultsField.uid;
	var searchResultsList = document.getElementById(resultsUid);
	if (!searchResultsList)
		return;
	var nodeTableBody = searchResultsList.getElementsByTagName("tbody").item(0);
	var nColSpan = this.resultsField.aFieldKeys.length;
	var nodeRow = document.createElement("tr"); 
	nodeRow.title = "Click to zoom to";
	nodeRow.style.backgroundColor = SIX.eSearchResultBackgroundColors.MOUSEOUT;
	nodeRow.onclick = function() 
						{
							var nodeTableBody = this.parentNode;
							var rowNodes = nodeTableBody.getElementsByTagName("tr");
							for (var j=0;j<rowNodes.length;j++)
							{
								if (SIX.Util.toHex(rowNodes.item(j).style.backgroundColor) == SIX.eSearchResultBackgroundColors.SELECTED)
								{
									rowNodes.item(j).style.backgroundColor = SIX.eSearchResultBackgroundColors.MOUSEOUT; 						
									break;
								}
							}			
							
							this.style.backgroundColor = SIX.eSearchResultBackgroundColors.SELECTED;
						}
	nodeRow.onmouseover = function()
							{
								if (SIX.Util.toHex(this.style.backgroundColor) != SIX.eSearchResultBackgroundColors.SELECTED) 
									this.style.backgroundColor = SIX.eSearchResultBackgroundColors.MOUSEOVER;
								if (!SIX.App.isIE)
									this.style.cursor = "pointer";
							}
	nodeRow.onmouseout = function()
							{
								if (SIX.Util.toHex(this.style.backgroundColor) != SIX.eSearchResultBackgroundColors.SELECTED) 
									this.style.backgroundColor = SIX.eSearchResultBackgroundColors.MOUSEOUT;
							}
	if (arguments.length < 2)
	{
		nodeRow.setAttribute("class", "nosort");
		nodeRow.setAttribute("className", "nosort");			
	}	

	//Last table cell is hidden. Its value corresponds to the node index in the search response xml
	var nodeIdCell = SIX.Util.createElement("td", "searchResult");
	if (SIX.App.isIE)
		nodeIdCell.style.display = "none";
	else 
		nodeIdCell.style.visibility = "hidden";

	var sCellValue = (arguments.length < 2) ? result : featureId;
	var textNodeCell = document.createTextNode(sCellValue);
	nodeIdCell.appendChild(textNodeCell);	
	nodeRow.appendChild(nodeIdCell);	
	
	if (bResultIsArray == false)
	{
		var nodeCell = SIX.Util.createElement("td", "searchResult");
		nodeCell.setAttribute("colSpan", nColSpan);
			
		var textNodeCell = document.createTextNode(result);
		nodeCell.appendChild(textNodeCell);
		nodeRow.appendChild(nodeCell);	  			
		nodeTableBody.appendChild(nodeRow);		
	}
	else
	{	
		for (var i=0; i<result.length; i++)
		{
			var nodeCell = document.createElement("td");
			nodeCell.setAttribute("class", "searchResult");
			nodeCell.setAttribute("className", "searchResult");
			if (result.length == 1)
				nodeCell.style.textAlign = "left";	
			var sNodeValue = (result[i] == "") ? " " : result[i];
			var textNodeCell = document.createTextNode(sNodeValue);
			nodeCell.appendChild(textNodeCell);
			nodeRow.appendChild(nodeCell);	  			
			nodeTableBody.appendChild(nodeRow);	
		}
	}
}

SIX.Search.prototype.deleteResults = function ()
{
	this.bGotResults = false;
	var resultsUid = this.resultsField.uid;
	var searchResultsList = document.getElementById(resultsUid);
	if (!searchResultsList)
		return;

	var nodeTableBody = searchResultsList.getElementsByTagName("tbody").item(0);
	while (nodeTableBody.hasChildNodes()) 
	{
		if (nodeTableBody.firstChild == nodeTableBody.lastChild)
			break;
		nodeTableBody.removeChild(nodeTableBody.lastChild);
	}		
}

SIX.Search.prototype.getFirstInputField = function ()
{
	var firstField = null;
	for (var i=0; i<this.fields.length; i++)
	{
		var field = this.fields[i];
		if ((field.style == "number") || (field.style == "string"))
		{
			firstField = field;
			break;
		}	
	}
	
	return firstField;
}

SIX.Search.prototype.setFocusOnFirstField = function () 
{
	//Set focus back to first search field.
	var firstField = this.getFirstInputField();
	if (firstField) 
	{
		var elemFirstField = document.getElementById(firstField.uid);
		if (elemFirstField)
		{
			elemFirstField.select();
			elemFirstField.focus();	
		}
	}
}

//	--------------------------------------------------------------------------- 
// xml	Class wrapper for XML management
//	---------------------------------------------------------------------------

SIX._XmlDomActiveX = null;
// used to find the Automation server name
SIX._GetXmlDomActiveX = function () 
{
	if (SIX._XmlDomActiveX)
		return SIX._XmlDomActiveX;
	var prefixes = ["MSXML3", "Microsoft", "MSXML2", "MSXML"];
	var o;
	for (var i = 0; i < prefixes.length; i++) {
		try {
			// try to create the objects
			var name = prefixes[i] + ".XmlDom";
			o = new ActiveXObject(name);
			SIX._XmlDomActiveX = name;
			return SIX._XmlDomActiveX;
		}
		catch (ex) {};
	}

	throw new Error("Could not find an installed XML parser");
}

SIX.Xml = function () {
	if(document.implementation && document.implementation.createDocument)
		this.rootXml = document.implementation.createDocument("", "", null);
	else if (window.ActiveXObject) {
		this.rootXml = new ActiveXObject(SIX._GetXmlDomActiveX());	// "Msxml.XmlDom" "Microsoft.XMLDOM"
		this.rootXml.validateOnParse = false;
		this.rootXml.resolveExternals = false;
		this.rootXml.async = false;
	}
	else {
		location.replace("upgrade_browser.htm");
		alert("This application requires a recent browser that can support<br>"
			+ "XML to run correctly.<br>"
			+ "Please upgrade your browser before using this application.");
	}
}

SIX.Xml.prototype.loadXmlText = function ( txt, strip ) 
{
	if( arguments.length < 2 )
		strip = true;
	this.rootXml.loadXML(txt);
	if( strip )
		this.stripWhiteSpace();
}

SIX.Xml.prototype.loadXmlUrl = function ( url, strip ) 
{
	if( arguments.length < 2 )
		strip = true;
	this.rootXml.load(url);
	if( strip )
		this.stripWhiteSpace();
}

SIX.Xml.prototype.stripWhiteSpace = function () 
{
	var notWhitespace = /\S/;
	var node = this.rootXml;
	for( var i=0; i<node.childNodes.length; i++ ) {
		if ((node.childNodes[i].nodeType == 3) && (!notWhitespace.test(node.childNodes[i].nodeValue))) {
			node.removeChild(node.childNodes[i]);
			i--;
		}
	}
}

// Create the loadXML method and xml getter for Mozilla/Netscape
if( !document.loadXML &&
	window.DOMParser &&
	window.XMLSerializer &&
	window.Node && Node.prototype && Node.prototype.__defineGetter__) {
	Document.prototype.loadXML = function (s) {
		var doc2 = (new DOMParser()).parseFromString(s, "text/xml");
		while(this.hasChildNodes())
			this.removeChild(this.lastChild);
		for (var i = 0; i < doc2.childNodes.length; i++) {
			if (doc2.childNodes[i].nodeType == 1)
			