//=======================================================
//		UTILS LIBRARY
//=======================================================
//
// Author:
//		Ionut Durbaca (Ion)
//
// Dependencies:
//      - images/loading.gif: see utils_setFeedback()
//
// Last update:
//		July 15, 2010
//
//=======================================================
var gUtils_FeedbackType_INFO = 0;
var gUtils_FeedbackType_WARN = 1;
var gUtils_FeedbackType_ERR = 2;
var gUtils_FeedbackType_OK = 100;

var gUtils_DDL_PLEASESELECT = 'Select...'; //See OpticConfig.cs/DDL_PLEASESELECT, the values must match

var gShowPageLimitAtRight = false; //Must be 'false' in Production mode 
//See utils_DEBUG_displayRightLimitForLetterPageFormatInLandscapeMode()

var gCustomDateChooser_Disabled = false;

var gLogEnabled = true;
var gLogObj = 0;
var gLogX = 0;
var gLogY = 0;
var gLogWidth = 1200;
var gLogHeight = 140;
var gLogZIndex = 100000;
var gLogOpacity = 0.80;
var gLogForeColor = '#FFEE99';
var gLogBkColor = '#000070';

var gMouseX = 0;
var gMouseY = 0;
document.onmousemove = utils_document_MouseMoved;

//the following used to animate opacity, see utils_animateOpacities() function
var gOpacityAnimation_Objects = new Array();
var gOpacityAnimation_TargetOpacities = new Array();
var gOpacityAnimation_OpacityChangeRates = new Array();
var gOpacityAnimation_StepMSec = 10;
var gOpacityAnimation_Lock = false;
var gUtils_indexIdHookMessages = 0;

var gUtils_lastOpenedPopupWindow = 0;
var gUtils_preloadedImgObjArray = 0;

var gTimedOperations = new XHashMap();
var gTimedOperationAutoId = 0;

var gUtils_KEYCODE_DELETE = 46;
var gUtils_KEYCODE_ENTER = 13;
var gUtils_KEYCODE_SHIFT = 16;
var gUtils_KEYCODE_CTRL = 17;
var gUtils_KEYCODE_ALT = 18;

var gUtils_KEYDOWN_SHIFT = false;
var gUtils_KEYDOWN_CTRL = false;
var gUtils_KEYDOWN_ALT = false;

var gUtils_ENCODED_CR = '&#xD;'; //carriage return (13)
var gUtils_ENCODED_LF = '&#xA;'; //line feed (10)
var gUtils_ENCODED_CRCR = '&#xD;&#xD;';
var gUtils_ENCODED_LFLF = '&#xA;&#xA;';
var gUtils_ENCODED_CRLF = '&#xD;&#xA;';
var gUtils_ENCODED_CRspaceCR = '&#xD; &#xD;';
var gUtils_ENCODING_tmpBRreplacement = '[#BR]';
var gUtils_ENCODING_QUOTE = '&#39;';
var gUtils_ENCODING_DBLQUOTE = '&#34;';
var gUtils_ENCODING_SPECIALDBLQUOTE = '&#168;'; //¨

var gXTreeMap_NOTFOUND = -1234567890;
var gXHashMap_NOTFOUND = gXTreeMap_NOTFOUND;

var gUtils_BrowserIsGoogleChrome; // true/false
try {
    gUtils_BrowserIsGoogleChrome =
        navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
}
catch (e) {
    gUtils_BrowserIsGoogleChrome = false;
}
//-------------------------------------------------------
function utils_findHtmlObject(id, doc) {
    return utils_$(id, doc);
}
//-------------------------------------------------------
function utils_$(id, doc) {
    if (!doc)
        doc = document;

    // quick browser tests
    //var ns4 = (doc.layers) ? true : false;
    //var ie4 = (doc.all && !doc.getElementById) ? true : false;
    //var ie5 = (doc.all && doc.getElementById) ? true : false;
    //var ns6 = (!doc.all && doc.getElementById) ? true : false;

    if (doc.getElementById)
        return doc.getElementById(id);
    else if (doc.all)
        return doc.all[id];
    else
        return null;
}
//-------------------------------------------------------
function utils_getHtmlElementAbsRectangleById(htmlId) {
    return utils_$rectById(htmlId);
}
//-------------------------------------------------------
function utils_getHtmlElementAbsRectangle(element) {
    return utils_$rect(element);
}
//-------------------------------------------------------
function utils_$rectById(htmlId) //former "utils_$rectById"
{
    return utils_$rect(utils_$(htmlId));
}
//-------------------------------------------------------
function utils_$rect(element) //former "utils_$rect"
{
    if (!element)
        alert("utils_$rect: element is null.");
    else
        return aux_utils_$rect(element);
}
//-------------------------------------------------------
function aux_utils_$rect(element) {
    var rx = element.offsetLeft;
    var ry = element.offsetTop;

    if (element.offsetParent) {
        var tmp = aux_utils_$rect(element.offsetParent);

        rx += tmp.x;
        ry += tmp.y;
    }

    var r = { x: rx, y: ry, width: element.offsetWidth, height: element.offsetHeight };

    if (element.scrollTop) {
        r.height -= element.scrollTop;
    }

    return r;
};
//-------------------------------------------------------
function utils_refreshParent(opacityStep) {
    if (opener && opener.document.forms[0]) {
        if ((opacityStep > 0) && (opacityStep < 0))
            opacityStep = 1.0;

        //opener.document.forms[0].submit();
        utils_blindDocument(
			opener.document,
			opacityStep,
			1.0,
			function() {
			    opener.document.location.reload();
			}
		);
    }
}
//-------------------------------------------------------
/**
* "Blinds" a document by applying a 100% by 100% div - by animating div opacity.
* blindingSpeed opacity increment on each 10 msec.
* When opacityThreshold is reached, the function fn is called.
*/
function utils_blindDocument(doc, blindingSpeed, opacityThreshold, fn) {
    var blindingDiv = doc.createElement("div");
    blindingDiv.id = "blindingDiv";
    blindingDiv.style.position = "absolute";
    blindingDiv.style.left = "0px";
    blindingDiv.style.top = "0px";
    blindingDiv.style.width = "120%";
    blindingDiv.style.height = "120%";
    blindingDiv.style.backgroundColor = "#FFFFFF";
    blindingDiv.style.zIndex = "32000";
    blindingDiv._Blind_fn = fn;
    doc.body.appendChild(blindingDiv);
    utils_applyOpacityObj(blindingDiv, 0.0);
    blindingDiv._Blind_run = function() {
        if (blindingDiv._opacity >= opacityThreshold) {
            blindingDiv._Blind_fn();
        }
        else {
            utils_applyOpacityObj(blindingDiv, blindingDiv._opacity + blindingSpeed);
            setTimeout(blindingDiv._Blind_run, 50);
        }
    };

    blindingDiv._Blind_run();
}
//-------------------------------------------------------
function utils_ifPopupThenOpenInRootWindow(url) {
    var winOpener = window.opener;

    if (winOpener) //open in root window, if this is a pop-up window - like WorkOrder.aspx
    {
        winOpener.location = url;
        window.close();
    }
}
//-------------------------------------------------------
function setFocus(ctlName) {
    var ctl = utils_$(ctlName);
    if (!ctl)
        alert("failed to find control: " + ctlName);
    else {
        try {
            ctl.focus();
            ctl.select();
        }
        catch (ex) {
        }
    }
}
//-------------------------------------------------------
function utils_setMouseXY(e) {
    try {
        // Detect if the browser is IE or not.
        // If it is not IE, we assume that the browser is NS.
        var IE = document.all ? true : false;

        if (IE) { // grab the x-y pos.s if browser is IE
            gMouseX = event.clientX + document.body.scrollLeft;
            gMouseY = event.clientY + document.body.scrollTop;
        }
        else {  // grab the x-y pos.s if browser is NS
            gMouseX = e.pageX;
            gMouseY = e.pageY;
        }

        // catch possible negative values in NS4
        if (gMouseX < 0)
            gMouseX = 0;

        if (gMouseY < 0)
            gMouseY = 0;
    }
    catch (e) {
    }

    return true;
}
//-------------------------------------------------------
function utils_document_MouseMoved(e) {
    utils_setMouseXY(e);
}
//-------------------------------------------------------
function getWindowWidth() {
    var windowWidth = 0;

    if (typeof (window.innerWidth) == 'number') {
        //Non-IE
        windowWidth = window.innerWidth;
    }
    else if (document.documentElement && document.documentElement.clientWidth) {
        //IE 6+ in 'standards compliant mode'
        windowWidth = document.documentElement.clientWidth;
    } else if (document.body && (document.body.clientWidth || document.body.clientHeight)) {
        //IE 4 compatible
        windowWidth = document.body.clientWidth;
    }

    return windowWidth;
}
//-------------------------------------------------------
function getWindowHeight() {
    var windowHeight = 0;

    if (typeof (window.innerHeight) == 'number') {
        //Non-IE
        windowHeight = window.innerHeight;
    }
    else if (document.documentElement && document.documentElement.clientHeight) {
        //IE 6+ in 'standards compliant mode'
        windowHeight = document.documentElement.clientHeight;
    } else if (document.body && (document.body.clientHeight || document.body.clientHeight)) {
        //IE 4 compatible
        windowHeight = document.body.clientHeight;
    }

    return windowHeight;
}
//-------------------------------------------------------
// Tests whether a string ends with a certain text.
//
function utils_stringEndsWith(str, text) {
    if (text.length > str.length)
        return false;
    else
        return str.indexOf(text) == (str.length - text.length);
}
//-------------------------------------------------------
//Replaces all occurences of 'key' with 'value' in the 'str' character string.
//
function utils_stringReplace(str, key, value) {
    while (true) {
        newStr = str.replace(key, value);

        if (newStr == str)
            return str; //no more replacements made

        str = newStr;
    }
}
//-------------------------------------------------------
// Smart parsing of screen units, e.g. "100", "100px", "100pt", "100in".
//
function utils_screenParseInt(str) {
    try {
        var indexUOM = str.indexOf('px');

        if (indexUOM < 0)
            indexUOM = str.indexOf('pt');

        if (indexUOM < 0)
            indexUOM = str.indexOf('in');

        if (indexUOM >= 0)
            str = str.substring(0, indexUOM);

        return utils_parseInt(str);
    }
    catch (ex) {
        alert('[utils_screenParseInt] Exception: ' + ex);

        return 0;
    }
}
//-------------------------------------------------------
function utils_parseInt(str) {
    try {
        if (str && str.length)
            return parseInt(str);
    }
    catch (ex) {
    }

    return 0;
}
//-------------------------------------------------------
// Smart parsing of screen units, e.g. "100", "100px", "100pt", "100in".
//
function utils_screenParseFloat(str) {
    try {
        var indexUOM = str.indexOf('px');

        if (indexUOM < 0)
            indexUOM = str.indexOf('pt');

        if (indexUOM < 0)
            indexUOM = str.indexOf('in');

        if (indexUOM >= 0)
            str = str.substring(0, indexUOM);

        return utils_parseFloat(str);
    }
    catch (ex) {
        alert('[utils_screenParseFloat] Exception: ' + ex);

        return 0;
    }
}
//-------------------------------------------------------
function utils_parseFloat(str) {
    try {
        if (str && str.length)
            return parseFloat(str);
    }
    catch (ex) {
    }

    return 0;
}
//-------------------------------------------------------
//
//  XStringBuilder
//
//-------------------------------------------------------
// Initializes a new instance of the XStringBuilder class
// and appends the given value if supplied
function XStringBuilder(value) {
    this.strings = new Array("");
    this.append(value);
}

// Appends the given value to the end of this instance.
XStringBuilder.prototype.append = function(value) {
    if (value)
        this.strings.push(value);
}

// Clears the string buffer
XStringBuilder.prototype.clear = function() {
    this.strings.length = 1;
}

XStringBuilder.prototype.length = function() {
    return this.strings.length;
}

// Converts this instance to a String.
XStringBuilder.prototype.toString = function() {
    return this.strings.join("");
}
//=======================================================
//
//  XTreeMap: a binary tree structure that maps KEYS to VALUES
//  (tree nodes sorted by KEYS: smallest keys on left side3)
//
//-------------------------------------------------------
function XTreeMap() {
    this.rootNode = 0; //none
    this.allKeys = new Array();
}
//-------------------------------------------------------
XTreeMap.prototype.getCount = function(key, value) {
    return this.allKeys.length;
}
//-------------------------------------------------------
XTreeMap.prototype.set = function(key, value) {
    if (!key)
        return;

    if (this.getValue(key) == gXTreeMap_NOTFOUND)
        this.allKeys.push(key);

    if (!this.rootNode)
        this.rootNode = new XTreeMapNode(key, value);
    else {
        if (key.toLowerCase)
            this.rootNode.set(key.toLowerCase(), value); //if string, keys are stored in lowercase
        else
            this.rootNode.set(key, value);
    }
}
//-------------------------------------------------------
XTreeMap.prototype.getAllKeys = function(key) {
    return this.allKeys;
}
//-------------------------------------------------------
XTreeMap.prototype.getValue = function(key) {
    if (!key || !this.rootNode)
        return gXTreeMap_NOTFOUND;

    return this.rootNode.getValue(key.toLowerCase ? key.toLowerCase() : key);
}
//=======================================================
function XTreeMapNode(key, value) {
    this.key = key;
    this.value = value;
    this.leftNode = 0;
    this.rightNode = 0;
}
//-------------------------------------------------------
XTreeMapNode.prototype.set = function(key, value) {
    if (this.key == key)
        this.value = value;
    else if (key < this.key) {
        if (!this.leftNode)
            this.leftNode = new XTreeMapNode(key, value);
        else
            this.leftNode.set(key, value);
    }
    else //key > this.key
    {
        if (!this.rightNode)
            this.rightNode = new XTreeMapNode(key, value);
        else
            this.rightNode.set(key, value);
    }
}
//-------------------------------------------------------
XTreeMapNode.prototype.getValue = function(key) {
    if (this.key == key)
        return this.value;
    else if (key < this.key) {
        if (!this.leftNode)
            return gXTreeMap_NOTFOUND;
        else
            return this.leftNode.getValue(key);
    }
    else //key > this.key
    {
        if (!this.rightNode)
            return gXTreeMap_NOTFOUND;
        else
            return this.rightNode.getValue(key);
    }
}
//=======================================================
//
//  XHashMap: a structure that maps KEYS to VALUES
//
//-------------------------------------------------------
function XHashMap() {
    this._keys = new Array();
    this._values = new Array();
}
//-------------------------------------------------------
XHashMap.prototype.set = function(key, value, caseInsensitiveKey, dontCheckKey) {
    if (!dontCheckKey) {
        if (caseInsensitiveKey) {
            lwrKey = key.toLowerCase();
            //check if key exists already: replace the associated value if so
            for (var i = 0; i < this._keys.length; i++) {
                if (this._keys[i].toLowerCase() == lwrKey) {
                    this._values[i] = value;
                    return;
                }
            }
        }
        else {
            //check if key exists already and replace if so
            for (var i = 0; i < this._keys.length; i++) {
                if (this._keys[i] == key) {
                    this._values[i] = value;
                    return;
                }
            }
        }
    }

    this._keys.push(key);
    this._values.push(value);
}
//-------------------------------------------------------
XHashMap.prototype.reset = function() {
    this._keys = new Array();
    this._values = new Array();
}
//-------------------------------------------------------
XHashMap.prototype.getKeyCount = function() {
    return this._keys.length;
}
//-------------------------------------------------------
XHashMap.prototype.getValueCount = function() {
    return this._values.length;
}
//-------------------------------------------------------
XHashMap.prototype.getKeyAt = function(index) {
    return this._keys[index];
}
//-------------------------------------------------------
XHashMap.prototype.setKeyAt = function(index, key) {
    this._keys[index] = key;
}
//-------------------------------------------------------
XHashMap.prototype.getValueAt = function(index) {
    return this._values[index];
}
//-------------------------------------------------------
XHashMap.prototype.setValueAt = function(index, value) {
    this._values[index] = value;
}
//-------------------------------------------------------
XHashMap.prototype.remove = function(key) {
    for (var i = 0; i < this._keys.length; i++) {
        if (this._keys[i] == key) {
            this.removeAt(i);
            return;
        }
    }
}
//-------------------------------------------------------
XHashMap.prototype.removeAt = function(index) {
    this._keys.splice(index, 1);
    this._values.splice(index, 1);
}
//-------------------------------------------------------
XHashMap.prototype.getValue = function(key) {
    for (var i = this._keys.length; i; )
        if (this._keys[--i] == key)
        return this._values[i];

    return gXHashMap_NOTFOUND;
}
//-------------------------------------------------------
XHashMap.prototype.containsKey = function(key) {
    for (var i = this._keys.length; i; )
        if (this._keys[--i] == key)
        return true;

    return false;
}
//-------------------------------------------------------
XHashMap.prototype.getKey = function(value) {
    for (var i = this._keys.length; i; )
        if (this._keys[--i] == key)
        return this._keys[i];

    return gXHashMap_NOTFOUND;
}
//-------------------------------------------------------
/**
* Returns a reference to the array containing all keys. 
*/
XHashMap.prototype.getAllKeys_Ref = function() {
    return this._keys;
}
//-------------------------------------------------------
/**
* Returns a reference to the array containing all values. 
*/
XHashMap.prototype.getAllValues_Ref = function() {
    return this._values;
}
//-------------------------------------------------------
XHashMap.prototype.toString_allKeys = function(separatorChar)//e.g. ':' -> '23:45:57:...'
{
    return utils_arrayToString(this._keys, separatorChar);
}
//-------------------------------------------------------
XHashMap.prototype.toString_allValues = function(separatorChar)//e.g. ':' -> 'John:Marry:Diana:...'
{
    return utils_arrayToString(this._values, separatorChar);
}
//-------------------------------------------------------
// Creates a clone of this object, pointing to the SAME keys and values. 
// (keys and values are not cloned, just the arrays that contains them)
XHashMap.prototype.clone = function() {
    var ret = new XHashMap();

    ret._keys = utils_arrayCopy(this._keys);
    ret._values = utils_arrayCopy(this._values);

    return ret;
}
//-------------------------------------------------------
XHashMap.prototype.copy = function(anotherXHashMap) {
    this._keys = utils_arrayCopy(anotherXHashMap._keys);
    this._values = utils_arrayCopy(anotherXHashMap._values);
}
//-------------------------------------------------------
function utils_followScreenHeight(htmlId, fractionOfScreenHeight, checkPeriodMSec, noAlertIfHtmlObjNotFound) {
    var htmlObj = utils_$(htmlId);
    if (!htmlObj) {
        if (!noAlertIfHtmlObjNotFound)
            alert('utils_followScreenHeight: No such object: ' + htmlId);

        return;
    }

    var rect = utils_$rect(htmlObj);

    if (screen) {
        var h = fractionOfScreenHeight * screen.height;
        if (rect.height != h)
            htmlObj.style.height = '' + h;
    }
    else
        alert("utils_followScreenHeight: no 'screen' defined");

    if (checkPeriodMSec)
        setTimeout("utils_followScreenHeight('" + htmlId + "'," + fractionOfScreenHeight + "," + checkPeriodMSec + ")", checkPeriodMSec);
}
//-------------------------------------------------------
function utils_applyOpacity(htmlID, fraction) {
    utils_applyOpacityObj(utils_$(htmlID), fraction);
}
//-------------------------------------------------------
function utils_applyOpacityObj(htmlObj, fraction) {
    if (!htmlObj)
        return;

    if (fraction < 0.0)
        fraction = 0.0;
    else if (fraction > 1.0)
        fraction = 1.0;

    htmlObj.style.filter = 'alpha(opacity=' + Math.round(100.0 * fraction) + ')'; //INTERNET EXPLORER only
    htmlObj.style.opacity = '' + fraction; //FIREFOX, SAFARI only

    htmlObj._opacity = fraction;
}
//-------------------------------------------------------
function utils_isText(strText) {
    if (!strText)
        return false;

    var str = "" + strText; //to avoid char arrays instead of pure strings, e.g. { 'h','e','l','l','o' } vs. "hello"  

    if (str.length) {
        for (var xi = 0; xi < str.length; xi++) {
            var ch = str.charAt(xi).toLowerCase();

            if (ch < 'a' || ch > 'z')
                return false;
        }

        return true;
    }

    return false;
}
//-------------------------------------------------------
function utils_isNumber(strNumber) {
    if (!strNumber)
        return false;

    var str = "" + strNumber; //to avoid char arrays instead of pure strings, e.g. { 'h','e','l','l','o' } vs. "hello"  

    if (str.length) {
        for (var xi = 0; xi < str.length; xi++) {
            var ch = str.charAt(xi);

            if (((ch < '0') || (ch > '9')) && (ch != '-') && (ch != '.'))
                return false;
        }

        return true;
    }

    return false;
}
//-------------------------------------------------------
function utils_clearField(id) {
    try {
        var obj = utils_findHtmlObj(id);

        if (!obj)
            alert('[clearField] No such element - id=' + id);
        else
            utils_setObjectValue(obj, '');
    }
    catch (ex) {
        alert('[utils_clearField] The html element with id=' + id + " does not have a 'value' property.");
    }
}
//-------------------------------------------------------
//Can be used for images, for example - to make sure they are fully shown inside a rectangle
//Return value: array containing newWidth and newHeight.
function utils_utils_scaleToFitRectangle(width, height, rectWidth, rectHeight) {
    var newWH = new Array();

    if (width > 0 && height > 0 && rectWidth > 0 && rectHeight > 0) {
        if (width > rectWidth) {
            height *= rectWidth;
            height /= width;
            width = rectWidth;
        }

        if (height > rectHeight) {
            width *= rectHeight;
            width /= height;
            height = rectHeight;
        }
    }

    newWH.push(Math.round(width));
    newWH.push(Math.round(height));

    return newWH;
}
//-------------------------------------------------------
// Adds elements from one array to another so that the added elements
// do not duplicate in the resulting array. Assumption is made that 
// the initial array (array1) does not contain duplicates.
// None of the arguments is modified. A new array is returned, containing the concatenation result.  
function utils_uniqueElem_concatenateArrays(array1, array2) {
    if (!array2)
        return array1;

    if (!array1)
        return array2;

    var ret = new Array();

    for (var i = 0; i < array1.length; i++)
        aux_utils_addUniqueElementToArray(array1[i], ret);

    for (var i = 0; i < array2.length; i++)
        aux_utils_addUniqueElementToArray(array2[i], ret);

    return ret;
}
//-------------------------------------------------------
/**
* Adds element e to array a only if a does not contain e already.
*/
function aux_utils_addUniqueElementToArray(e, a) {
    for (var j = 0; j < a.length; j++) {
        if (a[j] == e)
            return; //element exists in array
    }

    //Element not contained
    a.push(e);
}
//-------------------------------------------------------
// Returns a string without leading spaces
function utils_leftTrim(str) {
    if (!str)
        return "";

    var whitespace = new String(" \t\n\r");

    var s = new String(str);

    if (whitespace.indexOf(s.charAt(0)) != -1) {
        // We	have a string with leading blank(s)...

        var j = 0, i = s.length;

        // Iterate from the far left of string until we
        // don't have	any	more whitespace...
        while (j < i && whitespace.indexOf(s.charAt(j)) != -1)
            j++;

        // Get the substring from	the	first non-whitespace
        // character to the end of the string...
        s = s.substring(j, i);
    }
    return s;
}
//-------------------------------------------------------
// Returns a string without trailing spaces
function utils_rightTrim(str) {
    if (!str)
        return "";

    // We don't want	to trip	JUST spaces, but also tabs,
    // line feeds, etc.	Add	anything else you want to
    // "trim" here in Whitespace
    var whitespace = new String(" \t\n\r");

    var s = new String(str);

    if (whitespace.indexOf(s.charAt(s.length - 1)) != -1) {
        // We	have a string with trailing	blank(s)...

        var i = s.length - 1; 	  // Get length	of string

        // Iterate from the far right	of string until	we
        // don't have	any	more whitespace...
        while (i >= 0 && whitespace.indexOf(s.charAt(i)) != -1)
            i--;

        // Get the substring from	the	front of the string	to
        // where the last	non-whitespace character is...
        s = s.substring(0, i + 1);
    }

    return s;
}
//-------------------------------------------------------
// Returns a string without leading or trailing spaces
function utils_trim(str) {
    return utils_rightTrim(utils_leftTrim(str));

}
//-------------------------------------------------------
function utils_removeCharsBelowASCII(str, asciiCode) {
    if (!str || !str.length)
        return str;

    var ret = '';

    for (var i = 0, n = str.length; i < n; i++)
        if (str.charCodeAt(i) >= asciiCode)
        ret += str.charAt(i);

    return ret;
}
//-------------------------------------------------------
function utils_removeCharsAfterASCII(str, asciiCode) {
    if (!str || !str.length)
        return str;

    var ret = '';

    for (var i = 0, n = str.length; i < n; i++)
        if (str.charCodeAt(i) <= asciiCode)
        ret += str.charAt(i);

    return ret;
}
//-------------------------------------------------------
function utils_getDateYMD(year, month, day) {
    try {
        var ret = new Date();

        //The following is mandatory for successive getMonth() calls: if not called,
        //wrong month is returned by getMonth()  [crazy JavaScript thing]
        var tmp = ret.getMonth();
        tmp = ret.getDate();

        ret.setDate(1);
        ret.setMonth(1);
        ret.setFullYear(year);
        ret.setMonth(month - 1);
        ret.setDate(day);
       
        //extra-check, as JavaScript jumps to next month(s) if overflow, 
        //e.g. 'Nov 31, 2000' (invalid) is converted to 'Dec 1, 2000' (!)
        if (ret.getDate() != day || ret.getMonth() != month - 1 || ret.getFullYear() != year)
            return null;

        return ret;
    }
    catch(e)
    {
        return null;
    }
}
//-------------------------------------------------------
function utils_getMSecAsNaturalLanguage(msec, flagDoNotDisplayHours, flagDoNotDisplayMin, flagDoNotDisplaySec, flagDoNotDisplayMSec) {
    var ret = '';

    var oneSec = 1000; //msec
    var oneMin = 60000;
    var oneHour = 3600000;
    var oneDay = 86400000;

    if (msec > oneDay) {
        var days = Math.floor(msec / oneDay);

        msec -= days * oneDay;

        ret = '' + days;
        ret += ' day';

        if (days != 1)
            ret += 's';
    }

    if (msec > oneHour || ret.length) {
        var hours = Math.floor(msec / oneHour);

        msec -= hours * oneHour;

        if (ret.length)
            ret += ', ';

        if (!flagDoNotDisplayHours) {
            ret += hours;
            ret += ' hour';

            if (hours != 1)
                ret += 's';
        }
    }

    if (msec > oneMin || ret.length) {
        var mins = Math.floor(msec / oneMin);

        msec -= mins * oneMin;

        if (!flagDoNotDisplayMin) {
            if (ret.length)
                ret += ', ';

            ret += mins;
            ret += ' min';
        }
    }

    if (msec > oneSec || ret.length) {
        var secs = Math.floor(msec / oneSec);

        msec -= secs * oneSec;

        if (!flagDoNotDisplaySec) {
            if (ret.length)
                ret += ', ';

            ret += secs;
            ret += ' sec';
        }
    }

    if (!flagDoNotDisplayMSec) {
        if (ret.length)
            ret += ', ';

        ret += msec;
        ret += ' msec';
    }

    return ret;
}
//-------------------------------------------------------
function utils_getDateTime_MMDDYYYY_HHMMSS_AMPM() {
    var ret = '';
    var currentTime = new Date();
    var month = currentTime.getMonth() + 1;
    var day = currentTime.getDate();
    var year = currentTime.getFullYear();

    ret =
        ((month < 10) ? ('0' + month) : ('' + month)) + '/' +
        ((day < 10) ? ('0' + day) : ('' + day)) + '/' +
        year + ' ';

    var hours0 = currentTime.getHours();
    var hours = hours0 % 13;
    var minutes = currentTime.getMinutes();
    var seconds = currentTime.getSeconds();

    ret +=
        ((hours < 10) ? ('0' + hours) : ('' + hours)) + ':' +
        ((minutes < 10) ? ('0' + minutes) : ('' + minutes)) + ':' +
        ((seconds < 10) ? ('0' + seconds) : ('' + seconds)) + ' ';

    ret += (hours0 > 11) ? 'PM' : 'AM';

    return ret;
}
//-------------------------------------------------------
function utils_getDate_TODAY() {
    return new Date();
}
//-------------------------------------------------------
function utils_getDate_TOMORROW() {
    var d = new Date();
    d.setDate(d.getDate() + 1);
    return d;
}
//-------------------------------------------------------
function utils_getDate_YESTERDAY() {
    var d = new Date();
    d.setDate(d.getDate() - 1);
    return d;
}
//-------------------------------------------------------
function utils_getDate_DAYTHISWEEK(dayIndex) // (0..6)
{
    var d = new Date();
    var currentDay = d.getDay(); //0-based

    if (dayIndex != currentDay)
        d.setDate(d.getDate() + dayIndex - currentDay);

    return d;
}
//-------------------------------------------------------
function utils_getDate_DAYMONTHSWITCHED(dateObj) // returns a new Date object with the day and month values switched
{
    try {
        var ret = new Date(dateObj);

        var d = dateObj.getDate();
        var m = dateObj.getMonth();

        ret.setDate(m + 1);
        ret.setMonth(d - 1);

        return ret;
    }
    catch (e) {
        return 0;
    }
}
//-------------------------------------------------------
function utils_getDate_DAYNEXTWEEK(dayIndex) // (0..6)
{
    var d = utils_getDate_DAYTHISWEEK(dayIndex);

    d.setDate(d.getDate() + 7);

    return d;
}
//-------------------------------------------------------
function utils_get_CURRENTYEAR(as2digit) // 2008 or 08
{
    var ret = (new Date()).getFullYear();

    return as2digit ? (ret % 100) : ret;
}
//-------------------------------------------------------
function utils_get_CURRENTMONTH() // (0..11)
{
    return (new Date()).getMonth();
}
//-------------------------------------------------------
function utils_get_CURRENTDAYOFMONTH() // (1..28/29/30/31)
{
    return (new Date()).getDate();
}
//-------------------------------------------------------
function utils_get_CURRENTDAYOFWEEK() // (0..6)
{
    return (new Date()).getDay();
}
//-------------------------------------------------------
function utils_getHHMMSSsss(str) // returns an array containing exracted numeric values of the hour, minute, second, millisecond
{
    var ret = new Array(0, 0, 0, 0);

    str = utils_stringReplace(str, '.', ':'); //for an eventual time format like HH:MM:SS.SSS

    var tokens = str.split(':');

    for (var i = 0; i < tokens.length; i++) {
        try {
            ret[i] = utils_parseInt(tokens[i]);
        }
        catch (e) { }
    }

    return ret;
}
//-------------------------------------------------------
//Removes all occurences of 'key' from 'str' character string.
//
function utils_stringRemove(str, key) {
    return utils_stringReplace(str, key, '');
}
//-------------------------------------------------------
//Removes all double spaces, including tabs and new lines from a string.
function utils_stringSimplifyWhiteSpace(str, optional_replaceHTMLWhiteSpace) {
    if (!str)
        return '';

    str = utils_stringReplace(str, '\t', ' ');
    str = utils_stringReplace(str, '\r', ' ');
    str = utils_stringReplace(str, '\n', ' ');

    if (optional_replaceHTMLWhiteSpace) {
        str = utils_stringReplace(str, '&nbsp;', ' ');
        str = utils_stringReplace(str, '<br>', ' ');
        str = utils_stringReplace(str, '<BR>', ' ');
        str = utils_stringReplace(str, '<br/>', ' ');
        str = utils_stringReplace(str, '<BR/>', ' ');
        str = utils_stringReplace(str, '<br />', ' ');
        str = utils_stringReplace(str, '<BR />', ' ');
    }

    while (true) {
        var old_str = str;

        str = utils_stringReplace(str, '  ', ' '); //replace double spaces by one space

        if (old_str == str) {
            return utils_trim(str);
        }
    }
}
//-------------------------------------------------------
function utils_isDigit(chr) {
    return (chr >= '0') && (chr <= '9');
}
//-------------------------------------------------------
function utils_isSpace(chr) {
    return (chr == ' ') || (chr == '\t') || (chr == '\n') || (chr == '\r');
}
//-------------------------------------------------------
function utils_isLetter(chr) {
    return (chr >= 'a') && (chr <= 'z') || (chr >= 'A') && (chr <= 'Z');
}
//-------------------------------------------------------
//Inserts spaces between numbers and words.
//
function utils_stringSeparateNumbersFromWords(str) {
    var prevCharIsDigit;
    var n = str.length;
    for (var i = 0; i < n; i++) {
        var ch = str.charAt(i);
        var charIsDigit = utils_isDigit(ch);

        if (i > 0 && utils_isLetter(ch) && (prevCharIsDigit ^ charIsDigit) && !utils_isSpace(ch)) {
            str = str.substring(0, i) + ' ' + str.substring(i);
            n++;
        }

        prevCharIsDigit = charIsDigit;
    }

    return str;
}
//-------------------------------------------------------
/**
* Searches for and returns the index of a string in an array of strings (-1 if not found).
*/
function utils_getIndexInStringArray(stringArray, str, caseSensitive) {
    if (caseSensitive) {
        for (var i = 0; i < stringArray.length; i++) {
            if (stringArray[i] == str)
                return i;
        }
    }
    else {
        for (var i = 0; i < stringArray.length; i++) {
            if (stringArray[i].toLowerCase() == str.toLowerCase())
                return i;
        }
    }

    return -1;
}
//-------------------------------------------------------
function utils_toHexDigit(decimal) {
    if ((decimal >= 0) && (decimal <= 9))
        return decimal;
    else {
        switch (decimal) {
            case 10: return "A";
            case 11: return "B";
            case 12: return "C";
            case 13: return "D";
            case 14: return "E";
            case 15: return "F";
        }

        return 0;
    }
}
//-------------------------------------------------------
/*
* Converts a string representation of a hexadecimal number into a decimal number. Ignores '#'
* Example: strColor='#A0B088'
*/
function utils_hexToDecimal(strColor) {
    var ret = 0;

    for (var i = 0, n = strColor.length; i < n; i++) {
        var chr = strColor.charAt(i).toUpperCase();
        if (chr == '#')
            continue;

        var chrValue;
        switch (chr) {
            case 'F':
            case 'E':
            case 'D':
            case 'C':
            case 'B':
            case 'A':
                chrValue = 10 + (utils_atoi(chr) - utils_atoi('A'));
                break;
            case '9':
            case '8':
            case '7':
            case '6':
            case '5':
            case '4':
            case '3':
            case '2':
            case '1':
            case '0':
                chrValue = utils_atoi(chr) - utils_atoi('0');
                break;
        }

        ret = ret * 16 + chrValue;
    }

    return ret;
}
//-------------------------------------------------------
function utils_getInterpolatedColor(strColorStart, strColorEnd, fraction) {
    if (!strColorStart)
        return '';

    if (strColorStart.charAt(0) == '#')
        strColorStart = strColorStart.substring(1);

    if (!strColorEnd)
        strColorEnd = strColorStart;

    if (strColorEnd.charAt(0) == '#')
        strColorEnd = strColorEnd.substring(1);

    var color1Decimal_R = utils_hexToDecimal(strColorStart.substring(0, 2));
    var color2Decimal_R = utils_hexToDecimal(strColorEnd.substring(0, 2));
    var color1Decimal_G = utils_hexToDecimal(strColorStart.substring(2, 4));
    var color2Decimal_G = utils_hexToDecimal(strColorEnd.substring(2, 4));
    var color1Decimal_B = utils_hexToDecimal(strColorStart.substring(4));
    var color2Decimal_B = utils_hexToDecimal(strColorEnd.substring(4));

    var colorDecimal_R = Math.round(color1Decimal_R + fraction * (color2Decimal_R - color1Decimal_R));
    var colorDecimal_G = Math.round(color1Decimal_G + fraction * (color2Decimal_G - color1Decimal_G));
    var colorDecimal_B = Math.round(color1Decimal_B + fraction * (color2Decimal_B - color1Decimal_B));

    var ret =
        '#' +
        utils_toHex(colorDecimal_R, 2) +
        utils_toHex(colorDecimal_G, 2) +
        utils_toHex(colorDecimal_B, 2);

    return ret;
}
//-------------------------------------------------------
// Converts an integer (unicode value) to a char
function utils_itoa(i) {
    return String.fromCharCode(i);
}
//-------------------------------------------------------
// Converts a char into to an integer (unicode value)
function utils_atoi(a) {
    return a.charCodeAt();
}

//-------------------------------------------------------
function utils_getHexColor(intR, intG, intB) {
    return '#' + utils_toHex(intR, 2) + utils_toHex(intG, 2) + utils_toHex(intB, 2);
}
//-------------------------------------------------------
var gPower16 = new Array();
var power16 = 1;
for (var i = 0; i < 8; i++) {
    gPower16[i] = power16;
    power16 *= 16;
}
//-------------------------------------------------------
/**
* Decimal (int) to hexadecimal conversion (string).
* e.g. utils_toHex(2250, 4) ==> '08CA'
*/
function utils_toHex(decimalValue, numberOfHexDigits) {
    var ret = '';

    if (!numberOfHexDigits)
        numberOfHexDigits = 6;

    for (var i = numberOfHexDigits - 1; i >= 0; i--) {
        h = Math.floor(decimalValue / gPower16[i]);

        ret += '' + utils_toHexDigit(h);

        decimalValue -= h * gPower16[i];
    }

    return ret;
}
//-------------------------------------------------------
var gFlashBg_map = new XHashMap();
var gFlashBg_initialBgColor = new Array();
var gFlashBg_MAXTIMEMSEC = 210;
var gFlashBg_TIMESTEPMSEC = 70;
var gFlashBg_rate = 100.0 / gFlashBg_MAXTIMEMSEC;
function utils_flashBackground(htmlObj) {
    if (!htmlObj)
        return;

    gFlashBg_map.set(htmlObj, 0);

    if (gFlashBg_map.getKeyCount() == 1)
        utils_aux_flashBackground();
}
//-------------------------------------------------------
function utils_aux_flashBackground() {
    try {
        var running = false;

        var n = gFlashBg_map.getKeyCount();
        for (var i = 0; i < n; i++) {
            var htmlObj = gFlashBg_map.getKeyAt(i);
            var phase = gFlashBg_map.getValueAt(i);

            if (phase < gFlashBg_MAXTIMEMSEC) {
                if (phase == 0)
                    gFlashBg_initialBgColor[i] = htmlObj.style ? htmlObj.style.backgroundColor : 0; //Save initial color

                running = true;

                phaseH = utils_toHex(155 + phase * gFlashBg_rate, 2);

                phase += gFlashBg_TIMESTEPMSEC;
                gFlashBg_map.set(htmlObj, phase);

                htmlObj.style.backgroundColor = '#' + phaseH + phaseH + phaseH;
            }
            else {
                if (htmlObj.style)
                    htmlObj.style.backgroundColor = gFlashBg_initialBgColor[i]; //Restore initial color
            }
        }

        if (running)
            setTimeout('utils_aux_flashBackground()', gFlashBg_TIMESTEPMSEC);
        else {
            gFlashBg_map = new XHashMap();
            gFlashBg_initialBgColor = new Array();
        }
    }
    catch (ex) {
        alert('[utils_aux_flashBackground] Exception: ' + ex);
    }
}
//-------------------------------------------------------
/**
* Extracts the words only from a string. Returns an array contining the words.
*/
function utils_extractWords(str) {
    if (!str)
        return 0;

    words = str.match(new RegExp('([a-z]|[A-Z])+', 'g'));

    return utils_uniqueElem_concatenateArrays(new Array(), words);
}
//-------------------------------------------------------
/**
* Will no longer submit the form when pressing Enter (Return) key inside a text field.
*/
function utils_disableReturnKeyOnForm() {
    utils_disableCertainKeysOnForm([13]);
}
//-------------------------------------------------------
function utils_disableCertainKeysOnForm(keyArray) {

    if (!document._onkeydownFunctions) {
        utils_attachDocumentEvent(
            'onkeydown',
            function(evt) {
                return aux_utils_disableCertainKeysOnForm(evt, keyArray);
            });

        utils_attachDocumentEvent(
            'onkeyup',
            function(evt) {
                return aux_utils_disableCertainKeysOnForm(evt, keyArray);
            });

        utils_attachDocumentEvent(
            'onkeypress',
            function(evt) {
                return aux_utils_disableCertainKeysOnForm(evt, keyArray);
            });
    }
}
//-------------------------------------------------------
document._utils_getKeyCode = function(e) {

    var evt = e || window.event;
    return evt.keyCode ? evt.keyCode : evt.charCode;
};
//-------------------------------------------------------
function utils_installControlKeysMonitor() {
    utils_attachDocumentEvent(
        'onkeydown',
        function(e) {
            var keyCode = document._utils_getKeyCode(e)
            switch (keyCode) {
                case gUtils_KEYCODE_SHIFT:
                    gUtils_KEYDOWN_SHIFT = true;
                    break;
                case gUtils_KEYCODE_CTRL:
                    gUtils_KEYDOWN_CTRL = true;
                    break;
                case gUtils_KEYCODE_ALT:
                    gUtils_KEYDOWN_ALT = true;
                    break;
                /*                default:
                //setFeedback('KeyCodeDOWN: ' + keyCode);
                break;*/ 
            }
        });

    utils_attachDocumentEvent(
        'onkeyup',
        function(e) {
            var keyCode = document._utils_getKeyCode(e)
            switch (keyCode) {
                case gUtils_KEYCODE_SHIFT:
                    gUtils_KEYDOWN_SHIFT = false;
                    break;
                case gUtils_KEYCODE_CTRL:
                    gUtils_KEYDOWN_CTRL = false;
                    break;
                case gUtils_KEYCODE_ALT:
                    gUtils_KEYDOWN_ALT = false;
                    break;
                /*                default:
                //setFeedback('KeyCodeUP: ' + keyCode);
                break;*/ 
            }
        });
}
//-------------------------------------------------------
// strEventName: "onkeydown", "onkeyup", etc.
// fn: must have an "evt" argument, same as document.onkeydown(evt) has, for example.
// Example:
//      utils_attachDocumentEvent('onkeyup', function(evt){ alert('KEY UP'); })
function utils_attachDocumentEvent(strEventName, fn) {
    if (!document._utils_eventFunctions)
        document._utils_eventFunctions = new XHashMap();

    if (!utils_arrayContains(document._utils_eventFunctions._keys, strEventName)) {
        document['_' + strEventName] = document[strEventName]; //save default event processor
        document[strEventName] = function(evt) {
            var prevEventProcessor = document['_' + strEventName];
            if (prevEventProcessor)
                prevEventProcessor(evt); //execute default event processor

            for (var i = 0, n = document._utils_eventFunctions.getKeyCount(); i < n; i++) {
                if (document._utils_eventFunctions.getKeyAt(i) == strEventName) {
                    var f = document._utils_eventFunctions.getValueAt(i);
                    f(evt);
                }
            }
        };
    }

    document._utils_eventFunctions._keys.push(strEventName);
    document._utils_eventFunctions._values.push(fn);
}
//-------------------------------------------------------
function utils_setReturnKeyOnForm(fn) //fn must have one argument: keyCode
{
    if (!document._utils_keypressfunctions)
        document._utils_keypressfunctions = new Array();

    document._utils_keypressfunctions.push(fn);

    document.onkeypress = utils_onkeypress;
}
//-------------------------------------------------------
function utils_onkeypress(e) {
    if (!document._utils_keypressfunctions)
        return;

    var keyCode = 0;
    if (document.layers)
        keyCode = e.which;
    else
        keyCode = window.event.keyCode;

    for (var i = 0, n = document._utils_keypressfunctions.length; i < n; i++) {
        var fn = document._utils_keypressfunctions[i];
        fn(keyCode);
    }
}
//-------------------------------------------------------
function aux_utils_disableCertainKeysOnForm(e, keyArray) {
    var e = window.event ? window.event : e;
    //var e = e ? e : (event ? event : null);
    var node = e.target ? e.target : (e.srcElement ? e.srcElement : null);

    //if (node.type != "text")
    for (var i = 0, n = keyArray.length; i < n; i++)
        if (e.keyCode == keyArray[i])
        return utils_stopEvent(e);
}
//-------------------------------------------------------
// Just call this function on html body "onload" event, then use utils_log() to add info
// (a blue log window will be appended to html body, visible at top of screen, expandable/collapsable)
//
function utils_log_init(showInitially) {
    if (!gLogEnabled)
        return;

    if (!document.body) {
        setTimeout(utils_log_init, 100);
        return;
    }

    var gLogHolder = document.createElement("div");
    gLogHolder.style.position = 'absolute';
    gLogHolder.style.left = '' + gLogX + 'px';
    gLogHolder.style.top = '' + gLogY + 'px';
    gLogHolder.style.height = '18px';
    gLogHolder.style.backgroundColor = 'transparent';
    gLogHolder.style.color = '#EEEEEE';
    gLogHolder.style.align = 'left';
    gLogHolder.style.zIndex = '' + gLogZIndex;

    var gLogButtonMaximize = document.createElement("div");
    gLogButtonMaximize.style.position = 'absolute';
    gLogButtonMaximize.style.fontFamily = "Arial";
    gLogButtonMaximize.style.fontSize = "12px";
    gLogButtonMaximize.style.fontWeight = "bold";
    gLogButtonMaximize.style.left = '' + gLogX + 'px'; ;
    gLogButtonMaximize.style.top = '' + gLogY + 'px';
    gLogButtonMaximize.style.width = '25px';
    gLogButtonMaximize.style.align = 'center';
    gLogButtonMaximize.style.valign = 'middle';
    gLogButtonMaximize.style.backgroundColor = gLogBkColor;
    gLogButtonMaximize.style.color = '#EEEEEE';
    gLogButtonMaximize.style.zIndex = '' + (gLogZIndex + 2);
    gLogButtonMaximize.style.display = 'none';
    gLogButtonMaximize.innerHTML = '&nbsp;[+]&nbsp;';
    gLogButtonMaximize.style.cursor = 'pointer';
    gLogButtonMaximize.onclick = function() {
        gLogHolder.style.display = 'block';
        gLogButtonMaximize.style.display = 'none';
    };
    document.body.appendChild(gLogButtonMaximize);

    var gLogButtonMinimize = document.createElement("div");
    gLogButtonMinimize.style.position = 'absolute';
    gLogButtonMinimize.style.fontFamily = "Arial";
    gLogButtonMinimize.style.fontSize = "12px";
    gLogButtonMinimize.style.fontWeight = "bold";
    gLogButtonMinimize.style.left = '' + gLogX + 'px';
    gLogButtonMinimize.style.top = '' + gLogHeight + 'px';
    gLogButtonMinimize.style.width = '25';
    gLogButtonMinimize.style.align = 'center';
    gLogButtonMinimize.style.valign = 'middle';
    gLogButtonMinimize.style.backgroundColor = gLogBkColor;
    gLogButtonMinimize.style.color = '#EEEEEE';
    gLogButtonMinimize.style.zIndex = '' + (gLogZIndex + 2);
    gLogButtonMinimize.innerHTML = '&nbsp;[-]&nbsp;';
    gLogButtonMinimize.style.cursor = 'pointer';
    gLogButtonMinimize.onclick = function() {
        gLogHolder.style.display = 'none';
        gLogButtonMaximize.style.display = 'block';
    };
    gLogHolder.appendChild(gLogButtonMinimize);

    gLogObj = document.createElement("textarea");
    gLogObj.id = 'javaScriptLog';
    gLogObj.style.position = 'absolute';
    gLogObj.style.zIndex = '' + (gLogZIndex + 1);
    gLogObj.style.fontFamily = "Arial";
    gLogObj.style.fontSize = "12px";
    gLogObj.style.width = '' + gLogWidth;
    gLogObj.style.height = '' + gLogHeight;
    gLogObj.style.backgroundColor = gLogBkColor;
    gLogObj.style.color = gLogForeColor;
    gLogObj.wrap = 'off';
    gLogObj.readOnly = true;
    utils_setObjectValue(gLogObj, "");
    gLogHolder.appendChild(gLogObj);

    var gLogButtonClear = document.createElement("div");
    gLogButtonClear.style.position = 'absolute';
    gLogButtonClear.style.fontFamily = "Arial";
    gLogButtonClear.style.fontSize = "12px";
    gLogButtonClear.style.fontWeight = "bold";
    gLogButtonClear.style.left = '' + (gLogX + 25) + 'px';
    gLogButtonClear.style.top = '' + gLogHeight + 'px';
    gLogButtonClear.style.width = '50px';
    gLogButtonClear.style.align = 'center';
    gLogButtonClear.style.valign = 'middle';
    gLogButtonClear.style.backgroundColor = gLogBkColor;
    gLogButtonClear.style.color = '#EEEEEE';
    gLogButtonClear.style.zIndex = '' + (gLogZIndex + 2);
    gLogButtonClear.innerHTML = '&nbsp;&nbsp;[Clear]&nbsp;&nbsp;';
    gLogButtonClear.style.cursor = 'pointer';
    gLogButtonClear.onclick = function() {
        utils_setObjectValue(gLogObj, "");
    };
    gLogHolder.appendChild(gLogButtonClear);

    document.body.appendChild(gLogHolder);

    utils_applyOpacityObj(gLogObj, gLogOpacity);

    gLogHolder.style.display = 'none';
    gLogButtonMaximize.style.display = 'block';

    if (showInitially)
        gLogButtonMaximize.onclick();
}
//-------------------------------------------------------
function utils_log(text) {
    if (!gLogEnabled)
        return;

    if (gLogObj) {
        gLogObj.value += '>> ' + text + '\n';
        gLogObj.scrollTop += gLogObj.scrollHeight;
    }
}
//-------------------------------------------------------
function utils_timedOperation_MARK_BEGIN(operationBeginName, dontLogIt) {
    try {
        var timedOperationId = ++gTimedOperationAutoId;
        var timeNow = new Date().getTime();
        gTimedOperations.set(timedOperationId, [timeNow, operationBeginName]);

        if (!dontLogIt)
            utils_log("Timing '" + operationBeginName + "' ... (id=" + timedOperationId + ")");

        return timedOperationId;
    }
    catch (e) {
        utils_log('EXCEPTION - utils_timedOperation_MARK_BEGIN(): ' + e.message);
        return -1;
    }
}
//-------------------------------------------------------
function utils_timedOperation_MARK_END(timedOperationId, operationEndName, dontLogIt) {
    try {
        var time2 = new Date().getTime();
        var config = gTimedOperations.getValue(timedOperationId);
        if (!config)
            return;

        var time1 = config[0];
        var deltaTime = time2 - time1;

        if (!dontLogIt) {
            var operationBeginName = config[1];
            if (!operationBeginName)
                operationBeginName = '';

            var operationName =
                (operationEndName && operationEndName.length > 0) ?
                (operationBeginName + ' - ' + operationEndName) :
                operationBeginName;

            utils_log(
                operationName + ': ' + utils_getMSecAsNaturalLanguage(deltaTime) +
                " (id=" + timedOperationId + ")");
        }

        gTimedOperations.remove(timedOperationId);

        return deltaTime;
    }
    catch (e) {
        utils_log('EXCEPTION - utils_timedOperation_MARK_END(): ' + e.message);
        return -1;
    }
}
//-------------------------------------------------------
function utils_delayed_inputFieldSetValue(htmlInputObj, newValue, timeDelayMSec) {
    if (!htmlInputObj)
        return;

    setTimeout(function() {
        utils_setObjectValue(htmlInputObj, newValue);
        htmlInputObj.focus();
    }
		    , timeDelayMSec);
}
//-------------------------------------------------------
function utils_delayed_inputFieldSetFocus(htmlInputObj, timeDelayMSec) {
    if (!htmlInputObj)
        return;

    setTimeout(function() {
        htmlInputObj.focus();
        utils_setObjectValue(htmlInputObj, utils_getObjectValue(htmlInputObj));
        htmlInputObj.select();
    }
		    , timeDelayMSec);
}
//-------------------------------------------------------
/**
* Extracts the arguments from a request string, e.g. "http://test.com?a=1&b=2&c=2 ==> the arguments extracted (XHashMap): { (a,1), (b, 2), (c, 2) }
* Returns an array containing three elements: request base (as a string, "http://test.com"), the arguments (as a XHashMap) and the argument separator.
*/
function utils_extractRequestArguments(strRequest) {
    var args_and_values = new XHashMap();
    var argSeparator = '?';

    try {
        index = strRequest.indexOf(argSeparator); //window.location.search.substring(1);

        if (index < 0) {
            argSeparator = '';
            index = strRequest.indexOf('&');
        }

        if (index >= 0) {
            strArgsAndValues = strRequest.substring(index + 1);
            strRequest = strRequest.substring(0, index);

            var argsAndValuesArray = strArgsAndValues.split("&");
            for (var i = 0; i < argsAndValuesArray.length; i++) {
                if (argsAndValuesArray[i] == '')
                    continue;

                var av = argsAndValuesArray[i].split("=");
                args_and_values.set(av[0], av[1]);
            }
        }
    }
    catch (ex) {
        alert('[utils_extractRequestArguments] Exception: ' + ex);
    }

    ret = new Array();
    ret.push(strRequest); //base
    ret.push(args_and_values);
    ret.push(argSeparator);

    return ret;
}
//-------------------------------------------------------
/**
* This is the opposite of utils_extractRequestArguments(): 
*     "http://test.com", XHashMap containing: { (a,1), (b, 2), (c, 2) } and '?' ==> "http://test.com?a=1&b=2&c=2
*     "http://test.com", XHashMap containing: { (a,1), (b, 2), (c, 2) } and ''  ==> "http://test.com&a=1&b=2&c=2
*/
function utils_buildRequestString(strRequestBase, args_and_values, argSeparator) {
    var ret = strRequestBase;

    try {
        var nArgs = args_and_values.getKeyCount();

        if (nArgs > 0) {
            ret += argSeparator;

            for (var i = 0; i < nArgs; i++) {
                if (i || (argSeparator == ''))
                    ret += "&";

                ret += args_and_values.getKeyAt(i) + "=" + args_and_values.getValueAt(i);
            }
        }
    }
    catch (ex) {
        alert('[utils_buildRequestString] Exception: ' + ex);
    }

    return ret;
}
//-------------------------------------------------------
/**
* "http://www.there.com/path/file.ext" ==> "http://www.there.com/..." 
*/
function utils_replaceURLPathByDots(url) {
    var index1 = url.indexOf('//');
    if (index1 < 0)
        return url;
    index1 = url.indexOf('/', index1 + 2);
    if (index1 < 0)
        return url;

    var index2 = url.indexOf('&');
    if ((index2 >= 0) && (index2 < index1))
        index1 = index2;

    index2 = url.indexOf('?');
    if ((index2 >= 0) && (index2 < index1))
        index1 = index2;

    return url.substring(0, index1) + '/...';
}
//-------------------------------------------------------
/**
* Truncates the size of a html object's value to a certain number of characters.
*
* @param htmlObject Html object. Can be -for example- a text input field or a text area.<b> 
* @param maxChars Char limit. If <= 0, no limit is enforced.
* @param saySomethingOnMax OPTIONAL: if provided, will pop-up an alert box containing this text.
*/
function utils_limitText(htmlObject, maxChars, saySomethingOnMax) {
    try {
        if (!htmlObject)
            return;

        var val = utils_getObjectValue(htmlObject);

        if (!val || maxChars <= 0)
            return;

        if (val.length > maxChars) {
            utils_setObjectValue(htmlObject, val.substring(0, maxChars));

            if (saySomethingOnMax)
                alert(saySomethingOnMax);
        }
    }
    catch (ex) {
        alert('[utils_limitText] EXCEPTION: ' + ex);
    }
}
//-------------------------------------------------------
/**
* Builds and returns an array of randomized values for the interval [startValue ... endValue], step: stepValue.
* EXAMPLE:
*     utils_getRandomizedArray(20, 50, 10) returns something like: Array { 30, 50, 40, 20 } 
*/
function utils_getRandomizedArray(startValue, endValue, stepValue) {
    var ret = new Array();

    if ((startValue > endValue) || (startValue < endValue) && !stepValue)
        return ret;

    var initialArray = new Array();

    for (var v = startValue; v <= endValue; v += stepValue) {
        initialArray.push(v);
    }

    while (true) {
        var lenArray = initialArray.length;

        if (!lenArray)
            break;

        var index = Math.floor(lenArray * Math.random());

        ret.push(initialArray[index]);

        initialArray.splice(index, 1);
    }

    return ret;
}
//-------------------------------------------------------
/**
* Scans all children for a child node of a particular type.
* EXAMPLE: Search for an "img" node inside a "div" node.
*
* If check_htmlObj is set (true/1), search starts at htmlObj, otherwise only children are inspected.
*
* Example: utils_findDomNode(utils_$('staffTable'), 'tbody', false)
*/
function utils_findDomNode(htmlObj, tagName, check_htmlObj) {
    if (!htmlObj)
        return 0;
    
    if (check_htmlObj) {
        if (htmlObj.tagName && htmlObj.tagName.toLowerCase() == tagName.toLowerCase())
            return htmlObj;

        var ret = utils_findDomNode(htmlObj.nextSibling, tagName, true);
        if (ret)
            return ret;
    }

    return utils_findDomNode(htmlObj.firstChild, tagName, true);
}
//-------------------------------------------------------
/**
* Scans all children for ALL child nodes of a particular type.
* NOTE: If tagName is null (0), then ALL nodes will be returned.
* EXAMPLE: Search for all "img" nodes inside a "div" node.
*
* Usually, exploreSiblingsFlag is "false" for the root node: "domNode". For all children, the flag is always "true". 
* Returns an array of nodes found (html objects).
*
* EXAMPLE(1): utils_findDomNodes(htmlObj, 'input')
* EXAMPLE(2): utils_findDomNodes(htmlObj, ['input', 'select', 'textarea'])
*/
function utils_findDomNodes(domNode, tagName, exploreSiblingsFlag)//tagName can be an array of tag names
{
    if (!domNode)
        return 0;

    var ret = new Array();
    if (tagName && tagName.constructor == Array) {

        for (var i = 0, n = tagName.length; i < n; i++) {
            var ret1 = new Array();
            utils_findDomNodes_aux(domNode, tagName[i].toLowerCase(), ret1, exploreSiblingsFlag);

            ret = utils_arrayAppendArray(ret, ret1)
        }
    }
    else
        utils_findDomNodes_aux(domNode, tagName ? tagName.toLowerCase() : null, ret, exploreSiblingsFlag);

    return ret;
}
//-------------------------------------------------------
function utils_findDomNodes_aux(domNode, tagName, nodesFoundArray, exploreSiblingsFlag) {
    if (!domNode)
        return;

    if (!tagName || domNode.tagName && domNode.tagName.toLowerCase() == tagName)
        nodesFoundArray.push(domNode);

    if (exploreSiblingsFlag)
        utils_findDomNodes_aux(domNode.nextSibling, tagName, nodesFoundArray, true);

    utils_findDomNodes_aux(domNode.firstChild, tagName, nodesFoundArray, true);
}
//-------------------------------------------------------
/**
* Removes all children of DOM object (html) provided.
*/
function utils_removeAllChildren(htmlObj) {
    if (!htmlObj)
        return;

    while (true) {
        var child = htmlObj.firstChild;
        if (child)
            htmlObj.removeChild(child);
        else
            break;
    }

}
//-------------------------------------------------------
// Tests whether mouse pointer is inside a rectangle
function utils_isMouseOutsideRectangle(rect) {
    var ret =
        !rect ||
        (gMouseX < rect.x) || (gMouseX >= rect.x + rect.width) ||
        (gMouseY < rect.y) || (gMouseY >= rect.y + rect.height);

    return ret;
}
//-------------------------------------------------------
function utils_adjustXY(htmlObj, deltaX, deltaY) {
    if (!htmlObj)
        return;

    var x = utils_parseInt(htmlObj.style.left);
    var y = utils_parseInt(htmlObj.style.top);

    htmlObj.style.left = '' + (x + deltaX);
    htmlObj.style.top = '' + (y + deltaY);
}
//-------------------------------------------------------
function utils_rectangleToString(rect) {
    if (rect)
        return '[x,y,width,height]: ' + rect.x + ',' + rect.y + ',' + rect.width + ',' + rect.height;
    else
        return 'null';
}
//-------------------------------------------------------
function utils_mouseToString() {
    return '[gMouseX,gMouseY]: ' + gMouseX + ',' + gMouseY;
}
//-------------------------------------------------------
//Registration for opacity change and actual opacity change start, immediately.
// - targetOpacity: 0.0 ... 1.0
// - opacityChangeRate: opacity value / second, e.g. for an initial opacity of 1.0 and 
// - target of 0.0, target opacity will be reached in 10 seconds at a rate of 0.1
function utils_animateOpacity_Obj(htmlObj, targetOpacity, opacityChangeRate) {
    if (!htmlObj || opacityChangeRate == 0.0)
        return; //no animation can be done

    var mustStartTimer = (gOpacityAnimation_Objects.length == 0);

    gOpacityAnimation_Objects.push(htmlObj);
    gOpacityAnimation_TargetOpacities.push(targetOpacity);
    gOpacityAnimation_OpacityChangeRates.push(opacityChangeRate);

    if (mustStartTimer)
        setTimeout("utils_animateOpacities()", 10);
}
//-------------------------------------------------------
function utils_animateOpacity_ById(htmlId, targetOpacity, opacityChangeRate) {
    utils_animateOpacity_Obj(utils_$(htmlId), targetOpacity, opacityChangeRate);
}
//-------------------------------------------------------
function utils_animateOpacities() {
    try {
        if (gOpacityAnimation_Lock)
            return;

        gOpacityAnimation_Lock = true;

        var n = gOpacityAnimation_Objects.length;

        for (var i = 0; i < n; i++) {
            try {
                var endOfOpacityAnimation = false;

                var obj = gOpacityAnimation_Objects[i];
                var targetOpacity = gOpacityAnimation_TargetOpacities[i];
                var changeRate = gOpacityAnimation_OpacityChangeRates[i]; // opacityStep/sec

                //make sure target will be reached
                if (obj._opacity < targetOpacity && changeRate < 0 ||
                    obj._opacity > targetOpacity && changeRate > 0)
                    chageRate = gOpacityAnimation_OpacityChangeRates[i] = -changeRate;

                var newOpacity = obj._opacity + changeRate * gOpacityAnimation_StepMSec / 1000.0;

                if (newOpacity < 0.0) {
                    newOpacity = 0.0;
                    endOfOpacityAnimation = true;
                }
                else if (newOpacity > 1.0) {
                    newOpacity = 1.0;
                    endOfOpacityAnimation = true;
                }
                else if ((newOpacity < targetOpacity) && (changeRate < 0.0) || (newOpacity > targetOpacity) && (changeRate > 1.0)) {
                    newOpacity = targetOpacity;
                    endOfOpacityAnimation = true;
                }

                utils_applyOpacityObj(obj, newOpacity);

                if (endOfOpacityAnimation) {
                    gOpacityAnimation_Objects.splice(i, 1);
                    gOpacityAnimation_TargetOpacities.splice(i, 1);
                    gOpacityAnimation_OpacityChangeRates.splice(i, 1);

                    //break current loop and restart operation, as the arrays is now invalid.
                    gOpacityAnimation_Lock = false;
                    utils_animateOpacities();
                    return;
                }
            }
            catch (ex0) {
            }
        }

        if (n == 0) {
            gOpacityAnimation_Lock = false;
            return; //no elements in the animation arrays
        }
    }
    catch (ex) {
    }

    gOpacityAnimation_Lock = false;
    setTimeout("utils_animateOpacities()", gOpacityAnimation_StepMSec);
}
//-------------------------------------------------------
/**
*  Returns the visible (client) size of the document in browser: { width, height }
*
*  Interesting link: http://llbest.com/BrowserInfo.php
*/
function utils_getCurrentClientSize() {
    var _width = 0, _height = 0;

    if (typeof (window.innerWidth) == 'number') {
        //Non-IE
        _width = window.innerWidth;
        _height = window.innerHeight;
    }
    else if (document.documentElement &&
			 (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
        //IE 6+ in 'standards compliant mode'
        _width = document.documentElement.clientWidth;
        _height = document.documentElement.clientHeight;
    }
    else if (document.body &&
			 (document.body.clientWidth || document.body.clientHeight)) {
        //IE 4 compatible
        _width = document.body.clientWidth;
        _height = document.body.clientHeight;
    }

    return { width: _width, height: _height };
}
//-------------------------------------------------------
/**
*  Returns current { x, y } browser scroll position.
*
*  Interesting link: http://llbest.com/BrowserInfo.php
*/
function utils_getScrollXY() {
    var _x = 0, _y = 0;

    if (typeof (window.pageYOffset) == 'number') {
        //Netscape compliant
        _x = window.pageXOffset;
        _y = window.pageYOffset;
    }
    else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) {
        //DOM compliant
        _x = document.body.scrollLeft;
        _y = document.body.scrollTop;
    }
    else if (document.documentElement &&
			 (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {
        //IE6 standards compliant mode
        _x = document.documentElement.scrollLeft;
        _y = document.documentElement.scrollTop;
    }

    return { x: _x, y: _y };
}
//-------------------------------------------------------
/**
*  Returns monitor's surface size (less ~ 30px, probably for Windows task bar and any window's height.
*  { width, height }
*
*  Interesting link: http://llbest.com/BrowserInfo.php
*/
function utils_getScreenAvailableWidthHeight() {
    var _width = self.screen.availWidth;
    var _height = self.screen.availHeight;

    return { width: _width, height: _height };
}
//-------------------------------------------------------
/**
*  Returns the visible (client) size of the document in browser: { width, height }
*
*  NOTE: this is what should be used for printer setup calculations: represents actual document size, 
*        as currently displayed in browser. Depends on browser's size.
*
*  Interesting link: http://llbest.com/BrowserInfo.php
*/
function utils_getScrollWidthHeight() {
    var _width = document.body.scrollWidth ? document.body.scrollWidth : document.documentElement.scrollWidth;
    var _height = document.body.scrollHeight ? document.body.scrollHeight : document.documentElement.scrollHeight; ;

    return { width: _width, height: _height };

}
//-------------------------------------------------------
/**
*  Calculates and returns screen width/height (pixels) for a particular format of printed page.
*  Calculation is made at runtime so that 227 space characters in Arial format / size 8 fit chosen printed page width.
*
*  Recognized formats (pageFormat) are:
*  	- "A3 Wide" (12.01" x 17.99")
*	- "A3" (11.69" x 16.54")
*	- "A4" (8.27" x 11.69")
*	- "A5" (5.83" x 8.27")
*	- "B4" (10.12" x 14.33")
*	- "B5" (7.17" x 10.12")
*	- "12" x 18" (12.01" x 17.99")
*	- "Ledger" (11" x 17")
*	- "Legal" (8.5" x 14")
*	- "Executive" (7.25" x 10.5")
*	- "Invoice" (5.5" x 8.5")
*	- "Foolscap" (8.5" x 13")
*	- "8K" (10.63" x 15.35")
*	- "16K" (7.68" x 10.63")
*	- "DL" (4.33" x 8.66")
*	- "C5" (6.38" x 9.02")
*	- "COM10" (4.12" x 9.5")
*	- "Monarch" (3.88" x 7.5")
*	- "Letter" (8.5" x 11")
*
*  "isLandscape" flag indicates page rotation by 90 degrees. By default, it is false/null. (if not provided)
*
*  "leftMargin", "rightMargin", "topMargin" and "bottomMargin" default to "0.75in", "1in", "0.75in" and "1in"
*  if not provided (null).
*/
function utils_getWidthHeightInPixelsForPrintedPageFormat(pageFormat, isLandscape, leftMargin, rightMargin, topMargin, bottomMargin) {
    if (!leftMargin)
        leftMargin = "0.75in";

    if (!rightMargin)
        rightMargin = "0.75in";

    if (!topMargin)
        topMargin = "0.75in";

    if (!bottomMargin)
        bottomMargin = "0.75in";

    if (pageFormat == "A3 Wide") { printedPageWidth = "12.01in"; printedPageHeight = "17.99in"; }
    else if (pageFormat == "A3") { printedPageWidth = "11.69in"; printedPageHeight = "16.54in"; }
    else if (pageFormat == "A4") { printedPageWidth = "8.27in"; printedPageHeight = "11.69in"; }
    else if (pageFormat == "A5") { printedPageWidth = "5.83in"; printedPageHeight = "8.27in"; }
    else if (pageFormat == "B4") { printedPageWidth = "10.12in"; printedPageHeight = "14.33in"; }
    else if (pageFormat == "B5") { printedPageWidth = "7.17in"; printedPageHeight = "10.12in"; }
    else if (pageFormat == "12 x 18") { printedPageWidth = "12.01in"; printedPageHeight = "17.99in"; }
    else if (pageFormat == "Ledger") { printedPageWidth = "11in"; printedPageHeight = "17in"; }
    else if (pageFormat == "Legal") { printedPageWidth = "8.5in"; printedPageHeight = "14in"; }
    else if (pageFormat == "Executive") { printedPageWidth = "7.25in"; printedPageHeight = "10.5in"; }
    else if (pageFormat == "Invoice") { printedPageWidth = "5.5in"; printedPageHeight = "8.5in"; }
    else if (pageFormat == "Foolscap") { printedPageWidth = "8.5in"; printedPageHeight = "13in"; }
    else if (pageFormat == "8K") { printedPageWidth = "10.63in"; printedPageHeight = "15.35in"; }
    else if (pageFormat == "16K") { printedPageWidth = "7.68in"; printedPageHeight = "10.63in"; }
    else if (pageFormat == "DL") { printedPageWidth = "4.33in"; printedPageHeight = "8.66in"; }
    else if (pageFormat == "C5") { printedPageWidth = "6.38in"; printedPageHeight = "9.02in"; }
    else if (pageFormat == "COM10") { printedPageWidth = "4.12in"; printedPageHeight = "9.5in"; }
    else if (pageFormat == "Monarch") { printedPageWidth = "3.88in"; printedPageHeight = "7.5in"; }
    else /* if (pageFormat == "Letter") */{ printedPageWidth = "8.5in"; printedPageHeight = "11in"; }

    if (isLandscape) {
        var tmp = printedPageWidth;
        printedPageWidth = printedPageHeight;
        printedPageHeight = tmp;
    }

    printedPageWidth =
		"" +
		(utils_screenParseFloat(printedPageWidth)
		 - (leftMargin ? utils_screenParseFloat(leftMargin) : 0)
		 - (rightMargin ? utils_screenParseFloat(rightMargin) : 0)
		) +
		"in";
    printedPageHeight =
		"" +
		(utils_screenParseFloat(printedPageHeight)
		 - (topMargin ? utils_screenParseFloat(topMargin) : 0)
		 - (bottomMargin ? utils_screenParseFloat(bottomMargin) : 0)
		)
		+ "in";

    return utils_getWidthHeightInPixelsForPrintedPageWidthHeight(printedPageWidth, printedPageHeight);
}
//-------------------------------------------------------
/**
* Calculates and returns screen width/height (pixels) for a particular width/height (inch) of printed page.
* Calculation is made at runtime so that 227 space characters in Arial format / size 8 fit chosen printed page width.
*/
function utils_getWidthHeightInPixelsForPrintedPageWidthHeight(printedPageWidth, printedPageHeight, leftMargin, rightMargin, topMargin, bottomMargin) {
    var tmpReferenceContainer = document.createElement("div");
    tmpReferenceContainer.style.position = 'absolute';
    tmpReferenceContainer.style.left = '0';
    tmpReferenceContainer.style.top = '0';
    tmpReferenceContainer.style.width = printedPageWidth;
    tmpReferenceContainer.style.height = printedPageHeight;
    tmpReferenceContainer.style.backgroundColor = '#FFFFFF';
    tmpReferenceContainer.style.border = "1px solid black";
    tmpReferenceContainer.style.zIndex = '14141';
    document.body.appendChild(tmpReferenceContainer);
    var rect = utils_$rect(tmpReferenceContainer);
    document.body.removeChild(tmpReferenceContainer);

    return { width: rect.width, height: rect.height };
}
//-------------------------------------------------------
/**
* Intended essentially only for development, will display a line corresponding
* to the right border of a "letter" format page in landscape mode.
* Useful to get a visual of page widths, e.g. when they contain grids, to fit 
* size when printing.
* 
* Enable/Disable everywhere in the code by setting global variable: gShowPageLimitAtRight.
*/
function utils_DEBUG_displayRightLimitForLetterPageFormatInLandscapeMode() {
    if (gShowPageLimitAtRight) {
        var paperType = 'letter';
        var paperLandscapeMode = true;
        var pageRect = utils_getWidthHeightInPixelsForPrintedPageFormat(paperType, paperLandscapeMode);

        var pageLimitDiv = document.createElement('div');
        pageLimitDiv.id = 'pageLimitDiv';
        pageLimitDiv.style.position = 'absolute';
        pageLimitDiv.style.left = '' + pageRect.width;
        pageLimitDiv.style.top = '0px';
        pageLimitDiv.style.width = '2px';
        pageLimitDiv.style.height = '100%';
        pageLimitDiv.style.borderLeft = '2px solid red';
        pageLimitDiv.style.zIndex = '14141';
        document.body.appendChild(pageLimitDiv);

        var pageLimitDivText = document.createElement('div');
        pageLimitDivText.id = 'pageLimitDivText';
        pageLimitDivText.style.position = 'absolute';
        pageLimitDivText.style.left = '' + (1 + pageRect.width);
        pageLimitDivText.style.top = '0px';
        pageLimitDivText.style.border = '1px solid red';
        pageLimitDivText.style.backgroundColor = '#FFFFFF';
        pageLimitDivText.style.zIndex = '14142';
        pageLimitDivText.innerHTML = "<span style='padding-left: 5px; padding-right: 5px; padding-top: 2px; padding-bottom: 2px; background-color: #FFFFFF; color: #0000FF; font-family: Arial; font-size: 10px;'>Paper type: " + paperType + ", mode: " + (paperLandscapeMode ? "landscape" : "portrait") + ", width: " + pageRect.width + "px<br>To remove this message, clear 'gShowPageLimitAtRight' flag in utils.js</span>";
        pageLimitDivText.onmouseover = function() {
            var obj1 = utils_$('pageLimitDiv');
            var obj2 = this;
            obj1.style.display = obj2.style.display = 'none';

            setTimeout(function() { obj1.style.display = obj2.style.display = 'block'; }, 1000);
        }

        document.body.appendChild(pageLimitDiv);
        document.body.appendChild(pageLimitDivText);
    }
}
//-------------------------------------------------------
/**
* Creates and attaches a HTML element to anothter HTML element.
* htmlId: element to attach to
* innerHTML: inner html code for the new/attached html element
* width: width(pixels) for the new/attached html element
* height: height(pixels) for the new/attached html element
* xAlignmentType: "LEFT", "CENTER", "RIGHT"
* yAlignmentType: "TOP", "CENTER", "BOTTOM"
* optionalOffsetX
* optionalOffsetY
* Returns the new message object (html object), null (0) if errors encountered. 
*/
function utils_hookMessage_byId(
							htmlId,
							innerHTML,
							width,
							height,
							xAlignmentType,
							yAlignmentType,
							optionalOffsetX,
							optionalOffsetY
						 ) {
    var htmlObj = utils_$(htmlId);
    if (!htmlObj) {
        alert("utils_hookMessage() - No such element to attach message to: id=" + htmlId);
        return 0;
    }

    utils_hookMessage_byObj(
							htmlObj,
							innerHTML,
							width,
							height,
							xAlignmentType,
							yAlignmentType,
							optionalOffsetX,
							optionalOffsetY
						 );
}
//-------------------------------------------------------
/**
* Creates and attaches a HTML element to anothter HTML element.
* htmlId: element to attach to
* innerHTML: inner html code for the new/attached html element
* width: width(pixels) for the new/attached html element
* height: height(pixels) for the new/attached html element
* xAlignmentType: "LEFT", "CENTER", "RIGHT"
* yAlignmentType: "TOP", "CENTER", "BOTTOM"
* optionalOffsetX
* optionalOffsetY
* Returns the new message object (html object), null (0) if errors encountered. 
*/
function utils_hookMessage_byObj(
							htmlObj,
							innerHTML,
							width,
							height,
							xAlignmentType,
							yAlignmentType,
							optionalOffsetX,
							optionalOffsetY
						 ) {
    try {
        var rectHtmlObj = utils_$rect(htmlObj);

        var messageDiv = document.createElement("div");
        messageDiv.style.left = "0px";
        messageDiv.style.top = "0px";
        messageDiv.style.width = "" + width;
        messageDiv.style.height = "" + height;
        messageDiv.style.backgroundColor = "transparent";
        messageDiv.style.position = "absolute";
        messageDiv.style.zIndex = "1414";
        messageDiv.style.verticalAlign = "top";
        messageDiv.style.display = "none";
        messageDiv.innerHTML = innerHTML;
        document.body.appendChild(messageDiv);

        var x = rectHtmlObj.x;
        var y = rectHtmlObj.y;

        if (optionalOffsetX)
            x += optionalOffsetX;

        if (optionalOffsetY)
            y += optionalOffsetY;

        if (xAlignmentType == "LEFT") {
        }
        else if (xAlignmentType == "CENTER") {
            x += (rectHtmlObj.width - width) / 2;
        }
        else if (xAlignmentType == "RIGHT") {
            x += rectHtmlObj.width - width;
        }
        else
            alert("utils_hookMessage_byObj() - Invalid mode: xAlignmentType=" + xAlignmentType);

        if (yAlignmentType == "TOP") {
            y -= height;
        }
        else if (yAlignmentType == "CENTER") {
            y += (rectHtmlObj.height - height) / 2;
        }
        else if (yAlignmentType == "BOTTOM") {
            y += rectHtmlObj.height;
        }
        else
            alert("utils_hookMessage_byObj() - Invalid mode: xAlignmentType=" + xAlignmentType);

        messageDiv.style.left = "" + x + "px";
        messageDiv.style.top = "" + y + "px";
        messageDiv.id = "msg_" + (++gUtils_indexIdHookMessages);
        messageDiv.style.display = "block";

        return messageDiv;
    }
    catch (ex) {
        alert("EXCEPTION - utils_hookMessage_byObj(): " + ex);

        return 0;
    }
}
//-------------------------------------------------------
function utils_dettachChangeMonitorById(htmlId) {
    utils_dettachChangeMonitorByObj(utils_$(htmlId));
}
//-------------------------------------------------------
function utils_dettachChangeMonitor(htmlObj) {
    if (!htmlObj)
        return;

    try {
        if (htmlObj._ChgMon_enabled) {
            htmlObj._ChgMon_enabled = false;
            htmlObj._ChgMon_changeMonitorFn = function() { };
        }
    }
    catch (e) {
        //just a protection, in case htmlObj._ChgMon_changeMonitorFn change fails
        //only "htmlObj._ChgMon_enabled = false;" is essential
    }
}
//-------------------------------------------------------
function utils_attachChangeMonitorById(htmlId, idleTimeToFireFn_msec, fn, oneTimeOnly, fnArgs) {
    utils_attachChangeMonitor(utils_$(htmlId), idleTimeToFireFn_msec, fn, oneTimeOnly, fnArgs);
}
//-------------------------------------------------------
/**
* Sets up and launches a monitoring of an object's value property:
*   1. waits until the value is changed
*   2. waits for value to settle, meaning unchanged for idleTimeToFireFn_msec
*   3. executes function fn: function(){ ...... }
*   4. resumes (1.) unless oneTimeOnly is set
*/
function utils_attachChangeMonitor(htmlObj, idleTimeToFireFn_msec, fn, oneTimeOnly, fnArgs) {
    if (!htmlObj) {
        alert("ERROR - utils_attachChangeMonitor(): htmlObj is null.");
        return;
    }

    htmlObj._ChgMon_idleTimeToFireFn_msec = idleTimeToFireFn_msec;
    htmlObj._ChgMon_oneTimeOnly = oneTimeOnly;
    htmlObj._ChgMon_changeDirection = 0; // -1: text has reduced in size, 1: text has increased in size
    if (!utils_getObjectValue(htmlObj))
        utils_setObjectValue(htmlObj, "");

    htmlObj._ChgMon_changeMonitorInit = function() {
        htmlObj._ChgMon_refValue = utils_getObjectValue(htmlObj);
        htmlObj._ChgMon_refTime = (new Date()).getTime();
        htmlObj._ChgMon_refValueIsNew = 0;
        htmlObj._ChgMon_enabled = true;
    };

    htmlObj._ChgMon_changeMonitorFn = function(args) {
        try {
            if (!htmlObj._ChgMon_enabled)
                return;

            var val = utils_getObjectValue(htmlObj);

            if (val != htmlObj._ChgMon_refValue) {
                try {
                    htmlObj._ChgMon_changeDirection =
			            (val.length < htmlObj._ChgMon_refValue.length) ? -1 : 1;
                }
                catch (exChgDir) {
                    htmlObj._ChgMon_changeDirection = 0;
                }

                htmlObj._ChgMon_refValue = val;
                htmlObj._ChgMon_refTime = (new Date()).getTime();
                htmlObj._ChgMon_refValueIsNew = 1;
            }
            else if (htmlObj._ChgMon_refValueIsNew) {
                //check how long the value remained unchanged			
                var currentTime = (new Date()).getTime();
                var diff = currentTime - htmlObj._ChgMon_refTime;
                if (diff >= htmlObj._ChgMon_idleTimeToFireFn_msec) {

                    fn(args);

                    if (htmlObj._ChgMon_oneTimeOnly)
                        return;

                    htmlObj._ChgMon_changeMonitorInit();
                }
            }

            setTimeout(function() { htmlObj._ChgMon_changeMonitorFn(args); }, 50);
        }
        catch (ex) {
            //alert("EXCEPTION - utils_attachChangeMonitor(): " + ex);
        }
    };

    htmlObj._ChgMon_changeMonitorInit();
    htmlObj._ChgMon_changeMonitorFn(fnArgs);
}
//-------------------------------------------------------
/**
* Attaches a pop-up button to a html object that will show up only when hovering the html object
* and will disappear when moving away from the html object.
*
* Arguments:
*  - htmlObj: html object to attach button to
*  - buttonClassName: CSS class for the attached button (pass null/0 to ignore)
*  - zIndex: zIndex for the button, e.g. 1000
*  - xAlignmentType: X-alignment ("LEFT", "CENTER" or "RIGHT")
*  - yAlignmentType: Y-alignment ("TOP", "CENTER" or "BOTTOM")
*  - xOffset: offset from location determined for xAlignmentType
*  - yOffset: offset from location determined for yAlignmentType
*  - startOpacity: opacity to start from (0.0..1.0)
*  - fadeInSpeed: fade-in speed (0..1, 0.0: will never show up; 1.0: instant show up)
*  - fadeOutSpeed: fade-out speed (0..1, 0.0: will never disappear; 1.0: instant hiding)
*  - buttonCaption: button caption
*  - buttonWidth: button width (-1: use default value)
*  - buttonHeight: button height (-1: use default value)
*  - fn: function to be executed on button click: function(){ ...... }
*  - fnArg: function argument
*/
function utils_attachPopupButton(
	htmlObj, buttonClassName, zIndex,
	xAlignmentType, yAlignmentType, xOffset, yOffset,
	startOpacity, fadeInSpeed, fadeOutSpeed,
	buttonCaption, buttonWidth, buttonHeight,
	fn, fnArg) {
    try {
        if (!htmlObj) {
            alert("ERROR - utils_attachPopupButton(): htmlObj is null.");
            return;
        }

        var buttonObj = document.createElement("input");
        buttonObj.type = "button";
        buttonObj.value = buttonCaption;
        buttonObj.style.position = "absolute";
        buttonObj.style.left = "0px";
        buttonObj.style.top = "0px";
        if (buttonWidth != -1)
            buttonObj.style.width = "" + buttonWidth + "px";
        if (buttonHeight != -1)
            buttonObj.style.height = "" + buttonHeight + "px";
        buttonObj.style.zIndex = "" + zIndex;
        if (buttonClassName)
            buttonObj.className = buttonClassName;
        utils_applyOpacityObj(buttonObj, 0.0);
        document.body.appendChild(buttonObj);

        var rectHtmlObj = utils_$rect(htmlObj);
        var x = rectHtmlObj.x;
        var y = rectHtmlObj.y;

        if (xOffset)
            x += xOffset;

        if (yOffset)
            y += yOffset;

        var rectButtonObj = utils_$rect(buttonObj);

        if (xAlignmentType == "LEFT") {
        }
        else if (xAlignmentType == "CENTER") {
            x += (rectHtmlObj.width - rectButtonObj.width) / 2;
        }
        else if (xAlignmentType == "RIGHT") {
            x += rectHtmlObj.width - rectButtonObj.width;
        }
        else
            alert("utils_hookMessage_byObj() - Invalid mode: xAlignmentType=" + xAlignmentType);

        if (yAlignmentType == "TOP") {
            y -= rectButtonObj.height;
        }
        else if (yAlignmentType == "CENTER") {
            y += (rectHtmlObj.height - rectButtonObj.height) / 2;
        }
        else if (yAlignmentType == "BOTTOM") {
            y += rectHtmlObj.height;
        }
        else
            alert("utils_hookMessage_byObj() - Invalid mode: xAlignmentType=" + xAlignmentType);

        buttonObj.style.left = "" + x + "px";
        buttonObj.style.top = "" + y + "px";
        buttonObj.onclick = function() { fn(fnArg); };
        buttonObj._AnimBtn_opacityChangeDirection = 0; //1: fade-in, -1: fade-out, 0: no change
        buttonObj._AnimBtn_opacityChangeSpeed_fadeIn = Math.abs(fadeInSpeed) / 10.0; //considering 10msec step, see Timeout loop - in order to change opacity from 0 to 1 in 1 sec
        buttonObj._AnimBtn_opacityChangeSpeed_fadeOut = Math.abs(fadeOutSpeed) / 10.0;

        //Save current mouse handlers
        htmlObj._AnimBtn_oldOnMouseOver = htmlObj.onmouseover;
        htmlObj._AnimBtn_oldOnMouseOut = htmlObj.onmouseout;
        buttonObj._AnimBtn_terminated = false;

        buttonObj.onmouseover = htmlObj.onmouseover = function() {
            try {
                if (htmlObj._AnimBtn_oldOnMouseOver)
                    htmlObj._AnimBtn_oldOnMouseOver();

                buttonObj.focus();
                buttonObj._AnimBtn_targetOpacity = 1.0;
                buttonObj._AnimBtn_opacityChangeDirection = 1;
            }
            catch (ex) {
            }
        };

        buttonObj.onmouseout = htmlObj.onmouseout = function() {
            try {
                if (htmlObj._AnimBtn_oldOnMouseOut)
                    htmlObj._AnimBtn_oldOnMouseOut();

                buttonObj._AnimBtn_targetOpacity = 0.0;
                buttonObj._AnimBtn_opacityChangeDirection = -1;
            }
            catch (ex) {
            }
        };

        buttonObj._AnimBtn_targetOpacity = startOpacity;
        utils_applyOpacityObj(buttonObj, startOpacity);

        buttonObj._AnimBtn_animateOpacity = function() {
            try {
                if (!buttonObj._AnimBtn_terminated) {
                    var canChangeOpacity = true;

                    switch (buttonObj._AnimBtn_opacityChangeDirection) {
                        case 1:
                            var newOpacity = buttonObj._opacity + buttonObj._AnimBtn_opacityChangeSpeed_fadeIn;

                            if (newOpacity > buttonObj._AnimBtn_targetOpacity)
                                newOpacity = buttonObj._AnimBtn_targetOpacity;

                            if (buttonObj._opacity != newOpacity)
                                utils_applyOpacityObj(buttonObj, newOpacity);

                            break;
                        case -1:
                            var newOpacity = buttonObj._opacity - buttonObj._AnimBtn_opacityChangeSpeed_fadeOut;

                            if (newOpacity < buttonObj._AnimBtn_targetOpacity)
                                newOpacity = buttonObj._AnimBtn_targetOpacity;

                            if (buttonObj._opacity != newOpacity)
                                utils_applyOpacityObj(buttonObj, newOpacity);

                            break;
                        default:
                            break;
                    }

                    setTimeout(buttonObj._AnimBtn_animateOpacity, 10);
                }
            }
            catch (exAnimateOpacity) {
            }
        };

        buttonObj._AnimBtn_animateOpacity();

        return buttonObj;
    }
    catch (exFn) {
    }
}
//-------------------------------------------------------
/**
* Pop-up panel, alignment: right-top.
*/
function utils_attachPopupPanel(
	htmlObj, panelClassName, topZIndex,
	xOffset, yOffset,
	startOpacity, fadeInSpeed, fadeOutSpeed,
	panelWidth, panelHeight,
	innerHTML,
	optionalObjToIntegrateScrollFrom, //e.g. to adjust if inside a div with overflow
	functionToExecuteJustBeforePopingup,
	highlightTextColor, normalTextColor) {
    try {
        if (!htmlObj) {
            alert("ERROR - utils_attachPopupPanel(): htmlObj is null.");
            return;
        }

        var panelObj = document.createElement("div");
        panelObj.style.position = "absolute";
        panelObj.style.left = "0px";
        panelObj.style.top = "0px";
        if (panelWidth != -1)
            panelObj.style.width = "" + panelWidth + "px";
        if (panelHeight != -1)
            panelObj.style.height = "" + panelHeight + "px";
        if (panelClassName)
            panelObj.className = panelClassName;
        utils_applyOpacityObj(panelObj, 0.0);
        panelObj.innerHTML = innerHTML;
        document.body.appendChild(panelObj);

        panelObj._AnimPanel_opacityChangeDirection = 0; //1: fade-in, -1: fade-out, 0: no change
        panelObj._AnimPanel_opacityChangeSpeed_fadeIn = Math.abs(fadeInSpeed) / 10.0; //considering 10msec step, see Timeout loop - in order to change opacity from 0 to 1 in 1 sec
        panelObj._AnimPanel_opacityChangeSpeed_fadeOut = Math.abs(fadeOutSpeed) / 10.0;
        panelObj._AnimPanel_topZIndex = topZIndex;
        panelObj._htmlObj = htmlObj;
        panelObj._highlightTextColor = highlightTextColor ? highlightTextColor : '#0000FF';
        panelObj._normalTextColor = normalTextColor ? normalTextColor : '#000000';

        //Save current mouse handlers
        htmlObj._AnimPanel_oldOnMouseOver = htmlObj.onmouseover;
        htmlObj._AnimPanel_oldOnMouseOut = htmlObj.onmouseout;

        panelObj._AnimPanel_autoAdjustXY = function() {
            var rectHtmlObj =
		        panelObj._overrideDisplayOnObj ?
		        utils_$rect(panelObj._overrideDisplayOnObj) :
		        utils_$rect(panelObj._htmlObj);

            var x = rectHtmlObj.x;
            var y = rectHtmlObj.y;

            if (optionalObjToIntegrateScrollFrom) {
                x -= optionalObjToIntegrateScrollFrom.scrollLeft;
                y -= optionalObjToIntegrateScrollFrom.scrollTop;
            }

            if (xOffset)
                x += xOffset + rectHtmlObj.width;

            if (yOffset)
                y += yOffset;

            panelObj.style.left = "" + x + "px";
            panelObj.style.top = "" + y + "px";
        };

        htmlObj._tableCells = new Array();

        var tableCell = utils_findDomNode(htmlObj, 'td', false);

        while (tableCell) {
            htmlObj._tableCells.push(tableCell);

            tableCell = tableCell.nextSibling;
        }

        panelObj.onmouseover = htmlObj.onmouseover = function() {
            try {
                if (functionToExecuteJustBeforePopingup)
                    functionToExecuteJustBeforePopingup();

                panelObj._AnimPanel_terminated = false;

                panelObj._AnimPanel_autoAdjustXY();

                panelObj.style.display = "block";
                panelObj.style.zIndex = "" + panelObj._AnimPanel_topZIndex;
                panelObj.focus();
                panelObj._AnimPanel_targetOpacity = 1.0;
                panelObj._AnimPanel_opacityChangeDirection = 1;
            }
            catch (ex) {
            }
        };

        panelObj.onmouseout = htmlObj.onmouseout = function() {
            try {
                if (htmlObj._AnimPanel_oldOnMouseOut)
                    htmlObj._AnimPanel_oldOnMouseOut();

                panelObj._AnimPanel_targetOpacity = 0.0;
                panelObj._AnimPanel_opacityChangeDirection = -1;
            }
            catch (ex) {
            }
        };

        panelObj._AnimPanel_targetOpacity = startOpacity;
        utils_applyOpacityObj(panelObj, startOpacity);

        panelObj._AnimPanel_animateOpacity = function() {
            try {
                if (!panelObj._AnimPanel_terminated) {
                    var canChangeOpacity = true;

                    var objToDisplayOn =
                        panelObj._overrideDisplayOnObj ?
                        panelObj._overrideDisplayOnObj : panelObj._htmlObj;

                    switch (panelObj._AnimPanel_opacityChangeDirection) {
                        case 1:
                            objToDisplayOn.style.color = panelObj._highlightTextColor;

                            var newOpacity = panelObj._opacity + panelObj._AnimPanel_opacityChangeSpeed_fadeIn;

                            if (newOpacity > panelObj._AnimPanel_targetOpacity)
                                newOpacity = panelObj._AnimPanel_targetOpacity;

                            if (newOpacity == panelObj._AnimPanel_targetOpacity)
                                panelObj._AnimPanel_opacityChangeDirection = 0;

                            if (panelObj._opacity != newOpacity)
                                utils_applyOpacityObj(panelObj, newOpacity);

                            panelObj.style.zIndex = "" + (panelObj._AnimPanel_topZIndex - (1.0 - newOpacity) * 100);

                            break;
                        case -1:
                            objToDisplayOn.style.color = panelObj._highlightTextColor;

                            var newOpacity = panelObj._opacity - panelObj._AnimPanel_opacityChangeSpeed_fadeOut;

                            if (newOpacity < panelObj._AnimPanel_targetOpacity)
                                newOpacity = panelObj._AnimPanel_targetOpacity;

                            if (newOpacity == panelObj._AnimPanel_targetOpacity)
                                panelObj._AnimPanel_opacityChangeDirection = 0;

                            if (panelObj._opacity != newOpacity)
                                utils_applyOpacityObj(panelObj, newOpacity);

                            panelObj.style.zIndex = "" + (panelObj._AnimPanel_topZIndex - (1.0 - newOpacity) * 100);

                            break;
                        default:
                            if (!panelObj._AnimPanel_targetOpacity) {
                                panelObj.style.display = "none";
                                objToDisplayOn.style.color = panelObj._normalTextColor;
                            }

                            break;
                    }

                    setTimeout(panelObj._AnimPanel_animateOpacity, 10);
                }
            }
            catch (exAnimateOpacity) {
            }
        };

        panelObj._AnimPanel_animateOpacity();

        return panelObj;
    }
    catch (exFn) {
    }
}
//-------------------------------------------------------
/**
* Attaches logic to htmlObj_monitored so that on a value change, the value of htmlObj_monitoring 
* is adjusted automatically according to formula: fn(utils_getObjectValue(htmlObj_monitored)).
* "fn" should be something like: function fn(value){ ... }
*/
function utils_htmlValueMustFollowValue(htmlObj_monitored, htmlObj_monitoring, maxInputChars, fn) {
    try {
        if (!htmlObj_monitored) {
            alert("ERROR - utils_htmlValueMustFollowValue(): htmlObj_monitored is null");
            return;
        }

        if (!htmlObj_monitoring) {
            alert("ERROR - utils_htmlValueMustFollowValue(): htmlObj_monitoring is null");
            return;
        }

        htmlObj_monitored._ValueMustFollowValue_attached = true;
        htmlObj_monitored._ValueMustFollowValue_oldOnChange = htmlObj_monitored.onchange;
        htmlObj_monitored._ValueMustFollowValue_synchronizedValue = utils_getObjectValue(htmlObj_monitored);
        htmlObj_monitored._ValueMustFollowValue_shutdown = false;
        htmlObj_monitored._ValueMustFollowValue_maxInputChars = maxInputChars;
        htmlObj_monitored._ValueMustFollowValue_fn = fn;

        htmlObj_monitored._ValueMustFollowValue_run = function() {
            var monitored_val = utils_getObjectValue(htmlObj_monitored);

            if (htmlObj_monitored._ValueMustFollowValue_synchronizedValue != monitored_val) {
                if (monitored_val.length > htmlObj_monitored._ValueMustFollowValue_maxInputChars)
                    utils_setObjectValue(htmlObj_monitored, monitored_val.substring(0, htmlObj_monitored._ValueMustFollowValue_maxInputChars));

                htmlObj_monitored._ValueMustFollowValue_synchronizedValue = monitored_val;

                if (fn)
                    fn(monitored_val);
            }

            if (!htmlObj_monitored._ValueMustFollowValue_shutdown)
                setTimeout(htmlObj_monitored._ValueMustFollowValue_run, 50);
            else
                htmlObj_monitored._ValueMustFollowValue_attached = false;
        };

        htmlObj_monitored.onblur = function() {
            htmlObj_monitored._ValueMustFollowValue_shutdown = true;

            setTimeout(htmlObj_monitored.shutdown, 10);
        };

        htmlObj_monitored.shutdown = function() {
            fn(utils_getObjectValue(htmlObj_monitored));
        };

        htmlObj_monitored._ValueMustFollowValue_run();
    }
    catch (ex) {
    }
}
//-------------------------------------------------------
function utils_openPopup(url, params, closeLastOpenedPopupWindow, alwaysReturnFalse) {
    try {
        if (closeLastOpenedPopupWin && gUtils_lastOpenedPopupWindow)
            gUtils_lastOpenedPopupWindow.close();
    }
    catch (ex) {
    }

    gUtils_lastOpenedPopupWindow = window.open(url, '', params);

    if (gUtils_lastOpenedPopupWindow) {
        if (window.focus)
            setTimeout(function() { gUtils_lastOpenedPopupWindow.focus(); }, 200);
    }
    else
        alert("Please disable popup blockers and refresh (F5)");

    return alwaysReturnFalse ? false : gUtils_lastOpenedPopupWindow;
}
//-------------------------------------------------------
// alwaysReturnFalse: for URLs, e.g. <a href="
function utils_openPopupNoMenu(url, width, height, closeLastOpenedPopupWindow, alwaysReturnFalse) {
    try {
        if (closeLastOpenedPopupWin && gUtils_lastOpenedPopupWindow)
            gUtils_lastOpenedPopupWindow.close();
    }
    catch (ex) {
    }

    gUtils_lastOpenedPopupWindow = window.open(url, '', 'width=' + width + 'px, height=' + height + 'px, resizable=no, status=no, modal=yes, toolbar=no, menubar=no, directories=no, location=no, scrollbars=yes');

    if (gUtils_lastOpenedPopupWindow) {
        gUtils_lastOpenedPopupWindow.resizeTo(width, height);
        
        if (window.focus)
            setTimeout(function() { gUtils_lastOpenedPopupWindow.focus(); }, 200);
    }
    else
        alert("Please disable popup blockers and refresh (F5)");
    
    return alwaysReturnFalse ? false : gUtils_lastOpenedPopupWindow;
}
//-------------------------------------------------------
function utils_showButton(htmlId, visibleFlag) {
    utils_showButtonObj(utils_$(htmlId), visibleFlag);
}
//-------------------------------------------------------
function utils_showButtonObj(btnObj, visibleFlag) {
    if (btnObj)
        btnObj.style.display = (visibleFlag ? "inline" : "none");
    else
        alert('ERROR - utils_showButtonObj(): btnObj is null.');
}
//-------------------------------------------------------
function utils_checkCheckboxObj(cbObj, checked) {
    cbObj.checked = checked;
}
//-------------------------------------------------------
function utils_enableButtonObj(btnObj, enabled) {
    btnObj.disabled = !enabled;
}
//-------------------------------------------------------
function utils_isButtonEnabled(btnObj) {
    return !btnObj.disabled;
}
//-------------------------------------------------------
function utils_enableButton(htmlId, enabled) {
    var btnObj = utils_$(htmlId);

    utils_enableButtonObj(btnObj, enabled);
}

//-------------------------------------------------------
function utils_makeReadOnlyObj(obj, readOnly) {
    obj.disabled = (readOnly ? "disabled" : "");
}
//-------------------------------------------------------
function utils_makeReadOnly(htmlId, makeReadOnly) {
    var obj = utils_$(htmlId);

    utils_makeReadOnlyObj(obj, makeReadOnly);
}
//-------------------------------------------------------
function utils_attachValueChangeDetector(htmlObj, fn) {
    try {
        if (!htmlObj) {
            alert("ERROR - utils_attachValueChangeDetector(): htmlObj is null");
            return;
        }

        htmlObj._ValueChangeDetector_attached = true;
        htmlObj._ValueChangeDetector_oldOnChange = htmlObj.onchange;
        htmlObj._ValueChangeDetector_synchronizedValue = utils_getObjectValue(htmlObj);
        htmlObj._ValueChangeDetector_shutdown = false;
        htmlObj._ValueChangeDetector_fn = fn;

        htmlObj._ValueChangeDetector_run = function(htmlObj) {
            try {
                var v = utils_getObjectValue(htmlObj);

                if (htmlObj._ValueChangeDetector_synchronizedValue != v) {
                    htmlObj._ValueChangeDetector_synchronizedValue = v;
                    fn();
                }

                if (!htmlObj._ValueChangeDetector_shutdown)
                    setTimeout(function() { htmlObj._ValueChangeDetector_run(htmlObj); }, 50);
                else
                    htmlObj._ValueChangeDetector_attached = false;
            }
            catch (ex) {
                //alert("EXCEPTION - utils_attachValueChangeDetector(): " + ex);
            }
        };

        htmlObj.shutdown = function() {
            fn();
        };

        htmlObj._ValueChangeDetector_run(htmlObj);
    }
    catch (ex) {
    }
}
//-------------------------------------------------------
function utils_getFileExtension(fileName) {
    var indexDot = fileName.lastIndexOf(".");
    if (indexDot > 0)
        return fileName.substring(++indexDot);
    else
        return "";
}
//-------------------------------------------------------
function utils_getObjectValue(obj) {
    if (!obj)
        return 0;

    if (obj.value)
        return obj.value;
    else if (obj.innerHTML)
        return obj.innerHTML;
    else
        return "";
}
//-------------------------------------------------------
function utils_setObjectValue(obj, value) {
    if (!obj)
        return 0;

    try {
        obj.innerHTML = value ? value : "";
    }
    catch (ex) {
    }

    try {
        obj.value = value ? value : "";
    }
    catch (ex) {
    }
}
//-------------------------------------------------------
function utils_removeInvalidCharacters(obj, dontSimplifyWhiteSpace) {
    if (!obj)
        return;

    var text = utils_getObjectValue(obj);
    if (!text || !text.length)
        return; //nothing to do

    text = utils_stringReplace(
				utils_stringReplace(
					utils_stringReplace(
						utils_stringReplace(
							utils_stringReplace(
								utils_stringReplace(text, "\\", ""),
								"/", ""
							),
							">", ""
						),
						"<", ""
					),
					"\"", ""
				),
				"'", "`"
			);

    if (!dontSimplifyWhiteSpace) {
        text = utils_stringSimplifyWhiteSpace(text);
    }

    utils_setObjectValue(obj, text);
}
//-------------------------------------------------------
function utils_scrollBottom(obj) {
    if (!obj)
        return;

    obj.scrollTop = obj.scrollTop + obj.scrollHeight;
}
//-------------------------------------------------------
function utils_closePopupAndRefreshParent() {
    try {
        if (opener && opener.document && opener.document.location)
            opener.document.location.reload();
    }
    catch (ex1) {
    }

    try {
        if (window)
            window.close();
    }
    catch (ex2) {
    }
}
//=======================================================
function XMenu(menuCSSClass, normalMenuItemCSSClass, selectedMenuItemCSSClass, totalsCSSClass) {
    this._xMenuItems = new Array();

    this._htmlObj = document.createElement("div");
    this._htmlTotalsObj = document.createElement("div");

    this._htmlObj.className = menuCSSClass;
    this._htmlObj.style.position = "absolute";
    this.hide();
    this._normalMenuItemCSSClass = normalMenuItemCSSClass;
    this._selectedMenuItemCSSClass = selectedMenuItemCSSClass;
    this._selectedIndex = -1;
    document.body.appendChild(this._htmlObj);

    this._htmlTotalsObj.className = totalsCSSClass ? totalsCSSClass : menuCSSClass;
    this._htmlTotalsObj.style.position = "absolute";
    document.body.appendChild(this._htmlTotalsObj);
}
//-------------------------------------------------------
XMenu.prototype.addMenuItem = function(keyValue, textValue, onClickFn) {
    var xMenuItem = new XMenuItem(this, keyValue, textValue, onClickFn);
    this._xMenuItems.push(xMenuItem);
    xMenuItem._index = this.getItemCount() - 1;

    if (this._htmlObj && xMenuItem._htmlObj) {
        var brObj = document.createElement("br");
        brObj.style.position = "relative";
        brObj.style.display = "none";

        if (this._selectedIndex == -1) {
            xMenuItem._htmlObj.className = this._selectedMenuItemCSSClass;
            this._selectedIndex = xMenuItem._index;
        }
        else
            xMenuItem._htmlObj.className = this._normalMenuItemCSSClass;

        this._htmlObj.appendChild(xMenuItem._htmlObj);
        this._htmlObj.appendChild(brObj);
    }

    return xMenuItem;
}
//-------------------------------------------------------
XMenu.prototype.displayTotals = function(totalItemsCount) {
    var itemsVisible = this.getItemCount();

    this._htmlTotalsObj.innerHTML =
        (itemsVisible < totalItemsCount) ?
        ('(1..' + itemsVisible + ' of ' + totalItemsCount + ')') : ('(' + totalItemsCount + ')');
}
//-------------------------------------------------------
XMenu.prototype.show = function(x, y) {
    if (!this._htmlObj)
        return;

    this._htmlObj.style.left = "" + x;
    this._htmlObj.style.top = "" + y;

    for (var i = 0, n = this.getItemCount(); i < n; i++) {
        this._xMenuItems[i].show();
    }

    this._htmlObj.style.display = 'block';

    var rect = utils_$rect(this._htmlObj);
    this._htmlTotalsObj.style.left = "" + (rect.x + rect.width);
    this._htmlTotalsObj.style.top = "" + rect.y;
    this._htmlTotalsObj.style.display = 'block';
}
//-------------------------------------------------------
XMenu.prototype.hide = function() {
    for (var i = 0, n = this.getItemCount(); i < n; i++) {
        this._xMenuItems[i].hide();
    }

    this._htmlObj.style.display =
  	this._htmlTotalsObj.style.display = 'none';
}
//-------------------------------------------------------
XMenu.prototype.destroy = function() {
    if (this._htmlObj) {
        utils_removeAllChildren(this._htmlObj);

        document.body.removeChild(this._htmlObj);
        this._htmlObj = 0;

        document.body.removeChild(this._htmlTotalsObj);
        this._htmlTotalsObj = 0;
    }

    this._xMenuItems = new Array();
}
//-------------------------------------------------------
XMenu.prototype.getItemCount = function() {
    return this._xMenuItems.length;
}
//-------------------------------------------------------
XMenu.prototype.selectItem = function(xMenuItem) {
    this._selectedIndex = -1;

    for (var i = 0, n = this.getItemCount(); i < n; i++) {
        if (this._xMenuItems[i] == xMenuItem) {
            this._selectedIndex = i;
            this._xMenuItems[i]._htmlObj.className = this._selectedMenuItemCSSClass;
        }
        else
            this._xMenuItems[i]._htmlObj.className = this._normalMenuItemCSSClass;
    }
}
//-------------------------------------------------------
XMenu.prototype.selectItemUp = function() {
    if (this._selectedIndex <= 0)
        return;

    this.selectItem_byIndex(this._selectedIndex - 1);
}
//-------------------------------------------------------
XMenu.prototype.selectItemDown = function() {
    if (this._selectedIndex >= this.getItemCount() - 1)
        return;

    this.selectItem_byIndex(this._selectedIndex + 1);
}
//-------------------------------------------------------
XMenu.prototype.selectItem_byIndex = function(index) {
    this._selectedIndex = -1;

    for (var i = 0, n = this.getItemCount(); i < n; i++) {
        if (i == index) {
            this._selectedIndex = i;
            this._xMenuItems[i]._htmlObj.className = this._selectedMenuItemCSSClass;
        }
        else
            this._xMenuItems[i]._htmlObj.className = this._normalMenuItemCSSClass;
    }
}
//-------------------------------------------------------
XMenu.prototype.selectItem_byValue = function(value) {
    for (var i = 0, n = this.getItemCount(); i < n; i++) {
        if (this._xMenuItems[i]._htmlObj.innerHTML == value) {
            this._selectedIndex = i;
            this._xMenuItems[i]._htmlObj.className = this._selectedMenuItemCSSClass;
        }
        else
            this._xMenuItems[i]._htmlObj.className = this._normalMenuItemCSSClass;
    }
}
//-------------------------------------------------------
XMenu.prototype.getItemKey = function(index) {
    try {
        if (this._selectedIndex >= 0)
            return this._xMenuItems[index]._keyValue;
    }
    catch (ex) {
    }

    return "";
}
//-------------------------------------------------------
XMenu.prototype.getItemValue = function(index) {
    try {
        if (this._selectedIndex >= 0)
            return this._xMenuItems[index]._htmlObj.innerHTML;
    }
    catch (ex) {
    }

    return "";
}
//-------------------------------------------------------
XMenu.prototype.getSelectedItemValue = function() {
    return this.getItemValue(this._selectedIndex);
}
//-------------------------------------------------------
XMenu.prototype.getSelectedItemKey = function() {
    return this.getItemKey(this._selectedIndex);
}
//-------------------------------------------------------
XMenu.prototype.getSelectedItemIndex = function() {
    return this._selectedIndex;
}
//-------------------------------------------------------
function XMenuItem(xMenu, keyValue, textValue, onClickFn)//textValue is what is actually displayed
{
    this._xMenu = xMenu;
    this._keyValue = keyValue;
    this._htmlObj = document.createElement("div");
    this._htmlObj.style.position = "relative";
    this._htmlObj.innerHTML = textValue;
    this._htmlObj._xMenuItem = this;
    this._index = -1;
    this._onClickFn = onClickFn;

    xMenu._htmlObj.appendChild(this._htmlObj);

    this.hide();


    this._htmlObj.onmouseover = function() {
        xMenu.selectItem(this._xMenuItem);
    };

    this._htmlObj.onmousedown = function() {
    //this.onmousedownFlashMenuItem(0);
        if (this._xMenuItem._onClickFn)
            this._xMenuItem._onClickFn();
    };

    /*var menuItem = this;
    this._htmlObj.onmousedownFlashMenuItem = function(countDown) {
        if (countDown) {
            if (countDown % 2)
                setTimeout(function() {
                    menuItem._htmlObj.className = menuItem._xMenu._normalMenuItemCSSClass;
                    menuItem._htmlObj.onmousedownFlashMenuItem(--countDown);
                }, 30);
            else
                setTimeout(function() {
                    menuItem._htmlObj.className = menuItem._xMenu._selectedMenuItemCSSClass;
                    menuItem._htmlObj.onmousedownFlashMenuItem(--countDown);
                }, 20);
        }
        else {
            menuItem._htmlObj.className = menuItem._xMenu._selectedMenuItemCSSClass;
            menuItem._onClickFn();
        }
    };*/
}
//-------------------------------------------------------
XMenuItem.prototype.show = function() {
    if (!this._htmlObj)
        return;

    this._htmlObj.style.display = "block";
}
//-------------------------------------------------------
XMenuItem.prototype.hide = function() {
    if (this._htmlObj)
        this._htmlObj.style.display = "none";
}
//-------------------------------------------------------
XMenuItem.prototype.setSelected = function(isSelected, normalMenuItemCSSClass, selectedMenuItemCSSClass) {
    this._isSelected = isSelected;

    if (this._htmlObj) {
        this._htmlObj.style.zIndex = isSelected ? selectedMenuItemCSSClass : normalMenuItemCSSClass;
    }
}
//=======================================================
function utils_dettachValuePredictionMenu_ById(htmlId) {
    utils_dettachValuePredictionMenu(utils_$(htmlId));
}
//=======================================================
function utils_dettachValuePredictionMenu(htmlObj) {
    if (!htmlObj)
        return;

    if (htmlObj._xMenu) {
        htmlObj.onkeydown = function(e) { };
        htmlObj._xMenu.destroy();
        htmlObj._xMenu = 0;
    }
}
//=======================================================
function utils_attachValuePredictionMenu_ById(
	displayAtRight, maxMenuItems,
	htmlId, triggerMenuOnTextIdleMsec, allKeyValues,
	menuCSSClass, normalMenuItemCSSClass, selectedMenuItemCSSClass,
	afterActivationFn, textSearchMethod, displayTotals, totalsCSSClass,
	afterItemMatchFn) {
    var htmlObj = utils_$(htmlId);

    utils_attachValuePredictionMenu(
	    displayAtRight, maxMenuItems,
	    htmlObj, triggerMenuOnTextIdleMsec, allKeyValues,
	    menuCSSClass, normalMenuItemCSSClass, selectedMenuItemCSSClass,
	    afterActivationFn, textSearchMethod, displayTotals, totalsCSSClass,
	    afterItemMatchFn);
}
//=======================================================
/**
* Attaches automatically a pop-up menu to the right of a textbox having as menu items
* values that match the partial text typed in the textbox.
*    - displayAtRight: 
*        true (menu displayed on the right side of textbox)
*        false (just under textbox)
*    - maxMenuItems: limit on number of items displayed in pop-up menu
*    - htmlObj: textbox object
*    - triggerMenuOnTextIdleMsec: msec to wait since typing in textbox is stopped until
*      the menu is displayed
*    - allKeyValues: (Key-Values) XHashMap object. Values are displayed as menu items.
*    - menuCSSClass, normalMenuItemCSSClass, selectedMenuItemCSSClass: CSS class names
*    - afterActivationFn: function to execute after menu item selection. Can be null (0)
*      if no extra-processing is required
*    - textSearchMethod:
*        0 (default): anywhere
*        1: beginningOnly
*        2: wordBeginOnly (e.g. search for 'ho' prefixing each word 'hospital one' or 'a true hope'
*        NOTE: Add 16 to enforce case sensitive mode (e.g. 16=anywhere, 17=beginningOnly)
*    - displayTotals: will display items total count
*    - totalsCSSClass: CSS class name
*    - afterItemMatchFn: function to execute after the search for matches completes; provides
*      the number of matches as argument. (optional arg)
*/
function utils_attachValuePredictionMenu(
	displayAtRight, maxMenuItems,
	htmlObj, triggerMenuOnTextIdleMsec, allKeyValues,
	menuCSSClass, normalMenuItemCSSClass, selectedMenuItemCSSClass,
	afterActivationFn, textSearchMethod, displayTotals, totalsCSSClass,
	afterItemMatchFn) {
    if (!htmlObj) {
        alert("utils_attachValuePredictionMenu() - ERROR: htmlObj is null");
        return;
    }

    if (!textSearchMethod)
        textSearchMethod = 0;

    var current_xMenu = 0;

    var caseSensitive_textSearch;
    if (textSearchMethod >= 16) {
        caseSensitive_textSearch -= 16;
        caseSensitive_textSearch = true;
    }
    else
        caseSensitive_textSearch = false;

    htmlObj._predictionMenu_allKeyValues = allKeyValues;
    htmlObj._textSearchMethod = textSearchMethod;
    htmlObj._hasFocus = false;

    var fn = function(forceShowMenu) //if forceShowMenu=true, the menu will be shown even if value of htmlObj is empty
    {
        try {
            if (htmlObj._newMenuItemSelected) {
                htmlObj._newMenuItemSelected = false;
            }
            else {
                htmlObj._menuItemKey = 0;

                /*setTimeout(function(){					
                if (afterActivationFn)
                afterActivationFn(selectedItemKey);
                },
                300);*/
            }

            if (!current_xMenu && htmlObj._xMenu)
                current_xMenu = htmlObj._xMenu;

            if (current_xMenu) {
                current_xMenu.destroy();
                current_xMenu = 0;
            }
            htmlObj._xMenu = 0;

            var xMenu = new XMenu(menuCSSClass, normalMenuItemCSSClass, selectedMenuItemCSSClass, totalsCSSClass);

            var onClickFn = function() {
                if (xMenu) {
                    var selectedItemKey = xMenu.getSelectedItemKey();
                    var selectedItemValue = xMenu.getSelectedItemValue();
                    htmlObj._newMenuItemSelected = true;

                    utils_setObjectValue(htmlObj, selectedItemValue);
                    htmlObj._menuItemKey = selectedItemKey;

                    xMenu.destroy();
                    current_xMenu = xMenu = 0;

                    setTimeout(function() {
                        if (afterActivationFn)
                            afterActivationFn(selectedItemKey, selectedItemValue);
                    }, 0);
                }
            };
            xMenu._onClickFn = onClickFn;

            var value =
				caseSensitive_textSearch ?
				utils_getObjectValue(htmlObj) :
				utils_getObjectValue(htmlObj).toLowerCase();
            value = utils_trim(value);

            var totalMatches;
            if (forceShowMenu || value && value.length) {
                totalMatches = 0;
                var allKeys = htmlObj._predictionMenu_allKeyValues.getAllKeys_Ref();
                var allValues = htmlObj._predictionMenu_allKeyValues.getAllValues_Ref();

                var tokens_forValue = value.split(/[\s,;]+/);

                if (value.charAt(value.length - 1) == ',')
                    value = value.substring(0, value.length - 1);

                for (var i = 0, n = allValues.length; i < n; i++) {
                    var v =
						caseSensitive_textSearch ?
						allValues[i] :
						allValues[i].toLowerCase();

                    var match;
                    switch (htmlObj._textSearchMethod) {
                        case 0:
                            match = (v.indexOf(value) >= 0);
                            break;
                        case 1:
                            match = (v.indexOf(value) == 0);
                            break;
                        case 2: //wordBeginOnly
                            match = false;
                            var nTok_forValue = tokens_forValue.length;
                            var tokens = v.split(/[\s,;]+/); //split on ' ' or ',' or ';'
                            var nTok = tokens.length;
                            if (nTok_forValue == 1) {
                                //text typed contains only one token
                                for (var iTok = 0; iTok < nTok; iTok++) {
                                    var token = tokens[iTok];
                                    if (token.length && token.indexOf(value) == 0) {
                                        match = true;
                                        break;
                                    }
                                }
                            }
                            else {
                                //text typed contains more than one token;
                                // try matching full text entered: split into tokens and compare

                                if (nTok >= nTok_forValue) {
                                    //Create a temporary array with all elements of tokens_forValue
                                    //As these elements will be matched during search, they will be removed 
                                    //- in order to continue matching the remaining ones only
                                    var tmpTokens_forValue = new Array();
                                    for (var iTok_forValue = 0; iTok_forValue < nTok_forValue; iTok_forValue++) {
                                        if (tokens_forValue[iTok_forValue] &&
                                        tokens_forValue[iTok_forValue].length)
                                            tmpTokens_forValue.push(tokens_forValue[iTok_forValue]);
                                    }

                                    nTok_forValue = tmpTokens_forValue.length;

                                    match = true;
                                    for (var iTok_forValue = 0; iTok_forValue < nTok_forValue; iTok_forValue++) {
                                        var tmpToken_forValue = tmpTokens_forValue[iTok_forValue];

                                        var match_forValue = false;
                                        for (var iTok = 0; iTok < nTok; iTok++) {
                                            if (tokens[iTok].indexOf(tmpToken_forValue) == 0) {
                                                match_forValue = true;
                                                break;
                                            }
                                        }

                                        if (!match_forValue) {
                                            match = false;
                                            break;
                                        }
                                    }
                                }
                            }

                            break;
                        default:
                            match = false;
                            break;
                    }

                    if (match) {
                        ++totalMatches;

                        if (xMenu.getItemCount() < maxMenuItems)
                            xMenu.addMenuItem(allKeys[i], allValues[i], onClickFn);
                    }
                }

                if (displayTotals)
                    xMenu.displayTotals(totalMatches);
            }
            else
                totalMatches = htmlObj._predictionMenu_allKeyValues.getKeyCount();

            if (afterItemMatchFn)
                afterItemMatchFn(totalMatches, htmlObj._predictionMenu_allKeyValues.getKeyCount());

            current_xMenu = xMenu;
            htmlObj._xMenu = current_xMenu;
        }
        catch (ex) {
        }

        try {
            var nItems = current_xMenu.getItemCount();

            if (nItems &&
				(nItems > 1 || utils_getObjectValue(htmlObj).length != current_xMenu.getItemValue(0).length)) {
                var rect = utils_$rect(htmlObj);

                if (displayAtRight)
                    current_xMenu.show(rect.x + rect.width + 2, rect.y);
                else
                    current_xMenu.show(rect.x, rect.y + rect.height + 2);
            }
        }
        catch (ex) {
        }
    };

    utils_attachChangeMonitor(htmlObj, triggerMenuOnTextIdleMsec, fn, false);

    htmlObj._fn = function(forceShowMenu) {
        fn(forceShowMenu);
    }

    //htmlObj._onkeydown = htmlObj.onkeydown;//save it
    htmlObj.onkeydown = function(e) {
        try {
            if (current_xMenu) {
                var e = window.event ? window.event : e;
                var keyID = e ? e.keyCode : 0;

                if (keyID == 38) //ARROW UP
                {
                    current_xMenu.selectItemUp();
                    return utils_stopEvent(e);
                }
                else if (keyID == 40) //ARROW DOWN
                {
                    current_xMenu.selectItemDown();
                    return utils_stopEvent(e);
                }
                else if (keyID == 13) //ENTER
                {
                    current_xMenu._xMenuItems[current_xMenu._selectedIndex]._htmlObj.onmousedown();

                    return utils_stopEvent(e);
                }
            }
        }
        catch (ex) {
        }
    };

    htmlObj.onfocus = function() {

        this._hasFocus = true;
    };

    htmlObj.onblur = function() {

        this._hasFocus = false;

        setTimeout(function() {
            try {
                if (htmlObj._xMenu) {
                    htmlObj._xMenu.destroy();
                    htmlObj._xMenu = 0;
                }
            }
            catch (ex) {
            }
        },
		500); //must cover the time for executing this._htmlObj.onmousedown()
    };

    htmlObj.onclick = function() {
        try {
            if (htmlObj._fn)
                htmlObj._fn();
        }
        catch (ex) {
        }
    };
}
//-------------------------------------------------------
function utils_stopEvent(e) {
    if (!e)
        return false;

    e.cancelBubble = true;

    if (e.stopPropagation)
        e.stopPropagation();

    if (e.preventDefault)
        e.preventDefault();

    return false;
}
//-------------------------------------------------------
function utils_attachRemainingCharactersMonitor(
    htmlObj, maxCharacters, htmlObjToLogRemainingCharsTo, maxCharsReachedColor) {
    if (!htmlObj) {
        alert("ERROR - utils_attachRemainingCharactersMonitor(): htmlObj is null");
        return;
    }

    if (maxCharacters <= 0)
        return; //no limit

    if (!htmlObjToLogRemainingCharsTo) {
        alert("ERROR - utils_attachRemainingCharactersMonitor(): htmlObjToLogRemainingCharsTo is null");
        return;
    }

    htmlObjToLogRemainingCharsTo._normalForeColor = htmlObjToLogRemainingCharsTo.style.color;

    utils_attachValueChangeDetector(
        htmlObj,
        function() {
            var val = htmlObj.value; //utils_getObjectValue(htmlObj);

            if (val.length >= maxCharacters) {
                val = val.substring(0, maxCharacters);

                htmlObj.value = val; //utils_setObjectValue(htmlObj, val);

                htmlObjToLogRemainingCharsTo.style.color = maxCharsReachedColor;
            }
            else
                htmlObjToLogRemainingCharsTo.style.color =
                    htmlObjToLogRemainingCharsTo._normalForeColor;

            utils_setObjectValue(
                htmlObjToLogRemainingCharsTo,
                '' + val.length + '/' + maxCharacters);
        });
}
//-------------------------------------------------------
function utils_flashTooltip(htmlId, tooltipAttribute, tooltipClassName) {
    var htmlObj = utils_$(htmlId);
    if (!htmlObj) {
        alert('utils_flashTooltip: No such object: ' + htmlId);
        return;
    }

    utils_flashTooltipObj(htmlObj, tooltipAttribute, tooltipClassName);
}
//-------------------------------------------------------
/**
* Make sure the attribute 'customtooltip' is set on htmlObj and 
* that 'customtooltip' class is defined in CSS styles. If cssClassName
* is provided (not null), that class name will be used instead.
*
* Final note: make sure that title='' so that default tooltip does not 
* interfere with the custom one, defined by this function.
*
* example: <input type="radio" name="rb" title='' customtooltip="All users" onclick
*/
function utils_flashTooltipObj(htmlObj, cssClassName, xOffset, yOffset, optionalOverrideTooltip, optionalOpacityStep) {
    if (!htmlObj) {
        alert('utils_flashTooltipObj: Object is null');
        return;
    }

    htmlObj._rect = utils_$rect(htmlObj);

    if (!htmlObj._tooltipObj)
        htmlObj._tooltipObj = document.createElement("div");

    if (!xOffset)
        xOffset = -4;

    if (!yOffset)
        yOffset = -22;

    htmlObj._tooltipObj.style.position = 'absolute';
    htmlObj._tooltipObj.style.left = '' + (htmlObj._rect.x + xOffset);
    htmlObj._tooltipObj.style.top = '' + (htmlObj._rect.y + yOffset);
    htmlObj._tooltipObj.style.zIndex = '25000';
    htmlObj._tooltipObj.style.cursor = 'normal';
    htmlObj._tooltipObj._startOpacity = 0.85;
    htmlObj._tooltipObj._opacityStep = optionalOpacityStep ? optionalOpacityStep : -0.07;
    htmlObj._tooltipObj.innerHTML = optionalOverrideTooltip ? optionalOverrideTooltip : htmlObj.customtooltip;
    htmlObj._tooltipObj.className = cssClassName ? cssClassName : 'customtooltip';
    document.body.appendChild(htmlObj._tooltipObj);
    utils_applyOpacityObj(htmlObj._tooltipObj, htmlObj._tooltipObj._startOpacity);
    htmlObj._tooltipObj._rect = utils_$rect(htmlObj._tooltipObj);

    htmlObj._flashTooltip = function() {
        try {
            var isMouseOutside = utils_isMouseOutsideRectangle(htmlObj._rect);

            if (isMouseOutside) {
                var newOpacity = htmlObj._tooltipObj._opacity + htmlObj._tooltipObj._opacityStep;
                if (newOpacity < 0.0)
                    newOpacity = 0.0;

                utils_applyOpacityObj(htmlObj._tooltipObj, newOpacity);
            }
            else if (htmlObj._tooltipObj._opacity != htmlObj._tooltipObj._startOpacity)
                utils_applyOpacityObj(htmlObj._tooltipObj, htmlObj._tooltipObj._startOpacity);

            if (htmlObj._tooltipObj._opacity > 0.0) {
                setTimeout(htmlObj._flashTooltip, 50);
            }
            else {
                document.body.removeChild(htmlObj._tooltipObj);
                htmlObj._tooltipObj = 0;
            }
        }
        catch (ex) {
        }
    };

    htmlObj._flashTooltip();
}
//-------------------------------------------------------
function utils_createDivInMiddleOfScreenAndBlindPage(width, height, flagShow, overrideTop, overrideBlindingOpacity, overrideZIndex) {
    var docBlindObj = document.createElement('div');
    docBlindObj.style.position = 'absolute';
    docBlindObj.style.left = '0';
    docBlindObj.style.top = '0';
    docBlindObj.style.width = '100%';
    docBlindObj.style.height = '100%';
    docBlindObj.style.backgroundColor = '#000000';
    docBlindObj.style.zIndex = overrideZIndex ? ('' + overrideZIndex) : '50000';
    docBlindObj.style.display = flagShow ? "" : "none";
    docBlindObj.style.textAlign = 'center';
    utils_applyOpacityObj(docBlindObj, overrideBlindingOpacity ? overrideBlindingOpacity : 0.7);
    document.body.appendChild(docBlindObj);

    var rect = utils_$rect(docBlindObj);

    var divInMiddleOfScreenObj = document.createElement('div');
    divInMiddleOfScreenObj.style.position = 'absolute';
    divInMiddleOfScreenObj.style.zIndex = overrideZIndex ? ('' + (overrideZIndex + 1)) : '50001';
    divInMiddleOfScreenObj.style.display = flagShow ? "" : "none";
    divInMiddleOfScreenObj.style.top = '' + (overrideTop ? overrideTop : ((rect.height - height) / 2)) + 'px';
    divInMiddleOfScreenObj.style.left = '' + ((rect.width - width) / 2) + 'px';
    divInMiddleOfScreenObj.style.width = width ? ('' + width + 'px') : '100%';
    divInMiddleOfScreenObj.style.height = height ? ('' + height + 'px') : '100%';
    divInMiddleOfScreenObj.style.backgroundColor = 'White';
    //divInMiddleOfScreenObj.style.overflow = 'auto';//this breaks the search in InjuryClaimDetail.aspx?Mode=Add
    divInMiddleOfScreenObj._docBlindObj = docBlindObj;
    document.body.appendChild(divInMiddleOfScreenObj);

    divInMiddleOfScreenObj.destroy =
        function() {
            document.body.removeChild(this._docBlindObj);
            document.body.removeChild(this);
        };

    return divInMiddleOfScreenObj;
}
//-------------------------------------------------------
function utils_displaySubmitMessage(message, overrideWidth) {
    if (!message)
        message = "Saving...";

    var width = overrideWidth ? overrideWidth : 300;

    var htmlObj = utils_createDivInMiddleOfScreenAndBlindPage(width, 30, true, 300);
    htmlObj.className = 'divInMiddleOfScreen';
    htmlObj.style.textAlign = 'center';
    htmlObj.style.verticalAlign = 'middle';
    htmlObj.innerHTML = message;

    return htmlObj;
}
//-------------------------------------------------------
//imageLoadTimeoutMSec = -1: infinite wait, until all images are loaded
function utils_preloadImages(imageSrcArray, fnToDoAfterImagesLoaded, imageLoadTimeoutMSec) {
    if (!document.images)
        return;

    if (!gUtils_preloadedImgObjArray)
        gUtils_preloadedImgObjArray = new Array();

    for (var i = 0, n = imageSrcArray.length; i < n; i++) {
        var imgObj = new Image();
        imgObj._index = i;
        imgObj._loaded = false;
        imgObj._loadedFn = function() { this._loaded = true; }
        imgObj.onload = imgObj._loadedFn;
        imgObj.src = imageSrcArray[i];
        gUtils_preloadedImgObjArray.push(imgObj);
    }

    utils_preloadImages_waitForImagesToLoad(fnToDoAfterImagesLoaded, imageLoadTimeoutMSec);
}
//-------------------------------------------------------
function utils_preloadImages_waitForImagesToLoad(fnToDoAfterImagesLoaded, imageLoadTimeoutMSec) {
    var foundImageNotLoaded = false;

    for (var i = 0, n = gUtils_preloadedImgObjArray.length; i < n; i++) {
        if (!gUtils_preloadedImgObjArray[i]._loaded) {
            foundImageNotLoaded = true;
            break;
        }
    }

    if (foundImageNotLoaded) {
        if (imageLoadTimeoutMSec > 0)
            imageLoadTimeoutMSec -= 100;

        if (imageLoadTimeoutMSec > 0 ||
            imageLoadTimeoutMSec == -1) //infinite wait
        {
            setTimeout(function() {
                utils_preloadImages_waitForImagesToLoad(fnToDoAfterImagesLoaded, imageLoadTimeoutMSec);
            }, 50);

            return;
        }
    }

    if (fnToDoAfterImagesLoaded)
        fnToDoAfterImagesLoaded();
}
//-------------------------------------------------------
/*
* This functions returns an array containing 36 points to draw an
* ellipse.
*
* @param noOfPoints {int} Number of points
* @param x {double} X coordinate
* @param y {double} Y coordinate
* @param a {double} Semimajor axis
* @param b {double} Semiminor axis
* @param beta360 {double} Angle of the ellipse
* @param out_xArray Array object to be populated with X values
* @param out_yArray Array object to be populated with Y values
*/
function utils_calculateEllipse(noOfPoints, x, y, a, b, beta360, out_xArray, out_yArray) {
    // Angle is given by Degree Value
    var degreesToRadians = Math.PI / 180;
    var beta = -beta360 * degreesToRadians;
    var sinbeta = Math.sin(beta);
    var cosbeta = Math.cos(beta);
    var alphaStep = 2.0 * Math.PI / noOfPoints;
    var alpha = 0; //(noOfPoints % 2) ? 0 : alphaStep/2.0;

    for (var i = 0; i < noOfPoints; i++, alpha += alphaStep) {
        var sinAlpha = Math.sin(alpha);
        var cosAlpha = Math.cos(alpha);

        var X = x + a * cosAlpha * cosbeta - b * sinAlpha * sinbeta;
        var Y = y + a * cosAlpha * sinbeta + b * sinAlpha * cosbeta;

        out_xArray.push(X);
        out_yArray.push(Y);
    }
}
//-------------------------------------------------------
function utils_arrayIndexOf(arrayObj, obj) {
    var i = arrayObj.length;
    while (--i >= 0) {
        if (arrayObj[i] == obj)
            return i;
    }

    return -1;
};
//-------------------------------------------------------
function utils_arrayContains(arrayObj, obj) {
    return (utils_arrayIndexOf(arrayObj, obj) > -1);
};
//-------------------------------------------------------
//creates a copy of the entire array (produces a new array pointing to the same objects
//the original array contained)
function utils_arrayCopy(arrayObj) {
    return arrayObj ? arrayObj.slice(0, arrayObj.length) : arrayObj;
}
//-------------------------------------------------------
//returns a new array consisting of all elements of arrayObj1 and arrayObj2
//(arrayObj1 and arrayObj2 are not modified)
function utils_arrayAppendArray(arrayObj1, arrayObj2) {
    return arrayObj1.concat(arrayObj2);
}
//-------------------------------------------------------
function utils_arrayToString(arrayObj, separator) //e.g. to produce: "100:200:300:..."
{
    var ret = '';

    if (!separator)
        separator = ':';

    if (arrayObj)
        for (var i = 0, n = arrayObj.length; i < n; i++) {
        if (i)
            ret += separator;

        ret += arrayObj[i];
    }

    return ret;
};
//-------------------------------------------------------
function utils_monitorPhoneNumberFormat(htmlId, fnToExecuteOnChange) {
    var htmlObj = utils_$(htmlId);
    if (!htmlObj) {
        alert('ERROR in utils_monitorPhoneNumberFormat(): no such html object, htmlId="' + htmlId + '"');
        return;
    }

    utils_monitorPhoneNumberFormatObj(htmlObj, fnToExecuteOnChange);
}
//-------------------------------------------------------
function utils_monitorPhoneNumberFormatObj(htmlObj, fnToExecuteOnChange) {

    if (!htmlObj) {
        alert('ERROR in utils_monitorPhoneNumberFormatObj(): htmlObj is null.');
        return;
    }

    utils_dettachChangeMonitor(htmlObj);
    htmlObj._monitorPhoneNumberFormatFn = 0;

    setTimeout(function() {
        utils_aux_monitorPhoneNumberFormatObj(htmlObj, fnToExecuteOnChange);
    }, 50);
}
//-------------------------------------------------------
function utils_aux_monitorPhoneNumberFormatObj(htmlObj, fnToExecuteOnChange) {

    if (!htmlObj._monitorPhoneNumberFormatFn) {

        htmlObj._fnToExecuteOnChange = fnToExecuteOnChange;

        htmlObj._formatPhoneNumberObj = function() {
            var phoneNumber = utils_getObjectValue(htmlObj);
            var newPhoneNumber = utils_autoFormatPhoneNumber(phoneNumber);

            if (newPhoneNumber != phoneNumber) {
                utils_setObjectValue(htmlObj, newPhoneNumber);
                //utils_flashBackground(htmlObj);

                if (this._fnToExecuteOnChange)
                    this._fnToExecuteOnChange();
            }
        };

        htmlObj._monitorPhoneNumberFormatFn = function() {
            htmlObj._formatPhoneNumberObj();
        };

        htmlObj._formatPhoneNumberObj(); //formats once right after initialization

        utils_attachChangeMonitor(htmlObj, 400, htmlObj._monitorPhoneNumberFormatFn, false);
    }
}
//-------------------------------------------------------
function utils_autoFormatPhoneNumber(phoneNumber) {
    if (!phoneNumber || phoneNumber.length == 0)
        return phoneNumber;

    var flatPhoneNumber = utils_getFlatPhoneNumber(phoneNumber);

    if (flatPhoneNumber.length == 10)
    //FORMAT TO: (xxx) xxx-xxxx
        phoneNumber = '(' + flatPhoneNumber.substring(0, 3) + ') ' + flatPhoneNumber.substring(3, 6) + '-' + flatPhoneNumber.substring(6);
    else if (flatPhoneNumber.charAt(0) == '1' && flatPhoneNumber.length == 11)
    //FORMAT TO: 1 xxx xxx-xxxx
        phoneNumber = '1 ' + flatPhoneNumber.substring(1, 4) + ' ' + flatPhoneNumber.substring(4, 7) + '-' + flatPhoneNumber.substring(7);
    else
        phoneNumber = utils_getFlatPhoneNumber(phoneNumber, true);

    return phoneNumber;
}
//-------------------------------------------------------
function utils_getFlatPhoneNumber(phoneNumber, allowSpecialCharacters) {
    if (!phoneNumber)
        return '';

    var ret = '';

    for (var i = 0, n = phoneNumber.length; i < n; i++) {
        var c = phoneNumber.charAt(i);

        if (c >= '0' && c <= '9' || allowSpecialCharacters && (c == ' ' || c == '(' || c == ')' || c == '-' || c == '+' && !i))
            ret += c;
    }

    return ret;
}
//-------------------------------------------------------
function utils_addOptionToDDL(ddlObj, text) {
    utils_addOptionToDDL2(ddlObj, text, text);
}
//-------------------------------------------------------
function utils_addOptionToDDL2(ddlObj, text, value) {
    var optn = document.createElement("option");
    optn.text = text;
    optn.value = value;
    ddlObj.options.add(optn);

    return optn;
}
//-------------------------------------------------------
function utils_clearAllDDLOptions(ddlObj) {
    if (ddlObj == null || ddlObj.options == null)
        return;

    while (ddlObj.options.length > 0)
        ddlObj.remove(0);
}
//-------------------------------------------------------
function utils_getAllDDLOptions_asXTreeMap(ddlObj) { //ddl values must be unique, duplicates are lost
    var ret = new XTreeMap();
    
    if (ddlObj == null || ddlObj.options == null)
        return ret;

    for (var i = 0, n = ddlObj.options.length; i < n; i++)
        ret.set('' + ddlObj.options[i].value, ddlObj.options[i].text);

    return ret;
}
//-------------------------------------------------------
function utils_getAllDDLOptions_asXHashMap(ddlObj) {
    var ret = new XHashMap();

    if (ddlObj == null || ddlObj.options == null)
        return ret;

    for (var i = 0, n = ddlObj.options.length; i < n; i++)
        ret.set('' + ddlObj.options[i].value, ddlObj.options[i].text);

    return ret;
}
//-------------------------------------------------------
/*
* Finds the HTML id of an Infragistics control with a particular ASP name/id, e.g. "WebMaskEditFirstName":
* <igtxt:WebMaskEdit id="WebMaskEditFirstName" ... ></igtxt:WebMaskEdit>
* 
* dontAlertIfNotFound: false, if not provided (will ALERT if control not found).
*/
function utils_getIGControlId(serverId, dontAlertIfNotFound) {
    var control = utils_getIGControl(serverId, dontAlertIfNotFound);

    return control ? control.name : '';
}
//-------------------------------------------------------
/*
* Finds the Infragistics control with a particular name/id, e.g. "WebMaskEditFirstName":
* <igtxt:WebMaskEdit id="WebMaskEditFirstName" ... ></igtxt:WebMaskEdit>
* 
* dontAlertIfNotFound: false, if not provided (will ALERT if control not found).
*/
function utils_getIGControl(serverId, dontAlertIfNotFound) {
    var control = findControl(serverId);
    if (control)
        return control;
    else {
        if (!dontAlertIfNotFound)
            alert('Control not found for serverId="' + serverId + '"');

        return null;
    }
}
//-------------------------------------------------------
/*
Debugging help: shows a frame around a particular html object.
*/
var gUtilsDebugFrameAroundObj = 0;
function utils_DEBUG_showFrameAroundObject(htmlObj, strColor) /* strColor default: 'red' */
{
    if (!htmlObj)
        return;

    var rect = utils_$rect(htmlObj);

    if (gUtilsDebugFrameAroundObj)
        document.body.removeChild(gUtilsDebugFrameAroundObj);

    gUtilsDebugFrameAroundObj = document.createElement('div');
    gUtilsDebugFrameAroundObj.style.position = 'absolute';
    gUtilsDebugFrameAroundObj.style.left = '' + rect.x + 'px';
    gUtilsDebugFrameAroundObj.style.top = '' + rect.y + 'px';
    gUtilsDebugFrameAroundObj.style.width = '' + rect.width + 'px';
    gUtilsDebugFrameAroundObj.style.height = '' + rect.height + 'px';
    gUtilsDebugFrameAroundObj.style.border = 'solid 1px ' + (strColor ? strColor : 'red');
    gUtilsDebugFrameAroundObj.style.backgroundColor = 'Transparent';
    gUtilsDebugFrameAroundObj.style.zIndex = '30000';
    document.body.appendChild(gUtilsDebugFrameAroundObj);
}
//-------------------------------------------------------
function utils_DEBUG_showRectangle(x, y, width, height, bkColor) {
    var tmpObj = document.createElement('div');
    tmpObj.style.position = 'absolute';
    tmpObj.style.left = '' + x;
    tmpObj.style.top = '' + y;
    tmpObj.style.width = '' + width;
    tmpObj.style.height = '' + height;
    tmpObj.style.backgroundColor = bkColor;
    tmpObj.style.zIndex = '55777';
    utils_applyOpacityObj(tmpObj, 0.7);

    document.body.appendChild(tmpObj);
}
//-------------------------------------------------------
function utils_installCustomDateChooser_forWebGridCells(
    cellId, //e.g. argument of UltraWebGrid_AfterEnterEditModeHandler(gridName, cellId)
    cellKeys, // array of keys like "StartDate" in:  <igtbl:UltraGridColumn Key="StartDate" IsBound="True" ...
    fnOnSetup,
    fnOnChange,
    dontAssumeCurrentYear,
    isPastDate) {

    if (!igtbl_getCellById) {
        alert('ERROR - utils_installCustomDateChooser_forWebGridCells: igtbl_getCellById() is not defined.');
        return;
    }

    try {
        var cell = igtbl_getCellById(cellId);        
        var cellId = cell.Element.id;
        var indexUnderscore = cellId.lastIndexOf('_rc_');
        var editorId = cellId.substring(0, indexUnderscore) + '_tb';
        var dateInputObj = utils_$(editorId);
        if (!dateInputObj)
            return;

        dateInputObj._cell = 0;
        dateInputObj._cellDisabled = true;
        if (dateInputObj._color)
            dateInputObj.style.color = dateInputObj._color;
        
        for (var i = 0, n = cellKeys.length; i < n; i++)
            if (cell.Column.Key == cellKeys[i]) {
                if (!dateInputObj._customDateMonitorInstalled) //install if not installed already
                    utils_installCustomDateChooser(
                        dateInputObj, fnOnSetup, fnOnChange, dontAssumeCurrentYear, isPastDate);
                else {
                    dateInputObj._fnOnSetup = fnOnSetup;
                    dateInputObj._fnOnChange = fnOnChange;
                }
                
                try {
                    dateInputObj._cell = cell;
                    dateInputObj._cellDisabled = false;

                    //Attempt to convert date from initial text
                    var cellValue = cell.getValue();

                    if (cellValue && ('' + cellValue).length) {
                        var initialDate = new Date('' + cellValue);
                        dateInputObj._year = initialDate.getFullYear();
                        dateInputObj._month = initialDate.getMonth() + 1; //0-based
                        dateInputObj._day = initialDate.getDate();
                        dateInputObj.formatDate();
                    }
                    else
                        dateInputObj.value = '';
                }
                catch (ex) {
                    dateInputObj.value = '';
                }

                setTimeout(function() {
                    dateInputObj.value = '' + dateInputObj.value;
                    dateInputObj.select();
                    dateInputObj.focus();
                }, 100);
                
                return;
            }
    }
    catch (ex) {
        alert('EXCEPTION - utils_installCustomDateChooser_forWebGridCells: ' + ex.message);
    }
}
//-------------------------------------------------------
/**
Custom date control: a standard text box accepting he following formats
(if invalid date, the text color becomes RED):
- MMM*DD*YYYY e.g. Sep 2, 2009 - where '*' can be dashes, dots, commas, spaces,
forward and backward slashes, or nothing, e.g. Sep22009
- MMM*DD*YY e.g. Sep 2, 09
- MMM*DD (year should default to 2009) e.g. Sep 2
- YYYY*MMM*DD e.g. 2009 Sep 2
- YY*MMM*DD e.g. 2009 Sep 2
- MM*DD*YYYY e.g. 12 31 2001 (12312001)
- MM*DD*YY e.g. 12 31 01 (123101)
- MM*DD (year should default to 2009) e.g. 1231 or 0131
Also:
- full field formatting should take place only when the text box looses focus.
- dashes, dots, commas, spaces, forward and backward slashes are ignored.
- partial month names (English only) can be provided, e.g. 'a' for
'august', or 'ma' for 'may', along with full month names, both in upper- and 
lower-case characters.
*/
function utils_installCustomDateChooser(
    dateInputObj, /* textbox */
    fnOnSetup,
    fnOnChange,
    dontAssumeCurrentYear,
    isPastDate) {
    
    if (gCustomDateChooser_Disabled)
        return;

    if ((typeof dateInputObj) == 'string')
        dateInputObj = utils_$(dateInputObj);//assumed it was the htmlID provided instead of htmlObj

    if (!dateInputObj) {
        alert('ERROR - utils_installCustomDateChooser(): dateInputObj cannot be null');
        return;
    }    

    try {
        if (textInputObj.readOnly || textInputObj.disabled)
            return;
    }
    catch (ex) {
    }

    dateInputObj._shutdown = false;
    
    if (dateInputObj._customDateMonitorInstalled) {
        return;
    }

    dateInputObj._customDateMonitorInstalled = true;
    dateInputObj._flagYearHasBeenAssumed = true;
    dateInputObj._fnOnSetup = fnOnSetup;
    dateInputObj._fnOnChange = fnOnChange;
    dateInputObj._dontAssumeCurrentYear = dontAssumeCurrentYear;
    dateInputObj._isPastDate = isPastDate;
    dateInputObj._mmddyyyy = '';
    dateInputObj._color = dateInputObj.style.color; //saved color
    dateInputObj._validDate = false;
    
    //assignToInternalVariableOnly: will set the value to dateInputObj._value, but will not display it (will not assign to dateInputObj.value)
    dateInputObj.guessDate = function(overrideValue, assignToInternalVariableOnly) {

        if (this._shutdown || !this.value.length)
            return;

        if (this._cell && this._cellDisabled)
            return; //IG cell editing disabled

        this._day = 0;
        this._month = 0;
        this._year = 0;
        this._strDay = 0;
        this._strMonth = 0;
        this._strYear = 0;

        var str = overrideValue ? overrideValue : this.value;

        str = utils_stringReplace(str, '/', ' ');
        str = utils_stringReplace(str, ',', ' ');
        str = utils_stringReplace(str, '.', ' ');
        str = utils_stringReplace(str, '-', ' ');
        str = utils_stringReplace(str, '\\', ' ');
        str = utils_stringReplace(str, '_', ' ');
        str = utils_stringReplace(str, '\'', ' ');
        str = utils_stringReplace(str, '~', ' ');
        str = utils_stringReplace(str, '`', ' ');
        str = utils_stringReplace(str, '|', ' ');
        str = utils_stringReplace(str, '@', ' ');
        str = utils_stringReplace(str, '#', ' ');
        str = utils_stringReplace(str, '$', ' ');
        str = utils_stringReplace(str, '%', ' ');
        str = utils_stringReplace(str, '^', ' ');
        str = utils_stringReplace(str, '&', ' ');
        str = utils_stringReplace(str, '*', ' ');
        str = utils_stringReplace(str, '+', ' ');
        str = utils_stringReplace(str, '=', ' ');
        str = utils_stringReplace(str, '?', ' ');
        str = utils_stringReplace(str, '<', ' ');
        str = utils_stringReplace(str, '>', ' ');
        str = utils_stringReplace(str, '[', ' ');
        str = utils_stringReplace(str, ']', ' ');
        str = utils_stringReplace(str, '{', ' ');
        str = utils_stringReplace(str, '}', ' ');
        str = utils_stringReplace(str, '(', ' ');
        str = utils_stringReplace(str, ')', ' ');
        str = utils_stringReplace(str, 'N/A', ' ');

        if (!str.length)
            this._flagYearHasBeenAssumed = false; //will allow full year display if string is empty            

        var tokens = str.split(' ');

        var arrayNumbers = new Array();
        var arrayStrNumbers = new Array();
        for (var i = 0; i < tokens.length; i++) {
            var token = tokens[i];
            if (!token || !token.length)
                continue;

            var originalToken = token;
            //remove '0' at the beginning, e.g. when extracting the year from "may 1, 09"
            while (token.length > 1 && token.substring(0, 1) == '0')
                token = token.substring(1);

            if (utils_isNumber(token)) {
                var number = utils_parseInt(token);
                arrayNumbers.push(number);
                arrayStrNumbers.push(originalToken);
            }
            else {
                var strMonth = '' + token.toLowerCase();

                if (strMonth == 'january'.substring(0, strMonth.length))
                    this._month = 1;
                else if (strMonth == 'february'.substring(0, strMonth.length))
                    this._month = 2;
                else if (strMonth == 'march'.substring(0, strMonth.length))
                    this._month = 3;
                else if (strMonth == 'april'.substring(0, strMonth.length))
                    this._month = 4;
                else if (strMonth == 'may'.substring(0, strMonth.length))
                    this._month = 5;
                else if (strMonth == 'june'.substring(0, strMonth.length))
                    this._month = 6;
                else if (strMonth == 'july'.substring(0, strMonth.length))
                    this._month = 7;
                else if (strMonth == 'august'.substring(0, strMonth.length))
                    this._month = 8;
                else if (strMonth == 'september'.substring(0, strMonth.length))
                    this._month = 9;
                else if (strMonth == 'october'.substring(0, strMonth.length))
                    this._month = 10;
                else if (strMonth == 'november'.substring(0, strMonth.length))
                    this._month = 11;
                else if (strMonth == 'december'.substring(0, strMonth.length))
                    this._month = 12;
            }
        }

        switch (arrayNumbers.length) {
            case 3:
                //Assume MONTH/DAY/YEAR
                if (arrayStrNumbers[0].length <= 2) {
                    this._month = arrayNumbers[0];
                    this._strMonth = arrayStrNumbers[0];
                }

                if (arrayStrNumbers[1].length <= 2) {
                    this._day = arrayNumbers[1];
                    this._strDay = arrayStrNumbers[1];
                }

                if (arrayStrNumbers[2].length <= 4) {
                    this._year = arrayNumbers[2];
                    this._strYear = arrayStrNumbers[2];
                }

                break;

            case 2:
                if (this._month) {
                    if (arrayNumbers[0] > 31) {
                        if (arrayStrNumbers[0].length <= 4) {
                            this._year = arrayNumbers[0];
                            this._strYear = arrayStrNumbers[0];
                        }

                        if (arrayStrNumbers[1].length <= 2) {
                            this._day = arrayNumbers[1];
                            this._strDay = arrayStrNumbers[1];
                        }
                    }
                    else {
                        if (arrayStrNumbers[1].length <= 4) {
                            this._year = arrayNumbers[1];
                            this._strYear = arrayStrNumbers[1];
                        }

                        if (arrayStrNumbers[0].length <= 2) {
                            this._day = arrayNumbers[0];
                            this._strDay = arrayStrNumbers[0];
                        }
                    }
                }
                else {
                    //Assume MM/DD
                    if (arrayStrNumbers[0].length <= 2) {
                        this._month = arrayNumbers[0];
                        this._strMonth = arrayStrNumbers[0];
                    }

                    if (arrayStrNumbers[1].length <= 2) {
                        this._day = arrayNumbers[1];
                        this._strDay = arrayStrNumbers[1];
                    }
                }
                break;
            case 1:
                if (this._month && arrayNumbers[0] <= 31) {
                    if (arrayStrNumbers[0].length <= 2) {
                        this._day = arrayNumbers[0];
                        this._strDay = arrayStrNumbers[0];
                    }
                }
                break;
        }

        //Check for condensed information, e.g. Sep022009, isolate it and in such situation,
        //restart the entire guessing process        
        var tokenIndexInFocus = -1;
        var splitToken = 0;

        //check for LETTERS+NUMBERS combination
        for (var i = 0; i < tokens.length; i++) {
            var token = tokens[i];
            if (!token || !token.length)
                continue;

            var originalToken = token;

            if (!utils_isNumber(token) && !utils_isText(originalToken)) {
                //token contains LETTERS+NUMBERS combination
                tokenIndexInFocus = i;
                splitToken = '';
                var oT = "" + originalToken; //make sure it is an array
                for (var j = 0, nj = oT.length; j < nj; ) {
                    var partialToken = '';

                    //try extract all digits
                    for (; utils_isNumber(oT.charAt(j)) && j < nj; j++)
                        partialToken += oT.charAt(j);

                    //try extract all letters    
                    if (!partialToken.length)
                        for (; !utils_isNumber(oT.charAt(j)) && j < nj; j++)
                        partialToken += oT.charAt(j);

                    splitToken += partialToken + ' ';
                }

                break;
            }
        }

        if (!splitToken) {
            //Fix long numbers, e.g. 030209
            for (var i = 0; i < tokens.length; i++) {
                var token = tokens[i];
                if (!token || !token.length)
                    continue;

                var originalToken = token;

                if (utils_isNumber(token)) {
                    //check token size - if it is too long, it may represent a month-year-day 
                    //combination, all together - e.g. 030209, 03022009, 0302
                    if (originalToken.length == 3 && arrayStrNumbers.length == 1)//e.g. Sep 209 <--(2009)
                        splitToken =
                            originalToken.substring(0, 1) + ' ' +
                            originalToken.substring(1);
                    else if (originalToken.length == 4 && !this._day)
                        splitToken =
                            originalToken.substring(0, 2) + ' ' +
                            originalToken.substring(2);
                    else if (originalToken.length == 5)//e.g. Sep 22009
                        splitToken =
                            originalToken.substring(0, 1) + ' ' +
                            originalToken.substring(1);
                    else if (originalToken.length == 6) {
                        if (tokens.length == 1)
                            splitToken = //MMDDYY
                                originalToken.substring(0, 2) + ' ' +
                                originalToken.substring(2, 4) + ' ' +
                                originalToken.substring(4);
                        else if (this._month)
                            splitToken = //DDYY
                                    originalToken.substring(0, 2) + ' ' +
                                    originalToken.substring(2);
                    }
                    else if (originalToken.length == 8 && tokens.length == 1)
                        splitToken =
                            originalToken.substring(0, 2) + ' ' +
                            originalToken.substring(2, 4) + ' ' +
                            originalToken.substring(4);

                    if (splitToken) {
                        tokenIndexInFocus = i;
                        break;
                    }
                }
            }
        }

        if (splitToken) {
            //Original string contained condensed format
            //Rebuild string and replace current token with the split one
            var overrideToken = '';
            for (var j = 0, nj = tokens.length; j < nj; j++) {
                var token = tokens[j];
                if (!token || !token.length)
                    continue;

                overrideToken += ((j == tokenIndexInFocus) ? splitToken : token) + ' ';
            }

            if (overrideToken != str)
                this.guessDate(overrideToken); //restart guessing process

            return;
        }

        //Continue guessing/evaluating
        if (this._flagYearHasBeenAssumed && (this._ChgMon_changeDirection == 1))
            this._flagYearHasBeenAssumed = false; //set flag in order to allow default year

        //Year adjustment for dates like '02/03/93'
        if (this._year >= 0 && this._strYear && this._strYear.length <= 2 && this._year < 100) {
            this._year += 2000;
            this._flagYearHasBeenAssumed = true;

            var currentTime = new Date();

            //Round century
            var maxYear = currentTime.getFullYear();
            if (!this._isPastDate)
                maxYear += 50;

            while (this._year > maxYear)
                this._year -= 100; //'93' -> '2093' -> '1993', while '01' -> '2001' without adjustments
        }

        //Assume current year
        if (!this._year && this._month > 0 && this._day > 0 && !this._dontAssumeCurrentYear) {
            //Assume current year
            var currentTime = new Date();
            this._year = currentTime.getFullYear();
            this._flagYearHasBeenAssumed = true;
        }

        //validation
        this._validDate = true;
        if (this._day < 1 || this._month < 1 || this._year < 0)
            this._validDate = false;
        else if (this._month > 12 || !utils_getDateYMD(this._year, this._month, this._day)) {
            //invalid date: try switching DAY and MONTH        
            if (!utils_getDateYMD(this._year, this._month, this._day))
                this._validDate = false;
            else {
                var tmp = this._month;
                this._month = this._day;
                this._day = tmp;
            }
        }

        if (this._validDate) {
            if (assignToInternalVariableOnly)
                this.formatDate(assignToInternalVariableOnly);
            else {
                this.style.color = this._color;
                this.execOnChange();
            }
        }
        else {
            this._mmddyyyy = '';

            if (!this._hasFocus && this._month >= 1 && this._month <= 12 &&
                (arrayNumbers.length > 0 || tokens == 1))//month and day provided or month only, e.g. will ignore 'N/A' - without this condition, would extract 'A' and display 'april'
                this.formatDate(assignToInternalVariableOnly);

            if (!assignToInternalVariableOnly)
                this.setErrorColor();
        }
    };   //guessDate()

    dateInputObj.execOnChange = function() {
        if (this._fnOnChange)
            if ((typeof this._fnOnChange) == 'function')
                this._fnOnChange(this); //e.g. function(){ ... }
            else//if function is provided as a string
                if (this._fnOnChange.indexOf('(') < 0)
                    eval(this._fnOnChange + '(this)'); //e.g. 'setDOB'
                else
                    eval(this._fnOnChange); //e.g. 'setDOB()'
    };

    dateInputObj.execOnSetup = function() {
        if (this._fnOnSetup)
            if ((typeof this._fnOnSetup) == 'function')
                this._fnOnSetup(this); //e.g. function(){ ... }
            else//if function is provided as a string
                if (this._fnOnSetup.indexOf('(') < 0)
                    eval(this._fnOnSetup + '(this)'); //e.g. 'setDOB'
                else
                    eval(this._fnOnSetup); //e.g. 'setDOB()'
    };

    dateInputObj.setErrorColor = function() {
        this.style.color = '#C50000';
    };

    //assignToInternalVariableOnly: will set the value to dateInputObj._value, but will not display it (will not assign to dateInputObj.value)
    dateInputObj.formatDate = function(assignToInternalVariableOnly) {
        var strDate = '';
        if (this._year || this._month || this._day) {
            switch (this._month) {
                case 1: strDate += 'Jan'; break;
                case 2: strDate += 'Feb'; break;
                case 3: strDate += 'Mar'; break;
                case 4: strDate += 'Apr'; break;
                case 5: strDate += 'May'; break;
                case 6: strDate += 'Jun'; break;
                case 7: strDate += 'Jul'; break;
                case 8: strDate += 'Aug'; break;
                case 9: strDate += 'Sep'; break;
                case 10: strDate += 'Oct'; break;
                case 11: strDate += 'Nov'; break;
                case 12: strDate += 'Dec'; break;
                default: return;
            }

            if (this._day > 0)
                strDate += ' ' + this._day;

            if (this._year > 0)
                strDate += ', ' + this._year;
        }

        this._value = strDate;

        if (this.value != strDate) {
            if (!assignToInternalVariableOnly) {
                this.value = strDate;
                //utils_flashBackground(this);
            }
        }
    };

    dateInputObj.onfocus = function() {
        this._hasFocus = true;
    };

    dateInputObj.onblur = function() {
        this._hasFocus = false;

        var str = utils_trim(this.value);
        if (str.length) {
            this.guessDate();
            this.formatDate();

            if (dateInputObj._cell)
                dateInputObj._cell.setValue(dateInputObj.value);
        }
        
        this.execOnChange();
    };


    utils_attachChangeMonitor(
        dateInputObj,
        200,
        function() {
            try {
                dateInputObj.guessDate();
            }
            catch (ex) {
            }
        });

    dateInputObj.execOnSetup();

    dateInputObj.guessDate();
    
    dateInputObj.formatDate();
    
    return dateInputObj;
}
//-------------------------------------------------------
function utils_attachDottedCrossToMousePointer(containerHtmlId) {
    var containerObj = utils_$(containerHtmlId);
    var dottedAxisBorder = 'dashed 1px #AADEE1';

    containerObj._updateAccordingToMouseLocation = function() {
        try {
            var rect = utils_$rect(this);

            this._x = rect.x;
            this._y = rect.y;
            this._width = rect.width;
            this._height = rect.height;
        }
        catch (ex) {
        }

        if (!this._xAxisDiv && this._width) {
            this._xAxisDiv = document.createElement('div');
            this._xAxisDiv.style.position = 'absolute';
            this._xAxisDiv.style.borderLeft = dottedAxisBorder;
            this._xAxisDiv.style.backgroundColor = 'Transparent';
            document.body.appendChild(this._xAxisDiv);

            this._yAxisDiv = document.createElement('div');
            this._yAxisDiv.style.position = 'absolute';
            this._yAxisDiv.style.borderTop = dottedAxisBorder;
            this._yAxisDiv.style.backgroundColor = 'Transparent';
            document.body.appendChild(this._yAxisDiv);
        }

        if (this._width) {
            if (gMouseX >= this._x && gMouseX < this._x + this._width &&
                gMouseY >= this._y && gMouseY < this._y + this._height) {
                this._xAxisDiv.style.display =
                this._yAxisDiv.style.display = '';

                this._xAxisDiv.style.left = '' + gMouseX + 'px';
                this._xAxisDiv.style.top = '' + this._y + 'px';
                this._xAxisDiv.style.width = '1px';
                this._xAxisDiv.style.height = '' + this._height + 'px';

                this._yAxisDiv.style.left = '' + this._x + 'px';
                this._yAxisDiv.style.top = '' + gMouseY + 'px';
                this._yAxisDiv.style.width = '' + this._width + 'px';
                this._yAxisDiv.style.height = '1px';
            }
            else
                this._xAxisDiv.style.display = this._yAxisDiv.style.display = 'none';
        }

        setTimeout(function() {
            containerObj._updateAccordingToMouseLocation();
        }, 100);
    };

    containerObj._updateAccordingToMouseLocation();
}
//-------------------------------------------------------
function utils_scrollToBottom(htmlObj) //usually, a DIV
{
    if (htmlObj)
        htmlObj.scrollTop = htmlObj.scrollHeight;
}
//-------------------------------------------------------
function utils_isNullOrEmptyString(str) {
    return !str || !utils_trim(utils_stringRemove('' + str, '&nbsp;')).length;
}
//-------------------------------------------------------
//
// Adds dynamics to a html image object.
// strArray_EventName_ImgSrc example:
//['onmouseover', 'images/saveM.png', 'onmousedown', 'images/saveD.png', 'onmouseup', 'images/saveM.png' ]
//
function utils_configureImageSrcForEvents(imgObj, strArray_EventName_ImgSrc) {
    if (!imgObj) {
        alert('ERROR - utils_configureImageSrcForEvents(): imgObj is null');
        return;
    }

    imgObj._src = imgObj.src;

    for (var i = 0, n = strArray_EventName_ImgSrc.length; i < n; ) {
        eval("imgObj." + strArray_EventName_ImgSrc[i++] +
             "=function(){ this.src='" + strArray_EventName_ImgSrc[i++] + "'; }");
    }

    imgObj.onmouseout = function() { this.src = this._src; }
    imgObj.style.cursor = 'pointer';
}
//-------------------------------------------------------
function utils_getFieldIDsAndValues(idArray) {
    var ret = new Array();

    for (var i = 0, n = idArray.length; i < n; i++) {
        var id = idArray[i];
        var obj = utils_$(id);

        if (!obj) {
            alert("ERROR - utils_getFieldIDsAndValues(): no html object for id='" + id + "'");
            continue;
        }

        ret.push(id);
        ret.push(obj.value);
    }

    return ret;
}
//-------------------------------------------------------
function utils_toXml(obj, t) {
    if (obj._overrideType)
        t = obj._overrideType;
        
    if (obj.constructor == Array) {

        var s = new Array(), i, l = obj.length, v;
        var t2 = (t && t.charAt(t.length - 1) == 's') ? t.substring(0, t.length - 1) : t;

        if (obj._overrideType)
            t2 = obj._overrideType;

        for (i = 0; i < l; i++) {
            v = obj[i];

            switch (typeof v) {
                case 'undefined':
                case 'function':
                case 'unknown':
                    break;

                case 'object':
                    if (v != null)
                        s.push(utils_toXml(v, t2));

                    break;

                default:
                    s.push('<' + t2 + '>' + v + '</' + t2 + '>');
            }
        }
        //if (s.length)
            return '<' + t + '>' + s.join('') + '</' + t + '>';

        return s;
    }
    else if (obj.constructor == String) {
        return obj.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('\'', '&apos;').replace('"', '&quot;');
    }
    else //Object
    {
        var sa = new Array(''), se = new Array('');

        if (!t)
            t = obj._tagName || 'object';

        for (var i in obj) {
            if (obj.hasOwnProperty && obj.hasOwnProperty(i)) {
                var v = obj[i];

                var type_v = (typeof v);

                if (v && v.charAt)
                    type_v = 'string';

                switch (type_v) {
                    case 'undefined':
                    case 'function':
                    case 'unknown':
                        break;

                    case 'object':
                    case 'undefined':
                        if (v != null)
                            se.push(utils_toXml(v, i));

                        break;

                    default:
                        sa.push(' ' + i + '="' + utils_stringReplace('' + v, '"', gUtils_ENCODING_SPECIALDBLQUOTE) + '"');
                }
            }
        }

        var s = se.join('');

        return '<' + t + sa.join('') + ((s != '') ? '>' + s + '</' + t + '>' : '/>');
    }
}
//-------------------------------------------------------
// Extracts and returns the path, name and extension from a full path file name - as an array.
// Example:
//    'media/images/save.gif' ==> ['media/images', 'save', 'gif']
//
function utils_getFilePathNameAndExtension(fullPathFileName) {
    if (!fullPathFileName)
        return ['', '', ''];

    var folderName;
    var fileNameNoExtension;
    var fileExtension;

    var indexSlash = fullPathFileName.lastIndexOf("/");
    if (indexSlash < 0)
        indexSlash = fullPathFileName.lastIndexOf("\\");

    var fileNameAndExtension;
    if (indexSlash >= 0) {
        folderName = fullPathFileName.substring(0, indexSlash);
        fileNameAndExtension = fullPathFileName.substring(++indexSlash);
    }
    else {
        folderName = '';
        fileNameAndExtension = fullPathFileName;
    }

    var indexDot = fileNameAndExtension.lastIndexOf(".");
    if (indexDot > 0) {
        fileNameNoExtension = fileNameAndExtension.substring(0, indexDot);
        fileExtension = fileNameAndExtension.substring(++indexDot);
    }
    else {
        fileNameNoExtension = fileNameAndExtension;
        fileExtension = '';
    }

    return [folderName, fileNameNoExtension, fileExtension];
}
//-------------------------------------------------------
// Opposite of utils_getFilePathNameAndExtension().
// Example:
//   ['media/images', 'save', 'gif'] ==> 'media/images/save.gif'
//
function utils_getFullPathFromPathNameAndExtension(path_name_extension) {
    if (!path_name_extension || path_name_extension.length != 3)
        return '';

    var ret = '';
    if (path_name_extension[0] && path_name_extension[0].length &&
        path_name_extension[0].charAt(path_name_extension[0].length - 1) != '/')
        ret += path_name_extension[0] + '/';

    ret += path_name_extension[1];

    if (path_name_extension[2] && path_name_extension[2].length)
        ret += '.' + path_name_extension[2];

    return ret;
}
//-------------------------------------------------------
function utils_setFeedback(feedbackObj, message, flagShowWaitIcon, optionalIconSize, optionalIconSrc) {
    if (!feedbackObj)
        return;

    if (!message)
        message = '';

    if (!optionalIconSrc)
        optionalIconSrc = 'images/loading.gif';

    var iconSize;
    if (!optionalIconSize)
        iconSize = "";
    else
        iconSize = " style='width: " + optionalIconSize + "px'";

    var feedbackObjCSSClass = feedbackObj.className ? (" class='" + feedbackObj.className + "' ") : '';

    feedbackObj.innerHTML =
        "<table cellspacing='0' cellpadding='0' border='0' style='border-collapse: collapse'><tr>" +
        (flagShowWaitIcon ? "<td style='padding-right: 5px'><img alt='' src='" + optionalIconSrc + "'" + iconSize + "/></td>" : "") +
        "<td" + feedbackObjCSSClass + ">" + message + "</td><tr></table>";
}
//=======================================================
function utils_StringBuilder(value) {
    this.strings = new Array();
    
    if (value)
        this.append('' + value);
}
//-------------------------------------------------------
utils_StringBuilder.prototype.append = function(value) {
    if (value)
        this.strings.push('' + value);
}
//-------------------------------------------------------
utils_StringBuilder.prototype.clear = function() {
    this.strings.length = 0;
}
//-------------------------------------------------------
utils_StringBuilder.prototype.stringCount = function(value) {
    return this.strings.length;
}
//-------------------------------------------------------
utils_StringBuilder.prototype.toString = function(separator) {
    return this.strings.length ? this.strings.join(separator ? separator : '') : '';
}
//-------------------------------------------------------
// Enables/disables an image according to its current onmouseover, onmousedown, onmouseup, onmouseout
// settings. If flag is false, then clicking is disabled. Everything is done by shuffling mouse events.
//
// EXAMPLE:
//    utils_enableHtmlImg('buttonPrint', true, true);
// where:
//    <img id="buttonPrint" class="toolbarIcon" alt="Print" src="images/printD.png" 
//       onclick="buttonPrintClick() onmouseover="this.src='images/printM.png'" 
//       onmousedown="this.src='images/printD.png' onmouseup="this.src='images/printM.png'" 
//       onmouseout="this.src='images/print.png'" />
// 
function utils_enableHtmlImg(imgHtmlId, flag, changeImageAsWell_AccordingToFlag) {
    var imgObj = utils_$(imgHtmlId);

    if (flag) {
        if (imgObj._disabled) {
            imgObj._disabled = false;

            if (imgObj._saved_onclick)
                imgObj.onclick = imgObj._saved_onclick;

            if (imgObj._saved_onmouseover)
                imgObj.onmouseover = imgObj._saved_onmouseover;

            if (imgObj._saved_onmousedown)
                imgObj.onmousedown = imgObj._saved_onmousedown;

            if (imgObj._saved_onmouseup)
                imgObj.onmouseup = imgObj._saved_onmouseup;
        }

        if (changeImageAsWell_AccordingToFlag) {
            if (imgObj._saved_onmouseout)
                imgObj.onmouseout = imgObj._saved_onmouseout;

            if (imgObj.onmouseout)
                imgObj.onmouseout();
        }
    }
    else { //DISABLE
        if (!imgObj._disabled) {
            imgObj._disabled = true;

            if (!imgObj._saved_onclick) //save only the very first time
            {
                imgObj._saved_onclick = imgObj.onclick;
                imgObj._saved_onmouseover = imgObj.onmouseover;
                imgObj._saved_onmousedown = imgObj.onmousedown;
                imgObj._saved_onmouseup = imgObj.onmouseup;
                imgObj._saved_onmouseout = imgObj.onmouseout;
            }

            imgObj.onclick =
                    imgObj.onmouseover =
                    imgObj.onmousedown =
                    imgObj.onmouseup =
                    imgObj.onmouseout = function() { };
        }

        if (changeImageAsWell_AccordingToFlag && imgObj._saved_onmousedown)
            imgObj._saved_onmousedown();
    }
}
//-------------------------------------------------------
//Searches for a DOM (HTML) node that partially matches a certain id.
//NOTE: Search is case-insensitive.
//
//EXAMPLE: 
//  Say we have the following HTML code:
//     <div id='parentDiv'>
//        ...
//        <input id='tbCountryName' ... />
//        ...
//     </div>
//  A reference to <input id='tbCountryName' ... /> will be found by calling:
//
//  utils_findDomNodeWithIdLike(utils_$('parentDiv'), 'country')
//
function utils_findDomNodeWithIdLike(obj, idLike) {
    if (!idLike || !idLike.length)
        return null;

    var arrayAllObj = utils_findDomNodes(obj);
    idLike = idLike.toLowerCase();

    for (var i = 0, n = arrayAllObj.length; i < n; i++) {
        var aObj = arrayAllObj[i];

        if (aObj.id && aObj.id.toLowerCase().indexOf(idLike) >= 0)
            return aObj; //found
    }

    return null; //not found
}
//-------------------------------------------------------
// Returns the text of current drop-down list selection.
function utils_getDDLText(htmlObj) {
    return utils_getDDLText_forItem(htmlObj, htmlObj.selectedIndex);
}
//-------------------------------------------------------
// Returns the text of first item in a drop-down list.
function utils_getDDLText_firstItem(htmlObj) {
    return utils_getDDLText_forItem(htmlObj, 0);
}
//-------------------------------------------------------
// Returns the text of first item in a drop-down list.
function utils_getDDLText_forItem(htmlObj, itemIndex) {
    if (!htmlObj) {
        alert('ERROR - utils_getDDLText_forItem(): htmlObj is null.');
        return '';
    }

    if (!htmlObj.options) {
        alert('ERROR - utils_getDDLText_forItem(): htmlObj is not a drop-down list.');
        return '';
    }

    if (itemIndex >= 0 && itemIndex < htmlObj.options.length)
        return htmlObj.options[itemIndex].text
    else
        return '';
}
//-------------------------------------------------------
//Creates the html code for a SELECT (drop-down list) control.
// - xHashMap contains the values and text for the SELECT as (key, value) pairs
// - sbHtml: created it as sbHtml = new utils_StringBuilder('')
// - className: CSS class name
// - selectedValueOrText: selected value or text: 'value' or 'text' function of 
//   flag_onchange_assigntext. Can be null / no selection.
// - leftSideExpression: string representation of variable to set on value change, e.g.:
//   'whistoryrecords_getRegisteredItem(' + this.autoID + ').departmentID='
// - flag_onchange_assignvalue: if true, selected 'text' is assigned when 'onchange' event is called,
//   otherwise 'value' is assigned.
// - defaultOnEmptyValue: default value to use if empty/null value is encountered
function utils_appendDLL(xHashMap, sbHtml, className, selectedValueOrText, leftSideExpression, flag_onchange_assigntext, defaultOnEmptyValue) {

    if (!defaultOnEmptyValue)
        defaultOnEmptyValue = '';

    if (flag_onchange_assigntext)
        sbHtml.append('<select class="' + className +
            '" onchange="' + leftSideExpression + '(this.value.length ? this.options[this.selectedIndex].text : \'' + defaultOnEmptyValue + '\')">');
    else
        sbHtml.append('<select class="' + className +
            '" onchange="' + leftSideExpression + '(this.value.length ? this.value : \'' + defaultOnEmptyValue + '\')">');

    var allKeys = xHashMap.getAllKeys_Ref();
    var allValues = xHashMap.getAllValues_Ref();

    for (var i = 0, n = allKeys.length; i < n; i++) {
        var key = allKeys[i];
        var val = allValues[i];

        if (flag_onchange_assigntext && val == selectedValueOrText || !flag_onchange_assigntext && key == selectedValueOrText)
            sbHtml.append('<option value="' + key + '" selected>');
        else
            sbHtml.append('<option value="' + key + '">');

        sbHtml.append(val);
        sbHtml.append('</option>');
    }

    sbHtml.append('</select>');
}
//-------------------------------------------------------
function utils_preprocessEncodedHTML(xmlString) {
    try {
        if (!xmlString)
            return '';

        xmlString = utils_stringReplace(xmlString, '&amp;', '&');
        xmlString = utils_stringReplace(xmlString, '\n\r', '\r');
        xmlString = utils_stringReplace(xmlString, '\r\n', '\r');
        xmlString = utils_stringReplace(xmlString, '\r\r', '\r');
        xmlString = utils_stringReplace(xmlString, '\r', '\n');
        xmlString = utils_stringReplace(xmlString, '\n', gUtils_ENCODED_CR);
        xmlString = utils_stringReplace(xmlString, gUtils_ENCODED_CRLF, gUtils_ENCODED_CR);
        xmlString = utils_stringReplace(xmlString, gUtils_ENCODED_LFLF, gUtils_ENCODED_CR);
        xmlString = utils_stringReplace(xmlString, gUtils_ENCODED_CRCR, gUtils_ENCODED_CR);
        xmlString = utils_stringReplace(xmlString, gUtils_ENCODED_CRspaceCR, gUtils_ENCODED_CR);
        xmlString = utils_stringReplace(xmlString, gUtils_ENCODED_LF, gUtils_ENCODED_CR);
        xmlString = utils_stringReplace(xmlString, gUtils_ENCODED_CR, gUtils_ENCODING_tmpBRreplacement);        
    }
    catch (e) {
        alert('EXCEPTION - utils_preprocessEncodedHTML(): ' + e.message);
    }

    return xmlString;
}
//-------------------------------------------------------
function utils_getRadioButtonCheckedValue(rbObj) {
    try {
        for (var iRB = 0, nRB = rbObj.length; iRB < nRB; iRB++) {
            if (rbObj[iRB].checked)
                return '' + rbObj[iRB].value;
        }
    }
    catch (e) {
        alert('EXCEPTION - utils_getRadioButtonCheckedValue(): ' + e.message);
    }

    return null;
}
//-------------------------------------------------------
// Auto-complete functionality, tested in IE8 and Chrome
// tbObj: text box object, arrayAllValues: array of all possible string values, 
function utils_autoComplete(tbObj, arrayAllValues) {
    var autoCompleteMatchFound = false;
    for (var i = 0; i < arrayAllValues.length; i++) {
        if (arrayAllValues[i].toUpperCase().indexOf(tbObj.value.toUpperCase()) == 0) {
            autoCompleteMatchFound = true;
            break;
        }
    }

    if (autoCompleteMatchFound)
        arrayAllValues.selectedIndex = i;
    else
        return;

    if (tbObj.createTextRange || tbObj.selectionStart) {
        if (!autoCompleteMatchFound) {
            tbObj.value = tbObj.value.substring(0, tbObj.value.length - 1);
            return;
        }
        var strCursorKeys = "8;46;37;38;39;40;33;34;35;36;45;";
        if (strCursorKeys.indexOf(event.keyCode + ";") == -1) {
            var strOldValue = tbObj.value;
            var strNewValue = autoCompleteMatchFound ? arrayAllValues[i] : strOldValue;
            if (strNewValue != tbObj.value) {
                tbObj.value = strNewValue;
                utils_autoComplete_aux_setCaretTo(tbObj, strOldValue.length);
            }
        }
    }
}
//-------------------------------------------------------
function utils_autoComplete_aux_setCaretTo(tbObj, pos) {
    if (tbObj.createTextRange) {
        var range = tbObj.createTextRange();
        range.moveStart('character', pos);
        range.select();
    } else if (tbObj.selectionStart) {
        tbObj.focus();
        tbObj.setSelectionRange(pos, tbObj.value.length);
    }
    //else cannot do it
}
//-------------------------------------------------------
function utils_enforceMaxLength(tbObj, maxlength) {
    if (tbObj.value.length >= maxlength) {
        tbObj.value = tbObj.value.substring(0, maxlength);
        return false;
    }

    return true;
}
//-------------------------------------------------------
utils_installControlKeysMonitor();
//=======================================================
