﻿var _RegistredToolbars =
{
    arrToolbars: new Array(),
    hashToolbars: new Object(),

    addToolbar: function(objProxy) {
        if (this.hashToolbars[objProxy.ctid]) return;

        this.arrToolbars.push(objProxy);
        this.hashToolbars[objProxy.ctid] = objProxy;
    },

    removeToolbar: function(ctid) {
        if (!this.hashToolbars[ctid]) return;

        this.hashToolbars[ctid] = null;
        for (var i = 0; i < this.arrToolbars.length; i++) {
            if (typeof (this.arrToolbars[i].ctid) == "undefined" || this.arrToolbars[i].ctid == ctid) {
                this.arrToolbars.splice(i, 1);
                break;
            }
        }
    },

    isRegistred: function(ctid) {
        return (this.hashToolbars[ctid]) ? true : false;
    },

    getToolbar: function(ctid) {
        return (this.isRegistred(ctid)) ? this.hashToolbars[ctid] : null;
    },

    getAllToolbars: function() {
        return this.arrToolbars;
    }
};

function _TPIRegisterToolbarIE(activeXName) {
    try {
        var objProxy = new ActiveXObject(activeXName);
        _RegistredToolbars.addToolbar(objProxy);
    }
    catch (ex) { }
};

function _TPIRegisterToolbarFF(objProxy) {
    _RegistredToolbars.addToolbar(objProxy);
};

function _TPIUnregisterToolbar(ctid) {
    _RegistredToolbars.removeToolbar(ctid);
};

//Toolbar API
//FF - this class is overridden by toolbar.
//IE - this class catches all the calls IE didn't catch.

var BROWSERS_DEF =
{
    IE6: "IE6",
    IE7: "IE7",
    FF: "FF",
    SAFARI: "SAFARI"
};

var __TPI =
{
    Result: "",

    ResetResult: function() {
        this.Result = "";
    },

    SetResult: function(strResult) {
        this.Result += strResult;
    },

    GetResult: function(bIsMultipleResponse) {
        return _TPIHelper.GetResult(this.Result, bIsMultipleResponse);
    },

    ExecuteApiFunction: function(strFuncName, strParam) {
        //do nothing
    }
};

//for Safari MAC
var _TPI;
var isSafari;

if (typeof TPIObject != "undefined") {
    _TPI = TPIObject;
    isSafari = true;
}
else {
    _TPI = __TPI;
    isSafari = false;
}

//Wrapped JS function for IE to return Result
function _TPISetResult(strXmlResult) {
    _TPI.SetResult(strXmlResult);
};

//Helper object with service functions for interaction with the toolbar.
var _TPIHelper =
{
    IsSupportedFunction: "IsSupportedFunction",

    GetBrowser: function() {
        if (isSafari) return BROWSERS_DEF.SAFARI;

        var strAgent = navigator.userAgent;
        //IE
        if (strAgent.indexOf("MSIE") != -1) {
            //Try with regex to overcome the bug with multiple data in user agent
            var result = strAgent.match(/MSIE (\d*)/);
            if (result && result.length >= 2) {
                if (parseInt(result[1]) == 6)
                    return BROWSERS_DEF.IE6;
                else
                    return BROWSERS_DEF.IE7;
            }
            else {
                if (strAgent.indexOf("MSIE 6.") != -1)
                    return BROWSERS_DEF.IE6;
                //IE7
                else
                    return BROWSERS_DEF.IE7;
            }
        }
        else
        //FF - because safari is detected earlier.
            return BROWSERS_DEF.FF;
    },

    _normalizeArgs: function(args) {
        for (var i = 0; i < args.length; i++) {
            if (args[i] && args[i].replace) {
                args[i] = args[i].replace(/\\/g, "\\\\");
                args[i] = args[i].replace(/'/g, "\\'");
            }
        }
    },

    _ExecuteOldWay: function(args, ctid) {
        if (ctid) args.push(ctid);
        var strBrowser = this.GetBrowser();

        switch (strBrowser) {
            case BROWSERS_DEF.IE7:
                var frameSrc = "about:blank#javascript:" + "_TPI.ExecuteApiFunction('" + args.join("','") + "')";
                this.NavigateInIFrame(frameSrc);
                break;
            case BROWSERS_DEF.IE6:
                document.location.href = "javascript:" + "_TPI.ExecuteApiFunction('" + args.join("','") + "')";
                break;
            case BROWSERS_DEF.SAFARI:
                this._normalizeArgs(args);
                _TPI.ExecuteApiFunction(args.join("','"));
                break;
            case BROWSERS_DEF.FF:
                this._normalizeArgs(args);
                eval("_TPI.ExecuteApiFunction('" + args.join("','") + "')");
                break;
        }
    },

    //This function interacts directly with toolbar, by calling JS functions,
    //which IE interprets as navigation, and FF gets the function itself.
    //args :        [0] -       function name
    //              [2]..[n]    function arguments
    ExecuteFunctionSingle: function(args, ctid) {
        _TPI.ResetResult();

        //check if toolbar is registered via new register toolbar mechanism (both IE & FF)
        var objProxy = _RegistredToolbars.getToolbar(ctid);
        if (objProxy) {
            var result = objProxy.ExecuteApiFunction(args);
            _TPI.SetResult(result);
        }
        else
            this._ExecuteOldWay(args, ctid);

        return _TPI.GetResult(false);
    },

    ExecuteFunctionMultiple: function(args) {
        _TPI.ResetResult();

        this._ExecuteOldWay(args);

        var arrAllToolbars = _RegistredToolbars.getAllToolbars();
        if (arrAllToolbars.length != 0) {
            var objProxy = null;
            var result = "";
            for (var i = 0; i < arrAllToolbars.length; i++) {
                objProxy = arrAllToolbars[i];
                result = objProxy.ExecuteApiFunction(args);
                _TPI.SetResult(result);
            }
        }

        return _TPI.GetResult(true);
    },

    ExecuteFunctionAny: function(args) {
        _TPI.ResetResult();

        var arrAllToolbars = _RegistredToolbars.getAllToolbars();
        var isDone = false;
        if (arrAllToolbars.length != 0) {
            var objProxy = null;
            var strResult = "";
            var oResult = null;
            for (var i = 0; i < arrAllToolbars.length; i++) {
                objProxy = arrAllToolbars[i];
                strResult = objProxy.ExecuteApiFunction([this.IsSupportedFunction, args[0]]);
                oResult = this.GetResult(strResult, false);
                if (oResult.returnValue) {
                    strResult = objProxy.ExecuteApiFunction(args);
                    _TPI.SetResult(strResult);
                    isDone = true;
                    break;
                }
            }
        }

        //if no toolbars registered or non of the registred toolbars supports this function, 
        //execute it the old way, for older toolbars
        if (!isDone)
            this._ExecuteOldWay(args);

        return _TPI.GetResult(true);
    },

    NavigateInIFrame: function(frameSrc) {
        var arrBody = document.getElementsByTagName('body');
        iFrame = document.createElement('iframe');
        iFrame.setAttribute("width", "0");
        iFrame.setAttribute("height", "0");
        arrBody[0].appendChild(iFrame);
        iFrame.src = frameSrc;
        arrBody[0].removeChild(iFrame);
    },

    //Parse Result XML
    //If bIsMultipleResponse = true, return array of objects, each contains response from specific toolbar.
    GetResult: function(strResult, bIsMultipleResponse) {
        var oResult = new Object();
        var arrResult = null;

        if (strResult != "") {
            strResult = "<ROOT>" + strResult + "</ROOT>";
            var oRootXML = null;
            var bIsMulty = false;

            if (window.ActiveXObject) {
                oRootXML = new ActiveXObject("Microsoft.XMLDOM");
                oRootXML.async = "false";
                oRootXML.loadXML(strResult);
                oRootXML = oRootXML.documentElement;
            }
            else {
                var parser = new DOMParser();
                oRootXML = parser.parseFromString(strResult, "text/xml");
                oRootXML = oRootXML.documentElement;
            }

            if (oRootXML.childNodes.length > 1 || bIsMultipleResponse) {
                bIsMulty = true;
                arrResult = new Array();
            }

            for (var z = 0; z < oRootXML.childNodes.length; z++) {
                oXML = oRootXML.childNodes[z];
                if (typeof (oXML.tagName) != "undefined") {
                    //Convert Result XML to an Object
                    for (var i = 0; i < oXML.childNodes.length; i++) {
                        switch (oXML.childNodes[i].tagName) {
                            case "RETURN_VALUE": oResult.returnValue = this.GetBooleanValue(this.GetXMLNodeValue(oXML.childNodes[i])); break;
                            case "ERROR_CODE": oResult.errorCode = this.GetXMLNodeValue(oXML.childNodes[i]); break;
                            case "DATA": var oXmlNode = oXML.childNodes[i];
                                this.DataFactory(oResult, oXmlNode);
                                break;
                            default: break;
                        }
                    }

                    if (bIsMulty) {
                        arrResult.push(oResult);
                        oResult = new Object();
                    }
                }
            }
            if (bIsMulty) {
                oResult = arrResult;
            }
        }
        else {
            oResult.returnValue = false;
            oResult.errorCode = 0;
        }
        return oResult;
    },

    //Function which transfers string TRUE or FALSE into boolean.
    GetBooleanValue: function(strBool) {
        var bBool = (strBool.toLowerCase() == "true") ? true : false;
        return bBool;
    },

    //Factory for parsing data node from toolbar's response.
    DataFactory: function(oData, oXmlNode) {
        var xmlNode = null;

        for (var j = 0; j < oXmlNode.childNodes.length; j++) {
            xmlNode = oXmlNode.childNodes[j];
            switch (xmlNode.tagName) {
                //Toolbar info                                                                        
                case "TOOLBAR_INFO":
                    for (var i = 0; i < xmlNode.childNodes.length; i++) {
                        switch (xmlNode.childNodes[i].tagName) {
                            case "VERSION": oData.version = this.GetXMLNodeValue(xmlNode.childNodes[i]); break;
                            case "NAME": oData.name = this.GetXMLNodeValue(xmlNode.childNodes[i]); break;
                            case "CURRENT_CTID": oData.CTID = this.GetXMLNodeValue(xmlNode.childNodes[i]); break;
                            case "ORIGINAL_CTID": oData.originalCTID = this.GetXMLNodeValue(xmlNode.childNodes[i]); break;
                            case "IS_MULTICOMMUNITY": oData.isMulticommunity = this.GetBooleanValue(this.GetXMLNodeValue(xmlNode.childNodes[i])); break;
                            case "IS_GROUPING": oData.isGrouping = this.GetBooleanValue(this.GetXMLNodeValue(xmlNode.childNodes[i])); break;
                            //new myStuff feature, status                                                       
                            // 0: disabled,                                                       
                            // 1: enabled                                                       
                            //-1: unsupported                                                       
                            case "MY_STUFF_STATUS": oData.myStuffStatus = parseInt(this.GetXMLNodeValue(xmlNode.childNodes[i])); break;
                            default: break;
                        }
                    }

                    if (typeof oData.myStuffStatus == "undefined")
                        oData.myStuffStatus = -1;

                    break;
                //Single value                                                                        
                case "SINGLE_VALUE":
                    oData.data = this.GetXMLNodeValue(xmlNode);
                    break;
                //Menus info                                                                        
                case "MENUS_INFO":
                    oData.menusInfo = new Array();
                    for (var i = 0; i < xmlNode.childNodes.length; i++) {
                        var xmlNodeMenu = xmlNode.childNodes[i];
                        if (xmlNodeMenu.tagName == "MENU_INFO") {
                            var oDataMenu = new Object();
                            for (var k = 0; k < xmlNodeMenu.childNodes.length; k++) {
                                switch (xmlNodeMenu.childNodes[k].tagName) {
                                    case "COMP_ID": oDataMenu.componentID = this.GetXMLNodeValue(xmlNodeMenu.childNodes[k]); break;
                                    case "CAPTION": oDataMenu.caption = this.GetXMLNodeValue(xmlNodeMenu.childNodes[k]); break;
                                    case "ICON_URL": oDataMenu.iconUrl = this.GetXMLNodeValue(xmlNodeMenu.childNodes[k]); break;
                                    default: break;
                                }
                            }
                            oData.menusInfo.push(oDataMenu);
                        }
                    }
            }
        }
    },

    GetOldApiResult: function(bResult) {
        var oResult = new Object();
        oResult.returnValue = bResult;
        oResult.errorCode = 0;
        return oResult;
    },

    //Cross browser.
    GetXMLNodeValue: function(xmlNode) {
        if (xmlNode.text) {
            return xmlNode.text;
        }
        else if (xmlNode.childNodes.length != 0) {

            return xmlNode.childNodes[0].nodeValue;
        }
        else {
            return "";
        }
    },

    ToLegalXML: function(strXML) {
        strXML = strXML.replace(/&/g, '&amp;');
        strXML = strXML.replace(/</g, '&lt;');
        strXML = strXML.replace(/>/g, '&gt;');
        strXML = strXML.replace(/\'/g, '&apos;');
        strXML = strXML.replace(/\"/g, '&quot;');

        return strXML;
    },

    //Returns correct XML defining toolbar button
    BuildButtonXml: function(buttonLabel, buttonIcon, buttonTooltip, xmlData,
					   optionsDisplayText, optionsDisplayIcon, uniqueComponentID) {
        var strXML = '<BUTTON>';
        strXML += '<PERMISSIONS><EDIT>True</EDIT><MOVE>True</MOVE><DELETE>True</DELETE></PERMISSIONS>';
        strXML += '<USER_ATTRIBUTES><PERMISSION>FULL</PERMISSION><SHOW_IN_CHEVRON>True</SHOW_IN_CHEVRON></USER_ATTRIBUTES>';
        strXML += '<UNIQUE_COMP_ID>' + this.ToLegalXML(uniqueComponentID) + '</UNIQUE_COMP_ID>';
        strXML += '<DEFAULT_BUTTON_TEXT>' + this.ToLegalXML(buttonLabel) + '</DEFAULT_BUTTON_TEXT>';
        strXML += '<BUTTON_ICON_URL>' + this.ToLegalXML(buttonIcon) + '</BUTTON_ICON_URL>';
        strXML += '<BUTTON_TOOLTIP>' + this.ToLegalXML(buttonTooltip) + '</BUTTON_TOOLTIP>';
        //options display text/icon
        strXML += '<DISPLAY_TEXT>' + this.ToLegalXML(optionsDisplayText) + '</DISPLAY_TEXT>';
        strXML += '<DISPLAY_ICON>' + this.ToLegalXML(optionsDisplayIcon) + '</DISPLAY_ICON>';
        //data node
        strXML += xmlData;
        strXML += '</BUTTON>';

        return strXML;
    },

    //Returns correct XML defining toolbar RSS component
    BuildRssXml: function(buttonLabel, buttonIcon, rssFeedUrl, buttonNewMessagesIcon, optionsDisplayText,
									optionsDisplayIcon, refreshIntervalInMinutes, uniqueComponentID) {

        var strXML = '<RSS_FEED_ITEM>';
        strXML += '<PERMISSIONS><EDIT>True</EDIT><MOVE>True</MOVE><DELETE>True</DELETE></PERMISSIONS>';
        strXML += '<USER_ATTRIBUTES><PERMISSION>FULL</PERMISSION><SHOW_IN_CHEVRON>True</SHOW_IN_CHEVRON></USER_ATTRIBUTES>';
        strXML += '<UNIQUE_COMP_ID>' + this.ToLegalXML(uniqueComponentID) + '</UNIQUE_COMP_ID>';
        strXML += '<DISPLAY_TEXT>' + this.ToLegalXML(optionsDisplayText) + '</DISPLAY_TEXT>';
        strXML += '<DISPLAY_ICON>' + this.ToLegalXML(optionsDisplayIcon) + '</DISPLAY_ICON>';
        strXML += '<TITLE>' + this.ToLegalXML(buttonLabel) + '</TITLE>';
        strXML += '<LINK>' + this.ToLegalXML(rssFeedUrl) + '</LINK>';
        strXML += '<ICON_NORMAL_URL>' + this.ToLegalXML(buttonIcon) + '</ICON_NORMAL_URL>';
        strXML += '<ICON_STARED_URL>' + this.ToLegalXML(buttonNewMessagesIcon) + '</ICON_STARED_URL>';
        strXML += '<LIVE_MODE>False</LIVE_MODE>';
        strXML += '<INTERVAL>' + this.ToLegalXML(refreshIntervalInMinutes) + '</INTERVAL>';
        strXML += '<COMMENTS_LINK/>';
        strXML += '<RSS_VIEW>ADJUSTIVE</RSS_VIEW>';
        strXML += '</RSS_FEED_ITEM>';

        return strXML;
    },

    //Returns correct XML defining toolbar RadioSation
    BuildRadioStationXml: function(name, url, contantType, mediaType, uniqueComponentID) {
        var strXML = '<RADIO_STATION>';
        strXML += '<STATION_ID>' + this.ToLegalXML(uniqueComponentID) + '</STATION_ID>';
        strXML += '<STATION_NAME>' + this.ToLegalXML(name) + '</STATION_NAME>';
        strXML += '<STATION_URL>' + this.ToLegalXML(url) + '</STATION_URL>';
        strXML += '<TYPE>' + mediaType + '</TYPE>';
        strXML += '<CONTENT_TYPE>' + contantType + '</CONTENT_TYPE>';
        strXML += '</RADIO_STATION>';

        return strXML;
    },

    BuildMenuItemLinkType: function(menuItemCaption, menuItemIconUrl, linkUrl, linkTarget) {
        var strXML = '<MENU_ITEM>';
        strXML += '<CAPTION>' + this.ToLegalXML(menuItemCaption) + '</CAPTION>';
        strXML += '<ICON_URL>' + this.ToLegalXML(menuItemIconUrl) + '</ICON_URL>';
        strXML += '<DATA><TYPE>LINK</TYPE><LINK>';
        strXML += '<URL>' + this.ToLegalXML(linkUrl) + '</URL>';
        strXML += '<TARGET>' + this.ToLegalXML(linkTarget) + '</TARGET>';
        strXML += '</LINK></DATA></MENU_ITEM>';

        return strXML;
    },

    //Menu items functions ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    BuildMenuItemXML: function(menuItemCaption, menuItemIconUrl, xmlData) {
        var strXML = '<MENU_ITEM>';
        strXML += '<CAPTION>' + this.ToLegalXML(menuItemCaption) + '</CAPTION>';
        strXML += '<ICON_URL>' + this.ToLegalXML(menuItemIconUrl) + '</ICON_URL>';
        strXML += xmlData;
        strXML += '<HASH_CODE>' + MD5Hash.MD5(xmlData + menuItemCaption + menuItemIconUrl) + '</HASH_CODE>';
        strXML += '</MENU_ITEM>';
        return strXML;
    },

    BuildGadgetDataXML: function(linkUrl, width, height, defaultTarget) {
        var xmlData = '<DATA><TYPE>POP_HTML</TYPE><POP_HTML>';
        xmlData += '<URL>' + this.ToLegalXML(linkUrl) + '</URL>';
        xmlData += '<DEFAULT_TARGET>' + this.ToLegalXML(defaultTarget) + '</DEFAULT_TARGET>';
        xmlData += '<WIDTH>' + width + '</WIDTH>';
        xmlData += '<HEIGHT>' + height + '</HEIGHT>';
        xmlData += '</POP_HTML></DATA>';
        return xmlData;
    },

    BuildLinkDataXML: function(linkUrl, linkTarget) {
        var xmlData = '<DATA><TYPE>LINK</TYPE><LINK>';
        xmlData += '<URL>' + this.ToLegalXML(linkUrl) + '</URL>';
        xmlData += '<TARGET>' + this.ToLegalXML(linkTarget) + '</TARGET>';
        xmlData += '</LINK></DATA>';
        return xmlData;
    },

    BuildCommandDataXML: function(commandType) {
        var xmlData = '<DATA><TYPE>COMMAND</TYPE><COMMAND>';
        xmlData += '<TYPE>' + this.ToLegalXML(commandType) + '</TYPE>';
        xmlData += '</COMMAND></DATA>';
        return xmlData;
    },

    BuildApplicationDataXML: function(exeAlias, params, appNotFoundUrl) {
        var xmlData = '<DATA><TYPE>APPLICATION</TYPE><APPLICATION>';
        xmlData += '<EXE_ALIAS>' + this.ToLegalXML(exeAlias) + '</EXE_ALIAS>';
        xmlData += '<PARAMS>' + this.ToLegalXML(params) + '</PARAMS>';
        xmlData += '<APP_NOT_FOUND_URL>' + this.ToLegalXML(appNotFoundUrl) + '</APP_NOT_FOUND_URL>';
        xmlData += '</APPLICATION></DATA>';
        return xmlData;
    },
    //Menu items functions end ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    GetToolbarFunctionString: function(strFunctionName) {
        strFunctionName = strFunctionName.replace(/\(/, '');
        strFunctionName = strFunctionName.replace(/\)/, '');

        switch (strFunctionName) {
            case "Refresh": return "RefreshToolbarByCTID"; break;
            case "IsVisible": return "IsToolbarVisible"; break;
            case "GetInfo ": return "GetToolbarInfo"; break;
            case "AddGadget": return "AddComponentByXML"; break;
            case "AddRSS": return "AddComponentByXML"; break;
            case "AddLinkButton": return "AddComponentByXML"; break;
            default: return strFunctionName; break;
        }
    },

    parseBool: function(strValue) {
        if (!strValue) return false;

        return (strValue.toLowerCase() == "true");
    }
};

var _ManagerHelper =
{
    CreateReturnObject: function(iErrorCode, bReturnValue, data) {
        var objReturn = new Object();
        objReturn.errorCode = iErrorCode;
        objReturn.returnValue = bReturnValue;
        objReturn.data = data;

        return objReturn;
    },

    GetManagerReturnObject: function(objManagerReturn, IsData) {
        //not an array - returned error for all responses (probably not toolbars)
        if (!objManagerReturn.length)
            return objManagerReturn;

        var objReturn = new Object();
        var iCountUnsupported = 0;

        for (var i = 0; i < objManagerReturn.length; i++) {
            if (objManagerReturn[i].returnValue) {
                if (!IsData || objManagerReturn[i].data)
                    return objManagerReturn[i];
            }

            else if (objManagerReturn[i].errorCode == 2)
                iCountUnsupported++;

            //if at least one toolbar return untrusted domain, exit with untrusted domain error code
            else if (objManagerReturn[i].errorCode == 10)
                return this.CreateReturnObject(10, false, null);

            //if at least one toolbar returned mystuff not visible, exit
            else if (objManagerReturn[i].errorCode == 11)
                return this.CreateReturnObject(11, false, null);
        }

        //all toolbars are not support this command
        if (iCountUnsupported == objManagerReturn.length)
            return this.CreateReturnObject(2, false, null);

        //at least one toolbar responded, but the value is empty
        if (IsData)
            return this.CreateReturnObject(1, true, null);
        //unknown error - just to make sure that always returns some object
        else
            return this.CreateReturnObject(0, false, null);
    }
}

var ForceRefreshFlags =
{
    ALL: 0,
    SETTINGS: 1,
    CHAT: 2
    //RADIO: 4,
    //GROUPING: 8,
    //COMMUNITIES: 16,
    //MY_STUFF: 32
}

//User object to interact with toolbar / toolbars
var TPI =
{
    Toolbar: function(strCTID) {

        this.CTID = strCTID;
        if (this.CTID) {
            this.IsLatestApi = _TPIHelper.ExecuteFunctionSingle(["IsLatestApi"], strCTID).returnValue;
            this.IsToolbarInstalled = -1;
        }

        this.Refresh = function() {
            if (!this.CTID) return this.NoCTID();

            if (this.IsLatestApi)
                return _TPIHelper.ExecuteFunctionSingle(["RefreshToolbarByCTID"], this.CTID);
            else {
                var bResult = _RefreshToolbarByCTID(this.CTID);
                return _TPIHelper.GetOldApiResult(bResult);
            }
        };

        this.ForceRefresh = function() {
            if (!this.CTID) return this.NoCTID();

            if (this.IsLatestApi)
                return _TPIHelper.ExecuteFunctionSingle(["ForceRefreshToolbar"], this.CTID);
            else {
                var bResult = _RefreshToolbarByCTID(this.CTID);
                return _TPIHelper.GetOldApiResult(bResult);
            }
        };

        this.ForceRefreshServices = function(flags) {
            if (!this.CTID) return this.NoCTID();
            if (typeof (flags) == "undefined") return this.ParametersError();

            if (this.IsLatestApi) {
                if (this.IsSupportedFunction("ForceRefreshServices").returnValue)
                    return _TPIHelper.ExecuteFunctionSingle(["ForceRefreshServices", flags], this.CTID);
                else
                    return this.ForceRefresh();
            }
            else {
                var bResult = _RefreshToolbarByCTID(this.CTID);
                return _TPIHelper.GetOldApiResult(bResult);
            }
        };

        this.IsVisible = function() {
            if (!this.CTID) return this.NoCTID();

            if (this.IsLatestApi)
                return _TPIHelper.ExecuteFunctionSingle(["IsToolbarVisible"], this.CTID);
            else {
                var bResult = _IsToolbarInstalled(this.CTID)
                return _TPIHelper.GetOldApiResult(bResult);
            }
        };

        this.AddComponentByXML = function(strXML) {
            if (!this.CTID) return this.NoCTID();

            if (this.IsLatestApi)
                return _TPIHelper.ExecuteFunctionSingle(["AddComponentByXML", strXML], this.CTID);
            else {
                var bResult = _AddComponentByXML(strXML, this.CTID)
                return _TPIHelper.GetOldApiResult(bResult);
            }
        };

        this.GetInfo = function() {
            if (!this.CTID) return this.NoCTID();

            if (this.IsLatestApi)
                return _TPIHelper.ExecuteFunctionSingle(["GetToolbarInfo"], this.CTID);
            else {
                if (!this.GetIsToolbarInstalled())
                    return this.NoToolbarInstalled();
                else
                    return this.UnsupportedCommand()
            }
        };

        this.AddGadget = function(buttonLabel, buttonIcon, buttonTooltip, linkTargetUrl,
					   width, height, defaultTarget, optionsDisplayText, optionsDisplayIcon, uniqueComponentID) {
            if (!this.CTID) return this.NoCTID();

            //parameters checks
            if (buttonLabel == "" && buttonIcon == "" && optionsDisplayIcon == "" && optionsDisplayText == "") {
                return this.ParametersError();
            }

            if (typeof linkTargetUrl == "undefined" || linkTargetUrl == "") {
                return this.ParametersError();
            }

            if (typeof width == "undefined" || width == "" || height == "undefined" || height == "") {
                return this.ParametersError();
            }

            if (typeof optionsDisplayText == "undefined" || optionsDisplayText == "") {
                optionsDisplayText = buttonLabel;
            }

            if (typeof optionsDisplayIcon == "undefined" || optionsDisplayIcon == "") {
                optionsDisplayIcon = buttonIcon;
            }

            if (typeof uniqueComponentID == "undefined" || uniqueComponentID == "") {
                uniqueComponentID = "gadget" + linkTargetUrl + width + height;
            }

            if (typeof defaultTarget == "undefined" || defaultTarget == "") {
                defaultTarget = "_SELF";
            }

            var xmlData = "<DATA><TYPE>POP_HTML</TYPE><POP_HTML><URL>" + _TPIHelper.ToLegalXML(linkTargetUrl) + "</URL>";
            xmlData += "<DEFAULT_TARGET>" + _TPIHelper.ToLegalXML(defaultTarget) + "</DEFAULT_TARGET><WIDTH>" + width + "</WIDTH><HEIGHT>" + height + "</HEIGHT></POP_HTML></DATA>";

            var strXML = _TPIHelper.BuildButtonXml(buttonLabel, buttonIcon, buttonTooltip, xmlData,
				       optionsDisplayText, optionsDisplayIcon, uniqueComponentID);


            return this.AddComponentByXML(strXML);
        };

        this.AddRSS = function(buttonLabel, buttonIcon, rssFeedUrl, buttonNewMessagesIcon, optionsDisplayText,
									optionsDisplayIcon, refreshIntervalInMinutes, uniqueComponentID) {
            if (!this.CTID) return this.NoCTID();

            //parameters checks
            if (buttonLabel == "" && buttonIcon == "" && optionsDisplayIcon == "" && optionsDisplayText == "") {
                return this.ParametersError();
            }

            if (typeof rssFeedUrl == "undefined" || rssFeedUrl == "") {
                return this.ParametersError();
            }

            if (typeof optionsDisplayText == "undefined" || optionsDisplayText == "") {
                optionsDisplayText = buttonLabel;
            }

            if (typeof optionsDisplayIcon == "undefined" || optionsDisplayIcon == "") {
                optionsDisplayIcon = buttonIcon;
            }

            if (typeof buttonNewMessagesIcon == "undefined" || buttonNewMessagesIcon == "") {
                buttonNewMessagesIcon = buttonIcon;
            }

            if (typeof uniqueComponentID == "undefined" || uniqueComponentID == "") {
                uniqueComponentID = "rss" + rssFeedUrl;
            }

            if (typeof refreshIntervalInMinutes == "undefined" || refreshIntervalInMinutes == "") {
                refreshIntervalInMinutes = "120";
            }

            var strXML = _TPIHelper.BuildRssXml(buttonLabel, buttonIcon, rssFeedUrl, buttonNewMessagesIcon, optionsDisplayText,
								    optionsDisplayIcon, refreshIntervalInMinutes, uniqueComponentID);

            return this.AddComponentByXML(strXML);
        };

        this.AddLinkButton = function(buttonLabel, buttonIcon, buttonTooltip, linkTargetUrl,
					   linkTarget, optionsDisplayText, optionsDisplayIcon, uniqueComponentID) {
            if (!this.CTID) return this.NoCTID();

            //parameters checks
            if (buttonLabel == "" && buttonIcon == "" && optionsDisplayIcon == "" && optionsDisplayText == "") {
                return this.ParametersError();
            }

            if (typeof linkTargetUrl == "undefined" || linkTargetUrl == "") {
                return this.ParametersError();
            }

            if (typeof optionsDisplayText == "undefined" || optionsDisplayText == "") {
                optionsDisplayText = buttonLabel;
            }

            if (typeof optionsDisplayIcon == "undefined" || optionsDisplayIcon == "") {
                optionsDisplayIcon = buttonIcon;
            }

            if (typeof uniqueComponentID == "undefined" || uniqueComponentID == "") {
                uniqueComponentID = "button" + linkTargetUrl;
            }

            if (typeof linkTarget == "undefined" || linkTarget == "") {
                linkTarget = "SELF";
            }

            var xmlData = "<DATA><TYPE>LINK</TYPE><LINK><URL>" + _TPIHelper.ToLegalXML(linkTargetUrl) + "</URL><TARGET>" + _TPIHelper.ToLegalXML(linkTarget) + "</TARGET></LINK></DATA>";

            var strXML = _TPIHelper.BuildButtonXml(buttonLabel, buttonIcon, buttonTooltip, xmlData,
				       optionsDisplayText, optionsDisplayIcon, uniqueComponentID);

            return this.AddComponentByXML(strXML);
        };

        this.AddRadioStation = function(name, url, contantType, mediaType, uniqueComponentID) {
            if (!name || !url)
                return this.ParametersError();

            if (!contantType)
                contantType = "MP";

            if (!mediaType)
                mediaType = "STREAM";

            if (!uniqueComponentID)
                uniqueComponentID = url;

            var strXML = _TPIHelper.BuildRadioStationXml(name, url, contantType, mediaType, uniqueComponentID);

            if (!this.CTID) return this.NoCTID();

            if (this.IsLatestApi)
                return _TPIHelper.ExecuteFunctionSingle(["AddRadioStation", strXML], this.CTID);
            else {
                if (!this.GetIsToolbarInstalled())
                    return this.NoToolbarInstalled();
                else
                    return this.UnsupportedCommand();
            }
        };

        this.SwitchToCommunity = function(strCTID) {
            if (!this.CTID) return this.NoCTID();

            if (this.IsLatestApi)
                return _TPIHelper.ExecuteFunctionSingle(["SwitchToCommunity", strCTID], this.CTID);
            else {
                if (!this.GetIsToolbarInstalled())
                    return this.NoToolbarInstalled();
                else
                    return this.UnsupportedCommand();
            }
        };

        this.GetSupportedUserAddMenu = function() {
            if (!this.CTID) return this.NoCTID();

            if (this.IsLatestApi)
                return _TPIHelper.ExecuteFunctionSingle(["GetSupportedUserAddMenu"], this.CTID);
            else {
                if (!this.GetIsToolbarInstalled())
                    return this.NoToolbarInstalled();
                else
                    return this.UnsupportedCommand();
            }
        };

        this.AddLinkMenuItem = function(uniqueComponentID, menuItemCaption, menuItemIconUrl, linkUrl, linkTarget) {
            if (!this.CTID) return this.NoCTID();

            if (!uniqueComponentID || !linkUrl || (!menuItemCaption && !menuItemIconUrl))
                return this.ParametersError();

            var strXMLData = _TPIHelper.BuildLinkDataXML(linkUrl, linkTarget);
            var strXML = _TPIHelper.BuildMenuItemXML(menuItemCaption, menuItemIconUrl, strXMLData);

            if (this.IsLatestApi)
                return _TPIHelper.ExecuteFunctionSingle(["AddUserLinkMenuItem", strXML, uniqueComponentID], this.CTID);
            else {
                if (!this.GetIsToolbarInstalled())
                    return this.NoToolbarInstalled();
                else
                    return this.UnsupportedCommand();
            }
        };

        this.AddGadgetMenuItem = function(uniqueComponentID, menuItemCaption, menuItemIconUrl, linkUrl, width, height, defaultTarget) {
            if (!this.CTID) return this.NoCTID();

            if (!uniqueComponentID || !linkUrl || (!menuItemCaption && !menuItemIconUrl) || !width || !height)
                return this.ParametersError();

            var strXMLData = _TPIHelper.BuildGadgetDataXML(linkUrl, width, height, defaultTarget);
            var strXML = _TPIHelper.BuildMenuItemXML(menuItemCaption, menuItemIconUrl, strXMLData);

            if (this.IsLatestApi)
                return _TPIHelper.ExecuteFunctionSingle(["AddUserLinkMenuItem", strXML, uniqueComponentID], this.CTID);
            else {
                if (!this.GetIsToolbarInstalled())
                    return this.NoToolbarInstalled();
                else
                    return this.UnsupportedCommand();
            }
        };

        this.AddCommandMenuItem = function(uniqueComponentID, menuItemCaption, menuItemIconUrl, commandType) {
            if (!this.CTID) return this.NoCTID();

            if (!uniqueComponentID || (!menuItemCaption && !menuItemIconUrl) || !commandType)
                return this.ParametersError();

            var strXMLData = _TPIHelper.BuildCommandDataXML(commandType);
            var strXML = _TPIHelper.BuildMenuItemXML(menuItemCaption, menuItemIconUrl, strXMLData);

            if (this.IsLatestApi)
                return _TPIHelper.ExecuteFunctionSingle(["AddUserLinkMenuItem", strXML, uniqueComponentID], this.CTID);
            else {
                if (!this.GetIsToolbarInstalled())
                    return this.NoToolbarInstalled();
                else
                    return this.UnsupportedCommand();
            }
        };

        this.AddApplicationMenuItem = function(uniqueComponentID, menuItemCaption, menuItemIconUrl, exeAlias, params, appNotFoundUrl) {
            if (!this.CTID) return this.NoCTID();

            if (!uniqueComponentID || (!menuItemCaption && !menuItemIconUrl) || !exeAlias)
                return this.ParametersError();

            var strXMLData = _TPIHelper.BuildApplicationDataXML(exeAlias, params, appNotFoundUrl)
            var strXML = _TPIHelper.BuildMenuItemXML(menuItemCaption, menuItemIconUrl, strXMLData);

            if (this.IsLatestApi)
                return _TPIHelper.ExecuteFunctionSingle(["AddUserLinkMenuItem", strXML, uniqueComponentID], this.CTID);
            else {
                if (!this.GetIsToolbarInstalled())
                    return this.NoToolbarInstalled();
                else
                    return this.UnsupportedCommand();
            }
        };

        //************************ MyStuff functions ************************
        //asyncCallback will be called with following parameters(compGuid, isSucceeded, flags, state);
        //onErrorCallback will be called with (state) param
        this.AddMyStuffComponent = function(componentGuid, componentInstanceGuid, compName) {
            if (!this.CTID) return this.NoCTID();
            if (!componentGuid || !componentInstanceGuid) return this.ParametersError();
            if (typeof compName == "undefined") compName = "";

            if (this.IsLatestApi)
                return _TPIHelper.ExecuteFunctionSingle(["AddMyStuffComponent", componentGuid, componentInstanceGuid, compName], this.CTID);
            else {
                if (!this.GetIsToolbarInstalled())
                    return this.NoToolbarInstalled();
                else
                    return this.UnsupportedCommand();
            }
        };
        //************************ MyStuff functions end ************************

        this.IsSupportedFunction = function(strFunctionName) {
            if (!this.CTID) return this.NoCTID();
            if (this.IsLatestApi) {
                var strToolbarFunctionName = _TPIHelper.GetToolbarFunctionString(strFunctionName);
                return _TPIHelper.ExecuteFunctionSingle([_TPIHelper.IsSupportedFunction, strToolbarFunctionName], this.CTID);
            }
            else {
                if (!this.GetIsToolbarInstalled())
                    return this.NoToolbarInstalled();
                else
                    return this.UnsupportedCommand();
            }
        };

        //builds xml for response parser, for unsupported command
        this.UnsupportedCommand = function() {
            var strXmlResult = "<RETURN_OBJECT><RETURN_VALUE>false</RETURN_VALUE><ERROR_CODE>2</ERROR_CODE></RETURN_OBJECT>";
            return _TPIHelper.GetResult(strXmlResult);
        };

        //builds xml for response parser, for input parameters error
        this.ParametersError = function() {
            var strXmlResult = "<RETURN_OBJECT><RETURN_VALUE>false</RETURN_VALUE><ERROR_CODE>3</ERROR_CODE></RETURN_OBJECT>";
            return _TPIHelper.GetResult(strXmlResult);
        };

        this.NoToolbarInstalled = function() {
            var strXmlResult = "<RETURN_OBJECT><RETURN_VALUE>false</RETURN_VALUE><ERROR_CODE>0</ERROR_CODE></RETURN_OBJECT>";
            return _TPIHelper.GetResult(strXmlResult);
        };

        this.NoCTID = function() {
            var strXmlResult = "<RETURN_OBJECT><RETURN_VALUE>false</RETURN_VALUE><ERROR_CODE>6</ERROR_CODE></RETURN_OBJECT>";
            return _TPIHelper.GetResult(strXmlResult);
        };

        this.GetIsToolbarInstalled = function() {
            if (this.IsToolbarInstalled == -1) {
                this.IsToolbarInstalled = _IsToolbarInstalled(this.CTID);
            }

            return this.IsToolbarInstalled;
        }
    },

    //Helper manager to interract with multiple toolbars
    Manager: function() {
        this.GetAllToolbarsInfo = function() {
            return _TPIHelper.ExecuteFunctionMultiple(["GetAllToolbarsInfo"]);
        };

        this.AddAlert = function(alertId) {
            return _TPIHelper.ExecuteFunctionAny(["AddAlert", alertId]);
        };

        this.AddMyStuffAlert = function(alertId, alertProviderName) {
            return _TPIHelper.ExecuteFunctionAny(["AddMyStuffAlert", alertId, alertProviderName]);
        };

        this.IsSubscribedToAlert = function(alertId) {
            var objMultipleReturn = _TPIHelper.ExecuteFunctionAny(["IsSubscribedToAlert", alertId]);
            return _ManagerHelper.GetManagerReturnObject(objMultipleReturn, false);
        };

        this.IsSupportedFunction = function(strFunctionName) {
            var strToolbarFunctionName = _TPIHelper.GetToolbarFunctionString(strFunctionName);
            var objMultipleReturn = _TPIHelper.ExecuteFunctionAny([_TPIHelper.IsSupportedFunction, strToolbarFunctionName]);

            if (!objMultipleReturn.length)
                return objMultipleReturn;

            var bIsSucceeded = false;

            for (var i = 0; i < objMultipleReturn.length; i++) {
                //at least one toolbar support this function
                if (objMultipleReturn[i].returnValue)
                    return objMultipleReturn[i];

                //at least one toolbar supports the check-supported function, but not supports the requested function
                if (objMultipleReturn[i].errorCode == 1)
                    bIsSucceeded = true;
            }

            //at least one toolbar supports the check-supported function, but not supports the requested function
            if (bIsSucceeded)
                return _ManagerHelper.CreateReturnObject(1, false, null);
            //all toolbars doesn't recognize the heck-supported function
            else
                return _ManagerHelper.CreateReturnObject(2, false, null);
        };
    }
}

// ======================= Older API Functions ========================

//this is a blank object
//this object is used to catch all the calls 
//that the toolbars(IE & Firefox) didn't catch
var EBToolbarApi =
{
    Result: false,

    ResetResult: function() {
        this.SetResult(false);
    },

    SetResult: function(bValue) {
        this.Result = bValue;
    },

    GetResult: function() {
        return this.Result;
    },

    RefreshAllToolbars: function() {
        //do nothing...
    },

    RefreshToolbarByCTID: function(strCTID) {
        //do nothing...	
    },

    IsToolbarInstalled: function(strCTID) {
        //do nothing...
    },

    AddComponentByXML: function(strXML, strCTID) {
        //do nothing...
    }
}

//wrapped function to set result (for IE)
function EBSetResult(bValue) {
    EBToolbarApi.SetResult(bValue);
}

//-------------------------------------------------------------------

/***** Public API Functions *****/

/******************************** - IMPORTTANT - **********************************/
//                                                                                //
//	The functions should be called the earliset at the onload event of the page   //
//                                                                                //
/**********************************************************************************/

/************************************************/
/*			bool RefreshAllToolbars()			*/
/*	This function refreshes all toolbars.		*/
/************************************************/
function RefreshAllToolbars() {
    var bResult = _RefreshAllToolbars();

    var oManager = new TPI.Manager();
    var oResult = oManager.GetAllToolbarsInfo();
    var bResult = true;
    if (oResult.length) {
        for (var i = 0; i < oResult.length; i++) {
            var oToolbar = new TPI.Toolbar(oResult[i].CTID);
            var oRes = oToolbar.Refresh();
            bResult = oRes.returnValue || bResult;
        }
    }

    return bResult;
}

/************************************************/
/*		bool RefreshToolbarByCTID(strCTID)		*/
/*	This function refreshes the toolbar by CTID.*/
/************************************************/
function RefreshToolbarByCTID(strCTID) {
    var oToolbar = new TPI.Toolbar(strCTID);
    var oRes = oToolbar.Refresh();
    return oRes.returnValue;
}

/************************************************/
/*		bool IsToolbarInstalled(strCTID)	    */
/*	This function returns true if the toolbar   */
/*	with the given CTID is installed.			*/
/************************************************/
function IsToolbarInstalled(strCTID) {
    var oToolbar = new TPI.Toolbar(strCTID);
    var oRes = oToolbar.IsVisible();
    return oRes.returnValue;
}

/************************************************/
/*		bool AddComponentByXML(strXML,strCTID)  */
/*	This function returns true if the toolbar   */
/*	with the given CTID is installed.			*/
/************************************************/
function AddComponentByXML(strXML, strCTID) {
    var oToolbar = new TPI.Toolbar(strCTID);
    var oRes = oToolbar.AddComponentByXML(strXML);
    return oRes.returnValue;
}


//Inner functions for new API to talk to old toolbars
function __ExecuteOldApiFunction(strUrl) {
    var strBrowser = _TPIHelper.GetBrowser();
    switch (strBrowser) {
        case BROWSERS_DEF.IE6:
            location.href = "javascript:" + strUrl;
            break;
        case BROWSERS_DEF.IE7:
            _TPIHelper.NavigateInIFrame("about:blank#javascript:" + strUrl);
            break;
        case BROWSERS_DEF.FF:
            eval(strUrl);
            break;
    }
};

function _RefreshAllToolbars() {
    EBToolbarApi.ResetResult();

    __ExecuteOldApiFunction("EBToolbarApi.RefreshAllToolbars();");
    return EBToolbarApi.GetResult();
}

function _RefreshToolbarByCTID(strCTID) {
    EBToolbarApi.ResetResult();

    __ExecuteOldApiFunction("EBToolbarApi.RefreshToolbarByCTID('" + strCTID + "');");
    return EBToolbarApi.GetResult();
}

function _IsToolbarInstalled(strCTID) {
    EBToolbarApi.ResetResult();

    __ExecuteOldApiFunction("EBToolbarApi.IsToolbarInstalled('" + strCTID + "');");
    return EBToolbarApi.GetResult();
}

function _AddComponentByXML(strXML, strCTID) {
    EBToolbarApi.ResetResult();

    __ExecuteOldApiFunction("EBToolbarApi.AddComponentByXML(\"" + strXML + "\",\"CTID=" + strCTID.toUpperCase() + "\");");
    return EBToolbarApi.GetResult();
}


/*
* A JavaScript implementation of the RSA Data Security, Inc. this.MD5 Message
* Digest Algorithm, as defined in RFC 1321.
* Copyright (C) Paul Johnston 1999 - 2000.
* Updated by Greg Holt 2000 - 2001.
* See http://pajhome.org.uk/site/legal.html for details.
*/

/*
* Convert a 32-bit number to a hex string with ls-byte first
*/
var MD5Hash =
{
    hex_chr: "0123456789abcdef",

    rhex: function(num) {
        str = "";
        for (j = 0; j <= 3; j++)
            str += this.hex_chr.charAt((num >> (j * 8 + 4)) & 0x0F) +
               this.hex_chr.charAt((num >> (j * 8)) & 0x0F);
        return str;
    },

    /*
    * Convert a string to a sequence of 16-word blocks, stored as an array.
    * Append pthis.adding bits and the length, as described in the this.MD5 standard.
    */
    str2blks_MD5: function(str) {
        nblk = ((str.length + 8) >> 6) + 1;
        blks = new Array(nblk * 16);
        for (i = 0; i < nblk * 16; i++) blks[i] = 0;
        for (i = 0; i < str.length; i++)
            blks[i >> 2] |= str.charCodeAt(i) << ((i % 4) * 8);
        blks[i >> 2] |= 0x80 << ((i % 4) * 8);
        blks[nblk * 16 - 2] = str.length * 8;
        return blks;
    },

    /*
    * this.add integers, wrapping at 2^32. This uses 16-bit operations internally 
    * to work around bugs in some JS interpreters.
    */
    add: function(x, y) {
        var lsw = (x & 0xFFFF) + (y & 0xFFFF);
        var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
        return (msw << 16) | (lsw & 0xFFFF);
    },

    /*
    * Bitwise rotate a 32-bit number to the left
    */
    rol: function(num, cnt) {
        return (num << cnt) | (num >>> (32 - cnt));
    },

    /*
    * These functions implement the basic operation for each round of the
    * algorithm.
    */
    cmn: function(q, a, b, x, s, t) {
        return this.add(this.rol(this.add(this.add(a, q), this.add(x, t)), s), b);
    },

    ff: function(a, b, c, d, x, s, t) {
        return this.cmn((b & c) | ((~b) & d), a, b, x, s, t);
    },

    gg: function(a, b, c, d, x, s, t) {
        return this.cmn((b & d) | (c & (~d)), a, b, x, s, t);
    },

    hh: function(a, b, c, d, x, s, t) {
        return this.cmn(b ^ c ^ d, a, b, x, s, t);
    },

    ii: function(a, b, c, d, x, s, t) {
        return this.cmn(c ^ (b | (~d)), a, b, x, s, t);
    },

    /*
    * Take a string and return the hex representation of its this.MD5.
    */
    MD5: function(str) {
        x = this.str2blks_MD5(str);
        var a = 1732584193;
        var b = -271733879;
        var c = -1732584194;
        var d = 271733878;

        for (i = 0; i < x.length; i += 16) {
            var olda = a;
            var oldb = b;
            var oldc = c;
            var oldd = d;

            a = this.ff(a, b, c, d, x[i + 0], 7, -680876936);
            d = this.ff(d, a, b, c, x[i + 1], 12, -389564586);
            c = this.ff(c, d, a, b, x[i + 2], 17, 606105819);
            b = this.ff(b, c, d, a, x[i + 3], 22, -1044525330);
            a = this.ff(a, b, c, d, x[i + 4], 7, -176418897);
            d = this.ff(d, a, b, c, x[i + 5], 12, 1200080426);
            c = this.ff(c, d, a, b, x[i + 6], 17, -1473231341);
            b = this.ff(b, c, d, a, x[i + 7], 22, -45705983);
            a = this.ff(a, b, c, d, x[i + 8], 7, 1770035416);
            d = this.ff(d, a, b, c, x[i + 9], 12, -1958414417);
            c = this.ff(c, d, a, b, x[i + 10], 17, -42063);
            b = this.ff(b, c, d, a, x[i + 11], 22, -1990404162);
            a = this.ff(a, b, c, d, x[i + 12], 7, 1804603682);
            d = this.ff(d, a, b, c, x[i + 13], 12, -40341101);
            c = this.ff(c, d, a, b, x[i + 14], 17, -1502002290);
            b = this.ff(b, c, d, a, x[i + 15], 22, 1236535329);

            a = this.gg(a, b, c, d, x[i + 1], 5, -165796510);
            d = this.gg(d, a, b, c, x[i + 6], 9, -1069501632);
            c = this.gg(c, d, a, b, x[i + 11], 14, 643717713);
            b = this.gg(b, c, d, a, x[i + 0], 20, -373897302);
            a = this.gg(a, b, c, d, x[i + 5], 5, -701558691);
            d = this.gg(d, a, b, c, x[i + 10], 9, 38016083);
            c = this.gg(c, d, a, b, x[i + 15], 14, -660478335);
            b = this.gg(b, c, d, a, x[i + 4], 20, -405537848);
            a = this.gg(a, b, c, d, x[i + 9], 5, 568446438);
            d = this.gg(d, a, b, c, x[i + 14], 9, -1019803690);
            c = this.gg(c, d, a, b, x[i + 3], 14, -187363961);
            b = this.gg(b, c, d, a, x[i + 8], 20, 1163531501);
            a = this.gg(a, b, c, d, x[i + 13], 5, -1444681467);
            d = this.gg(d, a, b, c, x[i + 2], 9, -51403784);
            c = this.gg(c, d, a, b, x[i + 7], 14, 1735328473);
            b = this.gg(b, c, d, a, x[i + 12], 20, -1926607734);

            a = this.hh(a, b, c, d, x[i + 5], 4, -378558);
            d = this.hh(d, a, b, c, x[i + 8], 11, -2022574463);
            c = this.hh(c, d, a, b, x[i + 11], 16, 1839030562);
            b = this.hh(b, c, d, a, x[i + 14], 23, -35309556);
            a = this.hh(a, b, c, d, x[i + 1], 4, -1530992060);
            d = this.hh(d, a, b, c, x[i + 4], 11, 1272893353);
            c = this.hh(c, d, a, b, x[i + 7], 16, -155497632);
            b = this.hh(b, c, d, a, x[i + 10], 23, -1094730640);
            a = this.hh(a, b, c, d, x[i + 13], 4, 681279174);
            d = this.hh(d, a, b, c, x[i + 0], 11, -358537222);
            c = this.hh(c, d, a, b, x[i + 3], 16, -722521979);
            b = this.hh(b, c, d, a, x[i + 6], 23, 76029189);
            a = this.hh(a, b, c, d, x[i + 9], 4, -640364487);
            d = this.hh(d, a, b, c, x[i + 12], 11, -421815835);
            c = this.hh(c, d, a, b, x[i + 15], 16, 530742520);
            b = this.hh(b, c, d, a, x[i + 2], 23, -995338651);

            a = this.ii(a, b, c, d, x[i + 0], 6, -198630844);
            d = this.ii(d, a, b, c, x[i + 7], 10, 1126891415);
            c = this.ii(c, d, a, b, x[i + 14], 15, -1416354905);
            b = this.ii(b, c, d, a, x[i + 5], 21, -57434055);
            a = this.ii(a, b, c, d, x[i + 12], 6, 1700485571);
            d = this.ii(d, a, b, c, x[i + 3], 10, -1894986606);
            c = this.ii(c, d, a, b, x[i + 10], 15, -1051523);
            b = this.ii(b, c, d, a, x[i + 1], 21, -2054922799);
            a = this.ii(a, b, c, d, x[i + 8], 6, 1873313359);
            d = this.ii(d, a, b, c, x[i + 15], 10, -30611744);
            c = this.ii(c, d, a, b, x[i + 6], 15, -1560198380);
            b = this.ii(b, c, d, a, x[i + 13], 21, 1309151649);
            a = this.ii(a, b, c, d, x[i + 4], 6, -145523070);
            d = this.ii(d, a, b, c, x[i + 11], 10, -1120210379);
            c = this.ii(c, d, a, b, x[i + 2], 15, 718787259);
            b = this.ii(b, c, d, a, x[i + 9], 21, -343485551);

            a = this.add(a, olda);
            b = this.add(b, oldb);
            c = this.add(c, oldc);
            d = this.add(d, oldd);
        }
        return this.rhex(a) + this.rhex(b) + this.rhex(c) + this.rhex(d);
    }
};