/*
 * Copyright (C) 2005 - 2007 JasperSoft Corporation.  All rights reserved.
 * http://www.jaspersoft.com.
 *
 * Unless you have purchased a commercial license agreement from JasperSoft,
 * the following license terms apply:
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 *
 * This program is distributed WITHOUT ANY WARRANTY; and without the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see http://www.gnu.org/licenses/gpl.txt
 * or write to:
 *
 * Free Software Foundation, Inc.,
 * 59 Temple Place - Suite 330,
 * Boston, MA  USA  02111-1307
 *
 *  PLEASE NOTE: There are a few public domain functions in here for opacity changing that
 *  were found at: http://www.brainerror.net/index.php
 */

/**
 * A collection of Common Utilities.
 */

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// **IMPORTANT - PLEASE READ!!!****
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// In order to keep this file organized and readable please consider which section to add your new function
// Also check if there is already something there you can use or extend
// Remeber also we have prototype.js at our disposal
//
// These are the sections:
//
// Browser Sniffing
// Generic Functions
// DOM Navigation
// Element Inspection
// Element Positioning
// Element Displaying and Effects
// Element Overlays
// Element Enabling
// Table Utils
// Window Inspection
// Window Positioning
// Window Creation
// IFRAME Utils
// Event Utils
// String Utils
// URL Utils
// Busy Monitor
// Label Editor
// <Script> utils
// CSS utils
// Dialog events
// Keyboard events
// Ajax utils
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////
// Browser Sniffing.
///////////////////////////////////////////
function isMozilla() {
  return navigator.appName=="Netscape";
}

function isIE() {
  return navigator.appName=="Microsoft Internet Explorer";
}

function isIE6() {
    if (isIE()) {
        var version = navigator.appVersion.split("MSIE");
        var version = parseFloat(version[1]);
        return version === 6;
    }
    return false;
}

function isIEVersion7Upwards() {
    if (isIE()) {
        var version = navigator.appVersion.split("MSIE");
        var version = parseFloat(version[1]);
        return version >= 7;
    }
    return false;
}

///////////////////////////////////////////
// Generic Functions
///////////////////////////////////////////

function doNothing() {
    //what it says
}

///////////////////////////////////////////
// Type checking
///////////////////////////////////////////

function exists(obj) {
    return (typeof(obj)!== "undefined") && obj !== null;
}

function isArray(obj) {
    return (obj.constructor && obj.constructor.toString().indexOf('Array') != -1);
}

///////////////////////////////////////////
// DOM Navigation
///////////////////////////////////////////
function getParentDiv(clicked) {
    return getParentWithTagName(clicked,"DIV");
}

function getParentRow(clicked) {
    return getParentWithTagName(clicked,"TR");
}

function getParentTable(clicked) {
    return getParentWithTagName(clicked,"TABLE");
}

function getAbsoluteParent(child) {
  while (!child.style || child.style.position != 'absolute') {
    child = child.parentNode;
    if (child == null) return null;
  }
  return child;
}

function getParentWithTagName(myElement,aTagName) {
    while(myElement.tagName!=aTagName) {
        myElement=myElement.parentNode;
        if(myElement.tagName=="BODY")
            return null;
    }
    return myElement;
}

function getElementWithIdAndTagAndParent(id,tagName,parentElem) {
  var elems = parentElem.getElementsByTagName(tagName);
  for (var i=0; i<elems.length; i++) {
    if (elems[i].getAttribute("id")==id)
      return elems[i];
  }
  return null;
}

function isDescendantOf(child, parent) {
    while(child.tagName != "BODY") {
        if (child==parent) {
            return true;
        }
        child = child.parentNode;
    }
    return false;
}

function getCellIndex(cell) {
    return cell.cellIndex;
}

function getRowIndex(row) {
    return row.rowIndex;
}

///////////////////////////////////////////
// Element Inspection
///////////////////////////////////////////
/**
 * deprecated - use getBoxOffsets() below
 */
function getAbsoluteOffsets(thisObj) {
    var oLeft = thisObj.offsetLeft;
    var oTop = thisObj.offsetTop;
    var thisParent = thisObj.offsetParent;
    while (thisParent.tagName.toUpperCase() != "BODY" && thisParent.style.position != "absolute") {
        var oLeft = oLeft + thisParent.offsetLeft;
        var oTop = oTop + thisParent.offsetTop;
        thisParent = thisParent.offsetParent;
    }
    //add co-ords of absolute parent
    if (thisParent.style.position == "absolute") {
      oLeft = oLeft + thisParent.offsetLeft;
      oTop = oTop + thisParent.offsetTop;
    }
        if (isIE()) {
            //minor adjustment because IE handles offset slightly differently;
            //oLeft=oLeft+1;
            oTop=oTop+1;
        }
    return new Array(oLeft,oTop, oLeft + thisObj.offsetWidth, oTop + thisObj.offsetHeight);
}

/**
 * deprecated - use getBoxOffsets()[1] below
 * but keep this as a fallthrough method
 */
function getAbsoluteTopOffset(thisObj) {
    var oTop = thisObj.offsetTop;
    var thisParent = thisObj.offsetParent;
    while (thisParent.tagName.toUpperCase() != "BODY" && thisParent.style.position != "absolute") {
        var oTop = oTop + thisParent.offsetTop;
        thisParent = thisParent.offsetParent;
    }
    //add co-ords of absolute parent
    if (thisParent.style.position == "absolute") {
      oTop = oTop + thisParent.offsetTop;
    }
    if (isIE()) {
      //minor adjustment because IE handles offset slightly differently;
      oTop=oTop+1;
    }
    return oTop;
}

/**
 * reimplemenation of getAbsoluteOffsets()
 * but leaving original for now in case results vary slightly
 */
function getBoxOffsets(thisObj) {
    if (document.getBoxObjectFor) {
       var box = document.getBoxObjectFor(thisObj);
       posLeft = box.x;
       posTop = box.y;
    }
    else if (thisObj.getBoundingClientRect) {
       var box = thisObj.getBoundingClientRect();
       posLeft = box.left;
       posTop = box.top;
    } else {
        return getAbsoluteOffsets(thisObj);
    }
    return new Array(posLeft,posTop);
}

function isPointOverlayingObject(x,y,obj) {
    var offsets = getAbsoluteOffsets(obj);
    //window.status = offsets[0]+'-'+x+'-'+offsets[2]+'-'+offsets[1]+'-'+y+'-'+offsets[3];
    return x>offsets[0]&&x<offsets[2]&&y>offsets[1]&&y<offsets[3];
}

function boxesOverlap(leftA, topA, rightA, bottomA,leftB, topB, rightB, bottomB) {
    return !((topA>bottomB) || (bottomA<topB) || (leftA>rightB) || (rightA<leftB));
}

///////////////////////////////////////////
// Element Detection
///////////////////////////////////////////

/**
 * Answer true if the DOM element is nearly scrolled into view
 * @param {Object} theElem - dom elem to check for
 * @param {Object} offset - how close is almost (in pixels)
 */
function isAlmostInView(theElem, offset) {
  var theElemTop = parseInt(getBoxOffsets(theElem)[1]);
  var pageTop = $(theElem).cumulativeScrollOffset()[1];
  var viewableHeight = getWindowHeight();
  return theElemTop < pageTop + viewableHeight + offset;
}


///////////////////////////////////////////
// Element Positioning
///////////////////////////////////////////

// Note assumes obj display is NOT none
function centerLayer(obj) {
  obj.style.left=((getWindowWidth()-parseInt(obj.clientWidth))/2)+getScrollLeft();
  obj.style.top=((getWindowHeight()-parseInt(obj.clientHeight))/2)+getScrollTop();
}

//left, top, width, height are optional overrides
function fitObjectIntoScreen(obj, thisLeft, thisTop, thisWidth, thisHeight) {
    var l = thisLeft ? parseInt(thisLeft) : parseInt(obj.style.left);
    var t = thisTop ? parseInt(thisTop) : parseInt(obj.style.top);
    var w = thisWidth ? parseInt(thisWidth) : parseInt(obj.style.width);
    var h = thisHeight ? parseInt(thisHeight) : parseInt(obj.style.height);
    var sl = getScrollLeft();
    var st = getScrollTop();
    var sw = getWindowWidth();
    var sh = getWindowHeight();

    //scrollbar adjustment
    w = w + 20;
    h = h + 20;

    if (t + h > st + sh) t = st + sh - h;
    if (t < st) t = st;
    if (l + w > sl + sw) l = sl + sw - w;
    if (l < sl) l = sl;

    obj.style.left = l + "px";
    obj.style.top = t + "px";
}

function animatedMove(movingThing, fromX, fromY, toX, toY, stepSize) {
    var xOffset = Math.abs(toX-fromX);
    var yOffset = Math.abs(toY-fromY);
    var biggerOffset = Math.max(xOffset,yOffset);

    //if no step size then auto size it so there are 20 steps
    if (!stepSize) {
        stepSize = biggerOffset/20;
    }

    var increments = biggerOffset/stepSize;
    var count=0;

    if ((fromX==toX)&&(fromY==toY)) {
        //nothing to do
        return;
    }

    var xStep = xOffset/increments;
    var yStep = yOffset/increments;

    function moveIt() {
        if (count<increments) {
            count++;
            movingThing.style.left = parseInt(movingThing.style.left) + xStep;
            movingThing.style.top = parseInt(movingThing.style.top) + yStep;
            setTimeout(moveIt,0);
        } else {
            //finish off
            movingThing.style.left = toX;
            movingThing.style.top = toY;
        }
    }

    moveIt();

}

//
// this one fires of a series of increments at fized intervals
// theoretecially this should iron out browser delays
//
function animatedMoveFixedTimer(movingThing, fromX, fromY, toX, toY, stepSize) {
    var xOffset = Math.abs(toX-fromX);
    var yOffset = Math.abs(toY-fromY);
    var biggerOffset = Math.max(xOffset,yOffset);

    //if no step size then auto size it so there are 20 steps
    if (!stepSize) {
        stepSize = biggerOffset/20;
    }

    var increments = biggerOffset/stepSize;
    var count=0;

    if ((fromX==toX)&&(fromY==toY)) {
        //nothing to do
        return;
    }

    var xStep = xOffset/increments;
    var yStep = yOffset/increments;


    function moveIt() {
        movingThing.style.left = parseInt(movingThing.style.left) + xStep;
        movingThing.style.top = parseInt(movingThing.style.top) + yStep;
    }

    for (count; count<increments; count++) {
        setTimeout(moveIt,10*count);
    }

    //finish off
    //movingThing.style.left = toX;
    //movingThing.style.top = toY;

}


///////////////////////////////////////////
// Element Displaying and Effects
///////////////////////////////////////////

function toggleDisplay(elem) {
    if (elem.style.display=="none")
        elem.style.display="block";
    else
        elem.style.display="none";
}

function focusOn(id) {
    document.getElementById(id).focus();
}

function selectAndFocusOn(id){
    var el = document.getElementById(id);
    el.select();
    el.focus();
}

function renderImageOverCursor(imgId, event) {
    var img = document.getElementById(imgId);
    img.style.display="block";
    img.style.left=event.clientX;
    img.style.top=event.clientY;
}

function hideImageOverCursor(imgId) {
    var img = document.getElementById(imgId);
    img.style.display="none";
}

function fadeInOrOut(id, opacStart, opacEnd, millisecDelay) {
    //speed for each frame
    var speed = Math.round(millisecDelay / 100);
    var timer = 0;

    //determine the direction for the blending, if start and end are the same nothing happens
    if(opacStart > opacEnd) {
        for(i = opacStart; i >= opacEnd; i--) {
            setTimeout("changeOpac(" + i + ",'" + id + "')",(timer * speed));
            timer++;
        }
    } else if(opacStart < opacEnd) {
        for(i = opacStart; i <= opacEnd; i++)
            {
            setTimeout("changeOpac(" + i + ",'" + id + "')",(timer * speed));
            timer++;
        }
    }
}

//change the opacity for different browsers
function changeOpac(opacity, id) {
    var object = document.getElementById(id).style;
    object.opacity = (opacity / 100);
    object.MozOpacity = (opacity / 100);
    object.KhtmlOpacity = (opacity / 100);
    object.filter = "alpha(opacity=" + opacity + ")";
}

//
// blinking things is an array
//
function simpleBlink(blinkingThings, blinkTime, numberOfBlinks) {

    for (var index=0; index<blinkingThings.length; index++) {
        var blinkingThing = blinkingThings[index];
        blinkingThing.style.display = "none";
    }

    var count = 0;

    function toggleBlink() {
        var firstBlinkingThing = blinkingThings[0];
        if (firstBlinkingThing.style.display == "none") {
        for (var index=0; index<blinkingThings.length; index++) {
                var blinkingThing = blinkingThings[index];
                blinkingThing.style.display = "block";
            }
        } else {
            for (var index=0; index<blinkingThings.length; index++) {
                var blinkingThing = blinkingThings[index];
                blinkingThing.style.display = "none";
            }
            count++;
        }
        if (count>numberOfBlinks) {
            clearInterval(blinker);
            for (var index=0; index<blinkingThings.length; index++) {
                var blinkingThing = blinkingThings[index];
                blinkingThing.style.display = "block";
            }
        }
    }

    var blinker = window.setInterval(toggleBlink,blinkTime);

}

//
// blinking things is an array
//
function blinkWithClasses(blinkingThings, oldClassName, blinkClassName, finalClassName, blinkTime, numberOfBlinks) {

    for (var index=0; index<blinkingThings.length; index++) {
        var blinkingThing = blinkingThings[index];
        blinkingThing.className = oldClassName;
    }

    var count = 0;

    function toggleBlink() {
        var firstBlinkingThing = blinkingThings[0];
        if (firstBlinkingThing.className == oldClassName) {
            for (var index=0; index<blinkingThings.length; index++) {
                var blinkingThing = blinkingThings[index];
                blinkingThing.className = blinkClassName;
            }
        } else {
            for (var index=0; index<blinkingThings.length; index++) {
                var blinkingThing = blinkingThings[index];
                blinkingThing.className = oldClassName;
            }
            count++;
        }
        if (count>numberOfBlinks) {
            clearInterval(blinker);
            for (var index=0; index<blinkingThings.length; index++) {
                var blinkingThing = blinkingThings[index];
                blinkingThing.className = finalClassName
            }
        }
    }

    var blinker = window.setInterval(toggleBlink,blinkTime);

}

///////////////////////////////////////////
// Element Overlays
///////////////////////////////////////////

//haze out the entire usable page
function renderHazeLayer(left,top) {
  var theBody = document.body;
  return renderOverlay(left,top,theBody.scrollWidth,theBody.scrollHeight,'haze')
}

function renderOverlay(left,top,width,height,style) {
    var overlayObject = document.createElement("DIV");
    overlayObject.className=style;
    overlayObject.style.position="absolute";
    //overlayObject.style.zIndex=80;
    overlayObject.style.left=left;
    overlayObject.style.top=top;
    overlayObject.style.width=width;
    overlayObject.style.height=height;
    document.body.appendChild(overlayObject);

    return overlayObject;
}

function removeOverlay(overlayObject) {
    if (overlayObject && overlayObject.parentNode) {
        overlayObject.parentNode.removeChild(overlayObject);
    }
    if (overlayObject) {
        overlayObject = null;
    }
}

/**
 * set obj as a child of the Parent
 * if parent already set do nothing
 * @param {Object} obj
 * @param {Object} newParent
 */
function reParent(obj, newParent) {
    if (!obj) {
        return;
    }
    if (!obj.parentNode || (obj.parentNode != newParent)) {
        newParent.appendChild(obj);
    }
}

/**
 * Use this for positioning an overlay parented by an absolute element (not body)
 * IE needs to take scroll into account, Mozilla doesn't  
 * @param {Object} target
 */
function getScrollLeftForAbsoluteParent(parentObj) {
    return isIE() ? parentObj.scrollLeft : 0;    
}
function getScrollTopForAbsoluteParent(parentObj) {
    return isIE() ? parentObj.scrollTop : 0;    
}

//////////////////////////////////////
// Element Enabling
//////////////////////////////////////
function disableSelection(target){
  if (target) {
      if (typeof target.onselectstart!="undefined") //IE route
        target.onselectstart=function(){return false}
      else if (typeof target.style.MozUserSelect!="undefined") //Firefox route
        target.style.MozUserSelect="none"
      else //All other route (ie: Opera)
        ;//target.onmousedown=function(){return false}
      target.style.cursor = "default"
   }
}


function disableSelectionWithoutCursorStyle(target){
  if (target) {
      if (typeof target.onselectstart!="undefined") //IE route
        target.onselectstart=function(){return false}
      else if (typeof target.style.MozUserSelect!="undefined") //Firefox route
        target.style.MozUserSelect="none"
      else //All other route (ie: Opera)
        ;//target.onmousedown=function(){return false}
   }
}


function enableSelection(target){
  if (target) {
      if (typeof target.onselectstart != "undefined") {
          //IE route
          target.onselectstart = function(){
              return true
          }    
      } else if (typeof target.style.MozUserSelect != "undefined") {
            //Firefox route
            target.style.MozUserSelect = "text"
      } else {
        //All other route (e.g. Opera)
      }  
      target.style.cursor = "default"
   }
}

///////////////////////////////////////////
// Table Utils
///////////////////////////////////////////

function getCellAtColumn(cells,columnIndex) {
  columnCount = 0;
  for (i=0;i<cells.length;i++) {
    columnCount += cells.colspan;
    if (columnCount>=columnIndex)
      return cells[i];
  }
  return null;
}

// columnIndex indicates which column (within the supplied rows) we are interested in
function getCellAtRow(rows,columnIndex,rowIndex) {
  rowCount = 0;
  for (i=0;i<rows.length;i++) {
    var thisCell = rows[i].cells[columnIndex];
    rowCount += thisCell.rowspan;
    if (rowCount>=rowIndex)
      thisCell;
  }
  return null;
}

//fromT and toT can be TABLE or TBODY
function copyTable(fromT,toT,replace,paramsAreTBody) {
    var toBody= paramsAreTBody ? toT : toT.getElementsByTagName('TBODY')[0];
    var copyRows = fromT.rows;
    if (replace) {
        clearTable(toT);
    }
    try {
        toT.appendChild(fromT.getElementsByTagName('TBODY')[0]);
    } catch (e) {
        //nothing to copy?
    }
}

function clearTable(myTable) {
    if (document.all) {
        while(myTable.rows[0]) {
          myTable.deleteRow(0);
        }
    } else {
        myTable.innerHTML = "";
    }
}

///////////////////////////////////////////
// Window Inspection
///////////////////////////////////////////

function getWindowWidth() {
    if (isMozilla())
        return innerWidth;
  return document.body.clientWidth;
}

function getWindowHeight() {
  if (isMozilla())
        return innerHeight;
  return document.body.clientHeight;
}

function getScrollLeft() {
    if (isMozilla()) {
        return window.scrollX;
    }
    return document.body.scrollLeft;
}

function getScrollTop() {
    if (isMozilla()) {
        return window.scrollY;
    }
    return document.body.scrollTop;
}

///////////////////////////////////////////
// Window Positioning
///////////////////////////////////////////

function scrollXToMiddle(theBody) {
  //scroll to half the difference between page width and window width (if any)
  var excess = theBody.scrollWidth-getWindowWidth();
  if (excess>0)
    theBody.scrollLeft=excess/2;
}

///////////////////////////////////////////
// Window Creation
///////////////////////////////////////////

function launchNewWindow(url) {
    window.open(url);
}

///////////////////////////////////////////
// IFRAME Utils
///////////////////////////////////////////

function getIFrameDocument(iFrame) {
    var doc = iFrame.contentWindow || iFrame.contentDocument;
    if (doc.document) {
        doc = doc.document;
    }
    return doc;
}


///////////////////////////////////////////
// Event Utils
///////////////////////////////////////////

function getEvent(evt) {
  return (evt != null) ? evt : window.event;
}

//used to smother an event
function popEvent(e) {
    cancelEventBubbling(e?e:event);
}

function cancelEventBubbling(e)
{
    var e = e?e:event;
    e.cancelBubble = true;
    if (e.stopPropagation) e.stopPropagation();
}

function enterKeyHit(evt) {
        var charCode = whichKeyHit(evt)
    if (charCode == 13 || charCode == 3)
        return true;
    return false;
}

function whichKeyHit(evt) {
    evt = (evt) ? evt : window.event;
    var charCode = (evt.charCode) ? evt.charCode :
        ((evt.which) ? evt.which : evt.keyCode);
        return charCode;
}

function isCtrlHeld(evt) {
    return !!(evt ? evt.ctrlKey:event.ctrlKey);
}

function getEventTarget(evt) {
    var e = getEvent(evt);
    var target;
    if (e.target)
        target = e.target;
    else if (e.srcElement)
        target = e.srcElement;
    return target;
}

////////////////////////////////////////
// String Utils
////////////////////////////////////////

function trim(thisStr) {
 // skip leading and trailing whitespace
 // and return everything in between
  thisStr=thisStr.replace(/^\s*(.*)/, "$1");
  thisStr=thisStr.replace(/(.*?)\s*$/, "$1");
  return thisStr;
}

function startsWith(string1,string2) {
  return string1.indexOf(string2)==0;
}

function getTextAfterUnderscore(theText) {
  return getTextAfterSubstring(theText,'_')
}

function getTextBeforeUnderscore(theText) {
  return getTextBeforeSubstring(theText,'_')
}

function getTextAfterSubstring(theText,theSubstring) {
  var ssIndex = (theText.toLowerCase()).indexOf(theSubstring.toLowerCase());
  if (ssIndex==-1)
    return null;
  else
    return theText.substring(ssIndex+theSubstring.length);
}

function getTextBeforeSubstring(theText,theSubstring) {
  var ssIndex = (theText.toLowerCase()).indexOf(theSubstring.toLowerCase());
  if (ssIndex==-1)
    return theText;
  else
    return theText.substring(0,ssIndex);
}

function removeTrailingSlash(theString) {
    var lastChar = theString.substring(theString.length-1);
    if (lastChar == '/') {
        return theString.substring(0,theString.length-1);
    } else {
        return theString;
    }
}

function removeChars(theString, charArray) {
    var i=0;
    for (i;i<charArray.length;i++) {
        while (theString.indexOf(charArray[i])>-1) {
            theString = theString.replace(charArray[i],"");
        }
    }
    return theString;
}

/**
 * replaces all instances of str1 in str with str2
 * note: assumes str2 does not contain str1!
 */
function replaceAll(str,str1,str2) {
    while (str.indexOf(str1)>-1) {
        str = str.replace(str1,str2);
    }
    return str;
}

/**
 * @deprecated - user protoype's hash
 * var h = new Hash({ a: 'apple', b: 'banana', c: 'coconut' });
 * h.get('a'); // -> 'apple'
 */
// Used to create maps from JSON string, takes any even number of parameters
// var a = Hash(key1, value1, key2, value2);
function Hash() {
    var ret = new Array();
    ret.clazz = 'Hash';
    for (var i = 0; i < arguments.length; i += 2) {
        if (typeof(arguments[i + 1]) != 'undefined') {
            ret[arguments[i]] = arguments[i + 1];
            ret.push(arguments[i]);
        }
    }
    return ret;
}

///////////////////////////////////////////////////
// URL utils
///////////////////////////////////////////////////

/**
 * if no protocol specified assume http://
 */
function checkURLProtocol(urlString, enforcePrefix) {
    if (!enforcePrefix) {
        return urlString;
    }
    if (startsWith(urlString,"/")) {
        //relative URL
        return urlString.substring(1);
    }

    var commonProtocols = new Array("http://","https://","file://","mailto://","ftp://");
    for (var i=0; i<commonProtocols.length; i++) {
        if (startsWith(urlString,commonProtocols[i])) {
            return urlString;
        }
    }
    //no protocol specified so assume http://
    return commonProtocols[0] + urlString;
}

/**
 * do we use a '?'or an '&' or nothing?
 */
function getSymbolToAppendNextParam(urlSoFar) {
    var lastChar = urlSoFar.substring(urlSoFar.length-1)
    if ((lastChar == '?')||(lastChar == '&')) {
        return "";
    }
    if (urlSoFar.indexOf('?')>-1) {
        return "&";
    } else {
        return "?";
    }

}

/////////////////////////////////////////////////////////////////////////////////////
// Busy Monitor
/////////////////////////////////////////////////////////////////////////////////////

/**
 * monitors for run requests, and only invokes function after specified quiet time
 */
function BusyMonitor(waitTime,theFunction) {
    this.waitTime = waitTime;
    this.theFunction = theFunction;
    this.timer = null;
}

BusyMonitor.prototype.run = function() {
    if (this.timer != null) {
        //recent activity - so start a new timeout
        clearTimeout(this.timer);
    }
    this.timer = setTimeout(this.theFunction,this.waitTime);
}

////////////////////////////////////////
// Label Editor
////////////////////////////////////////

function LabelEditor(){
    this.inputElementTemplate;
    this.editingLabel;
}

LabelEditor.prototype.switchLabelToInput = function(parentCell, onBlurFunction, onKeyDownFunction){
    if (!this.inputElementTemplate)
        this.inputElementTemplate = document.createElement("INPUT");
    var thisInput = this.inputElementTemplate.cloneNode(false);
    thisInput.type = "text";
    thisInput.value = ((parentCell.textContent !== undefined)  ? parentCell.textContent : parentCell.innerText).strip();
    if (thisInput.value == 'undefined')
        thisInput.value = '';
    thisInput.tabIndex = 1;
    thisInput.onmouseup = popEvent;
    thisInput.onblur = onBlurFunction;
    thisInput.onkeydown = onKeyDownFunction;
    thisInput.style.width = Math.max(parentCell.offsetWidth, 50);
    parentCell.innerHTML = '';
    parentCell.appendChild(thisInput);
    thisInput.focus();
    thisInput.select();
    thisInput.setAttribute('id', 'editingLabel');
    this.editingLabel = 'editingLabel';
}

//assumes input was created by switchLabelToInput() above
LabelEditor.prototype.returnInputToLabel = function(parentCell) {
    var thisInputElem = parentCell.firstChild;
    thisInputElem.onblur=doNothing;
    var inputValue = thisInputElem.value?thisInputElem.value:'';
    parentCell.innerHTML=inputValue;
    this.editingLabel = false;
}



//////////////////////////////////////////
// <Script> utils
//////////////////////////////////////////

function evaluateScripts(containerObj){
    var root;
    if (!containerObj) {
        root = document;
    } else {
        root = $(containerObj);
    }
    var scripts = root.getElementsByTagName('SCRIPT');
    if (scripts != null) {
        for (var i = 0; i < scripts.length; i++) {
            var script = scripts[i];
            if (script && script.text) {
                window.eval(script.text);
            }
        }
    }
}

/**
 * @param {Array} scriptLibraryArray
 */
function loadJSFiles(scriptLibraryArray){
    if (scriptLibraryArray && Object.isArray(scriptLibraryArray)) {
        scriptLibraryArray.each(function(thisFile) {loadJSFile(thisFile)});
    }
}

function loadJSFile(thisFile){
    //unload the old one if any (tags only - for clean up)...
    if ($(thisFile)) {
        $(thisFile).parentNode.removeChild($(thisFile));    
    }
    var script = new Element("script");
    script.setAttribute("id", thisFile);
    script.setAttribute("type", "text/javascript");
    script.setAttribute("src", thisFile);
    $$("head")[0].insert(script,'bottom');
}

/**
 * only unloads the script tag
 */
function unloadAllScripts(){
    var scripts = document.getElementsByTagName('SCRIPT');
    if (scripts != null) {
        for (var i = 0; i < scripts.length; i++) {
            var script = scripts[i];
            script.parentNode.removeChild(script);
        }
    }
}

//////////////////////////////////////////
// CSS utils
//////////////////////////////////////////

/**
 * @param {Array} cssFileArray
 * @param addToBody - if true appends CSS to body (if possible), if false to head 
 */
function loadCSSFiles(cssFileArray, addToBody){
    if (cssFileArray && Object.isArray(cssFileArray)) {
        cssFileArray.each(function(thisFile) {loadCSSFile(thisFile, addToBody)});
    }
}

function loadCSSFile(thisFile, addToBody){
    //unload existing versions of this file so that new ones are at bottom of file
    unloadCSSFile(thisFile);

    var cssLink = new Element("link");
    cssLink.setAttribute('id', escape(thisFile));
    cssLink.setAttribute("rel", "stylesheet");
    cssLink.setAttribute("type", "text/css");
    cssLink.setAttribute("href", thisFile);
    if (addToBody && document.body) {
        document.body.appendChild(cssLink);            
    } else {
        $$("head")[0].insert(cssLink, 'bottom');            
    }    
}

/**
 * @param {Array} cssFileArray
 */
function unloadCSSFiles(cssFileArray){
    if (cssFileArray && Object.isArray(cssFileArray)) {
        cssFileArray.each(function(thisFile) {unloadCSSFile(thisFile)});
    }
}

function unloadCSSFile(thisFile){
    if ($(escape(thisFile))) {
        try {
            $(escape(thisFile)).parentNode.removeChild($(escape(thisFile)));
        }
        catch (ex) {
            alert (ex);
        }
    }
}

////////////////////////////
// Dialog events
////////////////////////////

/**
 * NOTE: requires drag.js library
 * @param {Object} event
 */
function dialogOnMouseDown(event) {
  var evt = (event != null) ? event : window.event;

  //if the target is the same as the one saved by cancelEventDrag(), do not initiate drag
  if (cancelEventDragTarget && cancelEventDragTarget == getEventTarget(evt)) {
    return;
  }

  new Dragger(
    evt,
    [getAbsoluteParent(window.event ? evt.srcElement : evt.target)],
    true,
    true,
    2,
    new DragListener());
}

//used by cancelEventDrag() to save the target for which drag is to be cancelled
var cancelEventDragTarget = null;

function cancelEventDrag(event) {
    //save the event target
    cancelEventDragTarget = getEventTarget(event);
}

///////////////////////////////
// Keyboard events
///////////////////////////////

// Contains all key codes
Keys = new Object();

Keys.DOM_VK_BACK_SPACE = 8;
Keys.DOM_VK_ENTER = 13;
Keys.DOM_VK_PAGE_UP = 33;
Keys.DOM_VK_DOWN = 40;
Keys.DOM_VK_INSERT = 45;
Keys.DOM_VK_DELETE = 46;
Keys.DOM_VK_0 = 48;
Keys.DOM_VK_9 = 57;
Keys.DOM_VK_NUMPAD0 = 96;
Keys.DOM_VK_NUMPAD9 = 105;
Keys.DOM_VK_DECIMAL = 110;
Keys.DOM_VK_COMMA = 188;
Keys.DOM_VK_PERIOD = 190;
Keys.DOM_VK_CANCEL = 3;
Keys.DOM_VK_HELP = 6;
Keys.DOM_VK_TAB = 9;
Keys.DOM_VK_CLEAR = 12;
Keys.DOM_VK_RETURN = 13;
Keys.DOM_VK_SHIFT = 16;
Keys.DOM_VK_CONTROL = 17;
Keys.DOM_VK_ALT = 18;
Keys.DOM_VK_PAUSE = 19;
Keys.DOM_VK_CAPS_LOCK = 20;
Keys.DOM_VK_ESCAPE = 27;
Keys.DOM_VK_SPACE = 32;
Keys.DOM_VK_PAGE_DOWN = 34;
Keys.DOM_VK_END = 35;
Keys.DOM_VK_HOME = 36;
Keys.DOM_VK_LEFT = 37;
Keys.DOM_VK_UP = 38;
Keys.DOM_VK_RIGHT = 39;
Keys.DOM_VK_PRINTSCREEN = 44;
Keys.DOM_VK_1 = 49;
Keys.DOM_VK_2 = 50;
Keys.DOM_VK_3 = 51;
Keys.DOM_VK_4 = 52;
Keys.DOM_VK_5 = 53;
Keys.DOM_VK_6 = 54;
Keys.DOM_VK_7 = 55;
Keys.DOM_VK_8 = 56;
Keys.DOM_VK_SEMICOLON = 59;
Keys.DOM_VK_EQUALS = 61;
Keys.DOM_VK_A = 65;
Keys.DOM_VK_B = 66;
Keys.DOM_VK_C = 67;
Keys.DOM_VK_D = 68;
Keys.DOM_VK_E = 69;
Keys.DOM_VK_F = 70;
Keys.DOM_VK_G = 71;
Keys.DOM_VK_H = 72;
Keys.DOM_VK_I = 73;
Keys.DOM_VK_J = 74;
Keys.DOM_VK_K = 75;
Keys.DOM_VK_L = 76;
Keys.DOM_VK_M = 77;
Keys.DOM_VK_N = 78;
Keys.DOM_VK_O = 79;
Keys.DOM_VK_P = 80;
Keys.DOM_VK_Q = 81;
Keys.DOM_VK_R = 82;
Keys.DOM_VK_S = 83;
Keys.DOM_VK_T = 84;
Keys.DOM_VK_U = 85;
Keys.DOM_VK_V = 86;
Keys.DOM_VK_W = 87;
Keys.DOM_VK_X = 88;
Keys.DOM_VK_Y = 89;
Keys.DOM_VK_Z = 90;
Keys.DOM_VK_CONTEXT_MENU = 93;
Keys.DOM_VK_NUMPAD1 = 97;
Keys.DOM_VK_NUMPAD2 = 98;
Keys.DOM_VK_NUMPAD3 = 99;
Keys.DOM_VK_NUMPAD4 = 100;
Keys.DOM_VK_NUMPAD5 = 101;
Keys.DOM_VK_NUMPAD6 = 102;
Keys.DOM_VK_NUMPAD7 = 103;
Keys.DOM_VK_NUMPAD8 = 104;
Keys.DOM_VK_MULTIPLY = 106;
Keys.DOM_VK_ADD = 107;
Keys.DOM_VK_SEPARATOR = 108;
Keys.DOM_VK_SUBTRACT = 109;
Keys.DOM_VK_SUBTRACT_UNDERSCORE_IE = 189;
Keys.DOM_VK_DIVIDE = 111;
Keys.DOM_VK_F1 = 112;
Keys.DOM_VK_F2 = 113;
Keys.DOM_VK_F3 = 114;
Keys.DOM_VK_F4 = 115;
Keys.DOM_VK_F5 = 116;
Keys.DOM_VK_F6 = 117;
Keys.DOM_VK_F7 = 118;
Keys.DOM_VK_F8 = 119;
Keys.DOM_VK_F9 = 120;
Keys.DOM_VK_F10 = 121;
Keys.DOM_VK_F11 = 122;
Keys.DOM_VK_F12 = 123;
Keys.DOM_VK_F13 = 124;
Keys.DOM_VK_F14 = 125;
Keys.DOM_VK_F15 = 126;
Keys.DOM_VK_F16 = 127;
Keys.DOM_VK_F17 = 128;
Keys.DOM_VK_F18 = 129;
Keys.DOM_VK_F19 = 130;
Keys.DOM_VK_F20 = 131;
Keys.DOM_VK_F21 = 132;
Keys.DOM_VK_F22 = 133;
Keys.DOM_VK_F23 = 134;
Keys.DOM_VK_F24 = 135;
Keys.DOM_VK_NUM_LOCK = 144;
Keys.DOM_VK_SCROLL_LOCK = 145;
Keys.DOM_VK_SLASH = 191;
Keys.DOM_VK_BACK_QUOTE = 192;
Keys.DOM_VK_OPEN_BRACKET = 219;
Keys.DOM_VK_BACK_SLASH = 220;
Keys.DOM_VK_CLOSE_BRACKET = 221;
Keys.DOM_VK_QUOTE = 222;
Keys.DOM_VK_META = 224;

///////////////////////////////////////////
// Ajax utils
///////////////////////////////////////////
function encodeUriParameter(string) {
    return encodeURIComponent(encodeURIComponent(string));
}
