/*

    Javascript for map management
    
    there can be multiple map controls on a page.  for instance, the normal map and in this case a preview map.

*/

// -------------------------- GLOBALS

var IMS2_mapActionWaitTime = 150; // ms

// -------------------------- FUNCTIONS

function IMS2_SetupMap(mapid,mapdatasource,initialLayer,pointBuffer,mapPolyTip){
    if(null != mapid && IMS2_MapManager){
        var mapdiv = document.getElementById('_mapdiv_' + mapid);
        if(null != mapdiv){
            IMS2_MapManager.AddMap(mapid,mapdatasource,initialLayer,pointBuffer,mapPolyTip);
        }
    }
}

function IMS2_TriggerMapRefresh(mapid,tick){
    if(null != mapid && IMS2_MapManager){
        IMS2_MapManager.RefreshMap(mapid,tick)
    }
}

function IMS2_TriggerMapMouseHover(mapid,tick){
    if(null != mapid && IMS2_MapManager){
        IMS2_MapManager.MapHover(mapid,tick)
    }
}

function IMS2_OnMapEvent(mapid,etype,e){
    if(null != mapid && IMS2_MapManager){
        e = IMS2_CorrectEvent(e);
        IMS2_MapManager.OnMapEvent(mapid,etype,e);
    }
    return(false);
}

function IMS2_GetMapDiv(mapid){
    return(document.getElementById("_mapdiv_" + mapid));
}

function IMS2_SetActiveLayer(mapid,lid){
    if(IMS2_MapManager && mapid && lid){
        var map = IMS2_MapManager.GetMap(mapid);
        if(map){
            map.SetActiveLayer(lid);
        }        
    }
}


// -------------------------- CLASSES

//manage all the maps
function IMS2_ClientMapManager(){
    var self = this;
 
    this.maps = new Array();
    this.didInit = false;
    
    function AddMap(mapid,mapdatasource,initialLayer,pointBuffer,mapPolyTip){
        if( (null != mapid) && (!self.ContainsMap(mapid)) ){
            var newMap = new IMS2_ClientMap(mapid,mapdatasource);
            self.maps[self.maps.length] = newMap;
            if(initialLayer){
                newMap.SetActiveLayer(initialLayer);
            }
            newMap.pointBuffer = pointBuffer;
            newMap.mapPolyTip = mapPolyTip;
            if(self.didInit){
                newMap.DefaultAction();
                newMap.Invalidate();
            }
        }
    }
    this.AddMap = AddMap;
    
    function ContainsMap(mapid){
        return(false);
    }
    this.ContainsMap = ContainsMap;
    
    function GetMap(mapid){
        var mapobj = null;
        for(var i = 0; i < self.maps.length; i++){
            var curMap = self.maps[i];
            if((null != curMap) && curMap.mapid == mapid){
                mapobj = curMap;
                break;
            }
        }
        return(mapobj);
    }
    this.GetMap = GetMap;
        
    function OnMapEvent(mapid,etype,e){
        var mapobj = self.GetMap(mapid);
        if(mapobj){
            if("resize" == etype){
                return(mapobj.OnResize());
            }else if("mouseup" == etype){
                return(mapobj.OnMouseUp(e));
            }else if("mousedown" == etype){
                return(mapobj.OnMouseDown(e));
            }else if("mousemove" == etype){
                return(mapobj.OnMouseMove(e));
            }else if("mouseout" == etype){
                return(mapobj.OnMouseOut(e));
            }else if("mousewheel" == etype){
                return(mapobj.OnMouseWheel(e));     
            }else if("click" == etype){
                return(mapobj.OnClick(e));
            }else if("dblclick" == etype){
                return(mapobj.OnDblClick(e));    
            }
        }
        return(true);
    }
    this.OnMapEvent = OnMapEvent;
    
    function FireResizeAll(){
        for(var i = 0; i < self.maps.length; i++){
            var curMap = self.maps[i];
            if(null != curMap){
                curMap.OnResize();
            }
        }
    }
    this.FireResizeAll = FireResizeAll;
    
    function RefreshMap(mapid,tick){
        var mapobj = self.GetMap(mapid);
        if(mapobj){
            mapobj.TriggerRefresh(tick);
        }
    }
    this.RefreshMap = RefreshMap;
    
    function MapHover(mapid,tick){
        var mapobj = self.GetMap(mapid);
        if(mapobj){
            mapobj.OnHover(tick);
        }
    }
    this.MapHover = MapHover;
    
    function ChangeMapAction(mapid,action){
        var mapobj = self.GetMap(mapid);
        if(mapobj){
            mapobj.ChangeAction(action);
        }
    }
    this.ChangeMapAction = ChangeMapAction;
    
    function GetLayerList(mapid){
        var mapobj = self.GetMap(mapid);
        if(mapobj){
            return(mapobj.layers);
        }
        return(null);
    }
    this.GetLayerList = GetLayerList;
    
    function AttachLayerListControlToMap(llcid,mapid,refreshOnChange,layerStyle,layerStyleSelected,groupStyle,imgPlus,imgMinus){
        var mapobj = self.GetMap(mapid);
        if(mapobj){
            return(mapobj.AddLayerListControl(llcid,mapid,refreshOnChange,layerStyle,layerStyleSelected,groupStyle,imgPlus,imgMinus));
        }
        return(false);
    }
    this.AttachLayerListControlToMap = AttachLayerListControlToMap;
    
    function AttachLegendControlToMap(id,mapid){
        var mapobj = self.GetMap(mapid);
        if(mapobj){
            return(mapobj.AddLegendControl(id,mapid));
        }
        return(false);
    }
    this.AttachLegendControlToMap = AttachLegendControlToMap;
    
    function GetLayerListControl(mapid,llcid){
        var mapobj = self.GetMap(mapid);
        if(mapobj){
            var ctl = mapobj.layerListControls;
            if(ctl){
                return(ctl.Get(llcid));
            }
        }
        return(null);
    }
    this.GetLayerListControl = GetLayerListControl;
    
    function Init(){
        for(var i = 0; i < self.maps.length; i++){
            var curMap = self.maps[i];
            if((null != curMap) && (false == curMap.didInit)){
                curMap.Invalidate();
                curMap.DefaultAction();
            }
        }
        self.didInit = true;
    }
    this.Init = Init;
}

//show the zoom/select box
function IMS2_ClientMapSelectBox(){
    var self = this;
    
    this.bgColor = "#00ff00";
    this.fgColor = "#009900";
    this.active = false;
    this.effect = false;
    this.x1 = -1;
    this.y1 = -1;
    this.x2 = -1;
    this.y2 = -1;
    
    this.fxAmnt = 0;
    this.fxTime = 500;
    this.fxFrames = 20;
    this.fxTarget = null;
    
    function GetOrderedBounds(){
        var x1 = 0;
        var y1 = 0;
        var x2 = 0;
        var y2 = 0;
        if(self.x2 > self.x1){
            x1 = self.x1;
            x2 = self.x2;
        }else{
            x1 = self.x2;
            x2 = self.x1;
        }
        if(self.y2 > self.y1){
            y1 = self.y1;
            y2 = self.y2;
        }else{
            y1 = self.y2;
            y2 = self.y1;
        }
        return([x1,y1,x2,y2]);
    }
    this.GetOrderedBounds = GetOrderedBounds;
    
    function GetBestFit(w,h){
        var bounds = self.GetOrderedBounds();
        var envw = bounds[2] - bounds[0];
        var envh = bounds[3] - bounds[1];
        var cx = bounds[0] + (envw * 0.5);
        var cy = bounds[1] + (envh * 0.5);
        var wth = (w != 0) ? (h / w) : 1;
        var htw = (h != 0) ? (w / h) : 1;
        
        var nw = envw;
        var nh = envh;
        
        nh = wth * nw;
        if(nh < envh){
            nh = envh;
            nw = htw * nh;
        }
        
        envw = nw * 0.5;
        envh = nh * 0.5;
        
        return([cx - envw,cy - envh,cx + envw,cy + envh]);
    }
    this.GetBestFit = GetBestFit;
    
    //fade zoom box - used for aesthetic value only to improve "user experience"
    function FX_ZoomFadeParent(){
        var endFX = false;
        if(!self.active){
            if(self.fxTarget){
                var w = 0;
                var h = 0;
                var left = 0;
                var top = 0;
                var parent = self.fxTarget.parentNode;
                self.fxAmnt += (0 != self.fxFrames) ? (1 / self.fxFrames) : 0.1;
                if(self.fxAmnt < 1){
                    if(parent){
                        if(self.x2 >= self.x1){
                            w = self.x2 - self.x1;
                            left = self.x1;
                        }else{
                            w = self.x1 - self.x2;
                            left = self.x2;
                        }
                        if(self.y2 >= self.y1){
                            h = self.y2 - self.y1;
                            top = self.y1;
                        }else{
                            h = self.y1 - self.y2;
                            top = self.y2;
                        }
                        //var ppos = IMS2_GetElementPosition(parent);
                        var psize = IMS2_GetElementDimension(parent);
                        var ld = (-left) * self.fxAmnt;
                        var td = (-top) * self.fxAmnt;
                        var wd = (psize[0] - w) * self.fxAmnt;
                        var hd = (psize[1] - h) * self.fxAmnt;
                        self.fxTarget.style["visibility"] = "visible";
                        self.fxTarget.style["left"] = (left + ld) + "px";
                        self.fxTarget.style["top"] = (top + td) + "px";
                        self.fxTarget.style["width"] = (w + wd) + "px";
                        self.fxTarget.style["height"] = (h + hd) + "px";
                        IMS2_SetElmOpacity(self.fxTarget,((1-self.fxAmnt) * 0.5));
                        setTimeout(self.FX_ZoomFadeParent,((0 != self.fxFrames) ? (self.fxTime / self.fxFrames) : (100)));
                    }
                }else{
                    self.fxTarget.style["visibility"] = "hidden";
                    self.effect = false;
                }
            }
        }else{
            self.effect = false;
        }
        if(endFX){
            
        }
    }
    this.FX_ZoomFadeParent = FX_ZoomFadeParent;
    
    function Start(x,y,elm,dotElm,fitElm){
        if(!self.active){
            self.active = true;
            var offx = 0;
            var offy = 0;
            if(IMS2_isMSIE()){
                offx = -2;
                offy = -2;
            }
            self.x1 = self.x2 = x + offx;
            self.y1 = self.y2 = y + offy;
            if(elm){
                elm.style["visibility"] = "visible";
                elm.style["left"] = (self.x1 + "px");
                elm.style["top"] = (self.y1 + "px");
                elm.style["width"] = "0px";
                elm.style["height"] = "0px";
                elm.style["background-color"] = self.bgColor;
                elm.style["backgroundColor"] = self.bgColor;
                elm.style["border-width"] = "1px";
                elm.style["borderWidth"] = "1px";
                elm.style["border-color"] = self.fgColor;
                elm.style["borderColor"] = self.fgColor;
                elm.style["border"] = "1px solid " + self.fgColor;
                IMS2_SetElmOpacity(elm,0.5);
            }
            if(dotElm){
                dotElm.style["visibility"] = "visible";
                dotElm.style["left"] = (self.x1 + "px");
                dotElm.style["top"] = (self.y1 + "px");
                dotElm.style["width"] = "0px";
                dotElm.style["height"] = "0px";
                dotElm.style["border-width"] = "1px";
                dotElm.style["borderWidth"] = "1px";
                dotElm.style["border-color"] = self.fgColor;
                dotElm.style["borderColor"] = self.fgColor;
                dotElm.style["border"] = "1px dashed " + self.fgColor;
                IMS2_SetElmOpacity(elm,0.75);
                
            }
        }else{
            self.Move(x,y,elm,dotElm,fitElm);
        }
    }
    this.Start = Start;
    
    function End(x,y,elm,dotElm,fitElm,doZoomEffect){
        if(true == self.active){
            self.Move(x,y,elm,dotElm,fitElm);
            self.active = false;
        }
        if(elm){
            elm.style["visibility"] = "hidden";
            elm.style["left"] = "0px";
            elm.style["top"] = "0px";
            elm.style["width"] = "0px";
            elm.style["height"] = "0px";
        }
        if(dotElm){
            dotElm.style["visibility"] = "hidden";
            dotElm.style["left"] = "0px";
            dotElm.style["top"] = "0px";
            dotElm.style["width"] = "0px";
            dotElm.style["height"] = "0px";
        }
        if(fitElm){
            var bestfitsize = IMS2_GetElementDimension(fitElm);
            var bestfit = self.GetBestFit(bestfitsize[0],bestfitsize[1]);
            if(bestfit){
                self.x1 = bestfit[0];
                self.y1 = bestfit[1];
                self.x2 = bestfit[2];
                self.y2 = bestfit[3];
            }
        }
        if(doZoomEffect){
            self.fxTarget = elm;
            self.fxAmnt = 0;
            self.FX_ZoomFadeParent();
        }
    }
    this.End = End;
    
    function Move(x,y,elm,dotElm,fitElm){
        if(true == self.active){
            var offx = 0;
            var offy = 0;
            if(IMS2_isMSIE()){
                offx = -2;
                offy = -2;
            }
            self.x2 = x + offx;
            self.y2 = y + offy;
            if(elm){
                var w = 0;
                var h = 0;
                var left = 0;
                var top = 0;
                if(self.x2 >= self.x1){
                    w = self.x2 - self.x1;
                    left = self.x1;
                }else{
                    w = self.x1 - self.x2;
                    left = self.x2;
                }
                if(self.y2 >= self.y1){
                    h = self.y2 - self.y1;
                    top = self.y1;
                }else{
                    h = self.y1 - self.y2;
                    top = self.y2;
                }                
                elm.style["left"] = left + "px";
                elm.style["top"] = top + "px";
                elm.style["width"] = w + "px";
                elm.style["height"] = h + "px";
            }    
            if(fitElm && dotElm){
                var bestfitsize = IMS2_GetElementDimension(fitElm);
                var bestfit = self.GetBestFit(bestfitsize[0],bestfitsize[1]);
                left = parseInt(bestfit[0]);
                top = parseInt(bestfit[1]);
                w = parseInt(bestfit[2] - bestfit[0]);
                h = parseInt(bestfit[3] - bestfit[1]);   
                dotElm.style["left"] = left + "px";
                dotElm.style["top"] = top + "px";
                dotElm.style["width"] = w + "px";
                dotElm.style["height"] = h + "px";
            }
        }
    }
    this.Move = Move;
}

//map object
function IMS2_ClientMap(mapid,mapdatasource){
    var self = this;
    
    this.didInit = false;
    this.mapid = mapid;
    this.mapdatasource = mapdatasource;
    this.imagew = 0;
    this.imageh = 0;
    this.mapUrl = "";
    this.mapImgPreload = new Image();
    this.olddivw = 0;
    this.olddivh = 0;
    this.xmlReq = null;
    this.xmlFeatureReq = null;
    this.xmlIdentifyReq = null;
    this.invalidated = false;
    this.lastInvalidationTick = 0;
    this.views = new IMS2_ViewHistory();
    this.isNewViewRequest = false;
    this.inputmgr = new IMS2_InputManager();
    this.selectBox = new IMS2_ClientMapSelectBox();
    this.layers = new IMS2_LayerList();
    this.layerListControls = new IMS2_LayerListControlManager();
    this.legendControls = new IMS2_LegendControlManager();
    this.selection = new IMS2_SelectionGroup();
    this.lastLegendUri = "";
    this.activeLayer = "";
    this.featureResultSet = null;
    this.drilldownResultSet = null;
    this.selectedAction = "selbox";
    this.curAction = "";
    this.prevAction = "";
    this.unit = "Pixel";
    this.objectIDZoomList = [];
    this.objectIDZoomListLayer = "";
    this.maxscale = 1;
    this.minscale = 0.00000001;
    this.polygonSurface = new IMS2_PolygonDrawing();
    this.pointBuffer = 0.5;
    this.polyPointCurSel = -1;
    this.mapPolyTip = "";
    this.measureRing = new IMS2_PolyRing();
    this.measureRing.closed = false;
    this.createRing = new IMS2_PolyRing();
    this.createRing.closed = false;
    this.lastPolyType = "";
    this.customHandleFeatures = null;
    this.onRightClick = null;
    this.onFeaturesReturned = null;
    this.lastMouseMoveTick = 0;
    this.lastMouseMovex = -1;
    this.lastMouseMovey = -1;
    this.tipatx = -1;
    this.tipaty = -1;
    this.mousein = false;
    this.onmaptip = null;
    this.onPolyCreateDblClick = null;
    this.enableMouseWheelZoom = true;
    this.acetates = new IMS2_AcetateList("_mapacetatediv_" + this.mapid);
    this.fullExtent;
    this.zoomToSelection = false;
    this.printFrame = new IMS2_PrintFrame();
    
    function toString(){
        return("" + self.mapid + "(" + self.mapdatasource +")");
    }
    this.toString = toString;
    
    function MouseIn(){
        self.mousein = true;
    }
    this.MouseIn = MouseIn
    
    function MouseOut(){
        self.mousein = false;
    }
    this.MouseOut = MouseOut
    
        function AddAcetate(x,y){
        return(self.acetates.Add(x,y));
    }
    this.AddAcetate = AddAcetate;
    
    function RemAcetate(val){
        return(self.acetates.Remove(val));
    }
    this.RemAcetate = RemAcetate;
    
    function ClearAcetates(){
        self.acetates.Clear();
    }
    this.ClearAcetates = ClearAcetates;
    
    
    //add a layer to the map that is not included in the axl
    //layer could be from data uploaded by user
    function AddDynamicLayer(id,name,type,dataset,workspace,renderer,visible,existingws){
        var dynlayer = self.layers.Add(id);
        if(dynlayer){
            dynlayer.name = name;
            dynlayer.type = type;
            dynlayer.dataset = dataset;
            dynlayer.workspace = workspace;
            dynlayer.renderer = renderer;
            dynlayer.visible = visible;
            dynlayer.dynamic = true;
            dynlayer.existingws = ((existingws)?(true):(false));
            
            //add layer to "My Layers" Group
            
//            if(!self.layers.GetGroup("My Layers")){
//				self.layers.AddGroup("My Layers")
//            }
            
            //self.layers.GetGroup("My Layers").
            
            //IMS2_InitializeQueryBuilders(self);
        }
        self.InvalidateMNOW();
        return(dynlayer);
    }
    this.AddDynamicLayer = AddDynamicLayer;
    
    function HasLegendControls(){
        if(self.legendControls){
            if(self.legendControls.controls){
                if(self.legendControls.controls.length > 0){
                    return(true);
                }
            }
        }
        return(false);
    }
    this.HasLegendControls = HasLegendControls;
    
    function _HandleCustomRightClick(x,y){
        if(self.onRightClick){
            self.onRightClick(x,y);
            return(true);
        }
        return(false);
    }
    this._HandleCustomRightClick = _HandleCustomRightClick;
    
    function SetActiveLayer(lid){
        self.activeLayer = lid;
        if(self.layerListControls){
            
            self.layerListControls.SetActiveLayer(lid,self.layers);
        }
        if(IMS2_Zoombars){
            IMS2_Zoombars.Update(self);
        }
    }
    this.SetActiveLayer = SetActiveLayer;
    
    function AddLayerListControl(llcid,mapid,refreshOnChange,layerStyle,layerStyleSelected,groupStyle,imgPlus,imgMinus){
        if(self.layerListControls){
            var llc = self.layerListControls.Add(llcid,mapid,refreshOnChange,layerStyle,layerStyleSelected,groupStyle,imgPlus,imgMinus);
            if(llc){
                llc.Update(self.layers);
                return(true);
            }
        }
        return(false);
    }
    this.AddLayerListControl = AddLayerListControl;
    
    function AddLegendControl(id,mapid){
        if(self.legendControls){
            var legendc = self.legendControls.Add(id,mapid);
            if(legendc){
                legendc.UpdateImage(self.lastLegendUri);
                return(true);
            }
        }
        return(false);
    }
    this.AddLegendControl = AddLegendControl;
    
    function MapHistoryMove(delta){
        if(self.views){
            var oldPos = self.views.GetPosition();
            self.views.Move(delta);
            if(oldPos != self.views.GetPosition()){
                self.InvalidateMNOW();
            }
        }
    }
    this.MapHistoryMove = MapHistoryMove;
    
    //get ready for user action, depending on selected tool
    function ChangeAction(action){
        self.CancelInput();
        self.prevAction = self.curAction;
        
        if("selpoly" == action || "selpolydrill" == action){
            self.lastPolyType = "poly";
            
//            //open up the options so user can select which layers to use
//            IMS2_Tasks.Add("opt");
//			task = IMS2_Tasks.Get(IMS2_Tasks.currentTask);
//			while(task.currentIndex != 2){
//				if(task.currentIndex > 2)
//				{
//					task.GetPreviousPage();
//				}else
//				{
//					task.GetNextPage();
//				}
//			}
        }
        if("selbox" == action || "selboxdrill" == action){
            
//            //open up the options so user can select which layers to use
//            IMS2_Tasks.Add("opt");
//			task = IMS2_Tasks.Get(IMS2_Tasks.currentTask);
//			while(task.currentIndex != 2){
//				if(task.currentIndex > 2)
//				{
//					task.GetPreviousPage();
//				}else
//				{
//					task.GetNextPage();
//				}
//			}
        }
        if("measure" == action){
            self.lastPolyType = "measure";
            if(self.measureRing){
                self.measureRing.closed = true;
            }
        }
        if("measuredist" == action){
            self.lastPolyType = "measure";
            if(self.measureRing){
                self.measureRing.closed = false;
            }
        }
        if("createpoly" == action){
            self.lastPolyType = "createpoly";
            if(self.createRing){
                self.createRing.closed = true;
                self.createRing.vertexSize = 4;
            }
        }
        if("createline" == action){
            self.lastPolyType = "createline";
            if(self.createRing){
                self.createRing.closed = false;
                self.createRing.vertexSize = 4;
            }
        }
        if("createpoint" == action){
            self.lastPolyType = "createpoint";
            if(self.createRing){
                self.createRing.closed = false;
                self.createRing.vertexSize = 10;
            }
        }
        if("prevmap" == action){
            self.MapHistoryMove(-1);
        }else if("nextmap" == action){
            self.MapHistoryMove(1);
        }else if("zoomall" == action){
            //var env = self.fullExtent
            var nv = new IMS2_View(self.fullExtent.x1,self.fullExtent.y1,self.fullExtent.x2,self.fullExtent.y2,self.GetWidth(),self.GetHeight());
			if(nv){
				// apply new view
			   self.AddView(nv);
			   self.InvalidateMNOW();
			}
        }else if("zoomlayer" == action){
			var env = self.layers.Get(self.activeLayer).envelope;
			var nv = new IMS2_View(env.x1,env.y1,env.x2,env.y2,self.GetWidth(),self.GetHeight());
			if(nv){
				// apply new view
			   self.AddView(nv);
			   self.InvalidateMNOW();
			}
		}else if("refresh" == action){
            self.InvalidateMNOW();
        }else if("reset" == action){
            window.location.reload();    
        }else if("clear" == action){
//            if(self.views){
////                var cv = self.views.GetCurrent();
////                if(cv){
////                    var nv = cv.Copy();
////                    if(nv){
////                        nv.env = null;
////                        self.AddView(nv);
//                        self.InvalidateMNOW();
//                    //}
//                //}
//            }
			self.ClearAcetates();
            self.ClearSelection();
        }else if("print" == action){
            IMS2_ResultPanes.panes[0].PrintPage(self,null);
        }else{
            self.SetSelectedAction(action);
        }
        self.RefreshPolygonSurface(0,0);
        self.RefreshAcetates(0,0);
    }
    this.ChangeAction = ChangeAction;
    
    function SetSelectedAction(action){
        self.selectedAction = (action)?(action):("");
        if(IMS2_ToolbarManager){
            IMS2_ToolbarManager.ApplyMapAction(self.selectedAction,self.mapid);
        }
        self.CancelInput();
    }
    this.SetSelectedAction = SetSelectedAction;
    
    function DefaultAction(){
        self.ChangeAction("zoomin");
    }
    this.DefaultAction = DefaultAction;
    
    //tell the map to get a new image of itself
    function Invalidate(){
        self.lastInvalidationTick = IMS2_GetTick();
        self.invalidated = true;
        setTimeout("IMS2_TriggerMapRefresh('" + self.mapid + "'," + self.lastInvalidationTick + ");",IMS2_mapActionWaitTime);
    }
    this.Invalidate = Invalidate;
    
    function InvalidateMNOW(){
        self.lastInvalidationTick = IMS2_GetTick();
        self.invalidated = true;
        TriggerRefresh(self.lastInvalidationTick)
    }
    this.InvalidateMNOW = InvalidateMNOW;
    
    function Validate(){
        self.lastInvalidationTick = 0;
        self.invalidated = false;
    }
    this.Validate = Validate;
    
    //figure out if the current request is the most recent
    //this way we can track when the latest process is done
    function IsLatestInvalidationRequest(tick){
        return(tick == self.lastInvalidationTick);
    }
    this.IsLatestInvalidationRequest = IsLatestInvalidationRequest;
    
    function IsProcessingRequest(){
        return(null != self.xmlReq);
    }
    this.IsProcessingRequest = IsProcessingRequest;
    
    function TriggerRefresh(tick){
        if(tick){
            if(IsLatestInvalidationRequest(tick) && self.invalidated){
                if(self.IsProcessingRequest()){
                    self.Invalidate();
                }else{
                    self.Refresh();
                }
            }
        }else{
            self.InvalidateMNOW();
        }
    }
    this.TriggerRefresh = TriggerRefresh;
    
    function Refresh(){
        if(self.didInit){
            self.RefreshFromService();
        }else{
            self.Init();
        }
        self.Validate();
    }
    this.Refresh = Refresh
    
    //change status at top of map to let user know when to proceed
    function UpdateLoadingFeedback(){
        var loadingDiv = GetLoadingMessageTag();
        if(!loadingDiv){
            var mapdiv = self.GetContainingDiv();
            mapdiv.innerHTML = mapdiv.innerHTML + "<div id=\"_loaddiv_" + self.mapid + "\" style=\"position: absolute;top: 0px; left: 0px;\"><i>Loading Map...</i></div>";
            loadingDiv = GetLoadingMessageTag();
        }
        if(loadingDiv){
            var isMap = IsProcessingRequest();
            var isFeature = (null != self.xmlFeatureReq);
            if(isMap || isFeature){
                var loadItems = new Array();
                if(isMap){
                    loadItems[loadItems.length] = "Map";
                }
                if(isFeature){
                    loadItems[loadItems.length] = "Features";
                }
                var loadStr = "";
                for(var i = 0;i < loadItems.length;i++){
                    loadStr += ((i > 0)?(","):("")) + loadItems[i];
                }
                loadStr = "<i>Loading" + ((""==loadStr)?(""):(" (" + loadStr + ")")) + "...</i>";
                loadingDiv.innerHTML = loadStr;
                loadingDiv.style["visibility"] = "visible";
            }else{
                loadingDiv.style["visibility"] = "hidden";
            }
        }
    }
    this.UpdateLoadingFeedback = UpdateLoadingFeedback;
    
    function GetContainingDiv(){
        return(document.getElementById('_mapdiv_' + self.mapid));
    }
    this.GetContainingDiv = GetContainingDiv;
    
    function GetSelectBoxDiv(){
        return(document.getElementById('_mapselectboxdiv_' + self.mapid));
    }
    this.GetSelectBoxDiv = GetSelectBoxDiv;
    
    function GetSelectBoxTrueDiv(){
        return(document.getElementById('_mapselectboxtruediv_' + self.mapid));
    }
    this.GetSelectBoxTrueDiv = GetSelectBoxTrueDiv;
    
    function GetWidth(){
        return(IMS2_GetElementWidth(self.GetContainingDiv()));
    }
    this.GetWidth = GetWidth;
    
    function GetHeight(){
        return(IMS2_GetElementHeight(self.GetContainingDiv()));
    }
    this.GetHeight = GetHeight;
    
    function GetMapImageTag(){
        var mapdiv = self.GetContainingDiv();
        if(mapdiv){
            var imgElm = document.getElementById('_mapimg_' + self.mapid);
            if(imgElm){
                return(imgElm);
            }
            var imgElms = mapdiv.getElementsByTagName("img");
            if(imgElms){
                if(imgElms.length > 0){
                    return(imgElms[0]);
                }
            }
        }
        return(null);
    }
    this.GetMapImageTag = GetMapImageTag;
    
    function GetLoadingMessageTag(){
        var loadingDiv = document.getElementById("_loaddiv_" + self.mapid);
        if(loadingDiv){
            return(loadingDiv);
        }
        return(null);
    }
    this.GetLoadingMessageTag = GetLoadingMessageTag;
    
    function GetMapInputDiv(){
        var loadingDiv = document.getElementById("_mapinputdiv_" + self.mapid);
        if(loadingDiv){
            return(loadingDiv);
        }
        return(null);
    }
    this.GetMapInputDiv = GetMapInputDiv;
    
    function GetAcetateDiv(){
        var acetateDiv = document.getElementById("_mapacetatediv_" + self.mapid);
        return(acetateDiv);
    }
    this.GetAcetateDiv = GetAcetateDiv;
    
    function SetMapImage(mapUrl){
        var mapdiv = self.GetContainingDiv();
        if(mapdiv){
            var imgElm = self.GetMapImageTag();
            if(imgElm){
                self.mapUrl = mapUrl;
                
                if(self.mapImgPreload){
                    self.mapImgPreload.src = mapUrl
                    self.ImagePreLoadCheck();
                }else{
                    self.OnImagePreLoaded();
                }
                RefreshPolygonSurface(0,0);
                RefreshAcetates(0,0);
            }
        }
    }
    this.SetMapImage = SetMapImage;
    
    //when the map changes, the "acetate objects" should as well
    function RefreshAcetates(offx,offy){
        if(!offx){
            offx = 0;
        }
        if(!offy){
            offy = 0
        }
        var acetates = self.acetates.acetates;
        for(var i = 0;i < acetates.length; i++){
            var acetate = acetates[i];
            var pos = MapCoordsToImgCoords([acetate.position],offx,offy);
            //alert("pos: " + pos + "\n" + "acetate: " + acetate + "\n" + "acetate.div: " + acetate.div.style);
            pos = pos[0];
            if(pos){
                var divelm = acetate.div;
                var divdim = IMS2_GetElementDimension(divelm);
                var x = pos[0];
                if("center" == acetate.positionType[0]){
                    x = Math.round(x - (divdim[0] * 0.5));
                }else if("right" == acetate.positionType[0]){
                    x -= divdim[0];
                }
                var y = pos[1];
                if("center" == acetate.positionType[1]){
                    y = Math.round(y - (divdim[1] * 0.5));
                }else if("right" == acetate.positionType[1]){
                    y -= divdim[1];
                }    
                divelm.style["left"] = x + "px";
                divelm.style["top"] = y + "px";
            }
            
            
        }
        
        
    }
    this.RefreshAcetates = RefreshAcetates;
    
    function RefreshPolygonSurface(offx,offy){
        // update drawing
        if(!offx){
            offx = 0;
        }
        if(!offy){
            offy = 0
        }
        /*var testpp = 
        [
            [11655997,3870789],
            [11665277,3864193],
            [11667612,3856313],
            [11665102,3854913],
            [11660549,3850593],
            [11657164,3851702]
        ];
        var testppimg = MapCoordsToImgCoords(testpp,offx,offy);
        self.polygonSurface.DrawPoints(testppimg);*/
        
        var cursel = self.selection.GetSelection(0);
        var drawOK = false;
        var ring = null;
        if(self.lastPolyType == "measure" && self.measureRing){
            ring = self.measureRing;
        }else if(self.lastPolyType == "createpoly" || self.lastPolyType == "createline" || self.lastPolyType == "createpoint" && self.createRing){
            ring = self.createRing;    
        }
        if(cursel && !ring){
            if(cursel.IsPoly() && cursel.ring){
                ring = cursel.ring;
            }
        }
        if(ring && ring.points.length > 0){
            var imgCoords = MapCoordsToImgCoords(ring.points,offx,offy);
            var centroidCoords = MapCoordsToImgCoords([ring.centroid],offx,offy);
            centroidCoords = (centroidCoords && centroidCoords.length > 0)?(centroidCoords[0]):(null);
            self.polygonSurface.DrawPoints(imgCoords,ring,centroidCoords);       
            drawOK = true;
        }
        if(false == drawOK){
            self.polygonSurface.Clear();
        }
    }
    this.RefreshPolygonSurface = RefreshPolygonSurface;
    
    function FinishPolySelection(){
        if("selpoly" == self.selectedAction){
            self.RequestSelectedFeatures();
        }else if("selpolydrill" == self.selectedAction){
            self.RequestSelectedFeaturesDrill();
        }
        self.InvalidateMNOW();
    }
    this.FinishPolySelection = FinishPolySelection;
    
    function ClearSelection(){
        if(self.selection){
            self.selection.Clear();
        }
        if(self.measureRing){
            self.measureRing.Clear();
        }
        if(self.createRing){
            self.createRing.Clear();
        }
        RefreshPolygonSurface(0,0);
        RefreshAcetates(0,0);        
        self.InvalidateMNOW();
        
        //clear results panel
        IMS2_ResultPanes.ClearPanels(self.mapid);
    }
    this.ClearSelection = ClearSelection;
    
    function OnResize(){
            var divw = self.GetWidth();
            var divh = self.GetHeight();
            if(divw != self.olddivw || divh != self.olddivh){
                self.Invalidate();
                self.CenterImage();
                self.olddivw = divw;
                self.olddivh = divh;
            }
            self.PositionBottomDiv();
            self.polygonSurface.Resize(divw,divh);
    }
    this.OnResize = OnResize;
    
    //the always needed method to calculate map units per pixel
    function MapCoordsToImgCoords(inpts,offx,offy){
        var opts = new Array();
        if(inpts){
            var cv = self.views.GetCurrent();
            if(cv){
                var env = cv.env;
                if(env){
                    var imgh = self.GetHeight();
                    var sx = self.GetWidth() / env.GetWidth();
                    var sy = imgh / env.GetHeight();
                    var orgx = env.GetLeft() * sx;
                    var orgy = (env.GetTop()+env.GetHeight()) * sy;
                    
                    var ptcount = inpts.length;
                    if(ptcount > 0){
                        opts[ptcount-1] = null;
                        for(var i = 0;i < ptcount;i++){
                            var x = Math.round((inpts[i][0] * sx) - orgx + offx);
                            var y = Math.round(orgy - (inpts[i][1] * sy) + offy);
                            opts[i] = [x,y];
                        }
                    }
                }
            }
        }
        return(opts);
    }
    this.MapCoordsToImgCoords = MapCoordsToImgCoords;
    
    function PositionBottomDiv(){
        var bottomDiv = document.getElementById("_mapbottomdiv_" + self.mapid);
        if(bottomDiv){
            bottomDiv.style["bottom"] = "0px";
        }
    }
    this.PositionBottomDiv = PositionBottomDiv;
    
    function ImagePreLoadCheck(){
        if(self.mapImgPreload){
            if(self.mapImgPreload.complete){
                self.OnImagePreLoaded();
            }else{
                setTimeout(self.ImagePreLoadCheck,10);
            }
        }else{
            self.OnImagePreLoaded();
        }
    }
    this.ImagePreLoadCheck = ImagePreLoadCheck
    
    function OnImagePreLoaded(){
        var imgElm = self.GetMapImageTag();
        if(imgElm){
            imgElm.src = self.mapUrl;
            imgElm.onload = self.OnImageTagLoad;
            imgElm.style["visibility"] = "visible";
        }
    }
    this.OnImagePreLoaded = OnImagePreLoaded;
    
    function OnImageTagLoad(){
        self.CenterImage();
    }
    this.OnImageTagLoad = OnImageTagLoad;
    
    function OnMouseDown(e){
        if(self.didInit){
            self.inputmgr.HandleMouseDown(e);
        }
        return(false);
    }
    this.OnMouseDown = OnMouseDown;
    
    function OnMouseUp(e){
        if(self.didInit){
            self.inputmgr.HandleMouseUp(e);
        }
        return(false);
    }
    this.OnMouseUp = OnMouseUp;
    
    function OnMouseMove(e){
        if(self.didInit){
            self.inputmgr.HandleMouseMove(e);
        }
        return(false);
    }
    this.OnMouseMove = OnMouseMove;
    
    function OnMouseOut(e){
        if(self.didInit){
            self.inputmgr.HandleMouseOut(e);
            self.MouseOut();
        }
        return(false);
    }
    this.OnMouseOut = OnMouseOut;
    
    function OnMouseWheel(e){
        if(self.didInit){
            self.inputmgr.HandleMouseWheel(e);
        }
        return(false);
    }
    this.OnMouseWheel = OnMouseWheel;
    
	function OnDblClick(e){
        if("selpoly" == self.selectedAction || "selpolydrill" == self.selectedAction){
            FinishPolySelection();
            //self.InvalidateMNOW();
            if(self.selection){
				self.selection.Clear();
			}
			self.RefreshPolygonSurface(0,0);
			//close selection layers options
            var task = IMS2_Tasks.HasType("opt");
            if(task){
				id = task.id;
				IMS2_Tasks.Remove(id);
			}
        }else if("measuredist" == self.selectedAction || "measure" == self.selectedAction || "measuredist" == self.curAction || "measure" == self.curAction){
            self.measureRing.Clear();
            self.RefreshPolygonSurface(0,0);
            self.RefreshAcetates(0,0);
//		This was screwing with sending the coords to BuildAOI			
//        }else if("createpoly" == self.selectedAction || "createline" == self.selectedAction){
//            if(self.onPolyCreateDblClick){
//                self.onPolyCreateDblClick();
//            }
        }
        return(false);
    }
    this.OnDblClick = OnDblClick;
    
    function AdjustPageCoordToInputCoord(x,y){
        var mapindiv = self.GetMapInputDiv();
        if(mapindiv){
            var inpos = IMS2_GetElementPosition(mapindiv);
            return([x - inpos[0],y - inpos[1]]);
        }
        return([0,0]);
    }
    this.AdjustPageCoordToInputCoord = AdjustPageCoordToInputCoord;
    
    function MouseDown(bnum,x,y){
        var imgpos = self.AdjustPageCoordToInputCoord(x,y);
        x = imgpos[0];
        y = imgpos[1];
        
        //if(1 == bnum && (self.selectedAction == "selpoly" || self.selectedAction == "selpolydrill")){
        if(1 == bnum){
            if(self.onRightClick){
                return;
            }
        }
        
        if(!self.IsProcessingRequest()){
            if("" == self.curAction){
                switch(self.selectedAction){
                    case "zoomin":
                        if(0 == bnum){
                            self.curAction = self.selectedAction;
                            self._ZoominInputStart(x,y);
                        }
                        break;
                    case "zoomout":
                        if(0 == bnum){
                            self.curAction = self.selectedAction;
                            self._ZoomoutInputStart(x,y);
                        }
                        break;
                    case "pan":
                        if(0 == bnum){
                            self.curAction = self.selectedAction;
                            self._PanInputStart(x,y);
                        }
                        break;
                    case "selbox":
                    case "selboxdrill":
                        if(0 == bnum){
                            self.curAction = self.selectedAction;
                            self._SelectInputStart(x,y);
                        }
                        break;
                    case "selpoly":
                    case "selpolydrill":
                        if(0 == bnum){
                            self.polyPointCurSel = GetPolySelPoint(x,y);
                        }
                        break;
                    case "measure":    
                    case "measuredist":
                        if(0 == bnum){
                            self.polyPointCurSel = GetMeasureSelPoint(x,y);
                        }
                        break;
                    case "createpoint":
                    case "createline":
                    case "createpoly":
                        if(0 == bnum){
                            self.polyPointCurSel = GetCreateSelPoint(x,y);
                        }
                        break;    
                }
            }
            else{
                switch(self.curAction){
                    case "zoomin":
                    case "zoomout":
                        if(1 == bnum){
                            self.CancelInput();
                        }
                        break;
                    case "selbox":
                    case "selboxdrill":
                        if(1 == bnum){
                            self.CancelInput();
                        }
                        break;
                    case "zoomout":
                        if(1 == bnum){
                            self.CancelInput();
                        }
                        break;
                    case "pan":
                        if(1 == bnum){
                            self.CancelInput();
                        }
                        break;
                }
            }
        }
    }
    this.MouseDown = MouseDown;
    
    function MouseUp(bnum,x,y,click){
        var imgpos = self.AdjustPageCoordToInputCoord(x,y);
        x = imgpos[0];
        y = imgpos[1];
        
        //if(1 == bnum && (self.selectedAction == "selpoly" || self.selectedAction == "selpolydrill")){
        //if(1 == bnum && (self.selectedAction == "selpoly" || self.selectedAction == "selpolydrill")){
        if(1 == bnum){
            if(_HandleCustomRightClick(x,y)){
				//self.CancelInput();
                return;
                
            }
        }
        //}
        //}
        
        if(!self.IsProcessingRequest()){
            if("" != self.curAction){
                switch(self.curAction){
                    case "zoomin":
                        self._ZoominInputEnd(x,y,click);
                        break;
                    case "selbox":
                    case "selboxdrill":
                        self._SelectInputEnd(x,y,click);
                        //close selection layers options
                        var task = IMS2_Tasks.HasType("opt");
                        if(task){
							id = task.id;
							IMS2_Tasks.Remove(id);
						}
                        break;
                    case "zoomout":
                        //self.ZoomFromMap([x,y,2],null);
                        //self.curAction = "";
                        self._ZoomoutInputEnd(x,y,click);
                        break;
                    case "pan":
                        self._PanInputEnd(x,y,click);
                        break;
                }
            }else{
                switch(self.selectedAction){
                    case "selpoly":
                    case "selpolydrill":
                        if(0 == bnum){
                            if(self.polyPointCurSel < 0){
                                self.PolySelPoint(x,y,"add");
                            }else{
                                MovePolySelPoint(x,y,self.polyPointCurSel);
                            }
                        }else if(1 == bnum){
                            self.PolySelPoint(x,y,"delins");
                        }
                        self.polyPointCurSel = -1;
                        break;
                    //case "createpoint":
                    case "createline":
                    case "createpoly":
                    case "createpoint":
                        self.createRing.vertexColor = "yellow";
                        if(0 == bnum){
                            if(self.polyPointCurSel < 0){
								if("createpoint" == self.selectedAction){
									self.CreateSelPoint(x,y,"new");
								}else{
									self.CreateSelPoint(x,y,"add");
								}
                            }else{
                                MoveCreateSelPoint(x,y,self.polyPointCurSel);
                            }
                        }else if(1 == bnum){
                            self.CreateSelPoint(x,y,"delins");
                        }
                        self.polyPointCurSel = -1;
                        break;
                    case "measure":    
                    case "measuredist":
                        if(0 == bnum){
                            if(self.polyPointCurSel < 0){
                                self.MeasureSelPoint(x,y,"add");
                            }else{
                                MoveMeasureSelPoint(x,y,self.polyPointCurSel);
                            }
                        }else if(1 == bnum){
                            self.MeasureSelPoint(x,y,"delins");
                        }
                        self.polyPointCurSel = -1;
                        break;
                    case "identify":
                        var idPt = ImageCoordToMapCoord(x,y);
                        ExecuteIdentify(idPt[0],idPt[1],self.activeLayer + ";",true);
                        break;    
                }
            }
        }
    }
    this.MouseUp = MouseUp;
    
    function GetMeasureSelPoint(x,y){
        var coord = ImageCoordToMapCoord(x,y);
        var ring = self.measureRing;
        if(ring){
            var selPt = ring.GetPointSelection(coord[0],coord[1],self.GetCurrentPxScale());
            return(selPt);
        }            
        return(-1);
    }
    this.GetMeasureSelPoint = GetMeasureSelPoint;
    
    function GetCreateSelPoint(x,y){
        var coord = ImageCoordToMapCoord(x,y);
        var ring = self.createRing;
        if(ring){
            var selPt = ring.GetPointSelection(coord[0],coord[1],self.GetCurrentPxScale());
            return(selPt);
        }            
        return(-1);
    }
    this.GetCreateSelPoint = GetCreateSelPoint;
    
    function MoveMeasureSelPoint(x,y,pnum){
        var coord = ImageCoordToMapCoord(x,y);
        var ring = self.measureRing;
        if(ring){
            if(ring.points && pnum >= 0 && pnum < ring.points.length){
                var pt = ring.points[pnum];
                pt[0] = coord[0];
                pt[1] = coord[1];
            }
            ring.UpdateAttrbutes();
        }            
        self.RefreshPolygonSurface(0,0);
    }
    this.MoveMeasureSelPoint = MoveMeasureSelPoint;
    
    //allow editing of points drawn by user
    function MoveCreateSelPoint(x,y,pnum){
        var coord = ImageCoordToMapCoord(x,y);
        var ring = self.createRing;
        if(ring){
            if(ring.points && pnum >= 0 && pnum < ring.points.length){
                var pt = ring.points[pnum];
                pt[0] = coord[0];
                pt[1] = coord[1];
            }
            ring.UpdateAttrbutes();
        }            
        self.RefreshPolygonSurface(0,0);
    }
    this.MoveCreateSelPoint = MoveCreateSelPoint;
    
    function MeasureSelPoint(x,y,action){
        var coord = ImageCoordToMapCoord(x,y);
        var ring = self.measureRing;
        if(!ring){
            ring = new IMS2_PolyRing();
            ring.closed = false;
        }
        if(ring){
            var isDel = false;
            var isIns = false;
            
            if("del" == action || "delins" == action || "insdel" == action){
                isDel = true;
            }
            if("ins" == action || "delins" == action || "insdel" == action){
                isIns = true;
            }
            
            if(isDel || isIns){
                var selPt = ring.GetPointSelection(coord[0],coord[1],self.GetCurrentPxScale());
                
                if(selPt >= 0 && isDel){
                    ring.DeletePoint(selPt);
                }else if((selPt < 0 || !isDel) && isIns){
                    ring.InsertPoint(coord[0],coord[1]);
                }
            }else{
                ring.AddPoint(coord[0],coord[1]);
            }
        }
        self.RefreshPolygonSurface(0,0);
    }
    this.MeasureSelPoint = MeasureSelPoint;
    
    function CreateSelPoint(x,y,action){
        var coord = ImageCoordToMapCoord(x,y);
        var ring = self.createRing;
        if(!ring){
            ring = new IMS2_PolyRing();
            ring.closed = ((self.lastPolyType=="createline")?(false):(true));
        }
        if(ring){
            var isDel = false;
            var isIns = false;
            
            if("del" == action || "delins" == action || "insdel" == action){
                isDel = true;
            }
            if("ins" == action || "delins" == action || "insdel" == action || "new" == action){
                isIns = true;
            }
            if("new" == action){
				ring.Clear();
            }
            
            if(isDel || isIns){
                var selPt = ring.GetPointSelection(coord[0],coord[1],self.GetCurrentPxScale());
                
                if(selPt >= 0 && isDel){
                    ring.DeletePoint(selPt);
                }else if((selPt < 0 || !isDel) && isIns){
                    ring.InsertPoint(coord[0],coord[1]);
                }
            }else{
                ring.AddPoint(coord[0],coord[1]);
            }
        }
        self.RefreshPolygonSurface(0,0);
    }
    this.CreateSelPoint = CreateSelPoint;
    
    function GetPolySelPoint(x,y){
        var coord = ImageCoordToMapCoord(x,y);
        var topsel = self.selection.GetSelection(0);
        if((topsel && !topsel.IsPoly()) || !topsel){
            topsel = new IMS2_Selection();
            self.selection.Select(topsel,false);
        }
        if(topsel){
            if(topsel.ring && topsel.IsPoly()){
                var selPt = topsel.ring.GetPointSelection(coord[0],coord[1],self.GetCurrentPxScale());
                return(selPt);
            }            
        }
        return(-1);
    }
    
    function MovePolySelPoint(x,y,pnum){
        var coord = ImageCoordToMapCoord(x,y);
        var topsel = self.selection.GetSelection(0);
        if((topsel && !topsel.IsPoly()) || !topsel){
            topsel = new IMS2_Selection();
            self.selection.Select(topsel,false);
        }
        if(topsel){
            if(topsel.ring && topsel.IsPoly()){
                if(topsel.ring.points && pnum >= 0 && pnum < topsel.ring.points.length){
                    var pt = topsel.ring.points[pnum];
                    pt[0] = coord[0];
                    pt[1] = coord[1];
                }
                topsel.ring.UpdateAttrbutes();
            }            
        }
        self.RefreshPolygonSurface(0,0);
    }
    
    function PolySelPoint(x,y,action){
        var coord = ImageCoordToMapCoord(x,y);
        var topsel = self.selection.GetSelection(0);
        if((topsel && !topsel.IsPoly()) || !topsel){
            topsel = new IMS2_Selection();
            self.selection.Select(topsel,false);
        }
        if(topsel){
            if(!topsel.ring){
                topsel.ring = new IMS2_PolyRing();
            }
            if(topsel.ring){
                var isDel = false;
                var isIns = false;
                
                if("del" == action || "delins" == action || "insdel" == action){
                    isDel = true;
                }
                if("ins" == action || "delins" == action || "insdel" == action){
                    isIns = true;
                }
                
                if("dellast" == action){
					topsel.ring.DeleteLastPoint();
					self.RefreshPolygonSurface(0,0);
					return;
                }
                                
                if(isDel || isIns){
                    var selPt = topsel.ring.GetPointSelection(coord[0],coord[1],self.GetCurrentPxScale());
                    
                    if(selPt >= 0 && isDel){
                        topsel.ring.DeletePoint(selPt);
                    }else if((selPt < 0 || !isDel) && isIns){
                        topsel.ring.InsertPoint(coord[0],coord[1]);
                    }
                }else{
                    topsel.ring.AddPoint(coord[0],coord[1]);
                }
            }
            self.RefreshPolygonSurface(0,0);
        }
    }
    this.PolySelPoint = PolySelPoint;
    
    function MouseWheel(val){
        if(val && self.enableMouseWheelZoom){
            var r = val * -2.0;
            if(val > 0){
                r = (-1.0)/r;
            }
            window.status = "" + r + " " + val;
            self.ChangeScaleRatio(r)
            return(false);
        }
        return(true);
    }
    this.MouseWheel = MouseWheel;
    
    function OnHover(tick){
        if(self.lastMouseMoveTick == tick && self.mousein && self.activeLayer && self.onmaptip){
            if(true){
                //window.status = "Requesting...";
                var coord = ImageCoordToMapCoord(self.lastMouseMovex,self.lastMouseMovey);
                if(coord){
                    self.tipatx = self.lastMouseMovex;
                    self.tipaty = self.lastMouseMovey;
                                        
                    var buffer = self.GetCurrentPxScale() * 4;
                    if(buffer > 2000){
                        buffer = 2000;
                    }
                    self.RequestToolTip(coord[0],coord[1],buffer,self.activeLayer);
                }
            }
        }
    }
    this.OnHover = OnHover;
    
    function MouseMove(x,y){
        var imgpos = self.AdjustPageCoordToInputCoord(x,y);
        x = imgpos[0];
        y = imgpos[1];
        
        self.lastMouseMovex = x;
        self.lastMouseMovey = y;
        self.lastMouseMoveTick = IMS2_GetTick();
        self.MouseIn();
        setTimeout("IMS2_TriggerMapMouseHover('" + self.mapid + "'," + self.lastMouseMoveTick + ")",1500);
        //window.status = x + "," + y;
        
        self.SetMouseCoordTip(x,y)
        
        if(!self.IsProcessingRequest()){
            if("" != self.curAction){
                self.curAction = self.selectedAction;
                switch(self.curAction){
                    case "zoomin":
                        self._ZoominInputTick(x,y);
                        break;
                    case "zoomout":
                        self._ZoomoutInputTick(x,y);
                        break;    
                    case "selbox":
                    case "selboxdrill":
                        self._SelectInputTick(x,y);
                        break;
                    case "pan":
                        self._PanInputTick(x,y);
                        break;
                }
            }else{
                switch(self.selectedAction){
                    case "selpoly":
                    case "selpolydrill":
                        if(self.polyPointCurSel >= 0){
                            MovePolySelPoint(x,y,self.polyPointCurSel);
                        }
                        break;
                    case "createline":
                    case "createpoly":
                    case "createpoint":
                        if(self.polyPointCurSel >= 0){
                            MoveCreateSelPoint(x,y,self.polyPointCurSel);
                        }
                        break;
                    case "measure":
                    case "measuredist":
                        if(self.polyPointCurSel >= 0){
                            MoveMeasureSelPoint(x,y,self.polyPointCurSel);
                        }
                        break;
                }
            }
        }
    }
    this.MouseMove = MouseMove;
    
    
    //display map coordinates on map
    function SetMouseCoordTip(mx,my){
        var mouseposDiv = document.getElementById("_mapmousepostip_" + self.mapid);
        if(mouseposDiv){
            var coord = ImageCoordToMapCoord(mx,my);
            if(coord){
                mouseposDiv.innerText = coord[0].toFixed(4) + " , " + coord[1].toFixed(4);
            }
        }
    }
    this.SetMouseCoordTip = SetMouseCoordTip;
    
    function ImageCoordToMapCoord(mx,my){
        var cv = self.views.GetCurrent();
        if(cv){
            var env = cv.env;
            if(env){
                var rx = self.GetWidth();
                var ry = self.GetHeight();
                rx = (0 != rx)?(mx/rx):(1);
                ry = (0 != ry)?(my/ry):(1);
                var x = env.GetLeft() + (env.GetWidth() * rx);
                var y = (env.GetHeight() + env.GetTop()) - (env.GetHeight() * ry);
                return([x,y]);
            }
        }
    }
    this.ImageCoordToMapCoord = ImageCoordToMapCoord;
    
    function CancelInput(){
        if("" != self.curAction){
            //self.curAction = self.selectedAction;
            switch(self.curAction){
                case "zoomin":
                    self.selectBox.End(0,0,GetSelectBoxDiv(),self.GetSelectBoxTrueDiv(),false);
                    break;
                case "selbox":
                case "selboxdrill":
                    self.selectBox.End(0,0,GetSelectBoxDiv(),self.GetSelectBoxTrueDiv(),false);
                    break;
            }
            self.curAction = "";
            self.inputmgr.ResetMouse();
        }
    }
    this.CancelInput = CancelInput;
    
    function _PanInputStart(x,y){
        self.selectBox.x1 = x;
        self.selectBox.y1 = y;
    }
    this._PanInputStart = _PanInputStart;
    
    function _PanInputEnd(x,y,click){
        if("pan" == self.curAction){
            if(click){
                self.ZoomFromMap([x,y,1],null);
            }else{
                var dx = self.selectBox.x1 - x;
                var dy = y - self.selectBox.y1;
                if(self.views){
                    var cv = self.views.GetCurrent();
                    if(cv){
                        var env = cv.env;
                        if(env){
                            var mapdim = IMS2_GetElementDimension(self.GetMapInputDiv());
                            env = env.Copy();
                            dx = (mapdim[0] != 0) ? (dx / mapdim[0]) : 0;
                            dy = (mapdim[1] != 0) ? (dy / mapdim[1]) : 0;
                            dx *= env.GetWidth();
                            dy *= env.GetHeight();
                            env.MoveCenter(dx,dy);
                            var nv = new IMS2_View(env.x1,env.y1,env.x2,env.y2,self.GetWidth(),self.GetHeight()) ;
                            if(nv){
                                // apply new view
                                self.AddView(nv);
                                self.InvalidateMNOW();
                            }
                        }
                    }
                }
            }
            self.curAction = "";
        }
    }
    this._PanInputEnd = _PanInputEnd;
    
    function _PanInputTick(x,y){
        var dx = x - self.selectBox.x1;
        var dy = y - self.selectBox.y1;
        var imgtag = self.GetMapImageTag();
        if(imgtag){
            self.CenterImageWH(IMS2_GetElementWidth(imgtag),IMS2_GetElementHeight(imgtag),dx,dy);
        }
    }
    this._PanInputTick = _PanInputTick;
    
    function _ZoominInputStart(x,y){
        self.selectBox.Start(x,y,self.GetSelectBoxDiv(),self.GetSelectBoxTrueDiv(),self.GetMapInputDiv());
    }
    this._ZoominInputStart = _ZoominInputStart;
    
    function _ZoominInputEnd(x,y,click){
        if("zoomin" == self.curAction){
            self.selectBox.End(x,y,GetSelectBoxDiv(),self.GetSelectBoxTrueDiv(),self.GetMapInputDiv(),true);
            self.curAction = "";
            self.ZoomFromMap(null,self.selectBox);
        }else{
            self.selectBox.End(x,y,GetSelectBoxDiv(),self.GetSelectBoxTrueDiv(),self.GetMapInputDiv(),false);
        }
    }
    this._ZoominInputEnd = _ZoominInputEnd;
    
    function _ZoominInputTick(x,y){
        self.selectBox.Move(x,y,GetSelectBoxDiv(),self.GetSelectBoxTrueDiv(),self.GetMapInputDiv());
    }
    this._ZoominInputTick = _ZoominInputTick;
    
    function _ZoomoutInputStart(x,y){
        self.selectBox.Start(x,y,self.GetSelectBoxDiv(),null,self.GetMapInputDiv());
    }
    this._ZoomoutInputStart = _ZoomoutInputStart;
    
    function _ZoomoutInputEnd(x,y,click){
       if("zoomout" == self.curAction){
            self.selectBox.End(x,y,GetSelectBoxDiv(),null,self.GetMapInputDiv(),true);
            self.curAction = "";
            //self.ZoomFromMap(null,self.selectBox);
            self.ZoomoutBox(self.selectBox,self.GetMapInputDiv());
        }else{
            self.selectBox.End(x,y,GetSelectBoxDiv(),null,self.GetMapInputDiv(),false);
        } 
    }
    this._ZoomoutInputEnd = _ZoomoutInputEnd;
    
    function ZoomoutBox(selBox,mapInputDiv){
        var selw = Math.abs(selBox.x1 - selBox.x2);
        var selh = Math.abs(selBox.y1 - selBox.y2);
        var cv = self.views.GetCurrent();
        var curEnv = cv.env;
        if(selw < 2 || selh < 2){
            var dx = curEnv.GetWidth();
	        var dy = curEnv.GetHeight();
	        var arrPt = ImageCoordToMapCoord(selBox.x1,selBox.y1);
	        var x1 = arrPt[0] - dx;
	        var y1 = arrPt[1] - dy;
	        var x2 = arrPt[0] + parseFloat(dx);
	        var y2 = arrPt[1] + parseFloat(dy); 
        	var nv = new IMS2_View(x1,y1,x2,y2,self.GetWidth(),self.GetHeight());
            self.AddView(nv);
            self.InvalidateMNOW();
        }else{
            var arrPt1 = ImageCoordToMapCoord(selBox.x1,selBox.y1);
	        var arrPt2 = ImageCoordToMapCoord(selBox.x2,selBox.y2);
	        var x1 = arrPt1[0];
	        var y1 = arrPt1[1];
	        var x2 = arrPt2[0];
	        var y2 = arrPt2[1];
	        if (x2 < x1)
	        {
		        tempX = x1;
		        x1 = x2;
		        x2 = tempX;
	        }
	        if (y2 < y1)
	        {
		        tempY = y1;
		        y1 = y2;
		        y2 = tempY;
	        }
	        dx = (curEnv.x2 - curEnv.x1);
	        dy = (curEnv.y2 - curEnv.y1);
	        cx = (x2 - x1) / 2 + parseFloat(x1);
	        cy = (y2 - y1) / 2 + parseFloat(y1);
	        dx = dx * dx / Math.abs(x2 - x1) / 2;
	        dy = dy * dy / Math.abs(y2 - y1) / 2;
	        x1 = cx - dx;
	        y1 = cy - dy;
	        x2 = parseFloat(cx) + parseFloat(dx);
	        y2 = parseFloat(cy) + parseFloat(dy);
	        var nv = new IMS2_View(x1,y1,x2,y2,self.GetWidth(),self.GetHeight());
            self.AddView(nv);
            self.InvalidateMNOW();
        }
    }
    this.ZoomoutBox = ZoomoutBox;
    
    function _ZoomoutInputTick(x,y){
        self.selectBox.Move(x,y,GetSelectBoxDiv(),null,self.GetMapInputDiv());
    }
    this._ZoomoutInputTick = _ZoomoutInputTick;
    
    function _SelectInputStart(x,y){
        self.selectBox.Start(x,y,self.GetSelectBoxDiv(),null,null);
    }
    this._SelectInputStart = _SelectInputStart;
    
    function _SelectInputEnd(x,y,click){
        if("selbox" == self.curAction || "selboxdrill" == self.curAction){
            var cv = self.views.GetCurrent();
            if(cv){
                var curEnv = cv.env;
                if(curEnv){
                    var mapdim = IMS2_GetElementDimension(self.GetMapInputDiv());
                    //var bb = self.selectBox.GetBestFit(mapdim[0],mapdim[1]);
                    var bb = self.selectBox.GetOrderedBounds();
                    if(mapdim && bb){
                        if(click){
                            bb[0] = bb[2] = bb[0] + 0.5;
                            bb[1] = bb[3] = bb[1] + 0.5;
                        }
                        var selEnv = self.GetSelectionEnvelope(curEnv,bb,mapdim);
                        self.SelectBox(selEnv,false);
                        if("selbox" == self.curAction){
                            self.RequestSelectedFeatures();
                        }else if("selboxdrill" == self.curAction){
                            self.RequestSelectedFeaturesDrill();
                        }
                    }   
                }
                self.curAction = "";
            }
        }
        self.selectBox.End(x,y,GetSelectBoxDiv(),self.GetSelectBoxTrueDiv(),null,false);
    }
    this._SelectInputEnd = _SelectInputEnd;
   
    function RequestSelectedFeatures(shortReport){
        var isShort = true;
        if(true == shortReport || false == shortReport){
            isShort = shortReport;
        }
        if(self.selection && !self.selection.layer){
            self.selection.layer = self.activeLayer;
        }
        if(self.selection && self.selection.layer && "" != self.selection.layer){
            self.RequestFeatures("<IMSXML><Identify layer=\"" + self.selection.layer + "\" short=\"" + isShort + "\">" + self.selection.CreateQueryXML(null,self.selection.buffer) + CreateLayersXMLString(self.layers,-1) + "</Identify></IMSXML>");
        }
    }
    this.RequestSelectedFeatures = RequestSelectedFeatures;
    
    function ExecuteIdentify(x,y,layers,isShort)
    {
        if(!layers){
            layers = "*";
        }
        isShort = (false == isShort)?(false):(true); // enforce boolean, prefer true
        var sel = new IMS2_SelectionGroup();
        var selpt = new IMS2_Selection();
        selpt.env = new IMS2_Envelope(x,y,x,y);
        sel.Select(selpt,false);
        sel.buffer = self.pointBuffer;
       
        var cv = self.views.GetCurrent();
		if(cv){
			var rawScale = cv.RawScale();
			if(rawScale < 100.0){
				sel.buffer = rawScale * 5.0; // 5 pixels radius
			}
		}
        self.RequestFeatures("<IMSXML><Identify layer=\"" + layers + "\" short=\"" + isShort + "\">" + sel.CreateQueryXML(null,sel.buffer) + CreateLayersXMLString(self.layers,-1) + "</Identify></IMSXML>");
    }
    this.ExecuteIdentify = ExecuteIdentify;
    
    function RequestSelectedFeaturesDrill(layerOverride,shortReport){
        var sel = selectLayers;
        if((selectLayers == "") || (selectLayers == "ActiveLayer")){
			sel = self.activeLayer + ";";
        }
        if(self.selection && !self.selection.layer){
            self.selection.layer = self.activeLayer;
        }
        var isShort = true;
        if(true == shortReport || false == shortReport){
            isShort = shortReport;
        }
        if(self.selection && self.selection.layer && "" != self.selection.layer){
            self.RequestFeatures("<IMSXML><Identify layer=\"" + sel + "\" short=\"" + isShort + "\">" + self.selection.CreateQueryXML(null,self.selection.buffer) + CreateLayersXMLString(self.layers,-1) + "</Identify></IMSXML>");
		}
	}
    this.RequestSelectedFeaturesDrill = RequestSelectedFeaturesDrill;
    
    function RequestSelectedFeaturesDrill2(layerid){
        //var sel = selectLayers;
        //if((selectLayers == "") || (selectLayers == "ActiveLayer")){
		//	sel = self.activeLayer + ";";
       // }
        if(self.selection && !self.selection.layer){
            self.selection.layer = layerid;
        }
        if(self.selection && self.selection.layer && "" != self.selection.layer){
			//return the short report automatically
            //alert("<IMSXML><Identify layer=\"" + layerid + ";\" short=\"true\">" + self.selection.CreateQueryXML(null,self.selection.buffer) + CreateLayersXMLString(self.layers,-1) + "</Identify></IMSXML>");
            self.RequestFeatures("<IMSXML><Identify layer=\"" + layerid + ";\" short=\"true\">" + self.selection.CreateQueryXML(null,self.selection.buffer) + CreateLayersXMLString(self.layers,-1) + "</Identify></IMSXML>");
        }
    }
    this.RequestSelectedFeaturesDrill2 = RequestSelectedFeaturesDrill2;
    
    function _SelectInputTick(x,y){
        self.selectBox.Move(x,y,GetSelectBoxDiv(),self.GetSelectBoxTrueDiv(),null);
    }
    this._SelectInputTick = _SelectInputTick;
    
    function BeginDrilldown(){
        
    }
    this.BeginDrilldown = BeginDrilldown;
    
    function SelectBox(env,addit){
        if(env && self.selection){
            self.selection.order = null;
            self.selection.buffer = 0;
            
            //only buffer point selection
            if((env.x1 == env.x2) || (env.y1 == env.y2))
            {
				var cv = self.views.GetCurrent();
				if(cv){
					self.selection.buffer = cv.RawScale();
					if(self.selection.buffer){
						self.selection.buffer *= 1.5;
					}
				}
            }
            self.selection.layer = self.activeLayer;
            
            var sel = new IMS2_Selection();
            sel.env = env.Copy();
            self.selection.Select(sel,addit);
            self.Invalidate();  
        }
    }
    this.SelectBox = SelectBox;
    
    function DataQuery(q,addit,order){
        if(q){
            DataQueryLayer(q,self.activeLayer,addit,order);
        }
    }
    this.DataQuery = DataQuery;
    
    function DataQueryLayer(q,lid,addit,order,invalidate){
        if(q){
			//q = q.replace(/&/g, '&amp;');
			q = q.replace(/&/g, '%26amp;');
			q = q.replace(/>/g,'greaterthan');
			q = q.replace(/</g,'lessthan');
			//alert(q);
            var sel = new IMS2_Selection();
            sel.query = q;
            self.selection.order = order;
            self.selection.layer = lid;
            self.selection.Select(sel,addit);
            if(false != invalidate){
				self.Invalidate();
			}
        }
    }
    this.DataQueryLayer = DataQueryLayer;
    
    function GetSelectionEnvelope(env,bb,mapdim){
        var newEnv = null;
        if(env && bb && mapdim){
            var rb = [1,1,1,1];
            rb[0] = bb[0] / mapdim[0];
            rb[2] = bb[2] / mapdim[0];
            rb[1] = bb[1] / mapdim[1];
            rb[3] = bb[3] / mapdim[1];
            var envw = env.GetWidth();
            var envh = env.GetHeight();
            if((bb[0] == bb[2]) && (bb[1] == bb[3])){
                rb[0] = env.x1 + (envw * rb[0]);
                rb[1] = env.y2 - (envh * rb[1]);
                newEnv = new IMS2_Envelope(rb[0],rb[1],rb[0],rb[1]) ;
            }else{
                newEnv = new IMS2_Envelope(env.x1 + (envw * rb[0]),env.y2 - (envh * rb[1]),env.x1 + (envw * rb[2]),env.y2 - (envh * rb[3]));
            }
        }
        return(newEnv);
    }
    this.GetSelectionEnvelope = GetSelectionEnvelope;
    
    function ChangePxScale(scale){
        var curScale = self.GetCurrentPxScale();
        if(curScale && curScale != 0){
            self.ChangeScaleRatio(((scale && scale >= self.minscale)?(scale):(self.minscale)) / curScale);
        }
    }
    this.ChangePxScale = ChangePxScale;
    
    function UpdateScaleInfo(){
        var curScale = self.GetCurrentPxScale();
        if(curScale > self.maxscale){
            self.maxscale = curScale;
        }
    }
    this.UpdateScaleInfo = UpdateScaleInfo;
    
    function AddView(nv){
        if(nv){
            self.views.Add(nv);
            self.UpdateScaleInfo();
        }
    }
    this.AddView = AddView;
    
    function SetView(nv){
        if(nv){
            self.views.ReplaceCurrent(nv);
            self.UpdateScaleInfo();
        }
    }
    this.SetView = SetView;
    
    function ChangeScaleRatio(r){
        var cv = self.views.GetCurrent();
        var nv = null;
        if(cv && cv.env){
            nv = cv.Copy();
            nv.env.ScaleDimensions(r,r)
        }
        if(nv){
            self.AddView(nv);
            self.InvalidateMNOW();
        }
    }
    this.ChangeScaleRatio = ChangeScaleRatio;
    
    function ZoomFromMap(ptout,selbox){
        var doRefresh = false;
        var cv = self.views.GetCurrent();
        var nv = null;
        if(cv){
            if(ptout){
                // zoom out from point
                // scale envelope by ptout[2], then recenter
                var env = cv.env;
                if(env){
                    var mapdim = IMS2_GetElementDimension(self.GetMapInputDiv());
                    var sx = (mapdim[0] != 0) ? (((mapdim[0] * 0.5) - ptout[0]) / mapdim[0]) : 0;
                    var sy = (mapdim[1] != 0) ? (((mapdim[1] * 0.5) - ptout[1]) / mapdim[1]) : 0;
                    sx = env.GetWidth() * -sx;
                    sy = env.GetHeight() * sy;
                    env = env.Copy();
                    env.MoveCenter(sx,sy);
                    if(ptout != 1){
                        env.ScaleDimensions(ptout[2],ptout[2]);
                    }
                    nv = new IMS2_View(env.x1,env.y1,env.x2,env.y2,self.GetWidth(),self.GetHeight()) ;
                }
            }else if(selbox){
                // zoom in using box or pt
                var mapdim = IMS2_GetElementDimension(self.GetMapInputDiv());
                var bb = self.selectBox.GetBestFit(mapdim[0],mapdim[1]);
                var curEnv = cv.env;
                if(curEnv){
                    var envw = curEnv.GetWidth();
                    var envh = curEnv.GetHeight();
                    var newEnv = null;
                    if((bb[0] == bb[2]) && (bb[1] == bb[3])){
                        // point
                        bb[0] = bb[0] / mapdim[0];
                        bb[2] = bb[2] / mapdim[0];
                        bb[1] = bb[1] / mapdim[1];
                        bb[3] = bb[3] / mapdim[1];
                        bb[0] = curEnv.x1 + (envw * bb[0]);
                        bb[1] = curEnv.y2 - (envh * bb[1]);
                        envw = envw * 0.25;
                        envh = envh * 0.25;
                        newEnv = new IMS2_Envelope(bb[0] - envw,bb[1] - envh,bb[0] + envw,bb[1] + envh) ;
                    }else{
                        // box
                        newEnv = self.GetSelectionEnvelope(curEnv,bb,mapdim);
                    }
                    if(newEnv){
                        nv = new IMS2_View(newEnv.x1,newEnv.y1,newEnv.x2,newEnv.y2,self.GetWidth(),self.GetHeight());
                    }
                }
            }
        }
        if(nv){
            // apply new view
            self.AddView(nv);
            self.InvalidateMNOW();
        }
    }
    this.ZoomFromMap = ZoomFromMap;
    
    function CenterImageWH(imgw,imgh,dx,dy){
        var imgtag = self.GetMapImageTag();
        if(imgtag){
            var divw = self.GetWidth();
            var divh = self.GetHeight();
            if(divw > 0 && divh > 0 && imgw > 0 && imgh > 0){
                var offx = (divw - imgw) / 2;
                var offy = (divh - imgh) / 2;
                if(dx){
                    offx += dx;
                }
                if(dy){
                    offy += dy;
                }
                imgtag.style["left"] = "" + offx + "px";
                imgtag.style["top"] = "" + offy + "px";
                RefreshPolygonSurface(offx,offy);
                RefreshAcetates(offx,offy);
            }
        }
    }
    this.CenterImageWH = CenterImageWH;
    
    function CenterImage(){
        var imgtag = self.GetMapImageTag();
        if(imgtag){
            self.CenterImageWH(IMS2_GetElementWidth(imgtag),IMS2_GetElementHeight(imgtag),0,0);
            RefreshPolygonSurface(0,0);
            RefreshAcetates(0,0);
        }
    }
    this.CenterImage = CenterImage;
    
    function InitRequest(getLayers){
        var initXMLRequest = "<IMSXML>"
        if(!(self.layers.didInit) && getLayers){
            initXMLRequest += "<Layers init=\"True\" datasource=\"" + self.mapdatasource + "\" />"
        }
        initXMLRequest += "<Map datasource=\"" + self.mapdatasource + "\" mapid=\"" + self.mapid + "\" imgw=\"" + self.GetWidth() + "\" imgh=\"" + self.GetHeight()+ "\" legend=\"" + ((self.HasLegendControls())?("True"):("False")) + "\" />"
        initXMLRequest += "</IMSXML>";
        self.MakeMapRequest(initXMLRequest,true);
    }
    this.InitRequest = InitRequest;
    
    //attach events to map object
    function Init(){
        self.olddivw = self.GetWidth();
        self.olddivh = self.GetHeight();
        var mapdiv = self.GetContainingDiv();
        var mapinputdiv = self.GetMapInputDiv();
        self.polygonSurface.Init(document.getElementById("_mappolygondiv_" + self.mapid),self.mapPolyTip);
        self.polygonSurface.Clear();
        if(mapdiv){
            IMS2_AttachEvent(mapdiv,"resize",function(){IMS2_OnMapEvent(self.mapid,"resize",null);});
            IMS2_AttachEvent(mapdiv,"dragstart",function(){return(false);});
            IMS2_AttachEvent(mapdiv,"drag",function(){return(false);});
            IMS2_AttachEvent(mapdiv,"selectstart",function(){return(false);});
            IMS2_AttachEvent(mapdiv,"contextmenut",function(){return(false);});
        }
        if(mapinputdiv){
            //IMS2_AttachEvent(mapinputdiv,"click",function(e){IMS2_OnMapEvent(self.mapid,"click",e);});
            IMS2_AttachEvent(mapinputdiv,"mousedown",function(e){IMS2_OnMapEvent(self.mapid,"mousedown",e);});
            IMS2_AttachEvent(mapinputdiv,"mouseup",function(e){IMS2_OnMapEvent(self.mapid,"mouseup",e);});
            IMS2_AttachEvent(mapinputdiv,"mousemove",function(e){IMS2_OnMapEvent(self.mapid,"mousemove",e);});
            IMS2_AttachEvent(mapinputdiv,"mouseout",function(e){IMS2_OnMapEvent(self.mapid,"mouseout",e);});
            IMS2_AttachEvent(mapinputdiv,"mousewheel",function(e){return(IMS2_OnMapEvent(self.mapid,"mousewheel",e));});
            IMS2_AttachEvent(mapinputdiv,"dragstart",function(){return(false);});
            IMS2_AttachEvent(mapinputdiv,"drag",function(){return(false);});
            IMS2_AttachEvent(mapinputdiv,"selectstart",function(){return(false);});
            IMS2_AttachEvent(mapinputdiv,"contextmenut",function(){return(false);});
            IMS2_AttachEvent(mapinputdiv,"dblclick",function(e){IMS2_OnMapEvent(self.mapid,"dblclick",e);});
        }
        if(self.inputmgr){
            self.inputmgr.onMouseDown = self.MouseDown;
            self.inputmgr.onMouseUp = self.MouseUp;
            self.inputmgr.onMouseMove = self.MouseMove;
            self.inputmgr.onMouseWheel = self.MouseWheel;
        }
        self.InitRequest(true);
    }
    this.Init = Init;
    
    
    function RefreshFromServiceString(w,h,s){
        
        var curView = null;
        var curw = self.GetWidth();
		var curh = self.GetHeight();
        if(w && h){
            curw = w;
            curh = h;
            curView = self.views.GetCurrent().Copy();
            if(curView){
                curView.ResizeImage(w,h,((s)?(true):(false)));
            }
            if(s)
            {
                var r = s / curView.RawScale();
                curView.env.ScaleDimensions(r,r);
            }
        }else{
            curView = self.GetAdjustedView();
            curw = self.GetWidth();
		    curh = self.GetHeight();
        }
		var refreshXMLRequest = "<IMSXML>";
		if(curw && curh && curw != 0 && curh != 0){
			refreshXMLRequest += "<Map datasource=\"" + self.mapdatasource + "\" mapid=\"" + self.mapid + "\" imgw=\"" + curw + "\" imgh=\"" + curh + "\" legend=\"" + ((self.HasLegendControls())?("True"):("False")) + "\" >";
			if(self.layers){
				refreshXMLRequest += self.CreateLayersXMLString(self.layers,curView.RawScale());
			}
			if(curView){
				refreshXMLRequest += self.CreateEnvelopeXMLString(curView.env);
			}
			if(self.selection && self.selection.layer){
				refreshXMLRequest += CreateSelectionHighlightString(self.selection);
			}
			refreshXMLRequest += "</Map>";

			self.objectIDZoomList = null;
		}
		refreshXMLRequest += "</IMSXML>";
		return(refreshXMLRequest);
    }
    this.RefreshFromServiceString = RefreshFromServiceString;
    
    function RefreshFromService(){
		self.MakeMapRequest(self.RefreshFromServiceString(),false);

	}
	this.RefreshFromService = RefreshFromService;

    
    function GetAdjustedView(){
        var nv = self.views.GetCurrent();
        if(nv){
            var curw = self.GetWidth();
            var curh = self.GetHeight();
            nv.ResizeImage(self.imagew,self.imageh,false);
            if(nv.imgw != curw || nv.imgh != curh){
                nv.ResizeImage(curw,curh,true);
            }
        }
        return(nv);
    }
    this.GetAdjustedView = GetAdjustedView;
    
    function GetCurrentPxScale(){
        if(self.views){
            var cv = self.views.GetCurrent();
            if(cv){
                return(cv.RawScale());
            }
        }
        return(null);
    }
    this.GetCurrentPxScale = GetCurrentPxScale;
    
    function CreateSelectionHighlightString(sel){
        var retXML = "";
        if(sel){
            var highlightXML = sel.CreateQueryXML(null,self.selection.buffer);
            if(highlightXML && highlightXML != ""){
                retXML = "<Highlight layer=\"" + sel.layer + "\">" + highlightXML + "</Highlight>";
            }
        }
        return(retXML);
    }
    this.CreateSelectionHighlightString = CreateSelectionHighlightString;
    
    function ZoomToFeatures(layer,ids){
        self.objectIDZoomListLayer = layer;
        if(ids){
            if(ids.length > 0){
                self.objectIDZoomList = new Array();
                for(var i = 0;i < ids.length; i++){
                    self.objectIDZoomList[i] = ids[i];
                }
            }
            self.InvalidateMNOW();
        }
    }
    this.ZoomToFeatures = ZoomToFeatures;
    
    function ZoomToSelection(layer)
    {
        self.zoomToSelection = true;
        self.objectIDZoomListLayer = layer;
        self.InvalidateMNOW();
    }
    this.ZoomToSelection = ZoomToSelection;
    
    function CreateEnvelopeXMLString(env){
        var retXML = "";
        if(((self.objectIDZoomList && self.objectIDZoomList.length > 0) || (true == self.zoomToSelection)) && self.objectIDZoomListLayer){
            
            if(true == self.zoomToSelection)
            {
                self.zoomToSelection = false;
                retXML = "<ZoomToSelection layer=\"" + self.objectIDZoomListLayer + "\" featureBuffer=\"" + self.pointBuffer + "\">" + self.selection.CreateQueryXML(null,self.selection.buffer) + "</ZoomToSelection>";
            }else{
                ids = "";
                for(var i = 0;i < self.objectIDZoomList.length; i++){
                    ids += "<Feature id=\"" + self.objectIDZoomList[i] + "\" />";
                }
                retXML = "<ZoomToFeatures layer=\"" + self.objectIDZoomListLayer + "\" featureBuffer=\"" + self.pointBuffer + "\">" + ids + "</ZoomToFeatures>";
            }
        }
        else if(env){
            retXML = "<Envelope x1=\"" + env.x1 + "\" y1=\"" + env.y1 + "\" x2=\"" + env.x2 + "\" y2=\"" + env.y2 + "\" />";
        }
        return(retXML);
    }
    this.CreateEnvelopeXMLString = CreateEnvelopeXMLString;
    
    function CreateLayersXMLString(ll,scale){
        var retXML = "";
        if(ll){
            if(ll.layers){
                if(ll.layers.length > 0){
                    retXML += "<Layers>";
                    for(var i = 0;i < ll.layers.length;i++){
                        var curl = ll.layers[i];
                        if(curl){
                            var drawLayer = curl.visible;
                            if(scale && (scale >= 0)){
                                drawLayer = curl.visible;// & curl.isScaleWithin(scale);
                            }
                            
                            if(curl.dynamic){
								
                                retXML += "<Layer id=\"" + curl.id + "\" visible=\"" + ((drawLayer)?("true"):("false")) + "\" dynamic=\"true\"" +
                                " name=\"" + curl.name + "\"" +
                                " dataset=\"" + curl.dataset + "\"" +
                                " workspace=\"" + curl.workspace + "\"" +
                                " existingws=\"" + curl.existingws + "\"" +
                                " type=\"" + curl.type + "\" >";
                                if(curl.defQuery){
									retXML += "<SPATIALQUERY where=\"" + curl.defQuery + "\" />";
                                }
                                
                                retXML += "<Renderer>" + curl.renderer + "</Renderer></Layer>";
                                    
                            }else{
                                retXML += "<Layer id=\"" + curl.id + "\" visible=\"" + ((drawLayer)?("true"):("false")) + "\" />";
                            }
                        }
                    }
                    retXML += "</Layers>";
                }
            }
        }
        return(retXML);
    }
    this.CreateLayersXMLString = CreateLayersXMLString;
    
    //get map tip info from ims
    function RequestToolTip(x,y,b,layer){
        var requestMade = false;
        if(null == self.xmlIdentifyReq){
            self.xmlIdentifyReq = IMS2_createXMLHTTPRequest();
            if(self.xmlIdentifyReq){
                var url = IMS_webserviceUrl + "/Tip";
                var reqXMLString = new String("");
                reqXMLString = reqXMLString.replace('%', '%25');
                self.xmlIdentifyReq.onreadystatechange = self.ProcessToolTipRequest;
                self.xmlIdentifyReq.open("POST", url, true);
                self.xmlIdentifyReq.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
                self.xmlIdentifyReq.send("service=" + self.mapdatasource + "&x=" + x + "&y=" + y + "&buffer=" + b + "&layer=" + layer);
                requestMade = true;
                self.UpdateLoadingFeedback();
            }
        }
        return(requestMade);
    }
    this.RequestToolTip = RequestToolTip;
    
    function ProcessToolTipRequest(){
        if(self.xmlIdentifyReq){
            if( self.xmlIdentifyReq.readyState == 4 )
		    {
			    if( self.xmlIdentifyReq.status == 200 )
			    {
			        if(self.xmlIdentifyReq.responseXML.normalize){
			            self.xmlIdentifyReq.responseXML.normalize();
			        }
			        var root = self.xmlIdentifyReq.responseXML.documentElement;   
			        
			        if(!root.childNodes[0]){
			            self.featureResultSet = null;
			        }else{
			            if(root.childNodes[0].nodeValue){
			                var drill = false;
			                var data = null;
			                try{
			                    eval("data = " + root.childNodes[0].nodeValue + ";");			            
			                }catch(e){
			                    data = null;
			                }
			                if(self.onmaptip){
			                    self.onmaptip(self.tipatx,self.tipaty,data);
			                }
			            }
			        }
			    }else{
			        alert("Connection error: " + self.xmlIdentifyReq.status + "\n" + self.xmlIdentifyReq.statusText + "\n" + self.xmlIdentifyReq.responseText);
			    }
			    try{
			        self.xmlIdentifyReq.abort();
			    }catch(e){;}
			    self.xmlIdentifyReq = null;
			    self.UpdateLoadingFeedback();
			}
        }
    }
    this.ProcessToolTipRequest = ProcessToolTipRequest;
    
    
    //send request to server to get features from selection
    function RequestFeatures(reqXML){
        var requestMade = false;
        if(null == self.xmlFeatureReq){
            self.xmlFeatureReq = IMS2_createXMLHTTPRequest();
            if(self.xmlFeatureReq){
                var url = IMS_webserviceUrl + "/Identify";
                var reqXMLString = new String(reqXML);
                reqXMLString = reqXMLString.replace(/%/g, '%25');
				self.xmlFeatureReq.onreadystatechange = self.ProcessFeaturesRequest;
                self.xmlFeatureReq.open("POST", url, true);
                self.xmlFeatureReq.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
                self.xmlFeatureReq.send("service=" + self.mapdatasource + "&queryXml=" + reqXMLString);
                //alert("request: " + "service=" + self.mapdatasource + "&queryXml=" + reqXMLString);
                requestMade = true;
                self.UpdateLoadingFeedback();
            }
        }
        return(requestMade);
    }
    this.RequestFeatures = RequestFeatures;
    
    function ProcessFeaturesRequest(){
        if(self.xmlFeatureReq){
            if( self.xmlFeatureReq.readyState == 4 )
		    {
			    if( self.xmlFeatureReq.status == 200 )
			    {
			        if(self.xmlFeatureReq.responseXML.normalize){
			            self.xmlFeatureReq.responseXML.normalize();
			        }
			        var root = self.xmlFeatureReq.responseXML.documentElement;   
			        
			        if(!root.childNodes[0]){
			            self.featureResultSet = null;
			        }else{
			            if(root.childNodes[0].nodeValue){
			                var drill = false;
			                var data = null;
			                try{
			                    eval("data = " + root.childNodes[0].nodeValue + ";");			            
			                }catch(e){
			                    data = null;
			                }
			                drill = true;
			                if(data){
			                    if(data.Values){
			                        drill = false;
			                    }
			                }
			                //drill = ((data.Values)?false:true);
			                if(self.customHandleFeatures){
			                    self.customHandleFeatures(data,drill);
			                }else{
			                    if(drill){
			                        self.drilldownResultSet = data;
			                    }else{
			                        self.featureResultSet = data;
			                    }
			                    IMS2_TriggerResultPaneReset(self.mapid);
								IMS2_TriggerResultPaneUpdate(self.mapid);
								if(self.onFeaturesReturned){
									self.onFeaturesReturned();
								}
			                }
			                
			            }
			        }
			    }else{
			        alert("Connection error: " + self.xmlFeatureReq.status + "\n" + self.xmlFeatureReq.statusText + "\n" + self.xmlFeatureReq.responseText);
			    }
			    try{
			        self.xmlFeatureReq.abort();
			    }catch(e){;}
			    self.xmlFeatureReq = null;
			    self.UpdateLoadingFeedback();
			}
        }
    }
    this.ProcessFeaturesRequest = ProcessFeaturesRequest;
    
    function MakeMapRequest(reqXML,force){
        var requestMade = false;
        if(!self.IsProcessingRequest() || true == force){
            self.xmlReq = IMS2_createXMLHTTPRequest();
            if(self.xmlReq){
                var reqXMLString = new String(reqXML);
                reqXMLString = reqXMLString.replace('%', '%25');
                var url = IMS_webserviceUrl + "/ClientMapRequest";
                self.xmlReq.onreadystatechange = self.ProcessMapRequest;
                self.xmlReq.open("POST", url, true);
                self.xmlReq.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
                self.xmlReq.send("reqXMLString=" + reqXMLString);
                requestMade = true;
                self.UpdateLoadingFeedback();
            }
        }
        return(requestMade);
    }
    this.MakeMapRequest = MakeMapRequest;
 
    function ProcessMapRequest(){
        if(self.xmlReq){
            if( self.xmlReq.readyState == 4 )
		    {
			    if( self.xmlReq.status == 200 )
			    {
			        var root = self.xmlReq.responseXML.documentElement;   
			        if(root){
			            for(var childi = 0; childi < root.childNodes.length; childi++){
			                self.HandleMapRequestNode(root.childNodes[childi]);
			            }
			        }
			    }else{
			        alert("Connection error: " + self.xmlReq.status + "\n" + self.xmlReq.statusText + "\n" + self.xmlReq.responseText);
			    }
			    try{
			        self.xmlReq.abort();
			    }catch(e){;}
			    
			    self.didInit = true;
			    self.xmlReq = null;
			    self.UpdateLoadingFeedback();
			}
        }
    }
    this.ProcessMapRequest = ProcessMapRequest;
    
    function HandleMapRequestNode(node){
        if(node){
            if("Map" == node.tagName){
                self.HandleMapRequestNode_Map(node);
            }
            else if("Layers" == node.tagName){
                self.HandleMapRequestNode_Layers(node);
            }
        }
        self.layers.UpdateScales(self.GetCurrentPxScale());
        self.layerListControls.Update(self.layers);
    }
    this.HandleMapRequestNode = HandleMapRequestNode;
    
    function HandleMapRequestNode_Layers(node){
        if(self.layers){
            try{
                self.unit = node.attributes.getNamedItem("unit").value;
                self.minscale = IMS2_ConvertValueLengthUnit(1,"pixel",self.unit);
            }catch(e){;}
            self.layers.Update(node,self.GetCurrentPxScale());
            //IMS2_InitializeQueryBuilders(self);
        }
    }
    this.HandleMapRequestNode_Layers = HandleMapRequestNode_Layers;
    
    function HandleMapRequestNode_Map(node){
        if(node){
            var mapImgUrl = null;
            var legendUrl = null;
            var iw = 0;
            var ih = 0;
            var x1 = 0;
            var y1 = 0;
            var x2 = 0;
            var y2 = 0;
            //try{
                mapImgUrl = node.attributes.getNamedItem("imgurl").value;
                self.SetMapImage(mapImgUrl);
            //}catch(e){;}
            //try{
                iw = parseInt(node.attributes.getNamedItem("imgw").value);
                ih = parseInt(node.attributes.getNamedItem("imgh").value);
                self.imagew = iw;
                self.imageh = ih;
            //}catch(e){;}
            try{
                legendUrl = node.attributes.getNamedItem("legendurl").value;
            }catch(e){;}
            //try{
                for(var childi = 0; childi < node.childNodes.length; childi++){
                    var curChildNode = node.childNodes[childi];
                    if(curChildNode){
                        if("Envelope" == curChildNode.tagName){
                            x1 = parseFloat(curChildNode.attributes.getNamedItem("x1").value);
                            y1 = parseFloat(curChildNode.attributes.getNamedItem("y1").value);
                            x2 = parseFloat(curChildNode.attributes.getNamedItem("x2").value);
                            y2 = parseFloat(curChildNode.attributes.getNamedItem("y2").value);
                            
                            //if this is the first response, store the enevlope for zoom to extent
							if(!self.didInit){
								self.fullExtent = new IMS2_Envelope(x1,y1,x2,y2);
							}
                        }
                        else if("Layers" == curChildNode.tagName){
                            self.HandleMapRequestNode_Layers(curChildNode);
                        }
                    }
                }
            //}catch(e){;}
            var v = new IMS2_View(x1,y1,x2,y2,iw,ih);
            if(self.views){
                if(false == self.isNewViewRequest){
                    self.SetView(v);       
                }else{
                    self.AddView(nv);
                }
                if(IMS2_Zoombars){
                    IMS2_Zoombars.Update(self);
                }
            }
            if(legendUrl){
                self.lastLegendUri = legendUrl;
                if(self.legendControls){
                    self.legendControls.UpdateImage(legendUrl);
                }
            }
        }
    }
    this.HandleMapRequestNode_Map = HandleMapRequestNode_Map;
    
    function ZoomToPoint(x,y){
		//buffer envelope by 10000 meters
		var zoomTolerance = 10000;
		var nv = new IMS2_View(x - zoomTolerance,y - zoomTolerance,parseInt(x) + zoomTolerance,parseInt(y) + zoomTolerance,self.GetWidth(),self.GetHeight()) ;
        if(nv){
            // apply new view
            self.AddView(nv);
            self.ClearAcetates();

            var acet = self.AddAcetate(x,y);
			acet.div.innerHTML="<img src=\"images/star.gif\" />";

            self.InvalidateMNOW();
        }
    }
    this.ZoomToPoint = ZoomToPoint;

}

// -------------------------- MORE GLOBALS

var IMS2_MapManager = new IMS2_ClientMapManager();
