/************************************************************************
 *                       ******  GLOBAL WARS(tm) *******
 ************************************************************************
 */
var Debug;

/* some globals */
var fmd; // frames.main.document

var statusLed;

var vcardW; // my vcardW;
var ebW
var searchW;
var errorW;

var Jabber = Class.create();

Jabber.onlstatus = {
	available: "online",
	chat: "free for chat",
	away: "away",
	xa: "not available",
	dnd: "do not disturb",
	invisible: "invisible",
	unavailable: "offline"
}

Jabber.cache = new Object();

Object.extend(Object.extend(Jabber.prototype, Subject.prototype), {
	initialize: function(dbg, id, options) {
		this.options = {
			timerval:      2000, // ms - only if polling, which we aren't.
			register:      false,
			autoPopup:     true,
			autoPopupAway: false,
			playSounds:    true,
			focusWindows:  true,
			timestamps:    false,
			usersHidden:   true,
			emptyGroups:   true,
			enableLog:     false
		}
		
		Object.extend(this.options, options || {});
		
		if (dbg && dbg.log)
			this.dbg = dbg; // always initialise a debugger
		else {
			this.dbg = new Object();
			this.dbg.log = function() { };
		}

		Debug = this.dbg;
		
		Jabber.cache[id] = this;
		
		this.id = id;

		this.messageHistory = new Array();
		this.historyIndex = 0;
		this.error_messages = new Array();
		this.docRoot = '/javascripts/jwchat/';

		/* Create referencable callback closures */
		this.getAgentsClosure = this.getAgentsCb.bind(this);
		this.getRosterClosure = this.getRosterCb.bind(this);
		this.getSavedStateClosure = this.getSavedStateCb.bind(this);
		this.getPrefsClosure = this.getPrefsCb.bind(this);
		this.getAnnotationsClosure = this.getAnnotationsCb.bind(this);
		this.getDiscoInfoClosure = this.getDiscoInfoCb.bind(this);
		this.getDiscoItemsClosure = this.getDiscoItemsCb.bind(this);
		this.getAgentBrowseClosure = this.getAgentBrowseCb.bind(this);
		this.getServerBrowseClosure = this.getServerBrowseCb.bind(this);
		this.getBookmarksClosure = this.getBookmarksCb.bind(this);
		this.storeCIDClosure = this.storeCIDCb.bind(this);
		
		this.init();
	},
	
	init: function() {
		this.con = new JSJaCHttpBindingConnection(this.dbg, this.id);
		
		this.logoutCalled = false;
		this.onlstat = '';
		this.onlmsg = '';

		/* register handlers */
		this.con.registerHandler('iq',this.handleIQSet.bind(this));
		this.con.registerHandler('presence',this.handlePresence.bind(this));
		this.con.registerHandler('message',this.handleMessage.bind(this));
		this.con.registerHandler('message',this.handleMessageError.bind(this));
		this.con.registerHandler('ondisconnect',this.handleDisconnect.bind(this));
		this.con.registerHandler('onconnect',this.handleConnected.bind(this));
		this.con.registerHandler('onerror',this.handleConError.bind(this));
	},
	
    createNodeNS: function(doc, name, xmlns) {
        if (typeof doc.createElementNS != 'undefined') {
		    return doc.createElementNS(xmlns, name);
        } else {
		    var el = doc.createElement(name);
		    el.setAttribute('xmlns',xmlns);
            return el;
        }
    },

	xlateStatus: function(key) {
		return Jabber.onlstatus[key];
	},
	
	makeURL: function(page,_args) {
	   var args = {rosterid: this.id};
		Object.extend(args, _args || {});
		
	   var sep='?';
	   var url=this.docRoot+page;
	   
	   for (arg in args) {
		   if (args[arg]) {
				url += sep+arg+'='+escape(args[arg]);
				sep='&'; 
		   }
	   }
	   
	   return url;
	},
	
	elToId: function(el) {
	   // First check if the triggering element is a user
	   if (el.className == 'rosterUser') {
		   return el.id;
	   } else {
		   var ancestors = Element.ancestors(el);
		   for (var i=0; i<ancestors.length; i++) {
			   if (ancestors[i].className == 'rosterUser') {
				   return cutResource(ancestors[i].id);
				   break;
			   }
		   }
	   }
	   
	   return null;
	},
	
	/* command line history */
	getHistory: function(key, message) {
		if ((key == "up") && (this.historyIndex > 0)) this.historyIndex--;
		if ((key == "down") && (this.historyIndex < this.messageHistory.length)) this.historyIndex++;
		if (this.historyIndex >= this.messageHistory.length) {
			if (this.historyIndex == this.messageHistory.length) return '';
			return message;
		} else {
			return this.messageHistory[this.historyIndex];
		}
	},
	
	addtoHistory: function(message) {
		if (is.ie5)
			this.messageHistory = this.messageHistory.concat(message);
		else
			this.messageHistory.push(message);
		this.historyIndex = this.messageHistory.length;
	},
	
	connect: function(httpServer, jabberServer, user, resource, password, portno, secure) {
		this.dbg.log('connect()', 2);
		this.httpServer = httpServer;
		this.jabberServer = jabberServer;
		if (user.indexOf("@") != -1) {
			this.jabberDomain = user.substring(user.indexOf("@")+1);
			this.user = user.substring(0,user.indexOf("@"));
		} else {
			this.jabberDomain = jabberServer;
			this.user = user;
		}
		this.resource = resource;
		this.password = password;
		this.portno = portno;
		this.secure = secure;
		this.jid = this.user + "@" + this.jabberDomain + "/" + this.resource;
		
		this.roster = new Roster(null, this.id, this.jabberServer, this.dbg);
		this.roster.usersHidden = this.options.usersHidden;
		this.roster.emptyGroups = this.options.emptyGroups;
		this.roster.nick = this.user; // remember nick for 1:1 Chats

		this.setupMenus();
		
		if (httpServer && httpServer != '' &&
			jabberServer && jabberServer != '' &&
			user && user != '')
			this.con.connect(this.httpServer,
				this.jabberServer,
				this.jabberDomain,
				this.user,
				this.resource,
				this.password,
				this.options.timerval,
				this.options.register,
				this.portno,
				this.secure);
	},
	
	reconnect: function() {
		var httpServer = this.httpServer;
		var jabberServer = this.jabberServer;
		var user = this.user;
		var resource = this.resource;
		var password = this.password;
		
		this.cleanUp();
		this.con.close();
		this.roster = null;
		this.con = null;
		this.init();
		this.connect(httpServer, jabberServer, user + "@" + this.jabberDomain, resource, password, this.portno, this.secure);
	},
	
	cleanUp: function() {
		/* close dependent windows */
		if (this.roster)
			this.roster.cleanUp();
		
		if (searchW && !searchW.closed)
			searchW.close();
		
		if (ebW && !ebW.closed)
			ebW.close();
	},

	logout: function() {
		this.logoutCalled = true;
		this.cleanUp();
	
		if (!this.con.connected())
			return;
	
		/* save state */
		var iq = new JSJaCIQ();
		iq.setIQ(null,this.jid,'set');
		var query = iq.setQuery('jabber:iq:private');
        var aNode = query.appendChild(this.createNodeNS(iq.getDoc(), 'jwchat', 'jwchat:state'));

		// save presence
		aNode.appendChild(iq.getDoc().createElement('presence')).appendChild(iq.getDoc().createTextNode(this.onlstat));
		
		var hiddengroups = '';
		if (typeof(this.roster) != 'undefined') {
			for (var i in this.roster.hiddenGroups)
				if (this.roster.hiddenGroups[i])
					hiddengroups += i+",";
		}
	
		if (hiddengroups != '')
			aNode.appendChild(iq.getDoc().createElement('hiddenGroups')).appendChild(iq.getDoc().createTextNode(hiddengroups));
	
		this.con.syncSend(iq);
	
		var aPresence = new JSJaCPresence();
		aPresence.setType('unavailable');
		this.con.syncSend(aPresence);
	
		this.con.disconnect();
	},

	/************************************************************************
	 * handleIQSet
	 ************************************************************************/
	handleIQSet: function(iq) {
		this.dbg.log("In IqSet",2);
		if (iq.getType() != "set") {
			this.dbg.log("not handling iq:\n"+iq.getDoc().xml,3);
			return;
		}
	
		this.dbg.log("got iq type 'set':\n"+iq.getDoc().xml,2);
	
		if (iq.getQueryXMLNS() != 'jabber:iq:roster') { // only handle roster items so far
			this.dbg.log("not handling iq:\n"+iq.getDoc().xml,1);
			return;
		}
	
		for (var i=0; i<iq.getQuery().childNodes.length; i++) {
			var item = iq.getQuery().childNodes.item(i);
			var user = this.roster.getUserByJID(cutResource(item.getAttribute('jid')));
			if (user) {
				// We already have this user on our roster
				user.subscription = item.getAttribute('subscription');
				if (item.getAttribute('subscription') == 'remove') {
					this.dbg.log("removing user " + user.jid,2);
					this.roster.removeUser(user);
				} else { // update user
					user.name = item.getAttribute('name')? item.getAttribute('name') : item.getAttribute('jid');
					user.groups = new Array('');
					for (var j=0; j<item.childNodes.length; j++)
						if (item.childNodes.item(j).nodeName == 'group')
							user.groups = user.groups.concat(item.childNodes.item(j).firstChild.nodeValue);
					this.roster.updateGroups();
			 	}
				if (item.getAttribute('subscription') == "from") {
					// Try and establish a mutual subscription - send a 'subscribe' presence
					// this.subscribe(user.jid);
				}
			} else {
				// got a new user
				if (this.roster.isGateway(cutResource(item.getAttribute('jid')))) { // auto add
					// get name
					var name = cutResource(item.getAttribute('jid'));
					for (var i in this.roster.disco)
						if (i == cutResource(item.getAttribute('jid')))
							name = this.roster.disco[i].getQuery().getElementsByTagName('identity').item(0).getAttribute('name');
		
					// add to roster
					var aUser = new RosterUser(cutResource(item.getAttribute('jid')),item.getAttribute('subscription'),["~gateways"],name);
					aUser.fulljid = item.getAttribute('jid');
					this.roster.addUser(aUser);
		
					// tell server about it
					var aIQ = new JSJaCIQ();
					aIQ.setType('set');
					var query = aIQ.setQuery('jabber:iq:roster');
					var aItem = query.appendChild(aIQ.getDoc().createElement('item'));
					aItem.setAttribute('jid',cutResource(item.getAttribute('jid')));
					aItem.setAttribute('name',name);
					aItem.appendChild(iq.getDoc().createElement('group')).appendChild(iq.getDoc().createTextNode('~gateways'));
		
					this.con.send(aIQ);
				} else {
					// This really is a previously unkown user.
					var name = item.getAttribute('name')? item.getAttribute('name') : item.getAttribute('jid');
					if (name.indexOf('@') != -1)
						name = name.substring(0,name.indexOf('@'));
		
					item.setAttribute('name',name);
					var jid = item.getAttribute('jid');
					var groups = new Array('');
					if (this.roster.isGateway(jid)) {
						groups = groups.concat('~gateways');
					} else {
						for (var j=0;j<item.childNodes.length;j++)
							if (item.childNodes.item(j).nodeName == 'group')
								groups = groups.concat(item.childNodes.item(j).firstChild.nodeValue);
					}
					this.roster.addUser(new RosterUser(jid,item.getAttribute('subscription'),groups,name));
		
					var aIQ = new JSJaCIQ();
					aIQ.setType('set');
					var query = aIQ.setQuery('jabber:iq:roster');
		
					var aItem = item.cloneNode(true);
					aItem.removeAttribute('subscription');
					aItem.removeAttribute('ask');
					query.appendChild(aItem);
		
					this.con.send(aIQ); // set stripped name
		
					if (item.getAttribute('subscription') == "from") {
						// Try and establish a mutual subscription - send a 'subscribe' presence
						this.subscribe(user.jid);
					}
				}
			}
		}
		this.roster.print();
	},

	handleConError: function(e) {
		var code = e.getAttribute('code');

/*		
		var rosterDiv = $('roster_'+this.id);
		if (rosterDiv)
			rosterDiv.innerHTML = "Code: " + code;
*/			
		if (!code || code == 'undefined') {
			setTimeout(this.reconnect.bind(this), 100);
			return;
		}

		switch (e.getAttribute('code')) {
		case '401':
			alert("Authorization failed");
			if (!this.con.connected())
				window.close();
			break;
		case '409':
			alert("Registration failed!\n\nPlease choose a different username!");
			break;
		case '503':
			alert("Service unavailable");
			break;
		case '500':
			if (!this.con.connected())
				if (confirm("Internal Server Error.\n\nDisconnected.\n\nReconnect?"))
					setTimeout(this.reconnect.bind(this), 100);
			break;
		case '404':
			if (confirm("You have been\ndisconnected.\n\nReconnect?"))
				setTimeout(this.reconnect.bind(this), 100);
			else
				this.con.close();
			break;		
		default:
			alert("An Error Occured:\nCode: "+e.getAttribute('code')+"\nType: "+e.getAttribute('type')+"\nCondition: "+e.firstChild.nodeName); // this shouldn't happen :)
			break;
		}
	},

	handleDisconnect: function() {
		var rosterDiv = $('roster_'+this.id);
		if (this.logoutCalled || this.onlstat == 'offline' || !rosterDiv)
			return;
	
		// disconnecting not with onunload handler triggered
		// statusLed.src = unavailableLed.src; // offline icon
		// this.statusMsg.value = '';
		
		rosterDiv.innerHTML = '';
	
		// 	if (confirm("Disconnected\n\nReconnect?"))
		// 		this.changeStatus(this.onlstat,this.onlmsg);
	},

	handleConnected: function() {
		this.dbg.log("Connected",0);
	   
		if (this.options.register && opener && opener.document.forms[0] && opener.document.forms[0].register)
			opener.document.forms[0].register.checked = false; 
	
		/* get list of agents */
		var iq = new JSJaCIQ();
		iq.setIQ(null,this.jid,'get','agents_1');
		iq.setQuery('jabber:iq:agents');
		this.roster.showMsg("Get Agents");
		this.con.send(iq,this.getAgentsClosure); // cascading information retrieval
	},
	
	setupMenus: function() {	
		if (is.iPhone) {
			// Create options page (see iPhone_menu.js)
			var oLinks = {
				'Add Contact': this.roster.openSubscriptionHandler.bind(this.roster),
				'Preferences': this.roster.openOptions.bind(this.roster),
				'1': '',
				'Services': this.roster.openRegistrations.bind(this.roster),
				'Account': this.roster.openAccount.bind(this.roster),
				'Password': this.roster.openPassword.bind(this.roster)
			}	   
			this.menu = new Menu('#mainMenu', 'menu', oLinks, {title: 'Options', effect: 'fromBottom', trigger: 'click', menuId: 'roster_menu', hideId: 'panel_'+this.id, parentEl: 'content'});
	   } else {
			// Create context menu (see yura_menu.js)
		   	var oLinks = {
				'Add Contact...': this.roster.openSubscriptionHandler.bind(this.roster),
				'Change Status...': this.openChangeStatus.bind(this),
				'Search...': this.openSearch.bind(this),
				'1': '',
				'Reconnect': this.reconnect.bind(this),
				'Registrations...': this.roster.openRegistrations.bind(this.roster),
				'Options...': this.roster.openOptions.bind(this.roster)
			}	   
		   this.menu = new Menu('#header_'+this.id, 'menu', oLinks);
		}
	   
		// Create context menu (see yura_menu.js)
	   	var oLinks = {
			'Edit User...': this.roster.openUserPropsHandler.bind(this.roster),
			'Show Info...': this.openUserInfoHandler.bind(this),
			'History...': this.openUserHistoryHandler.bind(this),
			'Edit Note...': this.roster.openUserNoteHandler.bind(this.roster),
			'1': '',
			'Delete Contact': this.roster.removeUserHandler.bind(this.roster),
			'Re-add Contact': this.roster.openSubscriptionHandler.bind(this.roster) // TODO this just subscribes us. We need to allow them to see us too.
		}
	   
		if (!is.iPhone)
			this.menu = new Menu('#roster_'+this.id, 'menu', oLinks);
		else {
		   	var oLinks = {
				'Edit Contact': this.roster.openUserPropsHandler.bind(this.roster),
				'Edit Note': this.roster.openUserNoteHandler.bind(this.roster),
				'1': '',
				'Delete Contact': this.roster.removeUserHandler.bind(this.roster),
				'Re-add Contact': this.roster.openSubscriptionHandler.bind(this.roster) // TODO this just subscribes us. We need to allow them to see us too.
			}
	   
			this.menu = new Menu('#roster_'+this.id, 'menu', oLinks, {targetClassName: 'rosterUser_details', title: 'Contact', subTitle: this.roster.getSubTitle.bind(this.roster), effect: 'fromRight', trigger: 'click', menuId: 'contact_config', hideId: 'panel_'+this.id, parentEl: 'content'})	
		}
	},

	/*
	*/
	handleGroupChatInvite: function(aMessage, x) {
		/* check if this is a groupchat invite */
		var x;
		for (var i=0; i<aMessage.getNode().getElementsByTagName('x').length; i++)
			if (aMessage.getNode().getElementsByTagName('x').item(i).getAttribute('xmlns') == 'http://jabber.org/protocol/muc#user') {
				x = aMessage.getNode().getElementsByTagName('x').item(i);
				break;
			}
	
		if (x) {
			var from, to, reason, pass;
			to = aMessage.getFrom();
			var aInvite = x.getElementsByTagName('invite').item(0);
			from = aInvite.getAttribute('from');
			if (aInvite.firstChild && aInvite.firstChild.nodeName == 'reason' && aInvite.firstChild.firstChild)
				reason = aInvite.firstChild.firstChild.nodeValue;
			if (x.getElementsByTagName('password').item(0))
				pass = x.getElementsByTagName('password').item(0).firstChild.nodeValue;
			//this.dbg.log("You have been invited to " + jid + " pass " + pass + " by " + from + "\nreason:" + reason,2);
			var user = this.roster.getUserByJID(cutResource(from));
			if (!user) {// users not in roster (yet)
				this.dbg.log("creating new user "+from,3);
				user = this.roster.addUser(new RosterUser(cutResource(from)));
				user.lastsrc = eval(user.status + "Led").src;
				this.roster.print();
			}
	
			if (typeof(user.iwArr) == 'undefined')
				user.iwArr = new Array();
	
			user.iwArr[to] = open(this.makeURL("groupchat_invite.html",{to:to, from:from, pass:pass, reason:reason}),"iw"+makeWindowName(to),"width=320,height=320,resizable=yes");		
		}
	
		return x;
	},
	
	/*
	*/
	setTimestamp: function(aMessage) {
		var x;
		
		// set current timestamp
		for (var i=0; i<aMessage.getNode().getElementsByTagName('x').length; i++)
			if (aMessage.getNode().getElementsByTagName('x').item(i).getAttribute('xmlns') == 'jabber:x:delay') {
				x = aMessage.getNode().getElementsByTagName('x').item(i);
				break;
			}
	
		if (x) {
			this.dbg.log("found offline message: "+x.getAttribute('stamp'),3);
			var stamp = x.getAttribute('stamp');
			aMessage.jwcTimestamp = new Date(Date.UTC(stamp.substring(0,4),stamp.substring(4,6)-1,stamp.substring(6,8),stamp.substring(9,11),stamp.substring(12,14),stamp.substring(15,17)));
		} else
			aMessage.jwcTimestamp = new Date();
	
	},

	/*
	 This method should really be in the roster instance 'aRoster'.
	*/
	handleChat: function(user, from, aMessage, aRoster) {
		this.addChatMsg(user, aMessage);
		
		if (user.chatW && !user.chatW.isClosed && user.chatW.popMsgs) {
			user.chatW.popMsgs();
			this.playSound('chat_recv');
		} else if (this.options.autoPopup && (this.options.autoPopupAway || this.onlstat == "available" || this.onlstat == "chat")) {
			aRoster.openChat(from);
			this.playSound('chat_recv');
		} else {
			if (this.options.focusWindows) window.focus();
				this.playSound('chat_queue');
		}

		// send back to server - [TODO]
		if (this.options.enableLog && typeof(this.loghost) != 'undefined') {
			var aIQ = new JSJaCIQ();
			aIQ.setType('set');
			aIQ.setTo(this.loghost);
            var aNode = aIQ.getNode().appendChild(this.createNodeNS(aIQ.getDoc(), 'archive', 'http://jabber.org/protocol/archive'));
			aNode.appendChild(aMessage.getNode().cloneNode(true));
			if (user.chatW && !user.chatW.isClosed && typeof(user.chatW.cid)!='undefined') {
				aNode.setAttribute('cid',user.chatW.cid);
				this.con.send(aIQ);
			} else
				this.con.send(aIQ,this.storeCIDClosure,user.jid);
		}
	},
	
	/*
	 This method should really be in the roster instance 'aRoster'.
	*/
	handleGroupChat: function(user, aMessage) {
		this.addChatMsg(user, aMessage);
		if (user.chatW && !user.chatW.isClosed && user.chatW.srcW && typeof(user.chatW.srcW.roster) != 'undefined' && user.chatW.popMsgs) {
		  user.chatW.popMsgs();
		} 
		
		this.playSound('chat_recv');			
	},
	
	/*
	Don't really handle events at the moment
	*/
	handleEvent: function(aMessage) {
		if (aMessage.getNode().getElementsByTagName('x').item(0).getAttribute('xmlns') == 'jabber:x:event') {
			this.dbg.log("Received an event from sender", 2);
			return true;
		} else
			return false;
	},
	
	/*
	This is a directed message
	*/
	handleDirectMessage: function(user, from, aMessage, aRoster) {
		this.addMsg(user, aMessage);
		if (this.options.autoPopup && (this.options.autoPopupAway || this.onlstat == "available" || this.onlstat == "chat") && (!user.mW || user.mW.closed)) {
			aRoster.openMessage(from);
			this.playSound('message_recv');
		} else if (user.mW && !user.mW.closed && user.messages.length > 0 && user.mW.document.forms[0]) {
			user.mW.document.forms[0].nextButton.disabled = false;
			if (this.options.focusWindows) user.mW.focus();
			this.playSound('message_recv');
		} else {
			if (this.options.focusWindows) window.focus();
				this.playSound('message_queue');
			// let arrow blink for toggled groups
			for (var i in user.groups) {
				if (user.groups[i] != '') {
					if (this.roster.hiddenGroups[user.groups[i]])
						fmd.images[user.groups[i]+"Img"].src = arrow_right_blinking.src;
				}					
			}		
		}

		// store message
		if (this.options.enableLog && typeof(this.loghost) != 'undefined') {
			var aIQ = new JSJaCIQ();
			aIQ.setType('set');
			aIQ.setTo(this.loghost);
			var aNode = aIQ.getNode().appendChild(this.createNodeNS(aIQ.getDoc(), 'archive', 'http://jabber.org/protocol/archive'));
			aNode.appendChild(aMessage.getNode().cloneNode(true));
			this.con.send(aIQ);
		}
	},
	
	/************************************************************************
	 * handleMessage
	 ************************************************************************
	 */
	handleMessage: function(aMessage) {
	
		this.dbg.log(aMessage.getDoc().xml,2);
	
		if (aMessage.getType() == 'error')
			return;

		if (this.handleGroupChatInvite(aMessage))
			return;
	
		this.setTimestamp(aMessage);

		var from = cutResource(aMessage.getFrom());
		var type = aMessage.getType();
		this.dbg.log("from: "+from+"\naMessage.getFrom(): "+aMessage.getFrom(),3);
		
		var user = this.roster.getUserByJID(from);
		if (user == null) {// users not in roster (yet)
			this.dbg.log("creating new user "+from,3);
			user = this.roster.addUser(new RosterUser(from));
			user.lastsrc = eval(user.status + "Led").src;
			this.roster.print();
		}
		
		this.dbg.log("got user jid: "+user.jid,3);
		
		// Get the right roster
		var aRoster = this.roster;
		if (type != 'groupchat' && user.roster && from != aMessage.getFrom()) { // private groupchat message
			aRoster = user.roster;
			from = aMessage.getFrom(); // use from with resource (had been cut off first)
			user = user.roster.getUserByJID(from);
		}

		// Dispatch based on te type of content
		if (type == 'chat') {
			this.handleChat(user, from, aMessage, aRoster);
		} else if (type == 'groupchat') {		
			this.handleGroupChat(user, aMessage);
		} else {	// Not chat and not groupchat
			if (this.handleEvent(aMessage))
				return;

			// It must be a message
			this.handleDirectMessage(user, from, aMessage, aRoster);
		}
	},

	/************************************************************************
	 * handleMessageError
	 ************************************************************************
	 */
	handleMessageError: function(aJSJaCPacket) {
		if (aJSJaCPacket.getType() != 'error')
			return;
	
		this.dbg.log(aJSJaCPacket.getDoc().xml,3);
	
		var user = this.roster.getUserByJID(cutResource(aJSJaCPacket.getFrom()));
	
		var error = aJSJaCPacket.getNode().getElementsByTagName('error').item(0);
        var msg = aJSJaCPacket.getFrom() + " - ";
		if (error) {
			msg += error.firstChild.nodeValue;
        }
        
		if (user.chatW && !user.chatW.isClosed && user.chatW.putMsgHTML) {
            user.chatW.putMsgHTML(msg,new Date(),aJSJaCPacket.getFrom(),null,true);
		} else {
		    alert("An Error Occured:\n"+msg);
        }
	
		this.playSound('error');
	},

	/************************************************************************
	 * handlePresence
	 ************************************************************************/
	handlePresence: function(presence) {
		this.dbg.log("In PresenceCb",2);
		this.dbg.log(presence.getDoc().xml,2);
	
		var from = cutResource(presence.getFrom());
		var type = presence.getType();
		var show = presence.getShow();
		var status = presence.getStatus();
	
	  var aRoster = this.roster;
	
	  if (from == cutResource(this.jid)) // skip my own presence msgs
		return;
	
	  if (type) {
		if (type == 'subscribe') {
			this.confirmSubscription(from);
			return;
		}
		
		if (type == 'subscribed') {
			var aPresence = new JSJaCPresence();
			aPresence.setTo(from);
			aPresence.setType('subscribe');
			this.con.send(aPresence);	
			
			return;	  
		}
				
		if (type == 'unsubscribe') {
		  alert("You have been unsubscribed from "+presence.getFrom()); /* [TODO] don't use alert here */
		  return;
		}

		if (type == 'unsubscribed') {
			var aPresence = new JSJaCPresence();
			aPresence.setTo(from);
			aPresence.setType('unsubscribe');
			this.con.send(aPresence);	
			
			return;	  
		}
	
			if (type == 'error') { // [TODO]
	
				var user = this.roster.getUserByJID(from);
				if (user && user.chatW && !user.chatW.isClosed && user.chatW.putMsgHTML) {
					if (presence.getNode().getElementsByTagName('error').item(0)) {
						var error = presence.getNode().getElementsByTagName('error').item(0);
						if (error.getElementsByTagName('text').item(0))
							user.chatW.putMsgHTML(error.getElementsByTagName('text').item(0).firstChild.nodeValue,new Date(),from,null,true);
						else if (error.firstChild && error.firstChild.nodeValue)
							user.chatW.putMsgHTML(error.firstChild.nodeValue,new Date(),from,null,true);								
					}
					
				}
	// 			for (var i in jabber.o) {
	// 				if (jabber.o[i].tagname == 'error' && jabber.o[i].data != '')
	// 					alert("From: "+from+"\nError (Code:"+jabber.o[i].code+")\n\n"+jabber.o[i].data);
	// 			}
				return;
			}
	  }
	
	  var user = this.roster.getUserByJID(from);
	  if (!user) // presence from unsubscribed user
	  {/*
		   if (type != 'unavailable') {
				// add user to roster as stalker
				var aIQ = new JSJaCIQ();
				aIQ.setType('set');
				var query = aIQ.setQuery('jabber:iq:roster');
				var aItem = query.appendChild(aIQ.getDoc().createElement('item'));
				aItem.setAttribute('jid',from);
				aItem.setAttribute('name',from);
		
				this.con.send(aIQ);
				
				alert("User '"+from+"' thinks they know you\n\nThey have been added to your\ncontacts as a stalker.");  
		   }
			   */
		return;
	   }
	
		/* handle presence for MUC */
		var x;
		for (var i=0; i<presence.getNode().getElementsByTagName('x').length; i++)
			if (presence.getNode().getElementsByTagName('x').item(i).getAttribute('xmlns') == 'http://jabber.org/protocol/muc#user') {
				x = presence.getNode().getElementsByTagName('x').item(i);
				break;
			}
	
	  if (user.roster && x) { 
		var ofrom = presence.getFrom().substring(presence.getFrom().indexOf('/')+1);
	
		this.dbg.log("jabber.from:"+presence.getFrom()+", ofrom:"+ofrom,3);
	
		var ouser = user.roster.getUserByJID(presence.getFrom());
		if (!ouser) // no user? create one!
		  ouser = new GroupchatRosterUser(presence.getFrom(),ofrom);
	
			var item = x.getElementsByTagName('item').item(0);
	
			ouser.affiliation = item.getAttribute('affiliation');
			ouser.role = item.getAttribute('role');
			ouser.nick = item.getAttribute('nick');
			ouser.realjid = item.getAttribute('jid');
			if (item.getElementsByTagName('reason').item(0))
				ouser.reason = item.getElementsByTagName('reason').item(0).firstChild.nodeValue;
			if (actor = item.getElementsByTagName('actor').item(0)) {
				if (actor.getAttribute('jid') != null)
					ouser.actor = actor.getAttribute('jid');
				else if (item.getElementsByTagName('actor').item(0).firstChild != null)
					ouser.actor = item.getElementsByTagName('actor').item(0).firstChild.nodeValue;
			}
			if (ouser.role != '') {
				ouser.add2Group(ouser.role+'s');
				
				/* check if it is our own presence
				 * must be done here cause we want to be sure that role != ''
				 */
				
				if (ouser.name == user.roster.nick) { // seems to be me
					user.roster.me = ouser; // store this reference
					if (user.chatW.updateMe)
						user.chatW.updateMe();
				}
			}
	
			this.dbg.log("ouser.jid: "+ ouser.jid + ", ouser.fulljid:" + ouser.fulljid + ", ouser.name:"+ouser.name+", user.roster.nick:"+user.roster.nick,3);
	
	
			var nickChanged = false;
			if (x.getElementsByTagName('status').item(0)) {
				var code = x.getElementsByTagName('status').item(0).getAttribute('code');
				switch (code) {
				case '201': // room created
					/* popup dialog to ask for whether to accept default
					 * configuration or make a custom room 
					 */
					if (confirm("A new room has been created but it awaits configuration from you. Do you want to do a custom configuration now?\nNote: Click on 'Cancel' to start with a default configuration!"))
						user.chatW.openConfig();
					else {
						var iq = new JSJaCIQ();
						iq.setType('set');
						iq.setTo(user.jid);
						var query = iq.setQuery('http://jabber.org/protocol/muc#owner');
						var x = query.appendChild(this.createNodeNS(iq.getDoc(), 'x', 'jabber:x:data'));
						x.setAttribute('type','submit');
						
						this.con.send(iq);
					}
					break;
				case '303': // nick change
					// display message
					if (!ouser.nick)
						return;
	
					var aMessage = new JSJaCMessage();
					aMessage.setFrom(user.jid);
					aMessage.setBody(""+ouser.name+" is now known as "+ouser.nick);
					this.addChatMsg(user, aMessage);
					if (user.chatW && !user.chatW.isClosed && user.chatW.popMsgs)
						user.chatW.popMsgs();
	
					// update nick if it's me
					if (ouser.name == user.roster.nick)
						user.roster.nick = ouser.nick;
	
					// remove old user
					var aChatW = ouser.chatW;
					user.roster.removeUser(ouser);
	
					// add new user
					ouser = new GroupchatRosterUser(presence.getFrom().substring(0,presence.getFrom().lastIndexOf('/')+1).concat(ouser.nick),ouser.nick);
	
					if (aChatW && !aChatW.isClosed) {
						ouser.chatW = aChatW;
						ouser.chatW.user = ouser;
					}
					user.roster.addUser(ouser);
					nickChanged = true;
					break;
				case '301': // user has been kicked
					var aMessage = new JSJaCMessage();
					aMessage.setFrom(user.jid);
					var body;
					if (ouser.actor)
						body = ""+ouser.name+" has been banned by "+ouser.actor;
					else
						body = ""+ouser.name+" has been banned";
					if (ouser.reason)
						body += ": " + ouser.reason;
					aMessage.setBody(body);
					this.addChatMsg(user, aMessage);
					if (user.chatW && !user.chatW.isClosed && user.chatW.popMsgs)
						user.chatW.popMsgs();			
					
					this.playSound('chat_recv');
					break;
				case '307': // user has been kicked
					var aMessage = new JSJaCMessage();
					aMessage.setFrom(user.jid);
					var body;
					if (ouser.actor)
						body = ""+ouser.name+" has been kicked by "+ouser.actor;
					else
						body = ""+ouser.name+" has been kicked";
					if (ouser.reason)
						body += ": " + ouser.reason;
					aMessage.setBody(body);
					this.addChatMsg(user, aMessage);
					if (user.chatW && !user.chatW.isClosed && user.chatW.popMsgs)
						user.chatW.popMsgs();	
								
					this.playSound('chat_recv');
					break;
				}
			}
	
		this.dbg.log("<"+ouser.name+"> affiliation:"+ouser.affiliation+", role:"+ouser.role,3);
	
		if (!user.roster.getUserByJID(presence.getFrom()) && !nickChanged) {
				// add user
		  user.roster.addUser(ouser);
	
				// show join message
				var aMessage = new JSJaCMessage();
				aMessage.setFrom(user.jid);
				aMessage.setBody(""+ouser.name+" has become available");
				this.addChatMsg(user, aMessage);
				if (user.chatW && !user.chatW.isClosed && user.chatW.popMsgs)
					user.chatW.popMsgs();			
	
				this.playSound('online');
	
		} else if (presence.getType() == 'unavailable' && !nickChanged) {
				// show part message
				var aMessage = new JSJaCMessage();
				aMessage.setFrom(user.jid);
				var body = ""+ouser.name+" has left";
				if (presence.getStatus())
					body += ": " + presence.getStatus();
				aMessage.setBody(body);
				this.addChatMsg(user, aMessage);
				if (user.chatW && !user.chatW.isClosed && user.chatW.popMsgs)
					user.chatW.popMsgs();			
	
				this.playSound('offline');
	
			} else
		  user.roster.updateGroups();
	
		// relink roster and user
		aRoster = user.roster;
		user = ouser;
	  } 
	
	  if (show) {
		if (user.status == 'unavailable')
		  this.playSound('online');
			// fix broken pressenc status
			if (show != 'chat' && show != 'away' && show != 'xa' && show != 'dnd')
				show = 'available';
		user.status = show;
	  } else if (type) {
		if (type == 'unsubscribe') {
		  user.subscription = 'from';
		  user.status = 'stalker';
		} else if (user.status != 'stalker')
		  user.status = 'unavailable';
		if (aRoster.name == 'GroupchatRoster' && !nickChanged) { // it's a groupchat roster
				// remove user
				if (!user.chatW || user.chatW.isClosed)
					aRoster.removeUser(user); // we don't need offline users in there
			}
		this.playSound('offline');
	  } else {
		if (user.status == 'unavailable') // user was offline before
		  this.playSound('online');
		user.status = 'available';
	  }
	
		var img = eval(user.status+"Led");
	  
	  if (user.lastsrc) // message is pending
		user.lastsrc = img.src;
	  
	  // show away message
	  if (status)
		user.statusMsg = status;
	  else
		user.statusMsg = null;
	  
		// update presence indicator of chat window
		if (user.chatW && !user.chatW.isClosed && user.chatW.updateUserPresence) 
			user.chatW.updateUserPresence();
	
	  aRoster.print(); // update roster
	},
	
	/* *** cascading onconnect callbacks *** */
	getAgentsCb: function(iq) {
		this.dbg.log("In AgentsCb",2);
		if (!iq || iq.getType() != 'result') {
			if (iq)
				this.dbg.log("Error fetching agents:\n"+iq.getDoc().xml,1);
			else
				this.dbg.log("Error fetching agents",1);
		} else {		
			this.dbg.log("got agents:\n"+iq.getDoc().xml,2);
				
			var agents = iq.getNode().firstChild.childNodes;
		
			/* query items */
			for (var i=0; i<agents.length; i++) {
				if (agents[i].nodeName != 'agent' || !agents[i].getAttribute('jid')) // skip those
					continue;
				var agent = new Object();
				for (var j=0; j<agents[i].childNodes.length; j++) {
					var node = agents[i].childNodes[j];
					if (node.firstChild)
						agent[node.nodeName] = node.firstChild.nodeValue;
					else
						agent[node.nodeName] = true;
				}
				this.roster.agents[agents[i].getAttribute('jid')] = agent;
			}
		}

		/* get/setup roster */
		iq = new JSJaCIQ();
		iq.setIQ(null,this.jid,'get','roster_1');
		iq.setQuery('jabber:iq:roster');

		this.roster.showMsg("Get Roster");
		this.con.send(iq, this.getRosterClosure);
	},

	getRosterCb: function(iq) {
		this.dbg.log("In RosterCb",2);
		if (!iq || iq.getType() != 'result') {
			if (iq)
				this.dbg.log("Error fetching roster:\n"+iq.getDoc().xml,1);
			else
				this.dbg.log("Error fetching roster",1);
			return;
		}
	
		this.dbg.log("got roster:\n"+iq.getDoc().xml,2);
		
		this.roster.addUsers(iq.getQuery().childNodes);

		// get saved state
		iq = new JSJaCIQ();
		iq.setIQ(null,this.jid,'get','jwchat_state');
		var query = iq.setQuery('jabber:iq:private');
		query.appendChild(this.createNodeNS(iq.getDoc(), 'jwchat', 'jwchat:state'));
	
		this.roster.showMsg("Get Saved State");
		this.con.send(iq, this.getSavedStateClosure);
	},

	getSavedStateCb: function(iq) {	   
		this.dbg.log("In SaveStateCb",2);
		
		if (!iq || iq.getType() != 'result')
			if (iq)
				this.dbg.log("Error retrieving saved state:\n"+iq.getDoc().xml,1);
			else
				this.dbg.log("Error retrieving saved state",1);
				
		if (iq && iq.getType() == 'result') {
			this.dbg.log(iq.getDoc().xml,3);
			var jNode = iq.getNode().getElementsByTagName('jwchat').item(0);
			for (var i=0; i<jNode.childNodes.length; i++) {
				var item = jNode.childNodes.item(i);
				if (item.nodeName == 'presence' && item.firstChild && this.onlstat == '')
					this.onlstat = item.firstChild.nodeValue;
				if (item.nodeName == 'hiddenGroups' && item.firstChild) {
					var hiddenGroups = item.firstChild.nodeValue.split(',');
					for (var j in hiddenGroups)
						if (hiddenGroups[j] != '')
							this.roster.hiddenGroups[hiddenGroups[j]] = true;
				}
			}
		}
	
		// get prefs
		iq = new JSJaCIQ();
		iq.setIQ(null,this.jid,'get','jwchat_prefs');
		var query = iq.setQuery('jabber:iq:private');
		query.appendChild(this.createNodeNS(iq.getDoc(), 'jwchat','jwchat:prefs'));
	
		this.roster.showMsg("Get Preferences");
		this.con.send(iq, this.getPrefsClosure);
	},

	getPrefsCb: function(iq) {
		this.dbg.log("In PrefsCb",2);
		if (!iq || iq.getType() != 'result')
			if (iq)
				this.dbg.log("Error retrieving preferences:\n"+iq.getDoc().xml,1);
			else
				this.dbg.log("Error retrieving preferences",1);
	
		this.dbg.log("preferences:\n"+iq.getDoc().xml,1);
		if (iq && iq.getType() == 'result') {
			this.dbg.log(iq.getDoc().xml,3);
			if (iq.getNode().getElementsByTagName('jwchat').item(0)) {
				var jNode = iq.getNode().getElementsByTagName('jwchat').item(0);
				for (var i=0; i<jNode.childNodes.length; i++) {
					this.options[jNode.childNodes.item(i).nodeName] = jNode.childNodes.item(i).firstChild.nodeValue.toLowerCase() == "true";
				}
			}
		}
	
	   if (is.iPhone) {
		   this.options.autoPopup = false;
		   this.options.autoPopupAway = false;
		   this.options.playSounds = false;
	   }
		this.roster.usersHidden = this.options.usersHidden;
		this.roster.emptyGroups = this.options.emptyGroups;
		this.con.setPollInterval(this.options.timerval);

		// print roster
		this.roster.print();
	
		// send presence
		this.dbg.log('onlstat='+this.onlstat, 2);
		if (this.onlstat == '')
			this.onlstat = 'available';
		this.dbg.log('onlstat='+this.onlstat, 2);
		this.changeStatus(this.onlstat);
	
		this.playSound('connected');
	
		// Start Service Discovery
		iq = new JSJaCIQ();
		iq.setIQ(this.con.domain,this.jid,'get','disco_item_1');
		iq.setQuery('http://jabber.org/protocol/disco#items');
	
		this.con.send(iq,this.getDiscoItemsClosure);
	
		// get bookmarks
		iq = new JSJaCIQ();
		iq.setIQ(null,this.jid,'get','storage_bookmarks');
		var query = iq.setQuery('jabber:iq:private');
		query.appendChild(this.createNodeNS(iq.getDoc(), 'storage', 'storage:bookmarks'));
	
		this.con.send(iq, this.getBookmarksClosure);
	
		// get annotations
		iq = new JSJaCIQ();
		iq.setIQ(null,this.jid,'get','jwchat_notes');
		var query = iq.setQuery('jabber:iq:private');
		query.appendChild(this.createNodeNS(iq.getDoc(), 'jwchat', 'jwchat:notes'));
	
		this.con.send(iq, this.getAnnotationsClosure);
		
		// browse agents
		for (var i in this.roster.agents) {
		   iq = new JSJaCIQ();
		   iq.setIQ(i,this.jid,'get','browse_'+i);
			iq.setQuery('jabber:iq:register');
			
			this.con.send(iq,this.getAgentBrowseClosure);
		}
	},

	savePrefs: function() {
		var iq = new JSJaCIQ();
		iq.setType('set');
		var query = iq.setQuery('jabber:iq:private');
		var jNode = query.appendChild(this.createNodeNS(iq.getDoc(), 'jwchat', 'jwchat:prefs'));

		this.options.usersHidden = this.roster.usersHidden;
		this.options.emptyGroups = this.roster.emptyGroups;
		for (var i in this.options) {
			var item = jNode.appendChild(iq.getDoc().createElement(i));
   			item.appendChild(iq.getDoc().createTextNode(this.options[i]));
		}

		this.dbg.log(iq.getDoc().xml,3);

		this.con.syncSend(iq);
	},

	getAgentBrowseCb: function(iq) {
		this.dbg.log("In AgentBrowseCb", 2);
		if (!iq)
			return;
		this.dbg.log(iq.getDoc().xml,2);
	},

	getServerBrowseCb: function(iq) {
		this.dbg.log("In ServerBrowseCb", 2);
		if (!iq)
			return;
		this.dbg.log(iq.getDoc().xml,2);
	},

	getDiscoItemsCb: function(iq) {
		if (!iq || iq.getType() != 'result') {
		/* Doesn't seem to give us anything that get agents doesn't give us */
			iq = new JSJaCIQ();
			iq.setIQ(this.con.domain,this.jid,'get','browse_1');
			iq.setQuery('jabber:iq:browse');
	
			this.con.send(iq,this.getServerBrowseClosure);
			return;
		}
		
		this.dbg.log(iq.getDoc().xml,2);
	
		var items = iq.getNode().firstChild.childNodes;
	
		/* query items */
		for (var i=0; i<items.length; i++) {
			if (items[i].nodeName != 'item' || !items[i].getAttribute('jid') || items[i].getAttribute('node')!=null) // skip those
				continue;
			var aIQ = new JSJaCIQ();
			aIQ.setIQ(items[i].getAttribute('jid'),this.jid,'get','disco_info_'+i);
			aIQ.setQuery("http://jabber.org/protocol/disco#info");
			
			this.con.send(aIQ, this.getDiscoInfoClosure);
		}
	},

	getDiscoInfoCb: function(iq) {
		if (!iq || iq.getType() != 'result')
			return;
	
		this.dbg.log(iq.getDoc().xml,2);
		if (iq.getType() == 'result') {
			this.roster.disco[iq.getFrom()] = iq;
			
			// If the identity does not have a name, set the name to jid
			if(iq.getNode().getElementsByTagName('identity').item(0).getAttribute('name') == null)
				iq.getNode().getElementsByTagName('identity').item(0).setAttribute('name', iq.getFrom());
	
			// set this.loghost
			if (iq.getNode().getElementsByTagName('identity').item(0)) {
				if (iq.getNode().getElementsByTagName('identity').item(0).getAttribute('category') == 'store') {
					for (var j=0; j<iq.getNode().getElementsByTagName('feature').length; j++) {
						if (iq.getNode().getElementsByTagName('feature').item(j).getAttribute('var') == 'http://jabber.org/protocol/archive') {
							this.loghost = iq.getFrom();
							break;
						}
					}
				}
			}
		}
	},

	getBookmarksCb: function(iq) {
		if (!iq || iq.getType() != 'result')
			return;
	
		this.dbg.log(iq.getDoc().xml,2);
	
		this.bookmarks = new Array();
	
		if (iq.getNode().getElementsByTagName('storage').item(0)) {
			var jNode = iq.getNode().getElementsByTagName('storage').item(0);
			for (var i=0; i<jNode.childNodes.length; i++) {
				var item = jNode.childNodes.item(i);
				if (item.nodeName == 'conference') {
					var bookmark = new Object();
					bookmark.jid = item.getAttribute('jid');
					bookmark.name = item.getAttribute('name');
					if (item.getAttribute('autojoin') == '1')
						bookmark.autojoin = '1';
					if (item.getElementsByTagName('nick').item(0))
						bookmark.nick = item.getElementsByTagName('nick').item(0).firstChild.nodeValue;
					if (item.getElementsByTagName('pass').item(0))
						bookmark.pass = item.getElementsByTagName('pass').item(0).firstChild.nodeValue;
					this.bookmarks[bookmarks.length] = bookmark;
					if (bookmark.autojoin == '1') {
						this.openGroupchat(bookmark.jid, bookmark.nick, bookmark.pass);
					}
				}
			}
		}
	},

	getAnnotationsCb: function(iq) {
		if (!iq || iq.getType() != 'result')
			return;
		
		this.dbg.log(iq.getDoc().xml,2);
		
		this.annotations = new Array();
	
		if (iq.getType() == 'result') {
			if (iq.getNode().getElementsByTagName('jwchat').item(0)) {
				var jNode = iq.getNode().getElementsByTagName('jwchat').item(0);
				for (var i=0; i<jNode.childNodes.length; i++)
					if (jNode.childNodes.item(i).nodeName == 'item' && jNode.childNodes.item(i).firstChild)
						this.annotations[jNode.childNodes.item(i).getAttribute('jid')] = jNode.childNodes.item(i).firstChild.nodeValue;
			}
		}
	},
	
	addChatMsg: function(user, msg) {
		user.chatmsgs = user.chatmsgs.concat(msg);
		this.raiseEvent(this.addChatMsg, user, msg);
	},
	
	addMsg: function(user, msg) {
		user.messages = user.messages.concat(msg);
		this.raiseEvent(this.addMsg, user, msg);
	},
	
	/************************************************************************
	 *                       ******  CHANGESTATUS   *******
	 ************************************************************************/
	sendPresence2Groupchats: function(gc,val,away) {
		var aPresence;
		for (var i=0; i<gc.length; i++) {
			aPresence = new JSJaCPresence();
			//aPresence.setXMLNS();
			aPresence.setTo(gc[i]);
			if (away && away != '')
				aPresence.setStatus(away);
			if (val != 'available')
				aPresence.setShow(val);
			this.con.syncSend(aPresence);
		}
	},
	
	changeStatus: function(val,away) {
	   if (val == "reconnect") {
		   this.reconnect();
		   return;
	   }
		this.dbg.log("changeStatus: "+val+","+away, 2);
		  
		this.onlstat = val;
		var item = $('status_'+val);
		if (item) {
			item.selected=true;
		}
		if (away)
			this.onlmsg = away;
		
		if (!this.con.connected() && val != 'offline') {
			this.init();
			this.connect();
			return;
		}
				
		var aPresence = new JSJaCPresence();
		  
		switch(val) {
		case "unavailable":
			val = "invisible";
			aPresence.setType('invisible');
			break;
		case "offline":
			val = "unavailable";
			aPresence.setType('unavailable');
			this.con.syncSend(aPresence);
			this.con.disconnect();
			var img = eval(val+"Led");
			// statusLed.src = img.src;
			// if (away)
			//   this.statusMsg.value = away;
			// else
			//   this.statusMsg.value = Jabber.onlstatus[val];
			this.cleanUp();
			return;
			break;
		case "available":
			val = 'available'; // needed for led in status bar
			if (away) {
				aPresence.setStatus(away);
			}
			aPresence.setPriority(8);
			break;
		case "chat":
			aPresence.setPriority(8);
		default:
			if (away) {
				aPresence.setStatus(away);
			}
			aPresence.setShow(val);
		}
		
		this.con.syncSend(aPresence);
		
		/* send presence to chatrooms
		 */
		if (typeof(this.roster) != 'undefined' && this.onlstat != 'invisible') {
			this.sendPresence2Groupchats(this.roster.getGroupchats(),this.onlstat,this.onlmsg);
		}
		
		var img = eval(val+"Led");
		// statusLed.src = img.src;
		// if (away)
		// 	this.statusMsg.value = away;
		// else
		// 	this.statusMsg.value = Jabber.onlstatus[val];
	},
	
	storeCIDCb: function(iq,jid) {
		if (!iq || iq.getType() != 'result')
			return;
	
		this.dbg.log(iq.getDoc().xml,2);
		var user = this.roster.getUserByJID(jid);
		if (user == null) {
			this.dbg.log("user with jid " + jid + " not found",1);
			return;
		}
	
			user.chatW.cid = iq.getNode().firstChild.getAttribute('cid');
	},
	
	/************************************************************************
	 *                         ****** Pop-Up's *******
	 ************************************************************************/

	confirmSubscription: function(aJid)	 {
		// 3. This request is out of the blue, ask if we want to subscribe:
		//    a) If the answer is 'yes' then reply with a 'presence' of type 'subscribed' followed
		//       by a presence of type 'subscribe'.
		//    b) If the answer is 'no' then reply with a 'presence' of type 'unsubscribed'
		if ((this.roster && this.roster.isGateway(aJid)) || confirm("'"+aJid+"' wants to add you as a contact. OK?")) {
			var fromPresence = new JSJaCPresence();
			fromPresence.setType('subscribed'); // We want them to see us!
			fromPresence.setTo(aJid);
			  
			this.con.syncSend(fromPresence);
		} else {
			this.rejectSubscription(aJid);
		}
	},

	subscribe: function(aJid) {
		var toPresence = new JSJaCPresence();
		toPresence.setType('subscribe');
		toPresence.setTo(aJid);
		
		this.con.syncSend(toPresence);
	},
	
	unsubscribeHandler: function(el) {
		var aJid = this.elToId(el);
		if (confirm("Unsubscribe from contact '"+aJid+"'?")) {
			this.unsubscribe(aJid);
		}
	},
	
	unsubscribe: function(aJid) {
		var fromPresence = new JSJaCPresence();
		fromPresence.setType('unsubscribe'); // Tell them to go away
		fromPresence.setTo(aJid);
		
		this.con.syncSend(fromPresence);
	},
	
	rejectSubscription: function(aJid) {
		var fromPresence = new JSJaCPresence();
		fromPresence.setType('unsubscribed'); // Tell them to go away
		fromPresence.setTo(aJid);
		
		this.con.syncSend(fromPresence);
	},
	
	openChangeStatus: function() {
	  if (!this.onlStatW || this.onlStatW.closed)
		this.onlStatW = open(this.makeURL("changestatus.html"),"onlStatW"+makeWindowName(this.jid),"width=330,height=240,resizable=yes");
	  this.onlStatW.focus();
	  return false;
	},

	openCustomPresence: function(aJid) {
		var user = this.roster.getUserByJID(aJid);
		if (!user)
			return;
	  if (!user.onlStatW || user.onlStatW.closed)
		user.onlStatW = open(this.makeURL("changestatus.html",{jid:aJid}),"onlStatW","width=330,height=240,resizable=yes");
	  user.onlStatW.focus();
	  return false;
	},
	
	sendCustomPresence: function(aJid,presence,msg) {
		var oPresence = new JSJaCPresence();
		oPresence.setTo(aJid);
		if (this.roster.getUserByJID(aJid).roster)
			oPresence.setXMLNS();
	
		switch (presence) {
		case 'offline':
			oPresence.setType('unavailable');
		case 'unavailable':
			oPresence.setType('unavailable');
			presence = "invisible";
		default:
			if (presence != 'available')
				oPresence.setShow(presence);
		}
	
		if (typeof(msg) != 'undefined' && msg != '')
			oPresence.setStatus(msg);
	
		this.dbg.log(oPresence.getDoc().xml,2);
		this.con.send(oPresence);
	},

	openUserInfoHandler: function(el) {
	   this.openUserInfo(this.elToId(el));
	},
	
	openUserInfo: function(aJid) {
		var newin = open(this.makeURL("vcard.html",{jid:aJid}),"vcardW"+makeWindowName(aJid),"width=400,height=580,scrollbars=yes");
	
		if (cutResource(aJid) == cutResource(this.jid))
			vcardW = newin;
		else {
			var user = this.roster.getUserByJID(cutResource(aJid));
			if (!user) {
				user = new RosterUser(aJid);
				this.roster.addUser(user);
			}
			if (user.roster) // groupchat(!)
				user = user.roster.getUserByJID(aJid);
			user.vcardW = newin;
		}
	
		return false;
	},
	
	openUserHistoryHandler: function(el) {
	   this.openUserHistory(this.elToId(el));
	},
	
	openUserHistory: function(aJid) {
	
		if (typeof(this.loghost) == 'undefined' || this.loghost == '')
			return;
	
		var user = this.roster.getUserByJID(aJid);
	
		if (user == null)
			return;
	
		if (!user.histW || user.histW.closed)
			user.histW = open(this.makeURL("userhist.html",{jid:aJid}),"histW"+makeWindowName(aJid),"width=600,height=400,resizable=yes,scrollbars=no");
		user.histW.focus();
	},
	
	openSearch: function() {
		if (!searchW || searchW.closed)
			searchW = open(this.makeURL("search.html"),"searchW"+makeWindowName(this.jid),"width=480,height=260,resizable=yes,scrollbars=yes");
		searchW.focus();
		return false;
	},
	
	openEditBookmarks: function() {
		if (!ebW || ebW.closed)
			ebW = open(this.makeURL("editbookmarks.html"),"ebw"+makeWindowName(this.jid),"width=330,height=290,resizable=yes");
		return false;
	},
	
	/* system sounds */
	playSound: function(action) {	
	  if (!this.options.playSounds)
		return;
	
	  if(!SOUNDS[action]) {
		this.dbg.log("no sound for '" + action + "'",1);
		return;
	  }
	
		this.dbg.log("Playsound()", 2);
		if (this.onlstat != '' && this.onlstat != 'available' && this.onlstat != 'chat')
			return;
	
		var soundDiv = $("body_"+this.id);
		
		if (!soundDiv)
			return;
		
		if (this.sound2Embed) {
			try {
				soundDiv.removeChild(this.sound2Embed);
			} catch (e) {
			}
			this.sound2Embed = null;
		}
		
		var soundEl = document.createElement("embed");
		soundEl.setAttribute("id", this.id + "_sound");
		soundEl.setAttribute("src", SOUNDS[action]);
		soundEl.setAttribute("hidden", true);
		soundEl.setAttribute("autostart", true);
		soundEl.setAttribute("quality", "high");
		soundEl.setAttribute("pluginspage", "http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash");
		soundEl.setAttribute("type", "application/x-shockwave-flash");
		soundDiv.appendChild(soundEl);
		this.sound2Embed = soundEl;
	}
});
