// -- Extract from File: browser.js ----------------------

function BrowserIsInternetExplorer() 
{ 
var UA = navigator.userAgent.toLowerCase(); 
return ((UA.indexOf('msie') != -1) && (UA.indexOf('spoofer') == -1) && (UA.indexOf('opera') == -1)); 
}

function BrowserIsElseAlert(Browser)
{
var Supported;
switch (Browser)
  {
  case 'IE': Supported = BrowserIsInternetExplorer(); break;
  case 'NS': Supported = BrowserIsNetscape(); break;
  case 'OP': Supported = BrowserIsOpera(); break;
  default:   Supported = false;
  }
if (!Supported) alert('Sorry, this feature is not supported by your browser.      ');
return(Supported);
}

function InternetExplorerVersion()
{
var UA          = navigator.userAgent.toLowerCase(); 
var IndexOfMSIE = UA.indexOf('msie');
var Version     = 0;
if (BrowserIsInternetExplorer() == true)
  Version = parseFloat(UA.substring(IndexOfMSIE+5));
return(Version);
}


// -- Extract from File: url.js ---------------------------

function GoToURL(url, anchorName, queryString, forgetCurrentPage)
{
if (anchorName  != '') url += '#' + anchorName;
if (queryString != '') url += '?' + queryString;
if (forgetCurrentPage)
       location.replace(url);
  else location.href = url;
}


// -- Extract from File: dom.js ---------------------------

function GetDOM()
{
var DOM = 0;
var browserVersion;
if (document.getElementById)
       DOM = 1;
  else {
       if ((document.all))
              DOM = 2;
         else {
              if (document.layers)
                     DOM = 3;
                else {
                     browserVersion = parseInt(navigator.appVersion);
                     if ((navigator.appName.indexOf('Netscape') != -1) && (browserVersion == 4)) DOM = 3;
                     }
              }
       }
return(DOM);
}

function GetNN4Layer(doc, lyrName)
{
var lyr = null;
var i   = 0;
for (i = 0; i < doc.layers.length; i++)
  {
  if (doc.layers[i].name == lyrName)
    {
    lyr = doc.layers[i];
    break;
    }
  if (doc.layers[i].document.layers.length > 0) lyr = GetNN4Layer(document.layers[i].document, lyrName);
  }
return(lyr);
}

function GetDomElement(DOMType, elemName)
{
var elem = null;
switch (DOMType)
  {
  case 1: elem = document.getElementById(elemName); break;
  case 2: elem = document.all(elemName); break;
  case 3: elem = GetNN4Layer(elemName); break;
  }
return(elem);
}


// -- Extract from File: dhtml.js ------------------------------

var vCSS    = false;
var vDOM    = 0;
var vIE6CSS = false;

function InitialiseDHTML()
{
var DOM = GetDOM();
if (document.images)
  {
  if ((document.body) && (document.body.style)) {vCSS = true;}
  if ((vCSS) && (DOM == 1)) {vDOM = 1;}
  if ((vCSS) && (DOM == 2)) {vDOM = 2;}
  if (DOM == 3) {vDOM = 3;}
  if ((document.compatMode) && (document.compatMode.indexOf('CSS1') >= 0)) {vIE6CSS = true;}
  }
}

function GetDHTMLElement(elemSpec)
{
var elem = null;
if (typeof elemSpec == 'string')
       elem = GetDomElement(vDOM, elemSpec);
  else elem = elemSpec;
return(elem);
}

function GetDHTMLElementStyle(elemSpec)
{
var E  = null;
var ES = null;
if (typeof(elemSpec) == 'string')
       E = GetDomElement(vDOM, elemSpec);
  else E = elemSpec;                      
if ((E) && ((vDOM == 1) || (vDOM == 2)))
       ES = E.style;
  else ES = E;
return(ES);
}

function GetDHTMLElementWidth(elemSpec)
{
var W = 0;
var E = GetDHTMLElement(elemSpec);
if (E)
  {
  if (E.offsetWidth)
         W = E.offsetWidth;
    else {
         if ((E.clip) && (E.clip.width))
                W = E.clip.width;
           else {
                if ((E.style) && (E.style.pixelWidth))
                       W = E.style.pixelWidth;
                  else W = 0;
                }
         }
  }
return parseInt(W);
}

function GetDHTMLElementHeight(elemSpec)
{
var H = 0;
var E = GetDHTMLElement(elemSpec);
if (E)
  {
  if (E.offsetHeight)
         H = E.offsetHeight;
    else {
         if ((E.clip) && (E.clip.height))
                H = E.clip.height;
           else {
                if ((E.style) && (E.style.pixelHeight))
                       H = E.style.pixelHeight;
                  else H = 0;
                }
         }
  }
return parseInt(H);
}

function GetLeftXOfPageVisibleInWindow()
{
var XS = 0;
if ((document.body) && (typeof document.body.scrollLeft != 'undefined'))
       XS = document.body.scrollLeft;
  else if (typeof window.pageXOffset != 'undefined') XS = window.pageXOffset;
return(XS);
}

function GetTopYOfPageVisibleInWindow()
{
var YS = 0;
if ((document.body) && (typeof document.body.scrollTop != 'undefined'))
       YS = document.body.scrollTop;
  else if (typeof window.pageYOffset != 'undefined') YS = window.pageYOffset;
return(YS);
}

function MoveDHTMLElementTo(elemSpec, x, y)
{
var ES = GetDHTMLElementStyle(elemSpec);
if (ES)
  {
  if (vCSS)
         {
         if (typeof ES.left == 'string')
                {
                ES.left = x + 'px';
                ES.top  = y + 'px';
                }
           else {
                ES.left = x;
                ES.top  = y;
                }
         }
    else if (vDOM == 3) ES.moveTo(x,y);
  }
}

function SetDHTMLElementZIndex(elemSpec, z)
{
var ES = GetDHTMLElementStyle(elemSpec);
if (ES) ES.zIndex = z;
}

function SetDHTMLElementBkgndColour(elemSpec, RGB)
{
var ES = GetDHTMLElementStyle(elemSpec);
if (ES)
  {
  switch (vDOM)
    {
    case 1: ES.backgroundColor = RGB; break;
    case 2: ES.backgroundColor = RGB; break;
    case 3: ES.bgColor = RGB;         break;
    }
  }
}

function SetDHTMLElementVisibility(elemSpec, visible)
{
var ES = GetDHTMLElementStyle(elemSpec);
if (ES)
  {
  if (visible)
         ES.visibility = 'visible';
    else ES.visibility = 'hidden';
  }
}

function GetInnerWindowWidth(win)
{
var W = 0;
if (window.innerWidth)
       W = win.innerWidth;
  else {
       if (vIE6CSS)
              W = win.document.body.parentElement.clientWidth;
         else {
              if ((win.document.body) && (win.document.body.clientWidth))
                     W = win.document.body.clientWidth;
                else W = 0;
              }
       }
return(W);
}

function GetInnerWindowHeight(win)
{
var H = 0;
if (window.innerHeight)
       H = win.innerHeight;
  else {
       if (vIE6CSS)
              H = win.document.body.parentElement.clientHeight;
         else {
              if ((win.document.body) && (win.document.body.clientHeight))
                     H = win.document.body.clientHeight;
                else H = 0;
              }
       }
return(H);
}

function LeftXOfInlineElem(elemSpec)
{
var E = GetDHTMLElement(elemSpec);
var X = 0;
while (E)
  {
  X += E.offsetLeft;
  E = E.offsetParent;
  }
if ((navigator.userAgent.indexOf('Mac') != -1) && 
    (typeof document.body.leftMargin != 'undefined'))
    X += document.body.leftMargin;
return(X);
}

function TopYOfInlineElem(elemSpec)
{
var E = GetDHTMLElement(elemSpec);
var Y = 0;
while (E)
  {
  Y += E.offsetTop;
  E = E.offsetParent;
  }
if ((navigator.userAgent.indexOf('Mac') != -1) && 
    (typeof document.body.leftMargin != 'undefined'))
    Y += document.body.topMargin;
return(Y);
}


// -- Extract from File: locate.js -----------------------------------

function AdjustElemLeftXToFitInWindow(elemLeftX, elemWidth, margin)
{
var winW  = GetInnerWindowWidth(window);
var docX  = GetLeftXOfPageVisibleInWindow();
if ((elemLeftX + elemWidth) > (docX + winW - margin)) elemLeftX = docX + winW - margin - elemWidth; 
if (elemLeftX < docX + margin) elemLeftX = docX + margin;
return(elemLeftX);
}

function AdjustElemTopYToFitInWindow(elemTopY, elemHeight, margin)
{
var winH  = GetInnerWindowHeight(window);
var docY  = GetTopYOfPageVisibleInWindow();
if ((elemTopY + elemHeight) > (docY + winH - margin)) elemTopY = docY + winH - margin - elemHeight; 
if (elemTopY < docY + margin) elemTopY = docY + margin;
return(elemTopY);
}

function DefineElemLeftXToCentreInWindow(elemWidth)
{
var winW      = GetInnerWindowWidth(window);
var docX      = GetLeftXOfPageVisibleInWindow();
var elemLeftX = Math.round(docX + ((winW - elemWidth) / 2));
return(elemLeftX);
}

function DefineElemTopYToCentreInWindow(elemHeight)
{
var winH      = GetInnerWindowHeight(window);
var docY      = GetTopYOfPageVisibleInWindow();
var elemTopY  = Math.round(docY + ((winH - elemHeight) / 2));
return(elemTopY);
}

function FitInWindow(elemSpec, idealLeftX, idealTopY, marginX, marginY)
{
var E  = GetDHTMLElement(elemSpec);
if (E)
  {
  var W = GetDHTMLElementWidth(E);
  var H = GetDHTMLElementHeight(E);
  var X = AdjustElemLeftXToFitInWindow(idealLeftX, W, marginX);
  var Y = AdjustElemTopYToFitInWindow(idealTopY, H, marginY);
  MoveDHTMLElementTo(E, X, Y);
  }
}

function FitXInWindow(elemSpec, idealLeftX, Y, marginX)
{
var E  = GetDHTMLElement(elemSpec);
if (E)
  {
  var W = GetDHTMLElementWidth(E);
  var X = AdjustElemLeftXToFitInWindow(idealLeftX, W, marginX);
  MoveDHTMLElementTo(E, X, Y);
  }
}

function FitYInWindow(elemSpec, X, idealTopY, marginY)
{
var E  = GetDHTMLElement(elemSpec);
if (E)
  {
  var H = GetDHTMLElementHeight(E);
  var Y = AdjustElemTopYToFitInWindow(idealTopY, H, marginY);
  MoveDHTMLElementTo(E, X, Y);
  }
}

function CentreInWindow(elemSpec)
{
var E  = GetDHTMLElement(elemSpec);
if (E)
  {
  var W = GetDHTMLElementWidth(E);
  var H = GetDHTMLElementHeight(E);
  var X = DefineElemLeftXToCentreInWindow(W);
  var Y = DefineElemTopYToCentreInWindow(H);
  MoveDHTMLElementTo(E, X, Y);
  }
}


// -- Extract from File: events.js -----------------------------

function GetMouseCoordsRelToPage(e)
{
var coords = {X:0, Y:0};
if (e.pageX)
       {
       coords.X = e.pageX;  
       coords.Y = e.pageY;
       }
  else {
       if (e.clientX)
         {
         coords.X = e.clientX + document.body.scrollLeft - document.body.clientLeft;
         coords.Y = e.clientY + document.body.scrollTop - document.body.clientTop;
         if ((document.body.parentElement) && (document.body.parentElement.clientLeft))
           {
           P = document.body.parentElement;
           coords.X = coords.X + P.scrollLeft - P.clientLeft;
           coords.Y = coords.Y + P.scrollTop - P.clientTop;
           }
         }
       }
return(coords);
}


// -- Extract from File: popup.js -----------------------------------

function ShowPopup(popupElemId, locationCode, X, Y, evt)
{
var E = GetDHTMLElement(popupElemId);
if (E)
  {
  switch (locationCode)
    {
    case 0: FitInWindow(E, X, Y, 2, 2);
            break;
    case 1: var mouse = GetMouseCoordsRelToPage(evt); 
            FitInWindow(E, mouse.X+X, mouse.Y+Y, 2, 2);
            break;
    case 2: CentreInWindow(E);
            break;
    case 3: FitXInWindow(E, X, Y, 2);
            break;
    case 4: FitYInWindow(E, X, Y, 2);
            break;
    case 5: var mouse = GetMouseCoordsRelToPage(evt); 
            FitXInWindow(E, mouse.X+X, Y, 2);
            break;
    case 6: var mouse = GetMouseCoordsRelToPage(evt); 
            FitYInWindow(E, X, mouse.Y+Y, 2);
            break;
    }
  SetDHTMLElementZIndex(E, 99);
  SetDHTMLElementVisibility(E, true);
  }
}

function HidePopup(popupElemId, evt)
{
SetDHTMLElementVisibility(popupElemId, false);
}


// -- Extract from File: forms.js --------------------------------

function FocusField(E, doSelect)
{
switch (E.type)
  {
  case 'text': 
  case 'textarea': E.focus(); if (doSelect) E.select(); break;
  case 'radio':
  case 'select-one':
  case 'select-multiple':
  case 'checkbox':
  case 'file':
  case 'password':
  case 'button':
  case 'reset':
  case 'submit': E.focus(); break;
  }
}

function FocusFormElement(formName, fieldName, doSelect)
{
var E = document.forms[formName].elements[fieldName];
FocusField(E, doSelect);
}

function IndicateIfNotValid(field, valid, alertTitle, alertMsg, autoFocus)
{
if (!valid)
  {
  if (alertTitle.length > 0) alert(alertTitle + '     \n\n' + alertMsg);
  if (autoFocus) FocusField(field, true);
  }
}

function fieldIsInteger(field, minValue, maxValue, alertTitle, autoFocus)
{
var valid      = true;
var alertMsg   = '';
var digitFound = false;
var v          = 0;
var c          = 0;
var s          = field.value;
s = s.toString();
if (s != null)
  {
  for (var i=0; i < s.length; i++)
    {
    if (valid)
      { 
      c = s.charCodeAt(i);
      if ((c >= 48) && (c <= 57))
             digitFound = true;
        else {
             if (c == 45)
                    {
                    if (i != 0)
                      {
                      valid = false;
                      alertMsg = 'Only the first character can be a minus sign     \n\n';
                      }
                    }
               else {
                    valid = false;
                    alertMsg = 'Please enter a valid integer     \n\n';
                    }
             }
      }
    }
  if ((valid) && (!digitFound))
    {
    valid = false;
    alertMsg = 'Please enter a valid integer     \n\n';
    }
  if (valid)
    {
    v = parseInt(s);
    if (v < minValue)
      {
      valid = false;
      alertMsg = 'The value must be at least '+minValue+'    \n\n';
      }
    if (v > maxValue)
      {
      valid = false;
      alertMsg = 'The value should be no more than '+maxValue+'     \n\n';
      }
    }
  }
IndicateIfNotValid(field, valid, alertTitle, alertMsg, autoFocus);
return(valid);
}


// -- Extract from File: fifo.js -------------------------------------

function StoreInFIFO(obj)
{
if (!(this.Full))
  {
  this.Buf[this.BufIn] = obj;
  if (this.BufIn < this.MaxI)
         this.BufIn = this.BufIn + 1;
    else this.BufIn = 0;
  this.NumEntriesInBuf = this.NumEntriesInBuf + 1;
  this.Empty = false;
  if (!(this.Full)) this.Full = (this.BufIn == this.BufOut);
  if (this.Full) this.BufOut = this.BufIn;
  }
}

function RemoveFromFIFO()
{
var Result = null;
if (!(this.Empty))
  {
  Result = this.Buf[this.BufOut];
  if (this.BufOut < this.MaxI)
         this.BufOut = this.BufOut + 1;
    else this.BufOut = 0;
  this.NumEntriesInBuf = this.NumEntriesInBuf - 1;
  this.Empty = (this.BufIn == this.BufOut);
  this.Full  = false;
  }
return(Result);
}

function ReadFIFO(DelayIndex)
{
var Result = null;
var I;
if (!(this.Empty))
  {
  if (DelayIndex < this.NumEntriesInBuf)
         {
         I = this.BufOut + this.NumEntriesInBuf - 1 - DelayIndex;
         if (I > this.MaxI) I = I - this.MaxI - 1;
         }
    else I = this.BufOut;
  Result = this.Buf[I];
  }
return(Result);
}

function ResetFIFO()
{
this.BufIn           = 0;
this.BufOut          = 0;
this.NumEntriesInBuf = 0;
this.Empty           = true;
this.Full            = false;  
}

function FIFO(NumEntries)
{
this.Reset    = ResetFIFO;
this.Store    = StoreInFIFO;
this.Remove   = RemoveFromFIFO;
this.Read     = ReadFIFO;
this.Buf      = new Array();
this.MaxI     = NumEntries-1;
this.Reset();
}


// -- Extract from File: animate.js ------------------------------------

function defaultX(T)             {return(0);}
function defaultY(T)             {return(0);}
function defaultR(T)             {return(0);}
function defaultTheta(T)         {return(0);}
function defaultFinished(T, X, Y){return(false);}

function StartAnimation()
{
if (this.running == false) 
  {
  this.running = true;
  this.interval = setInterval(this.StepMethodName, (this.tickP*1000));
  }
}

function StopAnimation()
{
if (this.running == true) 
  {
  clearInterval(this.interval);
  this.running = false;
  }
}

function ResetAnimation(newStartX, newStartY, newFinalX, newFinalY)
{
if (this.running == false)
  {
  this.stX  = newStartX || this.stX; 
  this.stY  = newStartY || this.stY;        
  this.finX = newFinalX || this.finX;
  this.finY = newFinalY || this.finY;
  this.T    = 0;        
  this.X    = this.stX;
  this.Y    = this.stY;
  this.FIFO.Reset();
  }
}

function DoAnimationStep()
{
var coords;
var trailIndex;
this.T = this.T + this.tickP;
this.X = (this.r(this.T)*Math.cos(this.theta(this.T))) + this.x(this.T) + this.stX;
this.Y = (this.r(this.T)*Math.sin(this.theta(this.T))) + this.y(this.T) + this.stY;
if (this.finished(this.T, this.X, this.Y))
  {
  this.Stop();
  if (this.finX > -10000) this.X = this.finX;
  if (this.finY > -10000) this.Y = this.finY;
  }
coords = {x:Math.round(this.X), y:Math.round(this.Y)};
if (this.FIFO.Full) this.FIFO.Remove();
this.FIFO.Store(coords);
for (trailIndex = 0; trailIndex <= (this.NumElementsInTrail-1); trailIndex++)
  {
  coords = this.FIFO.Read(this.Trail[trailIndex].tickDelay);
  MoveDHTMLElementTo(this.Trail[trailIndex].E, coords.x, coords.y);
  }
}

function AddAnimationTrailElement(elemId, TrailIndex, TickDelay)
{
var elem = GetDHTMLElement(elemId);
this.Trail[TrailIndex] = {E:elem, tickDelay: TickDelay};
this.NumElementsInTrail = this.NumElementsInTrail+1;
}

function Animation(objName, xFunc, yFunc, rFunc, thetaFunc, finishedFunc, tickPeriod, startX, startY, finalX, finalY, FIFOLength)
{
this.Trail              = new Array();
this.NumElementsInTrail = 0;
this.AddTrailElement    = AddAnimationTrailElement;
this.Start              = StartAnimation;
this.Stop               = StopAnimation;
this.Reset              = ResetAnimation;
this.DoStep             = DoAnimationStep;
this.StepMethodName     = objName+'.DoStep()';
this.x                  = xFunc;
this.y                  = yFunc;
this.r                  = rFunc;
this.theta              = thetaFunc;
this.finished           = finishedFunc;
this.tickP              = tickPeriod;
this.stX                = startX;
this.stY                = startY;
this.finX               = finalX;
this.finY               = finalY;
this.FIFO               = new FIFO(FIFOLength);
this.interval           = null;
this.running            = false;
this.Reset();
}

