// Code in this file has been distributed under the terms of the GNU LGPL

// xDef, Copyright 2001-2006 Michael Foster (Cross-Browser.com)
// Determine if all arguments are 'defined'.
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

// accepts any number of arguments
// Return true if all its arguments are defined

function xDef()
{
	for(var i=0; i<arguments.length; ++i){if(typeof(arguments[i])=='undefined') return false;}
	return true;
}

// xStr, Copyright 2001-2006 Michael Foster (Cross-Browser.com)
// Determine if all arguments are of type 'string'.
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

// accepts any number of arguments
// Return true if all its arguments are 'string' type

function xStr(s)
{
	for(var i=0; i<arguments.length; ++i){if(typeof(arguments[i])!='string') return false;}
	return true;
}

// xGetElementById, Copyright 2001-2006 Michael Foster (Cross-Browser.com)
// Get an object reference to the element object with the passed ID.
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

// e=id string or object reference
// Return object reference or null

// xNum, Copyright 2001-2006 Michael Foster (Cross-Browser.com)
// Determine if the arguments are of type Number.
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

// accepts any number of arguments
// Return true if all arguments are of type 'number', else false. Also returns false if isNaN.

function xNum()
{
	for(var i=0; i<arguments.length; ++i){if(isNaN(arguments[i]) || typeof(arguments[i])!='number') return false;}
	return true;
}

function xGetElementById(e)
{
	if(typeof(e)=='string') {
		if(document.getElementById) e=document.getElementById(e);
		else if(document.all) e=document.all[e];
		else e=null;
	}
	return e;
}

// xGetElementsByTagName, Copyright 2002-2007 Michael Foster (Cross-Browser.com)
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL
// Parameters
// t tag name String
// p parentEle element reference

function xGetElementsByTagName(t,p)
{
  var list = null;
  t = t || '*';
  p = p || document;
  if (typeof p.getElementsByTagName != 'undefined') { // DOM1
    list = p.getElementsByTagName(t);
    if (t=='*' && (!list || !list.length)) list = p.all; // IE5 '*' bug
  }
  else { // IE4 object model
    if (t=='*') list = p.all;
    else if (p.all && p.all.tags) list = p.all.tags(t);
  }
  return list || new Array();
}

// xGetElementsByClassName, Copyright 2002-2007 Michael Foster (Cross-Browser.com)
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL
// c String class name
// p Element parent element reference
// t String Tag Name
// f fnCallbackCallback function, iterates thru the returned list. Is passed a reference to each Element object found.

function xGetElementsByClassName(c,p,t,f)
{
  var r = new Array();
  var re = new RegExp("(^|\\s)"+c+"(\\s|$)");
//  var e = p.getElementsByTagName(t);
  var e = xGetElementsByTagName(t,p); // See xml comments.
  for (var i = 0; i < e.length; ++i) {
    if (re.test(e[i].className)) {
      r[r.length] = e[i];
      if (f) f(e[i]);
    }
  }
  return r;
}

// xBackground, Copyright 2001-2007 Michael Foster (Cross-Browser.com)
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL
// e=id string or object reference
// c=background color string
// i=background image URL string
// Return 'style.backgroundColor', a string

function xBackground(e,c,i)
{
  if(!(e=xGetElementById(e))) return '';
  var bg='';
  if(e.style) {
    if(xStr(c)) {e.style.backgroundColor=c;}
    if(xStr(i)) {e.style.backgroundImage=(i!='')? 'url('+i+')' : null;}
    bg=e.style.backgroundColor;
  }
  return bg;
}


// xFirstChild, Copyright 2004-2006 Michael Foster (Cross-Browser.com)
// An enhanced wrapper for element.firstChild.
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

// e=id string or object reference
// t=tagName string - optional. Case-insensitive.
// Returns e's first child with nodeName == t (if t is defined), else with nodeType == 1.

function xFirstChild(e,t)
{
	e = xGetElementById(e);
	var c = e ? e.firstChild : null;
	while (c) {
		if (c.nodeType == 1 && (!t || c.nodeName.toLowerCase() == t.toLowerCase())){break;}
		c = c.nextSibling;
	}
	return c;
}

// xGetComputedStyle, Copyright 2002-2006 Michael Foster (Cross-Browser.com)
// A safe wrapper for getComputedStyle and currentStyle.
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

// oEle=element object
// sProp=css property name
// bInt=if true, return value is an integer
// Return String, or integer if bInt is true.

function xGetComputedStyle(oEle, sProp, bInt)
{
	var s, p = 'undefined';
	var dv = document.defaultView;
	if(dv && dv.getComputedStyle){
		s = dv.getComputedStyle(oEle,'');
		if (s) p = s.getPropertyValue(sProp);
	}
	else if(oEle.currentStyle) {
		// convert css property name to object property name for IE
		var i, c, a = sProp.split('-');
		sProp = a[0];
		for (i=1; i<a.length; ++i) {
			c = a[i].charAt(0);
			sProp += a[i].replace(c, c.toUpperCase());
		}
		p = oEle.currentStyle[sProp];
	}
	else return null;
	return bInt ? (parseInt(p) || 0) : p;
}

// xInsertRule, Copyright 2006-2007 Michael Foster (Cross-Browser.com)
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL
// ss object or an id string.
// sel String - the CSS selector.
// rule String - the CSS rule for sel. Do not include the '{' and '}'.
// idx Integer - indicates where the rule will be inserted in ss.

function xInsertRule(ss, sel, rule, idx)
{
  if (!(ss=xGetElementById(ss))) return false;
  if (ss.insertRule) { ss.insertRule(sel + "{" + rule + "}", idx); } // DOM
  else if (ss.addRule) { ss.addRule(sel, rule, idx); } // IE
  else return false;
  return true;
}



// xTop, Copyright 2001-2006 Michael Foster (Cross-Browser.com)
// Return and optionally set the element's y coordinate.
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

// e=id string or object reference
// iY=desired integer y coordinate
// Return integer distance from top of rendered page in pixels

function xTop(e, iY)
{
	if(!(e=xGetElementById(e))) return 0;
	var css=xDef(e.style);
	if(css && xStr(e.style.top)) {
		if(xNum(iY)) e.style.top=iY+'px';
		else {
			iY=parseInt(e.style.top);
			if(isNaN(iY)) iY=xGetComputedStyle(e,'top',1);
			if(isNaN(iY)) iY=0;
		}
	}
	else if(css && xDef(e.style.pixelTop)) {
		if(xNum(iY)) e.style.pixelTop=iY;
		else iY=e.style.pixelTop;
	}
	return iY;
}

// xHeight, Copyright 2001-2006 Michael Foster (Cross-Browser.com)
// Return and optionally set the element's height.
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

// e=string or object reference
// u=unsigned set integer height; it is rounded to an integer
// Return integer height in pixels

function xHeight(e,h)
{
	if(!(e=xGetElementById(e))) return 0;
	if (xNum(h)) {
		if (h<0) h = 0;
		else h=Math.round(h);
	}
	else h=-1;
	var css=xDef(e.style);
	if (e == document || e.tagName.toLowerCase() == 'html' || e.tagName.toLowerCase() == 'body') {
		h = xClientHeight();
	}
	else if(css && xDef(e.offsetHeight) && xStr(e.style.height)) {
		if(h>=0) {
			var pt=0,pb=0,bt=0,bb=0;
			if (document.compatMode=='CSS1Compat') {
				var gcs = xGetComputedStyle;
				pt=gcs(e,'padding-top',1);
				if (pt !== null) {
					pb=gcs(e,'padding-bottom',1);
					bt=gcs(e,'border-top-width',1);
					bb=gcs(e,'border-bottom-width',1);
				}
				// Should we try this as a last resort?
				// At this point getComputedStyle and currentStyle do not exist.
				else if(xDef(e.offsetHeight,e.style.height)){
					e.style.height=h+'px';
					pt=e.offsetHeight-h;
				}
			}
			h-=(pt+pb+bt+bb);
			if(isNaN(h)||h<0) return;
			else e.style.height=h+'px';
		}
		h=e.offsetHeight;
	}
	else if(css && xDef(e.style.pixelHeight)) {
		if(h>=0) e.style.pixelHeight=h;
		h=e.style.pixelHeight;
	}
	return h;
}

// xWidth, Copyright 2001-2006 Michael Foster (Cross-Browser.com)
// Return and optionally set the element's width.
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

// e=id string or object reference
// u=unsigned desired integer width; it is rounded to an integer
// Return integer height in pixels

function xWidth(e,w)
{
	if(!(e=xGetElementById(e))) return 0;
	if (xNum(w)) {
		if (w<0) w = 0;
		else w=Math.round(w);
	}
	else w=-1;
	var css=xDef(e.style);
	if (e == document || e.tagName.toLowerCase() == 'html' || e.tagName.toLowerCase() == 'body') {
		w = xClientWidth();
	}
	else if(css && xDef(e.offsetWidth) && xStr(e.style.width)) {
		if(w>=0) {
			var pl=0,pr=0,bl=0,br=0;
			if (document.compatMode=='CSS1Compat') {
				var gcs = xGetComputedStyle;
				pl=gcs(e,'padding-left',1);
				if (pl !== null) {
					pr=gcs(e,'padding-right',1);
					bl=gcs(e,'border-left-width',1);
					br=gcs(e,'border-right-width',1);
				}
				// Should we try this as a last resort?
				// At this point getComputedStyle and currentStyle do not exist.
				else if(xDef(e.offsetWidth,e.style.width)){
					e.style.width=w+'px';
					pl=e.offsetWidth-w;
				}
			}
			w-=(pl+pr+bl+br);
			if(isNaN(w)||w<0) return;
			else e.style.width=w+'px';
		}
		w=e.offsetWidth;
	}
	else if(css && xDef(e.style.pixelWidth)) {
		if(w>=0) e.style.pixelWidth=w;
		w=e.style.pixelWidth;
	}
	return w;
}

// xClip, Copyright 2001-2007 Michael Foster (Cross-Browser.com)
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL
// e=id string or object reference
// t=integer y coordinate of top-left corner
// r=integer x coordinate of right-bottom corner
// b=integer y coordinate of right-bottom corner
// l=integer x coordinate of top-left corner
// Return element.style.clip

function xClip(e,t,r,b,l)
{
  if(!(e=xGetElementById(e))) return;
  if(e.style) {
    if (xNum(l)) e.style.clip='rect('+t+'px '+r+'px '+b+'px '+l+'px)';
    else e.style.clip='rect(0 '+parseInt(e.style.width)+'px '+parseInt(e.style.height)+'px 0)';
  }
}

// xClientWidth, Copyright 2001-2006 Michael Foster (Cross-Browser.com)
// Get the inner width of the window.
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

// Return Integer

function xClientWidth()
{
  var v=0,d=document,w=window;
  if(d.compatMode == 'CSS1Compat' && !w.opera && d.documentElement && d.documentElement.clientWidth)
    {v=d.documentElement.clientWidth;}
  else if(d.body && d.body.clientWidth)
    {v=d.body.clientWidth;}
  else if(xDef(w.innerWidth,w.innerHeight,d.height)) {
    v=w.innerWidth;
    if(d.height>w.innerHeight) v-=16;
  }
  return v;
}

// xClientHeight r5, Copyright 2001-2007 Michael Foster (Cross-Browser.com)
// Get the inner height of a window
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

// Return integer

function xClientHeight()
{
  var v=0,d=document,w=window;
  if((!d.compatMode || d.compatMode == 'CSS1Compat') && !w.opera && d.documentElement && d.documentElement.clientHeight)
    {v=d.documentElement.clientHeight;}
  else if(d.body && d.body.clientHeight)
    {v=d.body.clientHeight;}
  else if(xDef(w.innerWidth,w.innerHeight,d.width)) {
    v=w.innerHeight;
    if(d.width>w.innerWidth) v-=16;
  }
  return v;
}


// xPageX, Copyright 2001-2006 Michael Foster (Cross-Browser.com)
// Get the page-relative X position of the element.
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

// e=id string or object reference
// Return the page relative X position of e in pixels

function xPageX(e)
{
	if (!(e=xGetElementById(e))) return 0;
	var x = 0;
	while (e) {
		if (xDef(e.offsetLeft)) x += e.offsetLeft;
		e = xDef(e.offsetParent) ? e.offsetParent : null;
	}
	return x;
}

// xPageY, Copyright 2001-2006 Michael Foster (Cross-Browser.com)
// Get the page-relative Y position of the element.
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

// e=id string or object reference
// Return the page relative Y position of e in pixels

function xPageY(e)
{
	if (!(e=xGetElementById(e))) return 0;
	var y = 0;
	while (e) {
		if (xDef(e.offsetTop)) y += e.offsetTop;
		e = xDef(e.offsetParent) ? e.offsetParent : null;
	}
	return y;
}

// xScrollTop, Copyright 2001-2006 Michael Foster (Cross-Browser.com)
// Determine how far the window (or an element) has scrolled vertically.
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

// e=id string or object reference. If undefined return document scrollTop
// bWin=if true, ele is assumed to be a reference to a window object
// Return the number of pixels the element (or window) has scrolled vertically

function xScrollTop(e, bWin)
{
	var offset=0;
	if (!xDef(e) || bWin || e == document || e.tagName.toLowerCase() == 'html' || e.tagName.toLowerCase() == 'body') {
		var w = window;
		if (bWin && e) w = e;
		if(w.document.documentElement && w.document.documentElement.scrollTop) offset=w.document.documentElement.scrollTop;
		else if(w.document.body && xDef(w.document.body.scrollTop)) offset=w.document.body.scrollTop;
	}
	else {
		e = xGetElementById(e);
		if (e && xNum(e.scrollTop)) offset = e.scrollTop;
	}
	return offset;
}

// xLeft, Copyright 2001-2006 Michael Foster (Cross-Browser.com)
// Return and optionally set the element's x coordinate.
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

// e=id string or object reference
// iX=desired integer x coordinate
// Return integer e's x coordinate in pixels

function xLeft(e, iX)
{
	if(!(e=xGetElementById(e))) return 0;
	var css=xDef(e.style);
	if (css && xStr(e.style.left)) {
		if(xNum(iX)) e.style.left=iX+'px';
		else {
			iX=parseInt(e.style.left);
			if(isNaN(iX)) iX=xGetComputedStyle(e,'left',1);
			if(isNaN(iX)) iX=0;
		}
	}
	else if(css && xDef(e.style.pixelLeft)) {
		if(xNum(iX)) e.style.pixelLeft=iX;
		else iX=e.style.pixelLeft;
	}
	return iX;
}

// xAddEventListener, Copyright 2001-2006 Michael Foster (Cross-Browser.com)
// Register an event listener on the element.
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

// e=ID string or object reference.
// eT=EventTypeString event type: 'mousemove', 'click', 'resize', etc.
// eL=Reference to the listener function.
// cap=Boolean capture event flag.

function xAddEventListener(e,eT,eL,cap)
{
	if(!(e=xGetElementById(e)))return;
	eT=eT.toLowerCase();
	if(e.addEventListener)e.addEventListener(eT,eL,cap||false);
	else if(e.attachEvent)e.attachEvent('on'+eT,eL);
	else e['on'+eT]=eL;
}

// xSlideTo, Copyright 2001-2006 Michael Foster (Cross-Browser.com)
// Animated, linear motion with sinusoidal or linear rate. 
// Slide the element to the target position in the given time.
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

// e=id string or object reference
// x=integer x target
// y=integer y target
// uTime=total time of slide in ms

function xSlideTo(e, x, y, uTime)
{
	if (!(e=xGetElementById(e))) return;
	if (!e.timeout) e.timeout = 25;
	e.xTarget = x; e.yTarget = y; e.slideTime = uTime; e.stop = false;
	e.yA = e.yTarget - xTop(e); e.xA = e.xTarget - xLeft(e); // A = distance
	if (e.slideLinear) e.B = 1/e.slideTime;
	else e.B = Math.PI / (2 * e.slideTime); // B = period
	e.yD = xTop(e); e.xD = xLeft(e); // D = initial position
	var d = new Date(); e.C = d.getTime();
	if (!e.moving) _xSlideTo(e);
}
function _xSlideTo(e)
{
	if (!(e=xGetElementById(e))) return;
	var now, s, t, newY, newX;
	now = new Date();
	t = now.getTime() - e.C;
	if (e.stop) { e.moving = false; }
	else if (t < e.slideTime) {
		setTimeout("_xSlideTo('"+e.id+"')", e.timeout);

		s = e.B * t;
		if (!e.slideLinear) s = Math.sin(s);
//		if (e.slideLinear) s = e.B * t;
//		else s = Math.sin(e.B * t);

		newX = Math.round(e.xA * s + e.xD);
		newY = Math.round(e.yA * s + e.yD);
		xMoveTo(e, newX, newY);
		e.moving = true;
	}	
	else {
		xMoveTo(e, e.xTarget, e.yTarget);
		e.moving = false;
		if (e.onslideend) e.onslideend();
	}	
}

// xMoveTo, Copyright 2001-2006 Michael Foster (Cross-Browser.com)
// Set the element's x and y coordinates.
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

// e=id string or object reference
// x=integer x coordinate
// y=integer y coordinate

function xMoveTo(e,x,y)
{
	xLeft(e,x);
	xTop(e,y);
}

// added xHasClass, xAddClass, and xRemoveClass on 11/21/08

// xHasClass r3, Copyright 2005-2007 Daniel Frechette - modified by Mike Foster
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL
// e=id string or object reference
// c=class string 

function xHasClass(e, c)
{
  e = xGetElementById(e);
  if (!e || e.className=='') return false;
  var re = new RegExp("(^|\\s)"+c+"(\\s|$)");
  return re.test(e.className);
}

// xAddClass r3, Copyright 2005-2007 Daniel Frechette - modified by Mike Foster
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL
// e=id string or object reference
// c=class string 

function xAddClass(e, c)
{
  if ((e=xGetElementById(e))!=null) {
    var s = '';
    if (e.className.length && e.className.charAt(e.className.length - 1) != ' ') {
      s = ' ';
    }
    if (!xHasClass(e, c)) {
      e.className += s + c;
      return true;
    }
  }
  return false;
}

// xRemoveClass r3, Copyright 2005-2007 Daniel Frechette - modified by Mike Foster
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL
// e=id string or object reference
// c=class string 

function xRemoveClass(e, c)
{
  if(!(e=xGetElementById(e))) return false;
  e.className = e.className.replace(new RegExp("(^|\\s)"+c+"(\\s|$)",'g'),
    function(str, p1, p2) { return (p1 == ' ' && p2 == ' ') ? ' ' : ''; }
  );
  return true;
}

// xStyle r1, Copyright 2007 Michael Foster (Cross-Browser.com)
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL
// sProp = property
// sVal = value
// e1, e2, e3,... = one or more elements

function xStyle(sProp, sVal)
{
  var i, e;
  for (i = 2; i < arguments.length; ++i) {
    e = xGetElementById(arguments[i]);
    if (e.style) {
      try { e.style[sProp] = sVal; }
      catch (err) { e.style[sProp] = ''; } // ???
    }
  }
}


// <meta name='author' content='Mike Foster (Cross-Browser.com)'>
// <meta name='description' content='Cross-Browser Javascript Libraries and Applications'>
// <meta name='license' content='Distributed under the terms of the GNU LGPL (gnu.org)'>

var xthf;
function xOnLoadPrepareScrollTable()
{
	if (document.createElement && document.body && document.body.appendChild) {
		xthf = new xTableHeaderFixed(arguments);
	}
}

function xTableHeaderFixed(args)
{
	// Private Property
	// Private Event Listeners
	function onscroll()
	{
		for (var i = 0; i < args.length; ++i) {
			scroll(args[i]);
		}
	}
	function onresize()
	{
		for (var i = 0; i < args.length; ++i) {
			scroll(args[i], true);
		}
	}
	// Private Methods
	function scroll(tId, bResize)
	{
		var t = xGetElementById(tId);
		var h = xGetElementById(tId + 'thead');
		var st = xScrollTop();
		var th = xHeight(t);
		var tw = xWidth(t);
		var ty = xPageY(t);
		var tx = xPageX(t);
		var hh = xHeight(h);
		if (bResize) {
			xWidth(h, tw);
			xLeft(h, tx);
		}
		if (st <= ty + hh || st > ty + th) {	// CB update
			if (h.style.visibility != 'hidden') {
				h.style.visibility = 'hidden';
			}
		}
		else {
			/*@cc_on
			@if (@_jscript_version < 5.7) // IE6 or below
				if (st <= ty + hh || st > ty + th - hh) {	// begin CB update
					xSlideTo(h, tx, ty + th - hh, 700);
				} else { 
					xSlideTo(h, tx, st, 700); // end CB update
				}
			@else @*/
				if (h.style.position != 'fixed') {
					xMoveTo(h, tx, 0);
					h.style.position = 'fixed';
				} else {	// begin CB code
					if (st <= ty + hh || st > ty + th - hh) {
						xMoveTo(h, tx, ty + th - hh - st);
					} else { // fix mouse misalignment bug
						xMoveTo(h, tx, 0);
					}
				}	// end CB code
			/*@end @*/
			if (h.style.visibility != 'visible') {
				xWidth(h, tw);
				h.style.visibility = 'visible';
			}
		}
	}
	function init(tId)
	{
		var yAppVer = navigator.appVersion.toLowerCase();
		if ((yAppVer.indexOf('msie') > -1 && yAppVer.indexOf('macintosh') > -1)) {
			return null;
		}
		var tbl = xGetElementById(tId);
		var h = xFirstChild(tbl, 'thead');
		var t = document.createElement('table');
		t.appendChild(h.cloneNode(true));
		var d = document.createElement('div');
		d.id = tId + 'thead';
		d.style.position = 'absolute';
		d.style.visibility = 'hidden';
		d.appendChild(t);
		document.body.appendChild(d);
		//xParent(tbl).appendChild(d);
		xMoveTo(d, 0, -100); // this fixed (hides) a rendering problem in IE6
	}
	// Constructor Code
	if (args.length > 0) {
		for (var i = 0; i < args.length; ++i) {
			init(args[i]);
		}
		xAddEventListener(window, 'scroll', onscroll, false);
		xAddEventListener(window, 'resize', onresize, false);
		onresize();
	}
} // end xTableHeaderFixed

