
var trac = null;


var IconsAvailable = new Array(0,100,102,103,104,106,107,108,116,145,146,150,43,47,49,50,51,52,53,54,55,56,57,58,59,60,61,63,64,65,66,67,68,69,70,71,73,84,86,87,88,90,92,95,97,98,99);

var gPoints = false;
var gDatabase = false;
var gMenu = null;
var nsInit = false;
var nsInit2 = false;

var routeLayer = null;
var routeMarkerLayer = null;

var MapArray = new Array();

function CMapArrayElement(ctrlname, mapobj)
{
	this.ctrlname = ctrlname;
	this.mapobj = mapobj;
}


function GetIconName(cat)
{
    var icon = "0";

    for (i=0;i<IconsAvailable.length && icon=="0";i++)
        if (IconsAvailable[i]*1==cat*1) icon = cat;
    
    var iconname=ScriptBase()+"icon_small/"+icon+".png";
    return iconname;
}


function addLoadEvent(func) {
  var oldonload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      if (oldonload) {
        oldonload();
      }
      func();
    }
  }
}



// class from http://www.theurer.cc/blog/2005/12/15/web-services-json-dump-your-proxy/

function JSONscriptRequest(fullUrl) {
  // REST request path
  this.fullUrl = fullUrl;
  // Keep IE from caching requests
  this.noCache = "&noCache=" + (new Date()).getTime();
  // Get the DOM location to put the script tag
  this.headLoc = document.getElementsByTagName("head").item(0);
  // Generate a unique script tag id
  this.scriptId = "YJscriptId" + JSONscriptRequest.scriptCounter++;
}

// Static script ID counter
JSONscriptRequest.scriptCounter = 1;

// buildScriptTag method
JSONscriptRequest.prototype.buildScriptTag = function () {
  // Create the script tag
  this.scriptObj = document.createElement("script");

  // Add script object attributes
  this.scriptObj.setAttribute("type", "text/javascript");
  this.scriptObj.setAttribute("src", this.fullUrl + this.noCache);
  this.scriptObj.setAttribute("id", this.scriptId);
}

// removeScriptTag method
JSONscriptRequest.prototype.removeScriptTag = function () {
  // Destroy the script tag
  this.headLoc.removeChild(this.scriptObj);
}

// addScriptTag method
JSONscriptRequest.prototype.addScriptTag = function () {
  // Create the script tag
  this.headLoc.appendChild(this.scriptObj);
}

JSONscriptRequest.prototype.Go = function (progress) {
	if (!isUndefined(progress) && progress==true) gWait.Start();
	this.buildScriptTag();
	this.addScriptTag();
}


/**
 * CAatMap is the main class which will be used to interface with the mapping.
 * @constructor
 */
function CAatMap()
{
	this.csDebug = "";
	this.cDebug = null;
	this.wDebug = null;
	this.colBorder = "#888888";
	
	this.sGrabCursor = "url(http://www.streetmaps.co.za/cursors/handgrab.cur),auto";
	this.sHandCursor = "url(http://www.streetmaps.co.za/cursors/hand.cur),auto";
	this.sMagnifyCursor = "url(http://www.streetmaps.co.za/cursors/magnify.cur),auto";
	
	if (BrowserDetect.browser=="Opera") {
		this.sGrabCursor = "move";
		this.sHandCursor = "hand";
		this.sMagnifyCursor = "crosshair";
	}

	this.ClickCallback = null;
	this.APIKey = "";
	
	this.bDragging = false;
	this.bNoBranding = false;
	
	this.aOverride = new Array();
	
	this.nTileZoom = 0.01;
	this.nTileSize = 300;
	this.nTilePixels = this.nTileSize;
	this.nZoom = 0;
	this.nX = 30.85;
	this.nY = -29.78;
	
	this.bShowProgress = false;
	this.bShowGreyButtons = true;
	
	this.nOffsetX = 0;
	this.nOffsetY = 0;
	this.nClickX = 0;
	this.nClickY = 0;
	this.nOrigClickX = 0;
	this.nOrigClickY = 0;
	this.nZIndexBase = 0;
	
	this.nStatusSerial = 0;
	
	this.cScratch = null;

	this.gridTop = 0;
	this.gridLeft = 0;
	this.gridRight = 0;
	this.gridBottom = 0;
	
	this.nCtrlWidth = 0;
	this.nCtrlHeight = 0;
	this.nCtrlLeft = 0;
	this.nCtrlTop = 0;
	this.nCtrlAbsLeft = 0;
	this.nCtrlAbsTop = 0;
	
	this.cellCounter = 1000;

	this.csContainer = "";
	this.cContainer = null;

	this.cOutput = null;

	this.cControls = null;

	this.csCopyright = "&copy; 2007-2008";

	this.gAds = true;

	
	this.cCopyright = null;
	this.nCopyrightXMargin = 0;


	this.csBranding = "http://www.streetmaps.co.za/img/is.png";
	this.nBranding_width=64; 
	this.nBranding_height=37; 
	this.csBrandingTooltip = "In Association with Internet Solutions";
	this.csBrandingUrl = "http://www.is.co.za";


	this.cBranding = null;
	this.PositionCopyright = fPositionCopyright;
	this.cSlider = null;
	this.nScaleXOffset = 0;
	
	this.RedrawCallback = null;
	
	this.nSliderPressed = false;
	this.nSliderZoom = 0;
	
	this.PopupMenuCallback = null;
	
	this.aLayers = new Array();
	
	this.hybridLayer = null;
	this.numbersLayer = null;

	this.routeStartX=0;
	this.routeStartY=0;
	this.routeEndX=0;
	this.routeEndY=0;
	
	this.KeyDeltaX = 0;
	this.KeyDeltaY = 0;
	this.KeyTimerActive = 0;
	
	this.isSatellite = true;
	
	this.bNavigate = true;
	this.bScaleBar = true;
	
	this.sImageBase = ScriptBase()+"img/";
	this.bModeButtons = true;
	this.bPointsButtons = true;
	this.baseOverride = "";
	
	this.bPopupMenu = false;
	
	this.AJAX_Status_Callback=null;
	this.NoStatusCallback = false;
	this.status_url="";
	
	// d=drag; z=zoom
	this.dragMode = "m";
	this.dragRect = null;
	
	this.bHotKeyAssoc = false;
	this.bUseHotkeys = true;

	this.bMouseAssoc = false;
	this.bUseMouse = true;

	this.lastUpdX = 0;
	this.lastUpdY = 0;
	this.lastUpdZ = 0;
	
	this.noPopups = false;
	this.nSerial = 0;
	
	this.snaps = new Array ( 10.24, 5.12, 2.56, 1.28, 0.64, 0.32, 0.16, 0.08, 0.02, 0.01, 0.005, 0.0025, 0.00125, 0.000625 );
}

CAatMap.prototype.SetOverride = function(override, value)
{
	this.aOverride[override] = value;
}


CAatMap.prototype.GetOverride = function(override)
{
	if (this.aOverride[override]) return this.aOverride[override];
		else return "";
}


CAatMap.prototype.DisableURLPopups = function(val)
{
	this.noPopups = val;
}


CAatMap.prototype.SetHotkeys = function(val)
{
	this.bUseHotkeys = val;
}

CAatMap.prototype.SetMouse = function(val)
{
	this.bUseMouse = val;
}

CAatMap.prototype.XRelative = function(pixels)
{
		if (BrowserDetect.browser=="Explorer") return pixels;
		return pixels + this.nCtrlAbsLeft;
}


// returns an array (x1,y1,x2,y2)
CAatMap.prototype.GetBounds = function()
{
	return new Array( 
		this.GetX()-this.DegWidth()/2,  this.GetY()+this.DegHeight()/2,
		this.GetX()+this.DegWidth()/2,  this.GetY()-this.DegHeight()/2
	);
}


CAatMap.prototype.YRelative = function(pixels)
{
		if (BrowserDetect.browser=="Explorer") return pixels;
		return pixels + this.nCtrlAbsTop;
}


/**
 * Gets the Base URL of this page
 */
CAatMap.prototype.GetBaseUrl = function(override) { 
    var ret = this.baseOverride==""?BaseUrl():this.baseOverride; 
    if (!isUndefined(override)) {
        if (override=="air") {
            if ("" != "") ret = "http://";
        }
    }
    return ret;
}

/**
 * Sets the visibility of the mode buttons (map|hybrid|satellite).  false=off, true=on
 */
CAatMap.prototype.SetModeButtons = function(visible) { this.bModeButtons = visible; }

/**
 * Sets the visibility of the buttons if they're greyed.  false=off, true=on
 */
CAatMap.prototype.SetShowGreyButtons = function(visible) { this.bShowGreyButtons = visible; }

/**
 * Sets the visibility of the point button (points).  false=off, true=on
 */
CAatMap.prototype.SetPointsButtons = function(visible) { this.bPointsButtons = visible; }

/**
 * Sets the image base, where the system images are kept - default is "img\"
 */
CAatMap.prototype.SetImageBase = function(p) { 
	this.sImageBase = p; 
}

/**
 * Sets the visibility of the navigation controls on the map.  false=off, true=on
 */
CAatMap.prototype.SetNavigate = function(visible) { this.bNavigate = visible; }

/**
 * Sets the visibility of the scale on the map.  false=off, true=on
 */
CAatMap.prototype.SetScaleBar = function(visible) { this.bScaleBar = visible; }

/**
 * Sets the X-Coordinate of the map
 */
CAatMap.prototype.SetX = function(p) { this.nX = p; }

/**
 * Sets the Y-Coordinate of the map
 */
CAatMap.prototype.SetY = function(p) { this.nY = p; }

CAatMap.prototype.SetPopupMenu = function(p) { this.bPopupMenu = p; }

CAatMap.prototype.SetShowProgress = function(p) { this.bShowProgress = p; }

/**
 * Sets the zoom level at a tile level.  Must be a valid zoom level.
 * @see CAatmap@snaps
 */
CAatMap.prototype.SetTileZoom = function(p) { 
	// make sure this is a valid zoom level
	var okay = false;
	for (var l=0;l<this.snaps.length && !okay;l++) {
		if (this.snaps[l]==p) okay=true;
		if (!okay && this.snaps[l]<p) {
			p=this.snaps[l];
			okay=true;
		}
	}
	this.nTileZoom = p; 
	var nXTiles = this.nCtrlWidth / this.nTileSize;
	this.nZoom = this.nTileZoom * nXTiles;
}

CAatMap.prototype.Jump = function(x,y,tz) 
{ 
	this.SetX(x*1); 
	this.SetY(y*1); 
	this.SetTileZoom(tz*1);  
	this.AllMoved(); 
}

CAatMap.prototype.GetTileZoom = function(p) { return this.nTileZoom; }
CAatMap.prototype.SetZoom = function(p) { this.nZoom = p; this.AllMoved(); }
CAatMap.prototype.GetX = function() { return this.nX; }
CAatMap.prototype.GetY = function() { return this.nY; }
CAatMap.prototype.GetZoom = function() { return this.nZoom; }
CAatMap.prototype.SetCopyrightXMargin = function(val) { this.nCopyrightXMargin=val; }

CAatMap.prototype.GetBaseCursor = function() {
	if (this.dragMode=="m")
		return this.sHandCursor;
	else
		return this.sMagnifyCursor;
}

var lastCheckUrl = "";

CAatMap.prototype.DegWidth = function() { return this.nZoom*2; }


CAatMap.prototype.DegHeight = function()
{
		fact = this.nCtrlHeight/this.nCtrlWidth;
		return this.nZoom*2*fact;
}


CAatMap.prototype.AllMoved = function()
{
  for (layer=0;layer<this.aLayers.length;layer++) {
    var ob = this.aLayers[layer];
    ob.layerMoved = true;
  }
}

CAatMap.prototype.JumpTo = function(x,y,tz) { 
	this.Jump(x,y,tz);
	this.Init();
}


// maps all functions straight from 
// when wid, hei are used, instead the right or bottom coordinates must be passed, respectively
function CAatPolygonLayer()
{
	this.parent = null;

	this.nType = "";
	this.name = "";
	this.pos = 0;
	this.nX = 0;
	this.nY = 0;
	
	this.minX = null;
	this.minY = null;
	this.maxX = null;
	this.maxY = null;
	
	this.pMinX = 0;
	this.pMinY = 0;
	this.pMaxX = 0;
	this.pMaxY = 0;
	
	this.vCanvas = null;
	this.nCanvasLeft = 0;
	this.nCanvasTop = 0;
	this.nCanvasWidth = 0;
	this.nCanvasHeight = 0;
	
	this.IDCounter = 10;
	
	this.cbOver = null;
	this.cbOut = null;
	this.cbClick = null;
	
	this.jg = null;
	this.layerMoved = true;
	
	this.drawQueue = new Array();
	
	this.SetCursor = null;

}

function DrawCmd(typ, id, cbOver, cbOut, cbClick, p1, p2, p3, p4, p5, p6)
{
	this.type = typ;
	this.ID = id;
	this.p1 = p1;
	this.p2 = p2;
	this.p3 = p3;
	this.p4 = p4;
	this.p5 = p5;
	this.p6 = p6;
	this.cbOver = cbOver;
	this.cbOut = cbOut;
	this.cbClick = cbClick;
}


CAatPolygonLayer.prototype.SetMouseOver = function(func) { this.cbOver=func; }
CAatPolygonLayer.prototype.SetMouseOut = function(func) { this.cbOut=func; }
CAatPolygonLayer.prototype.SetMouseClick = function(func) { this.cbClick=func; }


CAatPolygonLayer.prototype.NewID = function()
{
	this.IDCounter++;
	return this.IDCounter;
}


CAatPolygonLayer.prototype.CheckMM = function(x,y)
{
	if (typeof(x)=="undefined" || typeof(y)=="undefined") return;
	if (this.minX==null || x<this.minX) this.minX=x;
	if (this.minY==null || y<this.minY) this.minY=y;
	if (this.maxX==null || x>this.maxX) this.maxX=x;
	if (this.maxY==null || y>this.maxY) this.maxY=y;
}

CAatPolygonLayer.prototype.Remove = function(id) { 
	for (lp=0;lp<this.drawQueue.length;lp++) {
		var itm = this.drawQueue[lp];
		if (itm.ID==id) {
			this.drawQueue.splice(lp,1);
			return;
		}
	}
}

CAatPolygonLayer.prototype.RemoveAll = function() { 
	this.drawQueue.splice(0,this.drawQueue.length);
}


CAatPolygonLayer.prototype.setColor = function(p1) { 
	this.drawQueue.push(new DrawCmd("setColor",null,null,null,null,p1)); 
}

CAatPolygonLayer.prototype.setStroke = function(p1) { 
	this.drawQueue.push(new DrawCmd("setStroke",null,null,null,null,p1)); 
}

CAatPolygonLayer.prototype.drawPolyline = function(p1,p2) { 
	var newid=this.NewID();
	for (pl=0;pl<p1.length;pl++) this.CheckMM(p1[pl], p2[pl]);
	this.drawQueue.push(new DrawCmd("drawPolyline",newid,this.cbOver,this.cbOut,this.cbClick,p1,p2)); 
	return newid;
}

CAatPolygonLayer.prototype.drawPolygon = function(p1d,p2d) { 
	var newid=this.NewID();
	for (pl=0;pl<p1d.length;pl++) this.CheckMM(p1d[pl], p2d[pl]);
	this.drawQueue.push(new DrawCmd("drawPolygon",newid,this.cbOver,this.cbOut,this.cbClick,p1d,p2d)); 
	return newid;
}

CAatPolygonLayer.prototype.fillPolygon = function(p1f,p2f) { 
	var newid=this.NewID();
	for (pl=0;pl<p1f.length;pl++) this.CheckMM(p1f[pl], p2f[pl]);
	this.drawQueue.push(new DrawCmd("fillPolygon",newid,this.cbOver,this.cbOut,this.cbClick,p1f,p2f)); 
	return newid;
}

CAatPolygonLayer.prototype.drawRect = function(p1,p2,p3,p4) { 
	var newid=this.NewID();
	this.CheckMM(p1,p2);
	this.CheckMM(p3,p4);
	this.drawQueue.push(new DrawCmd("drawRect",newid,this.cbOver,this.cbOut,this.cbClick,p1,p2,p3,p4)); 
	return newid;
}

CAatPolygonLayer.prototype.drawEllipse = function(p1,p2,p3,p4) { 
	var newid=this.NewID();
	this.CheckMM(p1,p2);
	this.CheckMM(p3,p4);
	this.drawQueue.push(new DrawCmd("drawEllipse",newid,this.cbOver,this.cbOut,this.cbClick,p1,p2,p3,p4)); 
	return newid;
}

CAatPolygonLayer.prototype.fillEllipse = function(p1,p2,p3,p4) { 
	var newid=this.NewID();
	this.CheckMM(p1,p2);
	if (p4) { // no p4, 3rd parm is radius in pixels
		this.CheckMM(p1+p3,p2+p4);
	}
	this.drawQueue.push(new DrawCmd("fillEllipse",newid,this.cbOver,this.cbOut,this.cbClick,p1,p2,p3,p4)); 
	return newid;
}

CAatPolygonLayer.prototype.fillArc = function(p1,p2,p3,p4,p5,p6) { 
	var newid=this.NewID();
	this.CheckMM(p1,p2);
	this.CheckMM(p3,p4);
	this.drawQueue.push(new DrawCmd("fillArc",newid,this.cbOver,this.cbOut,this.cbClick,p1,p2,p3,p4,p5,p6)); 
	return newid;
}

CAatPolygonLayer.prototype.setFont = function(p1,p2,p3) { 
	var newid=this.NewID();
	this.drawQueue.push(new DrawCmd("setFont",newid,this.cbOver,this.cbOut,this.cbClick,p1,p2,p3)); 
	return newid;
}

CAatPolygonLayer.prototype.drawString = function(p1,p2,p3) { 
	var newid=this.NewID();
	this.CheckMM(p2,p3);
	this.CheckMM(p2+this.parent.DegWidth(),p3+this.parent.DegHeight());
	this.drawQueue.push(new DrawCmd("drawString",newid,this.cbOver,this.cbOut,this.cbClick,p1,p2,p3)); 
	return newid;
}

CAatPolygonLayer.prototype.drawStringRect = function(p1,p2,p3,p4,p5) { 
	var newid=this.NewID();
	this.CheckMM(p2,p3);
	this.CheckMM(p2+p4,p3+this.parent.DegHeight());
	this.drawQueue.push(new DrawCmd("drawStringRect",newid,this.cbOver,this.cbOut,this.cbClick,p1,p2,p3,p4,p5)); 
	return newid;
}

CAatPolygonLayer.prototype.drawImage = function(p1,p2,p3,p4,p5) { 
	var newid=this.NewID();
	this.CheckMM(p2,p3);
	this.CheckMM(p4,p5);
	this.drawQueue.push(new DrawCmd("drawImage",newid,this.cbOver,this.cbOut,this.cbClick,p1,p2,p3,p4,p5)); 
	return newid;
}

CAatPolygonLayer.prototype.fillRect = function(p1,p2,p3,p4) { 
	var newid=this.NewID();
	this.CheckMM(p1,p2);
	this.CheckMM(p3,p4);
	this.drawQueue.push(new DrawCmd("fillRect",newid,this.cbOver,this.cbOut,this.cbClick,p1,p2,p3,p4)); 
	return newid;
}

CAatPolygonLayer.prototype.drawLine = function(x1,y1,x2,y2) {
	var newid=this.NewID();
	this.CheckMM(x1,y1);
	this.CheckMM(x2,y2);
	this.drawQueue.push(new DrawCmd("drawLine",newid,this.cbOver,this.cbOut,this.cbClick,x1,y1,x2,y2)); 
	return newid;
}


// where is xc in relation to the viewport's top-left
CAatPolygonLayer.prototype.TransX = function(xc)
{
	var eachdeg = this.parent.nCtrlWidth / this.parent.DegWidth();
	if (xc>0) {
		var leftside = this.parent.GetX() - this.parent.DegWidth()/2;
		var ret =  (xc - leftside ) * eachdeg;
	}  else {
		var leftside = this.parent.GetX() + this.parent.DegWidth()/2;
		var ret =  (xc - leftside ) * eachdeg;
		ret *= -1;
	}
	return parseInt(ret);
}

// where is yc in relation to the viewport's top-left
CAatPolygonLayer.prototype.TransY = function(yplace)
{
	var eachdeg = this.parent.nCtrlHeight / this.parent.DegHeight();
	var topside = this.parent.GetY() + this.parent.DegHeight()/2;
	var ret = (  yplace - topside ) * eachdeg;
	ret *=-1;
	return parseInt(ret);
}

CAatPolygonLayer.prototype.LocalTransX = function(xc)
{
	var eachdeg = this.nCanvasWidth / (this.maxX-this.minX);
	var ret = ( xc - this.minX ) * eachdeg;
	return parseInt(ret);
}

CAatPolygonLayer.prototype.LocalTransY = function(yc)
{
	var eachdeg = this.nCanvasHeight / (this.maxY-this.minY);
	var ret = ( yc - this.minY ) * eachdeg;
//	if (this.minY<0 || yc > 0) ret = this.nCanvasHeight - ret;
	return parseInt(ret);
}

CAatPolygonLayer.prototype.SetType = function(t) { this.nType = t; }

CAatPolygonLayer.prototype.TransXArr = function(inarr)
{
	var ret = new Array()
	for (al=0;al<inarr.length;al++)
		ret.push(this.LocalTransX(inarr[al]));
	return ret;
}

CAatPolygonLayer.prototype.TransYArr = function(inarr)
{
	var ret = new Array()
	for (al=0;al<inarr.length;al++)
		ret.push(this.LocalTransY(inarr[al]));
	return ret;
}

var PCBover = new Array();
var PCBout = new Array();
var PCBclick = new Array();
var PCBaction = "";
var PCBtimer = -1;
var PCB_trigger_ms = 100;
var PCBinside = 0;
var PCBlastaction = ""; // in out click
var PCBlasttrigger = ""; // in out click

function PCB_CB_Launch(act)
{
	if (act==PCBaction) return;
	if (PCBlastaction!="click") PCBaction=act;
	PCBlasttrigger=PCBlastaction;
	eval(act);
}

function PCB_CB_ReplaceAction(withaction)
{
	if (PCBtimer>=0) clearTimeout(PCBtimer);
	PCBtimer=setTimeout('PCB_CB_Launch("'+withaction+'")',PCB_trigger_ms);
}

function PCB_CB_Over(ref)
{
	var tocall = PCBover[ref]+"("+String(ref)+")";
	PCBinside = ref;
	PCBlastaction = "in";
	PCB_CB_ReplaceAction(tocall);
}

function PCB_CB_Out(ref)
{
	if (PCBlasttrigger!="in" && PCBlasttrigger!="click") return; // sometimes moving fast will give us false outs
	var tocall = PCBout[ref]+"("+String(ref)+")";
	PCBinside = 0;
	PCBlastaction = "out";
	PCB_CB_ReplaceAction(tocall);
}

function PCB_CB_Click(ref)
{
	var tocall = PCBclick[ref]+"("+String(ref)+")";
	PCBlastaction = "click";
	PCB_CB_ReplaceAction(tocall);
}

CAatPolygonLayer.prototype.RenderLayer = function(copyDiv, zIndex)
{
	var ret = new Array();
	var vDiv = CopyLayerDiv(copyDiv);
	
	// make sure that the bounds are at least the viewable area
	bnds = this.parent.GetBounds();
	if (this.minX==null || this.minX > bnds[0]) this.minX = bnds[0];
	if (this.minY==null || this.minY < bnds[1]) this.minY = bnds[1];
	if (this.maxX==null || this.maxX < bnds[2]) this.maxX = bnds[2];
	if (this.maxY==null || this.maxY > bnds[3]) this.maxY = bnds[3];

	// work out the pixel extents
	this.pMinX = this.TransX(this.minX);
	this.pMaxX = this.TransX(this.maxX);
	this.pMinY = this.TransY(this.minY);
	this.pMaxY = this.TransY(this.maxY);

//var aa = String(this.minX) + " ; " + String(this.minY) + " ; " + String(this.maxX)+ " ; " + String(this.maxY);
//alert(aa);
	// create a big div to hold everything
	this.vCanvas = document.createElement("div");
	this.nCanvasWidth = parseInt(this.pMaxX - this.pMinX);
	this.nCanvasHeight = Math.abs(parseInt(this.pMaxY - this.pMinY))
	this.vCanvas.style.width = this.nCanvasWidth;
	this.vCanvas.style.height = this.nCanvasHeight;
	this.vCanvas.style.position = "absolute";
	this.vCanvas.id = this.name+"_canv";

	this.nCanvasLeft = parseInt(this.pMinX);
	this.nCanvasTop = parseInt(Math.min(this.pMaxY,this.pMinY));
	this.vCanvas.style.left = this.nCanvasLeft;
	this.vCanvas.style.top = this.nCanvasTop;
	this.vCanvas.style.zIndex = zIndex;
	vDiv.appendChild(this.vCanvas);

	this.jg = new jsGraphics(this.vCanvas);
	for (dloop=0; dloop<this.drawQueue.length; dloop++) {
		var qi = this.drawQueue[dloop];
		PCBover[qi.ID]=qi.cbOver;
		PCBout[qi.ID]=qi.cbOut;
		PCBclick[qi.ID]=qi.cbClick;
		this.jg.setExtraParms(qi.ID, qi.cbOver?"PCB_CB_Over":null, qi.cbOut?"PCB_CB_Out":null, qi.cbClick?"PCB_CB_Click":null);
		switch (qi.type) {
			case "setColor" : this.jg.setColor( qi.p1 ); break;
			case "setStroke" : this.jg.setStroke( qi.p1 ); break;
			case "drawLine" : this.jg.drawLine( this.LocalTransX(qi.p1), this.LocalTransY(qi.p2), this.LocalTransX(qi.p3), this.LocalTransY(qi.p4) ); break;
			case "drawPolyline" : this.jg.drawPolyline( this.TransXArr(qi.p1), this.TransYArr(qi.p2), this.ID ); break;
			case "drawRect" : this.jg.drawRect( Math.min(this.LocalTransX(qi.p1),this.LocalTransX(qi.p3)), Math.min(this.LocalTransY(qi.p2),this.LocalTransY(qi.p4)), 
								Math.abs(this.LocalTransX(qi.p3)-this.LocalTransX(qi.p1)), Math.abs(this.LocalTransY(qi.p4)-this.LocalTransY(qi.p2)) ); break;
			case "fillRect" : this.jg.fillRect( Math.min(this.LocalTransX(qi.p1),this.LocalTransX(qi.p3)), Math.min(this.LocalTransY(qi.p2),this.LocalTransY(qi.p4)), 
								Math.abs(this.LocalTransX(qi.p3)-this.LocalTransX(qi.p1)), Math.abs(this.LocalTransY(qi.p4)-this.LocalTransY(qi.p2)) ); break;
			case "drawPolygon" : this.jg.drawPolygon( this.TransXArr(qi.p1), this.TransYArr(qi.p2) ); break;
			case "fillPolygon" : this.jg.fillPolygon( this.TransXArr(qi.p1), this.TransYArr(qi.p2) ); break;
			case "drawEllipse" : 
								if (typeof(p4)!="undefined") { // if no p4, then p3 = radius in pixels
									this.jg.drawEllipse( Math.min(this.LocalTransX(qi.p1),this.LocalTransX(qi.p3)), Math.min(this.LocalTransY(qi.p2),this.LocalTransY(qi.p4)), 
										Math.abs(this.LocalTransX(qi.p3)-this.LocalTransX(qi.p1)), Math.abs(this.LocalTransY(qi.p4)-this.LocalTransY(qi.p2)) );
								} else {
									this.jg.drawEllipse( this.LocalTransX(qi.p1)-qi.p3, this.LocalTransY(qi.p2)-qi.p3, qi.p3*2, qi.p3*2 );
								}
								break;
			case "fillEllipse" : 
								if (typeof(p4)!="undefined") { // if no p4, then p3 = radius in pixels
									this.jg.fillEllipse( Math.min(this.LocalTransX(qi.p1),this.LocalTransX(qi.p3)), Math.min(this.LocalTransY(qi.p2),this.LocalTransY(qi.p4)), 
										Math.abs(this.LocalTransX(qi.p3)-this.LocalTransX(qi.p1)), Math.abs(this.LocalTransY(qi.p4)-this.LocalTransY(qi.p2)) );
								} else this.jg.fillEllipse( this.LocalTransX(qi.p1)-qi.p3, this.LocalTransY(qi.p2)-qi.p3, qi.p3*2, qi.p3*2 );
								break;
			case "fillArc" : this.jg.fillArc( Math.min(this.LocalTransX(qi.p1),this.LocalTransX(qi.p3)), Math.min(this.LocalTransY(qi.p2),this.LocalTransY(qi.p4)), 
								Math.abs(this.LocalTransX(qi.p3)-this.LocalTransX(qi.p1)), Math.abs(this.LocalTransY(qi.p4)-this.LocalTransY(qi.p2)), qi.p5, qi.p6 ); break;
			case "setFont" : this.jg.setFont( qi.p1, qi.p2, qi.p3 ); break;
			case "drawString" : this.jg.drawString( qi.p1, this.LocalTransX(qi.p2), this.LocalTransY(qi.p3) ); break;
			case "drawStringRect" : this.jg.drawStringRect( qi.p1, this.LocalTransX(qi.p2), this.LocalTransY(qi.p3), Math.abs(this.LocalTransX(qi.p4)-this.LocalTransX(qi.p2)) ); break;
			case "drawImage" : this.jg.drawImage( qi.p1, Math.min(this.LocalTransX(qi.p2),this.LocalTransX(qi.p4)), Math.min(this.LocalTransY(qi.p3),this.LocalTransY(qi.p5)), 
								Math.abs(this.LocalTransX(qi.p4)-this.LocalTransX(qi.p2)), Math.abs(this.LocalTransY(qi.p5)-this.LocalTransY(qi.p3)) ); break;
		}
	}

	this.jg.paint();
	ret.push(vDiv);
	this.layerMoved = false;

	return ret;
}


CAatPolygonLayer.prototype.MoveMap = function(pDeltaX, pDeltaY)
{
	this.layerMoved = true;
}

CAatPolygonLayer.prototype.MoveScreen = function(pDeltaX, pDeltaY, pixels)
{
	if (pixels) {
		deltaX = pDeltaX;
		deltaY = pDeltaY;
	} else {
		var nPixScale = (this.parent.GetZoom()*2) / this.parent.nCtrlWidth;
		deltaX = pDeltaX / nPixScale;
		deltaY = pDeltaY / nPixScale;
	}

	if (this.vCanvas!=null) {
		this.nCanvasLeft += deltaX;
		this.nCanvasTop += deltaY;
		this.vCanvas.style.left = this.nCanvasLeft;
		this.vCanvas.style.top = this.nCanvasTop;
	}
	this.layerMoved = true;
}



function CAatPointLayer()
{
	this.parent = null;
	
	this.nType = "";
	this.name = "";
	this.aPoints = new Array();
	this.anchorX = "left";
	this.anchorY = "top";
	this.pos = 0;
	this.layerMoved = true;
}



var gImageBlank = "<img height=300 width=300 style='display:block;' src=\"http://www.streetmaps.co.za//img/blank.png\">";
var gInfoDiv = null;

var thumb_slider_top = 17;
var thumb_slider_left = 12;

function MapURL(obj,layer,width,height,scale,x,y)
{
	var useLayer = layer;
	var customUrl = obj.GetCustomUrl();
	var ret = "";
	
	switch (useLayer) {
		case "map" :
		case "num" :
			ret = obj.parent.GetBaseUrl()+"/maptile/"+layer+"/"+String(width)+"_"+String(height)+"/"+String(scale)+"/"+String(y.toFixed(6))+"_"+String(x.toFixed(6))+".png";
			break;
		case "sat" :
			if (obj.parent.GetOverride("sat_type").length) {
				useLayer = obj.parent.GetOverride("sat_type");
				customUrl = obj.parent.GetOverride("sat_url");
			} else {
				ret = obj.parent.GetBaseUrl("air")+"/maptile/"+layer+"/"+String(width)+"_"+String(height)+"/"+String(scale)+"/"+String(y.toFixed(6))+"_"+String(x.toFixed(6))+".jpg";
			}
			break;
		case "hyb" :
			ret = obj.parent.GetBaseUrl()+"/maptile/"+layer+"/"+String(width)+"_"+String(height)+"/"+String(scale)+"/"+String(y.toFixed(6))+"_"+String(x.toFixed(6))+".png";
			break;
		case "maproute" :		    
		    ret = obj.parent.GetBaseUrl()+"/maptile/"+layer+"/"+String(width)+"_"+String(height)+"/"+String(scale)+"/"+String(y.toFixed(6))+"_"+String(x.toFixed(6))+"/"+String(obj.routeStartX.toFixed(6))+"/"+String(obj.routeStartY.toFixed(6))+"/"+String(obj.routeEndX.toFixed(6))+"/"+String(obj.routeEndY.toFixed(6))+".png";			
		    break;
		case "route" :
		    //ret = obj.parent.GetBaseUrl()+"/maptile/"+layer+"/"+String(width)+"_"+String(height)+"/"+String(scale)+"/"+String(y.toFixed(6))+"_"+String(x.toFixed(6))+"/"+String(obj.routeStartX.toFixed(6))+"/"+String(obj.routeStartY.toFixed(6))+"/"+String(obj.routeEndX.toFixed(6))+"/"+String(obj.routeEndY.toFixed(6))+".png";					    
			ret = obj.parent.GetBaseUrl()+"/maptile/rw/"+String(width)+"_"+String(height)+"/"+String(scale)+"/"+String(y.toFixed(6))+"_"+String(x.toFixed(6))+"/"+String(obj.routeStartX.toFixed(6))+","+String(obj.routeStartY.toFixed(6))+","+String(obj.routeEndX.toFixed(6))+","+String(obj.routeEndY.toFixed(6))+".png";
			//ret = obj.parent.GetBaseUrl()+"directions_img.asp?width="+String(width)+"&height="+ String(height)+ "&scale=" + String(scale) + "&longitude=" + String(x.toFixed(6)) +"&latitude=" + String(y.toFixed(6))+"&pointtype=xy&points=" + String(obj.routeStartX.toFixed(6))+"/"+String(obj.routeStartY.toFixed(6))+","+String(obj.routeEndX.toFixed(6))+","+String(obj.routeEndY.toFixed(6)) +"&imgtype=png";
			break;
		case "multiroute" :
			ret = obj.parent.GetBaseUrl()+"/maptile/multiroute/"+String(width)+"_"+String(height)+"/"+String(scale)+"/"+String(y.toFixed(6))+"_"+String(x.toFixed(6))+"/"+obj.args1+".png";
			break;
		case "stats" :
		    ret = obj.parent.GetBaseUrl()+"/aatmap/render_stat.asp?width="+String(width)+"&height="+String(height)+"&scale="+String(scale)+"&x="+String(x)+"&y="+String(y)+"&df="+obj.userData[0]+"&ds="+obj.userData[1];
		    break;
	}

	if (ret=="" && useLayer=="custom") {
		ret = customUrl;
		ret = ret.replace(/%width%/gi, String(width));
		ret = ret.replace(/%height%/gi, String(height));
		ret = ret.replace(/%scale%/gi, String(scale));
		ret = ret.replace(/%x%/gi, String(x.toFixed(6)));
		ret = ret.replace(/%y%/gi, String(y.toFixed(6)));
		ret = ret.replace(/%baseurl%/gi, obj.parent.GetBaseUrl());
	}

	return PNG_Check_Str(ret);
}


function CAatPoint(pX,pY,pCaption,id,parent)
{
	this.pX = pX;
	this.pY = pY;
	this.id = id;
	this.pCaption = pCaption;
	this.layer = null;
	this.cDiv = null;
	this.cTable = null;
	this.cDivBG = null;
	this.cTableBG = null;
	this.pixX = 0;
	this.pixY = 0;
	this.pixW = 0;
	this.pixH = 0;
	this.parent = parent;
	this.img_fg_width = 20;
	this.img_fg_height = 32;
	this.img_bg_width = 32;
	this.img_bg_height = 32;
	this.style = "default";
	this.icon = null;
	this.tooltip = "";
	this.clickBehaviour = "";
	this.clickCallback = null;
	this.rawLabel = "";
	this.userRef = "";
	this.closeButton = false;
	this.iconStyle = "icon";
	
	this.uniqueid = String(Math.random());
	
	this.labelX = 0;
	this.labelY = 0;
	this.labelW = 0;
	this.labelH = 0;
	this.labelDivW = 0;
	this.divXShift = 0;
	this.divYShift = 0;
	this.iconAnchorX = "left";
	this.iconAnchorY = "top";
	
	this.iconX = 0;
	this.iconY = 0;
	this.iconW = 0;
	this.iconH = 0;
	this.iconSizeX = 16;
	this.iconSizeY = 16;
	this.labelAnchorX = "left";
	this.labelAnchorY = "top";
	this.iconHotX = 0;
	this.iconHotY = 0;

  this.visibleMin = 0;
  this.visibleMax = 99;
  this.isVisible = true;

  this.labelMin = 0;
  this.labeleMax = 0;
  
  this.dirtySize = true;
  this.dirtyContent = true;
    
	this.img_fg = this.parent.sImageBase+"bubble_yellow.png";
	this.img_bg = this.parent.sImageBase+"bubble_shadow.png";
	
	this.zIndexMod = 0;

}

CAatPoint.prototype.SetCaption = function(c) { this.pCaption=c; this.dirtyContent=true; }
CAatPoint.prototype.SetCallback = function(c) { this.clickCallback=c; }
CAatPoint.prototype.SetIconStyle = function(c) { this.iconStyle=c;  }

CAatPoint.prototype.Show = function()
{
	this.isVisible = true;
	this.dirtySize = true;
	this.dirtyContent = true;
}


CAatPoint.prototype.Hide = function()
{
	this.isVisible = false;
	this.dirtySize = true;
	this.dirtyContent = true;
}

CAatPoint.prototype.EnsureVisible = function (dostatus)
{
	var deltaX = 0, deltaY = 0;
	var sX=0, sY=0;

	if (this.pixX + this.labelW > this.parent.nCtrlWidth)  deltaX = (this.parent.nCtrlWidth - this.labelW - 20) - this.labelX;
	if (this.pixY + this.labelH > this.parent.nCtrlHeight)  deltaY = (this.parent.nCtrlHeight - this.labelH - 20) - this.labelY;
	if (this.pixY < 20) deltaY = (-this.pixY + 20);
	if (this.pixX < 20) deltaX = (-this.pixX + 20);
	
	if (deltaX>0) sX=+1;
	if (deltaX<0) sX=-1;
	if (deltaY>0) sY=+1;
	if (deltaY<0) sY=-1;

	var dDeltaX = Math.abs((this.parent.DegWidth()/this.parent.nCtrlWidth) * deltaX);
	var dDeltaY = Math.abs((this.parent.DegHeight()/this.parent.nCtrlHeight) * deltaY);
	
	if (deltaX != 0 || deltaY != 0) {
		newSetTimeout(SmoothMove,10,this.parent,0,0,10,dDeltaX,dDeltaY,sX,sY,0,dostatus);
	}
}

CAatPoint.prototype.MovePoint = function(x,y) { this.pX=x;this.pY=y;this.dirtyContent=true; }

function RectIntersect(ax1, ay1, ax2, ay2, bx1, by1, bx2, by2)
{
	var ret = false;
	if (   ( (ax1<bx1 && ax2>bx1) || (ax1<bx2 && ax2>bx2) || (ax1>bx1 && ax2<bx2) )
		&& ( (ay1<by1 && ay2>by1) || (ay1>by1 && ay2<by1) || (ay1<by2 && ay2>by2) || (ay1>by2 && ay2<by2) || (ay1>by1 && ay2<by2) || (ay1<by1 && ay2>by2) )
		) ret = true;

	return ret;
}


CAatPoint.prototype.SetIconSize = function(x,y) { this.iconSizeX=x; this.iconSizeY=y; this.dirtyContent=true; }

CAatPoint.prototype.ProcessClickJump = function(event, element)
{
    StopPropagation(event);
    this.style="label";

    for (pl=0;pl<this.layer.aPoints.length;pl++) {
        var pnt = this.layer.aPoints[pl];
        if (pnt.uniqueid!=this.uniqueid) {
            if (RectIntersect(pnt.labelX,pnt.labelY,pnt.labelX+pnt.labelW,pnt.labelY+pnt.labelH, this.labelX,this.labelY,this.labelX+this.labelW,this.labelY+this.labelH)) {
                this.layer.aPoints[pl].style="icon";
            }
        }
    }

    this.parent.Jump(this.pX,this.pY,this.parent.GetTileZoom());
    this.parent.Init(false);
}

CAatPoint.prototype.ProcessClick = function(event, element)
{
		// if this element uses a close button, and we've not clicked on it, get outta here
		if (this.closeButton) {
			if (element!=null) {
				var srcEl = SourceElement(event);
				if (srcEl.id.indexOf("_close")<0) return true;
			}
		}

    // clicked a point
    if (this.clickCallback!=null) {
    	this.clickCallback(this);
    	return;
    }

    switch (this.clickBehaviour) {
        case "toggle_all_label" :
        case "toggle_all_biglabel" :
        case "toggle_icon_label" :
        case "toggle_icon_biglabel" :

					var isHotspot = false;
					if (element!=null) {
      			var srcEl = SourceElement(event);
      			if (srcEl.id.slice(0,8)=="genlink_") isHotspot = true;
					}


      		if (isHotspot) {
      			// don't make this go away, it's a click-through
      			return true;
      		} else {
            if (event!=null) StopPropagation(event);
            if (this.style=="icon") {
            	if (this.clickBehaviour=="toggle_icon_biglabel" || this.clickBehaviour=="toggle_all_biglabel") {
                this.style="biglabel";
              } else {
                this.style="label";
              }
                // now we need to hide any of the points which are labels, that overlapping this
                
	            for (pl=0;pl<this.layer.aPoints.length;pl++) {
		            var pnt = this.layer.aPoints[pl];
		            if (pnt.uniqueid!=this.uniqueid) {
		                if (RectIntersect(pnt.labelX,pnt.labelY,pnt.labelX+pnt.labelW,pnt.labelY+pnt.labelH, this.labelX,this.labelY,this.labelX+this.labelW,this.labelY+this.labelH)) {
		                    this.layer.aPoints[pl].style="icon";
		                }
		            }
		        	}
		        
			        var deltaX = 0;
			        var deltaY = 0;
			        
			        // make sure this is in the window
			        if (this.labelX<0) deltaX = this.labelX - 32;
			        if (this.labelY<0) deltaY = this.labelY - 16;
			        if (this.labelX+pnt.labelW>this.parent.nCtrlWidth) deltaX = ((this.labelX+pnt.labelW)-this.parent.nCtrlWidth)+32;
			        if (this.labelY+pnt.labelH>this.parent.nCtrlHeight) deltaY = ((this.labelY+pnt.labelH)-this.parent.nCtrlHeight)+16;
			        if (deltaX!=0 || deltaY!=0) {
			            var signX = deltaX>0?-1:1;
			            var signY = deltaY>0?-1:1;
			            var scX = Math.abs(deltaX * (this.parent.DegWidth() / this.parent.nCtrlWidth ));
			            var scY = Math.abs(deltaY * (this.parent.DegHeight() / this.parent.nCtrlHeight ));
			            newSetTimeout(SmoothMove,10,this.parent,0,0,20,scX,scY,signX,signY,0);
			        } // if (deltaX!=0 || deltaY!=0) {
		        
            } else
                this.style="icon";
            this.dirtyContent = true;
            this.parent.Init(false);
            break;
        	}
    		}
	return false;
}


CAatPoint.prototype.SetVisible = function(min,max) { this.visibleMin=min; this.visibleMax=max; this.dirtyContent=true; }
CAatPoint.prototype.SetLabelVisible = function(min,max) { this.labelMin=min; this.labelMax=max; this.style="labellimited" }
CAatPoint.prototype.SetClickBehaviour = function(cb) { this.clickBehaviour = cb; }
CAatPoint.prototype.SetStyle = function(style)  { 
	this.style = style; 
	if (this.clickBehaviour=="") this.clickBehaviour="toggle_all_"+style;
	this.dirtyContent=true; 
}

CAatPoint.prototype.GetStyle = function()  { return this.style; }
CAatPoint.prototype.SetTooltip = function(tip) { this.tooltip = tip; }
CAatPoint.prototype.SetRawLabel= function(lbl) { this.rawLabel = lbl; this.dirtyContent=true; }
CAatPoint.prototype.SetIcon = function(icon) 
{
    this.icon = icon;
    this.dirtyContent=true;
}

CAatPoint.prototype.GetIcon = function()  { return this.icon; }

CAatPoint.prototype.Save = function()
{
	return new Array (
		this.style,
		this.tooltip,
		this.rawLabel,
		this.icon,
		this.iconAnchorX,
		this.iconAnchorY,
		this.labelAnchorX,
		this.labelAnchorY
	);
}

CAatPoint.prototype.Restore = function(state)
{
	this.SetStyle(state[0]);
	this.SetTooltip(state[1]);
	this.SetRawLabel(state[2]);
	this.SetIcon(state[3]);
	this.SetIconAnchor(state[4],state[5]);
	this.SetLabelAnchor(state[6],state[7]);
}

CAatPoint.prototype.SetImgFG = function(url,width,height) 
{ 
    this.img_fg = url; 
    if (!isUndefined(width)) this.img_fg_width=width;
    if (!isUndefined(height)) this.img_fg_height=height;
    this.dirtyContent=true;
}

CAatPoint.prototype.SetImgBG = function(url,width,height) 
{ 
    this.img_bg = url; 
    if (!isUndefined(width)) this.img_bg_width=width;
    if (!isUndefined(height)) this.img_bg_height=height;
    this.dirtyContent=true;
}

CAatPoint.prototype.SetIconAnchor = function(ax,ay) { this.iconAnchorX=ax; this.iconAnchorY=ay; this.dirtyContent=true; }
CAatPoint.prototype.SetLabelAnchor = function(ax,ay) { this.labelAnchorX=ax; this.labelAnchorY=ay; this.dirtyContent=true; }

CAatPointLayer.prototype.GetZoom = function() { return this.parent.nZoom; }
CAatPointLayer.prototype.GetTileZoom = function() { return this.parent.nTileZoom; }
CAatPointLayer.prototype.GetX = function() {  return this.parent.nX;  }
CAatPointLayer.prototype.GetY = function() {  return this.parent.nY;  }
CAatPointLayer.prototype.SetAnchor = function(ax,ay) { this.anchorX=ax; this.anchorY=ay; }
CAatPointLayer.prototype.SetDefaultAnchor = function(ax,ay) { this.anchorX=ax; this.anchorY=ay; }

/**
 * Returns the bounds of the layer in an array with format (x, y, x1, y1)
 */
CAatPointLayer.prototype.GetBounds = function() 
{ 
	var x=null, y=null, x1=null, y1=null;
	for (pl=0;pl<this.aPoints.length;pl++) {
		pt = this.aPoints[pl];
		if (pt.pX!=0 && pt.pY!=0) {
			if (pt.pX<x || x==null) x=pt.pX;
			if (pt.pY<y || y==null) y=pt.pY;
			if (pt.pX>x1 || x1==null) x1=pt.pX;
			if (pt.pY>y1 || y1==null) y1=pt.pY;
		}
	}
	return new Array(x,y,x1,y1);
}



var BubbleSeq = 1000;

function GetWidHei(txt, bWidth)
{
	// need to calculate the size of the content

	var cScratch = document.createElement("div");
	cScratch.style.position = "absolute";
	cScratch.style.visibility = "hidden";
	cScratch.style.left=PX(0);
	cScratch.style.top=PX(0);
	var widStr = "auto";
	if (!isUndefined(bWidth)) {
		if (bWidth!=0) widStr = PX(bWidth);
	}
	cScratch.style.width = widStr;
	
	document.body.appendChild(cScratch);
  MurderChildren(cScratch);
  var eltype="span";
  if (BrowserDetect.browser=="Webkit") eltype="div";
  var scDiv = document.createElement(eltype);
  scDiv.innerHTML = txt;
  cScratch.appendChild(scDiv);
  
  var contentWid = scDiv.offsetWidth;
  var contentHei = scDiv.offsetHeight;
  document.body.removeChild(cScratch);
  return new Array(contentWid, contentHei);
}


function BigBubble(caption, bWidth, bHeight, pPoint, pMap)
{
	var html = "";

	var WidHei = GetWidHei(caption, bWidth);
  contentWid = WidHei[0];
  contentHei = WidHei[1];
  
  if (bWidth==0) bWidth = contentWid + 24;
  
  if (bHeight==0) {
  	bHeight = contentHei + 95;
    if (bHeight<200) bHeight = 200;
  }
 
  if (bWidth<300) bWidth = 300;

  if (bWidth > pMap.nCtrlWidth - 64) bWidth = pMap.nCtrlWidth - 64;
  if (bHeight > pMap.nCtrlHeight - 64) bHeight = pMap.nCtrlHeight - 64;

  if (bHeight>700) bHeight = 700;

	var extrabit = "";
	var boxHeight = bHeight-93;
	var boxWidth = bWidth-23;
	if (contentHei>boxHeight) {
		extrabit += "overflow-y:scroll;";
		bWidth+=20;
		boxWidth+=20;
	}
	if (contentWid>boxWidth) extrabit += "overflow-x:scroll;";

	html += PNG(pMap.sImageBase+"popup_shadow.png", 48,93, "", 0,257, "genlink_nil", null, 0,bHeight-94);
	html += PNG(pMap.sImageBase+"popup_shadow.png", bWidth/2-48,93, "", 225,257, "genlink_nil", null, 48,bHeight-94);
	var wid = bWidth/2-148;
	html += PNG(pMap.sImageBase+"popup_shadow.png", wid>0?wid:0,93, "", 225,257, "genlink_nil", null, bWidth/2+100,bHeight-94);

	html += PNG(pMap.sImageBase+"popup_shadow.png", 96,93, "", 680,257, "genlink_nil", null, bWidth-48,bHeight-94);
	html += PNG(pMap.sImageBase+"popup_shadow.png", 100,93, "", 117,257, "genlink_nil", null, bWidth/2,bHeight-94);

	html += PNG(pMap.sImageBase+"popup_base.png", 24,24, "", 0,0, "genlink_nil", null, 0,0);
	var todo = bWidth-48;
	var xplace = 24;
	while (todo>0) {
		if (todo>400) chunk=400;
			else chunk=todo;
		html += PNG(pMap.sImageBase+"popup_base.png", chunk,24, "", 24,0, "genlink_nil", null, xplace,0);
		html += PNG(pMap.sImageBase+"popup_base.png", chunk,24, "", 200,606, "genlink_nil", null, xplace,bHeight-94);
		xplace += chunk;
		todo -= chunk;
	}
	html += PNG(pMap.sImageBase+"popup_base.png", 24,24, "", 676,0, "genlink_nil", null, bWidth-24,0);
	
	html += PNG(pMap.sImageBase+"popup_base.png", 24,bHeight-(94+24), "", 0,24, "genlink_nil", null, 0,24);
	html += PNG(pMap.sImageBase+"popup_base.png", 24,bHeight-(94+24), "", 676,24, "genlink_nil", null, bWidth-24,24);

	html += PNG(pMap.sImageBase+"popup_base.png", 24,24, "", 0,606, "genlink_nil", null, 0,bHeight-94);
	html += PNG(pMap.sImageBase+"popup_base.png", 24,24, "", 676,606, "genlink_nil", null, bWidth-24,bHeight-94);
	html += PNG(pMap.sImageBase+"popup_base.png", 85,94, "", 117,606, "genlink_nil", null, bWidth/2,bHeight-94);
	
	var idn = "genlink_"+String(BubbleSeq);
	var idn2 = "userctrl_"+String(BubbleSeq);
	BubbleSeq++;
	html += "<div id='"+idn+"' style='"+extrabit+"background:white;position:absolute;left:"+String(12)+"px;top:"+String(15)+"px;width:"+String(boxWidth)+"px;height:"+String(boxHeight)+"px;'>"+caption+"</div>";
	
	var moveover = (bHeight/2)*0.57-16;
	html += PNG(pMap.sImageBase+"popup_shadow.png", moveover,bHeight/2-93+24, "", 900-moveover,0, null, null, bWidth, bHeight/2-25);
	html = "<div id='genlink_none'>&nbsp;"+html+"</div>";
	html += "<div id='"+idn2+"_close' style='cursor:pointer;position:absolute;top:2px;width:10px;height:10px;left:"+String(bWidth-14)+"px;font-size:1px;'>"+PNG(pMap.sImageBase+"popup_close.png", 12,12, "", 0,0, "userctrl_close")+"</div>";

	// html, content width, content height, container width, container height, x offset, y offset, base_id
	// base_id: div name; *_close is the close button
	return new Array(html,bWidth,bHeight,bWidth*2,bHeight,-bWidth/2,0,idn);
}


function SmallBubble(caption, bWidth, bHeight, pPoint, pMap)
{
		caption = "<span style='font-size:9px;'>"+caption+"</span>"

	  if (bWidth<75 && bWidth>0) bWidth = 75;
	  if (bWidth > pMap.nCtrlWidth - (64-24)) bWidth = pMap.nCtrlWidth - 64;
	  if (bHeight > pMap.nCtrlHeight - 64) bHeight = pMap.nCtrlHeight - 64;
	  if (bHeight > 400) bHeight = 400;

		var WidHei = GetWidHei(caption,pMap.nCtrlWidth - (64+24));
		contentWid = WidHei[0];
		contentHei = WidHei[1];

	  if (bWidth==0) bWidth = contentWid + 24;
	  
	  if (bHeight==0) {
	  	bHeight = contentHei + 28;
	    if (bHeight<50) bHeight = 50;
	  }
	  
		var extrabit = "";
		var boxHeight = bHeight-28;
		var boxWidth = bWidth-24;
		if (contentHei>boxHeight) {
			extrabit += "overflow-y:scroll;";
			bWidth+=20;
			boxWidth+=20;
		}
		if (contentWid>boxWidth) extrabit += "overflow-x:scroll;";

		var capt = caption;
		widthbit = "";

		if (extrabit.length>0) {
			capt = "<div id='genlink_none' style='width:"+String(boxWidth)+"px;height:"+String(boxHeight)+"px;"+extrabit+"'>"+capt+"</div>"
		}

    ret = "<table style='width:"+String(bWidth)+"px;height:"+String(bHeight)+"px;' cellpadding=0 cellspacing=0 "+widthbit+"><tr><td width="+pPoint.iconW/2+"></td><td background='"+pMap.sImageBase+"bubw_tl.png' style='padding-left:6px;padding-top:6px;'>"+capt+"</td>"+
        "<td width=8 background='"+PNG_GIF(pMap.sImageBase+"bubw_tr")+"'></td></tr>"+
        "<tr height=28><td width="+pPoint.iconW/2+"></td><td background='"+PNG_GIF(pMap.sImageBase+"bubw_bl")+"'></td><td background='"+PNG_GIF(pMap.sImageBase+"bubw_br")+"'></td></tr>"
        "<tr height=28><td width="+pPoint.iconW/2+"></td><td background='"+PNG_GIF(pMap.sImageBase+"bubw_bl")+"'></td><td background='"+PNG_GIF(pMap.sImageBase+"bubw_br")+"'></td></tr>"
        "<tr height=28><td width="+pPoint.iconW/2+"></td><td background='"+PNG_GIF(pMap.sImageBase+"bubw_bl")+"'></td><td background='"+PNG_GIF(pMap.sImageBase+"bubw_br")+"'></td></tr>"
        "</table>";
   return ret;
}

function GetLabel(style, caption, width, height, pPoint, pMap)
{
	var ret;
	
	switch (style) {
		case "biglabel" :
			ret = BigBubble(caption, width, 0, pPoint, pMap);
			break;
		default:
			ret = SmallBubble(caption, width, height, pPoint, pMap)
      break;
	}
	
	return ret;
}


CAatPointLayer.prototype.RenderLayer = function(copyDiv, zIndex)
{
	var ret = new Array();
	var scale = this.parent.nZoom*2 / this.parent.nCtrlWidth;

	for (pl=0;pl<this.aPoints.length;pl++) {
		pt = this.aPoints[pl];

		if (this.parent.GetTileZoom()>=pt.visibleMin && this.parent.GetTileZoom()<=pt.visibleMax) {
	  		
		    dX = pt.pX - this.GetX();
		    dY = pt.pY - this.GetY();
		    plotX = dX / scale;
		    plotY = dY / scale;

        var usestyle=pt.style;
        if (pt.labelMin>0 || pt.labelMax>0) {
        	if ( (this.parent.GetTileZoom()<pt.labelMin || this.parent.GetTileZoom()>pt.labelMax) && usestyle=="labellimited") usestyle="icon";
        }
		    
		    if (pt.dirtyContent) {

			    vDiv = CopyLayerDiv(copyDiv);
			    vDiv.style.zIndex=zIndex+pt.zIndexMod+10;
			    if (usestyle=="icon") {
				    vDiv.style.zIndex=zIndex+pt.zIndexMod;
			    }
	  
			    pt.cDiv = vDiv;
			    vDivBG = null;

			    vTable = document.createElement("table");
			    pt.cTable = vTable;
			    vDiv.appendChild(vTable);
			    vRow = vTable.insertRow(-1);
			    vCell = vRow.insertCell(-1);
				    vTable.style.position = "absolute";
				    vTable.cellSpacing = 0;
				    vTable.cellPadding = 0;
				  
				  pt.iconHotX=-1;
				  pt.iconHotY=-1;

			    // work out the size of the icon
			    if (pt.icon!=null && usestyle!="onlylabel") {
			    		thisIcon = PNG(pt.icon,pt.iconSizeX,pt.iconSizeY,pt.tooltip,0,0,"mappoint_"+Math.random(),"default");
			    		var iW, iH;
			    		if (pt.iconStyle=="bubble") {
			    			iconCapt = "<table width=23 height=26 border=0 cellspacing=0 cellpadding=0 style='background-image:url("+ScriptBase()+"img/iconbubble_16.png);'><tr height=2><td colspan=3></td></tr><tr height=16><td width=2></td><td width=16>"+thisIcon+"</td><td width=5></td></tr><tr><td colspan=3></td></tr></table>";
			    			iW=23;
			    			iH=26;
			    			pt.iconHotX=10;
			    			pt.iconHotY=23;
			    		} else {
	            	iconCapt = thisIcon;
	            	iW = pt.iconSizeX;
	            	iH = pt.iconSizeY;
	            	pt.iconHotX=iW/2;
	            	pt.iconHotY=iH/2;
	            }
	            MurderChildren(this.parent.cScratch);
	            var scDiv = document.createElement("div");
	            scDiv.style.width = PX(iW);
	            scDiv.style.height = PX(iH);
	            scDiv.innerHTML = iconCapt;
	            this.parent.cScratch.appendChild(scDiv);
	            pt.iconW = scDiv.offsetWidth;
	            pt.iconH = scDiv.offsetHeight;
	        } else {
	        	iconCapt = "";
	        	pt.iconW = 0;
	        	pt.iconH = 0;
	        }

			    // work out the size of the label
			    var lbl;
			    if (pt.rawLabel.length==0) {
							lbl = GetLabel(usestyle,pt.pCaption,0,0,pt,this.parent);
	        } else {
	        	lbl = "<table style='z-index:"+String(zIndex+1+pt.zIndexMod)+";' cellspacing=0 cellpadding=0><tr><td width="+String(pt.iconW/2)+"></td><td>"+pt.rawLabel+"</td></tr></table>";
	        }

	        if (isArray(lbl)) {
	        	// width and height calculated for us
	        	labelCapt = lbl[0];
		        pt.labelW = lbl[1];
		        pt.labelH = lbl[2];
		        pt.labelDivW = lbl[3];
		        pt.divXShift = lbl[5];
		        pt.divYShift = lbl[6];
		        pt.closeButton = labelCapt.indexOf("_close"); // only closes if clicked on a control that ends with _close
	        } else {
	        	labelCapt = lbl;
		        MurderChildren(this.parent.cScratch);
		        var scDiv = document.createElement("div");
		        scDiv.style.width = "auto";
		        scDiv.innerHTML = labelCapt;
		        this.parent.cScratch.appendChild(scDiv);
		        pt.closeButton = false;

				    if (pt.rawLabel.length==0) {
			        if (scDiv.offsetWidth > this.parent.nCtrlWidth-64) {
			        	// max size = widht-64
			        	labelCapt = GetLabel(usestyle,pt.pCaption,this.parent.nCtrlWidth-64,0,pt,this.parent);
			        } else {
			        	labelCapt = GetLabel(usestyle,pt.pCaption,scDiv.offsetWidth,0,pt,this.parent);
			        }
			      } else {
		        	labelCapt = "<table width="+scDiv.offsetWidth+" cellspacing=0 cellpadding=0><tr><td width="+String(pt.iconW/2)+"></td><td>"+pt.rawLabel+"</td></tr></table>";
			      }
		
		        MurderChildren(this.parent.cScratch);
		        var scDiv = document.createElement("div");
		        scDiv.innerHTML = labelCapt;
		        this.parent.cScratch.appendChild(scDiv);
			       
		        pt.labelW = scDiv.offsetWidth;
		        pt.labelDivW = scDiv.offsetWidth;
		        pt.labelH = scDiv.offsetHeight;

				    if (pt.labelAnchorX=="labelcenter" || pt.labelAnchorX=="iconcenter") pt.divXShift =  -pt.iconW/2;
				    if (pt.labelAnchorY=="labelcenter" || pt.labelAnchorY=="iconcenter" || pt.labelAnchorY=="center") pt.divYShift = pt.iconH/2;

		        pt.labelH += pt.iconH;
		
	        }

		      labelCapt = URLRedirect(labelCapt,pt.userRef,"map",this.parent.noPopups);

			    switch (usestyle) {
			        case "default" :
	                default :
			            vCell.innerHTML = "<table width=40><tr><td>"+PNG(pt.img_fg,pt.img_fg_width,pt.img_fg_height,pt.pCaption)+"</td></tr></table>";
	
			            vDivBG = CopyLayerDiv(copyDiv);
			            pt.cDivBG = vDivBG;
	
			            if (pt.img_bg.length) {
			                vTableBG = document.createElement("table");
			                pt.cTableBG = vTableBG;
			                vDivBG.appendChild(vTableBG);
			                vRowBG = vTableBG.insertRow(-1);
			                vCellBG = vRowBG.insertCell(-1);
				                vTableBG.style.position = "relative";
				                vTableBG.cellSpacing = 0;
				                vTableBG.cellPadding = 0;
			                vCellBG.innerHTML = "<table width=40><tr><td>"+PNG(pt.img_bg,pt.img_bg_width,pt.img_bg_height,pt.pCaption)+"</td></tr></table>";
	                    } else {
	                        pt.cTableBG = null;
	                    }
	                    
	                    obWidth = pt.img_fg_width;
	                    obHeight = pt.img_fg_height;
	
			            break;

	                case "icon" :
			                vCell.innerHTML = iconCapt;
			                vCell.onclick = associateObjWithEvent(pt, "ProcessClick");

			                anchorX = pt.iconAnchorX;
			                anchorY = pt.iconAnchorY;
			                vDiv.style.zIndex=zIndex+pt.zIndexMod;
	                    break;

			        case "label" :
			        case "biglabel" :
			        case "onlylabel" :
			        case "labellimited" :
			        				vDiv.style.zIndex=zIndex+pt.zIndexMod+1;
			                vCell.innerHTML = labelCapt
			                if (pt.clickBehaviour=="toggle_all_label" || pt.clickBehaviour=="toggle_all_biglabel" || pt.closeButton) {
			                	vCell.onclick = associateObjWithEvent(pt, "ProcessClick");
			                	if (!pt.closeButton) vCell.style.cursor = "pointer";
			                }

			                if (iconCapt.length>0) {
			                	vRow2 = vTable.insertRow(-1);
			                	vCell2 = vRow2.insertCell(-1);
			                	vCell2.innerHTML = iconCapt
			                	vCell2.onclick = associateObjWithEvent(pt, "ProcessClick");
			                }

			                anchorX = pt.labelAnchorX;
			                anchorY = pt.labelAnchorY;
			            break;
			    }

	      } // dirtyContent
	      else {
	      	// use the last rendered thing
	      	vDiv = pt.cDiv;
  	    	vTable = pt.cTable;
	      }
	      
		    if (this.layerMoved || pt.dirtyContent) {
			    // use the anchor to calculate offsets
			    switch (pt.labelAnchorX) {
				    case "left" :
				    default: pt.labelX = this.parent.nCtrlWidth/2 + plotX + 0; break;
				    case "center" : pt.labelX = this.parent.nCtrlWidth/2 + plotX - pt.labelW/2; break;
				    case "labelcenter" : pt.labelX = this.parent.nCtrlWidth/2 + plotX -pt.labelW/2; break;
				    case "iconcenter" : pt.labelX = this.parent.nCtrlWidth/2 + plotX; break;
				    case "right" : pt.labelX = this.parent.nCtrlWidth/2 + plotX -pt.labelW; break;
			    }

			    switch (pt.labelAnchorY) {
				    case "top" :
				    default: pt.labelY = this.parent.nCtrlHeight/2 - plotY + 0; break;
				    case "center" : pt.labelY = this.parent.nCtrlHeight/2 - plotY - pt.labelH;  break;
				    case "labelcenter" : pt.labelY = this.parent.nCtrlHeight/2 - plotY -pt.labelH/2; break;
				    case "iconcenter" : pt.labelY = this.parent.nCtrlHeight/2 - plotY - pt.labelH; break;
				    case "bottom" : pt.labelY = this.parent.nCtrlHeight/2 - plotY -pt.labelH; break;
			    }

			    switch (pt.iconAnchorX) {
				    case "left" :
				    default: pt.iconX = this.parent.nCtrlWidth/2 + plotX + 0; break;
				    case "center" : pt.iconX = this.parent.nCtrlWidth/2 + plotX - pt.iconHotX; break;
				    case "right" : pt.iconX = this.parent.nCtrlWidth/2 + plotX -pt.iconW; break;
			    }

			    switch (pt.iconAnchorY) {
				    case "top" :
				    default: pt.iconY = this.parent.nCtrlHeight/2 - plotY + 0; break;
				    case "center" : pt.iconY = this.parent.nCtrlHeight/2 - plotY -pt.iconHotY; break;
				    case "bottom" : pt.iconY = this.parent.nCtrlHeight/2 - plotY -pt.iconH; break;
			    }

			    switch (usestyle) {
			        case "label" :
			        case "biglabel" :
			        case "onlylabel" :
			        case "labellimited" :
		                pt.pixX = pt.labelX+pt.divXShift;
		                pt.pixY = pt.labelY+pt.divYShift;
		                pt.pixW = pt.labelDivW;
		                pt.pixH = pt.labelH;
			            break;
			        case "icon" :
		                pt.pixX = pt.iconX;
		                pt.pixY = pt.iconY;
		                pt.pixW = pt.iconW;
		                pt.pixH = pt.iconH;
			            break;
			    }

			    if (vDivBG!=null) {
			        vTableBG.style.left = PX(pt.pixX);
			        vTableBG.style.top = PX(pt.pixY);
			        ret.push(vDivBG);
			    }

			    vTable.style.left = PX(pt.pixX);
			    vTable.style.top = PX(pt.pixY);
			    vDiv.style.width = PX(pt.pixW);
			    vDiv.style.height = PX(pt.pixH);
	
			    pt.cDiv = vDiv;
			    vTable.style.left="0px";
			    vTable.style.top="0px";
			    pt.cTable = vTable;
			    this.aPoints[pl] = pt;
			    pt.dirtyContent = false;
			    this.Revert(pt,pt.pixX,pt.pixY,this.parent);
			  } // layerMoved
			  
			  ret.push(pt.cDiv);

		} // is visible
	}

	this.layerMoved = false;

	return ret;
}


CAatPointLayer.prototype.SetZoom = function(zoom)
{
}


CAatPointLayer.prototype.Revert = function(pt, newX, newY, map)
{
  // reposition the div
  if (isNull(pt.cDiv)) return;

	pt.cDiv.style.left = PX(newX);
	pt.cDiv.style.top = PX(newY);

	if (pt.dirtySize) {
		pt.cDiv.style.display=(pt.isVisible?"block":"none");
		pt.cDiv.style.width = PX(pt.pixW);
		pt.cDiv.style.height = PX(pt.pixH);
		pt.dirtySize = false;
	}

	pt.pixX = newX;
	pt.pixY = newY;
return;
	
}


CAatPointLayer.prototype.MoveScreen = function(pDeltaX, pDeltaY, pixels)
{
	if (pixels) {
		deltaX = pDeltaX;
		deltaY = pDeltaY;
	} else {
		var nPixScale = (this.GetZoom()*2) / this.parent.nCtrlWidth;
		deltaX = pDeltaX / nPixScale;
		deltaY = pDeltaY / nPixScale;
	}
	
	for (al=0;al<this.aPoints.length;al++) {
		pt = this.aPoints[al];

       this.Revert(pt, pt.pixX+deltaX, pt.pixY+deltaY, this.parent);
		
	    pt.labelX+=deltaX;
	    pt.labelY+=deltaY;
	    pt.iconX+=deltaX;
	    pt.iconY+=deltaY;
	
	    if (!isNull(pt.cTableBG)) {
	        pt.cTableBG.style.left = PX(pt.pixX);
	        pt.cTableBG.style.top = PX(pt.pixY);
	    }
	}
	this.layerMoved = true;
}

CAatPointLayer.prototype.MoveMap = function(pDeltaX, pDeltaY)
{
	this.layerMoved = true;
}

CAatPointLayer.prototype.IsPoint = function(id)
{
	for (al=0;al<this.aPoints.length;al++) {
		pt = this.aPoints[al];
		if (pt.id==id) {
	        return true;
	    }
	}
	return false;
}


CAatPointLayer.prototype.FindPoint = function(id, altid)
{
	for (al=0;al<this.aPoints.length;al++) {
		pt = this.aPoints[al];
		if (pt.id==id) return this.aPoints[al];
		if (!isUndefined(altid) && pt.id==altid) return this.aPoints[al];
	}
	return null;
}


CAatPointLayer.prototype.DeletePoint = function(id)
{
	for (al=0;al<this.aPoints.length;al++) {
		pt = this.aPoints[al];
		if (pt.id==id) {
			this.aPoints.splice(al,1);
			return true;
		}
	}
	return false;
}


CAatPointLayer.prototype.RemoveAllPoints = function(exempt)
{
	if (isUndefined(exempt) || isNull(exempt)) {
    this.aPoints.splice(0,this.aPoints.length);
  } else {
  	var place=0;
  	while (place<this.aPoints.length) {
  		var skip=false;
  		for (var ex=0;ex<exempt.length && !skip;ex++) {
  			if (exempt[ex]==this.aPoints[place].id) skip=true;
  		}
  		if (skip) {
  			place++;
  		} else {
  			this.aPoints.splice(place,1);
  		}
  	}
  	
  }
}

CAatPointLayer.prototype.AddPoint = function(pX,pY,pCaption,id)
{
	var pt = new CAatPoint(pX,pY,pCaption,id,this.parent);
	pt.anchorX = this.anchorX;
	pt.anchorY = this.anchorY;
	pt.layer = this;
	this.aPoints.push(pt);
	return pt;
}

CAatPointLayer.prototype.SetCursor = function(parm)
{
//	this.cMasterTable.style.cursor = parm;
}


CAatPointLayer.prototype.SetType = function(newtype)
{
	this.nType = newtype;
}


function CAatBitmapLayer()
{
	this.parent = null;
	this.gridTop = 0;
	this.gridLeft = 0;
	this.gridRight = 0;
	this.gridBottom = 0;
	this.nOffsetX = 0;
	this.nOffsetY = 0;
	
	this.name = "";
	
	this.nType = "";

	this.nTileSize = 300;
	this.nTilePixels = this.nTileSize;
	this.cMasterTable = null;
	this.cMasterDiv = null;

	this.sBaseUrl = "";
	
	this.pos = 0;
	
	this.userData = null;
	
	this.customUrl = "";
	this.layerMoved = true;

	// general purpose arguments
	this.args1 = null;
	
	this.aOffscreen = new Array();
}

CAatBitmapLayer.prototype.SetCustomUrl = function(URL) { this.customUrl=URL; }
CAatBitmapLayer.prototype.GetCustomUrl = function() { return this.customUrl; }
CAatBitmapLayer.prototype.GetZoom = function() { return this.parent.nZoom; }
CAatBitmapLayer.prototype.GetX = function() { return this.parent.nX; }
CAatBitmapLayer.prototype.GetY = function() { return this.parent.nY; }
CAatBitmapLayer.prototype.GetTileZoom = function() { return this.parent.nTileZoom; }

CAatBitmapLayer.prototype.GetID = function()
{
	var ret = "maptile_"+this.parent.csContainer+"_"+String(this.parent.cellCounter);
	this.parent.cellCounter++;
	return ret;
}

CAatBitmapLayer.prototype.SetType = function(newtype)
{
	this.nType = newtype;
}


CAatBitmapLayer.prototype.SetCursor = function(parm)
{
	if (!this.cMasterTable) return;
	this.cMasterTable.style.cursor = parm;
	if (this.cSlider) this.cSlider.style.cursor = parm;
}



function KeyScroll(obj)
{
    if (obj.KeyDeltaX!=0 || obj.KeyDeltaY!=0) {

	    stepx = (obj.nScrollStepX/100) * obj.KeyDeltaX;
	    stepy = (obj.nScrollStepY/100) * obj.KeyDeltaY;

	    obj.SetX(obj.GetX()-stepx);
	    obj.SetY(obj.GetY()+stepy);

	    for (layer=0;layer<obj.aLayers.length;layer++) {
		    var ob = obj.aLayers[layer];
		    ob.MoveScreen(stepx,stepy,false);
		    ob.MoveMap(stepx,stepy);
	    }

        newSetTimeout(KeyScroll,20,obj)
    } else {
        obj.KeyTimerActive = 0;
    }
}


CAatMap.prototype.DocKeyUp = function(e,element)
{
	var KeyID = (window.event) ? event.keyCode : e.keyCode;
	
	switch (KeyID) {
	    case 37 : //left
	        this.KeyDeltaX = 0;
	        break;
	    case 39 : //right
	        this.KeyDeltaX = 0;
	        break;
	    case 38 : // up
	        this.KeyDeltaY = 0;
	        break;
	    case 40 : // down
	        this.KeyDeltaY = 0;
	        break;
	    case 33 : // pgup
	        this.doControlZoomOut(null,null);
	        break;
	    case 34 : // pgdn
	        this.doControlZoomIn(null,null);
	        break;
	}

}



CAatMap.prototype.DocKeyDown = function(e,element)
{
	var KeyID = (window.event) ? event.keyCode : e.keyCode;

    this.KeyDeltaPressed = KeyID;

	switch (KeyID) {
	    case 37 : //left
	        this.KeyDeltaX = +1;
	        break;
	    case 39 : //right
	        this.KeyDeltaX = -1;
	        break;
	    case 38 : // up
	        this.KeyDeltaY = +1;
	        break;
	    case 40 : // down
	        this.KeyDeltaY = -1;
	        break;
	}

    if ((this.KeyDeltaX!=0 || this.KeyDeltaY!=0) && this.KeyTimerActive==0) {
        this.KeyTimerActive=1;
        newSetTimeout(KeyScroll,20,this);
    }
	
}


function ImageCell(url,width,height)
{
	return "<img width='"+String(width)+"px' height='"+String(height)+"px' style='display:block;' src='"+url+"'>";
}


CAatMap.prototype.SetCursor = function(parm1)
{
	for (l=0;l<this.aLayers.length;l++) {
		var obj = this.aLayers[l];
		if (obj.SetCursor!=null) obj.SetCursor(parm1);
	}
	if (!this.cOutput) return;
	this.cOutput.style.cursor = parm1;
	if (this.cSlider!=null) this.cSlider.style.cursor = parm1;
}

function CMPlayers(a,b)
{
	if (a.pos<b.pos) return +1;
	if (a.pos>b.pos) return -1;
	return 0;
}


CAatMap.prototype.RemoveLayer = function(ref)
{
	for (l=0;l<this.aLayers.length;l++) {
		var match = false;
		if (isString(ref)) match = this.aLayers[l].name==ref;
		if (isObject(ref)) match = this.aLayers[l]==ref;
		if (match) {
			this.aLayers.splice(l,1);
			return true;
		}
	}
	return false;
}


CAatMap.prototype.GetLayer = function(name)
{
	for (l=0;l<this.aLayers.length;l++) {
		if (this.aLayers[l].name==name) {
			return this.aLayers[l];
		}
	}
	return null;
}


CAatMap.prototype.AddLayer = function(type,pos,name)
{
	switch (type) {
		case "point" :
			n = new CAatPointLayer();
			break;
		case "map" :
		case "sat" :
		case "hyb" :
		case "route" :
		default :
			n = new CAatBitmapLayer();
			break;
		case "poly" :
			n = new CAatPolygonLayer();
			break;
	}
	n.nX=this.nX;
	n.nY=this.nY;
	n.name=name;
	n.nZoom=this.nZoom;
	n.parent=this;
	n.SetType(type);
	n.pos = pos;
	var foundplace=0;
	
	this.aLayers.push(n);
	this.aLayers.sort(CMPlayers);
	
	return n;
}


CAatMap.prototype.SetCopyright = function(cr)
{

}

CAatMap.prototype.SetBranding = function(brand,width,height,tooltip,url) { 

}


function SmoothMove(objt,currx,curry,timeout,targetx,targety,signx,signy,stepnum,dostatus)
{
	stepx = targetx/10;
	stepy = targety/10;
	if (currx+stepx>targetx) stepx=targetx-currx;
	if (curry+stepy>targety) stepy=targety-curry;

	currx += stepx;
	curry += stepy;

	objt.SetX(objt.GetX()-signx*stepx);
	objt.SetY(objt.GetY()+signy*stepy);

	for (layer=0;layer<objt.aLayers.length;layer++) {
		var ob = objt.aLayers[layer];
		ob.MoveScreen(signx*stepx,signy*stepy,false);
		ob.MoveMap(signx*stepx,signy*stepy);
	}
	
	if (currx<targetx || curry<targety) {
    	newSetTimeout(SmoothMove,timeout,objt,currx,curry,timeout,targetx,targety,signx,signy,stepnum+1);
	} else {
		// end of scroll
		var initStatus = (!isUndefined(dostatus) && dostatus);
		objt.Init(initStatus);
	}
}


CAatMap.prototype.doControlDown = function(event, element)
{
	newSetTimeout(SmoothMove,10,this,0,0,20,0,this.nScrollStepY,+1,-1,0);
	return false;
}


CAatMap.prototype.doControlLeft = function(event, element)
{
	newSetTimeout(SmoothMove,10,this,0,0,20,this.nScrollStepX,0,+1,+1,0);
	return false;
}


CAatMap.prototype.doControlRight = function(event, element)
{
	newSetTimeout(SmoothMove,10,this,0,0,20,this.nScrollStepX,0,-1,+1,0);
	return false;
}


CAatMap.prototype.doControlUp = function(event, element)
{
	newSetTimeout(SmoothMove,10,this,0,0,20,0,this.nScrollStepY,+1,+1,0);
	return false;
}

CAatMap.prototype.doControlSatellite = function(event, element)
{
	for (l=0;l<this.aLayers.length;l++) {
		if (this.aLayers[l].nType=="map")
			this.aLayers[l].SetType("sat");
	}
	if (this.hybridLayer!=null) {
	    this.RemoveLayer(this.hybridLayer);
	    this.hybridLayer=null;
	}
	this.Init();
	return false;
}


CAatMap.prototype.doPoints = function(event, element)
{

/*
    if (this.GetTileZoom()>0.01) {
        alert("You need to zoom in closer to use this function.");
        return;
    }
*/
    gPoints = !gPoints;
    if (gPoints) gDatabase = false;
    
    this.AllMoved();

    if (this.RedrawCallback!=null) {
   		this.RedrawCallback(this,this.GetX(),this.GetY(),"doPoints");
   	}
   	
	return false;
}

CAatMap.prototype.doDragModeMove = function(event, element)
{
	if (this.dragMode=="m") return;
	this.dragMode = "m";
	this.Init();
}

CAatMap.prototype.doDragModeZoom = function(event, element)
{
	if (this.dragMode=="z") return;
	this.dragMode = "z";
	this.Init();
}


CAatMap.prototype.doControlMap = function(event, element)
{
	for (l=0;l<this.aLayers.length;l++) {
		if (this.aLayers[l].nType=="sat")
			this.aLayers[l].SetType("map");
	}
	if (this.hybridLayer!=null) {
	    this.RemoveLayer(this.hybridLayer);
	    this.hybridLayer=null;
	}
	this.Init();
	return false;
}

CAatMap.prototype.doControlHybrid = function(event, element)
{
	for (l=0;l<this.aLayers.length;l++) {
		if (this.aLayers[l].nType=="map")
			this.aLayers[l].SetType("sat");
	}

	if (this.hybridLayer==null) {
		// if not, add it
		this.hybridLayer = this.AddLayer("hyb",99,"hybrid");
	}
	
	this.Init();
	return false;
}


CAatMap.prototype.doControlNumbers = function(event, element)
{
	if (this.numbersLayer==null) {
		// if not, add it
		this.numbersLayer = this.AddLayer("num",99,"numbers");
	} else {
		this.RemoveLayer(this.numbersLayer);
		this.numbersLayer=null;
	}
	
	this.Init();
	return false;
}


CAatMap.prototype.doControlZoomIn = function(event, element)
{
	for (l=0;l<this.snaps.length;l++) {
		if (this.snaps[l]==this.GetTileZoom() && l<this.snaps.length-1) {
			this.SetTileZoom(this.snaps[l+1]);
			this.AllMoved();
			this.Init();
			return false;
		}
	}
	return false;
}


/**
 * Find a zoom level that will fit the numdeg into the screen
 */
CAatMap.prototype.FindZoom = function(numdegx, numdegy, notfound_override)
{
    tilesX = this.nCtrlWidth / this.nTileSize;
    tilesY = this.nCtrlHeight / this.nTileSize;
    
    if (isUndefined(numdegy)) {
    	// assume this is a square if not specified
    	numdegy = numdegx;
    }

		zoomX = numdegx / tilesX;
		zoomY = numdegy / tilesY;
		numdeg = zoomX<zoomY?zoomY:zoomX;

    for (l=1;l<this.snaps.length;l++) {
        if (this.snaps[l]<=numdeg) return this.snaps[l-1];
    }
    if (!isUndefined(notfound_override))
    	return notfound_override;
    else
    	return this.snaps[0];
}


CAatMap.prototype.FindBigger = function(closeto)
{
    tiles = this.nCtrlWidth / this.nTileSize;
    closeto /= tiles;
    for (l=1;l<this.snaps.length;l++) {
        if (this.snaps[l]<closeto) return this.snaps[l-1];
    }
    return this.snaps[this.snaps.length-1];
}


CAatMap.prototype.FindSnap = function(closeto)
{
	// like findbigger, but 25% tolerance downwards
    tiles = this.nCtrlWidth / this.nTileSize;
    closeto /= tiles;
    for (l=1;l<this.snaps.length;l++) {
        if (this.snaps[l]<closeto/1.25) return this.snaps[l-1];
    }
    return this.snaps[this.snaps.length-1];
}


CAatMap.prototype.doControlZoomOut = function(event, element)
{
	for (l=0;l<this.snaps.length;l++) {
		if (this.snaps[l]==this.GetTileZoom() && l>0) {
			this.SetTileZoom(this.snaps[l-1]);
			this.AllMoved();
			this.Init();
			return false;
		}
	}
	return false;
}


CAatMap.prototype.doSliderMouseDown = function(event, element)
{
	StopPropagation(event);
	this.nSliderPressed = true;
	this.nSliderClick=event.clientY;
	return false;
}


CAatMap.prototype.doSliderMouseMove = function(event, element)
{
	var nLeft = thumb_slider_left;
	var nTop = thumb_slider_top+4;

	if (!this.nSliderPressed) return;
	StopPropagation(event);
	var ydelta = event.clientY - this.nSliderClick;

	var spos = event.clientY - thumb_slider_top;
//	spos -= getAbsoluteTop(this.cContainer);
	spos -= getViewTop(this.cOutput);

	if (spos<8) spos=8;
	if (spos>111) spos=111;
	
	var tick = Math.floor((spos-4) / 8);
	this.nSliderZoom = this.snaps[tick];

	this.cSlider.style.top = PX(nTop + spos - 18);
	return false;
}


CAatMap.prototype.doSliderMouseUp = function(event, element)
{
	if (!this.nSliderPressed) return;
	StopPropagation(event);
	
	if (this.nSliderPressed) {
		this.JumpTo(this.GetX(),this.GetY(),this.nSliderZoom);
	}
	
	this.nSliderPressed = false;
	return false;
}


CAatMap.prototype.SliderUpdate = function()
{

	var nLeft = thumb_slider_left;
	var nTop = thumb_slider_top+4;
	// bottom to top, the zoom amounts

	// find out where we are in this array
	ourPos = 0;
	for (l=0;l<this.snaps.length;l++)
		if (this.GetTileZoom()==this.snaps[l]) ourPos=l;

	this.cSlider.style.width = PX(60);
	this.cSlider.style.height = PX(32);
	this.cSlider.style.left = PX( nLeft - 20 );
	this.cSlider.style.top = PX( nTop + ourPos * 8 - 10 );
}


function log10(n)
{
	return Math.log(n)/Math.LN10;
}

CAatMap.prototype.SetupScale = function()
{
	if (this.nCtrlWidth==0) return;

	var imgname = this.sImageBase+"scale.gif";
	var imgwidth = 512;
	var imgheight = 16;


	var newDiv = document.createElement("div");
	newDiv.style.position = "absolute";
	newDiv.style.zIndex = this.nZIndexBase+25;

	var width = this.nCtrlWidth/3;
	
	// work out how many m 1/3 of the map is
	var nDist = DistanceMeter(this.GetY(),this.GetX()-this.nZoom/3,this.GetY(),this.GetX()+this.nZoom/3);
	
	// get the scaling of the line
	var scale = nDist / width;
	
	// round to something nice
	var zeroes = Math.floor(log10(nDist));
	base = Math.round(nDist/Math.pow(10,zeroes));
	aDist = base * Math.pow(10,zeroes);
	nDist = aDist;
	
	// calculate final line length
	width = aDist/scale;
	
	// work out the units to use
	sDist = nDist.toFixed(0) + "m";
	
	if (nDist>1000)
		sDist = (nDist/1000).toFixed(1) + "km";
	if (nDist>10000)
		sDist = (nDist/1000).toFixed(0) + "km";

	var html = "<table cellspacing=0 cellpadding=0><tr height=18 style='vertical-align:top;'><td width=4></td>"+
		"<td width="+String(width/2)+"'><span style='font-weight: bold;font-family:arial;font-size:10px;color:white;vertical-align:top;'>&nbsp;0km</span></td>"+
		"<td align=right width="+String(width/2)+"><span style='font-weight: bold;font-family:arial;font-size:10px;color:white;vertical-align:top;'>"+sDist+"&nbsp;</span></td>"+
		"<td width=4></td>"+
		"</tr></table>";
	newDiv.innerHTML = html;
	newDiv.style.left = PX( 5+this.nScaleXOffset );
	newDiv.style.top = PX( this.nCtrlHeight - 21 );
	this.cOutput.appendChild(newDiv);

	var newDiv = document.createElement("div");
	newDiv.style.position = "absolute";
	newDiv.style.zIndex = this.nZIndexBase+25;
	
	var html = "<table cellspacing=0 cellpadding=0><tr height=18 valign=top style='vertical-align:top;'><td width=4></td>"+
		"<td width="+String(width/2)+"><span style='font-weight: bold;font-family:arial;font-size:10px;color:black;vertical-align:top;'>&nbsp;0km</span></td>"+
		"<td align=right width="+String(width/2)+"><span style='font-weight: bold;font-family:arial;font-size:10px;color:black;vertical-align:top;'>"+sDist+"&nbsp;</span></td>"+
		"<td width=4></td>"+
		"</tr></table>";
	newDiv.innerHTML = html;
	newDiv.style.left = PX( 4+this.nScaleXOffset );
	newDiv.style.top = PX( this.nCtrlHeight - 22 );
	this.cOutput.appendChild(newDiv);

	var newDivU = document.createElement("div");
	newDivU.style.zIndex = this.nZIndexBase+24;
	newDivU.innerHTML = GIF(imgname,imgwidth,imgheight,"");
	newDivU.style.left = PX( 4+this.nScaleXOffset );
	newDivU.style.top = PX( this.nCtrlHeight - 20 );
	newDivU.style.overflow = "hidden";
	newDivU.style.position = "absolute";
	newDivU.style.width = PX(4);
	newDivU.style.height = PX(18);
	this.cOutput.appendChild(newDivU);

	newDivU = document.createElement("div");
	newDivU.style.zIndex = this.nZIndexBase+24;
	newDivU.innerHTML = GIF(imgname,imgwidth,imgheight,"",-4,0);
	newDivU.style.left = PX( 8+this.nScaleXOffset );
	newDivU.style.top = PX( this.nCtrlHeight - 20 );
	newDivU.style.overflow = "hidden";
	newDivU.style.position = "absolute";
	newDivU.style.width = PX(width);
	newDivU.style.height = PX(18);
	this.cOutput.appendChild(newDivU);

	newDivU = document.createElement("div");
	newDivU.style.zIndex = this.nZIndexBase+24;
	newDivU.innerHTML = GIF(imgname,imgwidth,imgheight,"",-507,0);
	newDivU.style.left = PX( 8+this.nScaleXOffset + width );
	newDivU.style.top = PX( this.nCtrlHeight - 20 );
	newDivU.style.overflow = "hidden";
	newDivU.style.position = "absolute";
	newDivU.style.width = PX(4);
	newDivU.style.height = PX(18);
	this.cOutput.appendChild(newDivU);

}


CAatMap.prototype.SetupControls = function()
{
    var CENTER = -999;

	var controls = new Array (
		new Array (CENTER,0,this.sImageBase+"icons.png,64,256","doControlUp",32,17,"Pan North"),
		new Array (0,CENTER,this.sImageBase+"icons.png,0,256","doControlLeft",17,32,"Pan West"),
		new Array (-16,CENTER,this.sImageBase+"icons.png,30,256","doControlRight",17,32,"Pan East"),
		new Array (CENTER,-17,this.sImageBase+"icons.png,96,256","doControlDown",32,17,"Pan South"),
		new Array (6,0,this.sImageBase+"icons.png,129,252","doControlZoomOut",19,19,"Zoom Out"),
		new Array (6,19,this.sImageBase+"thumb_slider.png","*thumb",19,11,"Drag to zoom in/out"),
		new Array (6,19,this.sImageBase+"icons.png,230,170","*slider",17,119,"Drag to zoom in/out"),
		new Array (6,138,this.sImageBase+"icons.png,148,257","doControlZoomIn",19,19,"Zoom Closer"),
		new Array (-80,0,this.sImageBase+"icons.png,0,57,sat","doControlSatellite",78,19,"View Map as Aerial Photos"),
		new Array (-149,0,this.sImageBase+"icons.png,0,0,hybrid","doControlHybrid",78,19,"View Map as Street Map overlaying Aerial Photos"),
		new Array (-218,0,this.sImageBase+"icons.png,0,114,map","doControlMap",78,19	,"View Map as a Street Map"),
		new Array (-18,30,this.sImageBase+"icons.png,0,171,points","doPoints",19,78,"Turn Places On and Off"),
		new Array (-18,98,this.sImageBase+"icons.png,57,171,nums","doControlNumbers",19,78,"View Road Numbers"),
		new Array (30,0,this.sImageBase+"icons.png,95,0,modemove","doDragModeMove",75,19,"Makes the map move when you hold down the mouse button and drag"),
		new Array (105,0,this.sImageBase+"icons.png,95,38,modezoom","doDragModeZoom",69,19,"When you hold the left mouse button and drag, it will zoom into the area")
//		new Array (4,-21,this.sImageBase+"icons.png,168,256","*info",14,14,"")
	);
	offsetLeft = 0;
	offsetTop = 0;
	
	// set up some things
	var lMap = "off";
	var lHybrid = "off";
	var lNumbers = "off";
	var lSat = "off";
	var lPoints = "off";
	var lModeMove = "off";
	var lModeZoom = "off";

	for (l=0;l<this.aLayers.length;l++) {
		if (this.aLayers[l].nType=="map") lMap = "on";
		if (this.aLayers[l].nType=="sat") lSat = "on";
		if (this.aLayers[l].nType=="hyb") lHybrid = "on";
		if (this.aLayers[l].nType=="num") lNumbers = "on";
	}

	if (lHybrid=="on") lSat="off";

	if (gPoints) lPoints="on";

	if (this.GetTileZoom()>0.0025) {
		lNumbers="disabled";
	}

	if (!this.isSatellite) {
		lSat = "disabled";
		lHybrid = "disabled";
	}

	if (this.dragMode=="m") lModeMove = "on";
	if (this.dragMode=="z") lModeZoom = "on";
	

	for (al=0;al<controls.length;al++) {
		var newDiv = document.createElement("div");
			var el = controls[al];
		var notHot = false;
		var isModeButton = false;
		var isPointsButton = false;
		var drawThis = true;
		var inFront = false;
		
		var picSplit = el[2].split(",");
		var picName = "";
		var picX = 0, picY = 0;
		var picType = "";
		if (picSplit.length>1) {
			picName = picSplit[0];
			picX = picSplit[1]*1;
			picY = picSplit[2]*1;
			picType = picSplit[3];
			newDiv.id = "userctrl_"+picType;
		} else {
			picName = picSplit[0];
	    newDiv.id = "userctrl_"+String(al);
		}
		
		// need modifiers for the stateful buttons
		if (picType=="hybrid") {
			if (lHybrid=="on") inFront = true;
			if (lHybrid=="off") picY+=el[5]*1;
			if (lHybrid=="disabled") picY+=el[5]*2;
			if (!this.bModeButtons) drawThis=false;
			if (lHybrid=="disabled" && !this.bShowGreyButtons) drawThis=false;
		}

		if (picType=="nums") {
			if (lNumbers=="on") inFront = true;
			if (lNumbers=="off") picX+=el[4]*1;
			if (lNumbers=="disabled") picX+=el[4]*2;
			if (!this.bPointsButtons) drawThis=false;
			if (lNumbers=="disabled" && !this.bShowGreyButtons) drawThis=false;
		}

		if (picType=="sat") {
			if (lSat=="on") inFront = true;
			if (lSat=="off") picY+=el[5]*1;
			if (lSat=="disabled") picY+=el[5]*2;
			if (!this.bModeButtons) drawThis=false;
			if (lSat=="disabled" && !this.bShowGreyButtons) drawThis=false;
		}

		if (picType=="map") {
			if (lMap=="on") inFront = true;
			if (lMap=="off") picY+=el[5]*1;
			if (lMap=="disabled") picY+=el[5]*2;
			if (!this.bModeButtons) drawThis=false;
			if (lMap=="disabled" && !this.bShowGreyButtons) drawThis=false;
		}

		if (picType=="points") {
			if (lPoints=="on") inFront = true;
			if (lPoints=="off") picX+=el[4]*1;
			if (lPoints=="disabled") picX+=el[4]*2;
			if (!this.bPointsButtons) drawThis=false;
			if (lPoints=="disabled" && !this.bShowGreyButtons) drawThis=false;
		}

		if (picType=="modemove") {
			if (lModeMove=="on") inFront = true;
			if (lModeMove=="off") picY+=el[5]*1;
		}

		if (picType=="modezoom") {
			if (lModeZoom=="on") inFront = true;
			if (lModeZoom=="off") picY+=el[5]*1;
		}
		
    png = el[2];

    if (picName.length>0) {
    	html = PNG(picName,el[4],el[5],el[6],picX,picY,'userctrl_'+String(al));
    } else {
			html = PNG(png,el[4],el[5],el[6],0,0,'userctrl_'+String(al));
		}

		if (drawThis) {

		    if ((el[3]=="doControlHybrid" || el[3]=="doControlSatellite") && lSat=="disabled" ) notHot = true;
		    if (el[3]=="doPoints" && lPoints=="disabled" ) notHot = true;
		    if (el[3]=="doControlNumbers" && lPoints=="disabled" ) notHot = true;

		    if (el[3].length && el[3].slice(0,1)!="*" && !notHot) {
			    newDiv.onclick = associateObjWithEvent(this, el[3]);
		    }
		    
		    newDiv.innerHTML = html;
		    newDiv.style.position = "absolute";
		    if (!notHot) newDiv.style.cursor = "pointer";
		    
		    zIndex = this.nZIndexBase+25;
		    if (isModeButton && png.indexOf("_on")>0) zIndex++;
		    if (inFront) zIndex++;
		    
		    newDiv.style.zIndex = zIndex;
    		
		    var pLeft = el[0];
		    var pTop = el[1];
    		
		    if (pLeft==CENTER) {
		        pLeft = this.nCtrlWidth/2 - el[4]/2;
		    }

		    if (pTop==CENTER) {
		        pTop = this.nCtrlHeight/2 - el[5]/2;
		    }
    		
		    var posX = 0;
		    if (pLeft>=0)
			    posX = pLeft + offsetLeft;
		    else {
			    posX = this.nCtrlWidth + pLeft - offsetLeft + 1;
			    if (BrowserDetect.browser=="Explorer" && BrowserMode!="strict") posX-=2;
			  }

		    var posY = 0;
		    if (pTop>=0)
			    posY = pTop + offsetTop;
		    else
			    posY = this.nCtrlHeight + pTop - offsetTop + 1;
			  
//			  posY = YRelative(posY, this.cOutput);
//			  posX = XRelative(posX, this.cOutput);

		    newDiv.style.left = PX(posX);
		    newDiv.style.top = PX(posY);

		    this.cOutput.appendChild(newDiv);

		    if (el[3]=="*slider") {
			    // special setup for the slider
			    newDiv.style.cursor = "";
		    }

		    if (el[3]=="*info") {
		        gInfoDiv = newDiv;
		    }
    		
		    if (el[3]=="*thumb") {
			    // special setup for the thumb slider
			    newDiv.style.zIndex = this.nZIndexBase+50;
			    newDiv.style.left = PX(posX-20);
			    newDiv.style.top = PX(posY-10);
			    newDiv.innerHTML = "<table cellpadding=0 cellspacing=0 width=60 height=32><tr><td align=center>"+PNG(el[2],19,11,"",0,0,"thumbtracker","pointer")+"</td></tr></table>";
			    newDiv.onmousemove = associateObjWithEvent(this, "doSliderMouseMove");
			    newDiv.onmouseup = associateObjWithEvent(this, "doSliderMouseUp");
			    newDiv.style.cursor = this.sHandCursor;
    			
			    // the clicks on the background need to feed through to the main map
			    newDiv.onmousedown = associateObjWithEvent(this, "doMouseDown");
    			
			    this.cSlider=newDiv;
			    getObj("thumbtracker").onmousedown = associateObjWithEvent(this, "doSliderMouseDown");
			    this.SliderUpdate();
		    }
		} // drawThis

	}
	
}


CAatMap.prototype.BrandingClick = function(event, element)
{
	window.open(this.csBrandingUrl,"brandingwindow");	
}

function fPositionCopyright()
{
	if (this.bNoBranding) return;
	// put this at the bottom-right
	var newDiv = document.createElement("div");
	newDiv.innerHTML = "<font face=arial color=black style='font-size:11px;'><b>"+this.csCopyright+"</b></font>";
	newDiv.style.position = "absolute";
	newDiv.style.zIndex = this.nZIndexBase+25;
	newDiv.style.textAlign="right";
	this.cOutput.appendChild(newDiv);
	newDiv.style.left = PX( this.nCtrlWidth - newDiv.clientWidth - 4 - this.nCopyrightXMargin );
	newDiv.style.top = PX( this.nCtrlHeight - newDiv.clientHeight - 2 );

	newDiv = document.createElement("div");
	newDiv.innerHTML = "<font face=arial color=white style='font-size:11px;'><b>"+this.csCopyright+"</b></font>";
	newDiv.style.position = "absolute";
	newDiv.style.textAlign="right";
	newDiv.style.zIndex = this.nZIndexBase+25;
	this.cOutput.appendChild(newDiv);
	newDiv.style.left = PX( this.nCtrlWidth - newDiv.clientWidth - 5 - this.nCopyrightXMargin );
	newDiv.style.top = PX( this.nCtrlHeight - newDiv.clientHeight - 3 );

	var brDiv = document.createElement("div");
	brDiv.style.cursor = "pointer";


	this.csBranding = "http://www.streetmaps.co.za/img/is.png";
	this.nBranding_width=64; 
	this.nBranding_height=37; 
	this.csBrandingTooltip = "In Association with Internet Solutions";
	this.csBrandingUrl = "http://www.is.co.za";

	if (!isUndefined(this.csBranding) && this.nCtrlHeight>300) {
		if (!isNull(this.csBranding) && this.csBranding.length>0) {
			brDiv.innerHTML = PNG(this.csBranding,this.nBranding_width,this.nBranding_height,this.csBrandingTooltip);
		}
		if (this.csBrandingUrl.length>3) {
			brDiv.onmousedown = associateObjWithEvent(this, "BrandingClick");
		}
	}

	if (!isUndefined(this.csBranding) && this.nCtrlHeight>300) {
		if (!isNull(this.csBranding) && this.csBranding.length>0) {
			brDiv.innerHTML = PNG(this.csBranding,this.nBranding_width,this.nBranding_height,this.csBrandingTooltip);
		}
		if (this.csBrandingUrl.length>3) {
			brDiv.onmousedown = associateObjWithEvent(this, "BrandingClick");
		}
	}
	brDiv.style.position = "absolute";
	brDiv.style.zIndex = this.nZIndexBase+25;
	this.cOutput.appendChild(brDiv);
	brDiv.style.left = PX( this.nCtrlWidth - brDiv.clientWidth - this.nCopyrightXMargin - 4 );
	brDiv.style.top = PX( this.nCtrlHeight - brDiv.clientHeight - newDiv.clientHeight -4 );
	
	this.cCopyright = newDiv;
	this.cBranding = newDiv;
}



CAatBitmapLayer.prototype.MoveScreen = function(pDeltaX, pDeltaY, pixels)
{
	// move the layer by the specified amounts (map coordinates)
	var vTable = this.cMasterTable;

	if (pixels) {
		deltaX = pDeltaX;
		deltaY = pDeltaY;
	} else {
		var nPixScale = (this.GetZoom()*2) / this.parent.nCtrlWidth;
		deltaX = pDeltaX / nPixScale;
		deltaY = pDeltaY / nPixScale;
	}

	this.nOffsetX += deltaX;
	this.nOffsetY += deltaY;
	vTable.style.left = PX(this.nOffsetX);
	vTable.style.top = PX(this.nOffsetY);

	this.layerMoved = true;
}


function IntersectRectNve(ax1,ay1,ax2,ay2, bx1,by1,bx2,by2)
{
	return ( (ax1>=bx1 && ax1<=bx2 || ax2>=bx1 && ax2<=bx2 || ax1<=bx1 && ax2>=bx2 ) &&
			(ay1<=by1 && ay1>=by2 || ay2<=by1 && ay2>=by2 || ay1<=by1 && ay2>=by2  || ay1>=by1 && ay2<=by2) 
	);
}

function IntersectRect(ax1,ay1,ax2,ay2, bx1,by1,bx2,by2)
{
	return ( (ax1>=bx1 && ax1<=bx2 || ax2>=bx1 && ax2<=bx2 || ax1<=bx1 && ax2>=bx2 ) &&
			(ay1>=by1 && ay1<=by2 || ay2>=by1 && ay2<=by2 || ay1>=by1 && ay2<=by2 ) 
	);
}
	

CAatBitmapLayer.prototype.MoveMap = function(pDeltaX, pDeltaY)
{
	var vOrigTable = this.cMasterTable;
	var vTable = vOrigTable.cloneNode(true);
	var updated = false;

	// work out how many pixels to move by
	var scaler = this.nTileSize / this.GetTileZoom();
	pixX = pDeltaX * scaler;
	pixY = pDeltaY * scaler;

	// work out the coordinates covered in this map
	var left = this.GetX() - this.parent.DegWidth()/2;
	var top = this.GetY() + this.parent.DegHeight()/2;
	var right = this.GetX() + this.parent.DegWidth()/2;
	var bottom = this.GetY() - this.parent.DegHeight()/2;

//	this.parent.DebugPrint("Before: left="+String(left)+", top="+String(top)+", right="+String(right)+", bottom="+String(bottom));

	var nTileDegree = this.GetTileZoom() * 2;
	// now we need to check if we've gone out of bounds for displayed stuff
	while (left<=this.gridLeft) {

		// too far left .. insert a column there
		added=0;
		this.gridLeft -= nTileDegree;
		this.nOffsetX -= this.nTileSize;
		for (ly=0;ly<vTable.rows.length;ly++) {
			var vRow = vTable.rows[ly];
			var vCell = vRow.insertCell(0);
			vCell.id=this.GetID();
			vCell.width = PX(this.nTileSize);
			vCell.style.paddingBottom = 0;
			dX = this.gridLeft + 0 * nTileDegree + nTileDegree/2;
			dY = this.gridTop - (ly+1) * nTileDegree + nTileDegree/2;

			url = MapURL(this,this.nType,this.nTilePixels,this.nTilePixels,this.GetTileZoom(),dX,dY);
			this.aOffscreen.push(new Array(vCell.id,url));
//			this.parent.DebugPrint("Added: "+String(vCell.id)+" - "+url);
			vCell.innerHTML=gImageBlank;
			vCell.innerHTML = vCell.id;
			added++;
		}
		// pull the table left by a cell
//		this.parent.DebugPrint("Left: Added "+String(added)+" cells");
		vTable.style.left = PX(this.nOffsetX);
		updated = true;
	}
	
	while (right>=this.gridRight) {
		// too far right ... insert a column there
		added=0;
		this.gridRight += nTileDegree;
		for (ly=0;ly<vTable.rows.length;ly++) {
			var vRow = vTable.rows[ly];
			var vCell = vRow.insertCell(vRow.cells.length);
			vCell.id=this.GetID();
			vCell.width = PX(this.nTileSize);
			dX = this.gridRight + 0 * nTileDegree - nTileDegree/2;
			dY = this.gridTop - (ly+1) * nTileDegree + nTileDegree/2;
			url = MapURL(this,this.nType,this.nTilePixels,this.nTilePixels,this.GetTileZoom(),dX,dY);
			this.aOffscreen.push(new Array(vCell.id,url));
//			this.parent.DebugPrint("Added: "+String(vCell.id)+" - "+url);
			vCell.innerHTML=gImageBlank;
			added++;
		}
//		this.parent.DebugPrint("Right: Added "+String(added)+" cells");
		updated = true;
	}

	while (top>=this.gridTop) {
		// too far up ... insert a row there
		added=0;
		this.gridTop += nTileDegree;
		var vRow = vTable.rows[0];
		var colcount = vRow.cells.length;
		vRow = vTable.insertRow(0);
		this.nOffsetY -= this.nTileSize;
		for (lx=0;lx<colcount;lx++) {
			var vCell = vRow.insertCell(-1);
			vCell.id=this.GetID();
			vCell.width = PX(this.nTileSize);
			dX = this.gridLeft + lx * nTileDegree + nTileDegree/2;
			dY = this.gridTop + 0 * nTileDegree - nTileDegree/2;
			url = MapURL(this,this.nType,this.nTilePixels,this.nTilePixels,this.GetTileZoom(),dX,dY);
			this.aOffscreen.push(new Array(vCell.id,url));
//			this.parent.DebugPrint("Added: "+String(vCell.id)+" - "+url);
			vCell.innerHTML=gImageBlank;
			added++;
		}
		// pull the table up by a cell
		vTable.style.top = this.nOffsetY;
//		this.parent.DebugPrint("Top: Added "+String(added)+" cells");
		updated = true;
	}

	while (bottom<=this.gridBottom) {
		// too far up ... insert a row there
		added=0;
		this.gridBottom -= nTileDegree;
		var vRow = vTable.rows[0];
		var colcount = vRow.cells.length;
		vRow = vTable.insertRow(vTable.rows.length);
		for (lx=0;lx<colcount;lx++) {
			var vCell = vRow.insertCell(-1);
			vCell.id=this.GetID();
			vCell.width = PX(this.nTileSize);
			dX = this.gridLeft + lx * nTileDegree + nTileDegree/2;
			dY = this.gridBottom - 0 * nTileDegree + nTileDegree/2;
			url = MapURL(this,this.nType,this.nTilePixels,this.nTilePixels,this.GetTileZoom(),dX,dY);
			this.aOffscreen.push(new Array(vCell.id,url));
//			this.parent.DebugPrint("Added: "+String(vCell.id)+" - "+url);
			vCell.innerHTML=gImageBlank;
			added++;
		}
//		this.parent.DebugPrint("Bottom: Added "+String(added)+" cells");
		updated = true;
	}

	if (updated) {
		// swap in offscreen table
		this.cMasterDiv.replaceChild(vTable,vOrigTable);
		this.cMasterTable = vTable;
		// delete old table
		while (vOrigTable.childNodes.length >= 1) 
		{
			vOrigTable.removeChild(vOrigTable.firstChild);
		}
	}

	CheckOffscreen(this);
	this.layerMoved = true;

}


CheckOffscreen = function(obj)
{
	// check if anything onscreen needs the image loaded
	for (al=0;al<obj.aOffscreen.length;al++) {
		var off = obj.aOffscreen[al];
		var cell = getObj(off[0]);
		// if this is onscreen, show the image, delete from array
		if (cell==null) {
//		    this.parent.DebugPrint("Not Found: "+off[0]);
		} else {
			if (RectIntersect(
					cell.offsetLeft+obj.nOffsetX,
					cell.offsetTop+obj.nOffsetY,
					cell.offsetLeft+obj.nOffsetX+cell.offsetHeight,
					cell.offsetTop+obj.nOffsetY+cell.offsetHeight,
					0,0,obj.parent.nCtrlWidth,obj.parent.nCtrlHeight
					)) {
						cell.innerHTML = ImageCell(off[1],obj.nTileSize,obj.nTileSize);
						obj.aOffscreen.splice(al,1);
/*
						if (al>0) al--;
						newSetTimeout(CheckOffscreen,100,obj);
						return;
*/
			}

		} 
	}
}


CAatMap.prototype.ShowRoute = function(x,y,x1,y1,obj)
{
		if (routeLayer==null) {
			// if not, add it
			routeLayer = this.AddLayer("route",99,"route");
		}
		
		// set the routing layer
		routeLayer.routeStartX = x;
		routeLayer.routeStartY = y;
		routeLayer.routeEndX = x1;
		routeLayer.routeEndY = y1;
}


CAatMap.prototype.CalcRoute = function(x,y,x1,y1,obj)
{
		if (routeMarkerLayer==null) {
			routeMarkerLayer=this.AddLayer("point",99,"route");
			routeMarkerLayer.SetDefaultAnchor("center","bottom");
		}
		routeMarkerLayer.DeletePoint("route.start");
		var pt = routeMarkerLayer.AddPoint(x,y,"Route Start","route.start");
		pt.SetStyle("label");
		pt.SetIcon("icon_small/0.png");
        pt.SetIconAnchor("center","center");
		pt.SetLabelAnchor("left","bottom");

		routeMarkerLayer.DeletePoint("route.end");
		var pt = routeMarkerLayer.AddPoint(x1,y1,"Route End","route.end");
		pt.SetStyle("label");
		pt.SetIcon("icon_small/0.png");
    pt.SetIconAnchor("center","center");
		pt.SetLabelAnchor("left","bottom");
		
		this.ShowRoute(x,y,x1,y1,obj);

	    MurderChildren(getObj("resultsarea"));
	    sUrl = ScriptBase()+"ajax_route.asp?raw=1&fmt=js&jscb=AJAX_Route&x1="+String(x)+"&y1="+String(y)+"&x2="+String(x1)+"&y2="+String(y1)
	    // turn off points or database pane if they're there
	    gPoints = false;
	    gDatabase = false;

			callObj = new JSONscriptRequest(sUrl);
			callObj.Go(this.bShowProgress);
		
		this.Init(false);
}


CAatMap.prototype.MenuCallback = function(selected, x, y, obj)
{
	cX = getXRelative(x,obj.cContainer)-obj.nCtrlWidth/2;
	cX *= (obj.DegWidth()/obj.nCtrlWidth);
	cX += obj.GetX();

	cY = getYRelative(y,obj.cContainer)-obj.nCtrlHeight/2;
	cY *= (obj.DegHeight()/obj.nCtrlHeight);
	cY = obj.GetY() - cY;
	
	var mapReload = false;
	var processed = false;

	dirUpdate = false;

	if (selected=="abs.dir.from") {
	    selected = "dir.from";
	    cX = x;
	    cY = y;
	    processed=true;
	}

	if (selected=="abs.dir.to") {
	    selected = "dir.to";
	    cX = x;
	    cY = y;
	    processed=true;
	    mapReload=true;
	}

	if (selected=="dir.from") {
		routeStartX = cX;
		routeStartY = cY;
		dirUpdate = true;

		if (routeMarkerLayer==null) {
			routeMarkerLayer=obj.AddLayer("point",99,"route");
			routeMarkerLayer.SetDefaultAnchor("center","bottom");
		}
		routeMarkerLayer.DeletePoint("route.start");
		var pt = routeMarkerLayer.AddPoint(cX,cY,"Route Start","route.start");
		pt.SetStyle("label");
		pt.SetIcon("icon_small/0.png");
        pt.SetIconAnchor("center","center");
		pt.SetLabelAnchor("left","bottom");
	    processed=true;
	    mapReload=true;
	}

	if (selected=="dir.to") {
		routeEndX = cX;
		routeEndY = cY;
		dirUpdate = true;
		if (routeMarkerLayer==null) {
			routeMarkerLayer=obj.AddLayer("point",99,"route");
			routeMarkerLayer.SetDefaultAnchor("center","bottom");
		}
		routeMarkerLayer.DeletePoint("route.end");
		var pt = routeMarkerLayer.AddPoint(cX,cY,"Route End","route.end");
		pt.SetStyle("label");
		pt.SetIcon("icon_small/0.png");
        pt.SetIconAnchor("center","center");
		pt.SetLabelAnchor("left","bottom");
	    processed=true;
	    mapReload=true;
	}

	if (selected=="dir.clear") {
		routeStartX = 0;
		routeStartY = 0;
		routeEndX = 0;
		routeEndY = 0;
		if (routeLayer!=null) {
			obj.RemoveLayer(routeLayer);
			routeLayer=null;
			if (routeMarkerLayer!=null) {
				routeMarkerLayer.DeletePoint("route.start");
				routeMarkerLayer.DeletePoint("route.end");
			}
			getObj("resultsarea").innerHTML=gResultsAreaHTML;
			obj.Init();
		}
		mapReload=true;
		processed=true;
	}
	
	if (dirUpdate) {
		// re-initialise map
		obj.Init();
		if (routeStartX!=0 && routeStartY!=0 && routeEndX!=0 && routeEndY!=0) {
    	    obj.CalcRoute(routeStartX,routeStartY,routeEndX,routeEndY,this);
    	}
	} else {
	    if (mapReload) obj.Init();
	}
	
	if (!processed && obj.PopupMenuCallback!=null) {
		obj.PopupMenuCallback(obj,selected,cX,cY);
	}
}



function ShowZoomRect(obj,x,y,wid,hei)
{
		if (obj.dragRect==null) {
			obj.dragRect = document.createElement("div");
			obj.dragRect.onmousemove = associateObjWithEvent(obj, "doMouseMove");
			obj.dragRect.onmouseup = associateObjWithEvent(obj, "doMouseUp");
			obj.dragRect.id = "drag_rect";
			obj.cContainer.appendChild(obj.dragRect);
			obj.dragRect.innerHTML="<table width=100% height=100% background='"+PNG_GIF(obj.sImageBase+"/transparentzoom")+"'><tr><td></td></tr></table>";
			obj.dragRect.style.cursor = obj.sMagnifyCursor;
			obj.dragRect.style.overflow = "hidden";
			obj.dragRect.style.position = "absolute";
			obj.dragRect.style.zIndex = obj.nZIndexBase+50;
		}

    if (BrowserDetect.browser=="Explorer") {
        x -= getAbsoluteLeft(obj.cContainer);
        y -= getAbsoluteTop(obj.cContainer);
    }
    
    // take into account the scroll
    x += getScrollX();
    y += getScrollY();
		
		if (wid<0) {
			obj.dragRect.style.left = PX(x+wid-1);
			obj.dragRect.style.width = PX(-wid);
		} else {
			obj.dragRect.style.left = x;
			obj.dragRect.style.width = PX(wid);
		}
		
		if (hei<0) {
			obj.dragRect.style.top = PX(y+hei-1);
			obj.dragRect.style.height = PX(-hei);
		} else {
			obj.dragRect.style.height = PX(hei);
			obj.dragRect.style.top = PX(y);
		}
}



function CAatMenu(container,x,y,obj)
{
	this.x=x;
	this.y=y;
	this.container=container;
	this.menuDiv=null;
	this.tableMain=null;
	this.MenuCallback = null;
	this.obj=obj;
}


CAatMenu.prototype.doMouseDown = function(event, element)
{
	StopPropagation(event);
	return false;
}


function SrcElement(ev)
{
	if (BrowserDetect.browser=="Explorer") return ev.srcElement;
	
	var node = ev.target;
	while(node.nodeType != node.ELEMENT_NODE)
		node = node.parentNode;
		
	return node;
}

CAatMenu.prototype.doMouseOver = function(event, element)
{
	element.className = "rcmenu_hover";
	StopPropagation(event);
	return false;
}

CAatMenu.prototype.doMouseOut = function(event, element)
{
	element.className = "rcmenu_option";
	StopPropagation(event);
	return false;
}

CAatMenu.prototype.Remove = function()
{
	MurderChildren(this.menuDiv);
}

CAatMenu.prototype.doMouseClick = function(event, element)
{
	MurderChildren(this.menuDiv);
	StopPropagation(event);

	retX = this.x;
	retY = this.y;
	
	if (BrowserDetect.browser=="Explorer") {
	    retX -= getAbsoluteLeft(this.container);
	    retY -= getAbsoluteTop(this.container);
	}
	
	this.MenuCallback(element.id,retX,retY,this.obj);
	
	return false;
}

CAatMenu.prototype.doCloseMenu = function(event, element)
{
	if (SrcElement(event)==this.tableMain) {
		MurderChildren(this.menuDiv);
		StopPropagation(event);
	}
	return false;
}


CAatMenu.prototype.AddMenuOption = function(vTable, caption, icon, id)
{
	var vRow = vTable.insertRow(-1);
	var vCell = vRow.insertCell(-1);
	if (icon.length>0) vCell.innerHTML = PNG(icon);
	var vCell = vRow.insertCell(-1);
	vCell.innerHTML = caption;
	vCell.className = "rcmenu_option";
	vCell.noWrap = true;
	vCell.onmouseover = associateObjWithEvent(this, "doMouseOver");
	vCell.onmouseout = associateObjWithEvent(this, "doMouseOut");
	vCell.onclick = associateObjWithEvent(this, "doMouseClick");
	vCell.id = id;
}


CAatMenu.prototype.AddMenuDivider = function(vTable)
{
	var vRow = vTable.insertRow(-1);
	    vRow.style.height="5px";
	var vCell = vRow.insertCell(-1);
	    vCell.colSpan = 2;
	    vCell.innerHTML = "<hr class=menu>";
}


CAatMenu.prototype.ShowMenu = function()
{
	this.menuDiv = document.createElement("div");
	this.menuDiv.id = "menudiv";
	this.menuDiv.style.position = "absolute";
	this.menuDiv.style.zIndex = 250;
	document.body.appendChild(this.menuDiv);
	
	var vTable = document.createElement("table");
	this.tableMain = vTable;
		vTable.onmouseout = associateObjWithEvent(this, "doCloseMenu");
		vTable.className = "rcmenu";
		vTable.cellSpacing = 0;
		vTable.cellPadding = 2;
		vTable.id ="vtable";

	var vRow = vTable.insertRow(-1);
	var vCell = vRow.insertCell(-1);
	    vCell.colSpan=2;
		vCell.innerHTML = "Select An Option";
		vCell.noWrap = true;
		vCell.className = "rcmenu_heading";

	this.AddMenuOption(vTable,"Directions FROM Here","img/dir_from.png","dir.from");
	this.AddMenuOption(vTable,"Directions TO Here","img/dir_to.png","dir.to");
	this.AddMenuOption(vTable,"Clear Directions","","dir.clear");
	this.AddMenuOption(vTable,"Zoom to Street Level Here","","zoom.street");
	this.AddMenuOption(vTable,"Attach Your Photo here","","user.photo");

	this.AddMenuDivider(vTable);
	this.AddMenuOption(vTable,"<b>Personal Placemarkers</b>","","");
	this.AddMenuOption(vTable,"Add Personal Placemarker here","","placemarker.add");
	this.AddMenuOption(vTable,"Remove all Personal Placemarkers","","placemarker.remove");
	this.AddMenuDivider(vTable);
	this.AddMenuOption(vTable,"Close Menu","","");

	vTable.style.visibility = "hidden";
	this.menuDiv.appendChild(vTable);

	this.menuDiv.style.left = 0;
	this.menuDiv.style.top = 0;
	
	// make sure it stays in the container

  placeX = this.x-4;
  placeY = this.y-16;
  rightside = placeX+vTable.clientWidth-getAbsoluteLeft(this.container);
  bottomside = placeY+vTable.clientHeight-getAbsoluteTop(this.container);

	if (rightside > this.container.clientWidth) {
	    placeX += (this.container.clientWidth - rightside);
	    placeX -= 8;
//		placeX = (this.container.clientWidth - vTable.clientWidth-8) + getAbsoluteLeft(this.container);
	}

	if (bottomside > this.container.clientHeight) {
		placeY += (this.container.clientHeight - bottomside);
		placeY -= 8;
	}

	vTable.style.visibility = "visible";
	
	this.menuDiv.style.left = placeX;
	this.menuDiv.style.top = placeY;

}




CAatMap.prototype.doMouseUp = function(event, element)
{
  // pixels to count as a single click
  singleclick_threshold = 3;
	if (event.button==REF_RIGHT_BUTTON()) {
		// rt button
		if (this.bPopupMenu) {
			if (gMenu!=null) {
			    gMenu.Remove();
			    gMenu=null;
			}
			gMenu = new CAatMenu(this.cOutput, event.clientX, event.clientY, this);
			gMenu.MenuCallback=this.MenuCallback;
			gMenu.ShowMenu();
			return false;
		}
	} else {
    var srcEl = SourceElement(event)
    
    if (srcEl.id.slice(0,9)=="userctrl_" || srcEl.id.slice(0,9)=="mappoint_") {
    	// clicked on a user control or point - exit now
			this.SetCursor(this.GetBaseCursor());
			this.bDragging = false;
    	return;
    }

    retX = event.clientX - getAbsoluteLeft(this.cContainer) + getScrollX();
    retY = event.clientY - getAbsoluteTop(this.cContainer) + getScrollY();
    if (BrowserDetect.browser=="Explorer") {
    	retX-=2;
    	retY-=2;
    }

    obj = this;
    cX = retX-obj.nCtrlWidth/2;
    cX *= (obj.DegWidth()/obj.nCtrlWidth);
    cX += obj.GetX();

    cY = retY-obj.nCtrlHeight/2;
    cY *= (obj.DegHeight()/obj.nCtrlHeight);
    cY = obj.GetY() - cY;

    var deltaX = event.clientX-this.nOrigClickX;
    var deltaY = event.clientY-this.nOrigClickY;
		
		if (this.dragMode=="z") {
			if (!this.bDragging) return;
			this.SetCursor(this.GetBaseCursor());
			this.bDragging = false;

			// check if this is a single click
	    if (Math.sqrt(deltaX*deltaX + deltaY*deltaY) <= singleclick_threshold && this.ClickCallback!=null) {
	        this.ClickCallback(this,cX,cY);
	        return;
	    }

		// we have end drag coords, find beginning
      retX2 = this.nClickX - getAbsoluteLeft(this.cContainer);
      retY2 = this.nClickY - getAbsoluteTop(this.cContainer);
      retY2 += getScrollY();
      retX2 += getScrollX();
    
	    cX2 = retX2-obj.nCtrlWidth/2;
	    cX2 *= (obj.DegWidth()/obj.nCtrlWidth);
	    cX2 += obj.GetX();
	
	    cY2 = retY2-obj.nCtrlHeight/2;
	    cY2 *= (obj.DegHeight()/obj.nCtrlHeight);
	    cY2 = obj.GetY() - cY2;

	    // find the center and zoom level
	    cenX = (cX+cX2)/2;
	    cenY = (cY+cY2)/2;
	    xSize = Math.abs(cX-cX2);
	    ySize = Math.abs(cY-cY2);
	    if (xSize>ySize) cenZ = xSize/2;
	    	else cenZ = ySize/2;
			cenZ/=(this.nCtrlWidth/this.nTileSize);
			newZ = this.FindZoom(cenZ, cenZ, this.snaps[this.snaps.length-1]);
			if (newZ==this.nTileZoom) {
				newZ = this.FindSnap(cenZ);
			}

	    this.JumpTo(cenX, cenY, newZ);
	    MurderChildren(obj.dragRect);
	    obj.dragRect=null;
	    this.Init();
		}

		if (this.dragMode=="m") {
			if (!this.bDragging) return;
			this.SetCursor(this.GetBaseCursor());
			this.bDragging = false;

				// check if this is a single click
		    if (Math.sqrt(deltaX*deltaX + deltaY*deltaY) <= singleclick_threshold && this.ClickCallback!=null) {
//alert("x["+String(retX)+"] y["+String(retY)+"]");
		        this.ClickCallback(this,cX,cY);
		        return;
		    }
		    
			this.DoStatusUpdate();
	
	     if (this.RedrawCallback!=null) {
	     		this.RedrawCallback(this,this.GetX(),this.GetY(),"doMouseUp");
	    	}
		} // 	if (this.dragMode=="m") {
			return false;
		}
}



CAatMap.prototype.doMouseDown = function(event, element)
{
	var sel = SourceElement(event);
	if (sel.id.slice(0,8)=="genlink_") return false;

	if (event.button==REF_LEFT_BUTTON()) {
		if (this.dragMode=="m" || this.dragMode=="z") {
			this.bDragging = true;
			this.nClickX = event.clientX;
			this.nClickY = event.clientY;
			this.nOrigClickX = event.clientX;
			this.nOrigClickY = event.clientY;
		}

		if (this.dragMode=="z") {
//			ShowZoomRect(this,this.nOrigClickX,this.nOrigClickY,1,1);
		}

		if (this.dragMode=="m") {
			this.SetCursor(this.sGrabCursor);
		}
	}
	return false;
}


CAatMap.prototype.doMouseMove = function(event, element)
{
	if (this.dragMode=="m" || this.dragMode=="z") {
		if (!this.bDragging) return false;
		var deltaX = event.clientX-this.nClickX;
		var deltaY = event.clientY-this.nClickY;
	}

	if (this.dragMode=="z") {
		if (!this.bDragging) return false;
		ShowZoomRect(this,this.nOrigClickX,this.nOrigClickY,deltaX,deltaY);
	}

	if (this.dragMode=="m") {
		if (!this.bDragging) return false;
	
		var vTable = this.cMasterTable;
		
		var deltaX = event.clientX-this.nClickX;
		var deltaY = event.clientY-this.nClickY;
	
		var nPixScale = (this.nZoom*2) / this.nCtrlWidth;
		var pDeltaX = nPixScale * deltaX;
		var pDeltaY = nPixScale * deltaY;
		
		this.SetX(this.GetX()-pDeltaX);
		this.SetY(this.GetY()+pDeltaY);
	
		for (layer=0;layer<this.aLayers.length;layer++) {
			var ob = this.aLayers[layer];
			ob.MoveScreen(deltaX,deltaY,true);
			ob.MoveMap(pDeltaX,pDeltaY);
		}
		
		this.nClickX = event.clientX;
		this.nClickY = event.clientY;
	}
	
	return false;
}


CAatMap.prototype.doMouseEnter = function(event, element)
{
}

CAatMap.prototype.doMouseLeave = function(event, element)
{
    origElement = event.originalTarget?event.originalTarget:event.srcElement;

    if (origElement.id=="map_workspace")
	    return this.doMouseUp(event,element);
}



function CopyLayerDiv(copyDiv)
{
	var vvDiv = document.createElement("div");
	vvDiv.style.position="absolute";
	vvDiv.style.width = PX(unPX(copyDiv.style.width));
	vvDiv.style.height = PX(unPX(copyDiv.style.height));
	vvDiv.style.overflow = "hidden";
	
	return vvDiv;
}

CAatMap.prototype.doDoubleClick = function(event, element)
{
	var vTable = this.cMasterTable;

  retX = getXRelative(event.clientX,this.cContainer);
  retY = getYRelative(event.clientY,this.cContainer);

	var nClickX = this.nCtrlLeft+this.nCtrlWidth/2;
	var nClickY = this.nCtrlTop+this.nCtrlHeight/2;
	var deltaX = retX-nClickX;
	var deltaY = retY-nClickY;



	var nPixScale = (this.nZoom*2) / this.nCtrlWidth;
	var pDeltaX = nPixScale * deltaX;
	var pDeltaY = nPixScale * deltaY;
	var newX = this.GetX()+pDeltaX;
	var newY = this.GetY()-pDeltaY;
	
	// click the zoom in by one
	newzoom = this.GetTileZoom();
	for (l=0;l<this.snaps.length;l++) {
		if (this.snaps[l]==newzoom && l<this.snaps.length-1) {
			newzoom = this.snaps[l+1];
			break;
		}
	}
	
	this.JumpTo(newX,newY,newzoom);

}
CAatMap.prototype.doMouseScroll = function(event, element)
{
    if (!event) /* For IE. */
            event = window.event;
    if (event.wheelDelta) { /* IE/Opera. */
            delta = event.wheelDelta/120;
            /** In Opera 9, delta differs in sign as compared to IE.
             */
            if (window.opera)
                    delta = -delta;
    } else if (event.detail) { /** Mozilla case. */
            /** In Mozilla, sign of delta is different than in IE.
             * Also, delta is multiple of 3.
             */
            delta = -event.detail/3;
    }
    /** If delta is nonzero, handle it.
     * Basically, delta is now positive if wheel was scrolled up,
     * and negative, if wheel was scrolled down.
     */
    if (delta < 0){
    /* Zoom out */
            var vTable = this.cMasterTable;

            retX = getXRelative(event.clientX,this.cContainer);
            retY = getYRelative(event.clientY,this.cContainer);

            var nClickX = this.nCtrlLeft+this.nCtrlWidth/2;
	        var nClickY = this.nCtrlTop+this.nCtrlHeight/2;
	        var deltaX = retX-nClickX;
	        var deltaY = retY-nClickY;

	        var nPixScale = (this.nZoom*2) / this.nCtrlWidth;
	        var pDeltaX = nPixScale * deltaX;
	        var pDeltaY = nPixScale * deltaY;
	        var mX = this.GetX()+pDeltaX;
	        var mY = this.GetY()-pDeltaY;
        	
        	
	        var newX = this.GetX() - (mX - this.GetX());
	        var newY = this.GetY() - (mY - this.GetY());
	        
	        // click the zoom out by one
	        newzoom = this.GetTileZoom();
	        
	        for (l=this.snaps.length; l > 0;l--) {	        
		        if (this.snaps[l]==newzoom && l < this.snaps.length) {
			        newzoom = this.snaps[l-1];
			        break;			        
		       } 
	        }
	        if (newzoom != this.snaps[0]){
	            this.JumpTo(newX,newY,newzoom);	        
	        }
	        
		
    } else{
    /* Zoom In */
		    var vTable = this.cMasterTable;

            retX = getXRelative(event.clientX,this.cContainer);
            retY = getYRelative(event.clientY,this.cContainer);

	        var nClickX = this.nCtrlLeft+this.nCtrlWidth/2;
	        var nClickY = this.nCtrlTop+this.nCtrlHeight/2;
	        var deltaX = retX-nClickX;
	        var deltaY = retY-nClickY;



	        var nPixScale = (this.nZoom*2) / this.nCtrlWidth;
	        var pDeltaX = nPixScale * deltaX;
	        var pDeltaY = nPixScale * deltaY;
	        var mX = this.GetX()+pDeltaX;
            var mY = this.GetY()-pDeltaY;
	
	        var newX = this.GetX() - ((mX - this.GetX()) * -.5);
	        var newY = this.GetY() - ((mY - this.GetY()) * -.5); //this.GetY();
        	
	        // click the zoom in by one
	        newzoom = this.GetTileZoom();
	        for (l=0;l<this.snaps.length;l++) {
		        if (this.snaps[l]==newzoom && l<this.snaps.length-1) {
			        newzoom = this.snaps[l+1];
			        break;			        
		        }
	        }
        	if (newzoom != this.snaps[this.snaps.length]){
	            this.JumpTo(newX,newY,newzoom);	        
	        }	        
	  }	        
    if (event.preventDefault)
    event.preventDefault();
    event.returnValue = false;
}

CAatBitmapLayer.prototype.RenderLayer = function(copyDiv, zIndex)
{
	// work out the coordinates covered in this map
	var vDiv = CopyLayerDiv(copyDiv);
	this.cMasterDiv = vDiv;

	var Cleft = this.GetX()- this.parent.DegWidth()/2;
	var Ctop = this.GetY() + this.parent.DegHeight()/2;
	var Cright = this.GetX() + this.parent.DegWidth()/2;
	var Cbottom = this.GetY() - this.parent.DegHeight()/2;

	// work out grid coordinates that will include this window
	var nTileDegree = this.GetTileZoom() * 2;
	this.gridLeft = Cleft - Cleft % nTileDegree;
	this.gridTop = Ctop - Ctop % nTileDegree;
	this.gridRight = Cright + ( nTileDegree - Cright % nTileDegree);
	this.gridBottom = Cbottom - ( nTileDegree - Math.abs(Cbottom) % nTileDegree);
	
	// expand the grid by 1 block each way, to make for more attractive dragging and better preloading
	this.gridLeft -= nTileDegree;
	this.gridTop += nTileDegree;
	this.gridRight += nTileDegree;
	this.gridBottom -= nTileDegree;
	
	// work out how many tiles are needed
	var tilesX = Math.round((this.gridRight - this.gridLeft) / nTileDegree);
	var tilesY = Math.abs(Math.round((this.gridBottom - this.gridTop) / nTileDegree));
	
	// calculate the offset of the first tile, ie. origin outside the window
	var scaler = this.nTileSize / nTileDegree;
	this.nOffsetX = (this.gridLeft - Cleft) * scaler;
	this.nOffsetY = (Ctop - this.gridTop) * scaler;

	// create a table
	var vTable = document.createElement("table");
	vDiv.appendChild(vTable);
	vTable.style.zIndex = zIndex;
	vTable.style.position = "relative";
	vTable.cellSpacing = 0;
	vTable.cellPadding = 0;

	vTable.style.left = PX(this.nOffsetX);
	vTable.style.top = PX(this.nOffsetY);

	vTable.id = this.csOutput+"_table";
	this.cMasterTable = vTable;

	// create the tiles
	for (ly=0;ly<tilesY;ly++) {
		var vRow = vTable.insertRow(-1);
		vRow.style.zIndex = zIndex;
		vRow.height = PX(this.nTileSize);
		for (lx=0;lx<tilesX;lx++) {
			var vCell = vRow.insertCell(-1);
			vCell.id=this.GetID();
			vCell.width = PX(this.nTileSize);
			vCell.height = PX(this.nTileSize);
			vCell.style.zIndex = zIndex;
			vCell.style.paddingBottom = "0px";
			dX = this.gridLeft + lx * nTileDegree + nTileDegree/2;
			dY = this.gridTop - (ly+1) * nTileDegree + nTileDegree/2;

			// check if this is visible or not
			var visible = false;
			if (IntersectRectNve(dX-this.GetTileZoom(), dY+this.GetTileZoom(), dX+this.GetTileZoom(), dY-this.GetTileZoom(), Cleft,Ctop,Cright,Cbottom))
				visible = true;
			else
				visible = false;

			var imageurl = "";
			var url = MapURL(this,this.nType,this.nTilePixels,this.nTilePixels,this.GetTileZoom(),dX,dY);

			if (visible) {
				imageurl = ImageCell(url,this.nTileSize,this.nTileSize);
			} else {
				imageurl = gImageBlank;
				this.aOffscreen.push(new Array(vCell.id,url));
			}
			vCell.innerHTML=imageurl;
		}
	}

	this.layerMoved = false;

	return new Array(vDiv);
}


function RedoInit(obj)
{
	obj.Init();
}



function AJAX_Detail(ctrlname, dat)
{
	gWait.Stop();

	if (dat.type!="detail") {
	    alert("AJAX Error");
	    return;
	}
	
	var obj = MapArray[ctrlname];

	if (!isUndefined(obj)) {
		var map = obj.mapobj;
		var layername = dat.layername;

		// pop all points on the layer down to icons

		for (l=0;l<eval(layername).aPoints.length;l++) {
			eval(layername).aPoints[l].SetStyle("icon");
			eval(layername).aPoints[l].SetCaption("");
		}

		var pt = eval(layername).FindPoint(dat.poiref);
		if (isNull(pt)) {
			pt = eval(layername).FindPoint("point."+String(dat.inside));
		}
		
		if (isNull(pt)) {
			// need to add the point
			var id = "point."+dat.ref;
      var pt = fixedPoiLayer.AddPoint(dat.x*1,dat.y*1,"",dat.poiref);
      pt.userRef=dat.ref;
      pt.SetStyle("icon");
      pt.SetIcon(CheckIcon(dat.icon,16));
      pt.SetCallback(ReplaceBubbleInplace);
      pt.SetTooltip(dat.caption);
      pt.SetIconAnchor("center","center");
      pt.SetLabelAnchor("left","bottom");
      pt.SetClickBehaviour("toggle_icon_biglabel");
      pt.SetVisible(0,dat.cutoff);
		}

	  var icn = CheckIcon(dat.icon,16);
	  var htmlCaption = CreateCaption(dat,"point."+dat.ref,icn,"points",dat.layername,ctrlname);
	
		for (var pl=0;pl<eval(layername).aPoints.length;pl++) {
			if (eval(layername).aPoints[pl].id != dat.poiref) {
				eval(layername).aPoints[pl].style="icon";
			}
		}
	
		pt.SetCaption(htmlCaption);
		pt.SetStyle("biglabel");
		map.Init(false);
		pt.EnsureVisible(false);
	}
}


function ReplaceBubble(ref, poiref, layername, ctrlname)
{
	if (!layername) layername="poiLayer";
  var sUrl = ScriptBase()+"ajax_detail.asp?fmt=js&ctrl="+ctrlname+"&jscb=AJAX_Detail&ref="+ref+"&pref="+poiref+"&ln="+layername;
	callObj = new JSONscriptRequest(sUrl);
	callObj.Go(false);
}


function RemoveBubble(ref, poiref, layername, ctrlname)
{
	var pt = eval(layername).FindPoint(poiref);
	if (!layername) layername="poiLayer";
	pt.SetCaption("");
	pt.SetStyle("icon");
	pt.SetCallback(ReplaceBubbleInplace);
	var obj = MapArray[ctrlname];
	obj.mapobj.Init();
}


function ReplaceBubbleInplace(pt)
{
	if (pt.pCaption.length>0) {
		RemoveBubble(String(pt.userRef),"point."+String(pt.userRef),'fixedPoiLayer',pt.parent.csContainer);
	} else {
		ReplaceBubble(String(pt.userRef),"point."+String(pt.userRef),'fixedPoiLayer',pt.parent.csContainer);
	}
}

var fixedPoiLayer = null;
var prevCreds = "X";


function AJAX_Status(ctrlname,dat)
{
	gWait.Stop();

	var obj = MapArray[ctrlname];
	if (!isUndefined(obj)) {
		var map = obj.mapobj;
		
		if (map.gAds) {
		
			if (!isUndefined(dat.results.error)) return;
			
			var dirty = false;
	
			if (!isUndefined(dat.results) && !isUndefined(dat.results.points)) {
		
		    var exempt = new Array();
		    
		    dirty = true;
		
		    if (fixedPoiLayer==null) {
		        fixedPoiLayer = map.AddLayer("point",75,"points");
		        fixedPoiLayer.SetDefaultAnchor("center","center");
		    } else {
			    // see if anything's popped up
			    var poppedUp = "";
					for (var pl=0;pl<fixedPoiLayer.aPoints.length;pl++) {
						if (fixedPoiLayer.aPoints[pl].GetStyle()!="icon") {
							poppedUp = fixedPoiLayer.aPoints[pl].id;
							exempt.push(fixedPoiLayer.aPoints[pl].id);
						}
					}
		    	fixedPoiLayer.RemoveAllPoints(exempt.length>0?exempt:null);
		    }
		    
				for (var l=0;l<dat.results.points.length;l++) {
					var pnt = dat.results.points[l];
					// only update if it's not exempt

					var skip = false;
					for (var e=0;e<exempt.length;e++) {
						if (exempt[e] == "point."+pnt.ref) skip = true;
					}
					if (!pnt.pri) skip=1;

					if (!skip) {
						htmlCaption = "";
						var id = "point."+pnt.ref;
			      var pt = fixedPoiLayer.AddPoint(pnt.x*1,pnt.y*1,htmlCaption,id);
			      pt.userRef=pnt.ref;
			      pt.SetStyle(id==poppedUp?"biglabel":"icon");
			      pt.SetIcon(CheckIcon(pnt.icon,16));
			      pt.SetIconStyle("bubble");
			      pt.SetCallback(ReplaceBubbleInplace);
			      pt.SetTooltip(pnt.caption);
			      pt.SetIconAnchor("center","center");
			      pt.SetLabelAnchor("left","bottom");
			      pt.SetClickBehaviour("toggle_all_biglabel");
			      pt.SetVisible(0,pnt.cutoff);
			    }
				}
			} // gAds
		}

	var creds = "";

  if (!isUndefined(dat.results.credits)) {
	  for (l=0;l<dat.results.credits.length;l++) {
	      var cr = dat.results.credits[l];
	      var cred = cr.value.split(",");
	      copybit = "";
	      if (cred[2].length>0) {
	      	copybit = " &copy;" + cred[2];
	      }
	      credbit = cred[0] + copybit + " <span class='sm_copyright' style='cursor:pointer;text-decoration:underline;' onclick=\"CreditWindow('"+cred[3]+"');\">" + cred[1] + "</span><br>";
	      creds = creds + credbit
	      if (creds != prevCreds) {
	      	dirty = true;
		      prevCreds = creds;
	      }
	  }

		map.csCopyright=creds;
	}


		if (map.AJAX_Status_Callback!=null) {
			map.AJAX_Status_Callback(dat);
		} else {
	    if (dirty) map.Init();
		}
	}
}




CAatMap.prototype.Init = function(callbacks)
{
	if (this.cContainer.clientWidth==0) {
		newSetTimeout(RedoInit,100,this);
		return;
	}

	if (!this.bHotKeyAssoc && this.bUseHotkeys) {
	  document.onkeyup = associateObjWithEvent(this, "DocKeyUp");
  	document.onkeydown = associateObjWithEvent(this, "DocKeyDown");;
  	this.bHotKeyAssoc = true;
  }

	
	if (this.cScratch==null) {
		// create a scratch div
		this.cScratch = document.createElement("div");
		this.cScratch.style.position = "absolute";
		this.cScratch.style.visibility = "hidden";
		this.cScratch.style.left=PX(0);
		this.cScratch.style.top=PX(0);
		document.body.appendChild(this.cScratch);
		
		this.SetCopyright();
		this.SetBranding();
	}

	if (this.baseOverride=="") {
		var sc = document.getElementsByTagName('script');
		for (l=0;l<sc.length;l++) {
			var src = sc[l].src;
			src = src.toLowerCase();
			if (src.indexOf("aatmaps.js")>0) {
				src = src.substring(0,src.indexOf("aatmaps.js")-1)
				this.baseOverride = src;
			}
		}
	}

	if (this.cContainer==null) {
		this.DebugPrint("ERROR: Init - No Container Set");
		return;
	}
	
	// clean out the div
	while (this.cContainer.childNodes.length >= 1) 
	{
		this.cContainer.removeChild(this.cContainer.firstChild);
	}

	var oWidth = isNull(this.cContainer.clientWidth)?0:this.cContainer.clientWidth;
	var oHeight = isNull(this.cContainer.clientHeight)?0:this.cContainer.clientHeight;
	
	this.cOutput = document.createElement("div");
	this.bMouseAssoc=false;
	this.cOutput.id = "map_workspace";
	this.cContainer.appendChild(this.cOutput);
	this.cOutput.style.overflow = "hidden";
	this.cOutput.style.position = "absolute";
	
	if (BrowserDetect.browser=="Firefox" || BrowserDetect.browser=="Mozilla") {
		oWidth-=2;
		oHeight-=2;
	}
	this.cOutput.style.width = PX(oWidth);
	this.cOutput.style.height = PX(oHeight);
	
	this.nCtrlWidth = oWidth;
	this.nCtrlHeight = oHeight;
	
	if (BrowserDetect.browser=="Explorer") {
		this.nCtrlLeft = getAbsoluteLeft(this.cOutput);
		this.nCtrlTop = getAbsoluteTop(this.cOutput);	
	}

	this.nCtrlAbsLeft = getAbsoluteLeft(this.cOutput);
	this.nCtrlAbsTop = getAbsoluteTop(this.cOutput);

	var nXTiles = this.nCtrlWidth / this.nTileSize;

	if (this.nTileZoom==0) {
	    // work out the tile zoom to fit in the entire country
	    // needs to be 10.24 in total
	    target = 8.5 / nXTiles;
		for (l=this.snaps.length-1;l>=0;l--) {
		    if (this.snaps[l]>target) {
		        this.nTileZoom = this.snaps[l];
		        break;
		    }
		}
	}

	// work out the zoom, nNormalZoom = size for a tile
	this.nZoom = this.nTileZoom * nXTiles;

	this.nScrollStepX = this.nZoom * 1.5;
	this.nScrollStepY = this.nZoom * 1.5;

	if (this.bUseMouse && !this.bMouseAssoc) {
		this.cOutput.onmouseover = associateObjWithEvent(this, "doMouseEnter");
		this.cOutput.onmouseout = associateObjWithEvent(this, "doMouseLeave");
		this.cOutput.onmouseup = associateObjWithEvent(this, "doMouseUp");
		if (BrowserDetect.browser=="Webkit" && BrowserDetect.OS=="Mac") {
			this.cOutput.oncontextmenu = associateObjWithEvent(this, "doMouseUp");
		}
		this.cOutput.onmousedown = associateObjWithEvent(this, "doMouseDown");
		this.cOutput.onmousemove = associateObjWithEvent(this, "doMouseMove");
		this.cOutput.ondblclick = associateObjWithEvent(this, "doDoubleClick");
		
		this.cOutput.onmousewheel = associateObjWithEvent(this, "doMouseScroll");
		//For FireFox - Because FireFox Doesnt have onmousewheel event handler
		 if (this.cOutput.addEventListener) {
                this.cOutput.addEventListener ("DOMMouseScroll", associateObjWithEvent(this, "doMouseScroll"), false);
        }
		this.bMouseAssoc = true;
	}

	document.onmouseout = associateObjWithEvent(this, "MouseOutDragArea");

	// set the border of the container to something
	this.cOutput.style.border = "1px solid "+this.colBorder;

	this.PositionCopyright();

	if (this.bNavigate) this.SetupControls();
	if (this.bScaleBar) this.SetupScale();

	for (layer=0;layer<this.aLayers.length;layer++) {
		lr = this.aLayers[layer];
		lyr = lr.RenderLayer(this.cOutput,this.nZIndexBase+layer);
		for (al=0;al<lyr.length;al++) {
			this.cOutput.appendChild(lyr[al]);
		}
	}
	

	if (gInfoDiv!=null) {
	    gInfoDiv.title="header=[Map Information] body=[<b>X</b>: "+String(this.GetX().toFixed(6))+" <br><b>Y</b>: "+String(this.GetY().toFixed(6))+"<br>]";
	}

    var doCallbacks = callbacks;
    if (isUndefined(callbacks)) doCallbacks = true;

    if (this.RedrawCallback!=null && doCallbacks) {
   		this.RedrawCallback(this,this.GetX(),this.GetY(),"Init");
   	}

    if (doCallbacks) this.DoStatusUpdate();

		this.SetCursor(this.GetBaseCursor());
		this.nSerial++;

//    CheckStats();
}


CAatMap.prototype.DoStatusUpdate = function()
{
	var moved = Math.sqrt( Math.pow(this.GetX()-this.lastUpdX,2) + Math.pow(this.GetY()-this.lastUpdY,2) );
	// must have moved >20% of the screen
	if (this.lastUpdZ != this.DegWidth() || moved > this.DegWidth()/5) { 
		this.lastUpdZ = this.DegWidth();
		this.lastUpdY = this.GetY();
		this.lastUpdX = this.GetX();
	} else return;

		if (this.NoStatusCallback) return;
				
		var s = "m";
		for (l=0;l<this.aLayers.length;l++) {
			if (this.aLayers[l].nType=="sat") s="s";
		}
		if (this.hybridLayer!=null) s="h";
		p="0";

    // callback to update on-screen elements
    sUrl = ScriptBase()+"ajax_status.asp?fmt=js&jscb=AJAX_Status&ctrl="+this.csContainer+"&x1="+String(this.GetX()-this.DegWidth()/2)+"&y1="+String(this.GetY()+this.DegHeight()/2)+"&x2="+String(this.GetX()
    	+this.DegWidth()/2)+"&y2="+String(this.GetY()-this.DegHeight()/2)+"&x="+String(this.GetX())+"&y="+String(this.GetY())+"&z="+String(this.GetTileZoom())+"&ts="+this.nTileSize+"&m="+s+"&p="+p+"&key="+this.APIKey+"&serial="+String(this.nStatusSerial);

    if (sUrl!=this.status_url) {
    	this.nStatusSerial++;
			callObj = new JSONscriptRequest(sUrl);
			callObj.Go(this.bShowProgress);
	    this.status_url = sUrl;
	  }
}



CAatMap.prototype.Dispose = function()
{
  document.onkeyup = null;
 	document.onkeydown = null;
	MurderChildren(this.cScratch);
	MurderChildren(this.cOutput);
	MurderChildren(this.cContainer);
}


CAatMap.prototype.SetOutput = function(ctrlname)
{
	this.csContainer = ctrlname;
	this.cContainer = getObj(this.csContainer);
	if (isUndefined(MapArray[ctrlname])) {
		MapArray[ctrlname] = new CMapArrayElement(ctrlname, this);
	}
	
	// make sure the container has some critical styles
	this.cContainer.style.overflow="hidden";

//	this.DebugPrint("Control Set to ["+this.csOutput+"]");
//	this.DebugPrint("Control Details: width="+String(this.cOutput.clientWidth)+" height="+String(this.cOutput.clientHeight));
}


CAatMap.prototype.DebugPrint = function(str)
{
	if (this.cDebug==null) return;
	if (this.csDebug="") return;
	var d = this.cDebug.value;
	var now = new Date();
	var newline = FixedString(now.getHours(),2) + ":" + FixedString(now.getMinutes(),2) + ":" + FixedString(now.getSeconds(),2) + "." + FixedString(now.getMilliseconds(),3) + " ";
	newline = newline + str + "\n";
	d = newline + d;
	this.cDebug.value = d;
}

var sessVal = "";
var sessIteration = 0;
var sessComplete = "";
var sessUnique = "";

function SessionVar()
{
		if (sessVal.length==0) {
			var com = eval(sessComplete);
			return;
		}

    var iterSize = 1000; // chars per iteration
    bit = sessVal.substr(0,iterSize);
    sessVal = sessVal.substr(iterSize);
    sUrl = ScriptBase()+"ajax_session.asp?n="+sessUnique+"&r="+String(sessVal.length)+"&jscb=SessionVar();&i="+String(sessIteration)+"&v="+bit;
		callObj = new JSONscriptRequest(sUrl);
		callObj.Go(false);
		sessIteration++;
}


CAatMap.prototype.SetSessionVar = function(unique, val, oncomplete)
{
	sessVal = "["+val+"]";
	sessIteration = 0;
	sessUnique = unique;
	sessComplete = oncomplete;
	SessionVar();
}

CAatMap.prototype.ZoomBounds = function(x,y,x1,y1)
{
	if (isArray(x)) {
		y=x[1];
		x1=x[2];
		y1=x[3];
		x=x[0];
	}
	cenX = (x1*1+x*1)/2;
	cenY = (y1*1+y*1)/2;
	cenZ1 = Math.abs(y1*1-y*1)/2
	cenZ2 = Math.abs(x1*1-x*1)/2;
	cenZ = (cenZ1<cenZ2)?cenZ2:cenZ1;
	cenZ = this.FindZoom(cenZ);
  this.JumpTo(cenX, cenY, cenZ);

}


CAatMap.prototype.SetDebug = function()
{
	this.wDebug = window.open("","debugwindow","dependant=1,height=300,width=600,resizable=1");
	this.wDebug.document.write("<textarea cols=70 rows=15 id='debugwindow' NAME='debugwindow'></textarea>");
//	this.csDebug = ctrlname;
	this.cDebug = this.wDebug.document.getElementById("debugwindow");
//	this.cDebug.value = "";
	this.DebugPrint("Debug Activated");
}

CAatMap.prototype.MouseOutDragArea = function(event, element) 
{
    var outWindow = false;

    if (BrowserDetect.browser=="Explorer") {
        outWindow = (window.event.toElement==null);
    } else {
        if (event.target.firstChild) {
            if (event.target.firstChild.tagName=="HEAD") outWindow=true;
        }
    }

    if (outWindow) {
        return this.doMouseUp(event,element);
    }
}

// dynamic Nielsen integration

var stTicker = 0;

function CheckStats()
{

	if (!nsInit2) {
	    if (typeof nol_t != 'undefined') {
if(self['nol_t']) {
	trac = nol_t({cid:"za-alwaysactive",content:"0",server:"secure-za"}).record().post();
}
            nsInit2=true;
		    return;
	    }
	}

	if (!nsInit) {
		nsInit = true;
	}

    stTicker++;
    if (stTicker<100) {
	    setTimeout("CheckStats()", 100);
    }


}



var gaTicker = 0;
var pageTracker = null;
var gaInit = false;

function LScriptBase()
{
	var scr = document.getElementsByTagName("script");
	var ret = "";
	for (var l=0;l<scr.length;l++) {
		var src = scr[l].src;
		if (src.indexOf("aatmaps.js")>0) {
			ret = src.substr(0,src.indexOf("aatmaps.js"));
		}
	}
	return ret;
}

function CheckGA()
{
	if (typeof _gat != 'undefined') {
		pageTracker = _gat._getTracker("UA-3984417-1");
		pageTracker._trackPageview("/main");
		return;
	}

	gaTicker++;
	if (gaTicker<100) {
		setTimeout("CheckGA()", 100);
	}
}


if (!gaInit) {
	gaInit = true;
	var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
	var gaUrl = gaJsHost + "google-analytics.com/ga.js";
	callObj = new JSONscriptRequest(gaUrl);
	callObj.noCache="";
	callObj.Go(false);
	setTimeout("CheckGA()", 100);
}


function LoadStats()
{
/* nielsen
		callObj = new JSONscriptRequest("http://secure-za.imrworldwide.com/v60.js");
		callObj.noCache = "";
		callObj.Go(false);
*/
}

/* nielsen
if (BrowserDetect.browser=="Explorer") {
	addLoadEvent(LoadStats);
} else {
	document.write('<scr' + 'ipt type="text/javascript" src="http://secure-za.imrworldwide.com/v60.js"></scr' + 'ipt>');
}
*/

