// CrunchControl.js
//
// Copyright (c) 2006 Microsoft Corporation.  All rights reserved.
// Authors: John Douceur and Jeremy Elson
// Version: 3.3
// Date: 31 May 2006
//
// Revision History:
//
// 1.0 - 2006.05.16 (JD & JE): first public release
// 1.1 - 2006.05.18 (JD): support for XML files on remote HTTP servers
// 3.0 - 2006.05.23 (JD): ImportLayersFromAnchorHRef replaces ImportLayersFromCrunchFile
// 3.1 - 2006.05.24 (JD): added ImportLayersFromDivText
// 3.2 - 2006.05.25 (JD): use document.load() for files not accessed via HTTP
// 3.3 - 2006.05.31 (JD): added PopUpAlertsFromDivParagraphs
// 3.3 - 2007       (JH, JE): Added automatic legend support
// 4.0 - 2007.05.23 (JE): modified to support v4 of the VE API
// 5.1 - 2007.06.19 (JH): factored into objects

function _RegisterNamespaces()
{
	for (var i=0;i<arguments.length;i++)
	{
		var astrParts = arguments[i].split(".");
		var root = window;
		for (var j=0; j < astrParts.length; j++)
		{
			if (!root[astrParts[j]]) 
			{
					root[astrParts[j]] = new Object(); 
			}
			root = root[astrParts[j]];
		}
	}
}
_RegisterNamespaces('MSR.CVE');

MSR.CVE.maxMSXMLVersionToTry = 7;

MSR.CVE.CreateActiveXObject = function(objectName)
{
	if (window.ActiveXObject == undefined)
	{
		return null;
	}
	try
	{
		var newObject = new ActiveXObject(objectName);
		return newObject;
	}
	catch (dummy)
	{
		return null;
	}
}


MSR.CVE.CreateMSXMLObject = function(firstName, laterName)
{
	var newObject = null;
	for (var version = MSR.CVE.maxMSXMLVersionToTry; version > 2; version--)
	{
		newObject = MSR.CVE.CreateActiveXObject("MSXML2." + laterName + "." + version + ".0");
		if (newObject != null)
		{
//			alert("created " + laterName + " version " + version);
			return newObject;
		}
	}
	newObject = MSR.CVE.CreateActiveXObject("MSXML2." + laterName);
	if (newObject != null)
	{
		return newObject;
	}
	newObject = MSR.CVE.CreateActiveXObject("Microsoft." + firstName);
	return newObject;
}

MSR.CVE.ExecuteHTTPRequest = function(request, url)
{
	var httpReq = null;
	if (typeof XMLHttpRequest != "undefined")
	{
		httpReq = new XMLHttpRequest();
	}
	if (httpReq == null)
	{
		httpReq = MSR.CVE.CreateMSXMLObject("XMLHTTP", "XMLHTTP");
	}
	if (httpReq == null)
	{
		return null;
	}
	httpReq.open(request, url, false);
	httpReq.send(null);
	return httpReq;
}

MSR.CVE.CreateXMLDocument = function(xmlText)
{
	var xmlDocument = null;
	if (document.implementation != undefined
		&& document.implementation.createDocument != undefined)
	{
		xmlDocument = document.implementation.createDocument("", "", null);
	}
	if (xmlDocument == null)
	{
		xmlDocument = MSR.CVE.CreateMSXMLObject("XMLDOM", "DOMDocument");
	}
	if (xmlDocument == null)
	{
		return null;
	}

	if (xmlText == null)
	{
		return xmlDocument;
	}
	if (typeof xmlDocument.loadXML != "undefined")
	{
		xmlDocument.loadXML(xmlText);
		return xmlDocument;
	}
	if (typeof DOMParser != "undefined")
	{
		var domParser = new DOMParser();
		var stagingDoc = domParser.parseFromString(xmlText, "text/xml");
		for (var ixChild = 0; ixChild < stagingDoc.childNodes.length; ixChild++)
		{
			var subtree = xmlDocument.importNode(stagingDoc.childNodes[ixChild], true);
			xmlDocument.appendChild(subtree);
		}
		return xmlDocument;
	}
	return null;
}


MSR.CVE.xmlDOMFromText = function(xmlText)
{
	var xmlDocument = MSR.CVE.CreateXMLDocument(xmlText);
	if (xmlDocument == null)
	{
		alert("This application employs XML, which your browser does not support.");
		return null;
	}
	return xmlDocument;
}

MSR.CVE.xmlDOMFromURLViaHTTP = function(url)
{
	var request = MSR.CVE.ExecuteHTTPRequest("GET", url);
	if (request == null)
	{
		alert("This application requires an XMLHttpRequest object, which your browser does not provide.");
		return null;
	}
	if (request.status != 200)
	{
		alert("File " + url + " could not be retrieved.  Server returned status " + request.status + " (" + request.statusText + ")");
		return null;
	}
	return MSR.CVE.xmlDOMFromText(request.responseText);
}

MSR.CVE.xmlDOMFromURL = function(url)
{
	if (url.substring(0, 5) == "http:")
	{
		return MSR.CVE.xmlDOMFromURLViaHTTP(url);
	}
	else
	{
		var xmlDocument = MSR.CVE.CreateXMLDocument();
		xmlDocument.async = false;
		xmlDocument.load(url);
		return xmlDocument;
	}
}

MSR.CVE.getFirstSubTag = function(node, subTagName)
{
	var subTagList = node.getElementsByTagName(subTagName);
	if (subTagList == null)
	{
		return null;
	}
	if (subTagList.length < 1)
	{
		return null;
	}
	return subTagList[0];
}

MSR.CVE.getSubTagText = function(node, subTagName)
{
	var subTag = MSR.CVE.getFirstSubTag(node, subTagName);

	if (subTag == null)
	{
		return null;
	}
	else
	{
		return subTag.text;
	}
}

MSR.CVE.PopUpAlertsFromDivParagraphs = function(divId)
{
	var divElement = document.getElementById(divId);
	if (divElement == null)
	{
		alert("Div with id " + divId + " not found in document");
		return null;
	}
	for (var ixChild = 0; ixChild < divElement.childNodes.length; ixChild++)
	{
		var childNode = divElement.childNodes[ixChild];
		if (childNode.nodeType == 1 && childNode.nodeName.toLowerCase() == "p") // paragraph element
		{
			alert(childNode.innerText);
		}
	}
}

MSR.CVE.GetMapBounds = function(map)
{
	var mtop = 0;
	var mleft = 0;
	var centerPixel = map.LatLongToPixel(map.GetCenter());

	if (centerPixel == null)
	{
		return null;
	}

	var mbottom = 2*centerPixel.y;
	var mright = 2*centerPixel.x;

	// in 3D mode, this throws exceptions; just return null.
	try {
		var topLeft  = map.PixelToLatLong(new VEPixel(mleft, mtop));
		var botRight = map.PixelToLatLong(new VEPixel(mright, mbottom));
	}
	catch (dummy)
	{
		return null;
	}

	if (topLeft == null || botRight == null)
	{
		return null;
	}

	return new VELatLongRectangle(topLeft, botRight);
}

//
// Parse the URL and possibly call ActivateAlphaLayer, SetMapStyle
// and SetCenterAndZoom.  The URL should be of the format:
// YourPage.html?lat=12.34&lon=56.789&zoom=12&style=a&alpha=Some%20Layer&alpha=Another%20Layer
//
MSR.CVE.ApplyPermalink = function(map, form, layerList)
{
	var lat;
	var lon;
	var zoom;

	var argStartIndex = document.URL.indexOf('?');

	if (argStartIndex < 0)
	{
		return false;
	}

	// Clear all checkboxes in the form we were passed
	for (var i = 0; i < form.childNodes.length; i++)
	{
		if (form.childNodes[i].checked)
		{
			form.childNodes[i].checked = false;
		}
    }
	map.ClearAlphaLayers();

	// Parse the argument string
	var argString = unescape(document.URL.substring(argStartIndex+1));
	var argArray = argString.split("&");

	for (var i = 0; i < argArray.length; i++)
	{
		var arg = argArray[i];
		var attr;
		var value;

		if (arg.indexOf("=") >= 0)
		{
			var attrValueArray = arg.split("=");
			attr = attrValueArray[0];
			value = attrValueArray[1];
		}
		else
		{
			attr = arg;
			value = null;
		}

		switch (attr)
		{
			case "lat":
				if (value != null)
				{
					lat = parseFloat(value);
				}
				break;

			case "lon":
				if (value != null)
				{
					lon = parseFloat(value);
				}
				break;

			case "zoom":
				if (value != null)
				{
					zoom = parseInt(value);
				}
				break;

			case "style":
				if (value != null)
				{
					map.SetMapStyle(value);
				}
				break;

			case "alpha":
				if (value != null)
				{
					layerList.find(value).Activate(map);
					var checkBoxElement = document.getElementById("checkbox:" + value);

					if (checkBoxElement != null)
					{
						checkBoxElement.checked = true;
					}
				}
				break;
		}
	}

	if (lat != null && lon != null && zoom != null)
	{
		map.SetCenterAndZoom(new VELatLong(lat, lon), zoom);
		return true;
	}
	return false;
};

MSR.CVE.BoundsOverlap = function(bb0,bb1)
{
	var latIntersect =
		   (bb1.BottomRightLatLong.Latitude <= bb0.BottomRightLatLong.Latitude && bb0.BottomRightLatLong.Latitude <= bb1.TopLeftLatLong.Latitude)
		|| (bb1.BottomRightLatLong.Latitude <= bb0.TopLeftLatLong.Latitude && bb0.TopLeftLatLong.Latitude <= bb1.TopLeftLatLong.Latitude)
		|| (bb0.BottomRightLatLong.Latitude <= bb1.BottomRightLatLong.Latitude && bb1.BottomRightLatLong.Latitude <= bb0.TopLeftLatLong.Latitude)
		|| (bb0.BottomRightLatLong.Latitude <= bb1.TopLeftLatLong.Latitude && bb1.TopLeftLatLong.Latitude <= bb0.TopLeftLatLong.Latitude);
	var lonIntersect =
		   (bb1.TopLeftLatLong.Longitude <= bb0.TopLeftLatLong.Longitude && bb0.TopLeftLatLong.Longitude <= bb1.BottomRightLatLong.Longitude)
		|| (bb1.TopLeftLatLong.Longitude <= bb0.BottomRightLatLong.Longitude && bb0.BottomRightLatLong.Longitude <= bb1.BottomRightLatLong.Longitude)
		|| (bb0.TopLeftLatLong.Longitude <= bb1.TopLeftLatLong.Longitude && bb1.TopLeftLatLong.Longitude <= bb0.BottomRightLatLong.Longitude)
		|| (bb0.TopLeftLatLong.Longitude <= bb1.BottomRightLatLong.Longitude && bb1.BottomRightLatLong.Longitude <= bb0.BottomRightLatLong.Longitude);

	return latIntersect && lonIntersect;
}

MSR.CVE.ImportLayersFromAnchorHRef = function(anchorId, layerNamePrefix)
{
	var layerList = new MSR.CVE.LayerList();
	layerList.FromAnchorHRef(anchorId, layerNamePrefix);
	return layerList;
}

/* ////////////////////////////////////////////////////////////////////// */

MSR.CVE.LayerList = function()
{
	this.list = new Array();
	this.legendMap = new Array();	// quick reference by ID
	this.legendList = new Array();	// sorted in xml order
	this.nextID = 0;

    this.FromAnchorHRef = function(anchorId, layerNamePrefix)
    {
	    var anchorElement = document.getElementById(anchorId);
	    if (anchorElement == null)
	    {
		    alert("Anchor with id " + anchorId + " not found in document");
		    return null;
	    }
	    var xmlFileName = anchorElement.href;
	    var dom = MSR.CVE.xmlDOMFromURL(xmlFileName);
	    var tileRoot = "";

	    // Get the URL prefix where this XML file is located.  The tiles are
	    // rooted in the same place, unless the layer specifies a FilePath.
	    if (xmlFileName.lastIndexOf('/') > 0)
	    {
		    tileRoot = xmlFileName.substring(0, xmlFileName.lastIndexOf('/') + 1);
	    }

	    this.FromDom(dom, tileRoot, layerNamePrefix);
    }

	this.FromDom = function(dom, tileRoot, layerNamePrefix)
	{
		this.tileRoot = tileRoot;

		var layerNodes = dom.getElementsByTagName("Layer");
		
		for (var ixLayer = 0; ixLayer < layerNodes.length; ixLayer++)
		{
			var layerNode = layerNodes[ixLayer];
			try
			{
				var layer = new MSR.CVE.Layer();
				layer.FromDom(this, layerNode, layerNamePrefix);
				this.add(layer);
			}
			catch (e)
			{
				alert(e);
				throw(e);	// TODO
			}
		}
	}

	this.NextZIndex = function()
	{
		if (this.list.length==0)
		{
			// TODO ugly hardcoded constant, because we want each new
			// layer to be *above* the layer before.
			// (Can zIndices be negative?)
			return 1000;
		}
		else
		{
			return this.list[this.list.length-1].zIndex - 1;
		}
	}

	this._AllocateID = function()
	{
		var id = this.nextID;
		this.nextID += 1;
		return id;
	}

	// Internal callback used during construction.
	this._AddLegend = function(legend)
	{
		legend.ID = this._AllocateID();
		this.legendMap[legend.ID] = legend;
		this.legendList.push(legend);
	}

	this.add = function(crunchedLayer)
	{
		this.list.push(crunchedLayer);
	}

	this.find = function(layerName)
	{
		for (var ixLayer = 0; ixLayer < this.list.length; ixLayer++)
		{
			if (this.list[ixLayer].layerName == layerName)
			{
				return this.list[ixLayer];
			}
		}
		alert("No layer "+layerName);
		throw "No layer "+layerName;
	}

	this.FindLegend = function(legendID)
	{
		return this.legendMap[legendID];
	}

	// *
	// The code below supports automatic legends.
	// *

	this.legendMapControl = null;	// squirrel away for use by callbacks.
	this.currentLegendID = null;		// legend currently being displayed.

	this.StartAutomaticLegends = function(mapControl)
	{
		if (mapControl == null)
		{
			return;
		}

		if (this.legendMapControl != null)
		{
			return;
		}

		if (this.legendMap.length == 0)
		{
			return;
		}

		this.legendMapControl = mapControl;
		this.legendMapControl.AttachEvent("onendpan", this.MapPanned);
		this.legendMapControl.AttachEvent("onendzoom", this.MapPanned);

		// Squirrel away this legendList in a (gack) global, so that
		// javascript: action can find it.
		MSR.CVE._LayerListForLegends = this;
	}

	this.MapPanned = function(e)
	{
		MSR.CVE._LayerListForLegends.UpdateLegendList(e);
	}


	this.DisplayLegend = function(legendID)
	{
		// toggle behavior
		if (this.currentLegendID == legendID)
		{
			legendID = null;
		}
		var selectedLegendBoxDiv = document.getElementById("selectedLegendBox");
		var selectedLegendDiv = document.getElementById("selectedLegendBody");
		var closeBox = document.getElementById("closeLegendBox");

		this.currentLegendID = legendID;

		if (this.currentLegendID == null)
		{
			selectedLegendBoxDiv.style.visibility = "hidden";
			selectedLegendDiv.innerHTML = "";
			return;
		}

		var legend = this.FindLegend(this.currentLegendID);

		if (legend == null)
		{
			selectedLegendBoxDiv.style.visibility = "hidden";
			selectedLegendDiv.innerHTML = "";
			return;
		}

		selectedLegendBoxDiv.style.visibility = "visible";

		var legendTitleDiv = document.getElementById("selectedLegendTitle");
		legendTitleDiv.innerHTML = legend.DisplayName;

		// TODO scale popup max size to window size.
		var popupSize = new Object();
		popupSize.Width = 600;
		popupSize.Height = 400;
		if (legend.Width!=null)
		{
			popupSize.Width = Math.min(legend.Width+100, popupSize.Width);
			popupSize.Height = Math.min(legend.Height, popupSize.Height);
		}

		selectedLegendBoxDiv.style.width = popupSize.Width;
		selectedLegendBoxDiv.style.height =
			popupSize.Height +
			closeBox.offsetHeight +
			selectedLegendDiv.offsetLeft; // actually want border width.  close enough.

		if (legend.PopupType=="html")
		{
			selectedLegendDiv.innerHTML =
				"<p><iframe width=\""+popupSize.Width+"\" height=\""+popupSize.Height+"\" "
				+" src=\""+legend.URL
				+"\"><a href=\""
				+legend.URL
				+"\">(alt) Click here to get to legend</a></iframe>\n";
		}
		else
		{
			selectedLegendDiv.innerHTML =
				"<a href=\"" + legend.URL +"\">"
				+"<img width=\""+legend.Width+"px\""
				+" height=\""+legend.Height+"px\""
				+" src=\""+legend.URL
				+"\">"
				+"</a>\n";
		}
	}

	this.UpdateLegendList = function(e)
	{
		var legendDiv = document.getElementById("legendBody");
		var legendsFound = false;
		legendDiv.innerHTML = "<b>Legends:</b>";

		//var mapBounds = legendMapControl.GetMapView();
		var mapBounds = MSR.CVE.GetMapBounds(this.legendMapControl);

		for (ixLegend = 0; mapBounds != null && ixLegend < this.legendList.length; ixLegend++)
		{
			var legend = this.legendList[ixLegend];

			if (!MSR.CVE.BoundsOverlap(legend.Bounds, mapBounds))
			{	
				continue;
			}

			legendsFound = true;

			legendDiv.innerHTML += '<br><a href=' + "'" + 'javascript:MSR.CVE.DisplayLegend("'
				+ legend.ID
				+'");' + "'" + '>'
				+ legend.DisplayName
				+"</a>";
		}

		var legendBoxDiv = document.getElementById("legendBox");
		if (legendsFound == true)
		{
			legendBoxDiv.style.visibility = "visible";
		}
		else
		{
			legendBoxDiv.style.visibility = "hidden";
		}
	}
}

MSR.CVE._LayerListForLegends = null;
MSR.CVE.DisplayLegend = function(legendID)
{
	MSR.CVE._LayerListForLegends.DisplayLegend(legendID);
}

MSR.CVE.Layer = function()
{
	this.FromDom = function(layerList, layerNode, layerNamePrefix)
	{
		this.active = false;
		this.layerList = layerList;

		// Define the MapStyle that will later be passed to SetMapStyle or
		// ActivateAlphaLayer.
		this.layerName = layerNode.getAttribute("DisplayName");
		if (layerNamePrefix != null)
		{
			this.layerName = layerNamePrefix + layerName;
		}

		// Find naming scheme for the tiles
		var namingSchemeNode = MSR.CVE.getFirstSubTag(layerNode, "TileNamingScheme");
		var layerNamingScheme = null;
		if (namingSchemeNode != null)
		{
			layerNamingScheme = namingSchemeNode.getAttribute("Type");
		}
		if (layerNamingScheme == null)
		{
			throw("Layer " + layerName + ": no naming scheme specified");
		}

		// All naming schemes have these attributes.  Some schemes might have additional, special ones.
		var layerFilePath = namingSchemeNode.getAttribute("FilePath");
		if (layerFilePath == null || layerFilePath == "")
		{
			layerFilePath = layerList.tileRoot;
		}
		var layerFilePrefix = namingSchemeNode.getAttribute("FilePrefix");
		var layerFileSuffix = namingSchemeNode.getAttribute("FileSuffix");

		this.layerTilePath = "";
		// Create a GenerateFilename function based on the tile naming scheme.
		// The naming schemes define the relationship between a TileX/TileY/Zoom
		// and a tile URL.
		if (layerNamingScheme == "VE")
		{
			this.layerTilePath = layerFilePath + layerFilePrefix + "/%4" + layerFileSuffix;
			this.layerTilePath = this.layerTilePath.replace(/%20/g, " ");
		}
		else if (layerNamingScheme == "MC1")
		{
			throw("MC1 scheme not implemented yet");
		}
		else if (layerNamingScheme == "CGI")
		{
			throw("CGI scheme not implemented yet");
		}
		else
		{
			throw("XML specifies unknown tile naming scheme " + layerNamingScheme);
		}

		this.ID = layerList._AllocateID();

		this.veTileSourceSpecification =
			new VETileSourceSpecification(this.layerName, this.layerTilePath);

		this.veTileSourceSpecification.NumServers = 1;
		this.veTileSourceSpecification.ID = this.ID;

		// We set both 'zIndex' and 'ZIndex' since
		// there have been bugs in the MapControl that use one or the other
		// inconsistently.
		this.zIndex = layerList.NextZIndex();
		this.veTileSourceSpecification.zIndex = this.zIndex;
		this.veTileSourceSpecification.ZIndex = this.zIndex;

		// Get the DefaultView: a LatLonZoom that defines a useful view window
		// for this layer
		var defaultViewNode = MSR.CVE.getFirstSubTag(layerNode, "DefaultView");
		if (defaultViewNode != null)
		{
			var lat = defaultViewNode.getAttribute("lat");
			var lon = defaultViewNode.getAttribute("lon");
			var zoom = defaultViewNode.getAttribute("zoom");

			if (lat != null && lon != null && zoom != null)
			{
				var defaultView = new Object();

				var Lat = parseFloat(lat);
				var Lon = parseFloat(lon);
				defaultView.Zoom = parseInt(zoom);

				if (!isNaN(Lat) && !isNaN(Lon) && defaultView.Zoom > 0)
				{
					defaultView.veLatLong = new VELatLong(Lat, Lon);
					this.DefaultView = defaultView;
				}
				else
				{
					//alert("Ignoring invalid default view for layer " + layerName);
					this.DefaultView = null;
				}
			}
		}

		// Get the bounds, legend list, etc.
		this.sourceMapList = new MSR.CVE.SourceMapList();
		this.sourceMapList.FromDom(layerList, layerNode);
		this.veTileSourceSpecification.MinZoomLevel = 1;
		this.veTileSourceSpecification.MaxZoomLevel = this.sourceMapList.maxZoom;
	}

	this.Activate = function(mapControl, optionalOpacity)
	{
		if (optionalOpacity == null)
		{
			optionalOpacity = 1;
		}

		this.veTileSourceSpecification.Opacity = optionalOpacity;

		if (this.active)
		{
			// TODO not sure if this will actually update the display
			// to reflect an opacity change.
			return;
		}

		mapControl.AddTileLayer(this.veTileSourceSpecification, true);
	}

	this.Deactivate = function(mapControl)
	{
		mapControl.DeleteTileLayer(this.veTileSourceSpecification.ID);
	};

	// Given a name of a layer and a pointer to a MapControl,
	// set the view to be the "best view" for viewing that layer.
	this.SetDefaultView = function(mapControl)
	{
		if (this.DefaultView==null)
		{
			alert("No default view available for this layer.");
			return;
		}
		mapControl.SetCenterAndZoom(
			this.DefaultView.veLatLong, this.DefaultView.Zoom);
	}

}

MSR.CVE.SourceMapList = function()
{
	this.FromDom = function(layerList, layerNode)
	{
		this.Bounds = new Array();

		var sourceMapRecordListNode =
			MSR.CVE.getFirstSubTag(layerNode, "SourceMapRecordList");
		var sourceMapNodeList = sourceMapRecordListNode.getElementsByTagName("SourceMapRecord");
		this.maxZoom = 1;

		for (var ixSourceMap = 0; ixSourceMap < sourceMapNodeList.length; ixSourceMap++)
		{
			sourceMapInfo = new MSR.CVE.SourceMapInfo();
			sourceMapInfo.FromDom(layerList, this, sourceMapNodeList[ixSourceMap]);
			if (sourceMapInfo.maxZoom > this.maxZoom)
			{
				this.maxZoom = sourceMapInfo.maxZoom;
			}
		}
	}
}

MSR.CVE.SourceMapInfo = function()
{
	this.FromDom = function (layerList, sourceMapList, sourceMapNode)
	{
		this.displayName = sourceMapNode.getAttribute("DisplayName");
		this.maxZoom = parseInt(sourceMapNode.getAttribute("MaxZoom"));

		var mapRectNode = MSR.CVE.getFirstSubTag(sourceMapNode, "MapRectangle");
		if (mapRectNode != undefined)
		{
			var latLonList = mapRectNode.getElementsByTagName("LatLon");
			var lat0 = latLonList[0].getAttribute("lat");
			var lon0 = latLonList[0].getAttribute("lon");
			var lat1 = latLonList[1].getAttribute("lat");
			var lon1 = latLonList[1].getAttribute("lon");

			// note, we have to reverse the order of the lats -- MapCruncher emits maprectangles
			// as bottom-left, top-right; VELatLongRectangles use Top-Left, Bottom-Right
			var mapRect = new VELatLongRectangle(
				new VELatLong(parseFloat(lat1), parseFloat(lon0)),
				new VELatLong(parseFloat(lat0), parseFloat(lon1))
			);
			sourceMapList.Bounds.push(mapRect);

			// now create a legend record with these bounds
			var legendNode = MSR.CVE.getFirstSubTag(sourceMapNode, "SourceMapLegendFrame");
			if (legendNode != null)
			{
				var legend = new MSR.CVE.Legend();
				legend.FromDom(legendNode, this, mapRect);
				layerList._AddLegend(legend);
			}
		}
	}

	// Todo would be nice to add this function per SourceMap
	// this.SetDefaultView = function(mapControl)
}

MSR.CVE.Legend = function()
{
	this.FromDom = function(legendNode, sourceMapInfo, mapRect)
	{
		this.Bounds = mapRect;
		this.URL = legendNode.getAttribute("URL");
		this.DisplayName = sourceMapInfo.displayName;
		this.PopupType = "html";
		this.Width = parseInt(legendNode.getAttribute("Width"));
		this.Height =parseInt(legendNode.getAttribute("Height"));
	}
}

