/* PLATFORM class
   Copyright (c) 1999-2001 Technomancer Software
   www.technomancer.ws
   Identifies platform script is running on, as well
   as encapsulating common platform-specific tasks
*/
function Platform() {
  var agent    = navigator.userAgent.toLowerCase()
  this.version = parseFloat(navigator.appVersion)
  this.major   = Math.floor(this.version)
  this._isNS   =  ((agent.indexOf('mozilla')!=-1)
               && ((agent.indexOf('spoofer')==-1)
               &&  (agent.indexOf('compatible') == -1)))
  this.isNS    = Pl_isNS
  this.isNS2   = (this._isNS  && (this.major == 2))
  this.isNS3   = (this._isNS  && (this.major == 3))
  this.isNS4   = this.isNS(4)
  this.isNS4b  = (this.isNS4 && (this.version < 4.04))
  this._isIE   = (agent.indexOf("msie") != -1)
  this.isIE    = Pl_isIE
  this.isIE3   = (this._isIE && (this.major == 2))
  this.isIE4   = this.isIE(4)
  if(this._isIE) {
    var ver = agent.replace(/^.*msie/,'')
    this.version = parseFloat(ver)
    this.major = Math.floor(this.version)
  }
  this._isOpera = (agent.indexOf("opera") != -1)
  this.isOpera = Pl_isOpera
  this.isWin   = (agent.indexOf("win")!=-1)
  this.isMac   = (agent.indexOf("mac")!=-1)
  this.isUnix  = (agent.indexOf("x11")!=-1)
  this.screenWidth  = Pl_ScreenWidth
  this.screenHeight = Pl_ScreenHeight
  this.getObject    = Pl_GetObject
  this.absOffset    = Pl_AbsOffset
}
function Pl_isNS(ver)
{
  if(!ver) ver=999999
  return(this._isNS && this.version >= ver)
}
function Pl_isIE(ver)
{
  if(!ver) ver=999999
  return(this._isIE && this.version >= ver)
}
function Pl_isOpera(ver)
{
  if(!ver) ver=999999
  return(this._isOpera && this.version >= ver)
}
function Pl_ScreenWidth()
{
  if(this.isIE4)
    return document.body.clientWidth
  else if(this.isNS4)
    return innerWidth
}
function Pl_ScreenHeight()
{
  if(this.isIE4)
    return document.body.clientHeight
  else if(this.isNS4)
    return innerHeight
}
function Pl_GetObject(sID)
{
  if(this.isNS4)
    return eval("document['"+sID+"']")
  else if(this.isIE4)
    return eval(sID)
}
function Pl_AbsOffset(o)
{
  if(this.isIE(4)) {
    var oParent = o.offsetParent
    var a = (oParent != null)? this.absOffset(oParent) : [0,0]
    a[0] += o.offsetLeft
    a[1] += o.offsetTop
    return a
  }
  else if(this.isNS(4)) {
    var a = [o.left,o.top]
    return a
  }
}
var gPlatform = new Platform()
var gUndefined
/* RECTANGLE functions (lightweight)
   Copyright (c) 1999-2001 Technomancer Software
   www.technomancer.ws
   Generic rectangle functions
   Note rect arg order:  x1,y1 - x2,y2 which becomes l,t - r,b
   This differs from CSS order, which is t,r,b,l ("TRouBLe")
   These functions hide that convention
*/
function newRect(l,t,r,b) { return new Array(t,r,b,l) }
function rectLeft(aRect)  { return aRect[3] }
function rectTop(aRect)   { return aRect[0] }
function rectRight(aRect) { return aRect[1] }
function rectBottom(aRect){ return aRect[2] }
function setRectLeft(aRect,l) { aRect[3] = l;return(aRect)}
function setRectTop(aRect,t)  { aRect[0] = t;return(aRect)}
function setRectRight(aRect,r){ aRect[1] = r;return(aRect)}
function setRectBottom(aRect,b){aRect[2] = b;return(aRect)}
function Num(x) { return parseInt(x.toString()) }
/* SPRITE class
   Copyright (c) 1999-2001 Technomancer Software
   www.technomancer.ws
   Encapsulates "sprite" capabilities (moving DIVs
   or layers) to facilitate animation.
*/
function Sprite(sDiv,x,y,sParms)
{
  this.width       = sp_width
  this.height      = sp_height
  this.relRect     = sp_relRect
  this.absRect     = sp_absRect
  this.moveTo      = sp_moveTo
  this.moveBy      = sp_moveBy
  this.setSize     = sp_setSize
  this.clipRect    = sp_clipRect
  this.setClipRect = sp_setClipRect
  this.changeClipRect = sp_changeClipRect
  this.show        = sp_show
  this.hide        = sp_hide
  this.absLeft     = sp_absLeft
  this.absTop      = sp_absTop
  this.absRight    = sp_absRight
  this.absBottom   = sp_absBottom
  this.setContents = sp_setContents
  this.zIndex      = sp_zIndex
  this.setZIndex   = sp_setZIndex
  this.sDiv   = sDiv
  this.oDiv   = gPlatform.getObject(sDiv)
  if(gPlatform.isNS4) {
    this.oProps = this.oDiv
    this.orgWidth  = this.oDiv.clip.width
    this.orgHeight = this.oDiv.clip.height
  }
  else if(gPlatform.isIE4) {
    this.oProps  = this.oDiv.style
    this.orgWidth  = this.oDiv.offsetWidth
    this.orgHeight = this.oDiv.offsetHeight
  }
  this.curWidth  = this.orgWidth
  this.curHeight = this.orgHeight
  if(x != gUndefined && y != gUndefined)
    this.moveTo(x,y)
  this.setClipRect(this.relRect())
  if(!sParms || !sParms.match(/show/i))
    this.hide()
  return this
}
function sp_width()  { return this.curWidth  }
function sp_height() { return this.curHeight }
function sp_absLeft()  { return gPlatform.absOffset(this.oDiv)[0]}
function sp_absTop()   { return gPlatform.absOffset(this.oDiv)[1]}
function sp_absRight() { return this.absLeft()+this.width()}
function sp_absBottom(){ return this.absTop()+this.height()}
function sp_show()     { this.oProps.display = "block"}
function sp_hide()     { this.oProps.display = "none" }
function sp_relRect()
{
  return(newRect(0,0,this.width(),this.height()))
}
function sp_absRect()
{
  var aXY = gPlatform.absOffset(this.oDiv)
  return newRect(aXY[0],aXY[1],
                 aXY[0]+this.width(),aXY[1]+this.height())
}
function sp_moveTo(x,y)
{
  this.oProps.left = x
  this.oProps.top = y
}
function sp_setSize(x,y,bUpdateClip)
{
  if(x<0)x=0;if(y<0)y=0
  this.oProps.width  = this.curWidth  = x
  this.oProps.height = this.curHeight = y
  if(bUpdateClip) {
    var a = this.clipRect()
    a = setRectRight(a,x)
    a = setRectBottom(a,y)
    this.setClipRect(a)
  }
}
function sp_moveBy(x,y)
{
  this.oProps.left = Num(this.oProps.left)+x
  this.oProps.top =  Num(this.oProps.top)+y
}
function sp_clipRect()
{
  if(gPlatform.isIE4) {
    var aRect = this.oProps.clip.replace(/rect\(|\)|px/ig,'').split(' ')
    aRect[0]*=1;aRect[1]*=1;aRect[2]*=1;aRect[3]*=1
    return(aRect)
  }
  else if(gPlatform.isNS4) {
    var oRect = this.oProps.clip;
    return(newRect(oRect.left,oRect.top,oRect.right,oRect.bottom))
  }
}
function sp_setClipRect(aRect)
{
  var s
  if(gPlatform.isIE4)
    this.oProps.clip = "rect("+aRect+")"
  else if(gPlatform.isNS4) {
    this.oProps.clip.left = rectLeft(aRect)
    this.oProps.clip.top  = rectTop(aRect)
    this.oProps.clip.right= rectRight(aRect)
    this.oProps.clip.bottom = rectBottom(aRect)
  }
}
function sp_changeClipRect(l,t,r,b)
{
  var aRect = this.clipRect();
  aRect[0]+=t;aRect[1]+=r;aRect[2]+=b;aRect[3]+=l
  this.setClipRect(aRect)
}
function sp_setContents(s)
{
  if(gPlatform.isNS4) {
    this.oDiv.document.open()
    this.oDiv.document.write(s)
    this.oDiv.document.close()
  }
  else
    this.oDiv.innerHTML = s
}
function sp_zIndex()
{
  return this.oProps.zIndex;
}
function sp_setZIndex(z)
{
  if(z<0) z=0
  this.oProps.zIndex = z
}
/* SCRIPT ANIMATION control
   copyright 1999-2001 Technomancer Software
   www.technomancer.ws
   Choreograph actions, and have this control
   play back those actions
*/
var giNextFree = null
var giTimer = 0
var gaTimers = []
function nextFreeTimer()
{
  if(giNextFree == null)
    return(gaTimers.length)
  else {
    var iRet = giNextFree
    giNextFree = gaTimers[giNextFree]
    return iRet
  }
}
function freeTimer(i)
{
  gaTimers[i] = giNextFree
  giNextFree = i
}
function setScriptTimer(oScript)
{
  var i = nextFreeTimer()
  var iID = setTimeout("ScriptTimer("+i+")",oScript.speed())
  gaTimers[i] = new Array(oScript,iID)
  return i
}
function clearScriptTimer(oScript)
{
  var oEntry = gaTimers[oScript.iTimeID]
  if(oEntry) {
    clearTimeout(oEntry[1])
    freeTimer(oEntry[1])
  }
}
function ScriptTimer(i)
{
  var oEntry = gaTimers[i]
  if(oEntry[0].callback())
    setTimeout("ScriptTimer("+i+")",oEntry[0].speed())
  else
    freeTimer(i)
}
function Animator(aScript,fEvent)
{
  this.aScript   = aScript
  this.fEvent    = fEvent
  this.iTimeID   = null
  this.iScene    = 0
  this.bStopped  = true
  this.play      = An_play
  this.init      = An_init
  this.end       = An_end
  this.stop      = An_stop
  this.pause     = An_pause
  this.resume    = An_resume
  this.callback  = An_callback
  this.event     = An_event
  this.nextscene = An_nextscene
  this.speed     = An_speed
  return this
}
function An_play(iScene)
{
  if(iScene >= 0 && iScene < this.aScript.length)
    this.iScene = iScene
  this.event("play")
  var aScene = this.aScript[this.iScene]
  if(this.init()) {
    this.iTimeID = setScriptTimer(this,aScene[1])
    this.bStopped = false
  }
}
function An_stop()
{
  if(!this.bStopped) {
    this.event("stop")
    this.bStopped = true
  }
}
function An_pause()
{
  if(!this.bStopped) {
    this.event("pause")
    this.bStopped = true
  }
}
function An_resume()
{
  if(this.bStopped) {
    this.event("resume")
    var aScene = this.aScript[this.iScene]
    this.iTimeID = setScriptTimer(this,this.speed())
    this.bStopped = false
  }
}
function An_callback()
{
  if(this.bStopped) return false
  var aScene = this.aScript[this.iScene]
  if(!aScene[0]())
    if(!this.nextscene()) {
      this.event("end")
      return false
    }
  return true
}
function An_event(s)
{
  if(this.fEvent)
    return this.fEvent(s)
}
function An_speed()
{
  return this.aScript[this.iScene][1];
}
function An_init()
{
  var aScene = this.aScript[this.iScene]
  if(aScene[2])
    return aScene[2]()
  else
    return true
}
function An_end(aScene)
{
  var aScene = this.aScript[this.iScene]
  if(aScene[3])
    return aScene[3]()
  else
    return true
}
function An_nextscene()
{
  if(!this.end())
    return false
  if(++this.iScene < this.aScript.length)
    return this.init()
  else
    return false
}
