

// constructor for the raster stuff
// ParentDiv is the id of the container element where the map will live
// config has pixel sizes (pixelWidth and pixelHeight

function mapRaster(parentDiv, config)
{
  var previousmouse = 'mousemove';
  var posMouseDown = [];
  var dragMouseStart = [];
//  var dragging = false;
  var self = this;
	function dragF(evt)
	{
	  
	  function redraw(dX, dY, drag)
	  {
//	    setStatus2('redraw '+dX+','+dY + ','+drag)
  	  var p1 = self.pointOfPixels(dragMouseStart)
      var p2 = self.pointOfPixels([evt.clientX, evt.clientY])
      var W = dragW + (p1[0] - p2[0])
      var S = dragS + (p1[1] - p2[1])
      var E = dragE + (p1[0] - p2[0])
      var N = dragN + (p1[1] - p2[1])
			self.moveLayers(dX, dY)
      self.drawRaster (W, S, E, N, drag)
	  }
	  
//    setStatus ('event ' + evt.type + ' ' + evt.clientX + ' ' + evt.clientY + ' ' + 'button:' + evt.button)
  	if (!evt) var evt = window.event
		var target = evt.target?evt.target:(evt.srcElement)?evt.srcElement:null
//		if ((target.id != 'dragger' && target.className != 'noCSS') || !self.dragging)
//		{
//		  alert ('target='+target.id+','+document.body.id)
//		  return
//		}

//    if (target == document.body)
//      alert ('doc')
        
		switch (evt.type)
		{
			case 'mousedown':
//        setStatus2('md='+evt.which+' * ' + evt.button)
        if (self.timer) clearTimeout (self.timer)
        if (evt.button==2) // right
        {
//          showMenuByName(evt, 'mapMenu')
	        var posx = evt.clientX - getLeft(target, true);
          var posy = evt.clientY - getTop(target, true);
          self.lastPointClicked = self.pointOfPixels([posx, posy]);
        }
        else
        {
  				dragMouseStart = [evt.clientX, evt.clientY]
  				dragW = self.mapW
  				dragS = self.mapS
  				dragE = self.mapE
  				dragN = self.mapN
  				previousmouse = 'mousedown';
  			}
        break

      case 'click':
        previousmouse = 'mousedown'
      case 'mouseup':
//        setStatus2('mu='+evt.which+' * ' + evt.button)
        if (evt.button==2) // right
          return false
          
        if (self.dragging) 
        {
          redraw(0, 0, false)
          self.drawLayers(true)
        }
        self.dragging = false;
        if(previousmouse == 'mousedown')
        {
          if (self.onClick){
		        var posx = evt.clientX - getLeft(self.dragger, true);
            var posy = evt.clientY - getTop(self.dragger, true);
		        var p = self.pointOfPixels([posx, posy]);
            self.onClick(p);
          }
        }
        previousmouse = 'mouseup';
        break;
        
      case 'mouseout':
//        setStatus2('mout='+evt.which+' * ' + evt.button)
      	previousmouse = 'mouseout';
        break // do nothing
        alert('???')
        if (self.dragging) 
        {
          redraw(0, 0, false)
          self.drawLayers(true)
        }
      	self.dragging = false
      	previousmouse = 'mouseout';
      	break

      case 'mousemove':
//        setStatus2('mm='+evt.which+' * ' + evt.button)
//        setStatus2 ('mr '+ evt.clientX + ', '+evt.clientY)
// hide any menus in a while        
        //clearTimeout(mnuTimer);
        //mnuTimer = setTimeout (hideMenu, 500)
        var dX = evt.clientX - dragMouseStart[0]
			  var dY = evt.clientY - dragMouseStart[1]
        if(!self.dragging)
        {
          if((Math.abs(dX) > 5 || Math.abs(dY) > 5) && previousmouse == 'mousedown')
					{
					  self.dragging = true;
					}
					else
					{
					  var posx = evt.clientX - getLeft(self.dragger, true)
            var posy = evt.clientY - getTop(self.dragger, true)
            var p = self.pointOfPixels([posx, posy])
            if (self.mousePoint)
              self.mousePoint(p)
					}
        }
	    	if (self.dragging)
	    	{
	    	  redraw(dX, dY, true)
//  				dragMouseStart = [evt.clientX, evt.clientY]
  				previousmouse = 'mousemove';
  				if (self.onDrag) self.onDrag()
	    	}
	    	
		    break
            			
	  } // switch
	  return false
  }// dragF

  function scroll(delta) {
  	if (delta < 0)
  	  self.zoomIn(0.5) // OUT (same as Google)
  	else
  	  self.zoomIn(2.0) // IN
  }

  function wheel(event){
  	var delta = 0;
  	if (!event) event = window.event;
  	if (event.wheelDelta) {
  		delta = event.wheelDelta/120; 
  		if (window.opera) delta = -delta;
  	} else if (event.detail) {
  		delta = -event.detail/3;
  	}
  	if (delta)
  		scroll(delta);
    if (event.preventDefault)
      event.preventDefault();
    event.returnValue = false;
  }

  var SN = ['S', 'N']
  var WE = ['W', 'E']
  
  this.dragging = false

  this._dragF = dragF
    
  this.mapSet = null
                   
  this.divCache = []
  this.loadedTiles = []
  this.recycling = false
//  this.wheel = wheel
// set up mouse wheel handler
  if (window.addEventListener)
        // DOMMouseScroll is for mozilla
    window.addEventListener('DOMMouseScroll', wheel, false);
  window.onmousewheel = document.onmousewheel = wheel;
  
  if (!parentDiv)
  {
    var parentDiv = document.createElement('div')
    parentDiv.style.position = 'absolute'
    parentDiv.style.left = '0px'
    parentDiv.style.top = '0px'
    parentDiv.style.width = '100%'
    parentDiv.style.height = '100%'
  }
//  parentDiv.style.zIndex = 0
  
  this.divRaster = parentDiv

  if (config)
  {
    this.pixelWidth  = config.pixelWidth
    this.pixelHeight = config.pixelHeight
    this.fixScales = config.fixScales ? config.fixScales : false
  }
  else
  {
    this.pixelWidth  = 600
    this.pixelHeight = 600
    this.fixScales = false
  }

  this.divRaster.style.width = this.pixelWidth + 'px' //'100%'
  this.divRaster.style.height = this.pixelHeight + 'px' //'100%'

// doesn't seem to matter whether we create the dragger first or the tileParent

// create a tile parent
  var tileParent = document.createElement('div')
  tileParent.id = 'tileParent'
//  tileParent.style.visibility = 'hidden'
  tileParent.style.position = 'absolute'
  tileParent.style.left = '0px'
  tileParent.style.top = '0px'
//  tileParent.style.zIndex = 900
  parentDiv.appendChild(tileParent)
  this.tileParent = tileParent
//  tileParent.onclick = function (){alert('click')}
  
  disableSelection(tileParent);

// create a layer parent
  var layerParent = document.createElement('div')
  layerParent.id = 'layerParent'
//  layerParent.style.visibility = 'visible'
  layerParent.style.position = 'absolute'
  layerParent.style.left = '0px'
  layerParent.style.top = '0px'
//  layerParent.style.zIndex = 900
  parentDiv.appendChild(layerParent)
  this.layerParent = layerParent
  layerParent.onclick = function (){alert('layer click')}
  
  disableSelection(layerParent);

// create a dragger div if none defined
  var dragger = document.createElement('div')
  dragger.id = 'dragger'
//  dragger.src=''
//  dragger.style.visibility = 'visible'
//  dragger.style.width=this.pixelWidth
//  dragger.style.height=this.pixelHeight
  parentDiv.appendChild(dragger)
  disableSelection(dragger);
  
  dragger.oncontextmenu = function(evt){showMenuByName(evt, 'mapMenu');return false}
  
  addEvent (dragger, 'mousedown', self._dragF)
  addEvent (dragger, 'mouseup',   self._dragF)
  addEvent (dragger, 'mousemove', self._dragF)
  addEvent (dragger, 'mouseout',  self._dragF)
  addEvent (document, 'mouseup',  self._dragF)
  addEvent (document, 'mousemove',  self._dragF)

  this.dragger = dragger

// create a loading image
  var dloading = document.createElement('div')
  dloading.style.visibility = 'hidden'
  dloading.style.position = 'absolute'
  dloading.style.left = (this.pixelWidth/2-18)+'px'
  dloading.style.top = (this.pixelHeight/2-18)+'px'
  dloading.style.zIndex = 9999
  dloading.style.backgroundColor = 'white'
  var imgLoading = document.createElement('img')
  imgLoading.src='images/loading4.gif'
  imgLoading.style.width='36px'
  imgLoading.style.height='36px'
  dloading.appendChild(imgLoading)
  parentDiv.appendChild(dloading)
  this.dloading = dloading


  this.onDrawLayer = null
    
  this.layers = []  // initialise other map layers
  
/* each element is
 {type, - image or point
  name, 
  path, // callback fSyntax string - delivers image ref for image layer, [{point, name}] for point layer
  img, //the overlaid map image (image layer only)
  div, //the overlaid map container div (point layer only)
  displayed, //whether user wants displayed
  loaded, //whether validly loaded
  layerImg // the menu control image for status report
 }  
*/  
}

mapRaster.prototype.setMapSet = function (mapSetIndex)
{
  this.mapSetIndex = mapSetIndex
  this.mapSet = this.mapSets[this.mapSetIndex]
  if (this.mapW)
  {
    var midE = (this.mapW + this.mapE)/2
    var midN = (this.mapS + this.mapN)/2
  }
  else
  {
    var midE = (this.mapSet.extent[0] + this.mapSet.extent[2])/2
    var midN = (this.mapSet.extent[1] + this.mapSet.extent[3])/2
  }
  this.mapW = midE - 500
  this.mapS = midN - 500
  this.mapE = midE + 500
  this.mapN = midN + 500
}

mapRaster.prototype.setPixelSize = function (pixelWidth, pixelHeight)
{
  this.pixelWidth  = pixelWidth
  this.pixelHeight = pixelHeight
  this.divRaster.style.width = this.pixelWidth + 'px' //'100%'
  this.divRaster.style.height = this.pixelHeight + 'px' //'100%'
  this.dloading.style.left = (pixelWidth/2-18)+'px'
  this.dloading.style.top = (pixelHeight/2-18)+'px'
  this.drawRaster (this.mapW, this.mapS, this.mapE, this.mapN)
  this.drawLayers(true)
}

mapRaster.prototype.getRelativePixels = function (px)
{
  var posx = px[0] - getLeft(this.divRaster, true);
  var posy = px[1] - getTop(this.divRaster, true);
  return [posx, posy]
}

mapRaster.prototype.pointOfPixels = function(px)
  {
	  var E = this.mapW + (px[0] * this.metresPerPixelX)
	  var N = this.mapN - (px[1] * this.metresPerPixelY)
	  return [E, N]
  }

mapRaster.prototype.pixelsOfPoint = function(E, N)
  {
    var x = (E - this.mapW)/this.metresPerPixelX
    var y = (this.mapN - N)/this.metresPerPixelY
	  return [x, y]
  }

mapRaster.prototype.zoomInAndPanTo = function(factor, p)
{
  if (p) this.panToSmooth (p)
  this.zoomIn(factor)
}

mapRaster.prototype.zoomIn = function(factor)
  {
    if (this.fixScales)
    {
      if (factor > 1)
      {
        if (this.mapSetIndex > 0)
        {
          this.mapSetIndex--
          this.mapSet = this.mapSets[this.mapSetIndex]
      	  this.drawRaster (this.mapW, this.mapS, this.mapE, this.mapN)
        	this.drawLayers(true)
        }
      }
      else if (factor < 1)
      {
        if (this.mapSetIndex < this.mapSets.length-1)
        {
          this.mapSetIndex++
          this.mapSet = this.mapSets[this.mapSetIndex]
      	  this.drawRaster (this.mapW, this.mapS, this.mapE, this.mapN)
        	this.drawLayers(true)
        }
      }
    }
    else
    {
	    var EW = (factor-1)*(this.mapE - this.mapW)/(2*factor)
	    var NS = (factor-1)*(this.mapN - this.mapS)/(2*factor)
   	  this.drawRaster (this.mapW+EW, this.mapS+NS, this.mapE-EW, this.mapN-NS)
     	this.drawLayers(true)
   	}
  }

  
mapRaster.prototype.panTo = function (pt)
  {
    if (self.timer) clearTimeout (self.timer)

//    if (pt[0] >= this.mapW && pt[0] <= this.mapE && pt[1] >= this.mapS && pt[1] <= this.mapN)
//      return this.panToSmooth(pt)

	  var EW = (this.mapE - this.mapW)/2
	  var NS = (this.mapN - this.mapS)/2
	  
    var W = pt[0] - EW
    var S = pt[1] - NS
    var E = pt[0] + EW
    var N = pt[1] + NS
    
  	this.drawRaster (W, S, E, N)
   	this.drawLayers(true)
    
  }


mapRaster.prototype.panToSmooth = function (pt)
  {
    var self = this
    
    function panNearer()
    {
      pan(pt)
    } 
    
    function pan(pt)
    {
  	  var EW = (self.mapE - self.mapW)/2
  	  var NS = (self.mapN - self.mapS)/2
  	  
      var midE = (self.mapW + self.mapE)/2
      var midN = (self.mapS + self.mapN)/2
  
      var EDiff = pt[0] - midE
      var NDiff = pt[1] - midN
      
      if (self.timer) clearTimeout (self.timer)
  
      if (Math.abs(EDiff) > 1 || Math.abs(NDiff) > 1)
      {
  // draw half way    
        var E = midE + EDiff/4
        var N = midN + NDiff/4
      	self.drawRaster (E-EW, N-NS, E+EW, N+NS)
        self.timer = setTimeout (panNearer, 10)
      }
      else
      {
        self.panTo(pt)
  //    	this.drawRaster (midE-EW, midN-NS, midE+EW, midN+NS)
      }
    }
    
    if (window.hideLocation) hideLocation()
    this.unloadLayers()
    this.hideLayers()
    pan(pt)
    
  }

    
mapRaster.prototype.panBy = function (Efactor, Nfactor)
  {
	  var EW = Efactor*(this.mapE - this.mapW)
	  var NS = Nfactor*(this.mapN - this.mapS)
    var midE = (this.mapW + this.mapE)/2
    var midN = (this.mapS + this.mapN)/2
    midE += EW
    midN += NS
    
    this.panToSmooth ([midE, midN])

  }

mapRaster.prototype.setIdealScale = function()
  {
	  var midE = (this.mapE + this.mapW)/2
	  var midN = (this.mapN + this.mapS)/2
  	var tileSizeE = this.mapSet.tileSizeE
  	var tileSizeN = this.mapSet.tileSizeN
  	var tilePixelSizeX = this.mapSet.tilePixelSizeX
  	var tilePixelSizeY = this.mapSet.tilePixelSizeY

    var halfE = this.pixelWidth *(tileSizeE/tilePixelSizeX)/2
    var halfN = this.pixelHeight*(tileSizeN/tilePixelSizeY)/2
	  
    var W = midE - halfE
    var S = midN - halfN
    var E = midE + halfE
    var N = midN + halfN
  	this.drawRaster (W, S, E, N)
   	this.drawLayers(true)
  }

mapRaster.prototype.goTo = function (W, S, E, N)
{
  this.mapSet=null
  this.drawRaster (W, S, E, N)
  this.drawLayers(true)
}

mapRaster.prototype.drawRaster = function (W, S, E, N, addNewOnly)
  {
    hideMe() // remove tootip
    
    if (!this.fixScales || !this.mapSet) // either not fixed scale or a map hasn't yet been drawn
  	{
      var metresWidth = E - W
      var metresHeight = N - S
      
      if (this.pixelWidth == 0 || this.pixelHeight == 0) return
      
      this.metresPerPixelX = metresWidth / this.pixelWidth
      this.metresPerPixelY = metresHeight / this.pixelHeight
      if (this.metresPerPixelX > this.metresPerPixelY)
        this.metresPerPixelY = this.metresPerPixelX
      else
        this.metresPerPixelX = this.metresPerPixelY
  
  // select an appropriate map set
      if (this.mapSets.length == 0) return
      
      var mapSets = this.mapSets
      
      var mapSet = mapSets[mapSets.length-1]
  	  var	mapSetIndex = mapSets.length-1
      
      for (var i = 0; i < mapSets.length; i++)
      {
      	if (boxesIntersect ([W, S, E, N], mapSets[i].extent))
      	{
  		  	var tileSize = mapSets[i].tileSizeE
  		  	var tilePixelSize = mapSets[i].tilePixelSizeX
  	      var tileMetresPerPixel = tileSize / tilePixelSize
  	      if (this.fixScales)
  	        var useThis = tileMetresPerPixel > this.metresPerPixelX // will it simply fit?
  	      else
  	        var useThis = 1.3*tileMetresPerPixel > this.metresPerPixelX // 1.3 makes for better lack of squash/stretch
  	   	  if (useThis)
  	   	  {
  	   	  	mapSet = mapSets[i]
  	   	  	mapSetIndex = i
  	   	  	break
  	   	  }
       	} 
      }
      
      this.mapSet = mapSet
      this.mapSetIndex = mapSetIndex
    }
    else
      mapSet = this.mapSet
    
  	var tileSizeE = mapSet.tileSizeE
  	var tileSizeN = mapSet.tileSizeN
  	var tilePixelSizeX = mapSet.tilePixelSizeX
  	var tilePixelSizeY = mapSet.tilePixelSizeY
  	var tileBoundaryW = mapSet.tileBoundaryW
  	var tileBoundaryS = mapSet.tileBoundaryS

    var tileMetresPerPixel = tileSizeE / tilePixelSizeX
    
    if (this.fixScales) // adjust to ideal scale
    {
  	  var midE = (E + W)/2
  	  var midN = (N + S)/2
    	var tileSizeE = mapSet.tileSizeE
    	var tileSizeN = mapSet.tileSizeN
    	var tilePixelSizeX = mapSet.tilePixelSizeX
    	var tilePixelSizeY = mapSet.tilePixelSizeY
  
      var halfE = this.pixelWidth *(tileSizeE/tilePixelSizeX)/2
      var halfN = this.pixelHeight*(tileSizeN/tilePixelSizeY)/2
  	  
      W = midE - halfE
      S = midN - halfN
      E = midE + halfE
      N = midN + halfN
      this.metresPerPixelX = (E-W) / this.pixelWidth
      this.metresPerPixelY = (N-S) / this.pixelHeight
    }
  
//    setStatus (this.mapW + ',' + this.mapS + ',' + this.mapE + ',' + this.mapN  + ' -> ' + W + ',' + S + ',' + E + ',' + N)
    
    this.mapW = W
    this.mapS = S
    this.mapE = E
    this.mapN = N

// calculate the grid of maps that would cover this area - build a list
// for each existing div, delete if not required
// for each visible one, create if not already there

    var nTilesW = Math.floor (W / tileSizeE)
    var tileW = nTilesW * tileSizeE
//    var tileWOffset = W % tileSize

    var nTilesS = Math.floor (S / tileSizeN)
    var tileS = nTilesS * tileSizeN
//    var tileSOffset = S % tileSize
    
//    var tilePixelSizeX = tilePixelSize / metresPerPixelX
    var squashFactorX = this.metresPerPixelX / tileMetresPerPixel
    var squashFactorY = this.metresPerPixelY / tileMetresPerPixel
    
//    console.log ('WOffset', tileWOffset, 'SOffset', tileSOffset)
//    console.log ('squashFactorX', squashFactorX, 'squashFactorY', squashFactorY)
		var root = this.divRaster
		var root = this.tileParent
    
    var tilesVisible = {}
    var nTilesVisible=0
    tileW -= tileBoundaryW
    while (tileW < E)
    {
      var tileS = (nTilesS * tileSizeN) - tileBoundaryS
    	while (tileS < N)
    	{
    		var fileName = mapSet.getFileName(tileW, tileS)
    		if (fileName == undefined)
    		{
    			//console.log (mapSet.name, tileW, tileS)
    		}
    	
    		tilesVisible[fileName.toLowerCase()]={
    			                      fileName : fileName, 
    			                      left : Math.round((tileW-W)/this.metresPerPixelX),
    			                      top  : Math.round(this.pixelHeight-(tileS-S+tileSizeN)/this.metresPerPixelY),
    			                      width  : Math.round(tilePixelSizeX / squashFactorX),
    			                      height : Math.round(tilePixelSizeY / squashFactorY)
    			                    }
    		nTilesVisible++
    		tileS = tileS + tileSizeN
      }	
    	tileW = tileW + tileSizeE
    }

if (!addNewOnly)
{
// clear out redundant ones (not in the window) - put them into a separate array before removing from parent

//		var divs = root.getElementsByTagName('div')
//		console.log ('clear out from DIVs', divs.length)

    var divsToRemove = []		
		for (var tile in this.loadedTiles)
		{
		  var div = this.loadedTiles[tile]
			if (!div) alert (' no div ' + tile)
			var img = div.firstChild
//			if (!img) alert (' no img ' + tile)
			if (img)
			{
  			if (!tilesVisible[img.src.toLowerCase()])
  			{
          divsToRemove.push (this.loadedTiles[tile])
          div.style.visibility = 'hidden'
          delete this.loadedTiles[tile]
  			}
  			else
  			{
          div.style.visibility = 'visible'
  //					console.log ('NOT removing',  divs[i].id)
  			}
  		}
	  }

if (true)
{
    for (var i=0; i<divsToRemove.length; i++)
    {
      var div = root.removeChild (divsToRemove[i])
      if (this.recycling)
		    divCache.push (div)
	  }
}    
}	  

// create new ones and position the divs

    var nTilesCreated = 0
    var leftOffset = -parseInt(this.tileParent.style.left)
    var topOffset = -parseInt(this.tileParent.style.top)
    
    
    for (var tile in tilesVisible)
    {
    	var d, img
    	var isNew = false

  	  d = this.loadedTiles[tile]
//    	d = document.getElementById (tile)
    	  
    	if (d)
    	{
    		img = d.firstChild
    	}
    	else //if (!addNewOnly)
    	{
//			  console.log ('adding',  tile)
        if (this.divCache.length == 0)
        {
          nTilesCreated++
	    		d = document.createElement ('div')
      		d.style.position = 'absolute' 
      		d.className = 'divBackground';
    		  img = document.createElement ('img')
          img.className = 'imgBackground';
      		d.appendChild(img)
    		}
	    	else
	    	{
//	    		d = divCache.pop()
	    		d = this.divCache.splice(0, 1)[0]
      		img = d.firstChild
	    	}
    		this.loadedTiles[tile] = d
    		d.id = tile
    		root.appendChild (d)
    		img.src = tilesVisible[tile].fileName
    		isNew = true
    	}
    	if (!addNewOnly || isNew)
    	{
  		    d.style.left = (leftOffset + tilesVisible[tile].left) + 'px' 
  		    d.style.top = (topOffset + tilesVisible[tile].top) + 'px'   
  		
//        if (!this.fixScales)
//        {
  	  		img.style.width = tilesVisible[tile].width + 'px'
	    		img.style.height = tilesVisible[tile].height + 'px'
  		}
  	}

  }


mapRaster.prototype.addPointLayer = function(name, path, symbol, displayed, maxZoom)
{
  var l = this.layers.push()
  var d = document.createElement ('div');
  d.className='mapPointLayer';
  d.style.left = '0px'
  d.style.top = '0px'
  d.style.width='0px'
  d.style.height='0px'
//  d.onmouseover = function(evt){if (window.event) evt = window.event; setStatus('mouseMove '+evt.clientX)}
//	this.layerParent.appendChild(d);
	this.divRaster.appendChild(d);
	if (maxZoom == 'undefined')
	  maxZoom = 1000 // surely no more than 100 layers in a map!
	this.layers[l] = {type:'p', name:name, path:path, div:d, symbol:symbol, displayed:displayed, loaded:false, maxZoom:maxZoom}
	this.addLayerControlLayer(l)
	this.drawLayer(l)
	return l
}

mapRaster.prototype.addLayer = function(name, path, displayed)
{
  var l = this.layers.push()
  var d = document.createElement ('div');
  d.className='maplayer';
  d.style.left = '0px'
  d.style.top = '0px'
  var img = document.createElement ('img');
  img.style.visibility='hidden';
	d.appendChild(img);
	this.layerParent.appendChild(d);
	if (displayed=='undefined')
	  displayed=false
	this.layers[l] = {type:'i', name:name, path:path, img:img, symbol:null, displayed:displayed, loaded:false}
	this.addLayerControlLayer(l)
	this.drawLayer(l)
	return l
}

mapRaster.prototype.getExtent = function()
{
  return 'Geometry.Rectangle(' + this.mapW + ',' + this.mapS + ',' + this.mapE + ',' + this.mapN + ')'
}

mapRaster.prototype.getCanvas = function()
{
  return 'pixelWidth:' + this.pixelWidth + ', pixelHeight:' + this.pixelHeight + ', mapExtent:' + this.getExtent()
}

mapRaster.prototype.showLayer = function(layerIndex)
{
  var l = this.layers[layerIndex]
  if (l.img)
    l.img.style.visibility='visible'
  else
    l.div.style.visibility='visible'
  this.setLayerImage(layerIndex, 'tick')
}

mapRaster.prototype.hideLayer = function(layerIndex)
{
  var l = this.layers[layerIndex]
  if (!l) return
  if (l.img)
    l.img.style.visibility='hidden'
  else
  {
    l.div.style.visibility='hidden'
  }
  this.setLayerImage(layerIndex, 'cross')
}

mapRaster.prototype.hideLayers = function()
{
  for (var i=0;i<this.layers.length; i++)
    this.hideLayer(i)
}

mapRaster.prototype.drawLayer = function(layerIndex)
{
  if (!this.layers[layerIndex] || !this.layers[layerIndex].displayed || (this.mapSetIndex > this.layers[layerIndex].maxZoom))
  {
    this.hideLayer(layerIndex)
    return;
  }

  if (this.layers[layerIndex].loaded)
  {
    this.showLayer(layerIndex)
    return
  }

  //Do this with content
  
  switch (this.layers[layerIndex].type)
  {
    case 'i':
    {
      var onContent = function(content)
      {
        if (content)
        {
          var layer = this.layers[layerIndex]
          var layerName = 'layer'+layer.name+getID()
          var s = 'Canvas(' + content + ', {fileName:"temp/' + layerName + '", ' + this.getCanvas() + ', transparency:true, usePalette:true})'
          var img = layer.img
          var div = img.parentNode
          var self=this
          this.setLayerImage(layerIndex, 'query')
          this.loading(true)
          img.onload = function()
          {
            div.style.left = '0px';
            div.style.top = '0px';
            layer.loaded=true;
            self.showLayer(layerIndex);
            if (self.onDrawLayer) self.onDrawLayer(layerIndex);
            self.loading(false)
          }
          postObjectJSON (s, function (r) {img.src = r})
          
//          img.src = "Handler.ashx?s=" + s + "&format=png"
        }  
      }
    break;
    }

    case 'p':
    {
      var onContent = function(content)
      {
        function showPoints(pts)
        {
          function mo(name, x, y)
          {
            return function (evt) {if (self.dragging) return true; else return self.pointTip(name, x, y);}
          }
          
          var div = self.layers[layerIndex].div
          div.style.left = '0px';
          div.style.top = '0px';
          self.layers[layerIndex].data = []
//          alert('content='+content.length);
          for (var i=0; i<pts.length; i++)
          {
            var px = self.pixelsOfPoint(pts[i].point[0], pts[i].point[1])
            if (px[0] >=0 && px[0]<=self.pixelWidth && px[1] >=0 && px[1] <= self.pixelHeight)
            {
              var divPoint = document.createElement('img')
              divPoint.style.position='absolute'
//              divPoint.style.backgroundColor = 'red'
//              divPoint.style.width='6px'
//              divPoint.style.height='6px'
              divPoint.style.zIndex=2000
              divPoint.style.left = (px[0]-15)+'px'
              divPoint.style.top = (px[1]-15)+'px'
              divPoint.src=self.layers[layerIndex].symbol
//              divPoint.title=
//              alert(pts[i].name)
              var title = '<span style="font-weight:bold;color:blue">'+self.layers[layerIndex].name + '</span><br>' + pts[i].name
              divPoint.onmouseover = mo(title, px[0], px[1])
              divPoint.onmouseout =  mo('', px[0], px[1])
              self.layers[layerIndex].div.appendChild(divPoint)
//              var imgClone = divPoint.cloneNode(true)
//              imgClone.onmouseover = mo(title, px[0], px[1])
//              imgClone.onmouseout =  mo('', px[0], px[1])
//              self.dragger.appendChild(imgClone) // ###
            }
          }
          self.layers[layerIndex].loaded=true
          self.showLayer(layerIndex);
        }
        
        if (content)
        {
          var s = content
          var div = this.layers[layerIndex].div
          var self=this
          this.setLayerImage(layerIndex, 'query')
          postObjectJSON (s, showPoints)
        }  
      }
    break;
    }
    default:alert('unknown case '+this.layers[layerIndex].type)
  }
    
  //Scope the callback
  var self = this;
  var callback = function(content){
    onContent.call(self,content);
  }
    
//  alert('about to call PATH')
  
  var content = this.layers[layerIndex].path(this.mapW, this.mapS, this.mapE, this.mapN, callback)
//  alert('PATH called')
  
  this.hideLayer(layerIndex)
  
  //This is the 'original' method of calling.
  if(content)
  {
    callback(content);
  }
  
}

mapRaster.prototype.drawLayers = function(moved)
{
  try
  {
    for (var i=0;i<this.layers.length; i++)
    {
      if (moved)
        this.unloadLayer(i)
      this.drawLayer(i)
    }  
  }
  catch (ex)  
  {
    alert('drawLayers: ' + ex.toString())
  }
}

mapRaster.prototype.unloadLayer = function(layerIndex)
{
  var l = this.layers[layerIndex]
  if (!l) return
  
  l.loaded=false

  if (l.img)
  {
//    l.img.style.visibility='hidden'
}
  else
  {
//    l.div.style.visibility='hidden'
// clear out the old images
    while (l.div.hasChildNodes())
      l.div.removeChild(l.div.lastChild)    
  }    
}

mapRaster.prototype.unloadLayers = function()
{
  for (var i=0;i<this.layers.length; i++)
    this.unloadLayer(i)
}

mapRaster.prototype.moveLayers = function(dx, dy)
{
  var img, div, v
  for (var i=0;i<this.layers.length; i++)
  {
    div = this.layers[i].div // for point layers
    if (!div)
    {
      img = this.layers[i].img  // for image layers
//      if (!img)
//      {
//        var x=3
//        break
//      }
      div = img.parentNode
    }
//    v = parseInt(div.style.left)
    v = 0
    div.style.left = (v + dx) + 'px'
//    v = parseInt(div.style.top)
    v = 0
    div.style.top = (v + dy) + 'px'
    this.layers[i].loaded = false // unload without delete
  }   
  this.tileParent.style.left = dx + 'px'
  this.tileParent.style.top = dy + 'px'

}

mapRaster.prototype.toggleLayer = function(layerIndex)
{
  var l = mr.layers[layerIndex]
  l.displayed=!l.displayed
  this.drawLayer(layerIndex)
}

mapRaster.prototype.setLayerImage = function(layerIndex, imageName)
{
  var img=this.layers[layerIndex].layerImg
  img.src = 'images/'+imageName+'.png'
}

mapRaster.prototype.addLayerControlLayer = function(layerIndex)
{
  var self=this
  
  function setOpaque() {setOpacity(lc, 1)}
  function setTranslucent() {setOpacity(lc, 0.8)}
  function setTransparent() {setOpacity(lc, 0)}
  function highlight() {spn.style.backgroundColor = '#ff8888'}
  function lowlight()  {spn.style.backgroundColor = '#a6a0cc'}
  function toggle()    {self.toggleLayer(layerIndex)}
  function hideContents()
  {
    var spns = lc.getElementsByTagName('span')
    for (var i=0; i<spns.length; i++)
    {
      spns[i].style.display='none'
    }
  }  
  function showContents()
  {
    var spns = lc.getElementsByTagName('span')
    for (var i=0; i<spns.length; i++)
    {
      spns[i].style.display='inline'
    }
  }  
  function expand()    {lc.style.width='130px';lc.style.height='auto';lc.style.left='2px';lc.style.top='auto';lc.style.bottom='2px';Cimg.src='images/symbols/16/arrowSW.png';Cimg.title='Click to collapse';Cimg.onclick=collapse;showContents();}
  function collapse()  {lc.style.left='2px';lc.style.width='6px';lc.style.height='6px';Cimg.src='images/symbols/16/arrowNE.png';Cimg.title='Click to expand';Cimg.onclick=expand;hideContents();}
  var lc = document.getElementById('layers')
  var img = document.createElement('img')
  if (this.layers[layerIndex].displayed)
    img.src = 'images/tick.png'
  else
    img.src = 'images/cross.png'
  img.className='menu_toggle'
  lc.appendChild(img)
  var spn = document.createElement('span')
  spn.className = 'layerControlName'
  spn.onmouseover=highlight
  spn.onmouseout=lowlight
  spn.onclick=toggle
  var txt = document.createTextNode(this.layers[layerIndex].name)
  spn.appendChild(txt)
  lc.appendChild(spn)
  lc.appendChild(document.createElement('br'))
  this.layers[layerIndex].layerImg = img
  
  if (layerIndex==1)
  {
    lc.onmouseover=setOpaque
    lc.onmouseout=setTranslucent
    var Cimg = document.createElement('img')
    Cimg.src='images/symbols/16/arrowSW.png'
    Cimg.title='Click to collapse'
    Cimg.style.position='absolute'
    Cimg.style.visibility='visible'
    Cimg.style.left='auto'
    Cimg.style.right='-1px'
    Cimg.style.top='-1px'
    Cimg.style.border='solid black 1px'
    Cimg.style.backgroundColor='#ffffff'
    Cimg.onclick=collapse
    lc.appendChild(Cimg)
  }
}

function hideMe()
{
  var tipDiv = document.getElementById ('pointTip')
  if (tipDiv) 
    tipDiv.style.visibility = 'hidden'
}

function showMe()
{
  var tipDiv = document.getElementById ('pointTip')
  if (tipDiv) 
    tipDiv.style.visibility = 'visible'
}

mapRaster.prototype.pointTip = function(txt, x, y)
{
  
  var tipWidth = 150
  var tipHeight = 20
  var tipShiftX = 30
  var tipShiftY = -15
  var tipDiv = document.getElementById ('pointTip')
  if (!tipDiv)
  {
    tipDiv = document.createElement ('div')
    tipDiv.id = 'pointTip'
    tipDiv.style.backgroundColor = '#df8'
    tipDiv.style.fontSize = '8pt'
    tipDiv.style.border = 'solid 1px gray'
    tipDiv.style.position = 'absolute'
    tipDiv.style.width = tipWidth + 'px'
    tipDiv.style.zIndex = 1000
    tipDiv.style.overflow = 'hidden'
//    tipDiv.onmouseover = function() {tipDiv.style.visibility = 'hidden'}
    this.divRaster.appendChild (tipDiv)
    this.tipDiv = tipDiv
  }

  if (x>tipShiftX) x-=tipShiftX
  if (x+tipWidth > this.pixelWidth) x=(this.pixelWidth-tipWidth)
  if (y>tipShiftY) y-=tipShiftY
  if (y+tipHeight > this.pixelHeight) y=(this.pixelHeight-tipHeight)
  
  tipDiv.style.left = x+'px'
  tipDiv.style.top  = y+'px'

  if (txt == '')
  {
//    hideMe()
    if (this.tipTimer) clearTimeout (this.tipTimer)
    this.tipTimer=setTimeout ("hideMe()", 500)
  }
  else
  {
    if (this.tipTimer) clearTimeout (this.tipTimer)
    tipDiv.innerHTML = txt
    this.tipTimer=setTimeout ("showMe()", 200)
//    tipDiv.style.visibility = 'visible'
  }
    
//  alert (tipDiv.style.width + ',' + tipDiv.style.height)
    
}

mapRaster.prototype.loading = function(show)
{
  if (show)
  {
    this.dloading.style.visibility = 'visible'
    this.dragger.style.cursor = 'wait'
    setOpacity(this.dragger, 0.3)
  }
  else
  {
    this.dloading.style.visibility = 'hidden'
    this.dragger.style.cursor = 'pointer'
    setOpacity(this.dragger, 0)
  }
}