//------------------------------------------------------------------------------
// DomAPI core routines
// D. Kadrioski 3/1/2001
// (c) Nebiru Software 2001-2002
//------------------------------------------------------------------------------
// additional contributors:
// O. Conradi <conradi@cs.utwente.nl>
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// global constants
//------------------------------------------------------------------------------
var domAPIVersion    = "2.50";
var internalElmCount = 0;
var domAPIElms       = new Array();
function bodyElm()     {return document.getElementsByTagName("BODY").item(0)};
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// browser sniffing stuff
//------------------------------------------------------------------------------
var isDom  = document.getElementById?1:0;
var isIE   = (navigator.userAgent.indexOf("MSIE" )>0)?1:0;
var isNS   = (navigator.userAgent.indexOf("Gecko")>0)?1:0;
var isMac  = (navigator.userAgent.indexOf("Mac"  )>0)?1:0; //  <----- can someone verify this?
var isIE5  = 0;
var isIE50 = 0;
var isIE55 = 0;
var isIE60 = 0;
if(isIE){
  var i    = navigator.appVersion.indexOf("MSIE");
  var temp = navigator.appVersion.substring(i+5,i+8);
  isIE50   = (temp=="5.0");
  isIE55   = (temp=="5.5");
  isIE60   = (temp=="6.0");
  isIE6    = (temp=="6." );
  isIE5    = isIE50||isIE55;
};
// taken from http://www.your-site.com/~rinfo/case_studies/doctypes.html
// mozilla also has a non standard quirks mode http://mozilla.org/docs/web-developer/quirks/
// ie6 in backcompat mode acts as ie5
function checkStrict() {
  var strict = false;
  var d      = document.doctype;
  strict     = (document.compatMode=="CSS1Compat");
  strict     = (d&&d.systemId?(d.systemId.indexOf("strict")>-1?true:(d.publicId.indexOf("transitional")>-1?true:false)):(d&&d.publicId.indexOf("transitional")==-1?true:strict));
  strict     = (d&&d.name.indexOf(".dtd")>-1)?true:strict;
  return strict;
}
var isStrict    = checkStrict();
var needsBoxFix = (isIE5 && !isMac)||(isIE60 && !isStrict);
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// utility functions
//------------------------------------------------------------------------------
function isNil(s){ return s==null || !String(s).length};                                // value is null or blank
function rInt(s,d){return(isNil(s)||isNaN(s))?((isNil(d)||isNaN(d))?0:d):parseInt(s)};  // return value if integer else default
function rVal(s,d){return(isNil(s)?(isNil(d)?"":d):s)};                                 // return value if not nil else default
//------------------------------------------------------------------------------
function bodyWidth() {return parseInt(isIE?(!isStrict?document.body.clientWidth: document.documentElement.clientWidth ):window.innerWidth)};
function bodyHeight(){return isIE?(!isStrict?document.body.clientHeight:document.documentElement.clientHeight):window.innerHeight};
function scrollTop() {return isIE?document.body.scrollTop:pageYOffset};
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// box methods - these handle taking in optional arrays for setting box properties
// such as margin, padding and borders
// they also ensure a 4 element array is *always* returned when reading these properties
//------------------------------------------------------------------------------
function boxValuesOut(s){
  if(isNil(s))return [0,0,0,0];
  var a=s.split(" ");
  for(var i=0;i<a.length;i++)a[i]=parseInt(a[i]);
  switch(a.length){
    case 1:a[1]=a[0];a[2]=a[0];a[3]=a[0];break;
    case 2:a[2]=a[0];a[3]=a[1];break;
    case 3:a[3]=a[1];break;
  }
  return a;
};
function boxValuesIn(t,r,b,l){
  if(typeof(t)=="object" && t.length){r=t[1];b=t[2];l=t[3];t=t[0]} // in mozilla an array is always passed as 1 argument
  t=rInt(t);
  if(isNil(l)){
    if(isNil(b)){
      if(isNil(r)) {r=t; b=t; l=t}                    // 1 arg: t     -> t t t t
      else{b=t; l=r}                                  // 2 arg: t r   -> t r t r
    }else{
      r=rInt(r);
      l=r;                                            // 3 arg: t r b -> t r b r
    }
  }
  return t+"px "+r+"px "+b+"px "+l+"px";
};
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// here is the actual Elm declaration
//------------------------------------------------------------------------------
// this isn't an Object constructor.  I thought it was silly to construct an
// Object to encapsulate the DomElements which are already Objects themselves
// so I simply extend a given DOMElement by giving it new methods and properties.
// I'm not sure if this decreases or increases memory usage compared to creating
// a new Object constructor.
// certainly I lose the flexibility of prototypes, but I wanted the user to be
// able to use straight dot-notation to get at an Object.  With a prototype, I'd
// need to do something like this.elm.moveTo() instead of just this.moveTo() which
// I think is an better concept.
// I had toyed with the idea of adding the Elm's methods and properties directly
// to the Object.prototype which would solve all my problems, but IE refuses to
// work this way. If someone gets this to work, let me know!
// Here's my notes on the subject:
// Object.prototype.myboo         = function(){alert('boo')};
//   alert(Object.prototype.constructor);
//   Object.prototype.myboo         = function(){alert('boo')};      // works in ns
//   HTMLElement.prototype.myboo    = function(){alert('boo')};      // works in ns
//   HTMLDivElement.prototype.myboo = function(){alert('boo')};      // works in ns
//   Element.prototype.myboo        = function(){alert('boo')};      // works in ns
//   Object.prototype.myboo         = new Function("alert('boo');"); // works in ns
//   function myboo(){alert("boo")}; Object.prototype.myboo=myboo;   // works in ns
// None of these work in IE
//------------------------------------------------------------------------------
function Elm(i){
  var elm=getElm(i); if(!elm)return null;
  for(var a in elmProto)elm[a]  = elmProto[a];
  //--------
  elm.domAPIObjType             = "ELM";
  elm.domAPIIndex               = domAPIElms.length;
  domAPIElms[domAPIElms.length] = elm;
  return elm;
};
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// this is the method used to create an Elm on the fly, and not from existing
// html.  All the parameters are optional, defaults are provided for missing ones.
//------------------------------------------------------------------------------
function createElm(p,x,y,w,h,bg,c,t){ // parent,x,y,w,h,bgcolor,color,type
  var o = document.createElement(rVal(t,"DIV"));
  o.id  = "domAPI_Elm_"+internalElmCount;
  if(p)p.appendChild(o); else document.body.appendChild(o);
  var elm=Elm(     o.id); 
  //elm.setB(        0);
  //elm.setP(        0);
  //elm.setM(        0);
  elm.moveTo(      rInt(x),rInt(y));
  elm.setOverflow( "hidden");  // ie5.5 won't allow height smaller than font height
  if(!isNil(c)) elm.setColor(c);
  if(!isNil(bg))elm.setBgColor(bg);
  if(!isNil(w)) elm.setW(w);
  if(!isNil(h)) elm.setH(h);
  internalElmCount++;
  return elm;
};
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// base utility functions used by components
//------------------------------------------------------------------------------
function getElm(i){return document.getElementById(i)}; // getElm() is simply shorthand to document.getElementById()
//------------------------------------------------------------------------------
function findTarget(E,k){
  // based on the target node of the event passed, searches up the tree for a particular node
  if(isNS)var t=E.target;else var t=event.srcElement;
  return findParent(t,k);
};
//------------------------------------------------------------------------------
function findParent(e,k){
  // search up the domtree for a specific node type and returns it (or null if not exists)
  var t=e;
  while(t.parentNode){
    if(t.nodeName==k)return t;
    if(t.domAPIObjType && t.domAPIObjType==k)return t;
    t=t.parentNode;
  }
  return null;
};
//------------------------------------------------------------------------------
function getNodeIndex(e){
  // returns the index of a childnode in relation to it's siblings
  if(!e)return null;
  r=0;
  var t=e.previousSibling;
  while(t){
    r++;
    t=t.previousSibling;
  }
  return r;
};
//------------------------------------------------------------------------------
function insertElm(e,t,w){
  // valid "where" values are beforeBegin, afterBegin, beforeEnd and afterEnd
  // this function removes the elm from the page and re-inserts it at the specified target 
  e.moveTo();
  e.style.position="relative";
  if(!t)return;
  t.insertAdjacentElement(rVal(w,"afterBegin"),e);
};
//------------------------------------------------------------------------------
function ImageList(p,i,n,w,h){
  //------------------------------------------------------------------------------
  // ImageList allows you to flip through images of the same size arranged horz in a stack
  // excellent for rollovers or storing many image states at once
  // see Tree2 for an example of this in action
  //------------------------------------------------------------------------------
  w=rInt(w,16); h=rInt(h,16); n=rInt(n,2);
  var e     = createElm(p,0,0,w,h,null,null,"SPAN");
  e.setText( '<img width='+w+' height='+h+' border=0 style="padding:0px;margin:0px;visibility:hidden" />');
  e.I       = createElm(e,0,0,w*n,h,null,null,"IMG");
  e.w=w; e.h=h; e.n=n;
  e.style.position = "relative";
  e.style.display  = "inline";
  e.I.src          = rVal(i,"gui.gif");
  e.setImage=function(i){
    i=rInt(i);
    this.I.style.left = -(i)*this.w+"px";
    this.I.setClip(    0,
                       (i+1)*this.w,
                       this.h,
                       i*this.w);
    this.index=i;
  };
  e.setImage();
  e.domAPIObjType = "IMAGELIST";
  return e;
}
//------------------------------------------------------------------------------
// you can't prototype indexOf into an array, but this is the next best thing
// to use it, first declare your array    e.g. myArray=[];
// then assign the method:  myArray.indexOf=array_index_of;
// then you can look up elements in an array  e.g. myArray.indexOf("boo");
// it returns the index of the matching element or -1 if not found
function array_index_of(s){for(var a=0;a<this.length;a++)if(this[a]==s)return a;return -1};
//------------------------------------------------------------------------------


//------------------------------------------------------------------------------
// theme stuff
// you can include new themes by placing
// constructors in an external file
// see src/more_themes.js for an example of this
//------------------------------------------------------------------------------
function Theme(){
  this.name           = "default";
  //-----
  this.font           = '8pt "MS Sans Serif",Geneva,sans-serif'; // normal font (think listbox)
  this.bgColor        = "white";   // normal background
  this.fgColor        = "black";   // normal text
  //-----
  this.ctrlFont       = this.font; // control font (think buttons)
  this.ctrlBgColor    = "#127B35";  // control base
  this.ctrlFgColor    = "white";   // control text
  //-----
  this.hlFont         = this.font; // mouse-over font
  this.hlBgColor      = "#DDDDDD"; // mouse-over background
  this.hlFgColor      = "navy";    // mouse-over text
  //-----
  this.selFont        = '8pt "MS Sans Serif",Geneva,sans-serif'; // selected font
  this.selBgColor     = "#7BAD55";    // selected background
  this.selFgColor     = "white";   // selected text
  //-----
  this.bdrColor       = "";        // border color
  this.bdrWidth       = 0;     // border width
  this.bdrOutset      = "outset";  // what to use for outset
  this.bdrInset       = "inset";   // what to use for inset
  this.bdrSolid       = "solid";   // what to use for solid borders
  //-----
  this.colorFadeInc   = 10;        // for components that support color fading
  this.colorFadeSpeed = 10;        // for components that support color fading
};
//------------------------------------------------------------------------------
// this is the default theme - used if you pass null as a theme to component constructors
var themes            = [];
var defaultTheme      = new Theme();
themes[themes.length] = defaultTheme;
//------------------------------------------------------------------------------
function applyThemeToBody(t){
  t=t?t:defaultTheme;
  with(bodyElm().style){
    color           = t.fgColor;
    backgroundColor = t.bgColor;
    font            = t.font;
  }
};
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// color stuff
// this is just the base code to support core.js
// if you want more color functionallity, include ext/color_c.js
//------------------------------------------------------------------------------
function hexToInt(h){return parseInt(h.substring(1),16)};
//------------------------------------------------------------------------------
function intToHex(i){
  i=i.toString(16);
  while(i.length<6)i="0"+i;
  return "#"+i;
};
//------------------------------------------------------------------------------
function rgbToHex(s){
  // takes in an rgb array and returns the hex value
  // the complimentary function, hexToRGB() is located in ext/color_c.js
  var n  = Math.round(s[2]);
      n += Math.round(s[1]) << 8;
      n += Math.round(s[0]) << 16;
  return intToHex(n);
};
//------------------------------------------------------------------------------
function makeSureIsHexColor(s){
  // netscape 6 seems to return colors in rgb(0,0,0) format, even in you set
  // the color using hex format #FFFFFF.
  // this function will detect that and convert it to hex for use in the other
  // color routines (lightenColor, darkenColor, etc...)
  // - new in 2.07
  // If color_c.js has been included on this page, then this function can attempt
  // to resolve color constants such as "red" into their hex equivalents
  if(s.substring(0,4)=="rgb("){                              // must be ns, of course
    var temp=s.split("rgb(")[1].split(",");                  // pull out rgb values
    for(var i=0;i<temp.length;i++)temp[i]=parseInt(temp[i]); // convert them to integers
    return(rgbToHex(temp));                                  // turn it into hex and exit
  }else{
    if(isNil(s))        return s;     // nothing to do
    if(s.charAt(0)=="#")return s;     // must already be hex 
    // might be a color constant
    if(color_available){                 // looks like color_c.js has been included
      var i=lookupColorNames.indexOf(s); // attempt to lookup color constant
      if(i>0)s="#"+lookupColors[i];      // return the hex value for this constant
    }
    return s;
  }
};
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// extend mozilla (from http://www.mts.net/~tfriesen/dhtml)
//------------------------------------------------------------------------------
if(self.Node){ // mozilla
  Node.prototype.swapNode                 = swap_Node;    // add IE's swapNode (useful for sorting)
  Node.prototype.removeNode               = remove_Node;  // add IE's removeNode (used for deleting rows, etc...)
  Element.prototype.insertAdjacentElement = insertAdj_El; // add IE's insertAdjacentElement (used by insertElement)
}
function swap_Node(n){
  var p=n.parentNode;
  var s=n.nextSibling;
  this.parentNode.replaceChild(n,this);
  p.insertBefore(this,s);
  return this;
}
function remove_Node(a1){
  var p=this.parentNode;
  if(p&&!a1){
    var df=document.createDocumentFragment();
    for(var a=0;a<this.childNodes.length;a++)df.appendChild(this.childNodes[a]);
    p.insertBefore(df,this);
  }
  return p?p.removeChild(this):this;
}
function insertAdj_El(a1,a2){
  var p = this.parentNode;
  var s = a1.toLowerCase();
  if(s=="beforebegin")p.insertBefore(a2,this);
  if(s=="afterend"   )p.insertBefore(a2,this.nextSibling);
  if(s=="afterbegin" )this.insertBefore(a2,this.childNodes[0]);
  if(s=="beforeend"  )this.appendChild(a2);
  return a2;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// fix IE memory leak (seems to work marginally well)
//------------------------------------------------------------------------------

// 12/19/2001 - darin
// note: now that we are using elmProto, this code seems to be pretty useless
// use examples/getelementbyid_stress.htm to test elm creation time with a resource monitor

/*onunload=function(){
  var toRemoveList = "domAPIVersion,internalElmCount,domAPIElms,bodyElm,"+
                     "bodywidth,bodyHeight,scrollTop,isDom,isIE,isNS,isIE50,"+
                     "isIE55,isIE60,elmPropList,Elm,createElm,getElm,"+
                     "findTarget,findParent,getNodeIndex,Theme,themes,"+
                     "defaultTheme,applyThemeToBody,hexToInt,intToHex,"+
                     "rgbToHex,makeSureIsHexColor,addEvent,removeEvent";
  var b;
  var toRemove = toRemoveList.split(",");
  for(var a=0;a<domAPIElms.length;a++)
    for(b=0;b<elmProps.length;b++)  eval("domAPIElms[a]."+elmProps[b]+"=null");
  for(var a=0;a<toRemove.length;a++)eval(toRemove[b]+"=null");
};*/
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// event support (from scottandrew.com)
//------------------------------------------------------------------------------
function addEvent(o,t,fn){
  if(o.addEventListener){o.addEventListener(t,fn,true); return true}
  else if(o.attachEvent)return o.attachEvent("on"+t,fn);
  else alert("Handler could not be attached");
};
//------------------------------------------------------------------------------
function removeEvent(o,t,fn){
  if(o.removeEventListener){o.removeEventListener(t,fn,true);return true}
  else if(o.detachEvent)return o.detachEvent("on"+t,fn);
  else alert("Handler could not be removed");
};
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// everything from here down is the Elm() method implementations
// use /examples/elm to test
//------------------------------------------------------------------------------
function elmProto(){};
elmProto.getX         = function()       {return this.offsetLeft};
elmProto.getY         = function()       {return this.offsetTop};
elmProto.getW         = function()       {return this.offsetWidth};
elmProto.getH         = function()       {return this.offsetHeight};
elmProto.getP         = function()       {return boxValuesOut(this.style.padding)};
elmProto.getM         = function()       {return boxValuesOut(this.style.margin)};
elmProto.getB         = function()       {return boxValuesOut(this.style.borderWidth)};
elmProto.getZ         = function()       {return rInt(this.style.zIndex)};
elmProto.getColor     = function()       {return makeSureIsHexColor(this.style.color)};
elmProto.getBgColor   = function()       {return makeSureIsHexColor(this.style.backgroundColor)};
elmProto.getOverflow  = function()       {return this.style.overflow};
elmProto.getClip      = function()       {
  if(isNil(this.style.clip)) return [0,this.getW(),this.getH(),0];
  var c=this.style.clip.split("rect(")[1].split(" ");
  for(var i=0;i<c.length;i++) c[i]=parseInt(c[i]);
  return c;
};
//--------
elmProto.setX         = function(x)      {this.style.left    = rInt(x)+"px"};
elmProto.setY         = function(y)      {this.style.top     = rInt(y)+"px"};
elmProto.setW         = function(w)      {
  w=rInt(w);
  if(!needsBoxFix){var b=this.getB(); var p=this.getP(); w -= b[1]+b[3]+p[1]+p[3]; if(w<0)w=0}
  this.style.width = w+"px";
};
elmProto.setH         = function(h)      {
  h=rInt(h);
  if(!needsBoxFix){var b=this.getB(); var p=this.getP(); h -= b[0]+b[2]+p[0]+p[2]; if(h<0)h=0}
  this.style.height = h+"px";
};
elmProto.setP         = function(t,r,b,l){
  if(!needsBoxFix){var w=this.getW(); var h=this.getH()}
  this.style.padding = boxValuesIn(t,r,b,l);
  if(!needsBoxFix){this.setW(w); this.setH(h)}
};
elmProto.setB         = function(t,r,b,l){
  if(!needsBoxFix){var w=this.getW(); var h=this.getH()}
  this.style.borderWidth = boxValuesIn(t,r,b,l);
  if(!needsBoxFix){this.setW(w); this.setH(h)}
};
elmProto.setM         = function(t,r,b,l){this.style.margin               = boxValuesIn(t,r,b,l)};
elmProto.setZ         = function(z)      {this.style.zIndex               = rInt(z)};
elmProto.setColor     = function(c)      {this.style.color                = c};
elmProto.setBgColor   = function(c)      {this.style.backgroundColor      = c};
elmProto.setOverflow  = function(f)      {if(f!="hidden" && f!="scroll" && f!="auto") f="visible";this.style.overflow = f};
elmProto.setClip      = function(t,r,b,l){
  if(typeof(t)=="object" && t.length){r=t[1];b=t[2];l=t[3];t=t[0];}   // in mozilla an array is always passed as 1 argument
  this.style.clip= "rect("+rInt(t)+"px "+rInt(r,this.getW())+"px "+rInt(b,this.getH())+"px "+rInt(l)+"px)";
};
//--------
elmProto.setSize      = function(w,h)    {this.setW(w);this.setH(h)};
elmProto.setSizeBy    = function(w,h)    {this.setSize(this.getW()+parseInt(w),this.getH()+parseInt(h))};
elmProto.moveTo       = function(x,y)    {this.makeAbsolute();this.setX(x);this.setY(y)};
elmProto.moveBy       = function(x,y)    {this.moveTo( this.getX()+parseInt(x),this.getY()+parseInt(y))};
elmProto.moveToElm    = function(e)      {this.moveTo(e.getX(),e.getY())};
elmProto.makeAbsolute = function()       {this.style.position        = "absolute"};
elmProto.makeStatic   = function()       {this.style.position        = "static"};
elmProto.setText      = function(s)      {this.innerHTML             = s};
//--------
elmProto.visible      = function()       {return this.style.visibility=="visible"?true:false};
elmProto.hide         = function()       {this.style.visibility      = "hidden"};
elmProto.show         = function()       {this.style.visibility      = "visible"};
//--------
elmProto.buttonDown   = function(w)      {
  this.style.borderStyle = this.theme?this.theme.bdrInset :"inset";
  this.setB(rInt(w,this.theme?this.theme.bdrWidth:defaultTheme.bdrWidth));
};
elmProto.buttonUp     = function(w)      {
  this.style.borderStyle = this.theme?this.theme.bdrOutset:"outset";
  this.setB(rInt(w,this.theme?this.theme.bdrWidth:defaultTheme.bdrWidth));
};
//--------
elmProto.setClipBy    = function(t,r,b,l){
  if(typeof(t)=="object" && t.length){r=t[1];b=t[2];l=t[3];t=t[0];}   // in mozilla an array is always passed as 1 argument
  var c=this.getClip();
  c[0] += rInt(t);
  c[1] += rInt(r);
  c[2] += rInt(b);
  c[3] += rInt(l);

  this.setClip(c);
};
//------------------------------------------------------------------------------
// Bob Dankert
elmProto.setAlpha = function(a){
  if(!String(a).indexOf("%"))a=parseInt(a)*100;
  a=parseInt(a);
  if(a<0)a=0; if(a>100)a=100;
  if(isNS)this.style.MozOpacity = a+"%";
  if(isIE)this.style.filter     = "alpha(opacity="+a+")";
};
//------------------------------------------------------------------------------
elmProto.getAlpha = function(a){
  if(isNS)return rInt(parseInt(this.style.MozOpacity),100);
  if(isIE)return rInt(parseInt(this.style.filter.split("=")[1]),100);
};
//------------------------------------------------------------------------------