/****************************************************************************
 * Copyright (c) 1998-2007 Luna Imaging, Inc.  All Rights Reserved.
 *
 * This software is confidential and proprietary information of
 * Luna Imaging, Inc.  ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Luna Imaging, Inc.
 *
 * The software may not be copied, reproduced, translated or reduced to
 * any electronic medium or machine-readable form without
 * the prior written consent of Luna Imaging.
 *
 * You are not allowed to distribute the binary and source code
 * (if released) to third parties, without the prior written consent from
 * Luna Imaging.
 *
 * You are not allowed to reverse engineer, disassemble or decompile
 * code, or make any modifications of the binary or source code, remove
 * or alter any trademark, logo, copyright or other proprietary notices,
 * legends, symbols, or labels in the Software.
 *
 * You are not allowed to sub-license the Software or any derivative
 * work based on or derived from the Software.
 *
 * LUNA IMAGING MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
 * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT
 * NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
 * A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, LUNA IMAGING SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
 * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 *
 *  cruiz10020@yahoo.com
 *
 *  ImagePanel.js
 *
 *  Description:
 *    Renders a Panel used to play around with an image.
 *
 *  Structure:
 *
 *
 *  Requires:
 *    prototype.js - http://www.prototypejs.org/
 *
 *  Development History:
 *    4-8-2007       - created
 *
 *******************************************************************************/

/**
 * Constrcuts a new ImagePanel js object
 */
var Static_ImagePanel_EXTERNAL_ADDITIONAL_LEVELS = 2
var Static_ImagePanel_EXTERNAL_MEDIA_STARTING_LEVEL = 2;
function ImagePanel( postResizeFunction, postDestroyFunction )
{
  // Html dom elements
  this.mContainer = null;
  this.mGlassPlate = null;
  this.mImageCover = null
  this.mPanel = null;
  this.mBody = null;
  this.mImage = null;
  this.mZoomBox = null;
  this.mIsCurrentImagePanel = false;

  // js Objects
  this.mControl = null;
  this.mManager = null;

  // Attributes
  this.mTiles = null;
  this.mFullImageUrls = null;
  this.mLunaSourceUrl = null;
  this.mThumbnailUrl = null;
  this.mLunaLevel = null;
  this.mLunaMaxLevel = null;
  this.mResizeStartingWidth = null;
  this.mResizeStartingHeight = null;
  this.mBaseRatio = null;
  this.mPostResizeFunction = postResizeFunction;
  this.mPostDestoryFunction = postDestroyFunction;
  this.mConfirmBeforeClose = true;
  this.mCurrentViewPoint = null;
  this.mLastIdleTime = null;
  this.mScaleFactor = null;
  this.mThumbnailDimensions = null;

  // Flags / state changing attributes
  this.mLoadInProgress = null;
  this.mActionInProgress = null;//flag which specifies if an action is in progress
  this.mCurrentActionInProgress = null;//The action that is currently in progress
  this.mMoveStartingX = null;
  this.mMoveStartingY = null;
  this.mIgnoreSizeConstraints = null;
  this.mMaximizeOnLoad = false;
  this.mInitialized = false;
  
  // functions
  this.mFunctionFetchAnnotations;
  this.mFunctionFetchAnnotation;
  this.mFunctionEditAnnotation;

  // Constants
  this.CLASSNAME = 'imagePanel';
  this.CURRENT_IMAGE_PANEL_CLASSNAME = 'current';

  this.PAN_CURSOR_CLASSNAME = 'panCursor';
  this.PAN_ACTIVE_CURSOR_CLASSNAME = 'activePanCursor';
  this.MOVE_CURSOR_CLASSNAME = 'moveCursor';
  this.MOVE_ACTIVE_CURSOR_CLASSNAME = 'activeMoveCursor';
  this.ADD_ANNOTATION_ACTIVE_CURSOR_CLASSNAME = 'activeAddAnnotationCursor';

  this.ZOOM_BOX_CLASSNAME = 'zoomBox';
  this.DEFAULT_WIDTH = 60;
  this.DEFAULT_HEIGHT = 55;
  this.MIN_SIZE = 50;
  this.DEFAULT_LUNA_LEVEL = 5;
  this.DEFAULT_MAX_IMAGE_WIDTH = 750;
  this.DEFAULT_MAX_IMAGE_HEIGHT = 750;
  this.MAX_PANEL_WIDTH = 1500;
  this.MAX_PANEL_HEIGHT = 1000;
  this.MAX_INITIAL_TILES = 8;
  this.MAX_STARTING_SIZE = 384;
  this.EXTERNAL_MEDIA_STARTING_LEVEL = Static_ImagePanel_EXTERNAL_MEDIA_STARTING_LEVEL;
  this.EXTERNAL_ADDITIONAL_LEVELS = Static_ImagePanel_EXTERNAL_ADDITIONAL_LEVELS;
  this.TILE_UPDATE_DELAY = 500; // .5 seconds

  this.CONFIRMATION_BEFORE_CLOSE_MESSAGE = 'Do you want remove this image?';

  // method map
  this.init = ImagePanel_Init;
  this.render = ImagePanel_Render;
  this.reset = ImagePanel_Reset;
  this.close = ImagePanel_Close;
  this.resize = ImagePanel_Resize;
  this.constructBody = ImagePanel_ConstructBody;
  this.updateTiles = ImagePanel_UpdateTiles;
  this.constructTile = ImagePanel_ConstructTile;
  this.moveTo = ImagePanel_MoveTo;
  this.panTo = ImagePanel_PanTo;
  this.boxZoomImage = ImagePanel_BoxZoomImage;
  this.maximizePanelToImage = ImagePanel_MaximizePanelToImage;
  this.maximizeImageToPanel = ImagePanel_MaximizeImageToPanel;
  this.maximizePanelToContainer = ImagePanel_MaximizePanelToContainer;
  this.startAddAnnotation = ImagePanel_StartAddAnnotation;
  this.centerOnContainer = ImagePanel_CenterOnContainer;
  this.bringToFront = ImagePanel_BringToFront;
  this.updateCursor = ImagePanel_UpdateCursor;
  this.currentActionHasChanged = ImagePanel_CurrentActionHasChanged;
  this.endCurrentAction = ImagePanel_EndCurrentAction;
  this.getPosition = ImagePanel_GetPosition;
  this.getDimensions = ImagePanel_GetDimensions;
  this.getCurrentImageSize = ImagePanel_GetCurrentImageSize;
  this.getAdjustedDimensions = ImagePanel_GetAdjustedDimensions;
  this.getAdjustedOrigin = ImagePanel_GetAdjustedOrigin;
  this.getActualViewPoint = ImagePanel_GetActualViewPoint;
  this.getCurrentViewPoint = ImagePanel_GetCurrentViewPoint;
  this.getLunaLevel = ImagePanel_GetLunaLevel;
  this.getStackOrder = ImagePanel_GetStackOrder;
  this.setStackOrder = ImagePanel_SetStackOrder
  this.getScaleFactor = ImagePanel_GetScaleFactor;
  this.getDefaultLevel = ImagePanel_GetDefaultLevel;
  this.getLunaLevelRange = ImagePanel_GetLunaLevelRange;
  this.isImageFullyVisible = ImagePanel_IsImageFullyVisible;
  this.isPartialImage = ImagePanel_IsPartialImage;
  this.canFetchEntireImage = ImagePanel_CanFetchEntireImage;
  this.calculateTileDimensions = ImagePanel_CalculateTileDimensions;
  this.equalsCurrentAction = ImagePanel_EqualsCurrentAction;
  this.showControls = ImagePanel_ShowControls;
  this.hideControls = ImagePanel_HideControls;
  this.setLunaSourceUrl = ImagePanel_SetSourceUrl;
  this.setThumbnailUrl = ImagePanel_SetThumbnailUrl;
  this.setIgnoreSizeConstraints = ImagePanel_SetIgnoreSizeConstraints;
  this.setLunaLevel = ImagePanel_SetLunaLevel;
  this.setMaxLunaLevel = ImagePanel_SetMaxLunaLevel;
  this.setControl = ImagePanel_SetControl;
  this.setManager = ImagePanel_SetManager;
  this.setCurrentImagePanel = ImagePanel_SetIsCurrentImagePanel;
  this.setBaseRatio = ImagePanel_SetBaseRatio;
  this.setToolTip = ImagePanel_SetToolTip;
  this.setConfirmBeforeClose = ImagePanel_SetConfirmBeforeClose;
  this.setFullImageUrls = ImagePanel_SetFullImageUrls;
  this.updateImageCover = ImagePanel_UpdateImageCover;
  
  // Annotation stuff
  this.setFunctionFetchAnnotations = ImagePanel_SetFunctionFetchAnnoations;
  this.setFunctionFetchAnnotation = ImagePanel_SetFunctionFetchAnnoation;
  this.setFunctionEditAnnotation = ImagePanel_SetFunctionEditAnnotation;
  this.setFunctionRemoveAnnotation = ImagePanel_SetFunctionRemoveAnnotation;
  this.setFunctionFetchLegacyAnnotations = ImagePanel_SetFunctionFetchLegacyAnnotations;
  this.setFunctionAnnotationCount = ImagePanel_SetFunctionAnnotationCount;
  this.setMediaGroups = ImagePanel_SetMediaGroups;
  this.setCollectionPermissions = ImagePanel_SetCollectionPermissions;
  
  this.initializeAnnotationManager = ImagePanel_InitializeAnnotationManager;
  this.mAnnotationManager = null;
}

/**
 * Initializes all values and creates all elements required to display the panel.
 * @param startingX an optional starting x point
 * @param startingY an optional starting y point
 */
function ImagePanel_Init( startingX, startingY )
{
  // crate the different sections
  this.mPanel = document.createElement( 'div' );
  this.mBody = document.createElement( 'div' );
  this.mImage = document.createElement( 'img' );
  this.mZoomBox = document.createElement( 'div' );
  this.mGlassPlate = $( document.createElement( 'div' ) );
  this.mImageCover = $( document.createElement( 'div' ) );

  // add the sections to the container
  this.mPanel.appendChild( this.mImage );
  this.mPanel.appendChild( this.mBody );
  this.mPanel.appendChild( this.mImageCover );
  this.mPanel.appendChild( this.mZoomBox );

  // make them prototype objects
  this.mPanel = $(this.mPanel);
  this.mImage = $(this.mImage);
  this.mBody = $(this.mBody);
  this.mImageCover = $(this.mImageCover);
  this.mZoomBox = $( this.mZoomBox );

  // put references to ImagePanel
  this.mPanel.mediaPanel = this;
  this.mGlassPlate.mediaPanel = this;
  this.mImageCover.mediaPanel = this;
  this.mImage.mediaPanel = this;
  this.mBody.mediaPanel = this;
  this.mZoomBox.mediaPanel = this;


  // add events
  this.mPanel.onmousedown = Static_ImagePanel_StartMove;
  this.mPanel.onmouseover = Static_ImagePanel_MouseIn;
  this.mPanel.onmouseout = Static_ImagePanel_MouseOut;

  // set the classnames
  this.mPanel.addClassName( this.CLASSNAME );
  this.mZoomBox.className = this.ZOOM_BOX_CLASSNAME;

  this.mGlassPlate.addClassName( 'glassPlate' );

  // finally add the panel to the container
  this.mContainer.appendChild( this.mPanel );
  this.mContainer.appendChild( this.mGlassPlate );

  // prep image for freely moving
  this.mPanel.style.position = 'absolute';
  this.mPanel.style.overflow = 'hidden';
  
  //this.mAnnotationManager = new AnnotationManager( this.mContainer );
  this.initializeAnnotationManager();
  
  var margin = 10;
  var offset = Position.page( this.mContainer );
  this.mGlassPlate.hide();
  this.mGlassPlate.style.position = 'absolute';
  this.mGlassPlate.style.left = ( offset[0] + margin ) + 'px';
  this.mGlassPlate.style.top = ( offset[1] + margin ) + 'px';
  this.mGlassPlate.style.width = ( this.mContainer.getWidth() - ( 2 * margin ) ) + 'px';
  this.mGlassPlate.style.height = ( this.mContainer.getHeight() - ( 2 * margin ) ) + 'px';
  this.mGlassPlate.style.zIndex = 99999;

  YAHOO.util.Event.addListener( this.mGlassPlate, "mousemove", Static_ImagePanel_GlassPlate_Move );
  YAHOO.util.Event.addListener( this.mGlassPlate, "mouseup", Static_ImagePanel_GlassPlate_MouseUp );

  // originally for debugging, turns out IE7 needs a color other wise its as if the div is not there.
  this.mGlassPlate.style.backgroundColor = 'black';
  jshSetOpacity( this.mGlassPlate, .01 );

  this.mBody.style.position = 'absolute';
  this.mBody.style.top = '0px';
  this.mBody.style.left = '0px';
  this.mBody.style.overflow = 'hidden';

  this.mImageCover.style.position = 'absolute';
  this.mImageCover.style.top = '0px';
  this.mImageCover.style.left = '0px';
  this.mImageCover.style.overflow = 'hidden';

  // ie needs the div to have a background color
  // so that it doesnt grab the images behind the cover
  // therefore we set its opacity trasparent so that the image is not changed
  this.mImageCover.style.background = 'black';
  jshSetOpacity( this.mImageCover, .01 );

  this.mImage.style.position = 'absolute';
  this.mImage.style.top = '0px';
  this.mImage.style.left = '0px';

  this.mBody.style.width = '100%';
  this.mBody.style.height = '100%';

  this.mZoomBox.style.position = 'absolute';
  this.mZoomBox.style.display = 'none';
  this.mZoomBox.style.overflow = 'hidden';

  jshSetOpacity( this.mZoomBox, .40 );
  this.hideControls();

  // set a start size and make sure we have a default level
  this.mCurrentViewPoint = $( [ 0, 0 ] );
  this.mLunaLevel = this.getDefaultLevel();
  var maximizeOnLoad = this.mMaximizeOnLoad;
  this.resize( this.DEFAULT_WIDTH, this.DEFAULT_HEIGHT );
  this.mMaximizeOnLoad = maximizeOnLoad;
  this.panTo( 0, 0 );

  // position panel
  if( startingX && startingY )
  {
    this.moveTo( startingX, startingY );
  }

  // create the scaled version
  var ip = this;
  this.mImage.onload = function(e)
    {
      if( ip.mLunaSourceUrl == null )
      {
        var baseRatio = Static_ImagePanel_GetExternalMediaBaseRatio( ip.mImage.width, ip.mImage.height );
        ip.setBaseRatio( baseRatio[0], baseRatio[1] );
        ip.setMaxLunaLevel( ip.EXTERNAL_MEDIA_STARTING_LEVEL + ip.EXTERNAL_ADDITIONAL_LEVELS );

        if( ip.mMaximizeOnLoad == true )
        {
          ip.mLunaLevel = ip.EXTERNAL_MEDIA_STARTING_LEVEL;
          ip.setLunaLevel( ip.mLunaMaxLevel );
          ip.maximizePanelToImage();
        }

        if( ip.deducedState )
        {
          var deducedState = ip.deducedState;
          var ais = deducedState.actualImageSizeWidth;
          var longSide = ip.mBaseRatio[0];
          if( ip.mBaseRatio[1] > ip.mBaseRatio[0]  )
          {
            ais = deducedState.actualImageSizeHeight;
            longSide = ip.mBaseRatio[1];
          }

          // cal the level rounding to the nearest int
          // assume no scale factor ( ie 1 )
          deducedState.level = Math.round( Math.LN2 * Math.log( longSide / ais  ) );
          deducedState.level = Math.min( deducedState.level, ( Static_ImagePanel_EXTERNAL_ADDITIONAL_LEVELS + Static_ImagePanel_EXTERNAL_MEDIA_STARTING_LEVEL ) )
          deducedState.level = Math.max( 0, deducedState.level );

          // now that we have the level lets cal the scale factor
          deducedState.scaleFactorX = ( ( deducedState.actualImageSizeWidth * Math.pow( 2, deducedState.level ) ) / ip.mBaseRatio[0] );
          deducedState.scaleFactorY = ( ( deducedState.actualImageSizeHeight * Math.pow( 2, deducedState.level ) ) / ip.mBaseRatio[1] );
          deducedState.scaleFactor[0] = deducedState.scaleFactorX;
          deducedState.scaleFactor[1] = deducedState.scaleFactorY;

          ip.setLunaLevel( deducedState.level, deducedState.centerPoint, null, deducedState.scaleFactor );
        }
      }

      ip.mInitialized = true;
    };

  this.mImage.src = this.mThumbnailUrl;
}

function ImagePanel_GetLunaLevelRange()
{
  var toReturn = $( [ 0, this.getDefaultLevel() ] );

  if( this.mLunaSourceUrl == null )
  {
    toReturn[1] = this.EXTERNAL_MEDIA_STARTING_LEVEL + this.EXTERNAL_ADDITIONAL_LEVELS;
  }

  return toReturn;
}

function ImagePanel_GetDefaultLevel( startingLevel )
{
  if( this.mLunaSourceUrl == null )
  {
    // must be an external media
    startingLevel = this.EXTERNAL_MEDIA_STARTING_LEVEL;
  }
  else if( this.mLunaMaxLevel > 0 )
  {
    // size 2 which is 2 less than the max level
    startingLevel =  this.mLunaMaxLevel - 2;
  }
  else
  {
    if( ! startingLevel )
      startingLevel = this.mLunaMaxLevel;

    if( ( startingLevel >= 0 ) &&
        ( startingLevel <= this.mLunaMaxLevel ) )
    {

      var newSize = Static_ImagePanel_CalculateRatio( this.mBaseRatio, startingLevel );
      if( ! ( ( newSize[0] >= this.MIN_SIZE ) &&
              ( newSize[1] >= this.MIN_SIZE ) ) )
      {
        return this.getDefaultLevel( startingLevel - 1 );
      }
    }
  }

  return startingLevel;
}

function ImagePanel_UpdateTiles()
{
  if( this.mTiles )
  {
    // is there a delay on fetching tiles?
    if( this.mDelayTiles == null )
    {
      if( this.mLunaSourceUrl != null )
      {
        // must have no delay, so lets build the need tiles as needed.
        var imageDimensions = this.getCurrentImageSize();
        var maxNumberOfTiles = [ Math.ceil(imageDimensions[0] / this.DEFAULT_MAX_IMAGE_WIDTH), Math.ceil(imageDimensions[1] / this.DEFAULT_MAX_IMAGE_HEIGHT) ];
        var actual = this.getActualViewPoint();

        var width = this.DEFAULT_MAX_IMAGE_WIDTH;
        if( this.mScaleFactor && this.mScaleFactor[0] )
          width *= this.mScaleFactor[0];

        var height = this.DEFAULT_MAX_IMAGE_HEIGHT;
        if( this.mScaleFactor && this.mScaleFactor[1] )
          height *= this.mScaleFactor[1];

        width = Math.floor(width);
        height = Math.floor(height);

        var leftMostTile = Math.floor( actual[0] / width );
        leftMostTile = Math.max( leftMostTile, 0 );
        var rightMostTile = Math.ceil( ( actual[0] + this.mPanel.getWidth() ) / width );
        rightMostTile = Math.min( rightMostTile, maxNumberOfTiles[0]-1 );
        rightMostTile = Math.max( rightMostTile, 0 );

        var topMostTile = Math.floor( actual[1] / height );
        topMostTile = Math.max( topMostTile, 0 );
        var bottomMostTile = Math.ceil( ( actual[1] + this.mPanel.getHeight() ) / height );
        bottomMostTile = Math.min( bottomMostTile, maxNumberOfTiles[1]-1 );
        bottomMostTile = Math.max( bottomMostTile, 0 );

        for( var row = topMostTile; row <= bottomMostTile; row++ )
        {
          for( var column = leftMostTile; column <= rightMostTile; column++ )
          {
            this.constructTile( row, column );
          }
        }
      }
      else if( ( this.mFullImageUrls != null ) &&
             ( this.mFullImageUrls.length > 0 ) )
      {
        this.constructTile( 0, 0 );
      }
    }
  }
}

/**
 * Constrcuts the tiles body and preps for the constrcution of tiles
 */
function ImagePanel_ConstructBody()
{
  if( this.mBody )
  {
    // clear whatever is there
    this.mBody.update( '' );

    // determin the size
    var size = this.getCurrentImageSize();

    this.mBody.style.width = size[0] + 'px';
    this.mBody.style.height = size[1] + 'px';

    this.mImage.width = size[0];
    this.mImage.height = size[1];

    // might want to back this up at some point, but maybe not?
    this.mTiles = $( new Array() );
  }
}

/**
 * Constructs the tile at the given location if it has not yet been created.
 * Returns the tile
 */
function ImagePanel_ConstructTile( row, column )
{
  var positionX = 0;
  var positionY = 0;
  var tileWidth = 0;
  var tileHeight = 0;
  if( this.mLunaSourceUrl == null )
  {
    // must be some non luna image, they only get one big tile
    row = 0;
    column = 0;
    if( ( this.mTiles[row] == null  ) ||
        ( this.mTiles[row][column] == null ) )
    {
      if( ( this.mFullImageUrls != null ) &&
          ( this.getLunaLevel() < this.EXTERNAL_MEDIA_STARTING_LEVEL ) )
      {
        this.mTiles[row] = $( new Array() );
        this.mTiles[row][column] = $( document.createElement( 'img' ) );

        this.mBody.appendChild( this.mTiles[row][column] );
        this.mTiles[row][column].src = this.mFullImageUrls[this.mFullImageUrls.length - 1];
        this.mTiles[row][column].mediaPanel = this;
        this.mTiles[row][column].width = this.mImage.width;
        this.mTiles[row][column].height = this.mImage.height;

        // position it top left
        this.mTiles[row][column].style.position = 'absolute';
        this.mTiles[row][column].style.left = 0 + 'px';
        this.mTiles[row][column].style.top = 0 + 'px';
      }
    }
  }

  else if( this.mLunaSourceUrl != null )
  {
    var toReturn = null;
    //debug( 'ImagePanel_ConstructTile: ', column, ', ', row  )
    positionX = ( column * this.DEFAULT_MAX_IMAGE_WIDTH );
    positionY = ( row * this.DEFAULT_MAX_IMAGE_HEIGHT );

    // create row if its not there
    if( ! this.mTiles[row] )
      this.mTiles[row] = $( new Array() );

    // create column if not already there
    if( ! this.mTiles[row][column] )
    {
      var imageDimensions = this.getCurrentImageSize();
      if( ( positionX > imageDimensions[0] ) ||
          ( positionY > imageDimensions[1] ) )
      {
        // tile is out of range of image, do nothing
        return;
      }

      this.mTiles[row][column] = $( document.createElement( 'img' ) );
      this.mTiles[row][column].onerror = function(){this.onerror=null; this.style.display = 'none';};
      this.mBody.appendChild( this.mTiles[row][column] );
      this.mTiles[row][column].mediaPanel = this;

      // some attributes for later
      var tileDimensions = this.calculateTileDimensions( column, row );
      this.mTiles[row][column].sourceUrl =
        Static_ImagePanel_GetLunaImageUrl( this.mLunaSourceUrl, positionX, positionY,
          tileDimensions[0], tileDimensions[1], this.mLunaLevel );

      //debug( column, ', ', row, ' ; ', this.mTiles[row][column].sourceUrl );
      //this.mTiles[row][column].sourceUrl = '';
      //Case -1047:If the img url is empty, in IE it is breaking
      if (this.mTiles[row][column].sourceUrl == '')
      	   this.mTiles[row][column].sourceUrl = null;
      
      this.mTiles[row][column].src = this.mTiles[row][column].sourceUrl;
      //this.mTiles[row][column].style.backgroundColor = ( (column + row) % 2 == 0 ? 'red' : 'aqua' );
      //this.mTiles[row][column].style.backgroundImage = 'url( ' + this.mTiles[row][column].sourceUrl + ' )';
      //this.mTiles[row][column].style.backgroundRepeat = 'no-repeat';

      // scale as needed
      if( this.mScaleFactor )
      {
        if( this.mScaleFactor[0] != 0 )
        {
          this.mTiles[row][column].width = ( tileDimensions[0] * this.mScaleFactor[0] );
          positionX = Math.round( positionX * this.mScaleFactor[0] );
        }

        if( this.mScaleFactor[1]  != 0 )
        {
          this.mTiles[row][column].height = ( tileDimensions[1] * this.mScaleFactor[1] );
          positionY = Math.round( positionY * this.mScaleFactor[1] );
        }
      }

      // position it
      this.mTiles[row][column].style.position = 'absolute';
      this.mTiles[row][column].style.left = positionX + 'px';
      this.mTiles[row][column].style.top = positionY + 'px';

      toReturn = this.mTiles[row][column];
    }
  }


  return toReturn;
}

function ImagePanel_CalculateTileDimensions( column, row )
{
  var dimensions = [this.DEFAULT_MAX_IMAGE_WIDTH, this.DEFAULT_MAX_IMAGE_HEIGHT];
  var position = [ (dimensions[0] * column), (dimensions[1] * row) ];

  var imageSize = Static_ImagePanel_CalculateRatio( this.mBaseRatio, this.mLunaLevel );
  if( ( position[0] + dimensions[0] ) > imageSize[0] )
  {
    dimensions[0] = imageSize[0] - position[0];
  }

  if( ( position[1] + dimensions[1] ) > imageSize[1] )
  {
    dimensions[1] = imageSize[1] - position[1];
  }

  //debug( column, ', ', row, ' ; ', dimensions, ', ',position, ', ', imageSize);

  return dimensions;
}

/**
 * Renders the panel into the given container element
 */
function ImagePanel_Render( element, startingX, startingY )
{
  // render if an element is provided
  if( element )
  {
    this.mContainer = element;
    this.init( startingX, startingY );
  }
}

/**
 * Destroys the panel and notifies the manager.
 * Confirms before destroying if told to.
 */
function ImagePanel_Close( forceClose )
{
  if( ( forceClose == true ) ||
      ( ! this.mConfirmBeforeClose ) ||
      ( confirm( this.CONFIRMATION_BEFORE_CLOSE_MESSAGE ) ) )
  {

    // remove all annotations
	this.mAnnotationManager.removeAll();
	
    // tell manager to takes us off
    this.mManager.removeImagePanel( this );

    // remove from dom
    this.mContainer.removeChild( this.mPanel );
    this.mContainer.removeChild( this.mGlassPlate );

    // notify of destroy
    if( this.mPostDestoryFunction )
    {
      this.mPostDestoryFunction( this );
    }
  }
}

/**
 * Shows the controls by fading them out
 */
function ImagePanel_HideControls( opacity )
{
  //debug( 'hiding controls' ); )
  if( this.mControl )
    this.mControl.hideControls();
}

/**
 * Shows any controls by fading them in
 */
function ImagePanel_ShowControls()
{
  //debug( 'showing controls' );
  if( this.mControl )
    this.mControl.showControls();
}

/**
 * Sets te ImagePanelControl js object which tells the image panel
 * which operation to use.
 */
function ImagePanel_SetControl( imagePanelControl )
{
  this.mControl = imagePanelControl;
}

/**
 * Sets the ImagePanelManager
 */
function ImagePanel_SetManager( manager )
{
  this.mManager = manager;
}

function ImagePanel_SetIsCurrentImagePanel( isCurrent )
{
  this.mIsCurrentImagePanel = isCurrent;

  if( this.mIsCurrentImagePanel )
  {
    this.mPanel.className = this.CLASSNAME + ' ' + this.CURRENT_IMAGE_PANEL_CLASSNAME;
  }
  else
  {
    this.mPanel.className = this.CLASSNAME;
  }
}

function ImagePanel_SetBaseRatio( width, height )
{
  this.mBaseRatio = new Array();

  this.mBaseRatio[0] = width;
  this.mBaseRatio[1] = height;
}

function ImagePanel_SetToolTip( toolTopText )
{
  if( ! toolTopText )
  {
    toolTopText = '[]';
  }

  this.mImageCover.title = toolTopText;
}

function ImagePanel_SetConfirmBeforeClose( confirmBeforeClose )
{
  this.mConfirmBeforeClose = confirmBeforeClose;
}

/**
 * Moves the panel to the given x and y coordinates on the workspace/container.
 */
function ImagePanel_MoveTo( x, y )
{
  //debug( 'moveTo: ' + Position.within( this.mContainer, x, y ) + ', ' +  Position.within( this.mContainer, (x + $(this.mPanel).getWidth() ) , ( y + $(this.mPanel).getHeight() ) ) );
  //debug( Position.page( this.mContainer ) + ' ; ' + x + ', ' + y );

  var offset = Position.cumulativeOffset( this.mContainer );

  //debug( '1Move: ', x, ', ', y, ' ; offset: ', offset );

  // keep within limits
  x = Math.max( x, offset[0] );
  if( ( x + this.mPanel.getWidth() ) > ( offset[0] + this.mContainer.getWidth() ) )
  {
    x = ( offset[0] + this.mContainer.getWidth() ) - this.mPanel.getWidth();
  }

  y = Math.max( y, offset[1] );
  if( ( y + this.mPanel.getHeight() ) > ( offset[1] + this.mContainer.getHeight() ) )
  {
    y = ( offset[1] + this.mContainer.getHeight() ) - this.mPanel.getHeight();
  }

  //debug( '2Move: ', x, ', ', y, ' ; this.mContainer: ', this.mContainer.getWidth(), ', ', this.mContainer.getHeight() );

  this.mPanel.style.left = x + 'px';
  this.mPanel.style.top = y + 'px';

  var dimensions = this.getDimensions();
  var imageSize = this.getCurrentImageSize();
  this.mAnnotationManager.move( Math.round( this.mCurrentViewPoint[0] ), Math.round( this.mCurrentViewPoint[1] ), 
  								Math.round( imageSize[0] ), Math.round( imageSize[1] ), 
								Math.round( dimensions[0] ), Math.round( dimensions[1] ),
								x, y );
  
  return true;
}

/**
 * Moves the image to the given background position coordintate x y.
 * If given values are out of bounds the max will be used.
 * Note that x and y are image coordinates where 0,0 is the top left cornor
 * and ( width, 0 ) is the top right corner.
 */
function ImagePanel_PanTo( x, y )
{
  var dimensions = this.getDimensions();
  var imageSize = this.getCurrentImageSize();

  var fullyVisible = $([ ( dimensions[0] > imageSize[0] ), ( dimensions[1] > imageSize[1] ) ]);
  var origin = this.getAdjustedOrigin();
  var adjustedDimensions = this.getAdjustedDimensions();

  // keep within limits
  if( fullyVisible[0] )
  {
    // center on panel
    x = ( dimensions[0] / 2 );
  }
  else if( ( x == NaN ) || ( x < origin[0] ) )
  {
    // calc the bgp
    x = origin[0];
  }
  else if( x > (origin[0] + adjustedDimensions[0]) )
  {
    x = (origin[0] + adjustedDimensions[0]);
  }

  // make sure y is within range, if not set it
  if( fullyVisible[1] )
  {
    // center on panel
    y = ( dimensions[1] / 2 );
  }
  else if( ( y == NaN ) || ( y < origin[1] ) )
  {
    y = origin[1];
  }
  else if( y > (origin[1] + adjustedDimensions[1]) )
  {
    y = (origin[1] + adjustedDimensions[1]);
  }

  //debug( 'xy:', x, ', ', y );

  x = Math.round( x );
  y = Math.round( y );

  //debug( 'panningTo: ', x, ', ', y, ' dimensions: ', dimensions );

  // update the position
  this.mCurrentViewPoint[0] = x;
  this.mCurrentViewPoint[1] = y;

  //this.setToolTip( this.mCurrentViewPoint );

  // get the actual
  var actual = this.getActualViewPoint();

  if( ! fullyVisible[0] )
    actual[0] *= -1;

  if( ! fullyVisible[1] )
    actual[1] *= -1;

  //debug( '2panning: ' + x + ', ' + y + ' ; this.mCurrentViewPoint: ' + this.mCurrentViewPoint );
  this.mBody.style.left = ( actual[0] ) + 'px';
  this.mBody.style.top = ( actual[1] ) + 'px';

  this.mImage.style.left = ( actual[0] ) + 'px';
  this.mImage.style.top = ( actual[1] ) + 'px';
  
  //console.log( 'panel left: ', this.mPanel.style.left, ' panel top: ', this.mPanel.style.top)
  var offset = Position.cumulativeOffset( this.mPanel );
  //this.mAnnotations[0].move( this.mCurrentViewPoint[0], this.mCurrentViewPoint[1], imageSize[0], imageSize[1], dimensions[0], dimensions[1] );
  this.mAnnotationManager.move( Math.round( this.mCurrentViewPoint[0] ) , Math.round( this.mCurrentViewPoint[1] ), 
  								Math.round( imageSize[0] ), Math.round( imageSize[1] ), 
								Math.round( dimensions[0] ), Math.round( dimensions[1] ),
								offset[0], offset[1] );
  
  // update the tiles on a delay
  var im = this;
  if( this.mDelayTiles != null )
  {
    clearTimeout( this.mDelayTiles );
  }
  this.mDelayTiles = setTimeout( function(){ im.mDelayTiles = null; im.updateTiles(); }, this.TILE_UPDATE_DELAY );

  // tell the manager we have changed
  if( this.mManager )
  {
    this.mManager.imagePanelChanged( this );
  }

  return true;
}

function ImagePanel_UpdateImageCover()
{
  this.mImageCover.style.left = 0 + 'px';
  this.mImageCover.style.top = 0 + 'px';

  // resize image cover
  var dimensions = this.getDimensions();
  this.mImageCover.style.width = dimensions[0] + 'px';
  this.mImageCover.style.height = dimensions[1] + 'px';
}

/**
 * Sets the luna level used to fetched the image
 * where the level is between 0 and the maxLevel inclusive.
 * Note: If level changes will always cause the panel to build its self based ont he given level
 *
 * @param newLevel the new level of the image being displayed
 * @param postPanPoint the point to pan to after the level change.  Defaults to translated equvilate of this.mCurrentViewPoint;
 */
function ImagePanel_SetLunaLevel( newLevel, postPanPoint, translatePoint, scaleFactor )
{
  //debug( 'newLevel: ' + newLevel + ', this.mLunaMaxLevel' + this.mLunaMaxLevel );
  if( ( newLevel >= 0 ) && ( newLevel <= this.mLunaMaxLevel ) )
  {
    // make sure the new image isnt small than our allowed min size
    // or if we dont have a source url then let them do what they want
    var newSize = Static_ImagePanel_CalculateRatio( this.mBaseRatio, newLevel );
    if( ( newSize[0] >= this.MIN_SIZE ) &&
        ( newSize[1] >= this.MIN_SIZE ))
    {
      //debug( 'newLunaLevel: ', newLevel, ' ; oldLevel: ' + this.mLunaLevel, ' ; this.mCurrentViewPoint: ' + this.mCurrentViewPoint );

      // translate current view point to the new level
      if( postPanPoint && translatePoint )
      {
        postPanPoint = Static_ImagePanel_TranslatePoint( postPanPoint, this.mLunaLevel, newLevel, this.mBaseRatio );
      }
      else if( ! postPanPoint )
      {
        postPanPoint = Static_ImagePanel_TranslatePoint( this.mCurrentViewPoint, this.mLunaLevel, newLevel, this.mBaseRatio );
      }

      // do we have some custom scale factor
      if( scaleFactor )
      {
        this.mScaleFactor = scaleFactor;
      }
      else
      {
        this.mScaleFactor = null;
      }

      // set the new level
      this.mLunaLevel = newLevel;

      // construct new tiles
      if( this.mBody )
      {
        this.constructBody();

        // put in a delay on updating tiles so that the server is not overloaded
        // loading in tiles that are only seen for a split second
        var im = this;
        if( this.mDelayTiles != null )
        {
          clearTimeout( this.mDelayTiles );
        }
        this.mDelayTiles = setTimeout( function(){ im.mDelayTiles = null; im.updateTiles(); }, this.TILE_UPDATE_DELAY );

        // continue the with the image change
        this.panTo( postPanPoint[0], postPanPoint[1] );
        var dimensions = this.getDimensions();
        this.resize( dimensions[0], dimensions[1] );
        this.updateImageCover();
        if( this.mManager )
        {
          this.mManager.imagePanelChanged( this );
        }
      }
    }
    else
    {
      this.setLunaLevel( newLevel - 1, postPanPoint, translatePoint );
    }
  }
}

function ImagePanel_Reset()
{
  this.panTo( 0, 0 );
  this.setLunaLevel( this.getDefaultLevel() );
  this.maximizePanelToImage();
}

/**
 * The maximum level the image has.  level should be larger than zero
 */
function ImagePanel_SetMaxLunaLevel( maxLunaLevel )
{
  this.mLunaMaxLevel = maxLunaLevel;
}

/**
 * Sets the image source url
 */
function ImagePanel_SetSourceUrl( sourceUrl )
{
  if( sourceUrl )
  {
    this.mLunaSourceUrl = sourceUrl;
  }
}

function ImagePanel_SetFullImageUrls( fullImageUrls )
{
  this.mFullImageUrls = fullImageUrls;
}

function ImagePanel_SetThumbnailUrl( thumbnailUrl )
{
  if( thumbnailUrl )
  {
    this.mThumbnailUrl = thumbnailUrl
  }
}

function ImagePanel_SetIgnoreSizeConstraints( ignoreSizeConstraints )
{
  if( ignoreSizeConstraints == true )
  {
    this.mIgnoreSizeConstraints = true;
  }
  else
  {
    this.mIgnoreSizeConstraints = false;
  }
}

/**
 * Gets the actual view point versus the current view point whcih is the center of the panel
 */
function ImagePanel_GetActualViewPoint()
{
  var actual = $( new Array() );

  var panelSize = [];
  var dimensions = this.getDimensions();
  var imageSize = this.getCurrentImageSize();
  var fullyVisible = $([ ( dimensions[0] > imageSize[0] ), ( dimensions[1] > imageSize[1] ) ]);

  // we need to use the different dimensions
  // depending if the image is fully visible or not
  if( fullyVisible[0] )
  {
    panelSize[0] = imageSize[0];
  }
  else
  {
    panelSize[0] = dimensions[0];
  }

  if( fullyVisible[1] )
  {
    panelSize[1] = imageSize[1];
  }
  else
  {
    panelSize[1] = dimensions[1];
  }

  actual[0] = Math.round( this.mCurrentViewPoint[0] - ( panelSize[0] / 2 ) );
  actual[1] = Math.round( this.mCurrentViewPoint[1] - ( panelSize[1] / 2 ) );

  if( ( actual[0] == NaN ) )
    actual[0] = 0;

  if( ( actual[1] == NaN ) )
    actual[1] = 0;

  return actual;
}

/**
 * Returns the current view point
 */
function ImagePanel_GetCurrentViewPoint()
{
  return this.mCurrentViewPoint;
}

/**
 * Gets the adjusted origin of the image used primarily to help in panning
 */
function ImagePanel_GetAdjustedOrigin()
{
  var origin = $( new Array() );

  origin[0] = ( this.mPanel.getWidth() / 2 );
  origin[1] = ( this.mPanel.getHeight() / 2 );

  return origin;
}

/**
 * Gets the adjusted dimensions of the image used primarily to help panning
 */
function ImagePanel_GetAdjustedDimensions()
{
  var dimensions = $( new Array() );
  var size = this.getCurrentImageSize();

  dimensions[0] = size[0] - this.mPanel.getWidth();
  dimensions[1] = size[1] - this.mPanel.getHeight();

  return dimensions;
}

/**
 * Gets the current luna level
 */
function ImagePanel_GetLunaLevel()
{
  return this.mLunaLevel;
}

function ImagePanel_GetStackOrder()
{
  return parseInt( this.mPanel.getStyle( 'zIndex' ) );
}

function ImagePanel_SetStackOrder( index )
{
  if( ( index >= 0 ) || ( index < 0 ) )
  {
    this.mPanel.style.zIndex = index;
	this.mAnnotationManager.setStackOrder( index );
  }
}

function ImagePanel_GetScaleFactor()
{
  return this.mScaleFactor;
}


/**
 * Given a start point and an ending point the panel will adjust the zoom and position
 * such that the resulting box is taking up most of the space within the panel.
 */
function ImagePanel_BoxZoomImage( startX, startY, endX, endY )
{
  var boxDimensions = $( [ Math.abs( endX - startX ), Math.abs( endY - startY ) ] );

  // lets find the right level to maximize the current size
  // start at the largest size and work our way down.
  // first proportally sized that fits within the current view port
  var newLevel = 0;
  var avergeSideSize = ( boxDimensions[0] + boxDimensions[1] ) / 2;
  var panelDimensions = this.getDimensions();
  var direction = ( panelDimensions[0] > panelDimensions[1] ) ? 0 : 1;
  var currentRatio = Static_ImagePanel_CalculateRatio( this.mBaseRatio, this.mLunaLevel );
  var percentageOfZoomBox = avergeSideSize / currentRatio[direction];
  var ratio = Static_ImagePanel_CalculateRatio( this.mBaseRatio, newLevel );
  while( ( newLevel <= this.mLunaMaxLevel ) &&
         ( ( ratio[direction] * percentageOfZoomBox ) > panelDimensions[direction] ) )
  {
    // next level down
    newLevel++;
    ratio = Static_ImagePanel_CalculateRatio( this.mBaseRatio, newLevel );
  }

  // calc new start position
  var offset = Position.cumulativeOffset( this.mPanel );
  var actual = this.getActualViewPoint();
  var midPoint = $(new Array());
  midPoint[0] = Math.round( actual[0] + ( Math.min( startX, endX ) - offset[0] ) + ( boxDimensions[0] / 2 ) );
  midPoint[1] = Math.round( actual[1] + ( Math.min( startY, endY ) - offset[1] ) + ( boxDimensions[1] / 2 ) );

  /*debug(
    actual + " ; " +
    boxDimensions + " ; " +
    Math.min( startX, endX ) + ', ' + Math.min( startY, endY ) + " ; " +
    midPoint
  );*/

  // set new level and force update
  this.setLunaLevel( newLevel, midPoint, true );
}

/**
 * Maximizes the panel size to accomidate the image size such that the entire image is fully
 * visible without alterting the image
 */
function ImagePanel_MaximizePanelToImage()
{
  if( this.mBody )
  {

    if( ! this.mLunaLevel )
    {
      this.mLunaLevel = this.getDefaultLevel();
    }

    var size = this.getCurrentImageSize();
    this.resize( size[0], size[1], null, false  );
  }
}

/**
 * Maximizes the image such that all of the image is forced to fit within the current
 * panel size, AKA fit to frame. Note that this may distort the image.
 */
function ImagePanel_MaximizeImageToPanel( maintainAspectRatio )
{
  if( this.mBody )
  {
    // lets calculate the largest level that fits int he current panel dimensions
    var dimensions = this.getDimensions();
    var newDimensions = dimensions;
    var newSize = null;
    var level = 0;

    // TODO: optimize
    level = this.getLunaLevel();
    newDimensions = Static_ImagePanel_CalculateRatio( this.mBaseRatio, level );
    while( ( level >= 0 ) && !( ( newDimensions[0] >= dimensions[0] ) || ( newDimensions[1] >= dimensions[1] ) ) )
    {
      level--;
      newDimensions = Static_ImagePanel_CalculateRatio( this.mBaseRatio, level );
    }

    newDimensions = Static_ImagePanel_CalculateRatio( this.mBaseRatio, level );
    newSize = Static_ImagePanel_CalculateRatio( this.mBaseRatio, level );
    if( maintainAspectRatio != false )
    {
      var largeSide = ( newSize[0] > newSize[1] )? 0 : 1;
      var smallSide = ( largeSide == 0 )? 1 : 0;
      var ratio = newSize[smallSide] / newSize[largeSide];

      newSize[largeSide] = dimensions[largeSide];
      newSize[smallSide] = Math.round( newSize[largeSide] * ratio );

      // the small side is still to large to fit within the container
      // so lets flip it
      if( newSize[smallSide] > dimensions[smallSide] )
      {
        ratio = newSize[largeSide] / newSize[smallSide];
        newSize[smallSide] = dimensions[smallSide];
        newSize[largeSide] = Math.round( newSize[smallSide] * ratio );
      }

      // change dimensions and resize
      dimensions = newSize;
      this.resize( dimensions[0], dimensions[1] );
    }

    var scaleFactor = $([ ( dimensions[0] / newDimensions[0] ), ( dimensions[1] / newDimensions[1] ) ]);
    this.setLunaLevel( level, null, null, scaleFactor );
  }
}

/**
 * Resizes the image to take up as much of the cotnainer ( workspace ) by forcing
 * the image to largest size maintaing the aspect ratio.
 */
function ImagePanel_MaximizePanelToContainer( maintainAspectRatio )
{
  if( this.mContainer )
  {
    var dimensions = [ this.mContainer.getWidth(), this.mContainer.getHeight() ];
    this.resize( dimensions[0], dimensions[1] );
    this.maximizeImageToPanel( maintainAspectRatio );
    this.centerOnContainer();
  }
}

/**
 * centers the panel within the container
 */
function ImagePanel_CenterOnContainer()
{
  if( this.mContainer )
  {
    var offset = Position.cumulativeOffset( this.mContainer );
    var dimensions = [ this.mContainer.getWidth(), this.mContainer.getHeight() ];
    var panelDimensions = this.getDimensions();

    var x = Math.round( ( dimensions[0] / 2 ) - ( panelDimensions[0] / 2 ) ) + offset[0];
    var y = Math.round( ( dimensions[1] / 2 ) - ( panelDimensions[1] / 2 ) ) + offset[1];

    this.moveTo( x, y );
  }
}

/**
 * Resize the panel size capping it to the max panel width and height
 */
function ImagePanel_Resize( width, height, enforceMaxSize )
{
  var toReturn = true;
  if( width && height )
  {
    var size = this.getDimensions();
    var actual = [ Math.abs( parseInt( this.mBody.style.left ) ), Math.abs( parseInt( this.mBody.style.top ) ) ];

    // dont let them resize larger than the image size
    if( ( enforceMaxSize != false ) && ( this.mIgnoreSizeConstraints != true ) )
    {
      width = Math.min( width, size[0] );
      height = Math.min( height, size[1] );
    }

    // dont allow them to resize larger than the container/workspace
    width = Math.min( width, (this.mContainer.getWidth() - 20) );
    height = Math.min( height, (this.mContainer.getHeight() - 20) );

    // dont allow them to resize small than min
    // unless the image is that small
    width = Math.max( width, this.MIN_SIZE );
    height = Math.max( height, this.MIN_SIZE );

    // update the current view point otherwise we get a jump next time they pan.
    var borderWidth = parseInt( this.mPanel.getStyle( 'borderLeftWidth' ) ) + parseInt( this.mPanel.getStyle( 'borderRightWidth' ) );
    var borderHeight = parseInt( this.mPanel.getStyle( 'borderBottomWidth' ) ) + parseInt( this.mPanel.getStyle( 'borderTopWidth' ) );
    this.mCurrentViewPoint[0] += ( ( width + borderWidth - this.mPanel.getWidth() ) / 2 );
    this.mCurrentViewPoint[1] += ( ( height + borderHeight - this.mPanel.getHeight() ) / 2 );

    //debug( 'resizing: ', width, ', ', height );

    // resize panel
    this.mPanel.style.width = width + 'px';
    this.mPanel.style.height = height + 'px';
    this.updateImageCover();

    if( ( width > (size[0] - actual[0]) ) ||
        ( height > (size[1] - actual[1]) ) )
    {
      this.panTo( this.mCurrentViewPoint[0], this.mCurrentViewPoint[1] );
    }

    // tell the manager we have changed
    if( this.mManager )
    {
      this.mManager.imagePanelChanged( this );
    }

    this.mMaximizeOnLoad = false;
  }

  return toReturn;
}

/**
 * Passthrought to the manger, brings this panel to the front
 */
function ImagePanel_BringToFront()
{
  if( this.mManager )
  {
    this.mManager.setCurrentImagePanel( this );
  }
}

/**
 * Prepare to add an annotation to panel
 */
function ImagePanel_StartAddAnnotation( currentActionInProgress )
{
  if( this.mPanel )
  {
    var mediaPanel = this;
	//console.log('Start Adding Annotation');
	if (!(mediaPanel.mActionInProgress == true)) 
	{
      mediaPanel.updateCursor( true );

      mediaPanel.mActionInProgress = true;
      mediaPanel.mCurrentActionInProgress = currentActionInProgress;
	}
  }
}

/**
 * Updates the cursor based on the current action
 */
function ImagePanel_UpdateCursor( isActive )
{
  // set the right cursor based on current action,
  // only changes if needed to minimize IE's reloading of images
  if( this.equalsCurrentAction( this.mControl.ACTION_PAN ) )
  {
    if( isActive == true )
    {
      if( ! this.mGlassPlate.hasClassName( this.PAN_ACTIVE_CURSOR_CLASSNAME ) )
      {
        this.setCurrentImagePanel( this.mIsCurrentImagePanel );

        this.mPanel.className += ' ' + this.PAN_ACTIVE_CURSOR_CLASSNAME;
        this.mGlassPlate.className = this.PAN_ACTIVE_CURSOR_CLASSNAME;
        this.mBody.className = this.PAN_ACTIVE_CURSOR_CLASSNAME;
      }
    }
    else
    {
      this.setCurrentImagePanel( this.mIsCurrentImagePanel );

      this.mPanel.className += ' ' + this.PAN_CURSOR_CLASSNAME;
      this.mGlassPlate.className = this.PAN_CURSOR_CLASSNAME;
      this.mBody.className = this.PAN_CURSOR_CLASSNAME;
    }
  }
  else if( this.equalsCurrentAction( this.mControl.ACTION_MOVE ) )
  {
    if( isActive == true )
    {
      if( ! this.mGlassPlate.hasClassName( this.MOVE_ACTIVE_CURSOR_CLASSNAME ) )
      {
        this.setCurrentImagePanel( this.mIsCurrentImagePanel );

        this.mPanel.className += ' ' + this.MOVE_ACTIVE_CURSOR_CLASSNAME;
        this.mGlassPlate.className = this.MOVE_ACTIVE_CURSOR_CLASSNAME;
        this.mBody.className = this.MOVE_ACTIVE_CURSOR_CLASSNAME;
      }
    }
    else
    {
      this.setCurrentImagePanel( this.mIsCurrentImagePanel );

      this.mPanel.className += ' ' + this.MOVE_CURSOR_CLASSNAME;
      this.mGlassPlate.className = this.MOVE_CURSOR_CLASSNAME;
      this.mBody.className = this.MOVE_CURSOR_CLASSNAME;
    }
  }
  else if( this.equalsCurrentAction( this.mControl.ACTION_ADD_ANNOTATION ) )
  {
    if( isActive == true )
    {
      if( ! this.mGlassPlate.hasClassName( this.ADD_ANNOTATION_ACTIVE_CURSOR_CLASSNAME ) )
      {
        this.setCurrentImagePanel( this.mIsCurrentImagePanel );

        this.mPanel.className += ' ' + this.ADD_ANNOTATION_ACTIVE_CURSOR_CLASSNAME;
        this.mGlassPlate.className = this.ADD_ANNOTATION_ACTIVE_CURSOR_CLASSNAME;
        this.mBody.className = this.ADD_ANNOTATION_ACTIVE_CURSOR_CLASSNAME;
      }
    }
    else
    {
      this.setCurrentImagePanel( this.mIsCurrentImagePanel );

      this.mPanel.className += ' ' + this.ADD_ANNOTATION_ACTIVE_CURSOR_CLASSNAME;
      this.mGlassPlate.className = this.ADD_ANNOTATION_ACTIVE_CURSOR_CLASSNAME;
      this.mBody.className = this.ADD_ANNOTATION_ACTIVE_CURSOR_CLASSNAME;
    }
  }

  //alert('isActive: ' + isActive + ' ; ' + this.mPanel.className);
}

function ImagePanel_CurrentActionHasChanged( newAction )
{
  this.updateCursor( false );
}

function ImagePanel_EndCurrentAction( cancelAction )
{
  var obj = new Object();
  obj.srcElement = new Object();
  obj.srcElement.mediaPanel = this;

  if( this.mActionInProgress == true )
  {
    Static_ImagePanel_EndMove( obj, cancelAction );
  }
}

/**
 * Marks the end of a panel move operation
 */
function Static_ImagePanel_EndMove( e, cancelAction )
{
  var sourceElement = jshGetSourceElement( e );

  //debug( 'ending move cancelAction:' + cancelAction, sourceElement, ' ; ', sourceElement.mediaPanel, ' ; ', sourceElement.mediaPanel.mControl.getCurrentAction() );
  if( sourceElement.mediaPanel )
  {
    var mediaPanel = sourceElement.mediaPanel;

    if( mediaPanel.mActionInProgress == true )
    {
      // reset variables
      mediaPanel.mActionInProgress = null;
      mediaPanel.mCurrentActionInProgress = null;

      // lastly reset the action
      if( mediaPanel.mControl )
      {
        mediaPanel.mControl.setAction( null );
      }
      mediaPanel.updateCursor( false );

      // reset events
      mediaPanel.mGlassPlate.hide();
      mediaPanel.mGlassPlate.onmouseup = null;
      mediaPanel.mGlassPlate.onmousemove = null;
      mediaPanel.mGlassPlate.onmousedown = null;
      mediaPanel.mGlassPlate.onmouseout = null;
      mediaPanel.mContainer.onmouseup = null;
      mediaPanel.mContainer.onmousemove = null;
      mediaPanel.mContainer.onmousedown = null;
      mediaPanel.mContainer.mediaPanel = null;
      mediaPanel.mContainer.onmouseout = null;

      // reset poistion to original
      if( cancelAction == true )
      {
        if( mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_MOVE ) )
        {
          mediaPanel.moveTo( mediaPanel.mOriginalMoveStartingX, mediaPanel.mOriginalMoveStartingY );
        }
        else if( mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_PAN ) )
        {
          mediaPanel.panTo( mediaPanel.mOriginalPanStartingX, mediaPanel.mOriginalPanStartingY );
        }
        else if ( mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_RESIZE_TOP_LEFT ) ||
            mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_RESIZE_TOP_RIGHT ) ||
            mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_RESIZE_BOTTOM_RIGHT ) ||
            mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_RESIZE_BOTTOM_LEFT ) )
        {
          //mediaPanel.resize( mediaPanel.mResizeStartingWidth, mediaPanel.mResizeStartingHeight );
        }
      }
      else
      {
        // action was completed fine, so not canceled
        if( mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_ZOOM_BOX ) )
        {

          mediaPanel.boxZoomImage(
            mediaPanel.mOriginalStartingX,
            mediaPanel.mOriginalStartingY,
            mediaPanel.mMoveStartingX,
            mediaPanel.mMoveStartingY );
        }
        else if( mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_RESIZE_TOP_LEFT ) ||
            mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_RESIZE_TOP_RIGHT ) ||
            mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_RESIZE_BOTTOM_RIGHT ) ||
            mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_RESIZE_BOTTOM_LEFT ) )
        {
          // notify after resize
          if( mediaPanel.mPostResizeFunction )
          {
            // notify only if there was a change in size
            var dimensions = mediaPanel.getDimensions();
            if( ! ( ( mediaPanel.mResizeStartingWidth == dimensions[0] ) &&
                    ( mediaPanel.mResizeStartingHeight == dimensions[1] ) ) )
            {
              mediaPanel.mPostResizeFunction( mediaPanel, dimensions );
            }
          }
        }
      }

      // reset the last of the variables
      mediaPanel.mMoveStartingX = null;
      mediaPanel.mMoveStartingY = null;
      mediaPanel.mResizeStartingWidth = null;
      mediaPanel.mResizeStartingHeight = null;
      mediaPanel.mOriginalMoveStartingX = null;
      mediaPanel.mOriginalMoveStartingY = null;
      mediaPanel.mOriginalPanStartingX = null;
      mediaPanel.mOriginalPanStartingY = null;
      mediaPanel.mOriginalStartingX = null;
      mediaPanel.mOriginalStartingY = null;
      mediaPanel.mResizeStartingWidth = null;
      mediaPanel.mResizeStartingHeight = null;
      mediaPanel.mMoveStartingX = null;
      mediaPanel.mMoveStartingY = null;

      // hide zoom box
      mediaPanel.mZoomBox.hide();
    }
  }
}

/**
 * Marks the state of a panel move operation
 */
function Static_ImagePanel_StartMove( e )
{
  e = jshGetEvent( e );
  var sourceElement = jshGetSourceElement( e );

  //debug( 'Static_ImagePanel_StartMove:', sourceElement, ' ; ', sourceElement.mediaPanel, ' ; ', sourceElement.mediaPanel.mControl.getCurrentAction() );
  if( sourceElement.mediaPanel )
  {
    var mediaPanel = sourceElement.mediaPanel;

    if( mediaPanel.mControl )
    {
      if( mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_MOVE ) ||
          mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_PAN  ) ||
          mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_ZOOM_BOX  ) ||
          mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_RESIZE_TOP_LEFT ) ||
          mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_RESIZE_TOP_RIGHT ) ||
          mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_RESIZE_BOTTOM_RIGHT ) ||
          mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_RESIZE_BOTTOM_LEFT ) )
      {
        if( !( mediaPanel.mActionInProgress == true )  )
        {
          mediaPanel.bringToFront();
          mediaPanel.updateCursor( true );

          mediaPanel.mActionInProgress = true;
          mediaPanel.mCurrentActionInProgress = mediaPanel.mControl.getCurrentAction();
          
          // back up some values incase we cancel or need a reference
          var offset = Position.page( mediaPanel.mPanel );
          mediaPanel.mOriginalMoveStartingX = offset[0];
          mediaPanel.mOriginalMoveStartingY = offset[1];

          var viewPoint = mediaPanel.getCurrentViewPoint();
          mediaPanel.mOriginalPanStartingX = viewPoint[0];
          mediaPanel.mOriginalPanStartingY = viewPoint[1];

          var dimensions = mediaPanel.getDimensions();
          mediaPanel.mResizeStartingWidth = dimensions[0];
          mediaPanel.mResizeStartingHeight = dimensions[1];

          // this guy gets updated
          mediaPanel.mMoveStartingX = Event.pointerX( e );
          mediaPanel.mMoveStartingY = Event.pointerY( e );

          // this guy stays the same
          mediaPanel.mOriginalStartingX = Event.pointerX( e );
          mediaPanel.mOriginalStartingY = Event.pointerY( e );

          // set events on other elements to get a "smooth" motion
          mediaPanel.mGlassPlate.show();
          mediaPanel.mGlassPlate.mediaPanel = mediaPanel;
          mediaPanel.mGlassPlate.onmousemove = Static_ImagePanel_Move;
          mediaPanel.mGlassPlate.onmouseup = Static_ImagePanel_EndMove;
          mediaPanel.mGlassPlate.onmouseout = Static_ImagePanel_EndMove;

          mediaPanel.mContainer.mediaPanel = mediaPanel;
          mediaPanel.mContainer.onmousemove = Static_ImagePanel_Move;
          mediaPanel.mContainer.onmouseup = Static_ImagePanel_EndMove;
        }
      }
      else if( mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_ADD_ANNOTATION ) )
      { 
        if( !mediaPanel.mIsCurrentImagePanel ) //no need to bring to front again
		{
          mediaPanel.bringToFront();
          mediaPanel.updateCursor( true );
		}
        // Now in Edit Mode. The next click with bring up the edit panel.
		mediaPanel.mControl.setAction( mediaPanel.mControl.ACTION_EDIT_ANNOTATION );
        mediaPanel.mActionInProgress = true;
		
		mediaPanel.hideControls();
		//console.log("set action to ACTION_EDIT_ANNOTATION");
      }
	  else if( mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_EDIT_ANNOTATION ) )
      { 
	    // begins adding (editing) the new annotation. 
        Static_ImagePanel_AddAnnotation( e );
      }
    }
  }
}

/**
 * The actual work of a Move operation which can be either a
 * Panel move or pan based on what the control is set to
 */
function Static_ImagePanel_Move( e )
{
  e = jshGetEvent( e );
  var sourceElement = jshGetSourceElement( e );

  //debug( 'Static_ImagePanel_Move:', sourceElement, ' ; ', sourceElement.mediaPanel, ' ; ', sourceElement.mediaPanel.mControl.getCurrentAction() );
  if( sourceElement.mediaPanel )
  {
    // move the panel if we are in move mode
    var mediaPanel = sourceElement.mediaPanel;
    if( mediaPanel.mActionInProgress == true )
    {
    
      //If the current action has changed before the previous action ended, then continue the previous action      
      if (mediaPanel.mCurrentActionInProgress && mediaPanel.mCurrentActionInProgress != null)
      	    mediaPanel.mControl.setAction(mediaPanel.mCurrentActionInProgress);    
      	    
      
      // current mouse position      
      var endingX = Event.pointerX( e );
      var endingY = Event.pointerY( e );

      // cal how many px we are moving
      var difference = $( [ ( endingX - mediaPanel.mMoveStartingX ), ( endingY - mediaPanel.mMoveStartingY ) ] );

      // update values for next time
      mediaPanel.mMoveStartingX = endingX;
      mediaPanel.mMoveStartingY = endingY;

      mediaPanel.updateCursor( true );

      // do the action
      if( mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_MOVE ) )
      {
        // calc actual new move position
        var offset = Position.cumulativeOffset( mediaPanel.mPanel );
        var x = offset[0] + difference[0];
        var y = offset[1] + difference[1];

        mediaPanel.moveTo( x, y );
      }
      else if( mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_PAN ) )
      {
        // calc actual new pab position
        var x = mediaPanel.mCurrentViewPoint[0] - difference[0];
        var y = mediaPanel.mCurrentViewPoint[1] - difference[1];

        // do the pan
        mediaPanel.panTo( x, y );
      }
      else if( mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_ZOOM_BOX ) )
      {
        var offset = Position.cumulativeOffset( mediaPanel.mPanel );

        var x = Math.min( mediaPanel.mOriginalStartingX, endingX ) - offset[0];
        var y = Math.min( mediaPanel.mOriginalStartingY, endingY ) - offset[1];

        var width = Math.abs( endingX - mediaPanel.mOriginalStartingX );
        var height = Math.abs( endingY - mediaPanel.mOriginalStartingY );

        mediaPanel.mZoomBox.style.left = ( x ) + 'px';
        mediaPanel.mZoomBox.style.top = ( y ) + 'px';

        mediaPanel.mZoomBox.style.width = width + 'px';
        mediaPanel.mZoomBox.style.height = height + 'px';

        mediaPanel.mZoomBox.style.display = 'block';
      }
      else if( mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_RESIZE_TOP_LEFT ) )
      {
        var position = mediaPanel.getPosition();
        var dimensions = mediaPanel.getDimensions();

        mediaPanel.resize( ( dimensions[0] - difference[0] ), ( dimensions[1] - difference[1] ) );
        mediaPanel.moveTo( ( position[0] + difference[0] ), ( position[1] + difference[1] ) );
      }
      else if( mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_RESIZE_TOP_RIGHT ) )
      {
        var position = mediaPanel.getPosition();
        var dimensions = mediaPanel.getDimensions();

        mediaPanel.resize( ( dimensions[0] + difference[0] ), ( dimensions[1] - difference[1] ) );
        mediaPanel.moveTo( position[0], ( position[1] + difference[1] ) );
      }
      else if( mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_RESIZE_BOTTOM_RIGHT ) )
      {
        var dimensions = mediaPanel.getDimensions();

        mediaPanel.resize( ( dimensions[0] + difference[0] ), ( dimensions[1] + difference[1] ) );
      }
      else if( mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_RESIZE_BOTTOM_LEFT ) )
      {
        var position = mediaPanel.getPosition();
        var dimensions = mediaPanel.getDimensions();

        mediaPanel.resize( ( dimensions[0] - difference[0] ), ( dimensions[1] + difference[1] ) );
        mediaPanel.moveTo( ( position[0] + difference[0] ), position[1] );
      }
    }
  }
}

/**
 * Marks the end of a panel move operation
 */
function Static_ImagePanel_EndZoom( e, cancelAction )
{
  //debug( 'Static_ImagePanel_EndZoom' );
  e = jshGetEvent( e );
  var sourceElement = jshGetSourceElement( e );

  if( sourceElement.mediaPanel )
  {
    var mediaPanel = sourceElement.mediaPanel;

    if( mediaPanel.mZoomInProgress == true )
    {
      // hide the zoom box!
      mediaPanel.mZoomBox.style.display = 'none';

      // did some one cancel the action?
      if( ! ( cancelAction == true ) )
      {
        // did we get a single click?
        var isSingleClick = ( ( mediaPanel.mMoveStartingX == Event.pointerX( e ) ) && ( mediaPanel.mMoveStartingY == Event.pointerY( e ) ) );
        if( isSingleClick )
        {
          // calc new level
          var newLevel = 0;
          if( mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_ZOOM_IN ) )
            newLevel = (mediaPanel.mLunaLevel - 1);
          else if( mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_ZOOM_OUT ) )
            newLevel = (mediaPanel.mLunaLevel + 1);

          // lets focus around click and center on screen
          var point = $( new Array() );
          var offset = mediaPanel.getActualViewPoint();
          point[0] = offset[0] + Event.pointerX( e );
          point[1] = offset[1] + Event.pointerY( e );

          // set the level
          mediaPanel.setLunaLevel( newLevel, point, true );
        }
        else
        {
          // zooooooom box!
          var offset = Position.page( mediaPanel.mPanel );
          var actual = mediaPanel.getActualViewPoint();
          var startingX = actual[0] + ( mediaPanel.mMoveStartingX - offset[0] );
          var startingY = actual[1] + ( mediaPanel.mMoveStartingY - offset[1] );
          var endingX = actual[0] + Event.pointerX( e );
          var endingY = actual[1] + Event.pointerY( e );
          mediaPanel.boxZoom(
            startingX,
            startingY,
            endingX,
            endingY
          );
        }
      }

      // reset variables
      mediaPanel.mZoomInProgress = null;
      mediaPanel.mMoveStartingX = null;
      mediaPanel.mMoveStartingY = null;

      mediaPanel.mContainer.mediaPanel = null;
      mediaPanel.mContainer.onmousemove = null;
      mediaPanel.mContainer.onmousedown = null;
      mediaPanel.mBody.onmousemove = null;
      mediaPanel.mZoomBox.onmousemove = null;
    }
  }
}

/**
 * Marks the state of a panel move operation
 */
function Static_ImagePanel_StartZoom( e )
{
  //debug( 'starting zoom' );
  e = jshGetEvent( e );
  var sourceElement = jshGetSourceElement( e );

  if( sourceElement.mediaPanel )
  {
    var mediaPanel = sourceElement.mediaPanel;

    // skip if we are moving or panning
    if( !( mediaPanel.mResizeInProgress == true ) &&
        !( mediaPanel.mMoveInProgress == true ) )
    {
      mediaPanel.bringToFront();

      mediaPanel.mZoomInProgress = true;

      mediaPanel.mMoveStartingX = Event.pointerX( e );
      mediaPanel.mMoveStartingY = Event.pointerY( e );

      mediaPanel.mContainer.mediaPanel = mediaPanel;
      mediaPanel.mBody.onmousemove = Static_ImagePanel_Zoom;
      mediaPanel.mZoomBox.onmousemove = Static_ImagePanel_Zoom;
      mediaPanel.mContainer.onmouseup = Static_ImagePanel_EndZoom;

      var offset = Position.page( mediaPanel.mPanel );
      mediaPanel.mZoomBox.style.left = ( mediaPanel.mMoveStartingX - offset[0] ) + 'px';
      mediaPanel.mZoomBox.style.top = ( mediaPanel.mMoveStartingY - offset[1] ) + 'px';
    }
  }
}

function Static_ImagePanel_Zoom( e )
{
  //debug( 'Static_ImagePanel_Zoom' );
  e = jshGetEvent( e );
  var sourceElement = jshGetSourceElement( e );

  if( sourceElement.mediaPanel )
  {
    // move the panel if we are in move mode
    var mediaPanel = sourceElement.mediaPanel;

    // update the bounding box

    var offset = Position.page( mediaPanel.mPanel );
    var startingPointX = Math.min( mediaPanel.mMoveStartingX, Event.pointerX( e ) );
    var startingPointY = Math.min( mediaPanel.mMoveStartingY, Event.pointerY( e ) );

    mediaPanel.mZoomBox.style.left = ( startingPointX - offset[0] ) + 'px';
    mediaPanel.mZoomBox.style.top = ( startingPointY - offset[1] ) + 'px';

    mediaPanel.mZoomBox.style.width = Math.abs( Event.pointerX( e ) - mediaPanel.mMoveStartingX ) + 'px';
    mediaPanel.mZoomBox.style.height = Math.abs( Event.pointerY( e ) - mediaPanel.mMoveStartingY ) + 'px';

    mediaPanel.mZoomBox.style.display = 'block';
  }
}

/**
 * Gets the current postion of the panel as an array
 */
function ImagePanel_GetPosition()
{
  var toReturn = new Array();
  toReturn[ 0 ] = parseInt( this.mPanel.getStyle( 'left' ) );
  toReturn[ 1 ] = parseInt( this.mPanel.getStyle( 'top' ) );

  return toReturn;
}

/**
 * Gets teh current dimensions of the panel as an array
 */
function ImagePanel_GetDimensions()
{
  var toReturn = new Array();
  var borderWidth = parseInt( this.mPanel.getStyle( 'borderLeftWidth' ) ) + parseInt( this.mPanel.getStyle( 'borderRightWidth' ) );
  var borderHeight = parseInt( this.mPanel.getStyle( 'borderBottomWidth' ) ) + parseInt( this.mPanel.getStyle( 'borderTopWidth' ) );
  toReturn[ 0 ] = this.mPanel.getWidth() - borderWidth;
  toReturn[ 1 ] = this.mPanel.getHeight() - borderHeight;

  return toReturn;
}

function ImagePanel_GetCurrentImageSize()
{
  var toReturn = Static_ImagePanel_CalculateRatio( this.mBaseRatio, this.mLunaLevel );
  if( this.mScaleFactor )
  {
    if( this.mScaleFactor[0] )
      toReturn[0] *= this.mScaleFactor[0];

    if( this.mScaleFactor[1] )
      toReturn[1] *= this.mScaleFactor[1];
  }

  return toReturn;
}

/**
 * Returns true iff the image is fully visible in the current view port of the panel, ie on the x and y axis.
 * So if any portion of the panel is not fully covered by the image false is returned.
 *
 * If anyDirection is true then if on the x or y is fully visible true is returned.
 *
 */
function ImagePanel_IsImageFullyVisible( anyDirection )
{
  var panelDimensions = this.getDimensions();
  var imageSize = this.getCurrentImageSize();

  if( anyDirection == true )
  {
    if( panelDimensions[0] >= imageSize[0] ||
        panelDimensions[1] >= imageSize[1] )
    {
      return true;
    }
  }
  else if( panelDimensions[0] >= imageSize[0] &&
           panelDimensions[1] >= imageSize[1] )
  {
    return true;
  }


  return false;
}

/**
 * Returns true iff the image is partially hidden in the current view port of the panel, ie on the x and y axis.
 * So if the entire image is not fully visible true is returned.
 *
 * If anyDirection is true then if on the x or y is covered true is returned.
 *
 */
function ImagePanel_IsPartialImage( anyDirection )
{
  var panelDimensions = this.getDimensions();
  var imageSize = this.getCurrentImageSize();

  if( anyDirection == true )
  {
    if( panelDimensions[0] < imageSize[0] ||
        panelDimensions[1] < imageSize[1] )
    {
      return true;
    }
  }
  else if( panelDimensions[0] < imageSize[0] &&
           panelDimensions[1] < imageSize[1] )
  {
    return true;
  }


  return false;
}

/**
 * Returns true if the entire imge can be fetched at once
 *
 * @deprecated dont think this means much since titles
 */
function ImagePanel_CanFetchEntireImage()
{
  return false;
}

/**
 * Returns true if the given action is the current one
 */
function ImagePanel_EqualsCurrentAction( action )
{
  if( this.mControl.getCurrentAction() == action )
  {
    return true;
  }

  return false;
}

function ImagePanel_InitializeAnnotationManager()
{
  this.mAnnotationManager = new AnnotationManager( this.mContainer, this.mFunctionFetchAnnotations, this.mFunctionEditAnnotation, this.mFunctionFetchAnnotation, this.mFunctionRemoveAnnotation, this.mFunctionFetchLegacyAnnotations, this.mFunctionAnnotationCount, this.mMediaGroups, this.mCollectionPermissions );
}

function ImagePanel_SetFunctionFetchAnnoations( functionFetchAnnotations )
{
  this.mFunctionFetchAnnotations = functionFetchAnnotations;
}

function ImagePanel_SetFunctionFetchAnnoation( functionFetchAnnotation )
{
  this.mFunctionFetchAnnotation = functionFetchAnnotation;
}

function ImagePanel_SetFunctionEditAnnotation( functionEditAnnotation )
{
  this.mFunctionEditAnnotation = functionEditAnnotation;
}

function ImagePanel_SetFunctionRemoveAnnotation( functionRemoveAnnotation )
{
  this.mFunctionRemoveAnnotation = functionRemoveAnnotation;
}

function ImagePanel_SetFunctionFetchLegacyAnnotations( functionFetchLegacyAnnotations )
{
  this.mFunctionFetchLegacyAnnotations = functionFetchLegacyAnnotations;
}

function ImagePanel_SetFunctionAnnotationCount( functionAnnotationCount )
{
  this.mFunctionAnnotationCount = functionAnnotationCount;
}

function ImagePanel_SetMediaGroups( mediaGroups )
{
  this.mMediaGroups = mediaGroups;
}

function ImagePanel_SetCollectionPermissions( annotationCollectionPermissions )
{
  this.mCollectionPermissions = annotationCollectionPermissions;
}
/**
 * Static event function when a mouse in occurs
 */
function Static_ImagePanel_MouseIn( e )
{
  e = jshGetEvent( e );
  var sourceElement = jshGetSourceElement( e );

  // check the parent to see if he has an mediaPanel.
  if( ! sourceElement.mediaPanel )
  {
    sourceElement = sourceElement.up();
  }

  if( sourceElement.mediaPanel )
  {
    var mediaPanel = sourceElement.mediaPanel;

    // skip if we are in the middle of a move, pan, or resize
    if( ( mediaPanel.mActionInProgress != true ) )
    {
      if( mediaPanel.mControl )
      {
        mediaPanel.mLastIdleTime = new Date();
        var con = mediaPanel.mControl;
        setTimeout( function(){ con.hideIfIdle() }, con.IDLE_TIME );
      }

      // must not be over a control so lets show the controls
      mediaPanel.showControls();
    }
  }
}

/**
 * Static event function when a mouse out occurs
 */
function Static_ImagePanel_MouseOut( e )
{
  var e = jshGetEvent( e );
  var sourceElement = jshGetSourceElement( e );

  if( sourceElement.mediaPanel )
  {
    var mediaPanel = sourceElement.mediaPanel;

    // skip it if we are in the middle of a move, pan, or resize
    if( ( mediaPanel.mActionInProgress != true ) )
    {
      mediaPanel.hideControls();
    }
  }
}

function Static_ImagePanel_AddAnnotation( e )
{
  var e = jshGetEvent( e );
  var sourceElement = jshGetSourceElement( e );

  if ( sourceElement.parentNode.mediaPanel ) 
  {
  	var mediaPanel = sourceElement.parentNode.mediaPanel;
    //console.log( "Static_ImagePanel_AddAnnotation: currentAction: " + mediaPanel.mControl.getCurrentAction());
  	if (mediaPanel.equalsCurrentAction( mediaPanel.mControl.ACTION_EDIT_ANNOTATION )) 
	{
	  var dimensions = mediaPanel.getDimensions();
	  var imageSize = mediaPanel.getCurrentImageSize();
	  var offset = Position.cumulativeOffset(sourceElement);
      var toAddPosX = Event.pointerX( e );
      var toAddPosY = Event.pointerY( e );

      mediaPanel.mAnnotationManager.setStackOrder( mediaPanel.getStackOrder() );
	  var isAdded = mediaPanel.mAnnotationManager.add( 	mediaPanel.mAnnotationManager, Math.round(mediaPanel.mCurrentViewPoint[0]), Math.round(mediaPanel.mCurrentViewPoint[1]), 
	  										Math.round(imageSize[0]), Math.round(imageSize[1]), 
											Math.round(dimensions[0]), Math.round(dimensions[1]), 
											offset[0], offset[1], 
											toAddPosX, toAddPosY, 
											mediaPanel.mediaInfo.id, null, null );
      if( isAdded )
	  {
	  	// get it out of edit mode. Now allows panning.
		mediaPanel.mControl.setAction( mediaPanel.mControl.ACTION_DEFAULT );
        mediaPanel.mActionInProgress = false;
	  }
	}
  }
}


/**
 * Centralized location for constructing luna's image url
 */
function Static_ImagePanel_GetLunaImageUrl( sourceUrl, x, y, w, h, level )
{
  var toReturn = '';
  if( sourceUrl )
  {
    // mr sid specific url dimensions
    var adjustedX = 0;
    var adjustedY = 0;

    var url = sourceUrl.toLowerCase();
    if( url.indexOf( '.sid' ) >= 0 )
    {
      adjustedX = ( x + ( w / 2 ) ) * ( 1 << level );
      adjustedY = ( y + ( h / 2 ) ) * ( 1 << level );
    }
    else if( url.indexOf( '.jp2' ) >= 0 )
    {
      adjustedX = ( x ) * ( 1 << level );
      adjustedY = ( y ) * ( 1 << level );
    }
    else
    {
      // unsupported sourceUrl format
      return '';
    }

    toReturn = sourceUrl + '&x=' + adjustedX + '&y=' + adjustedY + '&width=' + w + '&height=' + h + '&level=' + level;

    //debug( '[' + x + ', ' + y + ']\n' + toReturn );
  }

  return toReturn;
}

function Static_ImagePanel_CalculateRatio( baseProportion, level )
{
  var toReturn = $( new Array() );
  if( ( level >= 0 ) && ( baseProportion ) )
  {
    var scaler = Math.pow( 2, level );
    toReturn[0] = Math.floor( baseProportion[0] / scaler );
    toReturn[1] = Math.floor( baseProportion[1] / scaler );
  }

  return toReturn;
}

function Static_ImagePanel_GetExternalMediaBaseRatio( width, height )
{
  var toReturn = [];
  if( width && height )
  {
    toReturn = [ ( width * Math.pow( Static_ImagePanel_EXTERNAL_ADDITIONAL_LEVELS, Static_ImagePanel_EXTERNAL_MEDIA_STARTING_LEVEL) ), ( height * Math.pow( Static_ImagePanel_EXTERNAL_ADDITIONAL_LEVELS, Static_ImagePanel_EXTERNAL_MEDIA_STARTING_LEVEL) ) ];
  }

  return toReturn;
}

function Static_ImagePanel_TranslatePoint( point, currentLevel, newLevel, baseProportion )
{
  var toReturn = $( new Array() );

  if( point && baseProportion )
  {
    var currentProportions = Static_ImagePanel_CalculateRatio( baseProportion, currentLevel );
    var newProportions = Static_ImagePanel_CalculateRatio( baseProportion, newLevel );

    // translate x
    toReturn[0] = Math.round( newProportions[0] * ( point[0] / currentProportions[0] ) );

    // translate y
    toReturn[1] = Math.round( newProportions[1] * ( point[1] / currentProportions[1] ) );

    //debug( $(point) + '  becomes  ' + $(toReturn) );
    //debug( $(currentProportions) +  ' ; ' + $(newProportions), ' ; ratio : ', ( point[0] / currentProportions[0] ), ' ; newLevel: ', newLevel, ' ; currentLevel: ', currentLevel );
  }

  return toReturn;
}

function Static_ImagePanel_GlassPlate_Move( e )
{
  var e = jshGetEvent( e );
  var sourceElement = jshGetSourceElement( e );
  
  if( sourceElement.mediaPanel )
  {
    var mediaPanel = sourceElement.mediaPanel;
    mediaPanel.mLastIdleTime = null;
  }
}

function Static_ImagePanel_GlassPlate_MouseUp( e )
{
  var e = jshGetEvent( e );
  var sourceElement = jshGetSourceElement( e );

  if( sourceElement.mediaPanel )
  {
    var mediaPanel = sourceElement.mediaPanel;

    mediaPanel.mLastIdleTime = new Date();
    var con = mediaPanel.mControl;
    setTimeout( function(){ con.hideIfIdle() }, con.IDLE_TIME );
  }
}







