// HOTKEYS
Event.observe(document,'keypress',handleHotKeys);

var HOTKEYS = { };

function handleHotKeys(e)
{
    var target = (e.target) ? e.target : e.srcElement;
    var key = String.fromCharCode((e.which) ? e.which : e.keyCode);
    if(target.tagName.toLowerCase() != 'input')
    {
        // passing event to hotkey handler so that the handler can cancel event bubbling
        if(HOTKEYS[key] != null) HOTKEYS[key](e);
    }
}

// Form Validation
var inputClass = "";
var selectClass = "";
var tableClass = "";
var beenProcessed = false;

var CommonRegExp = {
    'SignedInteger' : '^[-+]?\\d+$',
    'UnsignedInteger' : '^\\d+$',
    'SignedFloat' : '^[-+]?\\d*\\.?\\d*$',
    'UnsignedFloat' : '^[-+]?\\d*\\.?\\d*$',
    'ZipCode' : '^\\d{5}(-\\d{4})?$',
    'SocialSecurityNumber' : '^\\d{3}-\\d{2}-\\d{4}$',
    'CreditCardNumber' : '^((?:4\\d{3})|(?:5[1-5]\\d{2})|(?:6011)|(?:3[68]\\d{2})|(?:30[012345]\\d))[ -]?(\\d{4})[ -]?(\\d{4})[ -]?(\\d{4}|3[4,7]\\d{13})$',
    'USDollarAmount' : '^\\$(\\d{1,3}(\\,\\d{3})*|(\\d+))(\\.\\d{2})?$',
    'ISO8601Date' : '^([0-9]{4})(-([0-9]{2})(-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\\.([0-9]+))?)?(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?$'
}

function ControlsOK(errorClass,node)
{
    var checkTags = ['input','textarea','select','table'];
    var str = "";
    var firstControl;
    var iclasses = inputClass.split("|");
    var sclasses = selectClass.split("|");
    var tclasses = tableClass.split("|");
    var normalClass = "";

    if (node == null) node = document;
    
    var validityCheckMethod = InputOK;

    for(var e = 0; e < checkTags.length; e++)
    {
        if (checkTags[e] == 'select')
            validityCheckMethod = SelectOK;
        else if (checkTags[e] == 'table')
            validityCheckMethod = CheckBoxListOK;
        
        var elements = node.getElementsByTagName(checkTags[e]);
        
        for(var i = 0; i < elements.length; i++)
        {
            // determine which class list to pull normal class from
            normalClass = (checkTags[e] == 'input' || checkTags[e] == 'textarea') ? iclasses[i] : ((checkTags[e] == 'select')? sclasses[i]: tclasses[i]);
            
            // check validity
            if (!validityCheckMethod(elements.item(i), errorClass, normalClass))
            {
                // get index of first unvalidated element
                if (str == "") firstControl = elements.item(i);
                
                // add up all error messages 
                str += elements.item(i).getAttribute('errorMsg') + "\n"; 
            }
        }
    }
     
    // set this flag so we stop collecting colors
    beenProcessed = true;
    
    if (str != "")
    {
        // show user the error messages
        strMessage = "Invalid Values:\n\n" + str;
        alert(strMessage);
        // set focus to first messed up control
        firstControl.focus();
        return(false); 
    }
    else  // form values are valid
        return(true);
}

// validates a single select box control
function SelectOK(selectElement, errorClass, normalClass)
{
    // get original classess
    if (!beenProcessed)
    {
        normalClass = (!selectElement.className) ? "" : selectElement.className;
        if (selectClass != "") selectClass += "|";
        selectClass += normalClass;
    }

    // make sure control is of correct type
    if ((selectElement.tagName.toLowerCase() != "select"))
    {
        throw("Supplied control is not a select box control.");
        return(false);
    }
    
    // quit if it's not visible
    if (selectElement.style.display == "none")
        return true;  
    
    // assume all is good
    var ok = true;

    // check if element is mandatory; ie has a error message attribute
    if (selectElement.getAttribute("errorMsg") != null)
    {
        // check if single select or multiple select
        // if not multi-select, assume select index 0 
        // says "select" and is not a valid value
        if (!selectElement.getAttribute("multiple"))
        {
            if (selectElement.selectedIndex < 1)
                ok = false;
        }
        else
        {
            if (selectElement.selectedIndex < 0)
                ok = false;
        }
            
        if (!ok)
            selectElement.className = errorClass;
        else
            selectElement.className = normalClass;
    }
    
    return(ok);
}

// validates a single input control
function InputOK(inputElement, errorClass, normalClass)
{
    // get original classes
    if (!beenProcessed)
    {
        normalClass = (!inputElement.className) ? "" : inputElement.className;
        if (inputClass != "") inputClass += "|";
        inputClass += normalClass;
    }

    var tagName = inputElement.tagName.toLowerCase();
    
    // make sure control is of correct type
    if (tagName != 'input' && tagName != 'textarea')
    {
        throw("Supplied control is not an input or textarea element.");
        return false;
    }
    
    // quit if it's not visible
    if (inputElement.style.display == "none")
        return true;
    
    // quit if it's not a text or password input
    if (tagName == 'input')
    {
        var inputType = inputElement.getAttribute('type').toLowerCase();
        
        if (inputType != "text" && inputType != "password")
            return true;
    }

    // get input value and assume all is OK 
    var value = inputElement.value;
    var ok = true;
    
    // check min length for input
    var minLength = (inputElement.getAttribute('minlength')) ? inputElement.getAttribute('minlength') : 0;
    
    // skip if minlenth is 0 and no value was entered
    if (minLength > 0 || value.length > 0)
    {
        if (value.length >= minLength)
        {
            // check max length for input
            var maxLength = (inputElement.getAttribute('maxlength')) ? inputElement.getAttribute('maxlength') : 999999;
            
            if (value.length <= maxLength) 
            {
                // check if element is mandatory; ie has a pattern  
                var ptext = inputElement.getAttribute('pattern');
                
                if (ptext != null)
                {
                    if (typeof(CommonRegExp[ptext]) != 'undefined')
                        ptext = CommonRegExp[ptext];
                    
                    // create regular expression
                    var pattern = new RegExp(ptext, "g");
                    
                    // invalid character is found or the element was left emtpy 
                    ok = (value.match(pattern) != null);
                }
            }
            else
                ok = false;
        }
        else
            ok = false;
    }

    inputElement.className = (!ok) ? errorClass : normalClass;

    return(ok);
}

// validates a single input control
function CheckBoxListOK(tableElement,errorClass,normalClass)
{
    // get original classess
    if (!beenProcessed)
    {
        normalClass = (!tableElement.className) ? "" : tableElement.className;
//        if (tableClass != "") tableClass += "|";
//        tableClass += normalClass;
    }
    
    if ((tableElement.tagName.toLowerCase() != "table"))
    {
        throw("Supplied control is not a table control.");
        return(false);
    }
    
    // check if element is mandatory; ie has a error message attribute
    var etext = tableElement.getAttribute("errorMsg");
    var ok = false;
    
    if (etext != null)
    {
        var elements = tableElement.getElementsByTagName('input');
        for(var i = 0; i < elements.length; i++) 
        {
            if (elements[i].getAttribute('type').toLowerCase() == "checkbox")
            {
                if (elements[i].checked)
                {
                    ok = true;
                    break;
                }
            }
        }
    }
    else
        ok = true;
    
    if (!ok)
        tableElement.className = errorClass;
    else
        tableElement.className = normalClass;
        
    return(ok);
}

// FUI AJAX Functions/Objects

// override prototype's evalJSON method to use JSON.parse instead of eval and to not use X-JSON header
Ajax.Request.prototype.evalJSON = function()
{
    try {
        return JSON.parse(this.transport.responseText);
    } catch (e) {}
}

//*******************************************************************
// *BaseWebMethod*
//
// This object is the base class for making an api call via a proxy.
// Subclasses should specify default values to allow for ease-of-use.
//
function BaseWebMethod(proxy,wsdl,method,parameters,callback,requestType,responseType)
{
        this.init(proxy,wsdl,method,parameters,callback,requestType,responseType);
}

BaseWebMethod.prototype = {
    init: function(proxy,wsdl,method,parameters,callback,requestType,responseType)
    {
        this.proxy        = (proxy        == null) ? ''     : proxy;
        this.wsdl         = (wsdl         == null) ? ''     : wsdl;
        this.method       = (method       == null) ? ''     : method;
        this.parameters   = (parameters   == null) ? {}     : parameters;
        this.callback     = callback;
        this.requestType  = (requestType  == null) ? 'get'  : requestType;
        this.responseType = (responseType == null) ? 'json' : responseType;
        this.asynchronous = true;
        this.ebDefault    = function (result) {};
    },
    
    addHeader: function(header,value)
    {
        if(this.headers == null)
        {
            this.headers = [];
        }
        this.headers.push(header);
        this.headers.push(value);
    },
    
    addParameter: function(key,value)
    {
        if(this.parameters == null)
        {
            this.parameters = {};
        }
        this.parameters[key] = value;
    },
    
    getCallback: function()
    {
        if(this.callback == null)
        {
            try
            {
                this.callback = eval('cb' + this.method.substring(0, 1).toUpperCase() + 
                                     this.method.substring(1, this.method.length));
            }
            catch(err)
            {
                alert('There is no callback defined for function: "' + this.method + 
                      '" via proxy: "' + this.proxy + '"');
            }
        }
        return(this.callback);
    },
    
    getErrorback: function()
    {
        if(this.errorback == null)
        {
            try
            {
                this.errorback = eval('eb' + this.method.substring(0, 1).toUpperCase() + 
                                      this.method.substring(1, this.method.length));
            }
            catch(err)
            {
                this.errorback = this.ebDefault;
            }
        }
        return(this.errorback);
    },
    
    call: function()
    {
        var callback = function() { };
        if(this.asynchronous)
        {
            callback = this.getCallback();
        }
        var request = new Ajax.Request(
                                        this.proxy,
                                        {
                                          method:this.requestType,
                                          parameters:$H(
                                                         {
                                                           wsdl:this.wsdl,
                                                           method:this.method,
                                                           parameters:JSON.stringify(this.parameters),
                                                           soapHeaders:JSON.stringify(this.soapHeaders),
                                                           response:this.responseType
                                                         }
                                                       ).toQueryString(),
                                          onComplete:callback,
                                          onFailure:this.getErrorback(),
                                          requestHeaders:this.headers,
                                          asynchronous:this.asynchronous
                                        }
                                      );
        return(request);
    }
}

//*******************************************************************
// *LocalPHPMethod*
//
// This object is used for making calls against a local PHP library.
// PHP does not allow keyword arguments to function calls, therefore,
// you must provide one parameter called 'arguments' with an array
// of arguments to be given to the designated function. No other 
// parameters will be used. Not providing 
//
LocalPHPMethod.prototype = new BaseWebMethod();
LocalPHPMethod.prototype.constructor = LocalPHPMethod;
LocalPHPMethod.prototype.superclass = BaseWebMethod.prototype;

// Constructor
function LocalPHPMethod(method,parameters,callback,requestType,responseType)
{
    this.init('/proxies/php_lib_proxy.php','',method,parameters,callback,requestType,responseType);
}

//*******************************************************************
// *FUIHTMLRequest*
//
// This object is used for requesting static HTML files.
//
FUIHTMLRequest.prototype = new BaseWebMethod();
FUIHTMLRequest.prototype.constructor = FUIHTMLRequest;
FUIHTMLRequest.prototype.superclass = BaseWebMethod.prototype;

// Constructor
function FUIHTMLRequest(url,method,callback,requestType,responseType)
{
    method = (method == null || method == "") ? "requestHTML" : method;
    this.init(url,"",method,{},callback,requestType,responseType);
}

//*******************************************************************
// *timer*
//
// usage example:
//
// var t = new timer();
// t.stop();
// alert(t.runTime);
//
function timer(name)
{
    this.timerName = name;
    this.startTime = new Date();
    this.stopTime = null;
    this.runTime = 0;

    this.stop = function()
    {
        this.stopTime = new Date();
        this.runTime = (this.stopTime - this.startTime);
    }
}

// DHTML Functions
function setElementScroll(el,elPos)
{
	var obj = $(el);
	
	if(obj)
		$(elPos).value = obj.scrollTop;
}

function setScroll(what)
{
    setElementScroll(what,'scrollPos');
}

function elementScrollTo(el,elPos)
{
	var obj = $(el);
	
	if(obj)
		obj.scrollTop = $F(elPos);
}

function scrollTo(what)
{
    elementScrollTo(what,'scrollPos');
}

function toggle(obj)
{
    var el = document.getElementById(obj);
    if ( el.style.display != 'none' )
    {
        el.style.display = 'none';
        return false;
    }
    else
    {
        el.style.display = '';
        return true;
    }
}

function setWindowSelfId(windowId)
{
    var selfIdInput = $('ws_id');
    if (selfIdInput != null && selfIdInput.tagName.toLowerCase() == 'input')
        selfIdInput.value = windowId;
}

function setWindowOpenerId(openerId)
{
    var openerIdInput = $('wo_id');
    if (openerIdInput != null && openerIdInput.tagName.toLowerCase() == 'input')
        openerIdInput.value = openerId;
}

function pageOpen(newUrl,pageTitle,appId)
{
    if (window.parent != null && window.parent != window && window.parent.page_Open != null)
    {
        var win = (window.parent.page_Open(newUrl,pageTitle,appId));
        
        if (win.content.contentWindow != null)
        {
            // set window opener id on child window load otherwise setWindowOpenerId will be null
            Event.observe(win.content.contentWindow,'load',(function (win,parentWindowId) {
                return function()
                {
                    // pass the window id of this window to a function inside the new window
                    if (win.content.contentWindow.setWindowOpenerId != null)
                        win.content.contentWindow.setWindowOpenerId(parentWindowId);
                };
            })(win,$F('ws_id')));
        }
        
        return win;
    }
    else
        return (openBrowser(newUrl));
}

function pageClose(windowId)
{
    if (windowId != null && window.parent != null && window.parent != window && window.parent.page_Open != null)
    {
        var win = window.parent.getWindowByIFrameId(windowId);
        
        if (win != null) win.close();
    }
    else
        exit();
}

function openBrowser(url)
{
    return (window.open(url,'_blank', 'width=700, height=600, top=70, left=100, fullscreen=0, location=0, menubar=0, resizable=1, scrollbars=1, status=0, toolbar=0'));
}

function setAsSelectedItem(selectedItem,itemGroup,selectedClass,defaultClass)
{
    // set all non-selected itemGroup to the default class
    for(var i = 0; i < itemGroup.length; i++)
    {
        if(Element.hasClassName(itemGroup[i],selectedClass) && itemGroup[i] != selectedItem)
        {
            Element.removeClassName(itemGroup[i],selectedClass);
        }
        Element.addClassName(itemGroup[i],defaultClass);
    }
    
    // set the selected item to the selected class
    if(Element.hasClassName(selectedItem,defaultClass))
    {
        Element.removeClassName(selectedItem,defaultClass);
    }
    Element.addClassName(selectedItem,selectedClass);
}

function import_css(url)
{
    var isDuplicate = false;
    var head = document.getElementsByTagName("head")[0];
    var cssLinks = head.getElementsByTagName("link");
    var element = document.createElement("link");
    element.rel = 'stylesheet';
    element.href = url;
    element.type = "text/css";
    for(var i = 0; i < cssLinks.length; i++)
    {
        if(cssLinks[i].href == element.href)
        {
            isDuplicate = true;
            break;
        }
    }
    if(!isDuplicate)
        head.appendChild(element);
}

function import_javascript(url)
{
    var isDuplicate = false;
    var head = document.getElementsByTagName("head")[0];
    var scripts = head.getElementsByTagName("script");
    var element = document.createElement("script");
    element.src = url;
    element.type = "text/javascript";
    for(var i = 0; i < scripts.length; i++)
    {
        if(scripts[i].src == element.src)
        {
            isDuplicate = true;
            break;
        }
    }
    if(!isDuplicate)
        head.appendChild(element);
}

function import_widget(widgetName,widgetPath,parentNode)
{
    if(widgetName == null || widgetName == "")
        throw('widgetName: undefined');
    widgetPath = (widgetPath == null) ? '/widgets/' + widgetName + '/' : widgetPath;
    parentNode = (parentNode == null) ? document.body : $(parentNode);
    if($(widgetName + 'Container') == null)
    {
        var container = document.createElement('div');
        container.id = widgetName + 'Container';
        parentNode.appendChild(container);
    }
    import_javascript(widgetPath.rtrim('/') + '/' + widgetName + '.js');
    import_css(widgetPath.rtrim('/') + '/' + widgetName + '.css');
    fillContainer($(widgetName + 'Container'),widgetPath.rtrim('/') + '/' + widgetName + '.html',true);
}

function fillContainer(container,path,asynchronous)
{
    asynchronous = (asynchronous == null || (asynchronous != null && asynchronous == true)) ? true : false;
    var hr = new FUIHTMLRequest(path);
    if(asynchronous)
    {
        hr.callback = function(result) 
        {
            var scripts = result.responseText.extractScriptSources();
            for(var i = 0; i < scripts.length; i++)
            {
                import_javascript(scripts[i]);
            }
            Element.update(container,result.responseText);
        };
        hr.call();
    }
    else
    {
        hr.asynchronous = false;
        var result = hr.call();
        var scripts = result.transport.responseText.extractScriptSources();
        for(var i = 0; i < scripts.length; i++)
        {
            import_javascript(scripts[i]);
        }
        Element.update(container,result.transport.responseText);
    }
}

function replaceContainer(container,path,asynchronous)
{
    asynchronous = (asynchronous == null || (asynchronous != null && asynchronous == true)) ? true : false;
    var hr = new FUIHTMLRequest(path);
    if(asynchronous)
    {
        hr.callback = function(result) { Element.replace(container,result.responseText); };
        hr.call();
    }
    else
    {
        hr.asynchronous = false;
        var result = hr.call();
        Element.replace(container,result.transport.responseText);
    }
}

function fillContainerInnerHTML(container,path,asynchronous)
{
    asynchronous = (asynchronous == null || (asynchronous != null && asynchronous == true)) ? true : false;
    var hr = new FUIHTMLRequest(path);
    if(asynchronous)
    {
        hr.callback = function(result) 
        {
            var scripts = result.responseText.extractScriptSources();
            for(var i = 0; i < scripts.length; i++)
            {
                import_javascript(scripts[i]);
            }
            $(container).innerHTML = result.responseText.stripScripts();
            result.responseText.evalScripts();
        };
        hr.call();
    }
    else
    {
        hr.asynchronous = false;
        var result = hr.call();
        var scripts = result.transport.responseText.extractScriptSources();
        for(var i = 0; i < scripts.length; i++)
        {
            import_javascript(scripts[i]);
        }
        $(container).innerHTML = result.transport.responseText.stripScripts();
        result.transport.responseText.evalScripts();
    }
}

function insertAfter(parent,node,referenceNode)
{
    parent.insertBefore(node, referenceNode.nextSibling);
}

function getElementsByClass(searchClass,node,tag)
{
    var classElements = new Array();
    if ( node == null )
        node = document;
    if ( tag == null )
        tag = '*';
    var els = node.getElementsByTagName(tag);
    var elsLen = els.length;
    var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
    for (i = 0, j = 0; i < elsLen; i++)
    {
        if ( pattern.test(els[i].className) )
        {
            classElements[j] = els[i];
            j++;
        }
    }
    return(classElements);
}

// close browser window w/o confirmation
function exit()
{
    window.opener = top;
    window.close();
}

// kick out of frameset
function kickOut()
{
    if (window.self != window.top) window.top.location = window.self.location; 
}

function setCheckedValue(radioObj,newValue)
{
    if(!radioObj) return;
    
    var radioLength = radioObj.length;
        
    if(radioLength == undefined)
    {
        radioObj.checked = (radioObj.value == newValue);
        return;
    }
        
    for(var i = 0; i < radioLength; i++)
    {
        radioObj[i].checked = false;
        
        if(radioObj[i].value == newValue)
            radioObj[i].checked = true;
    }
}

function getCookie(name)
{
    var start = document.cookie.indexOf( name + "=" );
    var len = start + name.length + 1;
    if ( ( !start ) && ( name != document.cookie.substring( 0, name.length ) ) )
        return(null);
    if ( start == -1 ) return(null);
    var end = document.cookie.indexOf( ";", len );
    if ( end == -1 ) end = document.cookie.length;
    return(unescape(document.cookie.substring(len,end)));
}
    
function setCookie(name,value,expires,path,domain,secure)
{
    var today = new Date();
    today.setTime( today.getTime() );
    if ( expires )
        expires = expires * 1000 * 60 * 60 * 24;
    var expires_date = new Date( today.getTime() + (expires) );
    document.cookie = name+"="+escape( value ) +
        ( ( expires ) ? ";expires="+expires_date.toGMTString() : "" ) + //expires.toGMTString()
        ( ( path ) ? ";path=" + path : "" ) +
        ( ( domain ) ? ";domain=" + domain : "" ) +
        ( ( secure ) ? ";secure" : "" );
}
    
function deleteCookie(name,path,domain)
{
    if ( getCookie( name ) ) document.cookie = name + "=" +
            ( ( path ) ? ";path=" + path : "") +
            ( ( domain ) ? ";domain=" + domain : "" ) +
            ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
}

function handleEnterKey(evt,func,params)
{
    evt = (evt) ? evt : (window.event) ? event : null;
    if (evt)
    {
        var charCode = (evt.charCode) ? evt.charCode :
            ((evt.keyCode) ? evt.keyCode :
            ((evt.which) ? evt.which : 0));

        if (charCode == 13)
        {
            evt.returnValue = false;
            evt.cancel = true;
            func(params);
        }
    }
}

// allows programmatic assignment of an event handler to an objects (node) event
function addEvent(obj,type,fn)
{
    if (obj.addEventListener)
    {
        obj.addEventListener( type, fn, false );
        EventCache.add(obj, type, fn);
    }
    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);
    }
    else
    {
        obj["on"+type] = obj["e"+type+fn];
    }
}

// keeps a cache of eventhandlers assigned to object (node) events
var EventCache = function()
{
    var listEvents = [];
    var returnVal = {
        listEvents : listEvents,
        add : function(node, sEventName, fHandler)
        {
            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]);
                };
                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;
            };
        }
    };
    return(returnVal);
}();
addEvent(window,'unload',EventCache.flush);

// Customer Extensions to builtin JavaScript objects
Array.prototype.inArray = function(value)
{
    var i;
    for (i=0; i < this.length; i++)
    {
        if (this[i] === value)
            return(true);
    }
    return(false);
};

// Date extension for dealing with the date format used by Pioneer
Date.prototype.setISO8601 = function(string)
{
    var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})" +
        "(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?" +
        "(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
    var d = string.match(new RegExp(regexp));
    
    var offset = 0;
    var date = new Date(d[1], 0, 1);
    
    if (d[3]) { date.setMonth(d[3] - 1); }
    if (d[5]) { date.setDate(d[5]); }
    if (d[7]) { date.setHours(d[7]); }
    if (d[8]) { date.setMinutes(d[8]); }
    if (d[10]) { date.setSeconds(d[10]); }
    if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); }
    if (d[14])
    {
        offset = (Number(d[16]) * 60) + Number(d[17]);
        offset *= ((d[15] == '-') ? 1 : -1);
    }
    
    offset -= date.getTimezoneOffset();
    time = (Number(date) + (offset * 60 * 1000));
    this.setTime(Number(time));
}

Date.prototype.toISO8601String = function(format,offset)
{
    /* accepted values for the format [1-6]:
     1 Year:
       YYYY (eg 1997)
     2 Year and month:
       YYYY-MM (eg 1997-07)
     3 Complete date:
       YYYY-MM-DD (eg 1997-07-16)
     4 Complete date plus hours and minutes:
       YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00)
     5 Complete date plus hours, minutes and seconds:
       YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)
     6 Complete date plus hours, minutes, seconds and a decimal
       fraction of a second
       YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00)
    */
    if (!format) { var format = 6; }
    if (!offset)
    {
        var offset = 'Z';
        var date = this;
    }
    else
    {
        var d = offset.match(/([-+])([0-9]{2}):([0-9]{2})/);
        var offsetnum = (Number(d[2]) * 60) + Number(d[3]);
        offsetnum *= ((d[1] == '-') ? -1 : 1);
        var date = new Date(Number(Number(this) + (offsetnum * 60000)));
    }
    
    var zeropad = function (num) { return(((num < 10) ? '0' : '') + num); }
    
    var str = "";
    str += date.getUTCFullYear();
    if (format > 1) { str += "-" + zeropad(date.getUTCMonth() + 1); }
    if (format > 2) { str += "-" + zeropad(date.getUTCDate()); }
    if (format > 3)
    {
        str += "T" + zeropad(date.getUTCHours()) +
               ":" + zeropad(date.getUTCMinutes());
    }
    if (format > 5)
    {
        var secs = Number(date.getUTCSeconds() + "." +
                   ((date.getUTCMilliseconds() < 100) ? '0' : '') +
                   zeropad(date.getUTCMilliseconds()));
        str += ":" + zeropad(secs);
    }
    else if (format > 4) { str += ":" + zeropad(date.getUTCSeconds()); }
    
    if (format > 3) { str += offset; }
    return(str);
}

String.prototype.rtrim = function(trimstr)
{
    trimstr = (trimstr == null) ? " \t\r\n" : trimstr;
    var i = (this.length - 1);
    while(i > 0 && (trimstr.indexOf(this.charAt(i)) != -1))
        i--;
    return(this.substring(0, (i + 1)));
}

String.prototype.ltrim = function(trimstr)
{
    trimstr = (trimstr == null) ? " \t\r\n" : trimstr;
    var i = 0;
    while(i < this.length && (trimstr.indexOf(this.charAt(i)) != -1))
        i++;
    return(this.substring(i, this.length));
}

String.prototype.trim = function(trimstr)
{
    trimstr = (trimstr == null) ? " \t\r\n" : trimstr;
    var i = (this.length - 1);
    var e = 0;
    while(i > 0 && (trimstr.indexOf(this.charAt(i)) != -1))
        i--;
    while(e < this.length && (trimstr.indexOf(this.charAt(e)) != -1))
        e++;
    return(this.substring(e,(i + 1)));
}

String.prototype.extractScriptSources = function()
{
    var scriptFragment = '(?:<script.*src=(\\S+).*?>)(?:<\/script>)'
    var matchAll = new RegExp(scriptFragment, 'img');
    var matchOne = new RegExp(scriptFragment, 'im');
    return (this.match(matchAll) || []).map(function(scriptTag) {
      return (scriptTag.match(matchOne) || ['', ''])[1].trim("'\"");
    });
}

String.prototype.asTemplate = function()
{
    return(new Template(this.toString()));
}

// empty a dom node by removing all children
Object.extend(Element,{
    removeChildNodes: function(element)
    {
        element = $(element);
        while(element.childNodes[0] != null) element.removeChild(element.childNodes[0]);
    }
});

//*******************************************************************
// *SelectElementInterface*
//
// This object is used to extend HTMLSelectElements.
//
// Features:
//      * easily add new options
//      * easily add unique options
//      * easily empty HTMLSelectElements
//      * easily remove all options after a certian index
//
// USAGE: Object.extend($('element_id'),SelectElementInterface);
//
var SelectElementInterface = {
    addOption: function(text,value)
    {
        option = new Option(text,(value == null) ? text : value);
        this.options.add(option);
    },
    
    addUniqueOption: function(text,value)
    {
        option = new Option(text,(value == null) ? text : value);
        var duplicate = false;
        for(var i = 0; i < this.options.length; i++)
        {
            this.options[i].value = (this.options[i].value == '' || this.options[i].value == null) ? this.options[i].text : this.options[i].value;
            if(this.options[i].value == option.value && this.options[i].text == option.text)
            {
                duplicate = true;
                break;
            }
        }
        if(!duplicate)
        {
            this.options.add(option)
        }
    },
    
    removeAll: function()
    {
        this.options.length = 0;
    },
	
    removeSelected: function()
    {
		var count = this.options.length;
		
		for(var i=count-1; i>=0; i--)
		{
			if(this.options[i].selected)
				this.options[i] = null;
		}
    },

    removeAfter: function(index)
    {
        index = (index > this.options.length) ? this.options.length : index;
        this.options.length = index;
    },
    
    select: function(text,value)
    {
        for(var i = 0; i < this.options.length; i++)
        {
            this.options[i].value = (this.options[i].value == '' || this.options[i].value == null) ? this.options[i].text : this.options[i].value;
            if(this.options[i].value == value && this.options[i].text == text)
                this.options[i].selected = true;
            else
                this.options[i].selected = false;
        }
    },
    
    selectAll: function()
    {
        for(var i = 0; i < this.options.length; i++)
            this.options[i].selected = true;
    },
    
    selectByValue: function(value)
    {
        for(var i = 0; i < this.options.length; i++)
        {
            if(this.options[i].value == value)
                this.options[i].selected = true;
            else
                this.options[i].selected = false;
        }
    },
    
    selectByText: function(text)
    {
        for(var i = 0; i < this.options.length; i++)
        {
            if(this.options[i].text == text)
                this.options[i].selected = true;
            else
                this.options[i].selected = false;
        }
    },
    
    exist: function(text,value)
    {
        for(var i = 0; i < this.options.length; i++)
        {
            if(this.options[i].text == text && this.options[i].value == value)
                return true;
        }
        
        return false;
    }
};

// Helper Functions
function timeMethod(obj,method,count)
{
    if(typeof(obj[method]) != 'function') { return(false); }
    var i = 0;
    var t = new timer();
    while(i < count)
    {
        var result = obj[method]();
        i++;
    }
    t.stop();
    return(t.runTime);
}

function timeFunction(func,count)
{
    if(typeof(func) != 'function') { return(false); }
    var i = 0;
    var t = new timer();
    while(i < count)
    {
        var result = func();
        i++;
    }
    t.stop();
    return(t.runTime);
}

function getQueryArgs(queryString)
{
    queryString = unescape((queryString.charAt(0) == "?") ? queryString.substring(1,queryString.length) : queryString);
    var pairs = queryString.split('&');
    var queryArgs = {};
    for(var i = 0; i < pairs.length; i++)
    {
        var items = pairs[i].split("=");
        queryArgs[items[0]] = items[1];
    }
    return(queryArgs);
}



//*******************************************************************
// *FUITable*
//
// This object is used for creating HTML tables.
//
// Supports:
//      * Header Row
//      * Totals
//      * Sub Totals
//      * Sorting
//
function FUITable(headers,rows,headerTrans,totaledFields)
{
    this.init(headers,rows,headerTrans,totaledFields);
}

FUITable.prototype = {
    init: function(headers,rows,headerTrans,totaledFields)
    {
        this.headers = headers;
        this.rows = rows;
        this.footer = {};
        this.tableID = 'FUITable';
        this.displayFooter = false;
        this.displaySubTotals = false;
        this.groupField = headers[0];
        this.headerTrans = (headerTrans == null) ? {} : headerTrans;
        this.totaledFields = (totaledFields == null) ? [] : totaledFields;
        this.formatters = {};
        this.sortDirection = 1;
        this.lastSortFunction = '';
    },
    
    addFormatter: function(header,fctn)
    {
        if(this.formatters == null)
        {
            this.formatters = {};
        }
        this.formatters[header] = fctn;
    },
    
    sort: function(sortFunction)
    {
        var sortedRows = [];
        this.sortDirection = (this.lastSortFunction.toString() == sortFunction.toString()) ? (this.sortDirection * -1) : 1;
        
        if(this.displaySubTotals && this.totaledFields.length > 0)
        {
            var groupNames = [];
            var groups = {};
            for(var i = 0; i < this.rows.length; i++)
            {
                if(groups[this.rows[i][this.groupField]] == null)
                {
                    groups[this.rows[i][this.groupField]] = [];
                    groupNames.push(this.rows[i][this.groupField]);
                }
                groups[this.rows[i][this.groupField]].push(this.rows[i]);
            }
            for(var i = 0; i < groupNames.length; i++)
            {
                groups[groupNames[i]].sort(sortFunction);
                if(this.sortDirection == -1) groups[groupNames[i]].reverse();
                for(var e = 0; e < groups[groupNames[i]].length; e++)
                    sortedRows.push(groups[groupNames[i]][e]);
            }
            this.rows = sortedRows;
        }
        else
        {
            this.rows.sort(sortFunction)
            if(this.sortDirection == -1) this.rows.reverse();
        }
        this.lastSortFunction = sortFunction;
    },
    
    headerAsHTML: function()
    {
        var headerHTML = '<tr id="FUITableHeader" class="FUITableHeader"> ';
        for(var i = 0; i < this.headers.length; i++)
        {
            headerHTML += '<th id="FUITableHeader-cell-' + i + '" class="FUITableCell FUITable-column-' + i + '">' + ((this.headerTrans[this.headers[i]] == null) ?  this.headers[i] : this.headerTrans[this.headers[i]]) + '</th> ';
        }
        headerHTML += '</tr> ';
        return(headerHTML);
    },
    
    rowsAsHTML: function()
    {
        var last = '';
        var subTotalCounter = 0;
        var subTotal = {};
        var bodyHTML = '';
        for(var i = 0; i < this.rows.length; i++)
        {
            bodyHTML += '<tr id="FUITableRow-' + i + '" class="FUITableBody ' + ((i % 2 == 0) ? 'FUITableRow' : 'FUITableAltRow') + ' FUITable-row-' + i + '"> '
            for(var e = 0; e < this.headers.length; e++)
            {
                bodyHTML += '<td id="FUITableRow-cell-' + i + '-' + e + '" class="FUITableCell FUITable-column-' + e + '">' + ((this.formatters[this.headers[e]] == null) ? ((this.rows[i][this.headers[e]] == null) ? '' : this.rows[i][this.headers[e]]) : this.formatters[this.headers[e]](this.rows[i][this.headers[e]])) + '</td> ';
            }
            bodyHTML += '</tr> ';
            if(this.displaySubTotals && this.totaledFields.length > 0)
            {
                for(var j = 0; j < this.totaledFields.length; j++)
                {
                    if(subTotal[this.totaledFields[j]] == null)
                    {
                        subTotal[this.totaledFields[j]] = 0;
                    }
                    subTotal[this.totaledFields[j]] += (this.rows[i][this.totaledFields[j]] * 1);
                }
                if(((i + 1) >= this.rows.length) || ((i + 1) < this.rows.length && this.rows[i][this.groupField] != this.rows[i + 1][this.groupField]))
                {
                    bodyHTML += '<tr id="FUITableSubTotal-' + subTotalCounter + '" class="FUITableBody FUITableSubTotal"> '
                    for(var j = 0; j < this.headers.length; j++)
                    {
                        bodyHTML += '<td id="FUITableSubTotal-cell-' + subTotalCounter + '-' + j + '" class="FUITableCell FUITable-column-' + j + '">' + ((this.formatters[this.headers[j]] == null) ? ((subTotal[this.headers[j]] == null) ? ((j == 0) ? 'SUBTOTAL:' : '&nbsp;') : subTotal[this.headers[j]]) : this.formatters[this.headers[j]](subTotal[this.headers[j]])) + '</td> ';
                    }
                    bodyHTML += '</tr> ';
                    subTotal = {};
                    subTotalCounter++;
                }
            }
            last = this.rows[i][this.groupField];
        }
        return(bodyHTML);
    },
    
    footerAsHTML: function()
    {
        var total = {};
        var footerHTML = '';
        if(this.totaledFields.length > 0 && this.rows.length > 0)
        {
            for(var i = 0; i < this.rows.length; i++)
            {
                for(var j = 0; j < this.totaledFields.length; j++)
                {
                    if(total[this.totaledFields[j]] == null)
                    {
                        total[this.totaledFields[j]] = 0;
                    }
                    total[this.totaledFields[j]] += (this.rows[i][this.totaledFields[j]] * 1);
                }
            }
            footerHTML = '<tr id="FUITableFooter" class="FUITableFooter"> ';
            for(var i = 0; i < this.headers.length; i++)
            {
                footerHTML += '<td id="FUITableFooter-cell-' + i + '" class="FUITableCell FUITable-column-' + i + '">' + ((this.formatters[this.headers[i]] == null) ? ((total[this.headers[i]] == null) ? ((i == 0) ? 'TOTAL:' : '&nbsp;') : total[this.headers[i]]) : this.formatters[this.headers[i]](total[this.headers[i]])) + '</td> ';
            }
            footerHTML += '</tr> ';
        }
        return(footerHTML);
    },
    
    toHTML: function()
    {
        var tableHTML = '<table id="'+ this.tableID + '"> ';
        tableHTML += this.headerAsHTML();
        tableHTML += this.rowsAsHTML();
        if(this.displayFooter)
        {
            tableHTML += this.footerAsHTML();
        }
        tableHTML += '</table> ';
        return(tableHTML);
    }
}

// FUITable function generators
function FUITableNumericSortFunction(fieldName)
{
    var sortFunction = function(a,b)
    {
        var aa = parseFloat(a[fieldName]);
        if (isNaN(aa)) aa = 0;
        var bb = parseFloat(b[fieldName]); 
        if (isNaN(bb)) bb = 0;
        return(aa - bb);
    };
    return(sortFunction);
}

function FUITableCurrencySortFunction(fieldName)
{
    var sortFunction = function(a,b)
    {
        var aa = a[fieldName].replace(/[^0-9.]/g,'');
        aa = parseFloat(a);
        if (isNaN(aa)) aa = 0;
        var bb = b[fieldName].replace(/[^0-9.]/g,'');
        bb = parseFloat(b); 
        if (isNaN(bb)) bb = 0;
        return(aa - bb);
    }
    return(sortFunction);
}

function FUITableDateSortFunction(fieldName)
{
    var sortFunction = function(a,b)
    {
        var aa = new Date(a[fieldName]).getTime();
        aa = parseFloat(aa);
        if (isNaN(aa)) aa = 0;
        var bb = new Date(b[fieldName]).getTime();
        bb = parseFloat(bb); 
        if (isNaN(bb)) bb = 0;
        return(aa - bb);
    };
    return(sortFunction);
}

function FUITableCaseInsensitiveSortFunction(fieldName)
{
    var sortFunction = function(a,b)
    {
        var aa = a[fieldName].toLowerCase();
        var bb = b[fieldName].toLowerCase();
        if(aa > bb) return(1);
        if(aa < bb) return(-1);
        return(0);
    };
    return(sortFunction);
}

function FUITableDefaultSortFunction(fieldName)
{
    var sortFunction = function(a,b)
    {
        var aa = a[fieldName];
        var bb = b[fieldName];
        if(aa > bb) return(1);
        if(aa < bb) return(-1);
        return(0);
    };
    return(sortFunction);
}

/*
function getParsedTemplate($text,$modifiers) {
    $matchList = array();
    if(preg_match_all('/<%\w+%>/',$text,$matchList)) {
        foreach($matchList as $eachMatchList) {
            foreach($eachMatchList as $eachMatch) {
                $tmpMatch = $eachMatch;
                $tmpMatch = str_replace('<%','',$tmpMatch);
                $tmpMatch = str_replace('%>','',$tmpMatch);
                if(isset($modifiers[$tmpMatch])) {
                    $text = str_replace($eachMatch,$modifiers[$tmpMatch],$text);
                }
            }
        }
    }
    return($text);
}
*/

//*******************************************************************
// *Template*
//
// This object is used for parsing template strings. Values come from
// either a passed in object (key value pairs) or from the window object
//
// Example:
//      
//      var template = 'I have <%count%> tacos.'.asTemplate();
//      var parsedText = template.parse({count: 10});
//
//      OR
//
//      'I have <%count%> tacos.'asTemplate().parse({count: 10});
//      
// Result:
//
//      I have 10 tacos.
//
var Template = Class.create();
Object.extend(Template.prototype,{
    
    initialize: function(string)
    {
        this.string = string;
    },
    
    parse: function(data)
    {
        data = (data == null) ? window : data;
        var result = this.string;
        var matches = this.string.match(new RegExp('<%[A-z-_]+%>'));
        for(var i = 0; i < matches.length; i++)
        {
            var key = matches[i].trim('<%>');
            if(data[key] != null)
                result = result.replace(matches[i],data[key]);
        }
        return(result);
    }
    
});

function Template(str)
{
    this.str = str;
    this.parse = function(data)
    {
        var parsedStr = 'Parsed: ' + str;
        return(parsedStr);
    }
}


// FUNCTION_TIMERS = { 'function' : {'callCount' : 0,'runTime' : 0.0}};
var FUNCTION_TIMERS = {};

var FunctionTimer = Class.create();
Object.extend(FunctionTimer.prototype,{
    
    initialize: function(func)
    {
        if(func.constructor != null && func.constructor == Function)
            this.functionName = getFunctionName(func);
        else if(func.constructor != null && func.constructor == String)
            this.functionName = func
        else
            this.functionName = '_anonymous_';
        // create FUNCTION_TIMERS entry if it doesn't exist yet
        if(FUNCTION_TIMERS[this.functionName] == null)
            FUNCTION_TIMERS[this.functionName] = {'callCount' : 0,'runTime' : 0,'proxyTime' : 0,'wsdlTime' : 0,'soapTime' : 0};
        this.timer = new timer();
    },
    
    stop: function()
    {
        this.timer.stop()
        // FUNCTION_TIMERS entry should exist, but check to be safe
        if(FUNCTION_TIMERS[this.functionName] == null)
            FUNCTION_TIMERS[this.functionName] = {'callCount' : 0,'runTime' : 0,'proxyTime' : 0,'wsdlTime' : 0,'soapTime' : 0};
        FUNCTION_TIMERS[this.functionName]['callCount']++;
        FUNCTION_TIMERS[this.functionName]['runTime'] += this.timer.runTime / 100;
    }
    
});

function setAjaxFunctionTimer(functionName,proxyTime,wsdlTime,soapTime)
{
    functionName += ' (AJAX)';
    if(FUNCTION_TIMERS[functionName] == null)
        FUNCTION_TIMERS[functionName] = {'callCount' : 1,'runTime' : 0,'proxyTime' : (proxyTime == null) ? 0 : proxyTime,'wsdlTime' : (wsdlTime == null) ? 0 : wsdlTime,'soapTime' : (soapTime == null) ? 0 : soapTime};
    else
    {
        FUNCTION_TIMERS[functionName]['callCount']++;
        FUNCTION_TIMERS[functionName]['proxyTime'] += (proxyTime == null) ? 0 : proxyTime;
        FUNCTION_TIMERS[functionName]['wsdlTime'] += (wsdlTime == null) ? 0 : wsdlTime;
        FUNCTION_TIMERS[functionName]['soapTime'] += (soapTime == null) ? 0 : soapTime;
    }
}

function getFunctionName(caller)
{
    var ownName = caller.arguments.callee.toString();
    ownName = ownName.substr('function '.length);
    ownName = ownName.substr(0, ownName.indexOf('('));
    return((ownName != null && ownName != "") ? ownName : '_anonymous_');
}

function formatCommas(number)
{
	number = number.toString().split("").reverse().join("");
	number = number.replace(/(\d{3})\B/g,"$1,");
	number = number.split("").reverse().join("");

	return number;
}

function preLoadImages()
{
	var imSrcAr = new Array("");
	var imAr = new Array("");
	for(var i=0;i<imSrcAr.length;i++)
	{
		imAr[imAr.length] = new Image();
		imAr[imAr.length-1].src = "images/"+imSrcAr[i]
	}
}

function toggleDisplaySwitch(checked, id)
{
	id.style.display = (checked != false) ? 'block' : 'none';
}

/*
	Example Date.format usage:
		var myDate = new Date();
		alert(myDate.format('M jS, Y'));  // Returns "May 11th, 2006"
*/
Date.prototype.format = function(format)
{
	var returnStr = '';
	var replace = Date.replaceChars;
	
	for (var i = 0; i < format.length; i++)
	{
		var curChar = format.charAt(i);
		
		if (replace[curChar])
			returnStr += replace[curChar].call(this);
		else
			returnStr += curChar;
	}
	
	return returnStr;
};

Date.replaceChars = { 
					  shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
					  longMonths: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
					  shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
					  longDays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
					  
					  // Day
					  d: function() { return (this.getDate() < 10 ? '0' : '') + this.getDate(); },
					  D: function() { return Date.replaceChars.shortDays[this.getDay()]; },
					  j: function() { return this.getDate(); },
					  l: function() { return Date.replaceChars.longDays[this.getDay()]; },
					  N: function() { return this.getDay() + 1; },
					  S: function() { return (this.getDate() % 10 == 1 && this.getDate() != 11 ? 'st' : (this.getDate() % 10 == 2 && this.getDate() != 12 ? 'nd' : (this.getDate() % 10 == 13 && this.getDate() != 1 ? 'rd' : 'th'))); },
					  w: function() { return this.getDay(); },
					  z: function() { return "Not Yet Supported"; },
					  
					  // Week
					  W: function() { return "Not Yet Supported"; },
					  
					  // Month
					  F: function() { return Date.replaceChars.longMonths[this.getMonth()]; },
					  m: function() { return ((this.getMonth() + 1) < 10 ? '0' : '') + (this.getMonth() + 1); },
					  M: function() { return Date.replaceChars.shortMonths[this.getMonth()]; },
					  n: function() { return this.getMonth() + 1; },
					  t: function() { return "Not Yet Supported"; },
					  
					  // Year
					  L: function() { return "Not Yet Supported"; },
					  o: function() { return "Not Supported"; },
					  Y: function() { return this.getFullYear(); },
					  y: function() { return ('' + this.getFullYear()).substr(2); },
					  
					  // Time
					  a: function() { return this.getHours() < 12 ? 'am' : 'pm'; },
					  A: function() { return this.getHours() < 12 ? 'AM' : 'PM'; },
					  B: function() { return "Not Yet Supported"; },
					  g: function() { return this.getHours() == 0 ? 12 : (this.getHours() > 12 ? this.getHours() - 12 : this.getHours()); },
					  G: function() { return this.getHours(); },
					  // h: function() { return (this.getHours() < 10 || (12 < this.getHours() < 22) ? '0' : '') + (this.getHours() < 10 ? this.getHours() + 1 : this.getHours() - 12); },
					  h: function() { return (this.getHours() < 10 || (12 < this.getHours() && this.getHours() < 22) ? '0' : '') + (this.getHours() < 13 ? this.getHours() : this.getHours() - 12); },
					  H: function() { return (this.getHours() < 10 ? '0' : '') + this.getHours(); },
					  i: function() { return (this.getMinutes() < 10 ? '0' : '') + this.getMinutes(); },
					  s: function() { return (this.getSeconds() < 10 ? '0' : '') + this.getSeconds(); },
					  
					  // Timezone
					  e: function() { return "Not Yet Supported"; },
					  I: function() { return "Not Supported"; },
					  O: function() { return (this.getTimezoneOffset() < 0 ? '-' : '+') + (this.getTimezoneOffset() / 60 < 10 ? '0' : '') + (this.getTimezoneOffset() / 60) + '00'; },
					  T: function() { return "Not Yet Supported"; },
					  Z: function() { return this.getTimezoneOffset() * 60; },
					  
					  // Full Date/Time
					  c: function() { return "Not Yet Supported"; },
					  r: function() { return this.toString(); },
					  U: function() { return this.getTime() / 1000; }
					} 

/*
	Example Browswer Detect usage
	
	BrowserDetect.browser <- returns Browser that is being used
	
	if(BrowserDetect.browser == "Explorer")
		dosomethingInIE();
	
	if(BrowserDetect.browser == "Firefox")
		dosomethingInFirefox();		
	
	BrowserDetect.version <- returns the version of the browser being used
	BrowserDetect.OS <- returns the operating system being used
*/				
var BrowserDetect = {
	init: function () {
		this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
		this.version = this.searchVersion(navigator.userAgent)
			|| this.searchVersion(navigator.appVersion)
			|| "an unknown version";
		this.OS = this.searchString(this.dataOS) || "an unknown OS";
	},
	searchString: function (data) {
		for (var i=0;i<data.length;i++)	{
			var dataString = data[i].string;
			var dataProp = data[i].prop;
			this.versionSearchString = data[i].versionSearch || data[i].identity;
			if (dataString) {
				if (dataString.indexOf(data[i].subString) != -1)
					return data[i].identity;
			}
			else if (dataProp)
				return data[i].identity;
		}
	},
	searchVersion: function (dataString) {
		var index = dataString.indexOf(this.versionSearchString);
		if (index == -1) return;
		return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
	},
	dataBrowser: [
		{ 	string: navigator.userAgent,
			subString: "OmniWeb",
			versionSearch: "OmniWeb/",
			identity: "OmniWeb"
		},
		{
			string: navigator.vendor,
			subString: "Apple",
			identity: "Safari"
		},
		{
			prop: window.opera,
			identity: "Opera"
		},
		{
			string: navigator.vendor,
			subString: "iCab",
			identity: "iCab"
		},
		{
			string: navigator.vendor,
			subString: "KDE",
			identity: "Konqueror"
		},
		{
			string: navigator.userAgent,
			subString: "Firefox",
			identity: "Firefox"
		},
		{
			string: navigator.vendor,
			subString: "Camino",
			identity: "Camino"
		},
		{		// for newer Netscapes (6+)
			string: navigator.userAgent,
			subString: "Netscape",
			identity: "Netscape"
		},
		{
			string: navigator.userAgent,
			subString: "MSIE",
			identity: "Explorer",
			versionSearch: "MSIE"
		},
		{
			string: navigator.userAgent,
			subString: "Gecko",
			identity: "Mozilla",
			versionSearch: "rv"
		},
		{ 		// for older Netscapes (4-)
			string: navigator.userAgent,
			subString: "Mozilla",
			identity: "Netscape",
			versionSearch: "Mozilla"
		}
	],
	dataOS : [
		{
			string: navigator.platform,
			subString: "Win",
			identity: "Windows"
		},
		{
			string: navigator.platform,
			subString: "Mac",
			identity: "Mac"
		},
		{
			string: navigator.platform,
			subString: "Linux",
			identity: "Linux"
		}
	]

};

BrowserDetect.init();

/* This function takes a string and replaces it with another string for all occurrences */
String.prototype.replaceAll = function(s1, s2)
{ 
    return this.replace(new RegExp(s1,"g"), s2);
}

/* This function takes a string and returns a count of all the character occurrences that were specified */
String.prototype.charCount = function(str)
{
    return this.replaceAll('[^' + str + ']','').length + 1;
}

function addTextBoxOption(txtValue, toElement)
{
    if (toElement.tagName.toLowerCase() != "select")
        throw("Supplied control is not a select box control.");
    
    var toExt = Object.extend(toElement, SelectElementInterface);

    toExt.addUniqueOption(txtValue, txtValue);

    //$A(toElement.options).sort(function(a,b){ return((a.text.toLowerCase() < b.text.toLowerCase() ) ? -1 : 1); }).each(function(o,i){ toElement.options[i] = o;});
    
    return false;
}

function moveOption(fromElement, toElement)
{
    if (fromElement.tagName.toLowerCase() != "select" || toElement.tagName.toLowerCase() != "select")
        throw("Supplied control is not a select box control.");
    
    var fromExt = Object.extend(fromElement, SelectElementInterface);
    var toExt = Object.extend(toElement, SelectElementInterface);

    if (fromElement.selectedIndex >= 0)
    {
        for (var i = 0; i < fromElement.length; i++)
        {
            if (fromElement.options[i].selected)
                toExt.addUniqueOption(fromElement.options[i].text, fromElement.options[i].value);
        }

        fromExt.removeSelected();
//        $A(selectedSubtotals.options).sort(function(a,b){ return((a.text.toLowerCase() < b.text.toLowerCase() ) ? -1 : 1); }).each(function(o,i){ selectedSubtotals.options[i] = o;});
    }
    
    return false;
}

function moveAllOptions(fromElement, toElement)
{
    var fromExt = Object.extend(fromElement,SelectElementInterface);
    
    if (fromElement.length > 0)
    {
        fromExt.selectAll();
        moveOption(fromElement, toElement);
    }

    return false;        
}

function somethingChecked(list)
{
    var elements = list.getElementsByTagName('input');
    
    for(var i = 0; i < elements.length; i++) 
    {
        if (elements[i].getAttribute('type').toLowerCase() == "checkbox")
        {
            if (elements[i].checked)
                return true;
        }
    }
    
    return false;
}