/***	@fileoverview		AIM Javascript API*	@author	Steven G. Chipman - AOL - http://developer.aim.com*	@filename aimapi.js*	@copyright Copyright (c) 2007 AOL LLC. All rights reserved*	@version	0.19r2.1*	@revision	08.20.2007*/ /***	These object prototypes are required by the API.*/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: "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:0,	DOCUMENT_TITLE:document.title,	DEBUG:false,	BUDDY_LIST_DRAG:false,	DEFAULT_ICON: "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: "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: "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: "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: "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 h1 = document.createElement("h1");						h1.appendChild(document.createTextNode(groupings[i].name));			h1.xindex = i;			AIM.util.addEvent(h1,function() { AIM.ui.setHeaderState(this); },"click");			h1.className = "AIMBuddyListHeading";			li.appendChild(h1);						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 h1 = win.getElementsByTagName("h1")[0];		if(h1.className.indexOf("AIMBuddyListIMWindowNotifyOff") > -1) {			AIM.util.removeClass(h1,"AIMBuddyListIMWindowNotifyOff");			AIM.util.addClass(h1,"AIMBuddyListIMWindowNotifyOn");		} else {			AIM.util.removeClass(h1,"AIMBuddyListIMWindowNotifyOn");			AIM.util.addClass(h1,"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 h1 = win.getElementsByTagName("h1")[0];			h1.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;		// Get rid of any formatting that incoming IM may have.		msg = msg.replace(/<span.*?>(.*?)<\/span>/gi, "$1");				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 +="";		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 h1 = document.createElement("h1");		h1.appendChild(document.createTextNode(winTitle));		AIM.util.addEvent(h1,AIM.util.captureOffset,"mousedown");		h1.className = "AIMBuddyListWindowTitleBar";		AIM.util.addEvent(h1,function() { AIM.util.mDown = false; AIM.util.removeClass(this.parentNode,"AIMBuddyListIMWindowDragState"); },"mouseup");		win.appendChild(h1);							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");		h1.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("aimapi.text.en-us.js");// deprecationsAIM.widgets.IMMe = AIM.widgets.IM;