/*
 * CATS
 * JavaScript Library
 *
 * Portions Copyright (C) 2005 - 2007 Cognizo Technologies, Inc.
 *
 * EventCache Copyright (C) 2005 Mark Wubben with modifications made
 * by Cognizo Technologies, Inc. EventCache is licensed under the CC-GNU
 * LGPL <http://creativecommons.org/licenses/LGPL/2.1/>.
 *
 * addEvent() Copyright (C) 2001 Scott Andrew LePera with modifications
 * made by Cognizo Technologies, Inc. No license was given; however,
 * modifications made by Cognizo Technologies, Inc. are subject to the
 * terms of the CATS Public License Version 1.1 (see below).
 * http://www.scottandrew.com/weblog/articles/cbs-events
 *
 * The contents of this file are subject to the CATS Public License
 * Version 1.1a (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.catsone.com/.
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is "CATS Standard Edition".
 *
 * The Initial Developer of the Original Code is Cognizo Technologies, Inc.
 * Portions created by the Initial Developer are Copyright (C) 2005 - 2007
 * (or from the year in which this file was created to the year 2007) by
 * Cognizo Technologies, Inc. All Rights Reserved.
 *
 *
 * $Id: lib.js 1479 2007-01-17 00:22:21Z will $
 */

/* Data item type flags. These should match up with the flags
 * from config.php.
 */
DATA_ITEM_CANDIDATE = 100;
DATA_ITEM_CLIENT    = 200;
DATA_ITEM_CONTACT   = 300;
DATA_ITEM_JOBORDER  = 400;

/* Default timeout for AJAX requests; 15 seconds. */
AJAX_TIMEOUT = 15000;

/**
 * Returns true if the string is a valid positive integer.
 *
 * @return boolean
 */
function stringIsNumeric(string)
{
    for (var i = 0; i < string.length; i++)
    {
        var character = string.charAt(i);

        if ((character < '0') || (character > '9'))
        {
            return false;
        }
    }

    return true;
}

/**
 * Changes a parent document block's style attribute to make it hidden (by id).
 *
 * @return void
 */
function hideParentBlock(elementID)
{
    element = parent.document.getElementById(elementID);
    element.parentNode.removeChild(element);
}

/**
 * Changes a parent document block's style attribute to make it hidden (by id).
 *
 * @return void
 */
function showParentBlock(elementID)
{
    element = parent.document.getElementById(elementID);
    element.parentNode.removeChild(element);
}

/**
 * Opens a centered popup window.
 *
 * @return void
 */
function openCenteredPopup(url, name, width, height, scrollBars)
{
    var optionString;

    optionString  = 'width=' + width + ',height=' + height;
    optionString += ',top=' + ((screen.availHeight - height) / 2) + ',left=' + ((screen.availWidth - width) / 2);
    optionString += ',scrollbars=';
    optionString += (scrollBars ? 'yes' : 'no');

    /* Open the new window. */
    newWindow = window.open(url, name, optionString);

    /* If this window (parent) has focus, give focus to the popup (child). */
    if (window.focus)
    {
        newWindow.focus();
    }
}

/**
 * Redirects the browser to a url.
 *
 * @return void
 */
function goToURL(url)
{
    window.location = url;
}

/**
 * Redirects the browser to a url.
 *
 * @return void
 */
function parentGoToURL(url)
{
    parent.window.location = url;
}

function parentHidePopWin()
{
    parent.hidePopWin();
}

function parentHidePopWinRefresh()
{
    parent.hidePopWinRefresh();
}

/**
 * Encodes text for transmission via HTTP.
 *
 * @param string text to encode
 * @return string encoded text
 */
function urlEncode(text)
{
    /* Force JavaScript to always treat 'text' as a string. */
    text += '';

    text = text.replace(/\'/g, '%27');
    text = text.replace(/\"/g, '%22');
    text = escape(text);

    return text;
}


/**
 * Replaces HTML special characters in text to be output-safe.
 *
 * @param string text to escape
 * @return string escaped text
 */
function escapeHTML(text)
{
    text = text.replace('&', '&amp;');
    text = text.replace('<', '&lt;');
    text = text.replace('>', '&gt;');
    text = text.replace('"', '&quot;');
    text = text.replace("'", '&apos;');

    return text;
}

/**
 * Replaces output-save HTML with real text characters.
 *
 * @param string text to unescape
 * @return string escaped text
 */
function unEscapeHTML(text)
{
    text = text.replace('&amp;', '&');
    text = text.replace('&lt;', '<');
    text = text.replace('&gt;', '>');
    text = text.replace('&quot;', '"');
    text = text.replace('&apos;', "'");

    return text;
}

/**
 * Removes leading and trailing whitespace from text.
 *
 * @param string text to clean up
 * @return string cleaned string
 */
function trim(text)
{
    return text.replace(/^\s*|\s*$/g, '');
}

/**
 * Gets an XMLHTTP / XMLHttpRequest object for AJAX use.
 *
 * @return void
 */
function AJAX_getXMLHttpObject()
{
    /* Array of possible names for the Microsoft XMLHTTP ActiveX. */
    var MSXML_XMLHTTP_PROGIDS = new Array(
        'MSXML2.XMLHTTP.5.0',
        'MSXML2.XMLHTTP.4.0',
        'MSXML2.XMLHTTP.3.0',
        'MSXML2.XMLHTTP',
        'Microsoft.XMLHTTP'
    );

    var xmlHttp;

    try
    {
        xmlHttp = new XMLHttpRequest();
    }
    catch (errorA)
    {
        var found = false;

        /* Try to figure out what Microsoft might have called their ActiveX control. */
        for (var i = 0; (i < MSXML_XMLHTTP_PROGIDS.length && !found); i++)
        {
            try
            {
                xmlHttp = new ActiveXObject(MSXML_XMLHTTP_PROGIDS[i]);
                found = true;
            }
            catch (errorB)
            {
            }
        }

        if (!found)
        {
            return null;
        }
    }

    return xmlHttp;
}

/**
 * Sends HTTP content headers for an AJAX POST request.
 *
 * @return void
 */
function AJAX_sendPOSTHeaders(http, contentLength)
{
    http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    http.setRequestHeader('Content-length', contentLength);
    http.setRequestHeader('Connection', 'close');
}

/**
 * Returns a random hash to append to an HTTP POST to keep data from being
 * cached (URL-encoded).
 *
 * @return random POST variable hash
 */
function AJAX_getRandomPOSTHash()
{
    return '&rhash=' + urlEncode(parseInt(Math.random() * 99999999).toString());
}

/**
 * Returns a formatted session cookie to append to an HTTP POST.
 *
 * @return formatted cookie
 */
function AJAX_getPOSTSessionID(sessionCookie)
{
    return '&' + sessionCookie;
}

/**
 * Sends an AJAX HTTP POST request back to the specified URL.
 *
 * @return void
 */
function AJAX_POST(http, url, POSTData, callBack, extraTimeout, sessionCookie,
                   silentTimeout)
{
    /* Add a random hash to the POST data to keep IE from caching it. */
    POSTData += AJAX_getRandomPOSTHash();

    /* Append the session cookie if we're using secure AJAX. */
    if (sessionCookie != null)
    {
        POSTData += AJAX_getPOSTSessionID(sessionCookie);
    }

    /* Uncomment for debugging. */
    //alert(POSTData);

    /* Open the socket and send POST headers. */
    http.open('POST', url, true);
    AJAX_sendPOSTHeaders(http, POSTData.length);

    /* Callback function. */
    http.onreadystatechange = callBack;

    /* Send the data. */
    http.send(POSTData);

    /* Abort after timeout expires. */
    if(extraTimeout != 0)
    {
        window.setTimeout(
            function()
            {
                if (AJAX_isCallInProgress(http))
                {
                   http.abort();

                   if (!silentTimeout)
                   {
                       alert(
                           'Timeout on AJAX query after ' +
                           ((AJAX_TIMEOUT + extraTimeout) / 1000) +
                           ' seconds. Please refresh the page and try again.'
                       );
                   }
                }
            },
            AJAX_TIMEOUT + extraTimeout
        );
    }
}

/**
 * Sends an AJAX HTTP POST request to the CATS AJAX Delegation Module.
 *
 * @return void
 */
function AJAX_callCATSFunction(http, funcName, POSTData, callBack,
                               extraTimeout, sessionCookie, silentTimeout, noCache)
{
    /* Prepend the function name to the postdata. */
    var newPOSTData = 'f=' + funcName + POSTData;

    if (noCache == true)
    {
        newPOSTData = 'nobuffer=true&' + newPOSTData;
    }

    AJAX_POST(
        http,
        'ajax.php',
        newPOSTData,
        callBack,
        extraTimeout,
        sessionCookie,
        silentTimeout
    );
}

/**
 * Is an XMLHTTP object being used for an active call?
 *
 * @return boolean is object active
 */
function AJAX_isCallInProgress(http)
{
    switch (http.readyState)
    {
        case 1:
        case 2:
        case 3:
            return true;
            break;
    }

    return false;
}


/*
 ****************************************************************************
 * Notes / Job Description Truncation
 ****************************************************************************
 */


showFullDescription = false;
showFullNotes       = false;

function toggleDescription()
{
    var shortNode = document.getElementById('shortDescription');
    var fullNode  = document.getElementById('fullDescription');

    toggleNode(showFullDescription, shortNode, fullNode);

    if (showFullDescription == true)
    {
        showFullDescription = false;
    }
    else
    {
        showFullDescription = true;
    }
}

function toggleNotes()
{
    var shortNode = document.getElementById('shortNotes');
    var fullNode  = document.getElementById('fullNotes');

    toggleNode(showFullNotes, shortNode, fullNode);

    if (showFullNotes == true)
    {
        showFullNotes = false;
    }
    else
    {
        showFullNotes = true;
    }
}

function toggleNode(showFull, shortNode, fullNode)
{
    if (showFull == true)
    {
        shortNode.style.display = 'block';
        fullNode.style.display  = 'none';
    }
    else
    {
        shortNode.style.display = 'none';
        fullNode.style.display  = 'block';
    }
}

/**
 * Populates a form's City and State from Zip code using AJAX
 *
 * @return void
 */
function CityState_populate(zipEditID, indicatorID)
{
    var http = AJAX_getXMLHttpObject();

    var zip = document.getElementById(zipEditID).value;
    var indicator = document.getElementById(indicatorID);

    indicator.style.visibility = 'visible';

     /* Build HTTP POST data. */
    var POSTData = '&zip=' + urlEncode(zip);

    /* Anonymous callback function triggered when HTTP response is received. */
    var callBack = function ()
    {
        if (http.readyState != 4)
        {
            return;
        }

        if (!http.responseXML)
        {
            var errorMessage = "An error occurred while receiving a response from the server.\n\n"
                             + http.responseText;
            alert(errorMessage);
            indicator.style.visibility = 'hidden';

            return;
        }

        //alert(http.responseText);

        /* Return if we have any errors. */
        var errorCodeNode    = http.responseXML.getElementsByTagName('errorcode').item(0);
        var errorMessageNode = http.responseXML.getElementsByTagName('errormessage').item(0);
        if (!errorCodeNode.firstChild || errorCodeNode.firstChild.nodeValue != '0')
        {
            if (errorCodeNode.firstChild.nodeValue != '-2')
            {
                var errorMessage = "An error occurred while receiving a response from the server.\n\n"
                                 + errorMessageNode.firstChild.nodeValue;
                /* FIXME
                 * Do we have to popup an error dialog, if the zip lookup AJAX request fails?
                 */
                //alert(errorMessage);
                indicator.style.visibility = 'hidden';
            }
            return;
        }

        var cityNode  = http.responseXML.getElementsByTagName('city').item(0);
        var stateNode = http.responseXML.getElementsByTagName('state').item(0);

        if (document.getElementById('city'))
        {
            if (cityNode.firstChild)
            {
                document.getElementById('city').value = cityNode.firstChild.nodeValue;
            }
            else
            {
                document.getElementById('city').value = '';
            }
        }

        if (document.getElementById('state'))
        {
            if (stateNode.firstChild)
            {
                document.getElementById('state').value = stateNode.firstChild.nodeValue;
            }
            else
            {
                document.getElementById('state').value = '';
            }
        }
        indicator.style.visibility = 'hidden';
    }

    AJAX_callCATSFunction(http, 'zipLookup', POSTData, callBack, 0, null, false);
}

/* Returns the value of the radio button that is selected from a radio button
 * group.
 */
function getCheckedValue(radioObj)
{
    if (!radioObj)
    {
        return '';
    }

    var radioLength = radioObj.length;
    if (typeof(radioLength) == 'undefined')
    {
        if (radioObj.checked)
        {
            return radioObj.value;
        }

        return '';
    }

    for (var i = 0; i < radioLength; i++)
    {
        if (radioObj[i].checked)
        {
            return radioObj[i].value;
        }
    }

    return '';
}

/* Checks the specified radio button out of the radio button group by value. */
function setCheckedValue(radioObj, newValue)
{
    if (!radioObj)
    {
        return;
    }

    var radioLength = radioObj.length;
    if (typeof(radioLength) == 'undefined')
    {
        radioObj.checked = (radioObj.value == newValue.toString());
        return;
    }

    for (var i = 0; i < radioLength; i++)
    {
        radioObj[i].checked = false;
        if (radioObj[i].value == newValue.toString())
        {
            radioObj[i].checked = true;
        }
    }
}

function docjslib_getRealLeft(imgElem)
{
    xPos = eval(imgElem).offsetLeft;
    tempEl = eval(imgElem).offsetParent;
    while (tempEl != null)
    {
        xPos += tempEl.offsetLeft;
        tempEl = tempEl.offsetParent;
    }

    return xPos;
}

function docjslib_getRealTop(imgElem)
{
    yPos = eval(imgElem).offsetTop;
    tempEl = eval(imgElem).offsetParent;
    while (tempEl != null)
    {
        yPos += tempEl.offsetTop;
        tempEl = tempEl.offsetParent;
    }

    return yPos;
}

function findValueInArray(array, value)
{
    for (i = 0; i < array.length; i++)
    {
        if (array[i] == value)
        {
            return i;
        }
    }

    return -1;
}

function findValueInSelectList(selectList, value)
{
    for (i = 0; i < selectList.length; i++)
    {
        if (selectList[i].value == value)
        {
            return i;
        }
    }

    return -1;
}

if (Array.prototype.inArray == null)
{
    Array.prototype.inArray = function(value)
    {
        var i;

        for (i = 0; i < this.length; i++)
        {
            if (this[i] === value)
            {
                return true;
            }
        }

        return false;
    };
}

if (Array.prototype.push == null)
{
    Array.prototype.push = function()
    {
        for (var i = 0; i < arguments.length; i++)
        {
            this[this.length] = arguments[i];
        };

        return this.length;
    };
}

/* Event Cache uses an anonymous function to create a hidden scope chain.
 * This is to prevent scoping issues.
 */
var EventCache = function()
{
    var listEvents = [];

    /* This open-brace MUST BE on the same line as the return. */
    return {
        listEvents : listEvents,

        add : function (node, eventName, handler, useCapture)
        {
            listEvents.push(arguments);
        },

        flush : function()
        {
            var i, item;

            for (i = listEvents.length - 1; i >= 0; i = i - 1)
            {
                item = listEvents[i];

                if (item[0].removeEventListener)
                {
                    item[0].removeEventListener(item[1], item[2], item[3]);
                };

                /* From this point on we need the event names to be prefixed
                 * with 'on". */
                if (item[1].substring(0, 2) != 'on')
                {
                    item[1] = 'on' + item[1];
                };

                if (item[0].detachEvent)
                {
                    item[0].detachEvent(item[1], item[2]);
                };

                item[0][item[1]] = null;
            };
        }
    };
}();

function addEvent(obj, type, fn, useCapture)
{
    if (obj.addEventListener)
    {
        obj.addEventListener(type, fn, useCapture);
        EventCache.add(obj, type, fn, useCapture);
    }
    else if (obj.attachEvent)
    {
        obj['e' + type + fn] = fn;
        obj[type + fn] = function()
        {
            obj['e' + type + fn](window.event);
        }
        obj.attachEvent('on' + type, obj[type + fn]);
        EventCache.add(obj, type, fn, useCapture);
    }
    else
    {
        //alert('Handler could not be attached.');
    }
}

function removeEvent(obj, type, fn, useCapture)
{
    if (obj.removeEventListener)
    {
        obj.removeEventListener(type, fn, useCapture);
        return true;
    }

    if (obj.detachEvent)
    {
        return obj.detachEvent('on' + type, fn);
    }

    //alert('Handler could not be removed.');
}

function toggleHotList()
{
    if (document.getElementById('isHot').checked == true)
    {
        document.getElementById('hotListButton').style.display = '';
    }
    else
    {
        document.getElementById('hotListButton').style.display = 'none';
    }
}

function checkQuickSearchForm(form)
{
    fieldValue = document.getElementById('quickSearchFor').value;
    fieldLabel = document.getElementById('quickSearchLabel');

    if (fieldValue == '')
    {
        fieldLabel.style.color = '#ff0000';
        return false;
    }

    fieldLabel.style.color = '#000';

    return true;
}

/* This executes all the <script> tags in dynamically loaded javascript. */
function execJS(text)
{
    var working = text;
    var pos;
    pos = working.indexOf('<script');
    while (pos != -1)
    {
        working = working.substring(pos);
        pos = working.indexOf('>');
        if (pos == -1)
        {
            return;
        }
        working = working.substring(pos);
        pos = working.indexOf('</script>');
        var js = working.substring(1,pos);
        working = working.substring(pos);
        pos = working.indexOf('<script');
        eval(js);
    }
}