function MiniMap( elMiniMapViewport, zoomLevel, mapObj ) {
	
	this._elMiniMapViewport = elMiniMapViewport;
  this._miniMapViewportWidth = elMiniMapViewport.clientWidth;
  this._miniMapViewportHeight = elMiniMapViewport.clientHeight;

	this._zoomLevel = zoomLevel;
	this._mapPixelsPerMiniMapPixel = TileCache.getTilesPerZoomTile( zoomLevel );   // same ratio

  this._oMap = mapObj;

  // create the div to hold the map image tiles
  this._elMiniMapContainer = document.createElement("DIV");
  this._elMiniMapContainer.className = "mapContainer";
  this._elMiniMapContainer.id = "miniMapContainer";
  $("miniMapViewport").appendChild( this._elMiniMapContainer );
  

  this._elMiniMapTilesContainer = document.createElement("DIV");
  this._elMiniMapTilesContainer.className = "mapContainer";
  this._elMiniMapTilesContainer.id = "miniMapTilesContainer";
  this._elMiniMapContainer.appendChild( this._elMiniMapTilesContainer );
  

  this._oTileCache = new (TileCache)( this._elMiniMapTilesContainer, this._miniMapViewportWidth, this._miniMapViewportHeight, zoomLevel, "minimap", gIsSafari );

  this._elLens = null;
  this._elLensStyle = null;
  this._elLensInnerStyle = null;
  this._elLensFrameStyle = null;
  this._elLensMover = null;
  this._elLensMoverStyle = null;
  this._elLensMoverInnerStyle = null;
  this._elLensMoverFrameStyle = null;

  this._lensMoverDD = null;  // drag n drop
};

MiniMap.prototype = {

	destroy : function() {

    YAHOO.log( "minimap destroyed");

		// destroy the tile map
		this._oTileCache.destroy();
		this._oTileCache = null;

		// unregister the drag and drop events from the lens mover
		this._lensMoverDD.unreg();
		this._lensMoverDD = null;

		// remove and delete the mini map container
    this._elMiniMapContainer.parentNode.removeChild( this._elMiniMapContainer );
     _proxy_jslib_assign('', this._elMiniMapContainer, 'innerHTML', '=', ( ""));
    this._elMiniMapContainer = null;

		// delete references to all the lens and lens mover part elements
		this._elLens = null;
		this._elLensStyle = null;
		this._elLensInnerStyle = null;
		this._elLensFrameStyle = null;
		this._elLensMover = null;
		this._elLensMoverStyle = null;
		this._elLensMoverInnerStyle = null;
		this._elLensMoverFrameStyle = null;
	},


  draw : function() {

    YAHOO.log( "drawing minimap" );

    // reset the mini map container
    this._clearMiniMapContainer();
 
   // get the size and positon of the viewport on the map
    var mapInfo = this._oMap.getVisibleMapInfo();

    // compute the size and position of the lens and lens mover
    var lensMiniMapSize = this._translateViewportSizeToMiniMap( mapInfo.width, mapInfo.height );
		var lensMiniMapPosition = this._translateMapPositionToMiniMap( mapInfo.left,  _proxy_jslib_handle(mapInfo, 'top', '', 0, 0) );

    // position the mini map so that the lens is centered
    var centeredLensMiniMapPos = this._getCenteredLensMiniMapPosition( lensMiniMapPosition[0], lensMiniMapPosition[1],
                                                                       lensMiniMapSize[0], lensMiniMapSize[1]);
 		this._setMiniMapPosition( centeredLensMiniMapPos[0], centeredLensMiniMapPos[1] );


    // compute the top left visible zoom tile and create the tile images
    var miniMapPositionZoomTile = TileCache.getTileAtPixel( centeredLensMiniMapPos[0], centeredLensMiniMapPos[1] );
    this._oTileCache.createGridImages( miniMapPositionZoomTile[0], miniMapPositionZoomTile[1] );
    
		// add the lens and the lens mover to the map container
		this._createLens( this._elMiniMapContainer );
		this._createLensMover( this._elMiniMapContainer );

    // size and position the lens and lens mover on the minimap	
    this._setLensSize( lensMiniMapSize[0], lensMiniMapSize[1] );
		this._setLensMoverSize( lensMiniMapSize[0], lensMiniMapSize[1] );
		this._setLensPositionWithMapCoordinates( mapInfo.left,  _proxy_jslib_handle(mapInfo, 'top', '', 0, 0) );
		this.setLensMoverPositionWithMapCoordinates( mapInfo.left,  _proxy_jslib_handle(mapInfo, 'top', '', 0, 0) );

    this._showLens();
  },


  update : function( mapLeft, mapTop ) {

    // move the lens to where the lens mover is
    this._moveLensToLensMoverPosition();

    // move the map so that the lens is centered in the viewport
		var miniMapPos = this._translateMapPositionToMiniMap( mapLeft, mapTop );
		var centeredLensMiniMapPos = this._getCenteredLensMiniMapPosition( miniMapPos[0], miniMapPos[1] );
		this._setMiniMapPosition( centeredLensMiniMapPos[0], centeredLensMiniMapPos[1] );

    // let the image tiles to move if necessary
    this._oTileCache.reactToMapDrag( centeredLensMiniMapPos[0], centeredLensMiniMapPos[1] );
  },


	_createLens : function( elMiniMapContainer ) {
		// create the lens and cache refs to style objects
		var elLens = document.createElement( "DIV" );
		var elLensInner = document.createElement( "DIV" );
		var elLensFrame = document.createElement( "DIV" );
    
    this._elLens = elLens;
		this._elLensStyle = elLens.style;
		this._elLensInnerStyle = elLensInner.style;
		this._elLensFrameStyle = elLensFrame.style;
		
		elLens.className = "lens";
		elLens.id = "lens";
		elLensInner.id = "lensInner";
		elLensInner.className = "lensInner";
		elLensFrame.className = "lensFrame";
		
		elMiniMapContainer.appendChild( elLens );
		elLens.appendChild( elLensInner );
		elLens.appendChild( elLensFrame );
	},

	_createLensMover : function( elMiniMapContainer ) {
		// create the lens mover and cache refs to style objects
		var elLensMover = document.createElement( "DIV" );
		var elLensMoverInner = document.createElement( "DIV" );
		var elLensMoverFrame = document.createElement( "DIV" );

		this._elLensMover = elLensMover;
		this._elLensMoverStyle = elLensMover.style;
		this._elLensMoverInnerStyle = elLensMoverInner.style;
		this._elLensMoverFrameStyle = elLensMoverFrame.style;
		
		elLensMover.className = "lens";
		elLensMover.id = "lensMover";
		elLensMoverInner.id = "lensMoverInner";
		elLensMoverInner.className = "lensInner";
		elLensMoverFrame.className = "lensFrame";
		
		elMiniMapContainer.appendChild( elLensMover );
		elLensMover.appendChild( elLensMoverInner );
		elLensMover.appendChild( elLensMoverFrame );


    // add drag and drop handling ot the lens mover;
    var self = this;
    var config = {scroll:false};
		this._lensMoverDD = new (YAHOO.util.DD)( elLensMover, "minimap", scroll );
		this._lensMoverDD.endDrag = function() { self._onLensMoverEndDrag(); };
	},


  _clearMiniMapContainer : function() {

    // assume if the lens exists, then the lens mover and tile cache exist also
    if( this._elLens ) {
      this._elMiniMapContainer.removeChild( this._elLens );
    }
    if( this._elLensMover ){
      this._elMiniMapContainer.removeChild( this._elLensMover );
    }

    // destory the image tiles
    this._oTileCache.destroy();

    
    // null out all references to DOM elements
    this._elLens = null;
    this._elLensStyle = null;
    this._elLensInnerStyle = null;
    this._elLensFrameStyle = null;
    this._elLensMover = null;
    this._elLensMoverStyle = null;
    this._elLensMoverInnerStyle = null;
    this._elLensMoverFrameStyle = null;
  },



  
  _getCenteredLensMiniMapPosition : function( lensLeft, lensTop, lensWidth, lensHeight) {
    // returns the minimap coorinates to position the map so that the current lens location is centered in the viewport.
    //   parameters passed in override current lens settings in certain circumstances.   see below.

    // get the position and size of the lens

    // this is weird.  I want to allow this function to use the current style values of the lens element unless
    //   they are overridden by passed in parameters.   When the mini map is first drawn, this function gets
    //   called before the lens element is created, so I have to do some sneaky tests to prevent trying to read
    //   it's properties.
    // 
    //   I basically see three ways that this can get called:
    //      1. pass no parameters - current styles of the lens element are used
    //      2. pass in only lensLeft and lensTop - the lens width and height are taken from the lens element
    //      3. pass in lens left, top, width, and height -  first draw case, before the lens is created.
    if( !lensWidth ){
      var elLens = this._elLens;
      var lensWidth = parseInt( elLens.style.width );
      var lensHeight = parseInt( elLens.style.height );

      var lensLeft = lensLeft || elLens.offsetLeft;   // use the lensLeft parameter if present
      var lensTop = lensTop || elLens.offsetTop;      // use the lensTop parameter if present
    }      

    // determine the offsets to center the lens in the viewport
    var centeredLensMiniMapPositionX = lensLeft - Math.round( (this._miniMapViewportWidth - lensWidth) / 2 );
    var centeredLensMiniMapPositionY = lensTop - Math.round( (this._miniMapViewportHeight - lensHeight) / 2 );

    return [ centeredLensMiniMapPositionX, centeredLensMiniMapPositionY ];
  },

  _centerLensInViewport : function() {
    var centeredLensMapPosition = _getCenteredLensMiniMapPosition(); 

    this._setMiniMapPosition( centeredLensMapPosition[0], centeredLensMapPosition[1] );
    // position the map at the new offsets
  },

  





	// **************************
	//     LENS MOVER EVENTS
	// **************************

	_onLensMoverEndDrag : function() {
		var moverPos = this._getLensMoverPosition();
		var moverMapPos = this._translateMiniMapPositionToMap( moverPos[0], moverPos[1] );

    //this._oMap.setMapPosition( moverMapPos[0], moverMapPos[1] );
		this._oMap.animateMapToPosition( moverMapPos[0], moverMapPos[1] );
		
		

	},



	// **************************
	//   END LENS MOVER EVENTS
	// **************************

	
  _setMiniMapPosition : function( miniMapX, miniMapY ) {
    this._elMiniMapContainer.style.left = -miniMapX + "px";
     _proxy_jslib_assign('', this._elMiniMapContainer.style, 'top', '=', ( -miniMapY + "px"));
  },

	setMiniMapPositionWithMapCoordinates : function( mapX, mapY ) {
		var miniMapPos = this._translateMapPositionToMiniMap( mapX, mapY );
		this._setMiniMapPosition( miniMapPos[0], miniMapPos[1] );
	},


  _setLensPositionWithMiniMapCoordinates : function( miniMapLeft, miniMapTop ) {
    this._moveLens( miniMapLeft, miniMapTop );
  },

  _setLensMoverPositionWithMiniMapCoordinates : function( miniMapLeft, miniMapTop ) {
    this._moveLensMover( miniMapLeft, miniMapTop );
  },
  
	_setLensPositionWithMapCoordinates : function( mapLeft, mapTop ) {
		var miniPos = this._translateMapPositionToMiniMap( mapLeft, mapTop );
    this._setLensPositionWithMiniMapCoordinates( miniPos[0], miniPos[1] );
	},

	setLensMoverPositionWithMapCoordinates : function( mapLeft, mapTop ) {
		var miniPos = this._translateMapPositionToMiniMap( mapLeft, mapTop );
    this._setLensMoverPositionWithMiniMapCoordinates( miniPos[0], miniPos[1] );
	},

	_getLensMoverPosition : function() {
		return [ parseInt( this._elLensMoverStyle.left ),
						 parseInt(  _proxy_jslib_handle(this._elLensMoverStyle, 'top', '', 0, 0) ) ];
	},

	
	_translateMiniMapPositionToMap : function( miniMapLeft, miniMapTop ) {
		return [ Math.round( miniMapLeft * this._mapPixelsPerMiniMapPixel ),
						 Math.round( miniMapTop * this._mapPixelsPerMiniMapPixel ) ];
	},


	_translateViewportSizeToMiniMap : function( viewportWidth, viewportHeight ) {
		return [ Math.round( viewportWidth / this._mapPixelsPerMiniMapPixel ),
						 Math.round( viewportHeight / this._mapPixelsPerMiniMapPixel ) ];
	},

	_translateMapPositionToMiniMap : function( mapLeft, mapTop ) {
    return [ Math.round( mapLeft / this._mapPixelsPerMiniMapPixel ),
						 Math.round( mapTop / this._mapPixelsPerMiniMapPixel ) ];
	},



  _setLensSize : function( width, height ) {
		// the lens shows the position of the current viewport on the minimap

    this._elLensStyle.width = (width + 2) + "px";
    this._elLensStyle.height = (height + 2) + "px";
    this._elLensFrameStyle.width = width + "px";
    this._elLensFrameStyle.height = height + "px";
    this._elLensInnerStyle.width = width + "px";
    this._elLensInnerStyle.height = height + "px";
  },


	_setLensMoverSize : function( width, height ) {
		// the lens mover is the small frame that moves during a drag

    this._elLensMoverStyle.width = (width + 2) + "px";
    this._elLensMoverStyle.height = (height + 2) + "px";
    this._elLensMoverFrameStyle.width = width + "px";
    this._elLensMoverFrameStyle.height = height + "px";
    this._elLensMoverInnerStyle.width = width + "px";
    this._elLensMoverInnerStyle.height = height + "px";
	},


	_moveLens : function( left, top ) {
    this._elLensStyle.left = left + "px";
     _proxy_jslib_assign('', this._elLensStyle, 'top', '=', (  _proxy_jslib_handle(null, 'top', top, 0, 0) + "px"));
	},


	_moveLensMover : function( left, top ) {
    this._elLensMoverStyle.left = left + "px";
     _proxy_jslib_assign('', this._elLensMoverStyle, 'top', '=', (  _proxy_jslib_handle(null, 'top', top, 0, 0) + "px"));
	},


  _moveLensToLensMoverPosition : function() {
    var lensMoverPos = this._getLensMoverPosition();
    this._setLensPositionWithMiniMapCoordinates( lensMoverPos[0], lensMoverPos[1] );
  },
  
  _showLens : function() {
    this._elLensStyle.visibility = "visible";
    this._elLensMoverStyle.visibility = "visible";
  },


  _hideLens : function() {
    this._elLensStyle.visibility = "hidden";
    this._elLensMoverStyle.visibility = "hidden";
  }
}

 ;
_proxy_jslib_flush_write_buffers() ;