/// utils
var isMSIE = (document.all && document.all.item) || window.opera;
var mapCur = isMSIE?"hand":"default";

function ToStrF(numObj){ return numObj.toFixed(4);}

function IsComplete(elem) 
{
	if (elem.readyState) {
		return elem.readyState == "complete";
	}else{
		return elem.complete;
	}
}
function BindEvent(elem, eventName, handler)
{
	if (elem.addEventListener) {
		elem.addEventListener(eventName, handler, false);
	}else{
		elem["on" + eventName] = handler;
	}
}
function GetOffsetX(evt, elem){
	if (evt.offsetX) return evt.offsetX;
	else{
		var L=0;
		do{L+=elem.offsetLeft;}while(elem=elem.offsetParent);
		return evt.clientX - L + document.body.scrollLeft;
	}
}
function GetOffsetY(evt, elem){
	if (evt.offsetY) return evt.offsetY;
	else{
		var T=0;
		do{T+=elem.offsetTop;}while(elem=elem.offsetParent);
		return evt.clientY - T + document.body.scrollTop;
	}
}

function SetPixLeftTop(style, pxL, pxT){
/*	style.pixelTop = pxT;
	style.pixelLeft = pxL;*/
	style.top = pxT;
	style.left = pxL;
}
function SetPixRect(style, pxL, pxT, pxR, pxB){
	style.top = pxT;
	style.left = pxL;
	style.height = pxB-pxT;
	style.width = pxR-pxL;
}
function SetPixRectWH(style, pxL, pxT, pxW, pxH){
	style.top = pxT;
	style.left = pxL;
	style.height = pxH;
	style.width = pxW;
}
//function CreateDomElem(elemType){ return document.createElement(elemType);}

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); 
			}
		}
	}//function handlerRef

	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 LoadXMLDoc

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);}

///class Point
function Point(x, y){
	this.X = x;
	this.Y = y;
}
Point.prototype.Assign = function (x, y){
	this.X = x;
	this.Y = y;
}
Point.prototype.Dec = function (d){
	this.X -= d;
	this.Y -= d;
}
Point.prototype.SwapXY = function(){
	var x = this.Y;
	this.Y = this.X;
	this.X = x;
}
Point.prototype.toString = function (){
	return "(x: "+this.X+"; y: "+this.Y+")";
}

///class 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.AssignByPoint = function (cx, cy, dx, dy){
	this.X1 = cx-dx/2;
	this.Y1 = cy-dy/2;
	this.X2 = cx+dx/2;
	this.Y2 = cy+dy/2;
}
Rect.prototype.Dx = function (){ return this.X2-this.X1;}
Rect.prototype.Dy = function (){ return this.Y2-this.Y1;}
Rect.prototype.GetCenter = function (){ return new Point((this.X1 + this.X2)/2, (this.Y1 + this.Y2)/2);}
Rect.prototype.Offset = function (x, y){
	this.X1 += x;
	this.Y1 += y;
	this.X2 += x;
	this.Y2 += y;
}

///class 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);
}

///class Layer
function Layer(name, caption, image, isRastr){
	this.Name = name;
	this.Caption = caption;
	this.Image = image;
	this.IsRastr = (isRastr=="1")||(isRastr=="true");
}

///class MapPos
function MapPos(cx, cy, zoomscale){
	this.CX=cx;
	this.CY=cy;
	this.ZS=zoomscale;
}
MapPos.prototype.toString = function (){
	return "(cx: "+this.CX+"; cy: "+this.CY+"; zs: "+Math.round(1/this.ZS)+")";
}
MapPos.prototype.IsSame = function (cx, cy, zs){
	return (this.CX==cx)&&(this.CY==cy)&&(this.ZS==zs);
}

///class MapPosHistory
function MapPosHistory(image, maxHistCnt){
	//this.CurrPos = 0;
	this.CurrPos = -1;
	this.StopSave = true;
	this.InPop = false;
	this.OnAfterChange = null;
	this.Image = image;
	this.MPoses = new Array();
	if(maxHistCnt>0)this.MaxCnt = maxHistCnt;
	else this.MaxCnt = 100;
}
MapPosHistory.prototype.Push = function(cx, cy, zs){
	if(this.StopSave)return;
	if(!((this.CurrPos>=0)&&(this.CurrPos<this.MPoses.length)&&this.MPoses[this.CurrPos].IsSame(cx, cy, zs))){
/*		if(){
			this.MPoses.length = this.CurrPos+1;//обрубаем остаток массива
			return;
		}*/
		var aPos = new MapPos(cx, cy, zs);
		if(this.CurrPos<this.MaxCnt)this.CurrPos++;else this.MPoses.shift();
		this.MPoses[this.CurrPos]=aPos;
	}
	this.MPoses.length = this.CurrPos+1;//обрубаем остаток массива
	if(this.OnAfterChange)this.OnAfterChange();
}
MapPosHistory.prototype.Pop = function(backward){
	if((this.InPop)|| (!this.CanPop(backward))||this.Image.IsInUpdate())return;
	this.InPop=true;
	if(backward)this.CurrPos--;else this.CurrPos++;
	var aPos = this.MPoses[this.CurrPos];
	var ss = this.StopSave;
	this.StopSave=true;
	this.Image.NavigateMapPos(aPos.CX, aPos.CY, aPos.ZS);
	this.StopSave = ss;
	this.InPop=false;
	if(this.OnAfterChange)this.OnAfterChange();
	return aPos;
}
MapPosHistory.prototype.CanPop = function(backward){
	if(backward) return (this.CurrPos>0); else return (this.CurrPos<this.MPoses.length-1);
}
MapPosHistory.prototype.Clear = function(){this.MPoses.length=0;this.CurrPos=-1;}

///class SelRectFrame
function SelRectFrame(image){
	this.Image = image;
	this.Paint = false;
	this.ZoomRect = new Rect(0, 0, 0, 0);
	this.diffX = 0;
	this.diffY = 0;
	this.DivElem = null;
	this.BindFrame();
}
SelRectFrame.prototype.BindFrame = function (){
	var divElem = document.createElement("div");
	this.Image.ImagePlaceholder.appendChild(divElem);
	divElem.className="SelRect";
	divElem.style.position="absolute";
	divElem.style.display="none";
	if(!isMSIE){
		var THIS = this;
		divElem.onmouseup=function(e){THIS.OnMouseUp(e);};
		divElem.onmousemove=function(e){THIS.OnMouseMove(e);};
	}
	this.DivElem = divElem;
}
SelRectFrame.prototype.OnMouseDown = function (evt){//evt - событие на mapImage
	if(this.Paint==true)return this.OnMouseUp(evt); 
	if ((evt.button != 0 && evt.button != 1)||(this.DivElem==null)) return true;
	var ofX = GetOffsetX(evt, this.Image.ImgElement);
	var ofY = GetOffsetY(evt, this.Image.ImgElement);
	this.diffX = evt.clientX - ofX;
	this.diffY = evt.clientY - ofY;
	this.ZoomRect.Assign(ofX, ofY, ofX, ofY);
	this.DivElem.style.left = ofX;
	this.DivElem.style.top = ofY;
	this.DivElem.style.width = 0;
	this.DivElem.style.height = 0;
	this.DivElem.style.display = "";
	this.Paint = true;
	return false;
}
SelRectFrame.prototype.OnMouseMove = function (evt){
	if (this.Paint==false)return true;
	this.ZoomRect.X2 = evt.clientX - this.diffX;
	this.ZoomRect.Y2 = evt.clientY - this.diffY;
	if (this.ZoomRect.X2 >= this.ZoomRect.X1)
		this.DivElem.style.width = this.ZoomRect.Dx();
	else{
		this.DivElem.style.left = this.ZoomRect.X2;
		this.DivElem.style.width = -this.ZoomRect.Dx();
	}
	if (this.ZoomRect.Y2 >= this.ZoomRect.Y1)
		this.DivElem.style.height = this.ZoomRect.Dy();
	else{
		this.DivElem.style.top = this.ZoomRect.Y2;
		this.DivElem.style.height = -this.ZoomRect.Dy();
	}
	return false;
}
SelRectFrame.prototype.OnMouseUp = function (evt){
	if (this.Paint==false) return true;
	this.DivElem.style.display = "none";
	this.Image.ZoomToFitNewRectDevice(this.ZoomRect);
	this.Paint = false;
	return false;
}

///class NavigatorFrame
function NavigatorFrame(mapimage, dx, dy, navImgSrc){
	this.Image = mapimage;
	this.DivShowFrame = null;
	this.DivElem = null;
	this.DivScale = null;
	this.Visible = false;
	this.DX = dx;
	this.DY = dy;

	this.Projection = new Matrix();
	this.BackwardProjection = new Matrix();
	this.PaperRect = new Rect(0, 0, this.DX, this.DY);

	this.BindNavFrame(navImgSrc);
}
NavigatorFrame.prototype.BindNavFrame = function (navImgSrc){
	var divElem = document.createElement("div");
	this.Image.ImagePlaceholder.appendChild(divElem);
	divElem.className = "navigator";
	SetPixRectWH(divElem.style, this.Image.Width-this.DX-10, this.Image.Height-this.DY-10, this.DX+(isMSIE?4:0), this.DY+(isMSIE?4:0));
	var currNavig = this;
	divElem.onclick = function (eventObject){
		var evt = window.event ? window.event : eventObject;
		evt.returnValue = currNavig.OnClick(evt);
	}
	this.DivElem = divElem;

	divElem = document.createElement("div");
	this.DivElem.appendChild(divElem);
	divElem.className = "navigatorRect";
	divElem.style.width = 0;
	divElem.style.height = 0;
	this.DivShowFrame = divElem;

	var navImg = document.createElement("img");
	this.DivElem.appendChild(navImg);
	navImg.src = navImgSrc;

	divElem = document.createElement("div");
	this.Image.ImagePlaceholder.appendChild(divElem);
	divElem.className = "ScaleInfo";
	divElem.style.top = 10;
	divElem.style.left = 10;
	this.DivScale = divElem;

	this.Hide();
	this.ProjectionChanged();
}
NavigatorFrame.prototype.ProjectionChanged = function (){
	var dbInfo = this.Image.Db.DbInfo;
	var zsHorz, zsVert;
	if(this.Image.IsSwapXY()){
		zsHorz = this.DX/dbInfo.MapDY;
		zsVert = this.DY/dbInfo.MapDX;
	}else{
		zsHorz = this.DX/dbInfo.MapDX;
		zsVert = this.DY/dbInfo.MapDY;
	}
	var zoomscale = Math.min(zsHorz, zsVert);

	this.Projection.Clear();
	if(this.Image.YMirror==true) this.Projection.AddScaling(1, -1);
	this.Projection.AddScaling(zoomscale, zoomscale);
	this.Projection.AddRotation(this.Image.XAngle);
	this.BackwardProjection.AssignBackward(this.Projection);

	var paperCenter = new Point(dbInfo.MapCenterX, dbInfo.MapCenterY);
	this.Projection.ProjectPoint(paperCenter);//world to paper
	this.SetPaperOffset(paperCenter.X - this.PaperRect.Dx()/2, paperCenter.Y - this.PaperRect.Dy()/2);
}
//=1//NavigatorFrame.prototype.PaperToDeviceScale = function (){ return this.Dpi/0.0254;}
NavigatorFrame.prototype.SetPaperOffset = function (x, y){
	this.PaperRect.Offset(x - this.PaperRect.X1, y - this.PaperRect.Y1);
}
NavigatorFrame.prototype.PointPaperToDevice = function (p){
	p.Assign(
		(p.X - this.PaperRect.X1),
		this.DY - (p.Y - this.PaperRect.Y1));
}
NavigatorFrame.prototype.PointDeviceToPaper = function (p){
	p.Assign(
		p.X + this.PaperRect.X1,
		(this.DY - p.Y) + this.PaperRect.Y1);
}
NavigatorFrame.prototype.PointWorldToDevice = function (p){
	this.Projection.ProjectPoint(p);
	this.PointPaperToDevice(p);
}
NavigatorFrame.prototype.PointDeviceToWorld = function (p){
	this.PointDeviceToPaper(p);
	this.BackwardProjection.ProjectPoint(p);
}

NavigatorFrame.prototype.Show = function (){
	this.DivElem.style.display = '';
	this.DivScale.style.display = '';
	this.Visible = true;
	this.Repaint();
}
NavigatorFrame.prototype.Hide = function (){
	this.DivElem.style.display = 'none';
	this.DivScale.style.display = 'none';
	this.Visible = false;
}
NavigatorFrame.prototype.Repaint = function (){
	if(this.Visible==false) return;
	SetPixLeftTop(this.DivElem.style, this.Image.Width-this.DX-10, this.Image.Height-this.DY-10);
	var p0 = new Point(0, 0);
	var p1 = new Point(this.Image.Width, this.Image.Height);

	this.Image.PointDeviceToWorld(p0);
	this.Image.PointDeviceToWorld(p1);
	this.PointWorldToDevice(p0);
	this.PointWorldToDevice(p1);

	if(!isMSIE){p0.Dec(2);p1.Dec(2);}

	SetPixRectWH(this.DivShowFrame.style, Math.round(p0.X), Math.round(p0.Y), 
		Math.max(2, Math.round(p1.X-p0.X)), Math.max(2, Math.round(p1.Y-p0.Y)));

	this.DivScale.innerHTML = "масштаб 1: "+Math.round(1/this.Image.ZoomScale);
}
NavigatorFrame.prototype.OnClick = function (evt){
	var pt = new Point(GetOffsetX(evt, this.DivElem), GetOffsetY(evt, this.DivElem));
	if(!isMSIE)pt.Dec(2);
	this.PointDeviceToWorld(pt);
	this.Image.Projection.ProjectPoint(pt);
	this.Image.SetPaperCenter(pt);
	return false;
}

///class MapImage
function MapImage(db){
	this.Db = db;

	this.Width = 500;
	this.Height = 500;
	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.ImagePlaceholder = null;
	this.ImgElement = null;
	this.SelFrame = null;
	this.NavFrame = null;
	this.NavigatorImgSrc = "AllMapSmall.png";
	this.MapPoses = new MapPosHistory(this, 25);

	this.DragMode = false;
	this.DragStarted = false;
	this.DragStartX = 0;
	this.DragStartY = 0;
	this.DragStartImageLeft = 0;
	this.DragStartImageTop = 0;

	this.SelFrameMode = false;
	this.UpdateCnt = 0;
}
MapImage.prototype.IsInUpdate = function (){return (this.UpdateCnt>0);}
MapImage.prototype.BeginUpdate = function (){
	//if((this.UpdateCnt==0)&&this.ImgElement!=null)this.MapPoses.Push(this.CenterX, this.CenterY, this.ZoomScale, this.UpdateCnt.toString()+" "+arguments.callee.caller);
	this.UpdateCnt++;
}
MapImage.prototype.EndUpdate = function (){
	if (this.UpdateCnt>0){
		this.UpdateCnt--;
		if(this.UpdateCnt==0){
			this.ReDraw();
			if(this.ImgElement!=null)this.MapPoses.Push(this.CenterX, this.CenterY, this.ZoomScale, this.UpdateCnt.toString()+" "+arguments.callee.caller);
		}
	}
}

MapImage.prototype.BindImage = function (){
	this.ImagePlaceholder.style.overflow = "hidden";
	var img = document.createElement("img");
	this.ImagePlaceholder.appendChild(img);
	this.ImgElement = img;
	img["DataObject"] = this;
	img.style.position = "absolute";
	this.ImagePlaceholder.style.cursor = mapCur;//"hand";

	img.ondragstart = ImgElement_OnDragStart;
	img.onmousedown = ImgElement_OnMouseDown;
	img.onmouseup = ImgElement_OnMouseUp;
	img.onmousemove = ImgElement_OnMouseMove;
	img.onmousewheel = ImgElement_OnWheel;
	BindEvent(img, "DOMMouseScroll", ImgElement_OnWheel);
	if(!img.onreadystatechange) img.onload = ImgElement_OnLoad;
	else img.onreadystatechange = ImgElement_OnLoad;

	this.SelFrame = new SelRectFrame(this);
}
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.BuildParamStr = function (){
	var imgFormat = "";
	if (this.Db.LayerList.IsRastrLayersVisible()) imgFormat="&ImgType=jpg"
	return this.Db.DbParam + "&Layers=" + this.Db.LayerList.GetVisibleLayersString() +
		"&CenterX=" + this.CenterX + "&CenterY=" + this.CenterY +
		"&Width=" + this.Width +"&Height=" + this.Height + imgFormat +
		"&XAngle=" + this.XAngle +"&YMirror=" + this.YMirror +
		"&ZoomScale=" + this.ZoomScale + "&SelectedObject=" + this.SelectedObject;
}
//base function. makes redraw of map
MapImage.prototype.ReDraw = function (){
	if (this.ImgElement == null) return;
	this.ImagePlaceholder.style.top = 0;
	this.ImagePlaceholder.style.left = 0;
	this.ImagePlaceholder.style.width = this.Width;
	this.ImagePlaceholder.style.height = this.Height;
	this.ImgElement.src = this.Db.Config["page_image"] + this.BuildParamStr();
	if (this.NavFrame != null) this.NavFrame.Repaint();
}
MapImage.prototype.ProjectionChanged = function (){
	this.BeginUpdate();
	this.PaperRect = new Rect(0, 0, this.Width/this.PaperToDeviceScale(), this.Height/this.PaperToDeviceScale());
	this.Projection.Clear();
	if(this.YMirror==true) this.Projection.AddScaling(1, -1);
	this.Projection.AddScaling(this.ZoomScale, this.ZoomScale);
	this.Projection.AddRotation(this.XAngle);
	this.BackwardProjection.AssignBackward(this.Projection);

	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.EndUpdate();//this.ReDraw();
}
MapImage.prototype.SetPaperCenter = function (center){
	this.BeginUpdate();
	this.SetPaperOffset(center.X - this.PaperRect.Dx()/2, center.Y - this.PaperRect.Dy()/2);
	this.BackwardProjection.ProjectPoint(center);
	this.CenterX = center.X;
	this.CenterY = center.Y;
	this.EndUpdate();//this.ReDraw();
}
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.SetPaperCenter(center);
}
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.SetPaperCenter(center);
}
MapImage.prototype.Zoom = function (multiplier){ this.ZoomTo(this.ZoomScale * multiplier);}
MapImage.prototype.ZoomTo = function (zoomscale){
	var scaleList = this.Db.ScaleList;
	if (zoomscale<scaleList.MaxScale) zoomscale = scaleList.MaxScale;
	else if (zoomscale>scaleList.MinScale) zoomscale = scaleList.MinScale;
	if (this.ZoomScale == zoomscale) return;

	this.BeginUpdate();
	this.ZoomScale = zoomscale;
	scaleList.FindZoomIndex(zoomscale);//DropSelection();
	this.ProjectionChanged();
	this.EndUpdate();
}
MapImage.prototype.ZoomToFitNewRectDevice = function (rect){//of device coords
	if (Math.abs(rect.Dx())<2 || Math.abs(rect.Dy())<2) return;
	this.BeginUpdate();
	var center = rect.GetCenter();
	this.PointDeviceToWorld(center);
	this.CenterX = center.X;
	this.CenterY = center.Y;
	//coef zoom calc
	var zsWidth = Math.abs(this.Width/rect.Dx());
	var zsHeight = Math.abs(this.Height/rect.Dy());
	var zsMulCoef = zsWidth > zsHeight ? zsHeight : zsWidth;
	if (zsMulCoef==0)zsMulCoef=1;
	if (rect.X2<rect.X1) zsMulCoef = 1/zsMulCoef;//zoomOut
	this.Zoom(zsMulCoef);
	this.EndUpdate();
}
MapImage.prototype.SelectedObject = function (selectedObject){
	this.SelectedObject = selectedObject;
}
MapImage.prototype.Navigate = function (x, y, objectId){
	this.BeginUpdate();
	this.CenterX = x;
	this.CenterY = y;
	this.SelectedObject = objectId;
	this.ProjectionChanged();
	this.EndUpdate();
}
MapImage.prototype.NavigateMapPos = function (x, y, zoomscale){
	this.BeginUpdate();
	this.CenterX = x;
	this.CenterY = y;
	if (this.ZoomScale != zoomscale) this.ZoomTo(zoomscale);
	else this.ProjectionChanged();
	this.EndUpdate();
}
MapImage.prototype.IsSwapXY = function () 
{return Math.ceil( Math.abs( Math.cos( this.XAngle ) ) ) != 1;}

MapImage.prototype.NavigateAllMap = function (){
	this.BeginUpdate();
	this.CenterX = this.Db.DbInfo.MapCenterX;
	this.CenterY = this.Db.DbInfo.MapCenterY;

	var zsHorz, zsVert;
	if(this.IsSwapXY()){
		zsHorz = this.Width/this.PaperToDeviceScale() /this.Db.DbInfo.MapDY;
		zsVert = this.Height/this.PaperToDeviceScale()/this.Db.DbInfo.MapDX;
	}else{
		zsHorz = this.Width/this.PaperToDeviceScale() /this.Db.DbInfo.MapDX;
		zsVert = this.Height/this.PaperToDeviceScale()/this.Db.DbInfo.MapDY;
	}
	this.ZoomScale = Math.min(zsHorz, zsVert);
	this.ProjectionChanged();
	this.EndUpdate();
}
MapImage.prototype.ShowNavigator = function(){
	if (this.NavFrame == null) this.NavFrame = new NavigatorFrame(this, 150,150, this.NavigatorImgSrc);
	this.NavFrame.Show();
}
MapImage.prototype.HideNavigator = function(){
	if (this.NavFrame != null) this.NavFrame.Hide();
}
MapImage.prototype.SetNavigatorMode = function(visible){
	if (visible)this.ShowNavigator(); else this.HideNavigator();
}
MapImage.prototype.SwitchScaleFrameMode = function(){
	this.SetScaleFrameMode(!this.SelFrameMode);
}
MapImage.prototype.SetScaleFrameMode = function(visible){
	this.SelFrameMode = (visible == true);
	if (this.SelFrameMode==true) this.ImagePlaceholder.style.cursor = "crosshair";
	else this.ImagePlaceholder.style.cursor = mapCur;//"hand";
}
MapImage.prototype.DoEndDrag = function (evt){
	if (this.SelFrameMode==true){
		evt.returnValue = this.SelFrame.OnMouseUp(evt);
		return evt.returnValue;
	}
	if (this.DragMode==true) {
		this.DragMode = false;
		if (this.DragStarted == true) {
			this.DragStarted = false;
			this.ImagePlaceholder.style.cursor = mapCur;//"hand";
			var dx = evt.clientX - this.DragStartX;
			var dy = evt.clientY - this.DragStartY;
			this.ScrollDelta(dx, -dy);
		}else{
			var x = GetOffsetX(evt, this.ImgElement);
			var y = GetOffsetY(evt, this.ImgElement);
			if (this.Db.OnClick) {
				var onClickEvt = new Object();
				onClickEvt.Db = this.Db;
				onClickEvt.X = x;
				onClickEvt.Y = y;
				this.Db.OnClick(onClickEvt);
			}else{
				this.Db.ObjectList.SearchByCoordinates(x, y);
			}
		}
	}
}
//map image dependent events
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"];
	if (mapImage.SelFrameMode==true){
		evt.returnValue = mapImage.SelFrame.OnMouseDown(evt);
		return evt.returnValue;
	}
	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 ImgElement_OnMouseMove(eventObject){
	var evt = window.event ? window.event : eventObject;
	var mapImage = this["DataObject"];
	if (mapImage.SelFrameMode==true){
		evt.returnValue = mapImage.SelFrame.OnMouseMove(evt);
		return evt.returnValue;
	}
	var shift = 5;
	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;
				mapImage.ImagePlaceholder.style.cursor = "move";
			}
		}
		if (mapImage.DragStarted == true) {
			SetPixLeftTop(mapImage.ImgElement.style, (mapImage.DragStartImageLeft + dx) + "px",(mapImage.DragStartImageTop + dy) + "px");
/*			mapImage.ImgElement.style.left = (mapImage.DragStartImageLeft + dx) + "px";
			mapImage.ImgElement.style.top = (mapImage.DragStartImageTop + dy) + "px";*/
		}
	}
}
function ImgElement_OnMouseUp (eventObject){
	var evt = window.event ? window.event : eventObject;
	return this["DataObject"].DoEndDrag(evt);
}
function ImgElement_OnWheel(eventObject){
	var evt = window.event ? window.event : eventObject;
	var mapImage = this["DataObject"];
	if (mapImage.SelFrameMode==true) return false; //do nothing
	var delta = "wheelDelta" in evt ? evt.wheelDelta : evt.detail * -40;
	mapImage.Zoom(1+(delta/1200));
	evt.returnValue = false;
	return evt.returnValue;
}
function ImgElement_OnLoad() {
	if (IsComplete(this)){
		this.style.left = 0;
		this.style.top = 0;
	}
}

/// class MapObjectList 
function MapObjectList(db){
	this.Db = db;
	this.OnAfterSearch = null; //событие после поиска
}
MapObjectList.prototype.TextSearch = function (filter) 
{
	if(filter == "") {
		this.Placeholder.innerHTML = "<font color='red'><b>Не задана строка поиска</b></font>";
	}else{
		LoadXMLDoc(this.Db.Config["page_search"] + this.Db.DbParam + "&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"] + this.Db.DbParam + "&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"] + this.Db.DbParam + "&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");
	table.className = "SearchItem";
/*	table.cellSpacing = 0;
	table.cellPadding = 0;*/
	var tableBody = document.createElement("tbody");
	table.appendChild(tableBody);

	var tr = document.createElement("tr");
	tableBody.appendChild(tr);
	var td = document.createElement("td");
	tr.appendChild(td);
	td.width = "16px";
//	td.style.border = "dotted 1px #3363AC";

	var img = document.createElement("img");
	img.src = "img/go_rtl.gif";
	img.title = "Показать на карте";
	img.style.cursor = mapCur;//"hand";
	img.onclick = function () { image.Navigate(x, y, id);}
	td.appendChild(img);

	td = document.createElement("td");
	td.width = "100%";
	tr.appendChild(td);
	td.innerHTML = GetNodeText(node);
//	td.style.border = "dotted 1px #3363AC";
	return table;
}

MapObjectList.prototype.AfterSearch = function(xmlDoc, doSelect, centerObject){
	if (this.Placeholder == null) return;
	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 = "Найдено объектов: <b>" + objectList.length + "</b><br>";
	}
	this.Placeholder.appendChild(head);
	for (var i = 0; i < objectList.length; i++) {
		this.Placeholder.appendChild(GetNewElement(this.Db.Image, objectList[i]));
	}

	this.Db.Image.BeginUpdate();
	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();
	this.Db.Image.EndUpdate();
	if(this.OnAfterSearch)this.OnAfterSearch();
}
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);
}

/// class MapScale
function MapScale (caption, scale1to){
	this.ZoomScale = 1/scale1to;
	if (caption) this.Caption = caption+" (1:"+scale1to.toString()+")";
	else this.Caption = "1:"+scale1to.toString();
}

/// class MapScaleList //by hedgehog
function MapScaleList(db) {
	this.Db = db;
	this.Scales = new Array();
	this.ScaleInx = -1;
	this.Container = null;
	this.Placeholder = null;
	this.MaxScale = 1e-10;
	this.MinScale = 1;
	this.Busy = false;
	LoadXMLDoc(this.Db.Config["page_scales"] + this.Db.DbParam, this, "InitScaleList");
}
MapScaleList.prototype.InitScaleList = function (xmlDoc){
	this.Scales = new Array();
//	var objectList = xmlDoc.documentElement.selectNodes("Scales/Scale"/*'Scale'*/);
	var objectList = xmlDoc.documentElement.getElementsByTagName("Scales");
	if (objectList.length>0) objectList = objectList.item(0).getElementsByTagName("Scale");
	var curScale, node;
	for (var i = 0; i < objectList.length; i++) {
		node = objectList[i];
		curScale = new MapScale(node.getAttribute("Caption"), node.getAttribute("value1to"));
		this.Scales[i] = curScale;
		if (i==0 || curScale.ZoomScale<this.MaxScale) this.MaxScale = curScale.ZoomScale;
		if (i==0 || curScale.ZoomScale>this.MinScale) this.MinScale = curScale.ZoomScale;
	}
	this.BindScaleList();
}
MapScaleList.prototype.BindScaleList = function (){
	if (this.Placeholder == null) return;
	this.Placeholder.innerHTML = "";
	var cbx = document.createElement("select");
	this.Placeholder.appendChild(cbx);
	cbx.className = "scales";
	this.Container = cbx;
	var ScaleListControl = this;
	cbx.onchange = function (){
		if (this.selectedIndex<=0 || this.selectedIndex==ScaleListControl.ScaleInx) return false;
		ScaleListControl.Busy = true;
		ScaleListControl.ScaleInx = this.selectedIndex-1;
		ScaleListControl.Db.Image.ZoomTo(ScaleListControl.Scales[this.selectedIndex-1].ZoomScale);
		this.blur();
		ScaleListControl.Busy = false;
	}
	var op = document.createElement("option");
	cbx.appendChild(op);
	op.value = -1;
	op.text = "Выбор масштаба";
	for (var i = 0; i < this.Scales.length; i++) {
		op = document.createElement("option");
		cbx.appendChild(op);
		op.value = i;
		op.text = this.Scales[i].Caption;
	}
	cbx.selectedIndex = 0;
}
MapScaleList.prototype.DropSelection = function(){
	if(this.Busy)return;
	this.ScaleInx = -1;
	if(this.Container!=null)this.Container.selectedIndex = 0;
}
MapScaleList.prototype.FindZoomIndex = function(zoomscale){
	if(this.Busy)return;
	for(var i=0;i<this.Scales.length;i++)
		if(this.Scales[i].ZoomScale==zoomscale){
			this.ScaleInx = i;
			if(this.Container!=null)this.Container.selectedIndex = i+1;
			return;
		}
}
/// class MapLayerList
function MapLayerList(db){
	this.Db = db;
	this.Layers = new Array();
	this.LayersVisibility = new Array();
	LoadXMLDoc(this.Db.Config["page_layers"] + this.Db.DbParam, 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];
		var layer = new Layer(node.getAttribute("Name"), node.getAttribute("Caption"), node.getAttribute("Image"), node.getAttribute("rastr"));
		this.Layers[i] = layer;
		this.LayersVisibility[layer.Name] = (layer.Name.charAt(0)!="r");//true;
	}
	this.BindLayerList();
}
MapLayerList.prototype.BindLayerList = function (){
	if (this.Placeholder == null) return;
	var GetLayerControl = function (layerListControl, layerName) {
		var cb = document.createElement("input");
		cb.type= "checkbox";
		cb.onclick= function () {
			layerListControl.LayersVisibility[layerName] = this.checked;
			layerListControl.Db.Image.ReDraw();
		}
		return cb;
	}
	this.Placeholder.innerHTML = "";
	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 = this.LayersVisibility[layer.Name];//true;

		if(layer.Image && layer.Image!=""){
			var img = document.createElement("img");
			img.src = layer.Image;
			td.appendChild(img);
		}

		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;
}
MapLayerList.prototype.GetVisibleLayersString = function (){
	var result = "", layerName;
	for (var i = 0; i < this.Layers.length; i++) {
		layerName = this.Layers[i].Name;
		if (this.LayersVisibility[layerName]) {
			if(result.length>0) result += ",";
			result += layerName;
		}
	}
	return result;
}
MapLayerList.prototype.IsRastrLayersVisible = function (){
	var layerName;
	for (var i = 0; i < this.Layers.length; i++){
		layerName = this.Layers[i].Name;
		if (this.LayersVisibility[layerName] && this.Layers[i].IsRastr) return true;
	}
	return false;
}

/// class MapDbInfoEx //by hedgehog
function MapDbInfoEx(db){
	this.Db = db;

	this.MapCenterX = 0;
	this.MapCenterY = 0;
	this.MapDX = 100000;
	this.MapDY = 100000;
	LoadXMLDoc(db.Config["page_scales"] + db.DbParam, this, "InitDbInfo");
}
MapDbInfoEx.prototype.InitDbInfo = function (xmlDoc){
	var terrElem = xmlDoc.documentElement;//.selectSingleNode('Territory');
	this.MapCenterX = GetInt(terrElem.getAttribute("MapCenterX"));
	this.MapCenterY = GetInt(terrElem.getAttribute("MapCenterY"));
	this.MapDX = GetInt(terrElem.getAttribute("MapDX"));
	this.MapDY = GetInt(terrElem.getAttribute("MapDY"));
}

/// class MapDatabase
function MapDatabase(name){
	//isMSIE = document.all && document.all.item && !window.opera;
	this.Name = name;
	this.DbParam = "Db=" + name;

	this.Config = new Array();
	this.Config["page_image"] = "ingeoimage.asp?";
	this.Config["page_layers"] = "ingeolayers.asp?";
	this.Config["page_search"] = "ingeosearch.asp?";
	this.Config["page_scales"] = "ingeoScales.asp?";

	this.DbInfo = new MapDbInfoEx(this);
	this.ObjectList = new MapObjectList(this);
	this.Image = new MapImage(this);
	this.LayerList = new MapLayerList(this);
	this.ScaleList = new MapScaleList(this);
}
MapDatabase.prototype.BindImageControl = function (blockElement/*elem*/){
	this.Image.ImagePlaceholder = blockElement;
	this.Image.BindImage();
	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;
}
MapDatabase.prototype.BindScaleListControl = function (blockElement){
	this.ScaleList.Placeholder = blockElement;
	this.ScaleList.BindScaleList();
	return this.ScaleList;
}
