////////// HEX //////////

//Hex encode / decode

var hex = {
    encode : function (str) {
        var r = '';
        var e = str.length;
        var c = 0;
        var h;
        while (c < e) {
            h = str.charCodeAt(c++).toString(16);
            while (h.length<3) {
                h = '0' + h;
            }
            r += h;
        }
        return r;
    },

    decode : function (str) {
        var r = '';
        var e = str.length;
        var s;
        while (e >= 0) {
            s = e - 3;
            r = String.fromCharCode('0x' + str.substring(s,e)) + r;
            e = s;
        }
        return r;
    }
}

////////// BASE64 //////////

//Base64 encode / decode
//http://www.webtoolkit.info/

var base64 = {
    // private property
    _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

    // public method for encoding
    encode : function (input) {
        var output = "";
        var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
        var i = 0;

        input = base64._utf8_encode(input);

        while (i < input.length) {
            chr1 = input.charCodeAt(i++);
            chr2 = input.charCodeAt(i++);
            chr3 = input.charCodeAt(i++);

            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;

            if (isNaN(chr2)) {
                    enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
                    enc4 = 64;
            }

            output = output +
            this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
            this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
        }

        return output;
    },

    // public method for decoding
    decode : function (input) {
        var output = "";
        var chr1, chr2, chr3;
        var enc1, enc2, enc3, enc4;
        var i = 0;

        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

        while (i < input.length) {
            enc1 = this._keyStr.indexOf(input.charAt(i++));
            enc2 = this._keyStr.indexOf(input.charAt(i++));
            enc3 = this._keyStr.indexOf(input.charAt(i++));
            enc4 = this._keyStr.indexOf(input.charAt(i++));

            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;

            output = output + String.fromCharCode(chr1);

            if (enc3 != 64) {
                    output = output + String.fromCharCode(chr2);
            }
            if (enc4 != 64) {
                    output = output + String.fromCharCode(chr3);
            }
        }

        output = base64._utf8_decode(output);

        return output;
    },

    // private method for UTF-8 encoding
    _utf8_encode : function (string) {
        string = string.replace(/\r\n/g,"\n");
        var utftext = "";

        for (var n = 0; n < string.length; n++) {
            var c = string.charCodeAt(n);

            if (c < 128) {
                    utftext += String.fromCharCode(c);
            }
            else if ((c > 127) && (c < 2048)) {
                    utftext += String.fromCharCode((c >> 6) | 192);
                    utftext += String.fromCharCode((c & 63) | 128);
            }
            else {
                    utftext += String.fromCharCode((c >> 12) | 224);
                    utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                    utftext += String.fromCharCode((c & 63) | 128);
            }
        }

        return utftext;
    },

    // private method for UTF-8 decoding
    _utf8_decode : function (utftext) {
        var string = "";
        var i = 0;
        var c = c1 = c2 = 0;

        while ( i < utftext.length ) {
            c = utftext.charCodeAt(i);

            if (c < 128) {
                    string += String.fromCharCode(c);
                    i++;
            }
            else if ((c > 191) && (c < 224)) {
                    c2 = utftext.charCodeAt(i+1);
                    string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                    i += 2;
            }
            else {
                    c2 = utftext.charCodeAt(i+1);
                    c3 = utftext.charCodeAt(i+2);
                    string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                    i += 3;
            }
        }

        return string;
    }
}

////////// BISCUIT //////////

//Cookie Management

var biscuit = {
    _dailymsecs : (24 * 60 * 60 * 1000),

    make : function (name, value, days) {
        if (days) {
            var date = new Date();
            date.setTime(date.getTime() + (days * this._dailymsecs));
            var expires = '; expires=' + date.toGMTString();
        } else {
            var expires = '';
        }
        document.cookie = name + '=' + value + expires + '; path=/';
    },
    
    read : function (name) {
        var nameEQ = name + '=';
        var ca = document.cookie.split(';');
        for (var i = 0; i < ca.length; i++) {
            var c = ca[i];
            while (c.charAt(0) == ' ') {
                c = c.substring(1,c.length);
            }
            if (c.indexOf(nameEQ) == 0) {
                return c.substring(nameEQ.length,c.length);
            }
        }
        return null;
    },
    
    kill : function (name) {
        this.createCookie(name,'',-1);
    }
}

////////// RADICAL //////////

//Core Library

var radical = {
    ////////// PROPERTIES //////////
    //public property to track PAGE READY
    pagerdy : false,

    //public property to track MOVE operations
    linking : false,

    //public property to track POST operations
    posting : false,

    //public property to handle/track application objects
    objh : [],

    //public property to handle/track event objects
    evth : [],

    ////////// SCRIPTED MSIE SNIFF //////////
    ismsie : /*@cc_on!@*/false,

    ////////// SCRIPTED UNIQUE SESSION //////////
    jsid : hex.encode(window.location.protocol + window.location.host + window.location.port),

    ////////// GET ASCII CHARACTER //////////
    chr : function (ascii) {
        return String.fromCharCode(ascii);
    },

    outputLog : function (message, doStatus) {
        if (window.console) {
            console.log(message);
        }
        if (doStatus) {
            statusTray.update(message, 3000);
        }
    },

    ////////// SCRIPTED TOGGLE VISIBILITY //////////
    toggleVisibility : function (elementid) {
        if (document.getElementById(elementid) != null) {
            if (document.getElementById(elementid).style.visibility == 'hidden') {
                document.getElementById(elementid).style.visibility = 'visible';
            } else {
                document.getElementById(elementid).style.visibility = 'hidden';
            }
        }
    },

    ////////// SCRIPTED POPUP WINDOWS //////////
    //Action Link
    //<a href="javascript:radical.popupFunction('/loadhtml.php?where=module&what=application.php&openpage=1');">[My Link]</a>
    showURLpopup : function (location) {
        window.open(location,'pop','scrollbars=yes,status=no,menubar=no,resizable=no,location=no,toolbar=no,width=480,height=360');
    },

    showURLwin : function (location) {
        window.open(location,'win','scrollbars=yes,status=no,menubar=no,resizable=no,location=no,toolbar=no,width=800,height=600');
    },

    showURL : function (location) {
        window.open(location,'url','scrollbars=yes,status=no,menubar=no,resizable=no,location=no,toolbar=yes,width=800,height=600');
    },

    //Action Link
    //<a href="javascript:radical.popupFunction('/loadhtml.php?where=module&what=application.php&prntpage=1');">[My Link]</a>
    printURLwin : function (location) {
        window.open(location,'smlprt','scrollbars=yes,status=no,menubar=no,resizable=yes,location=no,toolbar=no,width=320,height=240');
    },

    printURLprt : function (location) {
        window.open(location,'bigprt','scrollbars=yes,status=no,menubar=no,resizable=yes,location=no,toolbar=no,width=640,height=480');
    },

    ////////// SCRIPTED Link //////////
    //Action Link
    //<input type="button" value="Link It!" onClick="radical.link('/loadhtml.php?where=module&what=application.php', 'on')" />
    link : function (url, onewaydoor) {
        //Initialize control values of link function
        //Setting linkStatus to false will prevent the execution of location function, and output any set error message(s)
        var linkStatus = true;
        var linkStatusMessages = new Array();
        var linkConfirm = true;

        if (linkStatus) {
            if (this.pagerdy) {
                //Nothing to do...
            } else {
                linkStatus = false;
                linkStatusMessages.push('ERROR: Page not completely loaded, please allow loading to complete.');
            }
        }

        if (linkStatus) {
            if (this.linking || this.posting) {
                linkStatus = false;
                linkStatusMessages.push('ERROR: There is another LINK/POST operation pending, please allow it to complete before proceeding.');
            } else {
                //Need an option to accomodate cases where linking does not jump to a
                //new page e.g. downloading files or returning binaries
                if (onewaydoor == 'on') {
                    this.linking = true;
                }
            }
        }

        if (linkStatus) {
            if (true) {
                linkConfirm = true;
            }

            if (linkConfirm == true) {
                try {
                    window.location = url;
                } catch(err) {
                    //Reset linking property back to false in the event of an error
                    this.linking = false;
                    radical.outputLog('Failed to set location:' + err.message, true);
                }
            } else {
                //Reset linking property back to false in the event we are not proceeding
                this.linking = false;
            }
        } else {
            radical.outputLog(linkStatusMessages.join('\n'), true);
        }

        //Reset linking property back to false at the end of the MOVE operation
        //this.linking = false;
        //Originally the design called for resetting the status setting back to
        //false so that interaction would be possible in the event of a failure,
        //but observation suggests that javascript does not wait for the
        //transition to finish, hence it resets too early (i.e. the subsequent
        //clicks work out of turn).  The final design turns the status setting
        //into a one-way-door to block multiple clicks.
    },

    ////////// SCRIPTED POST //////////
    //Action Link
    //<input type="button" value="Do This!" onClick="radical.post(this.form, '/loadhtml.php?where=module&what=application.php', '')" />
    post : function (form, url, validate) {
        //Initialize control values of post function
        //Setting postStatus to false will prevent the execution of post function, and output any set error message(s)
        var postStatus = true;
        var postStatusMessages = new Array();
        var postConfirm = false;

        if (postStatus) {
            if (this.pagerdy) {
                //Nothing to do...
            } else {
                postStatus = false;
                postStatusMessages.push('ERROR: Page not completely loaded, please allow loading to complete.');
            }
        }

        if (postStatus) {
            if (form.checkValidity()) {
                //Nothing to do...
            } else {
                postStatus = false;
                postStatusMessages.push('ERROR: Invalid Form Input, please make corrections before proceeding.');
            }
        }

        if (postStatus) {
            if (this.linking || this.posting) {
                postStatus = false;
                postStatusMessages.push('ERROR: There is another LINK/POST operation pending, please allow it to complete before proceeding.');
            } else {
                this.posting = true;
            }
        }

        if (postStatus) {
            if (validate == '' || validate == 'off') {
                postConfirm = true;
            }

            if (validate == 'validate' || validate == 'on') {
                postConfirm = confirm('ALERT!!! You cannot undo this action. Do you wish to proceed? (OK = Yes   Cancel = No)');
            }

            if (postConfirm == true) {
                try {
                    form.action = url;
                    form.submit();
                } catch(err) {
                    //Reset posting property back to false in the event of an error
                    this.posting = false;
                    radical.outputLog('Failed to submit form:' + err.message, true);
                }
            } else {
                //Reset posting property back to false in the event we are not proceeding
                this.posting = false;
            }
        } else {
          radical.outputLog(postStatusMessages.join('\n'), true);
        }

        //Reset posting property back to false at the end of the POST operation
        //this.posting = false;
        //Originally the design called for resetting the status setting back to
        //false so that interaction would be possible in the event of a failure,
        //but observation suggests that javascript does not wait for the
        //transition to finish, hence it resets too early (i.e. the subsequent
        //clicks work out of turn).  The final design turns the status setting
        //into a one-way-door to block multiple clicks.
    },

    ////////// SCRIPTED CHECKBOX SELECTOR //////////
    //Action Link
    //<a href="javascript:radical.SelectAll('choices[]');" onMouseOver="self.status='Select All'; return true;" onMouseOut="self.status=''; return true;"><img src="/imgs/bullet_checkmark.gif" border="0" alt="Select All"></a>
    selectAll : function (fieldname) {
        for (var i = 0; i < document.forms[0].elements.length; i++) {
            if (document.forms[0].elements[i].name == fieldname) {
                var e = document.forms[0].elements[i];
                e.checked =! e.checked;
            }
        }
    },

    //Action Link
    //<a href="javascript:radical.InvertCheckbox('choices[]');" onMouseOver="self.status='Select All'; return true;" onMouseOut="self.status=''; return true;"><img src="/imgs/bullet_checkmark.gif" border="0" alt="Select All"></a>
    invertCheckbox : function (fieldname) {
        for (var i = 0; i < document.forms[0].elements.length; i++) {
            if (document.forms[0].elements[i].name == fieldname) {
                var e = document.forms[0].elements[i];
                e.checked =! e.checked;
            }
        }
    },

    ////////// SCRIPTED RADIO BUTTON GET AND SET //////////
    //public method to return the value of the radio button that is checked
    //return an empty string if none are checked, or there are no radio buttons
    getRadioValue : function (radioObj) {
        if (!radioObj) {
            return '';
        }

        var radioLength = radioObj.length;

        if (radioLength == undefined) {
            if (radioObj.checked) {
                return radioObj.value;
            } else {
                return '';
            }
        }

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

        return '';
    },

    //public method to set the radio button with the given value as being checked
    //do nothing if there are no radio buttons
    //if the given value does not exist, all the radio buttons are reset to unchecked
    setRadioValue : function (radioObj, newValue) {
        if (!radioObj) {
            return;
        }

        var radioLength = radioObj.length;

        if (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;
            }
        }

        return;
    },

    ////////// SCRIPTED NUMERIC STEP BINDING //////////
    //public method to return the value step matched
    stepBound : function (step, current) {
      step = parseFloat(step);
      current = parseFloat(current);

      if (!isNaN(current)) {
        if (!isNaN(step)) {
          //Step value should always be non-negative
          step = Math.abs(step);

          //Figure out how many decimal places we need to round to later
          //based on the number of decimal places in the step value
          if ((step - parseInt(step)) == 0) {
            var decimalpart = (step - parseInt(step)).toString();
            var decimalposition = decimalpart.indexOf('.');
            var decimalplaces = 0;
          } else {
            var decimalpart = (step - parseInt(step)).toString();
            var decimalposition = decimalpart.indexOf('.');
            var decimalplaces = (decimalpart.substring((decimalposition + 1))).length;
          }

          if (step == 0) {
            //Division-by-zero handler
            current = 0;
          } else if (((current / step) % 1) != 0) {
            //Adjust current value by the amount of excess as calculated by the modulus.
            //Note that we scale the current value to fit the integer oriented operation
            //of modulus, and then scale the final value back after we determine the
            //"integerized" value.
            //Finally, we apply a sane decimal places value (above) to mitigate the precision
            //problems in JavaScript.  It's an imperfect hack so this function *might* be
            //somewhat unreliable until we get a complete solution.
            current = (((current / step) - ((current / step) % 1)) * step).toFixed(decimalplaces);
          }
        }

        return current;
      } else {
        return 0;
      }
    },

    ////////// SCRIPTED NUMERIC RANGE BINDING //////////
    //public method to return the value within the upper and lower bounds
    rangeBound : function (lower, upper, current) {
      lower = parseFloat(lower);
      upper = parseFloat(upper);
      current = parseFloat(current);

      if (!isNaN(current)) {
        if (!isNaN(lower)) {
          if (current < lower) {
            current = lower;
          }
        }

        if (!isNaN(upper)) {
          if (upper < current) {
            current = upper;
          }
        }

        return current;
      } else {
        return 0;
      }
    },

    ////////// SCRIPTED EVENT HANDLER //////////
    //public method to get event object (wrapper)
    getEvent : function (e) {
        if (!e) {
            e = window.event;
        }
        if (e.target) {
            return e.target;
        }
        return e.srcElement;
    },

    //public method to add event hook to element (wrapper)
    hookEvent : function (element, eventName, callBack) {
        if (typeof(element) == 'string') {
            element = document.getElementById(element);
        }
        if (element == null) {
            return;
        }
        //add event to events handler/tracker
        this.evth.push({
            'element'   : element,
            'eventName' : eventName,
            'callBack'  : callBack
        });
        try {
            if (element.addEventListener) {
                element.addEventListener(eventName, callBack, false);
            } else {
                if (element.attachEvent) {
                    element.attachEvent('on' + eventName, callBack);
                }
            }
        } catch(err) {
            radical.outputLog('Unable to hookEvent:' + err.message, false);
        }
    },

    //public method to remove event hook from element (wrapper)
    unhookEvent : function (element, eventName, callBack) {
        if (typeof(element) == 'string') {
            element = document.getElementById(element);
        }
        if (element == null) {
            return;
        }
        try {
            if (element.removeEventListener) {
                element.removeEventListener(eventName, callBack, false);
            } else {
                if (element.detachEvent) {
                    element.detachEvent('on' + eventName, callBack);
                }
            }
        } catch(err) {
            radical.outputLog('Unable to unhookEvent:' + err.message, false);
        }
    },

    ////////// SCRIPTED NODE REMOVAL //////////
    deleteChildren : function (node) {
        if (radical.ismsie && node) {
            try {
                var x = node.childNodes.length - 1;
                for (x; x >= 0; x--) {
                    var childNode = node.childNodes[x];
                    if (childNode.hasChildNodes()) {
                        this.deleteChildren(childNode);
                    }
                    node.removeChild(childNode);
                    if (childNode.outerHTML) {
                        childNode.outerHTML = '';
                    }
                    childNode = null;
                }
                node = null;
            } catch(err) {
                radical.outputLog('Unable to deleteChildren:' + err.message, false);
            }
        }
    },

    ////////// SCRIPTED mAJAX INIT //////////
    mAJAXInit : function (handle) {
        if(typeof(this.objh[handle]) == 'undefined') {
            this.objh[handle] = new mAJAX();
        }
    },

    ////////// SCRIPTED mList INIT //////////
    mListInit : function (handle, eid) {
        if(typeof(this.objh[handle]) == 'undefined') {
            this.objh[handle] = new mList(eid);
        } else {
            this.objh[handle].cleanUp();
            this.objh[handle] = null;
            this.objh[handle] = new mList(eid);
        }
    }
}

////////// CONTRACTIBLE HEADERS //////////

//Action Link
//<a onClick="cHeaders.toggle('foo');">[My Link]</a>
//<div id="foo" class="cHeadersOff">
//</div>

var cHeaders = {
    //private properties
    _cookieName : 'cHeaders_' + radical.jsid,

    _manifest : [],

    //public properties
    enablePersist : true,

    collapsePrevious : false,

    //private methods
    _loadState : function () {
        var manifestRaw = biscuit.read(this._cookieName);
        if (manifestRaw != null) {
            var manifestBuffer = manifestRaw.split('|');
            for (var key in manifestBuffer) {
                var manifestTemp = manifestBuffer[key].split('?');
                if (manifestTemp.length == 2) {
                    //restore _manifest
                    this._manifest[manifestTemp[0]] = manifestTemp[1];
                    //restore dom elements
                    var el = document.getElementById(manifestTemp[0]);
                    if (el != null) {
                        el.className = manifestTemp[1];
                    }
                }
            }
        }
    },

    _saveState : function () {
        var manifestBuffer = [];
        for (var eid in this._manifest) {
            if (this.collapsePrevious) {
                manifestBuffer.push(eid + '?' + 'cHeadersOff');
            } else {
                manifestBuffer.push(eid + '?' + this._manifest[eid]);
            }
            //flush _manifest
            this._manifest[eid] = null;
        }
        biscuit.make(this._cookieName,manifestBuffer.join('|'));
        //flush _manifest
        this._manifest = null;
    },

    //public methods
    toggle : function (eid) {
        var el = document.getElementById(eid);
        if (el != null) {
            el.className = (el.className == 'cHeadersOn') ? 'cHeadersOff' : 'cHeadersOn';
            if (this.enablePersist) {
                this._manifest[eid] = el.className;
            }
        }
    },

    doOnLoad : function () {
        if (this.enablePersist) {
            this._loadState();
        }
    },

    doOnUnLoad : function () {
        if (this.enablePersist) {
            this._saveState();
        }
    }
}

////////// STATUS TRAY //////////

//Action Link
//<a onClick="statusTray.update('This is a test', 3000);">[My Link]</a>

var statusTray = {
    //private properties
    trayId : 'statusTray',

    timeoutHandler : null,

    create : function () {
        try {
            if (document.getElementById(statusTray.trayId) == null) {
                var _stat = document.createElement('div');
                var _body = document.getElementsByTagName('body')[0];
                if (_stat != null && _body != null) {
                    _stat.id = statusTray.trayId;
                    _stat.style.background = '#FFFFB3';
                    _stat.style.visibility = 'hidden';
                    _body.insertBefore(_stat, _body.firstChild);
                } else {
                    _stat = null;
                    _body = null;
                }
            }
        } catch (err) {
            if (window.console) {
                console.log(message);
            }
        }
    },

    update : function (message, timeout) {
        try {
            var _stat = document.getElementById(statusTray.trayId);
            if (_stat != null) {
                radical.deleteChildren(_stat);
                _stat.innerHTML = message;
                _stat.style.visibility = 'visible';
                statusTray.timeoutHandler = setTimeout(statusTray.remove, parseInt(timeout));
            }
        } catch (err) {
            if (window.console) {
                console.log(message);
            }
        }
    },

    remove : function () {
        try {
            var _stat = document.getElementById(statusTray.trayId);
            if (_stat != null) {
                radical.deleteChildren(_stat);
                _stat.innerHTML = null;
                _stat.style.visibility = 'hidden';
                clearTimeout(statusTray.timeoutHandler);
            }
        } catch (err) {
            if (window.console) {
                console.log(message);
            }
        }
    }
}

////////// mAJAX Class //////////

//Dependencies
//The class depends on the availability of the sarissa XML library for parsing.

//Invoke Class
//<script language="JavaScript" type="text/javascript">
//var abc = new mAJAX();
//</script>

//Action Link (Get XML)
//<a href="javascript:abc.requestAjaxXMLObject('/loadhtml.php?where=object&what=obj_xsl.php', '/loadhtml.php?where=object&what=obj_xml.php', 'myajaxobject');">[AJAX Link]</a>

//Action Link (Get)
//<a href="javascript:abc.requestAjaxObject('/loadhtml.php?where=object&what=object.php', 'myajaxobject');">[AJAX Link]</a>

//Action Link (Post XML)
//<a href="javascript:abc.postAjaxXMLObject('/loadhtml.php?where=object&what=obj_xsl.php', '/loadhtml.php?where=object&what=obj_xml.php', 'myajaxobject', 'name1=' + value1 + '&name2=' + value2);">[AJAX Link]</a>

//Action Link (Post)
//<a href="javascript:abc.postAjaxObject('/loadhtml.php?where=object&what=object.php&ajaxpage=1', 'myajaxobject', 'name1=' + value1 + '&name2=' + value2);">[AJAX Link]</a>

//Data Return Area
//<div id="myajaxobject"></div>

function mAJAX() {
    //CORE METHODS
    //Create Ajax Object to handle connection to server
    var createAjaxObject = function() {
        if (window.XMLHttpRequest) {
            try {
                var ajaxObject = new XMLHttpRequest();
            } catch (error) {
                var ajaxObject = null;
                alert('Could not create Ajax Object!');
            }
        } else {
            try {
                var ajaxObject = new ActiveXObject('Msxml2.XMLHTTP');
            } catch (firstmserror) {
                try {
                    var ajaxObject = new ActiveXObject('Microsoft.XMLHTTP');
                } catch (secondmserror) {
                    var ajaxObject = null;
                    alert('Could not create Microsoft Ajax Object!');
                }
            }
        }

        //leakless return
        try {
            return ajaxObject;
        } finally {
            ajaxObject = null;
        }
    }

    //INITIALIZE CLASS PROPERTIES
    //Setup GET handlers using createAjaxObject()
    var xsl = createAjaxObject();
    var xml = createAjaxObject();
    var htm = createAjaxObject();

    //Setup POST handlers using createAjaxObject()
    var xslPost = createAjaxObject();
    var xmlPost = createAjaxObject();
    var htmPost = createAjaxObject();

    //PUBLIC METHODS
    this.indentity = function () {
        return 'mAJAX';
    }

    this.cleanUp = function () {
        xsl.onreadystatechange = null;
        xsl = null;
        xml.onreadystatechange = null;
        xml = null;
        htm.onreadystatechange = null;
        htm = null;
        xslPost.onreadystatechange = null;
        xslPost = null;
        xmlPost.onreadystatechange = null;
        xmlPost = null;
        htmPost.onreadystatechange = null;
        htmPost = null;
    }

    //Request XML fragment
    this.requestAjaxXMLObject = function(xslurlsource, xmlurlsource, eid) {
        if (xsl != null) {
            xsl.open('GET', xslurlsource, true);
            xsl.onreadystatechange = function() { handleAjaxXSLObject(xmlurlsource, eid); };
            xsl.send(null);
        } else {
            alert('XSL Handler is null.');
        }
    }

    //Request HTM fragment
    this.requestAjaxObject = function(urlsource, eid) {
        if (htm != null) {
            htm.open('GET', urlsource, true);
            htm.onreadystatechange = function() { handleAjaxObject(eid); };
            htm.send(null);
        } else {
            alert('HTM Handler is null.');
        }
    }

    //Submit DATA (XML fragment return)
    this.postAjaxXMLObject = function(xslurlsource, xmlurlsource, eid, data) {
        if (xslPost != null) {
            data = encodeURI(data);
            xslPost.open('GET', xslurlsource, true);
            xslPost.onreadystatechange = function() { handleAjaxXSLPostObject(xmlurlsource, eid, data); };
            xslPost.send(null);
        } else {
            alert('XSL Post Handler is null.');
        }
    }

    //Submit DATA (HTM fragment return)
    this.postAjaxObject = function(urltarget, eid, data) {
        if (htmPost != null) {
            data = encodeURI(data);
            htmPost.open('POST', urltarget, true);
            htmPost.onreadystatechange = function() { handleAjaxPostObject(eid); };
            htmPost.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            htmPost.send(data);
        } else {
            alert('HTM Post Handler is null.');
        }
    }

    //PRIVATE METHODS
    //Handle XSL Object that is returned from GET and
    //GET related XML object on sucessful response
    var handleAjaxXSLObject = function(xmlurlsource, eid) {
        //var node = document.getElementById(eid);
        if (xsl.readyState == 4) {
            if (xsl.status == 200) {
                if (xml != null) {
                    xml.open('GET', xmlurlsource, true);
                    xml.onreadystatechange = function() { handleAjaxXMLObject(eid, xsl.responseText); };
                    xml.send(null);
                } else {
                    alert('XML Handler is null.');
                }
            } else {
                //radical.deleteChildren(node);
                //node.innerHTML = 'handleAjaxXSLObject did not return 200';
            }
        }
    }

    //Handle XML Object that is returned from GET and
    //apply XSL before sending processed transformation to targeted eid
    var handleAjaxXMLObject = function(eid, xsltstring) {
        var node = document.getElementById(eid);
        if (xml.readyState == 0) {
            //radical.deleteChildren(node);
            //node.innerHTML = 'UNINITIALIZED';
        }
        if (xml.readyState == 1) {
            //radical.deleteChildren(node);
            //node.innerHTML = 'LOADING';
        }
        if (xml.readyState == 2) {
            //radical.deleteChildren(node);
            //node.innerHTML = 'LOADED';
        }
        if (xml.readyState == 3) {
            //radical.deleteChildren(node);
            //node.innerHTML = 'INTERACTIVE';
        }
        if (xml.readyState == 4) {
            if (xml.status == 200) {
                var xsltDocument = (new DOMParser()).parseFromString(xsltstring, 'text/xml');

                var xsltObject = new XSLTProcessor();
                xsltObject.importStylesheet(xsltDocument);

                var htmlDocument = xsltObject.transformToDocument(xml.responseXML);
                radical.deleteChildren(node);
                node.innerHTML = (new XMLSerializer()).serializeToString(htmlDocument);

                //Process embedded JavaScript
                var scripts = xml.responseXML.getElementsByTagName('script');
                var js = '';
                for (var s = 0; s < scripts.length; s++) {
                    if (scripts[s].childNodes[0].nodeValue == null) continue;
                    js += scripts[s].childNodes[0].nodeValue;
                }
                eval(js);
            } else {
                //radical.deleteChildren(node);
                //node.innerHTML = 'handleAjaxXMLObject did not return 200';
            }
        }
    }

    //Handle HTM Object that is returned from GET and
    //send to targeted eid
    var handleAjaxObject = function(eid) {
        var node = document.getElementById(eid);
        if (htm.readyState == 0) {
            //radical.deleteChildren(node);
            //node.innerHTML = 'UNINITIALIZED';
        }
        if (htm.readyState == 1) {
            //radical.deleteChildren(node);
            //node.innerHTML = 'LOADING';
        }
        if (htm.readyState == 2) {
            //radical.deleteChildren(node);
            //node.innerHTML = 'LOADED';
        }
        if (htm.readyState == 3) {
            //radical.deleteChildren(node);
            //node.innerHTML = 'INTERACTIVE';
        }
        if (htm.readyState == 4) {
            if (htm.status == 200) {
                radical.deleteChildren(node);
                node.innerHTML = htm.responseText;

                //Process embedded JavaScript
                var repattern = '(?:<script.*?>)((\n|\r|\n\r|.)*?)(?:</script>)';
                var re = new RegExp(repattern, 'img');
                var scripts = htm.responseText.match(re);
                if (scripts) {
                    var js = '';
                    for (var s = 0; s < scripts.length; s++) {
                        var re = new RegExp(repattern, 'im');
                        js += scripts[s].match(re)[1];
                    }
                    eval(js);
                }
            } else {
                //radical.deleteChildren(node);
                //node.innerHTML = 'handleAjaxObject did not return 200';
            }
        }
    }

    //Handle XSL Object that is returned from GET and
    //POST related XML object on sucessful response
    var handleAjaxXSLPostObject = function(xmlurlsource, eid, data) {
        //var node = document.getElementById(eid);
        if (xslPost.readyState == 4) {
            if (xslPost.status == 200) {
                if (xmlPost != null) {
                    xmlPost.open('POST', xmlurlsource, true);
                    xmlPost.onreadystatechange = function() { handleAjaxXMLPostObject(eid, xslPost.responseText); };
                    xmlPost.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
                    xmlPost.send(data);
                } else {
                    alert('XML Post Handler is null.');
                }
            } else {
                //radical.deleteChildren(node);
                //node.innerHTML = 'handleAjaxXSLPostObject did not return 200';
            }
        }
    }

    //Handle XML Object that is returned from POST and
    //apply XSL before sending processed transformation to targeted eid
    var handleAjaxXMLPostObject = function(eid, xsltstring) {
        var node = document.getElementById(eid);
        if (xmlPost.readyState == 0) {
            //radical.deleteChildren(node);
            //node.innerHTML = 'UNINITIALIZED';
        }
        if (xmlPost.readyState == 1) {
            //radical.deleteChildren(node);
            //node.innerHTML = 'LOADING';
        }
        if (xmlPost.readyState == 2) {
            //radical.deleteChildren(node);
            //node.innerHTML = 'LOADED';
        }
        if (xmlPost.readyState == 3) {
            //radical.deleteChildren(node);
            //node.innerHTML = 'INTERACTIVE';
        }
        if (xmlPost.readyState == 4) {
            if (xmlPost.status == 200) {
                var xsltDocument = (new DOMParser()).parseFromString(xsltstring, 'text/xml');

                var xsltObject = new XSLTProcessor();
                xsltObject.importStylesheet(xsltDocument);

                var htmlDocument = xsltObject.transformToDocument(xmlPost.responseXML);
                radical.deleteChildren(node);
                node.innerHTML = (new XMLSerializer()).serializeToString(htmlDocument);

                //Process embedded JavaScript
                var scripts = xmlPost.responseXML.getElementsByTagName('script');
                var js = '';
                for (var s = 0; s < scripts.length; s++) {
                    if (scripts[s].childNodes[0].nodeValue == null) continue;
                    js += scripts[s].childNodes[0].nodeValue;
                }
                eval(js);
            } else {
                //radical.deleteChildren(node);
                //node.innerHTML = 'handleAjaxXMLPostObject did not return 200';
            }
        }
    }

    //Handle HTM Object that is returned from POST and
    //send to targeted eid
    var handleAjaxPostObject = function(eid) {
        var node = document.getElementById(eid);
        if (htmPost.readyState == 0) {
            //radical.deleteChildren(node);
            //node.innerHTML = 'UNINITIALIZED';
        }
        if (htmPost.readyState == 1) {
            //radical.deleteChildren(node);
            //node.innerHTML = 'LOADING';
        }
        if (htmPost.readyState == 2) {
            //radical.deleteChildren(node);
            //node.innerHTML = 'LOADED';
        }
        if (htmPost.readyState == 3) {
            //radical.deleteChildren(node);
            //node.innerHTML = 'INTERACTIVE';
        }
        if (htmPost.readyState == 4) {
            if (htmPost.status == 200) {
                radical.deleteChildren(node);
                node.innerHTML = htmPost.responseText;

                //Process embedded JavaScript
                var repattern = '(?:<script.*?>)((\n|\r|\n\r|.)*?)(?:</script>)';
                var re = new RegExp(repattern, 'img');
                var scripts = htmPost.responseText.match(re);
                if (scripts) {
                    var js = '';
                    for (var s = 0; s < scripts.length; s++) {
                        var re = new RegExp(repattern, 'im');
                        js += scripts[s].match(re)[1];
                    }
                    eval(js);
                }
            } else {
                //radical.deleteChildren(node);
                //node.innerHTML = 'handleAjaxPostObject did not return 200';
            }
        }
    }
}

////////// mList Class//////////

//Example Input Field:
//<input type="text" name="myname" value="myvalue" onkeyup="list.find(3, '/loadhtml.php?where=object&what=object.php&what_to_do=0&ajaxpage=1', 'mydiv', 'myname', 'myvalue', '');" autocomplete="off" />";
//<div id="mydiv" style="background: #FFFFFF; visibility: hidden; height: 8em; overflow: auto; position: absolute; border: 1px solid #000000;"></div>

function mList(elementid) {
    //INITIALIZE CLASS PROPERTIES
    //Element Handlers
    var eid = document.getElementById(elementid);
    //var did = document.getElementById('debug' + elementid);

    //State Controls
    var ctrlstate = false;
    var liststate = false;

    //AJAX Handler
    var ajax = new mAJAX();

    //Monitor "Threads"
    var toMon = null;
    var inMon = setInterval(function () {
                                //if (did != null){
                                //    radical.deleteChildren(did);
                                //    did.innerHTML = ctrlstate + '|' + liststate + '|' + (new Date().getTime());
                                //}
                                if (ctrlstate || liststate) {
                                    show();
                                } else {
                                    if (toMon == null) {
                                        toMon = setTimeout(hide, 150);
                                    }
                                }
                            },
                            350);

    //PUBLIC METHODS
    this.indentity = function () {
        return 'mList';
    }

    this.cleanUp = function () {
        eid = null;
        //did = null;
        ctrlstate = null;
        liststate = null;
        ajax = null;
        clearTimeout(toMon);
        toMon = null;
        clearInterval(inMon);
        inMon = null;
    }

    this.setCtrl = function (state) {
        ctrlstate = state;
    }

    this.setList = function (state) {
        liststate = state;
    }

    this.find = function (minlength, href, name, value, args) {
        if (value.length >= minlength) {
            ajax.postAjaxObject(href, elementid, 'lookup_name=' + name + '&' + 'lookup_value=' + value + '&' + 'lookup_args=' + args);
        }
    }

    //PRIVATE METHODS
    var show = function () {
        if (eid != null){
            if (eid.style.visibility != 'visible') {
                eid.style.visibility = 'visible';
            }
        }
    }

    var hide = function () {
        if (eid != null){
            if (eid.style.visibility != 'hidden') {
                eid.style.visibility = 'hidden';
                radical.deleteChildren(eid);
                eid.innerHTML = '';
            }
        }
        if (toMon != null) {
            clearTimeout(toMon);
            toMon = null;
        }
    }
}

//OnLoad Event Stream Handler
function mLoadEventStream() {
    //run startup routines
    radical.pagerdy = true;
    cHeaders.doOnLoad();
    statusTray.create();
}

//OnUnLoad Event Stream Handler
function mUnLoadEventStream() {
    //run shutdown routines
    radical.pagerdy = null;
    cHeaders.doOnUnLoad();
    statusTray.remove();

    //de-link radical events
    for (var key in radical.evth) {
        //detatch event
        radical.unhookEvent(radical.evth[key].element, radical.evth[key].eventName, radical.evth[key].callBack);
        //set evth to null
        radical.evth[key].element = null;
        radical.evth[key].eventName = null;
        radical.evth[key].callBack = null;
        radical.evth[key] = null;
        //splice from array
        if (radical.evth[key] == null) {
            radical.evth.splice(key, 1);
        }
    }
    //set evth to null
    radical.evth = null;

    //de-link radical objects
    for (var key in radical.objh) {
        //run cleanUp if available
        if (radical.objh[key].cleanUp) {
          radical.objh[key].cleanUp();
        }
        //set objh to null
        radical.objh[key] = null;
        //splice from array
        if (radical.objh[key] == null) {
            radical.objh.splice(key, 1);
        }
    }
    //set objh to null
    radical.objh = null;

    //remove onload and onunload event listeners
    if (window.removeEventListener) {
        window.removeEventListener('load', mLoadEventStream, false);
        window.removeEventListener('unload', mUnLoadEventStream, false);
    } else {
        if (window.detachEvent) {
            window.detachEvent('onload', mLoadEventStream);
            window.detachEvent('onunload', mUnLoadEventStream);
        }
    }

    //set onload and onunload event listeners to null
    if (window.onload != null) {
        window.onload = null;
    }
    if (window.onunload != null) {
        window.onunload = null;
    }

    //remove primitive objects
    cHeaders = null;
    radical = null;
    biscuit = null;
    base64 = null;
    hex = null;
}

//attach onload and onunload event listeners
if (window.addEventListener) {
    window.addEventListener('load', mLoadEventStream, false);
    window.addEventListener('unload', mUnLoadEventStream, false);
} else {
    if (window.attachEvent) {
        window.attachEvent('onload', mLoadEventStream);
        window.attachEvent('onunload', mUnLoadEventStream);
    }
}
