/// utils
	function GetElem(idOrName) 
	{
		return document.getElementById(idOrName);
	}

	
	function IsComplete(elem) 
	{
		if (elem.readyState) {
			return elem.readyState == "complete";
		}
		else {
			return elem.complete;
		}
	}

  
  function LoadXMLDoc(url, obj, handlerName) 
  {
  	var req;

    var handlerRef = function() {
      if (req.readyState == 4) {
        if (req.status == 200) {
        	if (handlerName) {
	  		  	obj[handlerName](req.responseXML);
	  		  }
	  		  else {
	  		  	obj(req.responseXML);
	  		  }
        } 
        else {
				  document.write(req.responseText);
          alert("There was a problem retrieving the XML data:\n" + req.statusText); 
        }
      }
    }

    if (window.XMLHttpRequest) {
      req = new XMLHttpRequest();
      req.onreadystatechange = handlerRef;
      req.open("GET", url, true);
      req.send(null);
    } 
    else if (window.ActiveXObject) {
      req = new ActiveXObject("Microsoft.XMLHTTP");
      if (req) {
        req.onreadystatechange = handlerRef;
        req.open("GET", url, true);
        req.send();
      }
    }
  }


  function GetNodeText(node) 
  {
    var s = "";
    var childs = node.childNodes;
   	for (var i = 0; i < childs.length; i++) {
   		if (childs[i].nodeType == 3) {
   			s += childs[i].nodeValue;
   		}
   	}
   	return s;
  }


  function GetInt(value) 
  {
  	if (!value || value == "") {
	  	return 0;
	  }
	  else {
	  	return parseInt(value);
	  }	
  }

	function ShowElementProperties(elem) 
	{
		var s = "";
		for (var i in elem) {
			s += i + " ";
		}
		alert(s);
	}


/// Point
  function Point(x, y)
  {
  	this.X = x;
  	this.Y = y;
  }


  Point.prototype.Assign = function (x, y)
  {
  	this.X = x;
  	this.Y = y;
  }


///Rect
  function Rect(x1, y1, x2, y2)
  {
  	this.X1 = x1;
  	this.Y1 = y1;
  	this.X2 = x2;
  	this.Y2 = y2;
  }


  Rect.prototype.Assign = function (x1, y1, x2, y2)
  {
  	this.X1 = x1;
  	this.Y1 = y1;
  	this.X2 = x2;
  	this.Y2 = y2;
  }


  Rect.prototype.Dx = function ()
  {
  	return this.X2-this.X1;
  }


  Rect.prototype.Dy = function ()
  {
  	return this.Y2-this.Y1;
  }


  Rect.prototype.Offset = function (x, y)
  {
  	this.X1 += x;
  	this.Y1 += y;
  	this.X2 += x;
  	this.Y2 += y;
  }


  Rect.prototype.GetCenter = function ()
  {
  	return new Point((this.X1 + this.X2)/2, (this.Y1 + this.Y2)/2);
  }


/// Matrix
  function Matrix()
  {
  	this.Clear();
  }


  Matrix.prototype.Assign = function (a, b, c, d, e, f)
  {
  	this.A = a; this.B = b; this.C = c;
  	this.D = d; this.E = e; this.F = f;
  }


  Matrix.prototype.Clear = function ()
  {
  	this.Assign(1, 0, 0, 0, 1, 0);
  }


  Matrix.prototype.Mul = function (a, b, c, d, e, f)
  {
  	this.Assign(
  		this.A*a + this.D*b,
  		this.B*a + this.E*b,
  		this.C*a + this.F*b + c,
  		this.A*d + this.D*e,
  		this.B*d + this.E*e,
  		this.C*d + this.F*e + f);
  }


  Matrix.prototype.AddTranslation = function (x, y)
  {
  	this.Mul(1, 0, x, 0, 1, y);
  }


  Matrix.prototype.AddScaling = function (x, y)
  {
  	this.Mul(x, 0, 0, 0, y, 0);
  }


  Matrix.prototype.AddRotation = function (a)
  {
  	var sin_a = Math.sin(a);
  	var cos_a = Math.cos(a);
  	this.Mul(cos_a, -sin_a, 0, sin_a, cos_a, 0);
  }


  Matrix.prototype.AssignBackward = function (m)
  {
  	var d = m.A*m.E - m.B*m.D;
  	if(d == 0)
  		this.Clear();
  	else 
  		this.Assign(
  			m.E/d, -m.B/d, (m.B*m.F - m.C*m.E)/d,
  			-m.D/d, m.A/d, (m.C*m.D - m.A*m.F)/d);
  }


  Matrix.prototype.ProjectPoint = function (p)
  {
  	p.Assign(
  		this.A*p.X + this.B*p.Y + this.C,
  		this.D*p.X + this.E*p.Y + this.F);
  }


/// Layer
  function Layer(name, caption, image)
  {
  	this.Name = name;
  	this.Caption = caption;
  	this.Image = image;
  }


/// MapImage
  function MapImage(db)
  {
  	this.Width = 300;
  	this.Height = 300;
  	this.Dpi = 120;
  	this.CenterX = 0;
  	this.CenterY = 0;
  	this.YMirror = true;
  	this.XAngle = Math.PI/2.0;
  	this.ZoomScale = 1/10000;
  	this.Projection = new Matrix();
  	this.BackwardProjection = new Matrix();
  	this.PaperRect = new Rect();
  	this.SelectedObject = "";

 		this.Db = db;

 		this.DragMode = false;
 		this.DragStarted = false;
 		this.DragStartX = 0;
 		this.DragStartY = 0;
 		this.DragStartImageLeft = 0;
 		this.DragStartImageTop = 0;
	}


  MapImage.prototype.PaperToDeviceScale = function () 
  {
  	return this.Dpi/0.0254;
  }


  MapImage.prototype.SetPaperOffset = function (x, y)
  {
  	this.PaperRect.Offset(x - this.PaperRect.X1, y - this.PaperRect.Y1);
  }


  MapImage.prototype.PointPaperToDevice = function (p) 
  {
  	p.Assign(
  		(p.X - this.PaperRect.X1)*this.PaperToDeviceScale(),
  		this.Height - (p.Y - this.PaperRect.Y1)*this.PaperToDeviceScale());
  }


  MapImage.prototype.PointDeviceToPaper = function (p) 
  {
  	p.Assign(
  		p.X/this.PaperToDeviceScale() + this.PaperRect.X1,
  		(this.Height - p.Y)/this.PaperToDeviceScale() + this.PaperRect.Y1);
  }


  MapImage.prototype.PointWorldToDevice = function (p) 
  {
  	this.Projection.ProjectPoint(p);
  	this.PointPaperToDevice(p);
  }


  MapImage.prototype.PointDeviceToWorld = function (p) 
  {
  	this.PointDeviceToPaper(p);
  	this.BackwardProjection.ProjectPoint(p);
  }


  MapImage.prototype.ProjectionChanged = function () 
  {
  	this.PaperRect = new Rect(0, 0, this.Width/this.PaperToDeviceScale(), this.Height/this.PaperToDeviceScale());
  	this.Projection.Clear();
  	if(this.YMirror) {
  		this.Projection.AddScaling(1, -1);
  	}
  	this.Projection.AddScaling(this.ZoomScale, this.ZoomScale);
  	this.Projection.AddRotation(this.XAngle);
  	var paperCenter = new Point(this.CenterX, this.CenterY);
  	this.Projection.ProjectPoint(paperCenter);
  	this.SetPaperOffset(paperCenter.X - this.PaperRect.Dx()/2, paperCenter.Y - this.PaperRect.Dy()/2)	
  	this.BackwardProjection.AssignBackward(this.Projection);
  	var layerNames = "";

  	var visibleLayers = this.Db.LayerList.GetVisibleLayers();
  	for (var i = 0; i < visibleLayers.length; i++) {
 			if(layerNames != "") {
 				layerNames += ",";
 			}
 			layerNames += visibleLayers[i].Name;
  	}

  	if (this.ImgElement != null) {
  		this.ImagePlaceholder.style.width = this.Width;
  		this.ImagePlaceholder.style.height = this.Height;
    	this.ImgElement.src = 
    		this.Db.Config["page_image"] +  
    		"?Db=" + this.Db.Name + 
    		"&Layers=" + layerNames +
    		"&CenterX=" + this.CenterX + 
    		"&CenterY=" + this.CenterY +
    		"&Width=" + this.Width +
    		"&Height=" + this.Height +
    		"&XAngle=" + this.XAngle +
    		"&YMirror=" + this.YMirror +
    		"&ZoomScale=" + this.ZoomScale +
    		"&SelectedObject=" + this.SelectedObject;
    }

  }


  MapImage.prototype.Scroll = function (hor, ver) 
  {
  	var center = this.PaperRect.GetCenter();
  	center.X += this.PaperRect.Dx()/4*hor;
  	center.Y += this.PaperRect.Dy()/4*ver;
  	this.BackwardProjection.ProjectPoint(center);
  	this.CenterX = center.X;
  	this.CenterY = center.Y;
  	this.ProjectionChanged();
  }


  MapImage.prototype.ScrollDelta = function (dx, dy) 
  {
  	var center = new Point(this.Width/2, this.Height/2);
  	center.X -= dx;
  	center.Y += dy;
  	this.PointDeviceToPaper(center);
  	this.BackwardProjection.ProjectPoint(center);
  	this.CenterX = center.X;
  	this.CenterY = center.Y;
  	this.ProjectionChanged();
  }

  
  MapImage.prototype.Zoom = function (multiplier)
  {
  	this.ZoomScale *= multiplier;
  	this.ProjectionChanged();
  }

  
  MapImage.prototype.SelectedObject = function (selectedObject)
  {
  	this.SelectedObject = selectedObject;
  }

	
	MapImage.prototype.Navigate = function (x, y, objectId) 
	{
    this.CenterX = x;
    this.CenterY = y;
    this.SelectedObject = objectId;
    this.ProjectionChanged();
  }


	function ImgElement_OnDragStart(eventObject) 
	{
		var evt = window.event ? window.event : eventObject;

		evt.returnValue = false;
	}


	function ImgElement_OnMouseDown(eventObject) 
	{
		var evt = window.event ? window.event : eventObject;

		var mapImage = this["DataObject"];

		var x = evt.offsetX;
		var y = evt.offsetY;

 		mapImage.DragMode = true;

 		mapImage.DragStartX = evt.clientX;
 		mapImage.DragStartY = evt.clientY;

 		mapImage.DragStartImageLeft = GetInt(mapImage.ImgElement.style.left);
 		mapImage.DragStartImageTop = GetInt(mapImage.ImgElement.style.top);

 		mapImage.DragStarted = false;	

 		return false;
	}

	function GetOffsetX(evt, elem) 
	{
		if ("offsetX" in evt) {
			return evt.offsetX;
		}
		else {
			return evt.clientX - elem.offsetLeft + document.body.scrollLeft;

		}
	}

	function GetOffsetY(evt, elem) 
	{
		if ("offsetY" in evt) {
			return evt.offsetY;
		}
		else {
			return evt.clientY - elem.offsetTop + document.body.scrollTop;

		}
	}

	function ImgElement_EndDrag (mapImage, evt) 
	{
		if (mapImage.DragMode) {
	 		mapImage.DragMode = false;
			if (mapImage.DragStarted == true) {
				mapImage.DragStarted = false;
				var dx = evt.clientX - mapImage.DragStartX;
				var dy = evt.clientY - mapImage.DragStartY;
				mapImage.ScrollDelta(dx, -dy);
			}
			else {
				var x = GetOffsetX(evt, mapImage.ImgElement);
				var y = GetOffsetY(evt, mapImage.ImgElement);
				if (mapImage.Db.OnClick) {
					var onClickEvt = new Object();
					onClickEvt.Db = mapImage.Db;
					onClickEvt.X = x;
					onClickEvt.Y = y;

					mapImage.Db.OnClick(onClickEvt);
				}
				else {
					mapImage.Db.ObjectList.SearchByCoordinates(x, y);
				}
			}
		}
	}


	function ImgElement_OnMouseUp (eventObject) 
	{
		var evt = window.event ? window.event : eventObject;

		ImgElement_EndDrag(this["DataObject"], evt);
	}

	
	function ImgElement_OnMouseLeave(eventObject) 
	{
		var evt = window.event ? window.event : eventObject;

		ImgElement_EndDrag(this["DataObject"], evt);
	}


	function ImgElement_OnMouseMove(eventObject) 
	{
		var evt = window.event ? window.event : eventObject;

		var shift = 5;

		var mapImage = this["DataObject"];

		if (mapImage.DragMode == true) {
			var dx = evt.clientX - mapImage.DragStartX;
			var dy = evt.clientY - mapImage.DragStartY;

			if (mapImage.DragStarted == false) {
				if (Math.abs(dx) > shift || Math.abs(dy) > shift) {
					mapImage.DragStarted = true;
				}
			}
			
			if (mapImage.DragStarted == true) {
				mapImage.ImgElement.style.left = (mapImage.DragStartImageLeft + dx) + "px";
				mapImage.ImgElement.style.top = (mapImage.DragStartImageTop + dy) + "px";
			}
		}
	}

	function ImgElement_OnWheel(eventObject) {
		var evt = window.event ? window.event : eventObject;

		var mapImage = this["DataObject"];
		var delta = "wheelDelta" in evt ? evt.wheelDelta : evt.detail * -40;

    mapImage.Zoom(1+(delta/1200));
	}


	function ImgElement_OnLoad() {
		if (IsComplete(this)) {
			this.style.left = 0;
			this.style.top = 0;
		}
	}

/// MapObjectList 
	function MapObjectList(db) {
		this.Db = db;
	}


	MapObjectList.prototype.TextSearch = function (filter) 
	{
  	if(filter == "") {
  		this.Placeholder.innerHTML = "<font color='red'><b>Не задана строка поиска</b></font>";
  	}
  	else {
  		LoadXMLDoc(this.Db.Config["page_search"] + "?db=" + this.Db.Name + "&filter=" + escape(filter), this, "TextSearch_XmlRequestHandler");
  	}
	}

  MapObjectList.prototype.SearchByCoordinates = function (deviceX, deviceY) {
 		var p = new Point(deviceX, deviceY);
 		this.Db.Image.PointDeviceToWorld(p);
 		                             	
  	LoadXMLDoc(this.Db.Config["page_search"] + "?db=" + this.Db.Name + "&X=" + p.X + "&Y=" + p.Y + "&ZoomScale=" + this.Db.Image.ZoomScale, 
  		this, "SearchByCoordinates_XmlRequestHandler");
  }

  MapObjectList.prototype.NavigateToObject = function (objectID) {		                             	
  	LoadXMLDoc(this.Db.Config["page_search"] + "?db=" + this.Db.Name + "&ObjectId=" + objectID, 
  		this, "SelectObjects_XmlRequestHandler");
  }

	var GetNewElement = function (image, node) {
    var id = node.getAttribute("Id");
  	var x = node.getAttribute("X");
  	var y = node.getAttribute("Y");

 		var table = document.createElement("table");
 		var tableBody = document.createElement("tbody");
 		table.appendChild(tableBody);

 		var tr = document.createElement("tr");
 		tableBody.appendChild(tr);

 		var td = document.createElement("td");
 		tr.appendChild(td);

   	var img = document.createElement("img");
   	img.src = "images/go_rtl.png";
   	img.title = "Показать на карте";
   	img.style.cursor = "hand";
  	img.onclick = function () { image.Navigate(x, y, id); }
   	td.appendChild(img);

 		td = document.createElement("td");
 		tr.appendChild(td);
 		td.innerHTML = GetNodeText(node);

  	return table;
  }


	MapObjectList.prototype.AfterSearch = function(xmlDoc, doSelect, centerObject)
	{
		if (this.Placeholder != null) {
			this.Placeholder.innerHTML = "";

     	var objectList = xmlDoc.documentElement.getElementsByTagName('Object');
     	var head = document.createElement("span"); 
     	var totalFound = Number(xmlDoc.documentElement.getAttribute("TotalFound"));

     	if (totalFound > objectList.length) {
	     	head.innerHTML = "Показано " + objectList.length + " объектов из " + totalFound + " найденных<br>";
     	} 
     	else  {
	     	head.innerHTML = "<div id='objectList_Head'>Найдено объектов: <b>" + objectList.length + "</b></div>";
     	}
     	
     	this.Placeholder.appendChild(head);

     	for (var i = 0; i < objectList.length; i++) {
     		this.Placeholder.appendChild(GetNewElement(this.Db.Image, objectList[i]));
     	}

   		if (objectList.length > 0) {
   			var node1 = objectList[0];

				if (doSelect) {
					this.Db.Image.SelectedObject = node1.getAttribute("Id");
				}

				if (centerObject) {
	   			this.Db.Image.CenterX = node1.getAttribute("X");
		 			this.Db.Image.CenterY = node1.getAttribute("Y");
		 		}
   		}

      this.Db.Image.ProjectionChanged();
 		}
	}
	
	
	MapObjectList.prototype.TextSearch_XmlRequestHandler = function (xmlDoc) 
	{
	  this.AfterSearch(xmlDoc, false, false);
	}

	MapObjectList.prototype.SearchByCoordinates_XmlRequestHandler = function (xmlDoc) 
	{
	  this.AfterSearch(xmlDoc, true, false);
	}

	MapObjectList.prototype.SelectObjects_XmlRequestHandler = function (xmlDoc) 
	{
	  this.AfterSearch(xmlDoc, true, true);
	}


/// MapLayerList
	function MapLayerList(db) {
		this.Db = db;
  	this.Layers = new Array();
  	this.LayersVisibility = new Array();

		LoadXMLDoc(this.Db.Config["page_layers"] + "?db=" + this.Db.Name , this, "InitLayerList");
	}


	MapLayerList.prototype.InitLayerList = function (xmlDoc) 
	{
  	this.Layers = new Array();

   	var objectList = xmlDoc.documentElement.getElementsByTagName('Layer');

   	for (var i = 0; i < objectList.length; i++) {
   		var node =  objectList[i];
   		this.Layers[i] = new Layer(node.getAttribute("Name"), node.getAttribute("Caption"), node.getAttribute("Image"));
   		this.LayersVisibility[this.Layers[i].Name] = true;
   	}
   	
   	this.BindLayerList();
  }


	MapLayerList.prototype.BindLayerList = function () 
	{
	  var GetLayerControl = function (layerListControl, layerName) {
   		var cb = document.createElement("input");
   		cb.type= "checkbox";

   		cb.onclick  = function () {
   			layerListControl.LayersVisibility[layerName] = this.checked;
   			layerListControl.Db.Image.ProjectionChanged();
   		}

   		return cb;
	  }
		
		if (this.Placeholder != null) {
			this.Placeholder.innerHTML = "<div id='objectList_Head'>Слои карты</div>";

   		var table = document.createElement("table");
   		this.Placeholder.appendChild(table);
   		
   		var tableBody = document.createElement("tbody");
   		table.appendChild(tableBody);

     	for (var i = 0; i < this.Layers.length; i++) {
     		var layer = this.Layers[i];

     		var tr = document.createElement("tr");
     		tableBody.appendChild(tr);

     		var td = document.createElement("td");
     		tr.appendChild(td);
     		
     		var cb = GetLayerControl(this, layer.Name);
     		td.appendChild(cb);
     		cb.checked = true;

     		var label = document.createElement("span");
     		label.innerHTML = layer.Caption;
     		td.appendChild(label);
     	}

     	this.Db.Image.ProjectionChanged();
    }
	}


	MapLayerList.prototype.GetVisibleLayers = function () 
	{
	  var result = new Array();
	  for (var i = 0; i < this.Layers.length; i++) {
	  	var layer = this.Layers[i];
	  	if (this.LayersVisibility[layer.Name] == true) {
	  		result.push(layer);
	  	}
	  }
   	return result;
	}


/// MapDatabase
	function MapDatabase(name) {
		this.Name = name;

		this.Config = new Array();
		this.Config["page_image"] = "ingeoimage.asp";
		this.Config["page_layers"] = "ingeolayers.asp";
		this.Config["page_search"] = "ingeosearch.asp";

		this.ObjectList = new MapObjectList(this);
		this.Image = new MapImage(this);
		this.LayerList = new MapLayerList(this);
	}


  function BindEvent(elem, eventName, handler)
  {
    if (elem.addEventListener) {
      elem.addEventListener(eventName, handler, false);
    }
    else {
      elem["on" + eventName] = handler;
    }
  }
	
	MapDatabase.prototype.BindImageControl = function (elem) 
	{
		var imagePlaceholder = document.createElement("div");
		elem.appendChild(imagePlaceholder);
		this.Image.ImagePlaceholder = imagePlaceholder;
		imagePlaceholder.style.overflow = "hidden";

		var image = document.createElement("img");
		imagePlaceholder.appendChild(image);
  	this.Image.ImgElement = image;
		image["DataObject"] = this.Image;
		image.style.position = "relative";

		image.ondragstart = ImgElement_OnDragStart;
		image.onmousedown = ImgElement_OnMouseDown;
		image.onmouseup = ImgElement_OnMouseUp;
		image.onmousemove = ImgElement_OnMouseMove;
		image.onmouseleave = ImgElement_OnMouseLeave;
		image.onmousewheel = ImgElement_OnWheel;
		BindEvent(image, "DOMMouseScroll", ImgElement_OnWheel);
		image.onload = ImgElement_OnLoad;

  	return this.Image;
	}


	MapDatabase.prototype.BindObjectListControl = function (blockElement) 
	{
  	this.ObjectList.Placeholder = blockElement;
  	return this.ObjectList;
	}


	MapDatabase.prototype.BindLayerListControl = function (blockElement) 
	{
  	this.LayerList.Placeholder = blockElement;

  	this.LayerList.BindLayerList();
  
  	return this.LayerList;
	}

