﻿/// <reference name="mouse.js"/>
function hidestatus() {
    window.status = ''
    return true
}

if (document.layers)
    document.captureEvents(Event.MOUSEOVER | Event.MOUSEOUT)

document.onmouseover = hidestatus
document.onmouseout = hidestatus

/// <reference name="jscript2.js"/>
var message = "Why Right Click?"; // Your no right click message here
var closeWin = "0"; // Do you want to close window after message (1 for yes 0 for no)

function IE(e) {
    if (navigator.appName == 'Microsoft Internet Explorer' && (event.button == 2 || event.button == 3)) {
        alert(message); if (closeWin == "1") self.close();
        return false;
    }
}
function NS(e) {
    if (document.layers || (document.getElementById && !document.all)) {
        if (e.which == 2 || e.which == 3) {
            alert(message); if (closeWin == "1") self.close();
            return false;
        }
    }
}
document.onmousedown = IE; document.onmouseup = NS; document.oncontextmenu = new Function("return false");

/**
*/
String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ""); };
String.prototype.toBoolean = function() { if (this == "false") return false; return true; };
Array.prototype.indexOf = function(val) { for (i = 0; i < this.length; i++) if (this[i] == val) return i; return -1; };

// define your own baseResourceURI before this script is sourced to over-ride the default
if (typeof (baseResourceURI) == "undefined") var baseResourceURI = "";
if (typeof (baseLang) == "undefined") var baseLang = "en-us";

var AIM = {
    /**
    * General entry point for the API.
    */
    init: function() {
        if (!AIM.params.wimKey) AIM.params.wimKey = document.getElementById("AIMBuddyListContainer").getAttribute("wim_key");
        if (!AIM.params.wimKey) return alert(AIM.params.text.errors.missingKey);
        AIM.util.addEvent(window, AIM.util.cleanUp, "beforeunload");
        AIM.util.addEvent(document, AIM.eventHandlers.handleMouseMove, "mousemove");
        AIM.util.addEvent(document, function() { AIM.util.mDown = false; }, "mouseup");
        if (AIM.util.cookie.get("ablsnd") != null) AIM.params.SOUND = AIM.util.cookie.get("ablsnd").toBoolean();
    }
}

/**
*	Parameters that define how the API behaves. See the full documentation on developer.aim.com for details.
*/
AIM.params = {
    user: null,
    owner: null,
    sessionId: null,
    assertCaps: "",
    interestCaps: "",
    baseTransactionURI: "http://api.oscar.aol.com/",
    baseAuthURI: "https://api.screenname.aol.com/",
    listenerURI: null,
    wimKey: null,
    sound: true,
    token: null,
    language: "en-us", // deprecated by global baseLang
    transactions: {
        getBuddyInfo: "presence/get",
        getBuddyList: "presence/get",
        getToken: "auth/getToken",
        sendTextIM: "im/sendIM",
        sendDataIM: "im/sendDataIM",
        getPresenceInfo: "presence/get",
        startSession: "aim/startSession",
        setState: "presence/setState",
        typingStatus: "im/setTyping",
        endSession: "aim/endSession",
        logout: "auth/logout"
    },
    sounds: {
        IM: baseResourceURI + "im.wav"
    },
    callbacks: {
        getBuddyInfo: ["AIM.callbacks.getBuddyInfo"],
        getBuddyList: ["AIM.callbacks.getBuddyList"],
        getToken: ["AIM.callbacks.getToken"],
        sendTextIM: ["AIM.callbacks.sendTextIM"],
        sendDataIM: ["AIM.callbacks.sendDataIM"],
        getPresenceInfo: ["AIM.callbacks.getPresenceInfo"],
        startSession: ["AIM.callbacks.startSession"],
        typingStatus: ["AIM.callbacks.typingStatus"],
        setState: ["AIM.callbacks.setState"],
        endSession: ["AIM.callbacks.endSession"],
        listener: {
            im: ["AIM.ui.acceptIncomingMessage"],
            offlineIM: ["AIM.ui.acceptIncomingMessage"],
            imData: ["AIM.callbacks.acceptDataIM"],
            buddylist: ["AIM.ui.createBuddyList"],
            presence: ["AIM.ui.updateBuddyList"],
            typing: ["AIM.ui.updateTypingStatus"],
            sessionEnded: ["AIM.callbacks.sessionEnded"]
        }
    },
    emoticons: {
        ">:o": "angry",
        ":-\\[": "blush",
        ":'\\(": "crying",
        ":-!": "foot-in-mouth",
        ":-\\(": "frown",
        ":\\(": "frown",
        "=-o": "gasp",
        ":-D": "grin",
        ":D": "grin",
        "O:-\\)": "halo",
        ":-\\*": "kiss",
        ":-x": "lips-sealed",
        ":-\\$": "money-mouth",
        ":-\\)": "smile",
        ":\\)": "smile",
        ":-p": "tongue",
        ":p": "tongue",
        ":-\\\\": "undecided",
        ":-\\/": "undecided",
        "8-\\)": "cool",
        "8\\)": "cool",
        ";-\\)": "wink",
        ";\\)": "wink"
    },

    // AIM.params.text moved to language specific files, i.e. aimapi.text.en-us.js
    USE_EMOTICONS: false,
    SEND_OFFLINE_IM: 1,
    DOCUMENT_TITLE: document.title,
    DEBUG: false,
    BUDDY_LIST_DRAG: false,
    DEFAULT_ICON: baseResourceURI + "default_icon.png",
    AWAY_MSG_LIMIT: 1024,
    REQUEST_TIMEOUT: ((navigator.userAgent.indexOf("Firefox") > -1 || navigator.userAgent.indexOf("Camino") > -1 || window.opera) && !document.all) ? 2000 : 60000,
    NOTIFICATION_THROB: 1000,
    RENDER_SEND_BUTTON: true,
    UPDATE_DURATION: 5000,
    SHOW_OFFLINE: false,
    CREATE_AVAILABILITY_MENU_BL: true,
    CREATE_AVAILABILITY_MENU_IM: true,
    RETAIN_WINDOW: true,
    SHOW_TIMESTAMP: true,
    TWENTY_FOUR_HOUR_CLOCK: false,
    VISUAL_NOTIFICATION: true,
    MOZILLA: navigator.userAgent.indexOf("Firefox") > -1 && !document.all,
    MSIE: document.all && !window.opera,
    OPERA: window.opera
}

/**
*	Widget object to contain launch, kill and appearence definitions
*/
AIM.widgets = {
    buddyList: {
        launch: function() {
            AIM.init();
            if (!document.getElementById("AIMBuddyListContainer")) return alert(AIM.params.text.errors.noAIMContainer);
            if (!AIM.core.supportedBrowser()) alert(AIM.params.text.unsupportedBrowser);
            AIM.util.createStyleSheet(AIM.widgets.buddyList.appearance.styleSheetURI);
            if (document.getElementById("AIMBuddyList")) {
                var AIMBL = document.getElementById("AIMBuddyList")
                AIMBL.style.position = "static";
                AIMBL.style.top = "0";
                AIMBL.style.left = "0";
                return;
            }
            AIM.util.createSoundObjects();
            //AIM.params.sessionId = null;
            AIM.core.subscriptions = arguments[0] ? arguments[0] : "buddylist,presence,im,typing,offlineIM";
            AIM.transactions.getToken(AIM.core.subscriptions);
        },
        kill: function() {
            //document.getElementById("AIMBuddyList").parentNode.removeChild(document.getElementById("AIMBuddyList"));
            document.getElementById("AIMBuddyListContainer").innerHTML = "";
            if (document.getElementById("AIMBuddyListAwayBox")) document.getElementById("AIMBuddyListAwayBox").parentNode.removeChild(document.getElementById("AIMBuddyListAwayBox"));
            //AIM.util.removeEvent(document,AIM.eventHandlers.handleMouseMove,"mousemove");
            AIM.ui.removeAllIMWindows();
            AIM.transactions.endSession();
        },
        appearance: {
            styleSheetURI: baseResourceURI + "default-theme.css"
        }
    },
    IM: {
        launch: function() {
            AIM.init();
            if (!document.getElementById("AIMBuddyListContainer")) return alert(AIM.params.text.errors.noAIMContainer);
            if (!AIM.core.supportedBrowser()) alert(AIM.params.text.unsupportedBrowser);
            AIM.util.createStyleSheet(AIM.widgets.IM.appearance.styleSheetURI);
            AIM.util.addEvent(document, AIM.eventHandlers.handleMouseMove, "mousemove");
            if (arguments[0]) {
                AIM.ui.createIMWindow(arguments[0]);
            } else {
                AIM.ui.aimIdPrompt(AIM.params.owner);
            }
            //AIM.params.sessionId = null;
            //AIM.core.subscriptions = arguments[0]?arguments[0]:"im,typing";
            AIM.core.subscriptions = "im,typing";
            AIM.transactions.getToken(AIM.core.subscriptions);
        },
        kill: function() {
            AIM.ui.closeIMWindow(aimId);
            AIM.util.removeEvent(document, AIM.eventHandlers.handleMouseMove, "mousemove");
            AIM.transactions.endSession();
        },
        appearance: {
            styleSheetURI: baseResourceURI + "default-theme.css"
        }
    },
    getInfo: {
        launch: function() {
            AIM.init();
            AIM.ui.prepBuddyInfo(srcObj);
            AIM.transactions.getBuddyInfo(aimId);
        },
        kill: function() {
            document.getElementById("AIMBuddyListBuddyInfo").parentNode.removeChild(document.getElementById("AIMBuddyListBuddyInfo"));
        },
        appearance: {
            styleSheetURI: baseResourceURI + "default-theme.css"
        }
    },
    presence: {
        launch: function() {
            var fn = function() {
                AIM.init();
                AIM.transactions.getPresenceInfo();
                AIM.util.addEvent(document, AIM.eventHandlers.handleMouseMove, "mousemove");
            }
            window.addEventListener ? window.addEventListener("load", fn, false) : window.attachEvent("onload", fn);
        },
        kill: function() {
            AIM.util.addEvent(document, AIM.eventHandlers.handleMouseMove, "mousemove");
        },
        appearance: {
            styleSheetURI: baseResourceURI + "default-theme.css"
        }
    }
}

/**
*	AIM.core contains all of the methods that are REQUIRED for the API to funtion.
*/
AIM.core = {
    AIMData: [],
    authAttempts: 0,
    requestInterval: null,
    subscriptions: null,
    activeSession: false,
    pendingTransaction: null,
    /**
    *	Creates a DIV element that contains an IFRAME element for authentication/consent
    *	@param { String } url The url that will be the src of the iframe
    */
    createAuthWindow: function(url) {
        if (document.getElementById("AIMFrameContainer_AIMwindow")) {
            var oIframe = document.getElementById("AIMReqFrame");
        } else {
            var win = AIM.ui.createWindowFrame("AIMFrameContainer", "AIMFrameContainer", "Web AIM");
            oIframe = document.createElement("iframe");
            oIframe.setAttribute("id", "AIMReqFrame");
            oIframe.setAttribute("frameborder", "0");
            win.appendChild(oIframe);
            win.style.left = (document.getElementsByTagName("body")[0].offsetWidth - 510) / 2 + "px";
            document.getElementsByTagName("body")[0].appendChild(win);
        }
        oIframe.src = "about:blank";
        oIframe.src = url + "&nocache=" + Date.parse(new Date());
        document.getElementById("AIMFrameContainer_AIMwindow").style.display = "block";
        //document.getElementById("AIMFrameContainer_AIMwindow").style.top = (AIM.util.getScrollOffset(1) + document.getElementById("AIMFrameContainer_AIMwindow").offsetTop) + "px";
        window.scrollTo(0, 0);
        AIM.core.debug("createAuthWindow: " + url + "&nocache=" + Date.parse(new Date()));
        AIM.core.watchAuthRequest();
    },
    /**
    *	Checks to see if the browser the user is in is part of the "officially supported browser matrix"
    */
    supportedBrowser: function() {
        if (window.opera || document.layers || !document.getElementById) return false;
        var FF = navigator.userAgent.match("Firefox/[1-3]\.");
        var MSIE = navigator.userAgent.match(/MSIE [6-9]/);
        var SAF = navigator.userAgent.match(/Safari\/[4-9]/);
        if (FF || MSIE || SAF) return true;
        return false;
    },

    /**
    *	Called via setTimeout to check the status of the fragment identifier set by the authentication service
    *	when authentication/consent is complete.
    *  	1. AUTHCANCEL - user canceled login
    *	2. AUTHDONE - user successfully logged in
    *	3. INVALIDCALLBACK - invalid jsonp callback
    *	4. CONSENTINVALIDTOKEN - getconsent called with invalid enc token
    *	5. CONSENTDONE - consent done
    *	6. CONSENTCANCEL - user denied consent 
    *
    */
    watchAuthRequest: function() {
        var oLoc = location.href;
        if (oLoc.indexOf("#AUTHDONE") > -1 || oLoc.indexOf("#CONSENTDONE") > -1 || oLoc.indexOf("#CONSENTCANCEL") > -1 || oLoc.indexOf("#AUTHCANCEL") > -1 || oLoc.indexOf("#CONSENTINVALIDTOKEN") > -1) {
            if (oLoc.indexOf("#AUTHDONE") > -1) {
                AIM.core.debug("AIM.core.watchAuthRequest: Making a token request.");
                AIM.transactions.getToken(AIM.core.subscriptions);
            } else if (oLoc.indexOf("#CONSENTDONE") > -1) {
                if (!AIM.params.sessionId) {
                    AIM.core.debug("AIM.core.watchAuthRequest: No session id. Requesting one.");
                    AIM.transactions.startSession(AIM.core.subscriptions);
                } else {
                    AIM.core.destroyListenerObject(true);
                }
                if (AIM.core.pendingTransaction) {
                    switch (AIM.core.pendingTransaction.type) {
                        case "textIM":
                            AIM.transactions.sendTextIM(AIM.core.pendingTransaction.to, AIM.core.pendingTransaction.msg);
                            AIM.core.pendingTransaction = null;
                            break;
                        case "sendDataIM":
                            AIM.transactions.sendDataIM(AIM.core.pendingTransaction.to, AIM.core.pendingTransaction.msg, AIM.core.pendingTransaction.cap, AIM.core.pendingTransaction.dType);
                            AIM.core.pendingTransaction = null;
                            break;
                        case "setState":
                            AIM.transactions.setAwayMessage(AIM.params.text.awayMessage);
                            AIM.core.prendingTransaction = null;
                            break;
                    }
                    /*
                    if(AIM.core.pendingTransaction.type == "textIM") {
                    AIM.transactions.sendTextIM(AIM.core.pendingTransaction.to,AIM.core.pendingTransaction.msg);
                    AIM.core.pendingTransaction = null;
                    } else {
                    AIM.transactions.sendDataIM(AIM.core.pendingTransaction.to,AIM.core.pendingTransaction.msg,AIM.core.pendingTransaction.cap,AIM.core.pendingTransaction.dType);
                    AIM.core.pendingTransaction = null;
                    }
                    */
                }
            } else if (oLoc.indexOf("#CONSENTINVALIDTOKEN") > -1) {
                AIM.transactions.endSession();
                alert("Please log in again!");
            }
            location.replace("#aim");
            document.getElementById("AIMFrameContainer_AIMwindow").style.display = "none"
            //document.getElementById("AIMFrameContainer_AIMwindow").parentNode.removeChild(document.getElementById("AIMFrameContainer_AIMwindow"));
        } else {
            clearTimeout(oTimeout);
            var oTimeout = setTimeout(AIM.core.watchAuthRequest, 500);
        }
    },

    /**
    *	Sends a request to the host, i.e, an instant message, status update, etc.
    *	@param { Object } transactionObject An object defined by the AIM.transactions.* methods with properties required by the transaction
    */
    requestData: function(transactionObject) {
        var len = AIM.core.AIMData.length;
        transactionObject.timestamp = Date.parse(new Date());
        AIM.core.AIMData[len] = {};
        AIM.core.AIMData[len].oScript = document.createElement("script");
        AIM.core.AIMData[len].oScript.setAttribute("id", "AIMBuddyList-AIMData-" + len);
        AIM.core.AIMData[len].oScript.setAttribute("type", "text/javascript");
        AIM.core.AIMData[len].objData = transactionObject;
        if (transactionObject.dataURI.indexOf("?") == -1) {
            transactionObject.dataURI += "?r=" + len + "&nocache=" + Date.parse(new Date());
        } else {
            transactionObject.dataURI += "&r=" + len + "&nocache=" + Date.parse(new Date());
        }
        AIM.core.debug("requestData: " + transactionObject.dataURI);
        AIM.core.AIMData[len].oScript.setAttribute("src", transactionObject.dataURI);
        document.getElementsByTagName("head")[0].appendChild(AIM.core.AIMData[len].oScript);
    },

    /**
    *	Accepts all the incoming responses that result from requestData and routes them to the appropriate callback(s)
    *	@param { Object } json The JSON response from the host.
    */
    acceptData: function(json) {
        var requestId = parseInt(json.response.requestId);
        var code = parseInt(json.response.statusCode);
        if (code != 200) {
            AIM.core.debug("AIM.core.acceptData: Response Error! Code is " + code + "(" + AIM.params.text.errors.serverErrors[code] + "), transaction was " + AIM.core.AIMData[requestId].objData.type);
            if (code == 401) {
                var t = AIM.core.AIMData[requestId].objData.type;
                // only these transactions expect a 401 - if we get it on any other, kill the session
                if (t != "getToken" && t != "startSession" && t != "endSession") {
                    AIM.params.sessionId = null;
                    AIM.params.token = null;
                    AIM.transactions.endSession();
                    AIM.transactions.getToken(AIM.core.subscriptions);
                }
            }
        }
        try {
            AIM.core.debug("<b>AIM.core.acceptData:</b><br />" + json.response.toSource());
        } catch (err) { }
        var type = AIM.core.AIMData[requestId].objData.type;
        fn = eval("AIM.params.callbacks." + type);
        var i = fn.length;
        while (i-- > 0) {
            try {
                eval(fn[i] + "(json)");
            } catch (err) {
                AIM.core.debug("AIM.core.acceptData: Callback error! " + err.message + " (line " + err.line + ")")
            }
        }
        try {
            if (AIM.core.AIMData[requestId]) {
                if (AIM.core.AIMData[requestId].oScript) {
                    // Following line will cause IE to crash on a reload of the page and a relaunch of the app...go figure.
                    if (!AIM.params.MSIE) AIM.core.AIMData[requestId].oScript.parentNode.removeChild(AIM.core.AIMData[requestId].oScript);
                }
            }
        } catch (err) {
            AIM.core.debug("AIM.core.acceptData: Unable to remove AIM.core.AIMData[" + requestId + "] -- " + err.message);
        }
    },
    /**
    *	Listens for event updates (i.e., a buddy signs off) from the host and routes to the appropriate callback(s)
    *	@param { Object } json The JSON response from the host.
    */
    listen: function(json) {
        if (!json.response.data) return;
        //if(json.response.data.events.length == 0) return;
        AIM.core.destroyListenerObject(false);
        AIM.params.listenerURI = json.response.data.fetchBaseURL + "&f=json&c=AIM%2Ecore%2Elisten&timeout=" + AIM.params.REQUEST_TIMEOUT;
        if (json.response.data.events) {
            try {
                if (json.response.data.events.length > 0) AIM.core.debug("<b>AIM.core.listen:</b><br />" + json.response.data.toSource());
            } catch (err) { }
            if (json.response.statusCode == 200) {
                json.response.data.events = json.response.data.events.reverse();
                var i = json.response.data.events.length;
                while (i-- > 0) {
                    AIM.core.debug("<b>AIM.core.listen</b>:" + json.response.data.events[i].type);
                    fn = eval("AIM.params.callbacks.listener." + json.response.data.events[i].type);
                    var j = fn.length;
                    while (j-- > 0) {
                        try {
                            var oResponse = json.response.data.events[i].eventData;
                            eval(fn[j] + "(oResponse)");
                        } catch (err) {
                            AIM.core.debug("AIM.core.listen: Callback Error! " + err.message + " (line " + err.line + ")");
                        }
                    }
                }
            } else {
                // kill the session on a non 200 listener response?
            }
        }
        AIM.core.requestInterval = setTimeout("AIM.core.destroyListenerObject(true)", json.response.data.timeToNextFetch);
    },

    /**
    *	Creates a script element that "listens" for event updates from the host.
    */
    createListenerObject: function() {
        clearTimeout(AIM.core.requestInterval);
        AIM.core.destroyListenerObject(false);
        var oListener = document.createElement("script");
        oListener.setAttribute("type", "text/javascript");
        oListener.setAttribute("src", AIM.params.listenerURI + "&" + Date.parse(new Date()));
        oListener.setAttribute("id", "AIMListener");
        document.getElementsByTagName("head")[0].appendChild(oListener);
    },

    /**
    *	Destroys the data container object that houses the script element that made the request and all data associated with it.
    *	@param { Variant } objIndex The index in the AIM.core.AIMData array that corresponds to the data to be destroyed. This is generally the requestId property of the JSON response
    */
    destroyDataObject: function(objIndex) {
        return; // this function causing FF to crash all of a sudden...I hate teh intarwebs
        try {
            if (AIM.core.AIMData[objIndex]) {
                if (AIM.core.AIMData[objIndex].oScript) AIM.core.AIMData[objIndex].oScript.parentNode.removeChild(AIM.core.AIMData[objIndex].oScript);
            }
        } catch (err) { }
        AIM.core.AIMData[objIndex] = null;
    },

    /**
    *	Destroys the listener script object, and creates a new one if createNew is true.
    *	@param { Boolean } createNew Creates a new listener if true.
    */
    destroyListenerObject: function(createNew) {
        clearInterval(AIM.core.requestInterval);
        if (document.getElementById("AIMListener")) document.getElementById("AIMListener").parentNode.removeChild(document.getElementById("AIMListener"));
        if (createNew) AIM.core.createListenerObject();
    },

    /**
    *	Adds a callback to the callback object
    *	@param { Array } callbackObject The array that contains the callback functions for the event
    *	@param {String } newCallBack The name of the function to be called.
    */
    addCallback: function(callbackObject, newCallback) {
        callbackObject.push(newCallback);
    },

    /**
    *	Removes a callback from the specified callback array
    *	@param { Array } callbackObject The array that contains the callback function to be removed
    *	@param { String } oldCallback The callback to be removed
    */
    removeCallback: function(callbackObject, oldCallback) {
        callbackObject.splice(callbackObject.indexOf(oldCallback));
    },

    /**
    *	General debug function for the application. Appends a DIV to the body element with an id of "AIMDebugger" and writes out debug data if the DEBUG param is true.
    *	@param { String } str The string to be written out to the debug div.
    */
    debug: function(str) {
        if (!AIM.params.DEBUG) return;
        if (!document.getElementById("AIMDebugger")) {
            var dbg = document.getElementsByTagName("body")[0].appendChild(document.createElement("div"));
            dbg.setAttribute("id", "AIMDebugger");
        }
        document.getElementById("AIMDebugger").innerHTML = "<p><span style=\"color:green;\">" + new Date() + "(" + Date.parse(new Date()) + ")</span><br />" + str + "</p>" + document.getElementById("AIMDebugger").innerHTML;
    }
}

/**
*	Callback object contains all the functions that AIM.core.acceptData will call upon receiving a host response
*	for the given event type.
*/
AIM.callbacks = {
    /**
    *	The callback for a buddyList event
    *	@param { Object } json The json response from the host
    */
    getBuddyList: function(json) {
        var i = response.events.length;
        while (i-- > 0) {
            if ("getBuddyList" in response.events[i]) {
                var rIndex = i;
                break;
            }
        }
        AIM.ui.createBuddyList(response.events[rIndex].getBuddyList);
    },

    /**
    *	Called when an endSession event is received. Destroys IM windows, resets session data and cleans up.
    *	@param { Object } json The JSON response from the host
    */
    endSession: function(json) {
        try {
            if (document.getElementById("AIMBuddyList")) document.getElementById("AIMBuddyList").parentNode.removeChild(document.getElementById("AIMBuddyList"));
        } catch (err) {
            AIM.core.debug("AIM.callbacks.endSession: " + err.message);
        }
        AIM.core.destroyDataObject(json.response.requestId);
        AIM.core.destroyListenerObject(false);
        AIM.ui.destroyAllIMWindows();
        AIM.core.activeSession = false;
        AIM.util.cleanUp();
    },

    /**
    *	Requests a new session if a token is granted, otherwise pops the authorization window so the user can log in.
    *	@param { Object } json The JSON response from the host
    */
    getToken: function(json) {
        if (json.response.statusCode == 200) {
            //AIM.params.user = json.response.data.token.loginId;
            AIM.params.token = json.response.data.token.a;
            AIM.transactions.startSession(AIM.core.AIMData[json.response.requestId].objData.eventList);
        } else if (json.response.statusCode == 450 || json.response.statusCode == 401 || json.response.statusCode == 330) {
            AIM.core.createAuthWindow(json.response.data.redirectURL + "?k=" + AIM.params.wimKey);
        } else {
            alert(AIM.params.text.errors.serverErrors[json.response.statusCode]);
        }
        // for some reason the following line will crash IE6.
        //AIM.core.destroyDataObject(json.response.requestId);
    },

    /**
    *	Starts a new session on a succesfull startSession request. Creates the listener object for the session.
    *	@param { Object } json The JSON response from the host.
    */
    startSession: function(json) {
        if (json.response.statusCode == 200) {
            //if(AIM.params.user == "undefined") 
            AIM.params.user = json.response.data.myInfo.displayId;
            AIM.params.sessionId = json.response.data.aimsid;
            AIM.params.listenerURI = json.response.data.fetchBaseURL + "&f=json&c=AIM%2Ecore%2Elisten&timeout=" + AIM.params.REQUEST_TIMEOUT;
            AIM.core.destroyListenerObject(true);
            AIM.core.activeSession = true;
        } else if (json.response.statusCode == 450) {
            AIM.core.createAuthWindow(json.response.data.redirectURL + "&k=" + AIM.params.wimKey);
        } else if (json.response.statusCode == 451) {
            AIM.transactions.endSession();
            return alert(AIM.params.text.permissionDenied);
        } else {
            AIM.core.debug("Unable to start a session. Code is " + json.response.statusCode);
            alert(AIM.params.text.startSessionFailed);
        }
        AIM.core.destroyDataObject(json.response.requestId);
    },

    /**
    *	Called when the session ends, kills the buddy list widget
    */
    sessionEnded: function() {
        if (document.getElementById("AIMBuddyList")) {
            AIM.widgets.buddyList.kill();
        }
    },

    /**
    *	Callback for sendTextIM. Populates the message window upon success, or prompts the user for permission if needed.
    *	@param { Object } json The JSON response from the host.
    */
    sendTextIM: function(json) {
        if (json.response.statusCode != 450) {
            AIM.ui.populateMessageWindow(json);
            AIM.core.destroyDataObject(json.response.requestId);
        } else if (json.response.statusCode == 450) {
            AIM.core.createAuthWindow(json.response.data.redirectURL + "&k=" + AIM.params.wimKey);
            var winID = decodeURIComponent(AIM.core.AIMData[json.response.requestId].objData.to);
            if (winID.indexOf("+") == 0) winID = winID.replace(/\+/, "SMS");
            AIM.core.debug("AIM.callbacks.sendTextIM: winID == " + winID);
            AIM.core.destroyListenerObject(false);
            AIM.core.pendingTransaction = {
                msg: decodeURIComponent(AIM.core.AIMData[json.response.requestId].objData.msg),
                to: winID,
                type: "textIM"
            }
        } else if (json.response.statusCode == 451) {
            return alert(AIM.params.text.permissionDenied);
        }
    },

    sendDataIM: function(json) {

    },

    acceptDataIM: function(json) {

    },

    getBuddyInfo: function(response) {
        AIM.ui.showBuddyInfo(response.data);
    },

    /**
    *	Updates the UI after the user changes their online status.
    *	@param { Object } json The json response from the host.
    */
    setState: function(json) {
        if (json.response.statusCode == 450) {
            AIM.core.pendingTransaction = {
                type: "setState"
            }
            AIM.core.createAuthWindow(json.response.data.redirectURL + "&k=" + AIM.params.wimKey);
            return;
        }
        var oElements = AIM.util.getElementsByClassName(document.getElementsByTagName("body")[0], "span", "AIMBuddyListAvailabilityMenuActionPoint");
        switch (AIM.util.currentState) {
            case 0:
                var remover = ["Available", "Invisible"];
                var text = AIM.params.text.availabilityMenuAwayText;
                var adder = "Away";
                break;
            case 1:
                var remover = ["Away", "Invisible"];
                var text = AIM.params.text.availabilityMenuAvailableText;
                var adder = "Available";
                AIM.util.resetUserNotified();
                break;
            case 2:
                var remover = ["Away", "Available"];
                var text = AIM.params.text.availabilityMenuInvisibleText;
                var adder = "Invisible";
                break;
        }
        var i = oElements.length;
        while (i-- > 0) {
            oElements[i].innerHTML = text;
            AIM.util.removeClass(oElements[i].parentNode, "AIMBuddyListAvailabilityMenu" + remover[0]);
            AIM.util.removeClass(oElements[i].parentNode, "AIMBuddyListAvailabilityMenu" + remover[1]);
            AIM.util.addClass(oElements[i].parentNode, "AIMBuddyListAvailabilityMenu" + adder);
        }
        AIM.core.destroyDataObject(json.response.requestId);
    },

    /**
    *	Simply a callback to destroy the typing status data object when the status returns.
    *	@param { Object } json The JSON response from the host.
    */
    typingStatus: function(json) {
        AIM.core.destroyDataObject(json.response.requestId);
    },

    /**
    *	Updates the UI with data from getPresenceInfo
    *	@param { Object } json The JSON response from the host
    */
    getPresenceInfo: function(json) {
        AIM.ui.updatePresenceWidgets(json.response.data.users);
        AIM.core.destroyDataObject(json.response.requestId);
    }
}

/**
*	User interface helper methods.
*/
AIM.ui = {
    storedBuddyInfo: [],
    winZIndex: 10000,
    activeWindow: null,

    /**
    *	Creates a window that allows the user to define a custom away message.
    */
    createAwayMessage: function() {
        if (!document.getElementById("AIMBuddyListAwayBox_AIMwindow")) {
            var awayBox = AIM.ui.createWindowFrame("AIMBuddyListAwayBox", "AIMBuddyListAwayBox", AIM.params.text.awayMessageWindowTitle);
            var p = document.createElement("p");
            p.appendChild(document.createTextNode(AIM.params.text.awayMessageWindowInstructions));

            var txt = document.createElement("input");
            txt.setAttribute("type", "text");
            txt.setAttribute("maxlength", "1024");
            txt.setAttribute("id", "AIMBuddyListAwayMessageInput");
            txt.className = "AIMBuddyListIMWindowTextInput";
            txt.onkeyup = function(e) {
                keyCode = window.event ? event.keyCode : e.keyCode;
                if (keyCode == 13) {
                    AIM.transactions.setAwayMessage(this.value);
                    this.parentNode.style.display = "none";
                }
            }
            var btn = document.createElement("button");
            btn.setAttribute("type", "button");
            btn.appendChild(document.createTextNode("Ok"));
            AIM.util.addEvent(btn, function() { AIM.transactions.setAwayMessage(document.getElementById("AIMBuddyListAwayMessageInput").value); this.parentNode.style.display = "none"; }, "click");

            btn.className = "AIMBuddyListIMWindowButton";

            awayBox.appendChild(p);
            awayBox.appendChild(txt);
            awayBox.appendChild(btn);
            document.getElementById("AIMBuddyListContainer").appendChild(awayBox);
            //awayBox.style.top = ((AIM.util.getScrollOffset(1) + document.getElementById("AIMBuddyListContainer").offsetTop)) + "px";
            //awayBox.style.left = document.getElementById("AIMBuddyListContainer").offsetWidth + 15 + "px";
        }
        var ab = document.getElementById("AIMBuddyListAwayBox_AIMwindow");
        ab.style.display = "block";
        ab.style.zIndex = "19000";
        ab.style.top = ((AIM.util.getScrollOffset(1) + document.getElementById("AIMBuddyListContainer").offsetTop)) + "px";
        ab.style.left = document.getElementById("AIMBuddyListContainer").offsetWidth + 15 + "px";
    },

    /**
    *	Renders the buddy list
    *	@param { Object } data The JSON response from the host.
    */
    createBuddyList: function(data) {
        var createStart = Date.parse(new Date());
        if (document.getElementById("AIMBuddyList")) document.getElementById("AIMBuddyList").parentNode.removeChild(document.getElementById("AIMBuddyList"));

        var ul = document.createElement("ul");
        ul.setAttribute("id", "AIMBuddyList");

        var br = document.createElement("div");
        br.className = "AIMBuddyListBranding";
        br.setAttribute("id", "AIMBuddyListBrandingArea");
        br.appendChild(document.createTextNode(AIM.params.text.brandingText));
        if (AIM.params.BUDDY_LIST_DRAG) {
            if (document.all) {
                br.onmousedown = function(e) { this.parentNode.style.position = "absolute"; AIM.util.captureOffset(); }
            } else {
                AIM.util.addEvent(br, AIM.util.captureOffset, "mousedown");
                AIM.util.addEvent(br, function() { this.parentNode.style.position = "absolute"; }, "mousedown");
            }
            AIM.util.addEvent(br, function() { AIM.util.mDown = false; }, "mouseup");
        }

        //ul.appendChild(br);
        if (!document.getElementById("AIMBuddyListBrandingArea")) document.getElementById("AIMBuddyListContainer").appendChild(br);

        var sp = document.createElement("div");
        sp.className = "AIMBuddyListUserScreenName";
        sp.appendChild(document.createTextNode(AIM.params.user));
        ul.appendChild(sp);

        if (AIM.params.CREATE_AVAILABILITY_MENU_BL) {
            //document.getElementById("AIMBuddyListContainer").appendChild(AIM.ui.createAvailabilityMenu());
            ul.appendChild(AIM.ui.createAvailabilityMenu());
        }
        var groupings = data.groups;
        groupings = groupings.reverse();
        var glen = groupings.length;

        var headerState = AIM.util.cookie.get("headerState");
        if (headerState == null) {
            var k = 0; headerState = "";
            while (k < glen) {
                headerState += "1";
                k++;
            }
            AIM.util.cookie.set("headerState", headerState, true);
        }
        headerState = headerState.split("");

        var i = glen;
        while (i-- > 0) {
            var li = document.createElement("li");
            var h2 = document.createElement("h2");

            h2.appendChild(document.createTextNode(groupings[i].name));
            h2.xindex = i;
            AIM.util.addEvent(h2, function() { AIM.ui.setHeaderState(this); }, "click");
            h2.className = "AIMBuddyListHeading";
            li.appendChild(h2);

            var sul = document.createElement("ul");

            if (parseInt(headerState[i]) == 1 || typeof (headerState[i]) == "undefined") {
                sul.style.display = "block";
            } else {
                sul.style.display = "none";
            }
            var bClassName = groupings[i].name;
            bClassName = bClassName.replace(/ /g, "");
            sul.className = "AIMBuddyListGroup " + bClassName;
            sul.className += i % 2 ? " AIMBuddyListGroupEven" : " AIMBuddyListGroupOdd";

            var buddies = groupings[i].buddies;

            var blen = buddies ? buddies.length : 0;
            if (blen) buddies = buddies.reverse();
            var j = blen;
            while (j-- > 0) {
                var sli = document.createElement("li");
                var oGroupings = groupings[i].name.replace(/ /g, "_");
                sli.setAttribute("id", oGroupings + "_" + buddies[j].aimId);
                sli.setAttribute("wim_id", buddies[j].aimId);
                sli.setAttribute("wim_last_update", 0);
                sli.setAttribute("wim_timestamp", Date.parse(new Date()) / 1000);
                sli.className = "buddy " + buddies[j].state + " " + buddies[j].aimId;
                sli.appendChild(document.createTextNode(buddies[j].displayId));
                if (!AIM.params.SHOW_OFFLINE && buddies[j].state == "offline") sli.style.display = "none";
                sul.appendChild(sli);
                buddies[j].timestamp = Date.parse(new Date()) / 1000;
                AIM.ui.storedBuddyInfo[buddies[j].aimId] = buddies[j];

                AIM.util.addEvent(sli, AIM.eventHandlers.handleMouseover, "mouseover");
                AIM.util.addEvent(sli, AIM.eventHandlers.handleMouseout, "mouseout");
                AIM.util.addEvent(sli, AIM.eventHandlers.handleClick, "click");
            }
            li.appendChild(sul);
            ul.appendChild(li);
        }
        document.getElementById("AIMBuddyListContainer").appendChild(ul);
        AIM.ui.zebraStripeList(AIM.util.getAIMIDCollection(document.getElementById("AIMBuddyList").parentNode));
        document.getElementById("AIMBuddyList").style.display = "block";
        document.getElementById("AIMBuddyListContainer").style.display = "block";
        var createTotal = Date.parse(new Date()) - createStart;
        AIM.core.debug("AIM.ui.createBuddyList: creation took " + createTotal + "ms");
    },

    /**
    *	Creates the availability menu that contains things like "Set Away Message", "Send an IM" etc.
    *	Menu items and actions are defined in the AIM.params.text.availabilityMenu construct.
    */
    createAvailabilityMenu: function() {
        var avMenu = document.createElement("div");
        avMenu.className = "AIMBuddyListAvailabilityMenu";
        AIM.util.addClass(avMenu, AIM.util.currentState ? "AIMBuddyListAvailabilityMenuAvailable" : "AIMBuddyListAvailabilityMenuAway");
        var sp = document.createElement("span");
        sp.className = "AIMBuddyListAvailabilityMenuActionPoint";
        sp.appendChild(document.createTextNode(AIM.util.currentState ? AIM.params.text.availabilityMenuAvailableText : AIM.params.text.availabilityMenuAwayText));
        avMenu.appendChild(sp);

        avMenuFN = function() {
            var sm = this.getElementsByTagName("ul")[0];
            if (sm.style.display == "none") {
                sm.style.display = "block";
            } else {
                sm.style.display = "none";
            }
        }

        AIM.util.addEvent(avMenu, avMenuFN, "click");

        var avSubMenu = document.createElement("ul");
        avSubMenu.className = "AIMBuddyListAvailabilitySubMenu";
        avSubMenu.style.display = "none";

        for (var i in AIM.params.text.availabilityMenuItems) {
            if (AIM.params.text.availabilityMenuItems[i].text != null) {
                var n = document.createElement("li");
                n.className = "AIMBuddyListMenuItem";
                n.appendChild(document.createTextNode(AIM.params.text.availabilityMenuItems[i].text));
                if (AIM.params.text.availabilityMenuItems[i].cls) AIM.util.addClass(n, AIM.params.text.availabilityMenuItems[i].cls);
                n.xonclick = AIM.params.text.availabilityMenuItems[i].method;
                AIM.util.addEvent(n, AIM.eventHandlers.handleClick, "click");
            } else {
                var n = document.createElement("hr");
            }
            avSubMenu.appendChild(n);
        }
        avMenu.appendChild(avSubMenu);
        return avMenu;
    },

    /**
    *	Toggles the sound on and off.
    */
    toggleSound: function() {
        var sndMenuItems = AIM.util.getElementsByClassName(document.getElementById("AIMBuddyListContainer"), "li", "AIMBuddyListSoundToggle");
        var i = sndMenuItems.length;
        AIM.params.sound = AIM.params.sound ? false : true;
        while (i-- > 0) {
            AIM.util.removeClass(sndMenuItems[i], "AIMBuddyListSoundOff");
            AIM.util.removeClass(sndMenuItems[i], "AIMBuddyListSoundOn");
            AIM.util.addClass(sndMenuItems[i], AIM.params.sound ? "AIMBuddyListSoundOn" : "AIMBuddyListSoundOff");
        }
        AIM.util.cookie.set("ablsnd", AIM.params.sound, false);
    },

    /**
    *	Collapses or uncollapses a buddy group and sets a cookie to remember the state of the group.
    */
    setHeaderState: function(header) {
        state = header.nextSibling.style.display == "block" ? 0 : 1;
        header.nextSibling.style.display = state ? "block" : "none";
        var hState = AIM.util.cookie.get("headerState").split("");
        hState[header.xindex] = state;
        hState = hState.toString().replace(/,/g, "");
        AIM.util.cookie.set("headerState", hState, true);
    },

    /**
    *	Sets the z-index of the activeWin argument above all other IM windows
    *	@param { String } activeWin The value of the id attribute of the window whos z-index needs to be raised.
    */
    setIMWindowZIndex: function(activeWin) {
        try {
            if (activeWin.indexOf("+") == 0) activeWin = activeWin.replace(/\+/, "SMS");
            var oWin = document.getElementById(activeWin);
            AIM.ui.clearVisualNotification(activeWin);
            if (oWin.AIMTopWindow == "true") return;
            oWin.style.zIndex = AIM.ui.winZIndex;
            oWin.AIMTopWindow = "true";
            var windowCollection = AIM.ui.getIMWindows();
            var i = windowCollection.length;
            while (i-- > 0) {
                if (windowCollection[i].getAttribute("id") != activeWin) {
                    windowCollection[i].style.zIndex = AIM.ui.winZIndex - 1;
                    windowCollection[i].AIMTopWindow = "false";
                }
            }
        } catch (err) {
            AIM.core.debug("AIM.ui.setIMWindowZIndex: " + err.message);
        }
    },

    /**
    *	Updates the UI to reflect the typing status of a buddy
    *	@param { Object } resonse The JSON response from the host.
    */
    updateTypingStatus: function(response) {
        return;
        if (!document.getElementById(response.aimId + "_typingStatus") && response.event != "none") return;
        var winID = response.aimId;
        if (winID.indexOf("+") == 0) winID = winID.replace(/\+/, "SMS");
        var obj = document.getElementById(winID + "_typingStatus");
        switch (response.typingStatus) {
            case "typing":
                obj.innerHTML = AIM.params.text.userTyping;
                obj.className = "AIMBuddyListTypingStatusTyping";
                break;
            case "typed":
                obj.innerHTML = AIM.params.text.userTyped;
                obj.className = "AIMBuddyListTypingStatusTyped";
                break;
            case "none":
                obj.innerHTML = AIM.params.text.userStoppedTyping;
                obj.className = "AIMBuddyListTypingStatusStoppedTyping";
                break;
        }
    },

    /**
    *	Updates the Buddy List UI when users sign on/off, go away, etc.
    *	@param { Object } response The JSON response from the host.
    */
    updateBuddyList: function(response) {
        var aimIds = AIM.util.getElementsByAIMID(response.aimId, document.getElementById("AIMBuddyListContainer"));
        var i = aimIds.length;
        while (i-- > 0) {
            AIM.util.removeClass(aimIds[i], "online");
            AIM.util.removeClass(aimIds[i], "idle");
            AIM.util.removeClass(aimIds[i], "away");
            AIM.util.removeClass(aimIds[i], "mobile");
            AIM.util.removeClass(aimIds[i], "offline");
            AIM.util.addClass(aimIds[i], response.state);

            if (response.state != "offline") {
                aimIds[i].style.display = "block";
                aimIds[i].setAttribute("wim_timestamp", Date.parse(new Date()) / 1000);
            } else {
                aimIds[i].style.display = AIM.params.SHOW_OFFLINE ? "block" : "none";
            }

            if (response.displayId != aimIds[i].innerHTML.trim()) aimIds[i].innerHTML = response.displayId;
        }
        response.timestamp = Date.parse(new Date()) / 1000;
        AIM.ui.storedBuddyInfo[response.aimId] = response;
        AIM.ui.zebraStripeList(AIM.util.getAIMIDCollection(document.getElementById("AIMBuddyListContainer")));
    },

    /**
    *	Updates presence widgets DOM-wide to reflect their owners current status
    *	@param { Array } users The id's of the users who's status we're updating.
    */
    updatePresenceWidgets: function(users) {
        var i = users.length;
        while (i-- > 0) {
            var oElements = AIM.util.getElementsByClassName(document.body, "*", users[i].aimId);
            AIM.ui.storedBuddyInfo[users[i].aimId] = users[i];
            var j = oElements.length;
            while (j-- > 0) {
                AIM.util.removeClass(oElements[j], "AIMPresenceWidget_online");
                AIM.util.removeClass(oElements[j], "AIMPresenceWidget_offline");
                AIM.util.removeClass(oElements[j], "AIMPresenceWidget_away");
                AIM.util.addClass(oElements[j], "AIMPresenceWidget_" + users[i].state);
                //oElements[j].setAttribute("title",users[i].displayId + " is " + users[i].state);
                oElements[j].setAttribute("wim_id", users[i].aimId);
                if (users[i].state != "offline") {
                    AIM.util.addEvent(oElements[j], AIM.eventHandlers.handleMouseover, "mouseover");
                } else {
                    oElements[j].setAttribute("title", users[i].displayId + " is not available right now.");
                }
            }
        }
        AIM.ui.prepBuddyInfo(document.body);
    },

    /**
    *	Prompts a dialog to allow the user to enter any ID they wish to send an IM to.
    *	@param { String } aimId The id of the user to send the IM to.
    */
    aimIdPrompt: function(aimId) {
        if (!document.getElementById("AIMIDPrompt_AIMwindow")) {
            var win = AIM.ui.createWindowFrame("AIMIDPrompt", "AIMBuddyListAwayBox", "Send IM");
            var p = document.createElement("p");
            p.appendChild(document.createTextNode(AIM.params.text.aimIdPromptMessage));

            var txt = document.createElement("input");
            txt.setAttribute("type", "text");
            txt.setAttribute("maxlength", "96");
            txt.setAttribute("id", "AIMIDInput");
            txt.className = "AIMBuddyListIMWindowTextInput";
            txt.value = aimId
            txt.onkeyup = function(e) {
                var keyCode = window.event ? event.keyCode : e.keyCode;
                if (keyCode == 13) {
                    var aimId = this.value.trim();
                    if (aimId != "") AIM.ui.createIMWindow(aimId.toLowerCase());
                    document.getElementById("AIMIDPrompt_AIMwindow").style.display = "none";
                }
            }

            var btn = document.createElement("button");
            btn.setAttribute("type", "button");
            btn.appendChild(document.createTextNode("Ok"));
            var fn = function() {
                var aimId = document.getElementById("AIMIDInput").value.trim();
                if (aimId != "") AIM.ui.createIMWindow(aimId.toLowerCase());
                document.getElementById("AIMIDPrompt_AIMwindow").style.display = "none";
            }
            AIM.util.addEvent(btn, fn, "click");

            win.appendChild(p);
            win.appendChild(txt);
            win.appendChild(btn);
            document.getElementById("AIMBuddyListContainer").appendChild(win);
            //win.style.left = (document.getElementsByTagName("body")[0].offsetWidth - win.offsetWidth)/2 + "px";
            //win.style.top = "10px";
            btn.className = "AIMBuddyListIMWindowButton";

        }
        var ab = document.getElementById("AIMIDPrompt_AIMwindow");
        ab.style.zIndex = "19000";
        ab.style.top = ((AIM.util.getScrollOffset(1) + document.getElementById("AIMBuddyListContainer").offsetTop)) + "px";
        ab.style.left = document.getElementById("AIMBuddyListContainer").offsetWidth + 15 + "px";
        ab.style.display = "block";
    },

    /**
    *	Accepts an incoming instant mesage and routes it to the appropriate window.
    *	@param { Object } response The JSON response from the host.
    */
    acceptIncomingMessage: function(response) {
        var aimId = response.source.aimId;
        var winID = aimId;
        if (winID.indexOf("+") == 0) winID = winID.replace(/\+/, "SMS");
        AIM.ui.createIMWindow(aimId);
        AIM.ui.populateIncomingMessageWindow(response);
        document.getElementById(winID + "_typingStatus").innerHTML = "";
        if (AIM.params.VISUAL_NOTIFICATION) {
            document.title = "IM received from " + aimId;
            if (!AIM.util.visualNotificationTimer[winID + "_AIMwindow"]) AIM.ui.showVisualNotification(winID + "_AIMwindow");
        }

        if (!AIM.util.currentState && !AIM.util.userNotified[aimId]) {
            var msg = AIM.params.text.awayMessage;
            try {
                msg = decodeURIComponent(msg);
            } catch (err) { }
            AIM.transactions.sendTextIM(aimId, AIM.params.text.autoReplyNotice + " " + msg);
            AIM.util.userNotified[aimId] = true;
        }
    },

    /**
    *	Gives access to all of the currently open IM windows
    *	@returns An array containing object references to all of the windows.
    *	@type Array
    */
    getIMWindows: function() {
        var winArray = document.getElementById("AIMBuddyListContainer").getElementsByTagName("div");
        var collection = [];
        var i = winArray.length;
        while (i-- > 0) if (winArray[i].getAttribute("id")) if (winArray[i].getAttribute("id").indexOf("_AIMwindow") != -1) collection.push(winArray[i]);
        return collection;
    },

    /**
    *	Iterates through the buddy list, applying even/odd classes to elements in the buddy list
    *	@param { Array } objectCollection An array of objects to loop over an apply the classes to.
    */
    zebraStripeList: function(objectCollection) {
        var i = objectCollection.length;
        var tracker = i;
        while (i-- > 0) {
            if (objectCollection[i].style.display != "none") {
                var obj = objectCollection[i];
                AIM.util.removeClass(obj, "even");
                AIM.util.removeClass(obj, "odd");
                AIM.util.addClass(obj, tracker % 2 ? "even" : "odd");
                tracker--;
            }
        }
    },

    /**
    *	Called on an interval that sets the title bar of the IM window to On/Off to act as a notification that an IM has been recieved.
    *	@param { String } windowId The id of the window to apply the notification to.
    */
    showVisualNotification: function(windowId) {
        var win = document.getElementById(windowId);
        if (!win) return;
        var h2 = win.getElementsByTagName("h2")[0];
        if (h2.className.indexOf("AIMBuddyListIMWindowNotifyOff") > -1) {
            AIM.util.removeClass(h2, "AIMBuddyListIMWindowNotifyOff");
            AIM.util.addClass(h2, "AIMBuddyListIMWindowNotifyOn");
        } else {
            AIM.util.removeClass(h2, "AIMBuddyListIMWindowNotifyOn");
            AIM.util.addClass(h2, "AIMBuddyListIMWindowNotifyOff");
        }

        var fn = function() { AIM.ui.showVisualNotification(windowId); }
        AIM.util.visualNotificationTimer[windowId] = setTimeout(fn, AIM.params.NOTIFICATION_THROB);
    },

    /**
    *	Clears the running notification interval and sets the window title bar back to normal.
    *	@param { String } windowId The id of the window to clear the notification from.
    */
    clearVisualNotification: function(windowId) {
        var win = document.getElementById(windowId);
        if (win) {
            var h2 = win.getElementsByTagName("h2")[0];
            h2.className = "AIMBuddyListWindowTitleBar";
        }
        clearTimeout(AIM.util.visualNotificationTimer[windowId]);
        document.title = AIM.params.DOCUMENT_TITLE;
        AIM.util.visualNotificationTimer[windowId] = null;
    },

    /**
    *	Populates the hovering element that displays buddy information like Away Message, Profile, etc.
    *	@param { Object } buddyInfo Contains all of the relevant data about the user. Comes from a presence update and initial buddy list event update.
    */
    showBuddyInfo: function(buddyInfo) {
        var AIMInfo = document.getElementById("AIMBuddyListBuddyInfo");
        if (!AIMInfo) return;
        if (!buddyInfo.icon) {
            if (buddyInfo.buddyIcon) buddyInfo.icon = buddyInfo.buddyIcon;
        }
        if (!buddyInfo.icon && !buddyInfo.buddyIcon) buddyInfo.icon = AIM.params.DEFAULT_ICON;
        if (buddyInfo.state == "offline") {
            var mHTML = "<table cellpadding=\"2\" cellspacing=\"0\"><tr><td>" + buddyInfo.displayId + " is not currently signed on.</td></tr></table>"
        } else {
            if (buddyInfo.state == "mobile") {
                var onlineTime = "";
            } else {
                var elapsedSinceLaunch = (Date.parse(new Date()) / 1000) - buddyInfo.timestamp;
                var oTime = buddyInfo.onlineTime + elapsedSinceLaunch;
                var onlineTime = "<br />Online For: " + AIM.util.elapsedFromSeconds(oTime);
            }

            if (buddyInfo.idleTime) {
                //var elapsedSinceLaunch = (Date.parse(new Date()) / 1000) - buddyInfo.timestamp;
                //var oTime = buddyInfo.idleTime + elapsedSinceLaunch;
                if (buddyInfo.idleTime >= 60) {
                    var oTime = Math.floor(buddyInfo.idleTime / 60) + " hours.";
                } else {
                    var oTime = buddyInfo.idleTime + " minutes.";
                }
                var oIdleTime = "<br />Idle For: " + oTime;
            } else {
                var oIdleTime = "";
            }

            var mHTML = "<table cellpadding=\"2\" cellspacing=\"0\"><tr><td><img src=\"" + buddyInfo.icon + "\" width=\"48\" height=\"48\" alt=\"Buddy Icon\" /></td>"
            mHTML += "<td><b>" + buddyInfo.displayId + "</b>" + onlineTime + oIdleTime + "</td></tr>";

            if (buddyInfo.profileMsg) mHTML += "<tr><td><b>Profile</b></td><td><p>" + buddyInfo.profileMsg + "</p></td></tr>";
            mHTML += "</table>";
            if (buddyInfo.awayMsg) {
                var elapsedSinceLaunch = (Date.parse(new Date()) / 1000) - buddyInfo.timestamp;
                var oTime = buddyInfo.awayTime + elapsedSinceLaunch;
                var oAwayTime = AIM.util.elapsedFromSeconds(oTime);
                var msg = buddyInfo.awayMsg;
                try {
                    //msg = unescape(msg);
                    msg = decodeURIComponent(msg);
                } catch (err) {
                    //msg = unescape(msg);
                }
                mHTML += "<table cellpadding=\"0\" cellspacing=\"0\" class=\"away\"><tr valign=\"top\"><td width=\"33%\"><b>Away Message:</b></td><td>" + msg + "</td></tr><tr><td><b>Away For:</b></td><td>" + oAwayTime + "</td></tr></table>";
            }
        }
        AIMInfo.innerHTML = mHTML;
        AIMInfo.style.display = "block";
    },

    /**
    *	Populates the correct window with outgoing IMs once the host has responded after the IM is sent.
    *	@param { Object } json The JSON response from the host.
    */
    populateMessageWindow: function(json) {
        var requestId = parseInt(json.response.requestId);
        var winSN = AIM.core.AIMData[requestId].objData.to;
        var winID = decodeURIComponent(winSN);
        if (winID.indexOf("+") == 0) winID = winID.replace(/\+/, "SMS");
        var msg = AIM.core.AIMData[requestId].objData.msg;
        try {
            //msg = unescape(msg);
            msg = decodeURIComponent(msg);
        } catch (err) {
            AIM.core.debug("AIM.ui.populateMessageWindow:" + err.message);
        }

        oSN = AIM.params.user;

        msg = AIM.util.formatMessage(msg);
        msg = AIM.ui.addEmoticons(msg);

        var msgWin = document.getElementById("AIMTextArea_" + winID)
        var xHTML = msgWin.innerHTML;
        oSN == AIM.params.user ? clsName = "AIMBuddyListUser" : clsName = "AIMBuddyListUserBuddy";
        var ts = AIM.params.SHOW_TIMESTAMP ? AIM.util.formatTimeStamp(new Date()) : "";

        xHTML += "<p class=\"even\"><b class=\"" + clsName + "\">" + oSN + ": </b><span class=\"AIMBuddyListTimeStamp\">" + ts + "</span> " + msg + "</p>";
        if (json.response.statusCode != 200) {
            msg = AIM.params.text.errors.serverErrors[json.response.statusCode]; // + "(" + json.response.statusCode + ")";
            var sysmsg = "<p class=\"even\"><b class=\"AIMBuddyListUserBuddy\">System Message: </b><span class=\"AIMBuddyListTimeStamp\">" + ts + "</span> " + msg + "</p>";
            xHTML += sysmsg;
        }
        msgWin.innerHTML = xHTML;
        msgWin.scrollTop = msgWin.scrollHeight;

        AIM.core.destroyDataObject(requestId);
    },

    /**
    *	Replaces emoticons with images, i.e., :) becomes smile.png
    *	@param { String } txt The text to have the regexp permformed on
    *	@return { String} the modifed string
    */
    addEmoticons: function(txt) {
        if (AIM.params.USE_EMOTICONS) {
            for (var i in AIM.params.emoticons) {
                var r = eval("/(" + i + ")/gi");
                if (txt.match(r)) txt = txt.replace(r, "<img src=\"" + baseResourceURI + "emoticons/" + AIM.params.emoticons[i] + ".png\" alt=\"(" + AIM.params.emoticons[i] + ")\" />");
            }
        }
        return txt;
    },

    /**
    *	Populates the correct IM window with incoming IMs
    *	@param { Object } response The JSON object from the host. Comes via the listener.
    */
    populateIncomingMessageWindow: function(response) {
        var aimId = response.source.aimId
        var msg = response.message;
        if (msg.match(/<a ([^>]*)>([^<]*)<\/a>/g)) msg = msg.replace(/href/gi, "target=\"_blank\" href");
        if (msg.match(/<img ([^>]*)>/g)) {
            msg = msg.replace(/</g, "&lt;");
            msg = msg.replace(/>/g, "&gt;");
        }

        try {
            //msg = unescape(msg);
            msg = decodeURIComponent(msg);
        } catch (err) { }

        msg = AIM.ui.addEmoticons(msg);

        var winID = aimId;
        if (winID.indexOf("+") == 0) winID = winID.replace(/\+/, "SMS");
        var msgWin = document.getElementById("AIMTextArea_" + winID)
        var xHTML = msgWin.innerHTML;
        var ts = AIM.params.SHOW_TIMESTAMP ? AIM.util.formatTimeStamp(new Date(parseInt(response.timestamp) * 1000)) : "";

        if (msg.indexOf("<div") == 0) {
            var breaker = " style=\"display:inline;\"";
        } else {
            var breaker = "";
        }
        xHTML += "<p class=\"odd\"" + breaker + "><b class=\"AIMBuddyListUserBuddy\">" + response.source.displayId + ": </b><span class=\"AIMBuddyListTimeStamp\">" + ts + "</span> " + msg + "</p>";
        if (msg.indexOf("<div") == 0) xHTML += "<br />";
        msgWin.innerHTML = xHTML;
        if (AIM.params.sound) AIM.util.playSound("IM");
        msgWin.scrollTop = msgWin.scrollHeight;
    },

    /**
    *	Creates the basic elements required for a window, and sets up drag and title.
    *	@param { String } identifier The "id" of the window.
    *	@param { String } clsName The "class" of the window.
    *	@param { String } winTitle The title of the window.
    *	@return { HTMLObject } An DIV element styled like a window, w/o content.
    *	@type HTMLObject
    */
    createWindowFrame: function(identifier, clsName, winTitle) {
        var win = document.createElement("div");
        win.setAttribute("id", identifier + "_AIMwindow");
        win.style.zIndex = 10000;
        win.className = clsName;
        win.AIMTopWindow = "false";

        var h2 = document.createElement("h2");
        h2.appendChild(document.createTextNode(winTitle));
        AIM.util.addEvent(h2, AIM.util.captureOffset, "mousedown");
        h2.className = "AIMBuddyListWindowTitleBar";
        AIM.util.addEvent(h2, function() { AIM.util.mDown = false; AIM.util.removeClass(this.parentNode, "AIMBuddyListIMWindowDragState"); }, "mouseup");
        win.appendChild(h2);

        var clBtn = document.createElement("div");
        clBtn.className = "AIMBuddyListWindowCloseButton";
        clBtn.setAttribute("title", "Close this Window.");
        AIM.util.addEvent(clBtn, function() { AIM.ui.removeIMWindow(identifier + "_AIMwindow"); }, "click");
        h2.appendChild(clBtn);
        return win;
    },

    /**
    *	Creates an IM window for the given id
    *	@param { String} aimId The ID of the user for whom to create the window.
    */
    createIMWindow: function(aimId) {
        windowId = aimId;
        if (windowId.indexOf("+") == 0) windowId = windowId.replace(/\+/, "SMS");
        var IMWin = document.getElementById(windowId + "_AIMwindow")
        if (!IMWin) {
            var win = AIM.ui.createWindowFrame(windowId, "AIMBuddyListIMWindow", aimId);
            var txtArea = document.createElement("div");
            txtArea.className = "AIMBuddyListIMWindowTextArea";
            txtArea.setAttribute("id", "AIMTextArea_" + windowId);

            var txtInput = document.createElement("input");
            txtInput.setAttribute("type", "text");
            txtInput.className = "AIMBuddyListIMWindowTextInput";
            txtInput.setAttribute("id", "AIMTextInput_" + windowId);
            txtInput.setAttribute("wim_aimId", aimId);
            txtInput.setAttribute("maxlength", "1024");
            AIM.util.addEvent(txtInput, AIM.eventHandlers.handleKeyUp, "keyup");

            if (AIM.params.RENDER_SEND_BUTTON) {
                var okBtn = document.createElement("button");
                okBtn.setAttribute("type", "button");
                okBtn.className = "AIMBuddyListIMWindowButton";
                okBtn.setAttribute("id", "AIMBuddyListIMWindowButton_" + windowId);
                okBtn.setAttribute("wim_aimId", aimId);
                okBtn.appendChild(document.createTextNode(AIM.params.text.sendButtonText));
                AIM.util.addEvent(okBtn, AIM.eventHandlers.handleClick, "click");
            }

            var typingStatus = document.createElement("span");
            typingStatus.className = "AIMBuddyListTypingStatus";
            typingStatus.setAttribute("id", windowId + "_typingStatus");

            if (AIM.params.CREATE_AVAILABILITY_MENU_IM) win.appendChild(AIM.ui.createAvailabilityMenu());
            win.appendChild(txtArea);
            win.appendChild(txtInput);
            if (AIM.params.RENDER_SEND_BUTTON) win.appendChild(okBtn);
            win.appendChild(typingStatus);

            var y = ((AIM.util.getScrollOffset(1) + document.getElementById("AIMBuddyListContainer").offsetTop));
            var x = document.getElementById("AIMBuddyListContainer").offsetWidth + 15;
            var isOverlap = function(x, y) {
                var win = AIM.ui.getIMWindows();
                var i = win.length;
                while (i-- > 0) if (win[i].offsetLeft == x && win[i].offsetTop == y) return true
                return false;
            }

            while (isOverlap(x, y)) {
                x += 20;
                y += 20;
            }
            win.style.top = y + "px";
            win.style.left = x + "px";
            AIM.util.addEvent(win, function() { AIM.ui.setIMWindowZIndex(this.getAttribute("id")); }, "click");
            document.getElementById("AIMBuddyListContainer").appendChild(win);

        }
        document.getElementById(windowId + "_AIMwindow").style.display = "block";
        /*
        try {
        document.getElementById("AIMTextInput_" + windowId).focus();
        } catch(err) {
        AIM.core.debug("AIM.ui.createIMWindow: " + err.message);
        }*/
    },

    /**
    *	Removes an IM window from view. Sets display to none if RETAIN_WINDOW is true, removes from the DOM otherwise.
    *	@param { String } windowId The id of the window to be removed.
    */
    removeIMWindow: function(windowID) {
        var rWin = document.getElementById(windowID);
        if (AIM.params.RETAIN_WINDOW) {
            rWin.style.display = "none";
        } else {
            rWin.parentNode.removeChild(rWin);
        }
    },

    /**
    *	Removes all IM windows from view. Sets display to none if RETAIN_WINDOW is true, removes from the DOM otherwise.
    */
    removeAllIMWindows: function() {
        var rWins = AIM.ui.getIMWindows();
        var i = rWins.length;
        while (i-- > 0) {
            if (AIM.params.RETAIN_WINDOW) {
                rWins[i].style.display = "none";
            } else {
                rWins[i].parentNode.removeChild(rWins[i]);
            }
        }
    },

    /**
    *	Destroys all IM windows, removing them from the DOM. Ignores RETAIN_WINDOW
    */
    destroyAllIMWindows: function() {
        var rWins = AIM.ui.getIMWindows();
        var i = rWins.length;
        while (i-- > 0) rWins[i].parentNode.removeChild(rWins[i]);
    },

    /**
    *	Prepares the buddy info display element, placing it at the appropriate coordinates, etc.
    *	@param { Object } cObj The object that spawned the event, used to determine where to place the element for display.
    */
    prepBuddyInfo: function(cObj) {
        if (!document.getElementById("AIMBuddyListBuddyInfo")) {
            var div = document.createElement("div");
            div.setAttribute("id", "AIMBuddyListBuddyInfo");
            div.style.zIndex = "20000";
            document.getElementById("AIMBuddyListContainer").appendChild(div);
            try {
                AIM.util.addEvent(document.getElementById("AIMBuddyListContainer"), function() { if (document.getElementById("AIMBuddyListBuddyInfo")) document.getElementById("AIMBuddyListBuddyInfo").style.display = "none"; }, "mouseout");
            } catch (err) { }
        }
        try {
            AIMInfo = document.getElementById("AIMBuddyListBuddyInfo");
            //var y = (AIM.util.calculateOffset(cObj).y + (cObj.offsetHeight +25));// - document.getElementById("AIMBuddyList").scrollTop;
            var y = AIM.util.calculateOffset(cObj).y;
            if (document.getElementById("AIMBuddyList")) y -= document.getElementById("AIMBuddyList").scrollTop
            if (document.getElementById("AIMBuddyListBrandingArea")) y -= document.getElementById("AIMBuddyListBrandingArea").offsetHeight;
            AIMInfo.style.top = y + "px"; //(AIM.util.calculateOffset(cObj).y + (cObj.offsetHeight+5)) + "px";
            //AIMInfo.style.top = (cObj.offsetTop + (cObj.offsetHeight+10)) + "px";
            //var x = AIM.util.calculateOffset(cObj).x;
            var x = (cObj.offsetLeft + cObj.offsetWidth) + 20;
            //if(document.getElementById("AIMBuddyListContainer")) x+= document.getElementById("AIMBuddyListContainer").offsetWidth;

            AIMInfo.style.left = x + "px";
        } catch (err) { }
    }
}

/**
*	A set of methods for sending data to the host.
*/
AIM.transactions = {
    /**
    *	Sends a text IM
    *	@param { String } aimId The id to whom the IM should go.
    *	@param { String } txt The message to tbe sent.
    */
    sendTextIM: function(aimId, txt) {
        if (txt.trim() == "") return;
        aimId = encodeURIComponent(aimId);
        txt = encodeURIComponent(txt);
        tObj = {
            dataURI: AIM.params.baseTransactionURI + AIM.params.transactions.sendTextIM + "?aimsid=" + AIM.params.sessionId + "&message=" + txt + "&t=" + aimId + "&f=json&c=AIM%2Ecore%2EacceptData&offlineIM=" + AIM.params.SEND_OFFLINE_IM,
            type: "sendTextIM",
            to: aimId,
            msg: txt
        }
        AIM.core.requestData(tObj);
    },

    sendDataIM: function(aimId, data, cap, type) {
        if (data.trim() == "") return;
        var aimId = encodeURIComponent(aimId);
        var data = encodeURIComponent(data);
        var cap = encodeURIComponent(cap);
        var type = encodeURIComponent(type);

        var tObj = {
            dataURI: AIM.params.baseTransactionURI + AIM.params.transactions.sendDataIM + "?aimsid=" + AIM.params.sessionId + "&data=" + data + "&k=" + AIM.params.wimKey + "&t=" + aimId + "&type=" + type + "&cap=" + cap + "&f=json&c=AIM%2Ecore%2EacceptData",
            type: "sendDataIM",
            to: aimId,
            data: data,
            dType: type,
            cap: cap
        }
        AIM.core.requestData(tObj);
    },

    /**
    *	Deprecated. Buddy list data is returned by the listener when a session begins and buddylist is subscribed to.
    */
    getBuddyList: function() {
        var tObj = {
            dataURI: AIM.params.baseTransactionURI + AIM.params.transactions.getBuddyList,
            aimId: AIM.params.user,
            type: "getBuddyList"
        };
        AIM.core.requestData(tObj);
    },

    /**
    *	Deprecated. Buddy info comes from the initial buddylist event, and subsequent presence events when buddylist and presence are subscribed to
    */
    getBuddyInfo: function(oScreenName) {
        var tObj = {
            dataURI: AIM.params.baseTransactionURI + AIM.params.transactions.getBuddyInfo + "?displayId=" + oScreenName,
            aimId: oScreenName,
            type: "getBuddyInfo"
        }
        AIM.core.requestData(tObj);
    },

    /**
    *	Requests a token from the host. This is the first method to be called - the API will not function without a valid token.
    *	@param { String } eList A comma dilimited list of events the application should subscribe to. Defined in AIM.core.subscriptions.
    */
    getToken: function(eList) {
        if (AIM.params.token) {
            AIM.transactions.startSession(eList);
            return;
        }
        var tObj = {
            dataURI: AIM.params.baseAuthURI + AIM.params.transactions.getToken + "?k=" + AIM.params.wimKey + "&f=json&c=AIM%2Ecore%2EacceptData",
            type: "getToken",
            eventList: eList
        }
        AIM.core.requestData(tObj);
    },

    /**
    *	Request presence data from the host. Call is made anonymously (no login required).
    */
    getPresenceInfo: function() {
        presence = AIM.util.getPresenceContainers();
        var i = presence.length, paramString = "";
        while (i-- > 0) paramString += "t=" + presence[i].aimId + "&";
        paramString = paramString.substring(0, paramString.lastIndexOf("&"));
        var tObj = {
            dataURI: AIM.params.baseTransactionURI + AIM.params.transactions.getPresenceInfo + "?" + paramString + "&k=" + AIM.params.wimKey + "&awayMsg=1&f=json&c=AIM%2Ecore%2EacceptData",
            presenceObject: presence,
            type: "getPresenceInfo"
        }
        AIM.core.requestData(tObj);
    },

    /**
    *	Starts a session with the service - called by getToken's callback on successful token receipt.
    *	@param { String } eventList List of events to subscribe to. Defined in AIM.core.subscriptions.
    */
    startSession: function(eventList) {
        if (!eventList) eventList = AIM.core.subscriptions;
        var ses = AIM.params.sessionId ? "&aimsid=" + AIM.params.sessionId : "";
        var tObj = {
            dataURI: AIM.params.baseTransactionURI + AIM.params.transactions.startSession + "?k=" + AIM.params.wimKey + ses + "&events=" + eventList + "&a=" + AIM.params.token + "&assertCaps=" + AIM.params.assertCaps + "&interestCaps=" + AIM.params.interestCaps + "&f=json&c=AIM%2Ecore%2EacceptData",
            type: "startSession"
        }
        AIM.core.requestData(tObj);
    },

    /**
    *	Ends the session with the AIM service and the SNS service, logging the user out.
    */
    endSession: function() {
        var tObj = {
            dataURI: AIM.params.baseTransactionURI + AIM.params.transactions.endSession + "?k=" + AIM.params.wimKey + "&aimsid=" + AIM.params.sessionId + "&f=json&c=AIM%2Ecore%2EacceptData",
            type: "endSession"
        }
        AIM.core.requestData(tObj);
        var tObj = {
            dataURI: AIM.params.baseAuthURI + AIM.params.transactions.logout + "?k=" + AIM.params.wimKey + "&f=json&a=" + AIM.params.token + "&c=AIM%2Ecore%2EacceptData",
            type: "endSession"
        }
        AIM.core.requestData(tObj);
        if (document.getElementById("AIMBuddyListContainer")) document.getElementById("AIMBuddyListContainer").style.display = "none";
    },

    /**
    *	Notifies the host that the users online status has changed (i.e, away, idle, etc)
    *	@param { String } status The status of the user. away, idle, mobile, offline, invisible
    */
    setState: function(status) {
        if (status == "away") {
            var awayMessage = "&away=" + AIM.params.text.awayMessage;
            AIM.util.currentState = 0;
        } else if (status == "invisible") {
            var awayMessage = "";
            AIM.util.currentState = 2;
        } else {
            var awayMessage = "";
            AIM.util.currentState = 1;
        }
        var tObj = {
            dataURI: AIM.params.baseTransactionURI + AIM.params.transactions.setState + "?aimsid=" + AIM.params.sessionId + "&f=json&c=AIM%2Ecore%2EacceptData&view=" + status + "" + awayMessage,
            type: "setState"
        }
        AIM.core.requestData(tObj);
    },

    /**
    *	Sets the users away message, and then changes the users status to Away.
    *	@param { String } msg The away message to be used.
    */
    setAwayMessage: function(msg) {
        msg = AIM.util.formatMessage(msg);
        if (msg.length > AIM.params.AWAY_MSG_LIMIT) msg = msg.substring(0, AIM.params.AWAY_MSG_LIMIT);
        if (msg.trim() == "") msg = "I am away from my computer right now.";
        msg = encodeURIComponent(msg);
        AIM.params.text.awayMessage = msg;
        AIM.util.resetUserNotified();
        AIM.transactions.setState("away");
    },

    /**
    *	Not yet implimented.
    */
    setProfile: function() {

    },

    /**
    *	Notifies the host that the user is typing.
    *	@param { String } tStatus The status of the typing. typing typed or none.
    *	@param { aimId } The id of the user to notify of the typing status.
    */
    typingStatus: function(tStatus, aimId) {
        if (new Date() - AIM.util.typingStatusTimeStamp < 2000) return;
        AIM.util.typingStatusTimeStamp = new Date();
        aimId = encodeURIComponent(aimId);
        var tObj = {
            dataURI: AIM.params.baseTransactionURI + AIM.params.transactions.typingStatus + "?aimsid=" + AIM.params.sessionId + "&t=" + aimId + "&typingStatus=" + tStatus + "&f=json&c=AIM%2Ecore%2EacceptData",
            type: "typingStatus"
        }
        AIM.core.requestData(tObj);
    }
}

/**
*	Numerous utility methods 
*/

AIM.util = {
    offsetX: 0,
    offsetY: 0,
    dragObj: null,
    mDown: false,
    typingTimer: [],
    typingStatusTimeStamp: new Date(),
    visualNotificationTimer: [],
    userNotified: [],
    currentState: 1,

    /**
    *	Adds an event handler for the specified event to the specified object
    *	@param { HTMLObject } oElement The element to appy the event handler to
    *	@param { Function } oFunction The function to act as the event handler
    *	@param { String } strEvent The event name (without the "on" prefix), i.e., mousemove or load
    */
    addEvent: function(oElement, fnFunction, strEvent) {
        if (oElement.addEventListener) {
            oElement.addEventListener(strEvent, fnFunction, false);
        } else {
            // attachEvent is useless w/o "this" support...
            //oElement.attachEvent("on" + strEvent,fnFunction);
            eval("oElement.on" + strEvent + "= fnFunction");
        }
    },

    removeEvent: function(oElement, fnFunction, strEvent) {
        oElement.removeEventListener ? oElement.removeEventListener(strEvent, fnFunction, false) : oElement.detachEvent("on" + strEvent, fnFunction);
    },
    /**
    *	Called from the beforeunload event to unhook various things and end the session.
    */
    cleanUp: function() {
        document.title = AIM.params.DOCUMENT_TITLE;
        //try {
        //if(document.getElementById("AIMReqFrame")) document.getElementById("AIMReqFrame").parentNode.removeChild(document.getElementById("AIMReqFrame"));
        AIM.util.currentState = 1;
        if (AIM.core.activeSession) {
            AIM.transactions.endSession();
            alert(AIM.params.text.autoLogOut);
        }
        //} catch(err) { }
        AIM.params.token = null;
        AIM.params.sessionId = null;
    },

    /**
    *	Creates a script element and appends it to the head element
    *	@param { String } uri The url of the javascript file to import
    */
    importScript: function(uri) {
        var oScript = document.createElement("script");
        oScript.setAttribute("type", "text/javascript");
        oScript.setAttribute("src", uri);
        document.getElementsByTagName("head")[0].appendChild(oScript);
    },

    /**
    *	Captures the x,y offset of the mouse pointer relative to the element that was clicked
    *	@param { Event } e The click event.
    */
    captureOffset: function(e) {
        AIM.util.mDown = true;
        AIM.util.dragObj = e ? e.target.parentNode : event.srcElement.parentNode;
        var nx = AIM.util.dragObj.offsetLeft;
        var ny = AIM.util.dragObj.offsetTop;

        if (window.event) {
            AIM.util.offsetX = window.event.clientX - nx;
            AIM.util.offsetY = window.event.clientY - ny;
        } else {
            AIM.util.offsetX = e.pageX - nx;
            AIM.util.offsetY = e.pageY - ny;
        }
    },

    /**
    *	Calculates the offset of an element all the way to the nth offsetParent
    *	@param { HTMLObject } obj The object for which the calculations should be performed
    *	@return An object with x and y properties
    *	@type Object
    */
    calculateOffset: function(obj) {
        var offset = { x: 0, y: 0 }
        while (obj.offsetParent) {
            if (obj.offsetParent) {
                if (obj.offsetTop) offset.y += obj.offsetTop;
                if (obj.offsetLeft) offset.x += obj.offsetLeft;
                var obj = obj.offsetParent;
            }
        }
        return offset;
    },

    /**
    *	Resets the array that keeps track of if a user has recieved the clients away message. Called when the user comes back or changes their away message.
    */
    resetUserNotified: function() {
        for (i in AIM.util.userNotified) AIM.util.userNotified[i] = false;
    },

    /**
    *	Creates a link element and appends it to the head of the document.
    *	@param { String } uri The URI to the css file.
    */
    createStyleSheet: function(uri) {
        var oLinks = document.getElementsByTagName("head")[0].getElementsByTagName("link");
        var i = oLinks.length;
        while (i-- > 0) if (oLinks[i].getAttribute("href") == uri) return;
        var css = document.createElement("link");
        css.setAttribute("type", "text/css");
        css.setAttribute("rel", "stylesheet");
        css.setAttribute("href", uri);
        document.getElementsByTagName("head")[0].appendChild(css);
    },

    /**
    *	Cookie handler methods.
    */
    cookie: {
        /**
        *	Gets the value of a cookie
        *	@param { String } cookieName The name of the cookie who's value you want
        *	@return The value of the cookie. null if the cookie isnt found.
        *	@type String
        */
        get: function(cookieName) {
            var c = document.cookie;
            c = c.split(";");
            var i = 0;
            do {
                var v = c[i].split("=");
                if (v[0].trim() == cookieName.trim()) {
                    return v[1];
                    break;
                }
                i++;
            } while (c[i]);
            return null;
        },
        /**
        *	Rudimentary cookie setting function
        *	@param { String } cookieName The name of the cookie to set.
        *	@param { String } cookieValue The value the cookie.
        *	@param { Boolean } session If the cookie is a session cookie or not. Sets the expire to current date + one year if false.
        */
        set: function(cookieName, cookieValue, session) {
            var cookieString = cookieName + "=" + cookieValue + ";domain=" + document.domain + ";path=/";
            if (!session) cookieString += ";expires=" + new Date(Date.parse(new Date()) + 31536000000);
            document.cookie = cookieString;
        }
    },
    /**
    *	Adds a class to an element.
    *	@param { HTMLObject } oElement The element to apply the class to.
    *	@param { String } oClassName The class to apply.
    */
    addClass: function(oElement, oClassName) {
        if (!oElement.className) {
            oElement.className = oClassName;
        } else {
            var newClassName = oElement.className + " " + oClassName;
            oElement.className = newClassName;
        }
    },
    /**
    *	Removes a class from an element.
    *	@param { HTMLObject } oElement The element to remove the class from.
    *	@param { String } oClassName The class to be removed.
    */
    removeClass: function(oElement, oClassName) {
        var re = new RegExp('(?:^|\\s+)' + oClassName + '(?:\\s+|$)', 'g');
        oElement.className = oElement.className.replace(re, " ");
    },
    /**
    *	Creates embeds for all of the sounds defined in AIM.params.sounds. Not used by MSIE or Opera.
    */
    createSoundObjects: function() {
        if (document.all) return;
        for (var i in AIM.params.sounds) {
            if (document.getElementById("snd_" + i)) continue;
            var emb = document.createElement("embed");
            emb.setAttribute("autostart", "false");
            emb.setAttribute("src", AIM.params.sounds[i]);
            emb.setAttribute("id", "snd_" + i);
            emb.setAttribute("name", "snd_" + i);
            emb.setAttribute("hidden", "true");
            emb.setAttribute("width", "0");
            emb.setAttribute("height", "0");
            emb.setAttribute("enablejavascript", "true");
            emb.setAttribute("type", "audio/x-wav");
            emb.className = "AIMBuddyListSoundObject";
            document.getElementById("AIMBuddyListContainer").appendChild(emb);
        }
    },

    /**
    *	Plays a sound.
    *	@param { String } sndType The type of sound to play. Should match the property in AIM.params.sounds.
    */
    playSound: function(sndType) {
        if (!AIM.params.sound) return;
        try {
            if (document.all) {
                if (document.getElementById("msieSndObj")) document.getElementById("msieSndObj").parentNode.removeChild(document.getElementById("msieSndObj"));
                var sndObj = document.createElement("bgsound");
                sndObj.setAttribute("src", AIM.params.sounds[sndType]);
                sndObj.setAttribute("id", "msieSndObj");
                document.getElementById("AIMBuddyListContainer").appendChild(sndObj);
            } else {
                //var sndObj = document.getElementById("snd_" + sndType);
                var sndObj = eval("document.snd_" + sndType);
                sndObj.Stop();
                sndObj.Rewind();
                sndObj.Play();
            }
        } catch (err) {
            AIM.core.debug("AIM.util.playSound: " + err.message);
        }
    },

    /**
    *	Formats a string, replacing < with &lt;, etc.
    *	@param { String } oString The string to format.
    *	@return The formatted string.
    *	@type String
    */
    formatMessage: function(oString) {
        /*
        if(oString.match(/<a ([^>]*)>([^<]*)<\/a>/g))  oString = oString.replace(/href/gi,"target=\"_blank\" href");
        if(oString.match(/<img ([^>]*)>/g))  {
        oString = oString.replace(/</g,"&lt;");
        oString = oString.replace(/>/g,"&gt;");
        }
        */
        //oString = oString.replace(/&/g,"&amp;");
        oString = oString.replace(/</g, "&lt;");
        oString = oString.replace(/>/g, "&gt;");
        return oString;
    },

    /**
    *	Takes a UTC timestamp and converts it to something human-readable
    *	@param { Long } oTimeStamp A UTC timestamp
    *	@return The UTC timestamp converted to [HH:MM]
    *	@type String
    */
    formatTimeStamp: function(oTimeStamp) {
        var h = oTimeStamp.getHours();
        if (!AIM.params.TWENTY_FOUR_HOUR_CLOCK) if (h > 12) h -= 12;
        var m = oTimeStamp.getMinutes();
        if (m < 10) m = "0" + m;
        return "[" + h + ":" + m + "]";
    },

    /**
    *	Calculates the amount of time that has elapsed based on the number of seconds passed in
    *	@param { Integer } oElapsed The number of seconds elapsed since the person came online, went offline, went away, etc
    *	@return A formated string of the elapsed time, i.e., 12 Hours, 8 minutes.
    *	@type String
    */
    elapsedFromSeconds: function(oElapsed) {
        var seconds = oElapsed % 60;
        Math.floor(oElapsed /= 60);
        var minutes = Math.round(oElapsed % 60);
        Math.floor(oElapsed /= 60);
        var hours = Math.floor(oElapsed % 24);
        var days = Math.floor(oElapsed / 24);

        var oString = "";
        if (days > 0) oString += days + " Days, ";
        if (hours > 0) oString += hours + " Hours, ";
        if (minutes > 0) oString += minutes + " Minutes";

        if (oString == "") oString = "1 Minute";
        return oString;

    },
    /**
    *	Gets a reference to all of the presence widgets on the page.
    *	@return A reference to all of the presence widgets on the page.
    *	@type Array
    */
    getPresenceContainers: function() {
        var ele = AIM.util.getElementsByClassName(document.getElementsByTagName("body")[0], "*", "AIMPresenceWidget");
        var presenceObj = [];
        var i = ele.length;
        while (i-- > 0) {
            presenceObj[i] = {
                element: ele[i],
                aimId: ele[i].className.substring(ele[i].className.indexOf(" "), ele[i].className.length).trim()
            }
        }
        return presenceObj;
    },

    /**
    *	Provides object references to all of the elements that represent screen names
    *	@param { HTMLObject } parentObj The object that contains the elements. Generally AIMBuddyList or AIMBuddyListContainer
    *	@return An array of HTML Elements that represent screen names in a buddy list.
    *	@type Array
    */
    getAIMIDCollection: function(parentObj) {
        var objs = parentObj.getElementsByTagName("*");
        var i = objs.length;
        var returnArr = [];
        while (i-- > 0) if (objs[i].getAttribute("wim_id")) returnArr.push(objs[i]);
        return returnArr;
    },

    /**
    *	Provides a reference to all elements that represent a given screen name
    *	@param { String } wimId The screen name to query for
    *	@param { HTMLObject } The HTML element to run the query in
    *	@return An array of HTML elements that represent that screen name
    *	@type Array
    */
    getElementsByAIMID: function(wimId, oContainer) {
        var objs = oContainer.getElementsByTagName("*");
        var returnArr = [];
        var i = objs.length;
        while (i-- > 0) {
            oWIM = objs[i].getAttribute("wim_id")
            if (oWIM) if (oWIM == wimId) returnArr.push(objs[i]);
        }
        return returnArr;
    },
    /**
    *	Adaptation of Snook/Nyman's getElementsByClassName method: http://www.robertnyman.com/2005/11/07/the-ultimate-getelementsbyclassname/
    *	@param { HTMLObject } oElm The element to search within
    *	@param { String } strTagName The tag name to query on - "*" for all of 'em
    *	@param { String } strClassName The class to query for
    *	@return Any elements who's className match strClassName
    *	@type Array
    */
    getElementsByClassName: function(oElm, strTagName, strClassName) {
        var arrElements = (strTagName == "*" && document.all) ? document.all : oElm.getElementsByTagName(strTagName);
        var arrReturnElements = [];
        strClassName = strClassName.replace(/\-/g, "\\-");
        var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)");
        var i = arrElements.length;
        while (i-- > 0) if (oRegExp.test(arrElements[i].className)) arrReturnElements.push(arrElements[i]);
        return (arrReturnElements)
    },

    /**
    *	Returns the scroll offset of the window
    *	@param { Boolean } which True for vertical offset, false for horizontal
    *	@return The scroll offset of the window
    *	@type Integer
    */
    getScrollOffset: function(which) {
        if (which) {
            if (document.body.scrollTop != 0) return document.body.scrollTop;
            if (document.documentElement.scrollTop != 0) return document.documentElement.scrollTop;
        } else {
            if (document.body.scrollLeft != 0) return document.body.scrollTop;
            if (document.documentElement.scrollLeft != 0) return document.documentElement.scrollLeft;
        }
        return 0;
    }
}

AIM.eventHandlers = {
    handleMouseover: function(e) {
        var srcObj = e ? e.target : event.srcElement;
        fn = function() { AIM.ui.showBuddyInfo(srcObj); }
        if (srcObj.className.indexOf("buddy") > -1 || srcObj.className.indexOf("AIMPresenceWidget") > -1) {
            AIM.ui.prepBuddyInfo(srcObj);
            AIM.ui.showBuddyInfo(AIM.ui.storedBuddyInfo[srcObj.getAttribute("wim_id")]);
        }
    },

    handleMouseout: function(e) {
        try {
            var srcObj = e ? e.target : event.srcElement;
            if (srcObj.className.indexOf("buddy") > -1) {
                if (document.getElementById("AIMBuddyListBuddyInfo")) document.getElementById("AIMBuddyListBuddyInfo").style.display = "none";
            }
        } catch (err) {
            //AIM.core.debug("AIM.eventHandlers.handleMouseout: " + err.message);
        }
    },

    handleClick: function(e) {
        var srcObj = e ? e.target : event.srcElement;
        if (srcObj.className.indexOf("buddy") > -1) {
            AIM.ui.createIMWindow(srcObj.getAttribute("wim_id"));
        } else if (srcObj.className == "AIMBuddyListIMWindowButton") {
            var winID = srcObj.getAttribute("wim_aimId");
            if (winID.indexOf("+") == 0) winID = winID.replace(/\+/, "SMS");
            AIM.transactions.sendTextIM(srcObj.getAttribute("wim_aimId"), srcObj.oValue);
            srcObj.oValue = "";
            document.getElementById("AIMTextInput_" + winID).value = "";
        } else if (srcObj.className.indexOf("AIMBuddyListMenuItem") == 0) {
            eval(srcObj.xonclick);
        }
    },

    handleMouseMove: function(e) {
        if (!AIM.util.mDown) return;
        y = window.event ? window.event.clientY : e.clientY;
        if (y <= 5) return;
        x = window.event ? window.event.clientX - AIM.util.offsetX : e.clientX - AIM.util.offsetX;
        y = window.event ? window.event.clientY - AIM.util.offsetY : e.clientY - AIM.util.offsetY;
        if (AIM.params.MOZILLA) {
            y += AIM.util.getScrollOffset(1);
            x += AIM.util.getScrollOffset(0);
        }
        AIM.util.dragObj.style.top = y + "px";
        AIM.util.dragObj.style.left = x + "px";
        if (AIM.util.dragObj.className.indexOf("AIMBuddyListIMWindow") > -1) {
            if (AIM.util.dragObj.className.indexOf("DragState") == -1) AIM.util.addClass(AIM.util.dragObj, "AIMBuddyListIMWindowDragState");
            AIM.ui.setIMWindowZIndex(AIM.util.dragObj.id);
        }
    },

    handleKeyUp: function(e) {
        var srcObj = e ? e.target : event.srcElement;
        var keyCode = window.event ? window.event.keyCode : e.keyCode;
        if (srcObj.getAttribute("wim_aimId")) {
            var oSN = srcObj.getAttribute("wim_aimId");
            var winID = oSN;
            if (winID.indexOf("+") == 0) winID = winID.replace(/\+/, "SMS");
            AIM.ui.setIMWindowZIndex(winID + "_AIMwindow");
            if (!AIM.util.typingTimer[oSN]) {
                var fn = function() { AIM.transactions.typingStatus("typed", oSN); }
                AIM.util.typingTimer[oSN] = setTimeout(fn, 8000);
            } else {
                AIM.transactions.typingStatus("typing", oSN);
            }
            //if(srcObj.value == "") AIM.transactions.typingStatus("none",oSN);
            if (keyCode == 13) {
                clearTimeout(AIM.util.typingTimer[oSN]);
                AIM.util.typingTimer[oSN] = null;
                AIM.transactions.sendTextIM(oSN, srcObj.value);
                //AIM.transactions.typingStatus("none",oSN);
                srcObj.value = "";
            } else {
                if (document.getElementById("AIMBuddyListIMWindowButton_" + winID)) document.getElementById("AIMBuddyListIMWindowButton_" + winID).oValue = srcObj.value;
            }
        }
    }
}
// language file
AIM.util.importScript("http://o.aolcdn.com/aim/web-aim/lang/aimapi.text." + baseLang + ".js");
// deprecations
AIM.widgets.IMMe = AIM.widgets.IM;
/**
*	myPresence.js
*	
*/

AIM.params.callbacks.getPresenceInfo = ["presenceWidget.displayPresence"];
var presenceWidget = {
    init: function() {
        AIM.params.DEBUG = false;
        AIM.params.wimKey = "sc1et--3OjlKUUST";
        AIM.transactions.getPresenceInfo();
    },
    displayPresence: function(json) {
        if (json.response.statusCode == 200) {
            if (json.response.data.users[0].state == "online") {
                document.getElementById("myPresence").innerHTML = "<a href=\"http://www.scottbennettcaller.com/bubbachat.aspx\" target=\"_blank\" onclick=\"window.open (this.href, 'popupwindow', 'width=250,height=300,scrollbars=0,resizable=0'); return false;\"  title=\"Click to IM!\"><img src=\"http://www.scottbennettcaller.com/images/status_online.gif\" border=\"none\" />Scott is " + json.response.data.users[0].state + "</a><br/><div align=\"center\"><marquee direction=\"left\" width=\"87%\" scrollamount=\"5\">Click above to IM!</marquee></div>";
            }
            if (json.response.data.users[0].state == "mobile") {
                document.getElementById("myPresence").innerHTML = "<a href=\"http://www.scottbennettcaller.com/bubbachat.aspx\" target=\"_blank\" onclick=\"window.open (this.href, 'popupwindow', 'width=250,height=300,scrollbars=0,resizable=0'); return false;\"  title=\"Click to TXT!\"><img src=\"http://www.scottbennettcaller.com/images/status_mobile.gif\" border=\"none\" />Scott is " + json.response.data.users[0].state + "</a><br/><div align=\"center\"><marquee direction=\"left\" width=\"87%\" scrollamount=\"5\">Click above to TXT!</marquee></div>";
            }
            if (json.response.data.users[0].state == "away") {
                document.getElementById("myPresence").innerHTML = "<a href=\"http://www.scottbennettcaller.com/bubbachat.aspx\" target=\"_blank\" onclick=\"window.open (this.href, 'popupwindow', 'width=250,height=300,scrollbars=0,resizable=0'); return false;\" title=\"Click to IM!\"><img src=\"http://www.scottbennettcaller.com/images/status_away.gif\" border=\"none\" />Scott is " + json.response.data.users[0].state + "</a><br/><div align=\"center\"><marquee direction=\"left\" width=\"87%\" scrollamount=\"5\">Click above to IM!</marquee></div>";
            }
            if (json.response.data.users[0].state == "offline") {
                document.getElementById("myPresence").innerHTML = "<a href=\"mailto:scott@scottbennettcaller.com\" title=\"Click to E-mail!\"><img src=\"http://www.scottbennettcaller.com/images/status_offline.gif\" border=\"none\" />Scott is " + json.response.data.users[0].state + "</a><br/><div align=\"center\"><marquee direction=\"left\" width=\"87%\" scrollamount=\"5\">Click above to E-mail!</marquee></div>";
            }
            if (json.response.data.users[0].state == "idle") {
                document.getElementById("myPresence").innerHTML = "<a href=\"http://www.scottbennettcaller.com/bubbachat.aspx\" target=\"_blank\" onclick=\"window.open (this.href, 'popupwindow', 'width=250,height=300,scrollbars=0,resizable=0'); return false;\" title=\"Click to IM!\"><img src=\"http://www.scottbennettcaller.com/images/status_away.gif\" border=\"none\" />Scott is " + json.response.data.users[0].state + "</a><br/><div align=\"center\"><marquee direction=\"left\" width=\"87%\" scrollamount=\"5\">Click above to IM!</marquee></div>";
            }
        }
    }
}
window.addEventListener ? window.addEventListener("load", presenceWidget.init, false) : window.attachEvent("onload", presenceWidget.init);
function reload() {
    AIM.transactions.getPresenceInfo();
    setTimeout("reload()", 5500);
}
/// <reference name="jscript3.js"/>
var message = document.title;
var message = message + "          "
i = "0"
var temptitle = ""
var speed = "150"

function titler() {
    if (!document.all && !document.getElementById)
        return
    document.title = temptitle + message.charAt(i)
    temptitle = temptitle + message.charAt(i)
    i++
    if (i == message.length) {
        i = "0"
        temptitle = ""
    }
    setTimeout("titler()", speed)
}
window.onload = titler; reload();
