// lupe.js, (C) Walter Bislin, walter.bislins.ch, Januar 2008
//
// description and download:
//  http://walter.bislins.ch/projekte/javascript/lupe/
//
// dependecies:
//  include: <script src="x.js" type="text/javascript"></script>
//  include: <script src="ic.js" type="text/javascript"></script>
//
// History:
//  11.12.2008: EnableDblClick neu
//  15.08.2008: Relative und individuelle Positionierung des Zoombildes programmiert
//  29.01.2008: Problem mit IE6 behoben (Umlaute im Kommentaren können auf best. Servern Script korrumpieren!)
//  13.02.2007: Zoom-Source (small picture) no more limited to image type object
//              New properties AddPosX, AddPosY, ZIndex
//  15.02.2007: new function CLupeInit replaces CLupeHTML
//              CLupeInit can be placed anywhere on the page and can have a list of zoom pics to preload
//              CLupeInit sets up an onload-event (see xOnLoad) to create the html objects for the lupe
//              new function CLupePics replaces CLupePreload
//              CLupePics is usefull, if url's of zoom pics is not known yet on CLupeInit
//              CLupePics can be placed anywhere. it installs an onload-event (see xOnLoad) handler to preload the zoom pics
//  23.12.2007: new function CLupeDebug
//              Self diagnosis implemented

var CLupeObj = null;

// CLupeObj.State's
var CLHidden = 0;
var CLLoading = 1;
var CLZoomIn = 2;
var CLZoomed = 3;
var CLZoomOut = 4;

function CLupe()
{
  if (CLupeObj) return; // only one instance allowed!
  // public properties
  this.DebugOn = false;
  this.EnableInitOnClick = false;
  this.EnableDblClick = true;
  this.BorderColor = 'black';
  this.BorderWidth = 1;
  this.BaseZIndex = 1;
  this.ZIndex = 100;
  this.TimeSpan = 500;
  this.TimerInterval = 40; // 25 fps
  this.HideSmall = false; // to hide small image during zoom
  this.TimeModifyFunc = null;
  this.Enabled = true;
  this.VAlign = 'ToMiddle'; // Top, Middle, Bottom, ToMiddle, Relative
  this.HAlign = 'ToCenter'; // Left, Center, Right, ToCenter, Relative
  this.VMargin = 0;
  this.HMargin = 0;
  this.TargetOffsetX = 0;
  this.TargetOffsetY = 0;
  this.TargetElement = '';
  this.LoadText = 'Lade...';
  this.ErrorText = 'Zoom-Fehler!';
  this.ErrMsg = '';
  // private properties
  this.AddPosX = 1; // position correcture of smallPos
  this.AddPosY = 1;
  this.SmallPosX = 0;
  this.SmallPosY = 0;
  this.SmallWidth = 0;
  this.SmallHeight = 0;
  this.BigPosX = 0;
  this.BigPosY = 0;
  this.BigWidth = 0;
  this.BigHeight = 0;
  this.CurrVAlign = this.VAlign;
  this.CurrHAlign = this.HAlign;
  this.CurrTargetEle = null;
  this.CurrTargetOffX = 0;
  this.CurrTargetOffY = 0;
  this.StartTime = 0;
  this.Timer = null;
  this.BigImgID = -1;
  this.SmallImg = null;
  this.WaitObj = null;
  this.ErrObj = null;
  this.ZoomImg = null;
  this.State = CLHidden;
  this.InitExecuted = false;
  this.PreloadExecuted = false;
  this.InitForced = false,
  this.HtmlWritten = false;
  this.DebugDisplayed = false;
  CLupeObj = this;
}

// new init functions
function CLupeInit() { CLupeObj.Init(CLupeInit.arguments); }
function CLupePics() { CLupeObj.LoadPicsOnPageLoad(CLupePics.arguments); }
function CLupeDebug() { CLupeObj.DebugOn = true; }

// old init functions
function CLupeHTML() { CLupeObj.HTML(); }
function CLupePreload() { CLupeObj.Preload(CLupePreload.arguments); }
function CLupePreloadList(aUrlList) { CLupeObj.Preload(aUrlList); }

// lupe functions
function CLupeZoom(aImgName,aBigImgUrl,aXOffset,aYOffset,aRelEleID) { CLupeObj.Zoom(aImgName,aBigImgUrl,aXOffset,aYOffset,aRelEleID); }
function CLupeUnZoom() { CLupeObj.UnZoom(); }
function CLupeEnable() { CLupeObj.Enable(); }
function CLupeDisable() { CLupeObj.Disable(); }

// implementation
CLupe.prototype.Init = function(aImgUrlList) {
  function AfterPageLoad() {
    me.CreateHtmlObjects();
    if (aImgUrlList.length > 0) me.Preload(aImgUrlList);
    me.InitExecuted = true;
  }
  if (this.InitExecuted) return;
  var me = this;
  xOnLoad(AfterPageLoad);
}

CLupe.prototype.ForceInit = function() {
  if (this.InitExecuted) return;
  this.CreateHtmlObjects();
  this.InitExecuted = true;
  this.InitForced = true;
}

CLupe.prototype.LoadPicsOnPageLoad = function(aImgUrlList) {
  function AfterPageLoad() {
    if (aImgUrlList.length > 0) me.Preload(aImgUrlList);
  }
  var me = this;
  xOnLoad(AfterPageLoad);
}

CLupe.prototype.Preload = function(aImgUrlList) {
  IC.PreloadImages( aImgUrlList );
  this.PreloadExecuted = true;
}

CLupe.prototype.Diagnose = function() {
  var ics = IC.GetStatus();
  var s = '';
  if (this.ErrMsg != '') {
    s += 'Errors:\n';
    s += this.ErrMsg + '\n';
  }
  if (!this.PreloadExecuted) {
    s += 'Warning: no images preloaded!\nUse CLupeInit or CLupePics to preload the zoom images.'
    this.PreloadExecuted = true;
  }
  if (s == '') {
    if (ics != '') {
      s = 'CLupe Status: ok\nbut problems with some images detected.\ncheck url\'s in CLupeInit and CLupeZoom!';
    }
  } else {
    s = 'CLupe Status:\n\n' + s;
  }
  if (ics != '') {
    s += '\n\nIC Status (Image Caching and Preload):\n' + ics + '\n' + IC.ErrorMsg;
    IC.ResetStatus();
  }
  if (s != '' || !this.DebugDisplayed) {
    if (s == '') s = 'CLupe Status: all fine!';
    alert( s );
  }
  this.ErrMsg = '';
  this.DebugDisplayed = true;
}

CLupe.prototype.AddError = function( aMsg ) {
  this.ErrMsg += aMsg + '\n';
}

CLupe.prototype.CreateHtmlObjects = function()
{
  function OnClick() { me.UnZoom(); }
  function OnDblClick() { me.NewWindow(); }
  var me = this;
  var oImg = xCreateElement('img');
  if (!oImg || !oImg.style) {
    this.AddError( 'CLupe.CreateHtmlObjects: creating CLupe HTML failed (xCreateElement)' );
    return;
  }
  oImg.id = 'ZoomPic';
  oImg.style.position = 'absolute';
  oImg.style.visibility = 'hidden';
  oImg.style.zIndex = this.ZIndex;
  if (this.BorderWidth > 0) {
    oImg.style.border = this.BorderWidth+'px solid '+this.BorderColor;
  }
  oImg.onclick = OnClick;
  if (this.EnableDblClick) oImg.ondblclick = OnDblClick;
  var oDivWait = xCreateElement('div');
  var oDivError = xCreateElement('div');
  var oTextWait = xCreateTextNode(this.LoadText);
  var oTextError = xCreateTextNode(this.ErrorText);
  if (!oDivWait || !oDivError || !oTextWait || !oTextError) {
    this.AddError( 'CLupe.CreateHtmlObjects: creating CLupe HTML failed (xCreateTextNode)' );
    return;
  }
  xAppendChild(oDivWait,oTextWait);
  xAppendChild(oDivError,oTextError);
  oDivWait.id = 'ZoomPicWait';
  oDivWait.style.position = 'absolute';
  oDivWait.style.visibility = 'hidden';
  oDivWait.style.zIndex = this.BaseZIndex + 1;
  oDivWait.style.backgroundColor = 'white';
  oDivWait.style.color = 'black';
  oDivWait.style.padding = '0 4px';
  oDivWait.style.fontSize = '10pt';
  oDivWait.style.border = '1px solid black';
  //
  oDivError.id = 'ZoomPicError';
  oDivError.style.position = 'absolute';
  oDivError.style.visibility = 'hidden';
  oDivError.style.zIndex = this.BaseZIndex + 1;
  oDivError.style.backgroundColor = 'white';
  oDivError.style.color = 'black';
  oDivError.style.padding = '0 4px';
  oDivError.style.fontSize = '10pt';
  oDivError.style.border = '1px solid black';
  // Objekte als erste in body einfuegen
  var oElements = xGetElementsByTagName('body');
  if (!oElements || oElements.length < 1) {
    this.AddError( 'CLupe.CreateHtmlObjects: creating CLupe HTML failed (no body tag found)' );
    return;
  }
  var oBody = oElements[0];
  if (!xHasChildNodes(oBody)) {
    this.AddError( 'CLupe.CreateHtmlObjects: creating CLupe HTML failed (no html elements in body tag found)' );
    return;
  }
  oElements = xChildNodes(oBody);
  xInsertBefore(oBody,oDivError,oElements[0]);
  xInsertBefore(oBody,oDivWait,oDivError);
  xInsertBefore(oBody,oImg,oDivWait);
  this.WaitObj = oDivWait;
  this.ErrObj = oDivError;
  this.ZoomImg = oImg;
  this.HtmlWritten = true;
}

CLupe.prototype.HTML = function()
{
  if (this.HtmlWritten) return;
  var s = '<img id="ZoomPic" src="" style="cursor: url(http://www.hansebautechnik.de/hand.cur);position:absolute;visibility:hidden;z-index:'+this.ZIndex+';';
  if (this.BorderWidth > 0) s += 'border:'+this.BorderWidth+'px solid '+this.BorderColor+';';
  s += '" onclick="CLupeObj.UnZoom()" ondblclick="CLupeObj.NewWindow()">'
  document.writeln( s );
  document.writeln( '<div id="ZoomPicWait" '+
    'style="cursor: url(http://www.hansebautechnik.de/hand.cur);position:absolute;visibility:hidden;z-index:'+(this.BaseZIndex+1)+';background-color:white;color:black;padding:0 4px;font-size:10pt;border:1px solid black;">'+
    this.LoadText+'</div>' );
  document.writeln( '<div id="ZoomPicError" '+
    'style="cursor: url(http://www.hansebautechnik.de/hand.cur);position:absolute;visibility:hidden;z-index:'+(this.BaseZIndex+1)+';background-color:white;color:black;padding:0 4px;font-size:10pt;border:1px solid black;">'+
    this.ErrorText+'</div>' );
  this.HtmlWritten = true;
  this.InitExecuted = true;
}

CLupe.prototype.Zoom = function(aImgName, aBigImgUrl, aXOffset, aYOffset, aRelEleID )
{
  // fallback when xOnLoad failed:
  if (!this.InitExecuted) {
    if (this.EnableInitOnClick) {
      this.AddError( 'CLupeZoom: CLupe not initialized - forcing init now!\nCheck CLupeInit and ensure no onload is in body tag!' );
      this.ForceInit();
      if (!this.HtmlWritten) {
        this.AddError( 'CLupeZoom: forced Init failed, give up here.' );
        return;
      }
    } else {
      if (this.DebugOn) {
        this.AddError( 'CLupeZoom: CLupe not initialized!\nCheck CLupeInit and ensure no onload is in body tag\nor set CLupeObj.EnableInitOnClick = true;' );
        this.Diagnose();
      }
      return;
    }
  }
  if (this.DebugOn) this.Diagnose();
  if (!this.Enabled) return;
  if (!this.WaitObj) this.WaitObj = xGetElementById('ZoomPicWait');
  if (!this.ErrObj)  this.ErrObj  = xGetElementById('ZoomPicError');
  if (!this.ZoomImg) this.ZoomImg = xGetElementById('ZoomPic');
  var bigImgID = IC.FindImage(aBigImgUrl);
  
  // init current target position and alignment
  this.CurrVAlign = this.VAlign;
  this.CurrHAlign = this.HAlign;
  this.CurrTargetEle = null;
  this.CurrTargetOffX = this.TargetOffsetX;
  this.CurrTargetOffY = this.TargetOffsetY;
  if (this.TargetElement != '') {
    var relEle = xGetElementById(this.TargetElement);
    if (relEle) this.CurrTargetEle = relEle;
  }
  // overwrite global settings with local arguments
  if (xNum(aXOffset) || xNum(aYOffset) || xStr(aRelEleID)) {
    this.CurrVAlign = 'Relative';
    this.CurrHAlign = 'Relative';
    this.CurrTargetEle = null;
    this.CurrTargetOffX = 0;
    this.CurrTargetOffY = 0;
  }
  if (xNum(aXOffset)) this.CurrTargetOffX = aXOffset;
  if (xNum(aYOffset)) this.CurrTargetOffY = aYOffset;
  if (xStr(aRelEleID)) {
    var relEle = xGetElementById(aRelEleID);
    if (relEle) this.CurrTargetEle = relEle;
  }

  if ((this.State != CLHidden) && (bigImgID != -1) && (bigImgID == this.BigImgID)) {
    if (this.State == CLLoading) {
      xHide( this.WaitObj );
      xHide( this.ErrObj );
      this.State = CLHidden;
      return;
    }
    if (this.State == CLZoomIn || this.State == CLZoomed) {
      this.UnZoom();
      return;
    }
    // this.State == CLZoomOut
    if (this.Timer) {
      clearTimeout( this.Timer );
      this.Timer = null;
    }
    this.StartTime = xTimeMS() - this.TimeSpan + (xTimeMS() - this.StartTime);
    this.State = CLZoomIn;
    var me = this; // closure -> http://walter.bislins.ch/lexi/closure.html
    this.Timer = setTimeout(function(){me.Enlarge();}, this.TimerInterval);
    return;
  }

  if (this.State == CLLoading) {
    xHide( this.WaitObj );
    xHide( this.ErrObj );
    this.State = CLHidden;
  }
  else if (this.State != CLHidden) {
    if (this.Timer) {
      clearTimeout( this.Timer );
      this.Timer = null;
    }
    this.HideZoomImg();
  }
  this.SmallImg = xGetElementById(aImgName);
  if ((bigImgID != -1) && IC.IsLoaded(bigImgID)) {
    this.BigImgID = bigImgID;
    this.StartZoom();
  }
  else {
    this.GetSmallImgData();
    var y = (this.SmallHeight - xHeight(this.WaitObj) - 5);
    xMoveTo( this.WaitObj, this.SmallPosX+3, this.SmallPosY+y );
    xMoveTo( this.ErrObj, this.SmallPosX+3, this.SmallPosY+y );
    xShow( this.WaitObj );
    this.State = CLLoading;
    var me = this;
    this.BigImgID = IC.LoadImage( aBigImgUrl, function(aImgID){me.OnLoad(aImgID);} );
  }
}

CLupe.prototype.Enable = function() { this.Enabled = true; }
CLupe.prototype.Disable = function() { this.Enabled = false; }

CLupe.prototype.GetSmallImgData = function()
{
  this.SmallWidth = xWidth(this.SmallImg) + 2*this.BorderWidth;
  this.SmallHeight = xHeight(this.SmallImg) + 2*this.BorderWidth;
  this.SmallPosX = xPageX(this.SmallImg) + (xWidth(this.SmallImg)-this.SmallWidth)/2 + this.AddPosX; // Raender beruecksichtigen
  this.SmallPosY = xPageY(this.SmallImg) + (xHeight(this.SmallImg)-this.SmallHeight)/2 + this.AddPosY;
}

CLupe.prototype.OnLoad = function( aImgID )
{
  if ((this.State == CLLoading) && (this.BigImgID == aImgID)) {
    var imgState = IC.Image(aImgID).CacheState
    if (imgState == ICLoaded) {
      this.StartZoom();
    }
    else if (imgState == ICError || imgState == ICAbort) {
      this.State = CLHidden;
      xHide( this.WaitObj );
      xShow( this.ErrObj );
      var me = this; // closure -> http://walter.bislins.ch/lexi/closure.html
      setTimeout(function(){xHide(me.ErrObj);}, 2500);
    }
  }
}

CLupe.prototype.Range = function( aValue, aMin, aMax ) {
  return aMin + (aMax-aMin)*aValue;
}

CLupe.prototype.StartZoom = function()
{
  this.BigImg = IC.Image(this.BigImgID);
  this.ZoomImg.src = this.BigImg.src;
  this.GetSmallImgData();
  this.BigWidth = this.BigImg.width + 2*this.BorderWidth;
  this.BigHeight = this.BigImg.height + 2*this.BorderWidth;
  if ((this.SmallWidth >= this.BigWidth) || (this.SmallHeight >= this.BigHeight)) return;
  // assert: this.BigImg is greater than this.SmallImg

  var clW = xClientWidth();
  var clX = xScrollLeft();
  if (this.CurrHAlign == 'Left') {
    this.BigPosX = this.HMargin;
    // move big image into client range
    if ((this.BigPosX+this.BigWidth) > (clX+clW)) this.BigPosX = (clX+clW) - this.BigWidth;
    if ((this.BigPosX)               < (clX)    ) this.BigPosX =  clX;
  } else if (this.CurrHAlign == 'Right') {
    this.BigPosX = (clX+clW) - this.BigWidth - this.HMargin;
    // move big image into client range
    if ((this.BigPosX) < (clX)) this.BigPosX =  clX;
  } else if (this.CurrHAlign == 'Relative') {
    var ref = this.SmallImg;
    if (this.CurrTargetEle) ref = this.CurrTargetEle;
    this.BigPosX = xPageX(ref) + this.CurrTargetOffX;
  } else {
    // compute big position: move big image according to its size rel. to the windows range
    // to the center of the windows range (client range). As smaller the image, as farther away
    // from the center.
    var dxCenter = 1;
    if (this.BigWidth <= clW) {
      // assert: this.BigImg width full inside client range
      dxCenter = (this.BigWidth-this.SmallWidth)/(clW-this.SmallWidth);
      if (dxCenter < 0) dxCenter = 0;
    }
    if (this.CurrHAlign == 'Center') dxCenter = 1;
    var cxBig = clW / 2;
    var cxSmall = this.SmallPosX-clX + (this.SmallWidth/2);
    var cx = dxCenter*(cxBig-cxSmall)+cxSmall;
    this.BigPosX = clX + cx - this.BigWidth/2;
    if (this.BigPosX < 0) this.BigPosX = 0;
    if (this.BigWidth <= clW) {
      // move big image into client range
      if ((this.BigPosX+this.BigWidth) > (clX+clW)) this.BigPosX = (clX+clW) - this.BigWidth;
      if ((this.BigPosX)               < (clX)    ) this.BigPosX =  clX;
    }
  }
  var clH = xClientHeight();
  var clY = xScrollTop();
  if (this.CurrVAlign == 'Top') {
    this.BigPosY = this.VMargin;
    // move big image into client range
    if ((this.BigPosY+this.BigHeight) > (clY+clH)) this.BigPosY = (clY+clH) - this.BigHeight;
    if ((this.BigPosY)                < (clY)    ) this.BigPosY =  clY;
  } else if (this.CurrVAlign == 'Bottom') {
    this.BigPosY = (clY+clH) - this.BigHeight - this.VMargin;
    // move big image into client range
    if ((this.BigPosY) < (clY)) this.BigPosY =  clY;
  } else if (this.CurrVAlign == 'Relative') {
    var ref = this.SmallImg;
    if (this.CurrTargetEle) ref = this.CurrTargetEle;
    this.BigPosY = xPageY(ref) + this.CurrTargetOffY;
  } else {
    var dyCenter = 1;
    if (this.BigHeight <= clH) {
      // assert: this.BigImg height full inside client range
      dyCenter = (this.BigHeight-this.SmallHeight)/(clH-this.SmallHeight);
      if (dyCenter < 0) dyCenter = 0;
    }
    if (this.CurrVAlign == 'Middle') dyCenter = 1;
    var cyBig = clH / 2;
    var cySmall = this.SmallPosY-clY + (this.SmallHeight/2);
    var cy = dyCenter*(cyBig-cySmall)+cySmall;
    this.BigPosY = clY + cy - this.BigHeight/2;
    if (this.BigPosY < 0) this.BigPosY = 0;
    if (this.BigHeight <= clH) {
      // move big image into client range
      if ((this.BigPosY+this.BigHeight) > (clY+clH)) this.BigPosY = (clY+clH) - this.BigHeight;
      if ((this.BigPosY)                < (clY)    ) this.BigPosY =  clY;
    }
  }
  this.StartTime = xTimeMS();
  var me = this; // closure -> http://walter.bislins.ch/lexi/closure.html
  this.Timer = setTimeout(function(){me.Enlarge();}, this.TimerInterval);
}

CLupe.prototype.Enlarge = function()
{
  if (this.Timer) {
    clearTimeout(this.Timer);
    this.Timer = null;
  }
  var param = (xTimeMS() - this.StartTime) / this.TimeSpan;
  var eom = param >= 1;
  if (param > 1) param = 1;
  if (this.TimeModifyFunc) param = this.TimeModifyFunc(param);
  if (param < 0) param = 0;
  if (param > 1) param = 1;
  var x = this.Range( param, this.SmallPosX, this.BigPosX );
  var y = this.Range( param, this.SmallPosY, this.BigPosY );
  var w = this.Range( param, this.SmallWidth, this.BigWidth );
  var h = this.Range( param, this.SmallHeight, this.BigHeight );
  xMoveTo( this.ZoomImg, x, y );
  xResizeTo( this.ZoomImg, w, h );
  if (this.State != CLZoomIn) {
    xHide( this.WaitObj );
    xHide( this.ErrObj );
    xShow( this.ZoomImg );
    if (this.HideSmall) xHide( this.SmallImg );
    this.State = CLZoomIn;
  }
  var me = this;
  if (eom)
  {
    this.State = CLZoomed;
    this.Timer = setTimeout(function(){me.CheckOutOfWindow();}, 200);
  }
  else
  {
    this.Timer = setTimeout(function(){me.Enlarge();}, this.TimerInterval);
  }
}

CLupe.prototype.CheckOutOfWindow = function()
{
  var space = (xClientHeight()-this.BigHeight) / 2;
  var newY = xScrollTop() + space;
  var toleranz;
  if (space > 0) {
    toleranz = space + (this.BigHeight * 2 / 3);
  } else {
    toleranz = -space + (xClientHeight() * 2 / 3);
  }
  if (Math.abs(newY-this.BigPosY) > toleranz) {
    this.UnZoom();
    return;
  }
  var me = this;
  this.Timer = setTimeout(function(){me.CheckOutOfWindow();}, 200);
}

CLupe.prototype.NewWindow = function()
{
  if (!this.HtmlWritten) return;
  if (this.HideSmall) xShow( this.SmallImg );
  xHide( this.ZoomImg );
  xMoveTo( this.ZoomImg, 0, 0 );
  xResizeTo( this.ZoomImg, 0, 0 );
  this.State = CLHidden;
  location.href = IC.ImageUrl(this.BigImgID);
}

CLupe.prototype.UnZoom = function()
{
  if (!this.HtmlWritten) return;
  if (this.State == CLHidden || this.State == CLZoomOut) return;
  if (this.State == CLLoading) {
    xHide( this.WaitObj );
    xHide( this.ErrObj );
    this.State = CLHidden;
    return;
  }
  if (this.Timer) {
    clearTimeout(this.Timer);
    this.Timer = null;
  }
  this.SmallPosX = xPageX(this.SmallImg) + (xWidth(this.SmallImg)-this.SmallWidth)/2 + this.AddPosX; // Raender beruecksichtigen
  this.SmallPosY = xPageY(this.SmallImg) + (xHeight(this.SmallImg)-this.SmallHeight)/2 + this.AddPosY;
  if (this.State == CLZoomIn) {
    this.StartTime = xTimeMS() - this.TimeSpan + (xTimeMS() - this.StartTime);
  } else {
    this.StartTime = xTimeMS();
  }
  this.State = CLZoomOut;
  var me = this; // closure -> http://walter.bislins.ch/lexi/closure.html
  this.Timer = setTimeout(function(){me.Shrink();}, this.TimerInterval);
}

CLupe.prototype.HideZoomImg = function()
{
  if (this.HideSmall) xShow( this.SmallImg );
  xHide( this.ZoomImg );
  xMoveTo( this.ZoomImg, 0, 0 );
  xResizeTo( this.ZoomImg, 0, 0 );
  this.State = CLHidden;
}

CLupe.prototype.Shrink = function()
{
  if (this.Timer) {
    clearTimeout(this.Timer);
    this.Timer = null;
  }
  var param = (xTimeMS() - this.StartTime) / this.TimeSpan;
  var eom = param >= 1;
  if (param > 1) param = 1;
  if (this.TimeModifyFunc) param = this.TimeModifyFunc(param);
  if (param < 0) param = 0;
  if (param > 1) param = 1;
  var x = this.Range( param, this.BigPosX, this.SmallPosX );
  var y = this.Range( param, this.BigPosY, this.SmallPosY );
  var w = this.Range( param, this.BigWidth, this.SmallWidth );
  var h = this.Range( param, this.BigHeight, this.SmallHeight );
  xMoveTo( this.ZoomImg, x, y );
  xResizeTo( this.ZoomImg, w, h );
  if (eom)
  {
    this.HideZoomImg();
  }
  else
  {
    xMoveTo( this.ZoomImg, x, y );
    xResizeTo( this.ZoomImg, w, h );
    var me = this; // closure -> http://walter.bislins.ch/lexi/closure.html
    this.Timer = setTimeout(function(){me.Shrink();}, this.TimerInterval);
  }
}

CLupeObj = new CLupe();

CLupeObj.TimeModifyFunc = function( aValue ) { return (0.5 - 0.5 * Math.cos(Math.PI*aValue)); }

