/**
 * jt_utils.js - "JavaScript Toolkit" utility functions
 *
 * Copyright (c) 2005-2007  Joseph Oster, wingo.com
 */

/* GENERIC FUNCTIONS */
String.prototype.trim = function () {
  return this.replace(/^\s+/g, '').replace(/\s+$/g, '');
  }

function jt_strEmpty(st) {
  // return 'true' unless 'st' is a non-null string
  if (typeof st == 'string') return (st.trim() == '');
  else return true;
  }

function $(divName) {
  return document.getElementById(divName);
  }

function jt_ShowHideElm(elm, showIt) {
  if (elm) elm.style.visibility = (showIt) ? "visible" : "hidden";
  }

function jt_ShowNoneElm(elm, showIt, showStyle) {
  if (!showStyle) showStyle = "";
  if (elm) elm.style.display = (showIt) ? showStyle : "none";
  }

function jt_ShowHide(divName, showIt) {
  jt_ShowHideElm(document.getElementById(divName), showIt);
  }

function jt_ShowNone(divName, showIt, showStyle) {
  jt_ShowNoneElm(document.getElementById(divName), showIt, showStyle);
  }


/********** BEGIN: screen handling **********/
function jt_valPx(pixels) {
  return pixels + "px";
  }

function jt_moveTo(obj, x, y) {
  obj.style.left = jt_valPx(x);
  obj.style.top = jt_valPx(y);
  }

function jt_Point(x, y) {
  // returns a "Point" object with '.x' and '.y' properties
  this.x = x;
  this.y = y;
  }

function jt_getOffsetXY(obj, findID, point) {
  // 'findID' and 'point' are optional
  // returns 'jt_Point' object with '.x' and '.y' offsets of 'obj' relative to page
  // or relative to 'findID' if 'findID' is used and found as a parent
  if (point) { // re-use when valid; good practice when dragging
    point.x = obj.offsetLeft;
    point.y = obj.offsetTop;
    }
  else point = new jt_Point(obj.offsetLeft, obj.offsetTop);
  var parent = obj.offsetParent;
  while (parent != null) {
    if (findID && (parent.id == findID)) break;
    point.x += parent.offsetLeft;
    point.y += parent.offsetTop;
    parent = parent.offsetParent;
    }
  return point;
  }

function jt_getOffsetX(obj) {
  // returns 'x' coordinate of 'obj'
  var xPos = obj.offsetLeft;
  var parent = obj.offsetParent;
  while (parent != null) {
    xPos += parent.offsetLeft;
    parent = parent.offsetParent;
    }
  return xPos;
  }

function jt_getOffsetY(obj) {
  // returns 'y' coordinate of 'obj'
  var yPos = obj.offsetTop;
  var parent = obj.offsetParent;
  while (parent != null) {
    yPos += parent.offsetTop;
    parent = parent.offsetParent;
    }
  return yPos;
  }

function jt_currStyle(divToRead) {
  // return current (derived) CSS style object
  var cs = divToRead.style;
  if (window.getComputedStyle) cs = window.getComputedStyle(divToRead,null);
  else if (divToRead.currentStyle) cs = divToRead.currentStyle;
  return cs;
  }

function jt_width(divToRead) {
  var wide = jt_currStyle(divToRead).width;
  return (wide = 'auto') ? divToRead.offsetWidth : parseInt(wide);
  }

function jt_height(divToRead) {
  var high = jt_currStyle(divToRead).height;
  return (high = 'auto') ? divToRead.offsetHeight : parseInt(high);
  }

function jt_winW() {
  return window.innerWidth ? window.innerWidth : document.body.offsetWidth;
  }

function jt_winH() {
  return window.innerHeight ? window.innerHeight : document.body.offsetHeight;
  }

function jt_scrollTop() {
  if (window.pageYOffset) return window.pageYOffset;
  else if (document.documentElement && (document.documentElement.scrollTop > 0)) return document.documentElement.scrollTop;
  else return document.body.scrollTop;
  }
/********** END: screen handling **********/


/********** BEGIN: Event handling **********/
function jt_AddListener(obj, evType, fn) {
  if (obj.addEventListener) {
    obj.addEventListener(evType, fn, false);
    return true;
    }
  else if (obj.attachEvent) return obj.attachEvent('on' + evType, fn);
  else return false;
  }

function jt_fixE(ev) {
  var e = ev ? ev : window.event;
  return e;
  }
/********** END: Event handling **********/


/********** BEGIN: FORM handling **********/
function jt_setRadio(radioFld, val) {
  // set 'radioFld' button with value == val and return 'true' (if not disabled!)
  for (var i=0; i<radioFld.length; i++)
    if (radioFld[i].value == val)
      if (!radioFld[i].disabled) {
        radioFld[i].checked = true;
        return true;
        }
  return false;
  }

function jt_getRadio(radioFld) {
  // return value of selected 'radioFld' button
  var st = "";
  for (var i=0; i<radioFld.length; i++)
    if (radioFld[i].checked) {
      st = radioFld[i].value;
      break;
      }
  return st;
  }

function foSelected(pulldown) {
  // return value of selected item
  var st = "";
  for (var i=0; i<pulldown.options.length; i++)
  if (pulldown.options[i].selected) {
    if (pulldown.options[i].value) st = pulldown.options[i].value
    else st = pulldown.options[i].text;
    break;
    }
  return st;
  }

function foPosInList(pulldown, val) {
  // return position of 'val' in pulldown menu, -1 if not found
  if (val != "")
    for (var i=0; i<pulldown.options.length; i++) {
      var opVal = pulldown.options[i].value;
      if (opVal == "") opVal = pulldown.options[i].text;
      if (opVal == val) {
        return i;
        break;
        }
      }
  return -1;
  }

function foSetSelectVal(pulldown, val) {
  // set "SELECTED" for item in pulldown menu with 'value==val'
  var p = foPosInList(pulldown,val);
  if (p != -1) pulldown.options.selectedIndex = p;
  }

function jt_formStr(aForm) {
  var qSt = '';
  for (var i=0; i<aForm.elements.length; i++) {
    if ((aForm.elements[i].type == 'radio') || (aForm.elements[i].type == 'checkbox')) {
      if (aForm.elements[i].checked) qSt += '&' + aForm.elements[i].name + '=' + aForm.elements[i].value || 'checked';
      }
    else if (aForm.elements[i].type.indexOf('select') != -1) {
      for (var j=0; j<aForm.elements[i].options.length; j++) {
        if (aForm.elements[i].options[j].selected) qSt += '&' + aForm.elements[i].name + '=' + encodeURIComponent((aForm.elements[i].options[j].value) ? aForm.elements[i].options[j].value : aForm.elements[i].options[j].text);
        }
      }
    else if (!jt_strEmpty(aForm.elements[i].value)) qSt += '&' + aForm.elements[i].name + '=' + encodeURIComponent(aForm.elements[i].value);
    }
  return qSt;
  }
/********** END: FORM handling **********/


function jt_parseQuery(queryString) {
  // converts name/value pairs in 'queryString' to JS object
  var qObj = new Object();
  var stQuery = (queryString) ? queryString : location.search; // use 'location.search' if 'queryString' is null
  if (stQuery.indexOf("?") == 0) stQuery = stQuery.substring(1);
  if (stQuery) {
    var nvPairs = stQuery.split("&");
    for (var i=0; i < nvPairs.length; i++) {
      var posEq = nvPairs[i].indexOf("=");
      if (posEq != -1) {
        var nam = nvPairs[i].substring(0,posEq);
        if (nam.indexOf('.') == -1) qObj[nam] = nvPairs[i].substring(posEq+1);
        }
      }
    }
  return qObj;
  }

function jt_safeHTML(st) {
  // encode - same as 'u:htmlencode' tag
  if (st.length == 0) return "";
  st = st.replace(/</gi,"&lt;");
  st = st.replace(/>/gi,"&gt;");
  st = st.replace(/\"/gi,'&quot;');
  st = st.replace(/\'/gi,"&#39;");
  st = st.replace(/\\/gi,"&#92;");
  return st;
  }

function jt_unsafeHTML(st) {
  // decode - opposite of 'u:htmlencode' tag
  if (st.length == 0) return "";
  st = st.replace(/&lt;/gi,"<");
  st = st.replace(/&gt;/gi,">");
  st = st.replace(/&quot;/gi,'"');
  st = st.replace(/&#39;/gi,"'");
  st = st.replace(/&#92;/gi,"\\");
  return st;
  }

function jt_plural(qty, lbl) {
  // returns plural suffix or optional extended format if 'lbl' is present; example: qty=12, lbl="server", return value="12 servers"
  var stPlural = (qty == 1) ? "" : "s";
  if (lbl) stPlural = qty + " " + lbl + stPlural;
  return stPlural;
  }

function jt_alignCorner(elmToMove, elmAnchor, TlTrBlBr, xOffset, yOffset) {
  // aligns 'elmToMove' with 'elmAnchor' based on 2-character 'TlTrBlBr' indicating corner: 'TL' | 'TR' | 'BL' | 'BR'
  xOffset = xOffset ? xOffset : 0; // optional param
  yOffset = yOffset ? yOffset : 0; // optional param
  var anchorXY = jt_getOffsetXY(elmAnchor);
  var xxOffset = (TlTrBlBr.indexOf('R') != -1) ? elmToMove.offsetWidth - elmAnchor.offsetWidth : 0;
  var yyOffset = (TlTrBlBr.indexOf('B') != -1) ? elmToMove.offsetHeight : 0;
  jt_moveTo(elmToMove, anchorXY.x - xxOffset + xOffset, anchorXY.y - yyOffset + yOffset);
  }

function jt_divOnScrn(divOnScrn) {
  var divPos = jt_getOffsetXY(divOnScrn);
  var newX = divPos.x;
  var newY = divPos.y;
  if (divPos.x + divOnScrn.offsetWidth - document.body.scrollLeft > document.body.clientWidth) newX = document.body.scrollLeft + document.body.clientWidth - divOnScrn.offsetWidth;
  if (divPos.x < document.body.scrollLeft) newX = document.body.scrollLeft;
  if (divPos.y + divOnScrn.offsetHeight - document.body.scrollTop > document.body.clientHeight) newY = document.body.scrollTop + document.body.clientHeight - divOnScrn.offsetHeight;
  if (divPos.y < document.body.scrollTop) newY = document.body.scrollTop;
  if ((newX != divPos.x) || (newY != divPos.y)) jt_moveTo(divOnScrn, newX, newY);
  }

function jt_appendRelative(dragDIV, newParentDIV, xOffset, yOffset) {
  var dragPos = jt_getOffsetXY(dragDIV);
  var newParentPos = jt_getOffsetXY(newParentDIV);
  var xPos = dragPos.x - newParentPos.x;
  var yPos = dragPos.y - newParentPos.y;
  jt_moveTo(dragDIV, xPos + (xOffset ? xOffset : 0), yPos + (yOffset ? yOffset : 0));
  newParentDIV.appendChild(dragDIV);
  }

function jt_DelChildren(elm) {
  // remove all child nodes of 'elm' from DOM and delete them
  while (elm.hasChildNodes()) {
    var oldChild = elm.removeChild(elm.childNodes[elm.childNodes.length-1]);
    delete(oldChild);
    }
  }


// from http://www.quirksmode.org/js/detect.html
var BrowserDetect = {
  init: function () {
    this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
    this.version = this.searchVersion(navigator.userAgent) || this.searchVersion(navigator.appVersion) || "an unknown version";
    this.OS = this.searchString(this.dataOS) || "an unknown OS";
    },
  searchString: function (data) {
    for (var i=0;i<data.length;i++)  {
      var dataString = data[i].string;
      var dataProp = data[i].prop;
      this.versionSearchString = data[i].versionSearch || data[i].identity;
      if (dataString) {
        if (dataString.indexOf(data[i].subString) != -1) return data[i].identity;
        }
      else if (dataProp) return data[i].identity;
      }
    },
  searchVersion: function (dataString) {
    var index = dataString.indexOf(this.versionSearchString);
    if (index == -1) return;
    return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
    },
  dataBrowser: [
      {string: navigator.vendor, subString: "Apple", identity: "Safari"},
      {prop: window.opera, identity: "Opera"},
      {string: navigator.vendor, subString: "iCab", identity: "iCab"},
      {string: navigator.vendor, subString: "KDE", identity: "Konqueror"},
      {string: navigator.userAgent, subString: "Firefox", identity: "Firefox"},
      {string: navigator.userAgent, subString: "Netscape", identity: "Netscape"},  // for newer Netscapes (6+)
      {string: navigator.userAgent, subString: "MSIE", identity: "Explorer", versionSearch: "MSIE"},
      {string: navigator.userAgent, subString: "Gecko", identity: "Mozilla", versionSearch: "rv"},
      {string: navigator.userAgent, subString: "Mozilla", identity: "Netscape", versionSearch: "Mozilla"} // for older Netscapes (4-)
      ],
  dataOS : [
      {string: navigator.platform, subString: "Win", identity: "Windows"},
      {string: navigator.platform, subString: "Mac", identity: "Mac"},
      {string: navigator.platform, subString: "Linux", identity: "Linux"}
      ]
  };
BrowserDetect.init();

var jt_FixPNG = (BrowserDetect.browser == 'Explorer') && (BrowserDetect.version < 7);

var spImg = new Image();   spImg.src = "/images/sp.gif";

function correctPNG(imgObj) {
  // IE only - must call image loads!
  if (jt_FixPNG) {
    if (typeof imgObj == 'string') imgObj = document.getElementById(imgObj);
    else if (!imgObj) {
      if (this) imgObj = this;
      else {
        var e = jt_fixE(imgObj);
        imgObj = e.target ? e.target : e.srcElement;
        }
      }
    imgObj.onload = null;
    if (imgObj.src.indexOf("sp.gif") == -1) {
      imgObj.style.width = jt_valPx(imgObj.attributes.width.value);
      imgObj.style.height = jt_valPx(imgObj.attributes.height.value);
      imgObj.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + imgObj.src + "')";
      imgObj.src = spImg.src;
      }
    }
  }


function jt_boxOverlap(objectA, objectB, mode) {
  // box collision detector; returns area of overlap (if any) in pixels between DOM elements 'objectA' and 'objectB'
  // adapted from http://www.gamedev.net/reference/articles/article754.asp
  // 'mode' is optional - two special modes are offered:
  //   mode='X' - test for overlap of X-coordinates only; ignore Y
  //   mode='Y' - test for overlap of Y-coordinates only; ignore X
  var xyA = jt_getOffsetXY(objectA);
  var xyB = jt_getOffsetXY(objectB);

  var objAxTL = xyA.x; // AX1 - xTopLeft
  var objAyTL = xyA.y; // AY1 - yTopLeft
  var objAxBR = xyA.x + objectA.offsetWidth; // AX2 - xBottomRight
  var objAyBR = xyA.y + objectA.offsetHeight; // AY2 - yBottomRight

  var objBxTL = xyB.x; // BX1
  var objByTL = xyB.y; // BY1
  var objBxBR = xyB.x + objectB.offsetWidth; // BX2
  var objByBR = xyB.y + objectB.offsetHeight; // BY2
/*
alert("objAxTL=" + objAxTL + " - objAyTL=" + objAyTL + " - objAxBR=" + objAxBR + " - objAyBR=" + objAyBR + "\n" +
      "objBxTL=" + objBxTL + " - objByTL=" + objByTL + " - objBxBR=" + objBxBR + " - objByBR=" + objByBR);

reject the following conditions:
AX2<BX1
AY2<BY1
BX2<AX1
BY2<AY1
*/
  if (mode != 'Y') {
    if (objAxBR < objBxTL) return -1;
    if (objBxBR < objAxTL) return -1;
    }
  if (mode != 'X') {
    if (objAyBR < objByTL) return -1;
    if (objByBR < objAyTL) return -1;
    }
/*
If AX1<BX1 then CX1=BX1 and CX2=AX2, otherwise, CX1=AX1 and CX2=BX2
If AY1<BY1 then CY1=BY1 and CY2=AY2, otherwise, CY1=AY1 and CY2=BY2
*/
  var objCxTL;
  var objCyTL;
  var objCxBR;
  var objCyBR;
  if (objAxTL < objBxTL) {
    objCxTL = objBxTL;
    objCxBR = objAxBR;
    }
  else {
    objCxTL = objAxTL;
    objCxBR = objBxBR;
    }
  if (objAyTL < objByTL) {
    objCyTL = objByTL;
    objCyBR = objAyBR;
    }
  else {
    objCyTL = objAyTL;
    objCyBR = objByBR;
    }
  var olX = objCxBR - objCxTL;
  var olY = objCyBR - objCyTL;
  if (mode == 'X') return olX;
  else if (mode == 'Y') return olY;
  else return olX * olY;
  } // jt_boxOverlap


/********* BEGIN: jt_AnimCalc *********/
function jt_AnimCalc(callFunc, specs) {
  this._callFunc = callFunc;
  this.ranges = {};
  this._specs = {};
  this.setSpecs(jt_AnimCalc.defaultSpecs);
  if (specs) this.setSpecs(specs);
  }

jt_AnimCalc.prototype.setRange = function(name, from, to) {
  this.ranges[name] = {'from':from, 'to':to, 'delta':to - from};
  }

jt_AnimCalc.prototype.flipRange = function() {
  for(var prop in this.ranges) {
    var temp = this.ranges[prop].from;
    this.ranges[prop].from = this.ranges[prop].to;
    this.ranges[prop].to = temp;
    this.ranges[prop].delta = this.ranges[prop].to - this.ranges[prop].from;
    }
  }

jt_AnimCalc.prototype.start = function(callDone) {
  clearInterval(this.timer);
  this._callDone = callDone;
  this._callFunc(this.step = this._frac = 0);
	var _this = this;
  this.timer = setInterval(function() {
      _this.step++;
      _this._frac = (1 - Math.cos(_this.step * _this.piStep)) / 2;
      _this._callFunc(_this.step);
      if (_this.step == _this._specs.numSteps) {
        clearInterval(_this.timer);
        if (_this._callDone) _this._callDone();
        }
      },
      _this.interval);
  }

jt_AnimCalc.prototype.value = function(name) {
  return this.ranges[name].from + Math.round(this._frac * this.ranges[name].delta);
  }

jt_AnimCalc.prototype.setSpecs = function(specs) {
  for(var prop in specs) this._specs[prop] = specs[prop];
  this.interval = Math.round(this._specs.elapsed / this._specs.numSteps);
  this.piStep = Math.PI / this._specs.numSteps;
  }

jt_AnimCalc.defaultSpecs = {'numSteps':10, 'elapsed':333}
/********* END: jt_AnimCalc *********/


/********** BEGIN: trace feature **********/
var traceAreaDIV;
var traceAreaX = 100;
var traceAreaY = 50;
var traceAreaMax = 70;
var traceCount = 0;

function clearTrace() {
  traceAreaDIV.style.color = "#a9a9a9";
  traceAreaDIV.style.textAlign = "left";
  traceAreaDIV.innerHTML = '<a href="javascript:clearTrace()">clear trace</a><br>';
  }

function traceTxt(txt) {
  if (!traceAreaDIV) {
    traceAreaDIV = document.createElement("div");
    traceAreaDIV.style.position = "absolute";
    jt_moveTo(traceAreaDIV, traceAreaX, traceAreaY);
    document.body.appendChild(traceAreaDIV);
    }
  if (traceCount % traceAreaMax == 0) clearTrace();
  traceAreaDIV.innerHTML += txt + "<br>";
  traceCount++;
  }


var qParams = new jt_parseQuery(); // for session ID and trace

function traceMsg(msg) {
  if (qParams.trace == 1) traceTxt(msg);
  }

function traceXML(req, locMsg) {
  if (qParams.traceXML == 1) traceTxt(locMsg + "<br>" + jt_safeHTML(req.responseText).replace(/\n/gi,"<br>"));
  }
/********** END: trace feature **********/

function objToString(anObj, sep) {
  // convert any JS object to string of name:value pairs separated by optional 'sep'
  // NOTE: pass '<br />' for 'sep' to display as HTML
  if (!sep) sep = ' ][ '; // good for 'alert()' msgs
  var st = "";
  for(var prop in anObj) {
    if ((prop.charAt(0) == '$') || (prop == 'domConfig') || ((typeof anObj[prop]) == 'function')) continue;
    if (st != "") st += sep;
    st += prop + ':' + anObj[prop];
    }
  return st;
  }

