
var XMPPClient = new Class({
	boshURL: "/http-bind/",
	connection: null,
	connected: false,
	statuses: {}, // full JID -> status
	subscriptions: null, // bare JID -> subscription state

	// internal event handlers
	
	onConnect: function(status) {
		this.connected = (status == Strophe.Status.CONNECTED);
		if (status == Strophe.Status.DISCONNECTED && false)
			alert("Disconnected");
		if (this.connected) {
			this.connection.send($pres().tree());
			this.getRoster();
			this.connection.addHandler(this.onMessage.bind(this), null, 'message', 'chat');
			this.connection.addHandler(this.onSubscribe.bind(this), null, 'presence', 'subscribe');
			this.connection.addHandler(this.onSubscribed.bind(this), null, 'presence', 'subscribed');
			this.connection.addHandler(this.onStatusChange.bind(this), null, 'presence');
			this.connection.addHandler(this.onRosterFetched.bind(this), Strophe.NS.ROSTER, 'iq');
		}
		return true;
	},
	
	getRoster: function() {
		this.connection.sendIQ(
			$iq({type: 'get'}).c('query', {xmlns: Strophe.NS.ROSTER}).tree(),
			this.onRosterFetched.bind(this));
	},

	onRawInput: function(data) { if (false && data.indexOf('s') != -1) alert("RECVD: " + data); },
	onRawOutput: function(data) { if (false && data.indexOf('subscribe') != -1) alert("SENT: " + data); },

	// public interface

	initialize: function() {
		this.connection = new Strophe.Connection(this.boshURL);
		this.connection.rawInput = this.onRawInput.bind(this);
		this.connection.rawOutput = this.onRawOutput.bind(this);
	},

	connect: function(user, password, resource) {
		this.connection.connect(user + '/' + resource, password,
			this.onConnect.bind(this));
	},

	disconnect: function() {
		this.connection.disconnect();
	},

	ensureConnection: function() {
		if (!this.connected)
			throw "Not connected";
	},

	// XMPP actions
	
	sendMessage: function(to, text) {
		this.ensureConnection();
		this.connection.send($msg({'to': to, type: 'chat'}).c('body').t(text).tree());
	},

	sendChatNotification: function(to, state) {
		this.ensureConnection();
		this.connection.send($msg({'to': to, type: 'chat'}).c(state).attrs({'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
	},

	subscribe: function(to) {
		this.ensureConnection();
		this.connection.send($pres({'to': to, type: 'subscribe'}).tree());
	},

	replyToSubscribe: function(to, success) {
		this.ensureConnection();
		this.connection.send($pres({'to': to, type: success ? 'subscribed' : 'unsubscribed'}).tree());
	},

	deleteFromRoster: function(contact, callback) {
		this.ensureConnection();
		var attrs = {'jid': contact, 'subscription': 'remove'};
		this.connection.sendIQ($iq({type: 'set'}).c('query', {xmlns: Strophe.NS.ROSTER}).c('item').attrs(attrs).tree(), callback, callback);
	},

	// events

	getChatPanel: function(contact, dontCreate) {
		var bare = Strophe.getBareJidFromJid(contact);
		var panel = chatPanelPlacer.getPanel(bare);
		if (panel)
			return {panel: panel, created: false};
		else if (dontCreate)
			return null;
		return {panel: new ChatPanelParent(bare, this, chatPanelPlacer, titleFlasher), created: true};
	},

	onMessage: function(msg) {
		var elems = msg.getElementsByTagName('body');
		var from = msg.getAttribute('from');
		if (!from)
			return true;
		var bare = Strophe.getBareJidFromJid(from);
		if (elems.length == 0 && !chatPanelPlacer.getPanel(bare))
			return true;
		var chatPanel = this.getChatPanel(from);
		chatPanel.panel.onMessage(msg);
		return true;
	},
	
	onSubscribe: function(msg) {
		var from = msg.getAttribute('from');
		if (!from)
			return true;
		// always allow subscription
		this.replyToSubscribe(from, true);
		var chatPanel = this.getChatPanel(from, true);
		if (chatPanel)
			chatPanel.panel.onIncomingSubscribe(msg);
		return true;
	},
	
	onSubscribed: function(msg) {
		this.getRoster();
		return true;
	},
	
	checkVoice: function(jid) {
		var found = false;
		for (var fullJid in this.statuses) {
			if (jid == Strophe.getBareJidFromJid(fullJid)) {
				found = true;
				if (this.statuses[fullJid].voice)
					return 'voice';
			}
		}
		if (found)
			return 'novoice';
		else {
			if (this.subscriptions && (this.subscriptions[jid] == 'to' || this.subscriptions[jid] == 'both'))
				return 'noanswer';
			else
				return 'noauth';
		}
	},

	// status management
	parseStatus: function(msg) {
		var result = {status: 'available', text: null};
		var type = msg.getAttribute('type');
		if (type == 'unavailable')
			result.status = 'offline';
		var statusElements = msg.getElementsByTagName('status');
		if (statusElements && statusElements.length > 0) {
			result.text = Strophe.getText(statusElements[0]);
		}
		var cElements = msg.getElementsByTagName('c');
		if (cElements && cElements.length > 0) {
			var ext = cElements[0].getAttribute('ext');
			result.ext = ext;
			if (ext.indexOf('voice-v1') != -1)
				result.voice = true;
		}
		return result;
	},

	getStatus: function(contact) {
		for (var fullJid in this.statuses) {
			if (contact == Strophe.getBareJidFromJid(fullJid)) {
				return this.statuses[fullJid];
			}
		}
		return {status: 'offline', text: null};
	},

	onStatusChange: function(msg) {
		var type = msg.getAttribute('type');
		if (type && type != 'unavailable')
			return true;
		var from = msg.getAttribute('from');
		var statusInfo = this.parseStatus(msg);
		if (statusInfo.status != 'offline')
			this.statuses[from] = statusInfo;
		else
			delete this.statuses[from];
		var chatPanel = this.getChatPanel(from, true);
		if (chatPanel)
			chatPanel.panel.onStatusChange(msg);
		// TODO: put into some other place
		var statusImg = document.getElement('img[name=status_' + Base64.encode(Strophe.getBareJidFromJid(from)) + ']');
		if (statusImg)
			statusImg.src = '/static/img/pidgin/' + statusInfo.status + '.png';
		return true;
	},

	// roster management
	onRosterFetched: function(msg) {
		if (!this.subscriptions)
			this.subscriptions = {};
		var items = msg.getElementsByTagName('item');
		for (var i = 0; i < items.length; i++) {
			var contact = items[i].getAttribute('jid');
			var subscription = items[i].getAttribute('subscription');
			if (contact && subscription)
				this.subscriptions[contact] = subscription;
		}
	}

});

var ChatPanelPlacer = new Class({
	panelWidth: 250,
	lowerHeight: 200,
	titleHeight: 20,
	lowerBorder: 1,
	horizontalMargin: 20,
	panels: [],

	onWindowResize: function(event) {
		for (var idx = 0; idx < this.panels.length; idx++) {
			var panel = this.panels[idx];
			if (panel)
				panel.position();
		}
	},

	initialize: function() {
		window.addEvent('resize', this.onWindowResize.bind(this));
		window.addEvent('scroll', this.onWindowResize.bind(this));
	},

	getDims: function(idx) {
		return {
			right: this.horizontalMargin * (idx + 1) + this.panelWidth * idx,
			height: this.lowerHeight + this.titleHeight + 2 * this.lowerBorder,
			titleHeight: this.titleHeight
		};
	},

	getPanelDims: function(panel) {
		for (var idx = 0; idx < this.panels.length; idx++) {
			if (this.panels[idx] == panel)
				return this.getDims(idx);
		}
		return null;
	},

	registerPanel: function(panel) {
		var idx = 0;
		for (idx = 0; idx < this.panels.length; idx++) {
			if (!this.panels[idx])
				break;
		}
		this.panels[idx] = panel;
	},

	unregisterPanel: function(panel) {
		for (var idx = 0; idx < this.panels.length; idx++) {
			if (this.panels[idx] == panel)
				this.panels[idx] = null;
		}
	},

	getPanel: function(contact) {
		for (var idx = 0; idx < this.panels.length; idx++) {
			if (this.panels[idx] && this.panels[idx].contact == contact)
				return this.panels[idx];
		}
		return null;
	},

	closeChildren: function() {
		this.panels.each(function(panel) {
			panel.closeChild();
		});
	}
});

var TitleFlasher = new Class({
	titles: [],
	originalTitle: null,

	flash: function() {
		if (document.title != this.originalTitle)
			document.title = this.originalTitle;
		else if (this.titles.length != 0)
			document.title = this.titles[0];
	},

	initialize: function() {
		this.originalTitle = document.title;
		this.flash.periodical(2000, this);
	},

	addTitle: function(title) {
		if (!this.titles.contains(title))
			this.titles.push(title);
	},

	removeTitle: function(title) {
		this.titles.erase(title);
	}

});

var titleFlasher = new TitleFlasher();

var ChatPanelBase = new Class({
	xmppClient: null,
	titleFlasher: null,
	contact: null,
	resource: null,
	
	panel: null,
	chatDialog: null,
	lastDialogWidth: 0,
	chatBox: null,
	
	testSpan: null, // span for testing font width
	pauseTimeout: null,
	urlre: null,

	initialize: function(contact, xmppClient, titleFlasher) {
		if (!contact || !xmppClient || !titleFlasher)
			throw "Not enough parameters to create chat panel";

		this.contact = contact;
		this.xmppClient = xmppClient;
		this.titleFlasher = titleFlasher;
	
		// main panel	
		this.panel = new Element('div', {'class': 'chatPanel'}).inject(document.body);
		this.panel.addEvents({
			'click': (function(event) {
					this.titleFlasher.removeTitle(this.getTitle());
				}).create({bind: this, event: true})
		});

		// span for testing text width
		this.testSpan = new Element('span', {'class': 'chatDialog'}).inject(document.body);

		// title
		var chatTitle = new Element('div', {'class': 'chatTitle'}).inject(this.panel);

		new Element('img', {'class': 'chatStatus', 'src': ''}).inject(chatTitle);
		var shownContact = contact;
		var maxContactLength = this.getMaxContactLength();
		if (contact.length > maxContactLength)
			shownContact = contact.substr(0, maxContactLength - 1) + '...';
		new Element('div', {'class': 'chatContact', 'html': shownContact}).inject(chatTitle);
		
		var lowerPanel = new Element('div', {'class': 'chatLowerPanel'}).inject(this.panel);

		// chat dialog
		this.chatDialog = new Element('div', {'class': 'chatDialog'}).inject(lowerPanel);
		
		// chat box
		var chatBoxPanel = new Element('div', {'class': 'chatBoxPanel'}).inject(lowerPanel);
		var chatBox = new Element('textarea', {'class': 'chatBox'}).inject(chatBoxPanel);
		chatBox.addEvent('keypress', this.onKeyPress.create({'bind': this, 'event': true}));
		chatBox.focused = false;
		chatBox.addEvent('focus', function() {chatBox.focused = true;});
		chatBox.addEvent('blur', function() {chatBox.focused = false;});
		this.chatBox = chatBox;

		// change status
		this.changeStatus(this.xmppClient.getStatus(contact));

		this.urlre = /^(((http|ftp|https):\/\/)|([wW]{3}\.))([A-Za-z0-9-]+\.)+([A-Za-z]{2,3})($|(\/(\S)*$)|(\?(\S)*$))/;
	},

	getMaxContactLength: function() {
		return Number.MAX_VALUE;
	},

	focusChatBox: function() {
		this.chatBox.focus();
	},

	getTextWidth: function(text) {
		this.testSpan.innerHTML = text;
		var width = this.testSpan.offsetWidth ? this.testSpan.offsetWidth : this.testSpan.clientWidth;
		this.testSpan.innerHTML = '';
		return width;
	},

	prepareText: function(text) {
		var escaped = text.replace(/&/g,'&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/'/g,'\\\'');
		var words = escaped.split(' ');
		var newwords = words.map(function(word) {
			var result = [];
			var copy = new String(word);
			while (copy.length > 0) {
				var maxLength = 60;
				if (this.lastDialogWidth > 0) {
					while (this.getTextWidth(copy.substr(0, maxLength)) > this.lastDialogWidth - 30)
						maxLength -= 5;
				}
				result.push(copy.substr(0, maxLength));
				copy = copy.substr(maxLength, copy.length);
			}
			result = result.join('<br>');
			if (this.urlre.test(word)) {
				var url = word;
				if (/^[wW]{3}/.test(word))
					url = 'http://' + url;
				result = '<a href=' + url + ' target="_blank">' + result + '</a>';
			}
			return result;
		}, this);
		return newwords.join(' ');
	},

	addMessage: function(source, text) {
		var prepared = this.prepareText(text);
		var chatMessage = new Element('div', {'class': 'chatMessage', 'html': '<b>' + source + ': </b>' + prepared, 'name': 'chatMessage'});
		var chatObjects = this.chatDialog.getElementsByTagName('div');
		var lastMessage = null;
		for (var i = 0; i < chatObjects.length; i++) {
			if (chatObjects[i].name != 'chatState')
				lastMessage = chatObjects[i];
		}
		if (!lastMessage)
			chatMessage.inject(this.chatDialog, 'top');
		else
			chatMessage.inject(lastMessage, 'after');
		this.chatDialog.scrollTop = this.chatDialog.scrollHeight;
	},
	
	addInfo: function(text, name) {
		var chatMessage = new Element('div', {'class': 'chatInfo', 'html': text, 'name': name});
		chatMessage.inject(this.chatDialog);
		this.chatDialog.scrollTop = this.chatDialog.scrollHeight;
	},

	deleteInfo: function(name) {
		var elems = this.panel.getElements('div[name=' + name + ']');
		for (var i = 0; i < elems.length; i++) {
			elems[i].dispose();
			elems[i].destroy();
		}
	},

	changeStatus: function(statusInfo) {
		var statusImg = this.panel.getElement('img[class=chatStatus]');
		statusImg.src = '/static/img/pidgin/' + statusInfo.status +'.png';
		statusImg.title = statusInfo.text ? statusInfo.text : statusInfo.status;
	},

	checkSubscription: function() {
		if (!this.xmppClient.subscriptions)
			return;
		var subscription = this.xmppClient.subscriptions[this.contact];
		if (subscription != 'to' && subscription != 'both') {
			this.xmppClient.subscribe(this.contact);
			this.addInfo(gettext('asked_authorization'), 'authInfo');
		}
	},

	getFullContact: function() {
		var to = this.contact;
		if (this.resource) {
			if (this.xmppClient.statuses[to + '/' + this.resource])
				to += '/' + this.resource;
			else
				this.resource = null;
		}
		return to;
	},

	onPaused: function() {
		this.xmppClient.sendChatNotification(this.getFullContact(), 'paused');
		this.pauseTimeout = null;
	},

	onKeyPress: function(event) {
		this.titleFlasher.removeTitle(this.getTitle());
		if (event.key != 'enter') {
			if (this.pauseTimeout)
				$clear(this.pauseTimeout);
			else
				this.xmppClient.sendChatNotification(this.getFullContact(), 'composing');
			this.pauseTimeout = this.onPaused.delay(5000, this);
			return;
		}
				
		var text = this.chatBox.value;
		if (/^(\s*)$/.test(text))
			return false;
		
		if (this.pauseTimeout) {
			$clear(this.pauseTimeout);
			this.onPaused();
		}

		var to = this.getFullContact();
		this.xmppClient.sendMessage(to, text);
		this.addMessage(gettext('me'), text);
		this.checkSubscription();
		this.chatBox.value = "";
		return false;
	},

	getTitle: function() {
		return Strophe.getNodeFromJid(this.contact) + ' ' + gettext('wrote_message') + '...';
	},

	flashTitle: function() {
		this.titleFlasher.addTitle(this.getTitle());
	},

	onMessage: function(msg) {
		this.resource = Strophe.getResourceFromJid(msg.getAttribute('from'));
		var elems = msg.getElementsByTagName('body');
		if (elems.length == 0) {
			var composing = msg.getElementsByTagName('composing').length != 0;
			var paused = msg.getElementsByTagName('paused').length != 0;
			//var active = msg.getElementsByTagName('active').length != 0;
			//var inactive = msg.getElementsByTagName('inactive').length != 0;
			this.deleteInfo('chatState');
			if (composing)
				this.addInfo(Strophe.getNodeFromJid(this.contact) + ' ' + gettext('is_typing') + '...', 'chatState');
		} else {
			this.addMessage(Strophe.getNodeFromJid(this.contact), Strophe.getText(elems[0]));
			//if (!this.chatBox.focused)
			this.flashTitle();
			this.checkSubscription();
		}
		return true;
	},

	onIncomingSubscribe: function(pres) {
		this.addInfo(gettext('authorization_sent'), 'authInfo');
		return true;
	},

	onStatusChange: function(pres) {
		var type = pres.getAttribute('type');
		if (type && type != 'unavailable')
			return true;
		this.changeStatus(this.xmppClient.getStatus(this.contact));
		return true;
	}

});

var ChatPanelParent = new Class({
	Extends: ChatPanelBase,

	panelRegistrar: null,
	minimized: null,
	childWindow: null, // external chat window
	maxContactLength: 21,

	initialize: function(contact, xmppClient, panelRegistrar, titleFlasher) {
		this.panelRegistrar = panelRegistrar;
		this.panelRegistrar.registerPanel(this);
		this.parent(contact, xmppClient, titleFlasher);

		var chatTitle = this.panel.getElement('div[class=chatTitle]');
		var chatClose = new Element('div', {'class': 'chatClose'}).inject(chatTitle);

		// close button
		var linkClose = new Element('a', {'class': 'chatCloseLink',
			'href': 'javascript: void(0)', 'html': '[x]'});
		linkClose.addEvent('click', this.onClose.create({'bind': this, 'event': true}));

		// minimize button
		var linkMinimize = new Element('a', {'class': 'chatMinimizeLink',
			'href': 'javascript: void(0)', 'html': '[_]'});
		chatTitle.addEvent('click', this.onMinimize.create({'bind': this, 'event': true}));
		
		// maximize button
		var linkMaximize = new Element('a', {'class': 'chatMinimizeLink',
			'href': 'javascript: void(0)', 'html': '[+]'});
		linkMaximize.addEvent('click', this.onMaximize.create({'bind': this, 'event': true}));
		
		linkMinimize.inject(chatClose);
		linkMaximize.inject(chatClose);
		linkClose.inject(chatClose);

		this.position();
	},

	getMaxContactLength: function() {
		return this.maxContactLength;
	},
	
	position: function() {
		var dims = this.panelRegistrar.getPanelDims(this);
		
		var lowerPanel = this.panel.getElement('div[class=chatLowerPanel]');
		var minimizeLink = this.panel.getElement('a[class=chatMinimizeLink]');

		if (!this.minimized) {
			this.panel.style.height = dims.height;
			lowerPanel.style.display = 'block';
			minimizeLink.innerHTML = '[_]';
		} else {
			this.panel.style.height = dims.titleHeight;
			lowerPanel.style.display = 'none';
			minimizeLink.innerHTML = '[&oline;]';
		}
		
		this.panel.style.right = dims.right;
		this.panel.style.bottom = 0;
		if (!this.childIsOpen()) {
			this.panel.style.display = 'block';
			this.lastDialogWidth = this.chatDialog.offsetWidth ? this.chatDialog.offsetWidth : this.chatDialog.clientWidth;
		}
	},

	addMessage: function(source, text, noRecurse) {
		this.parent(source, text);
		if (this.childIsOpen() && !noRecurse) {
			this.childPanel.addMessage(source, text, true);
		}
	},

	flashTitle: function() {
		if (this.childIsOpen())
			this.childPanel.flashTitle();
		else
			this.parent();
	},

	addInfo: function(text, name, noRecurse) {
		this.parent(text, name);
		if (this.childIsOpen() && !noRecurse)
			this.childPanel.addInfo(text, name, true);
	},

	deleteInfo: function(name) {
		this.parent(name);
		if (this.childIsOpen())
			this.childPanel.deleteInfo(name);
	},

	onMessage: function(msg) {
		if (!this.childIsOpen())
			this.panel.style.display = 'block';
		this.parent(msg);
	},

	onClose: function(event) {
		this.panelRegistrar.unregisterPanel(this);
		this.panel.dispose();
		this.panel.destroy();
		this.testSpan.dispose();
		this.testSpan.destroy();
		this.titleFlasher.removeTitle(this.getTitle());
		if (this.childWindow)
			this.childWindow.close();
		return false;
	},

	onMinimize: function(event) {
		this.minimized = !this.minimized;
		//this.xmppClient.sendChatNotification(this.getFullContact(), this.minimized ? 'inactive' : 'active');
		this.position();
		this.titleFlasher.removeTitle(this.getTitle());
		return false;
	},

	onMaximize: function(event) {
		if (!this.childIsOpen()) {
			this.childWindow = window.open('/chat-window/?contact=' + encodeURI(this.contact) + '&rnd=' + Math.random(), '_blank',
				'toolbar=no, menubar=no, titlebar=no, location=no, status=no, resizable=yes, ' +
				'height=400, width=400, top=200, left=200');
		}
		this.childWindow.focus();
		this.titleFlasher.removeTitle(this.getTitle());
		this.panel.style.display = 'none';
		return false;
	},

	onChildLoaded: function(childPanel) {
		this.childPanel = childPanel;
	},

	childIsOpen: function() {
		if (!this.childPanel)
			return false;
		if (this.childWindow.closed) {
			this.childWindow = null;
			this.childPanel = null;
			return false;
		}
		return true;
	},

	getDialogHTML: function() {
		return this.chatDialog.innerHTML;
	},

	closeChild: function() {
		if (this.childPanel) {
			this.childWindow.close();
			this.childWindow = null;
			this.childPanel = null;
		}
		this.position();
		this.chatDialog.scrollTop = this.chatDialog.scrollHeight;
	}
});

var ChatPanelChild = new Class({
	Extends: ChatPanelBase,

	parentPanel: null,

	initialize: function(contact, xmppClient, titleFlasher, parentPanel) {
		this.parent(contact, xmppClient, titleFlasher);
		this.parentPanel = parentPanel;
		this.parentPanel.onChildLoaded(this);

		var chatTitle = this.panel.getElement('div[class=chatTitle]');
		var chatClose = new Element('div', {'class': 'chatClose'}).inject(chatTitle);

		// minimize button
		var linkMinimize = new Element('a', {'class': 'chatMinimizeLink',
			'href': 'javascript: void(0)', 'html': '[_]'});
		linkMinimize.inject(chatClose);
		chatTitle.addEvent('click', this.onMinimize.create({'bind': this, 'event': true}));

		this.position();
	},

	position: function() {
		this.panel.style.height = '100%';
		this.panel.style.width = '100%';
		this.panel.style.border = 'none';
		this.panel.style.display = 'block';
		this.lastDialogWidth = this.chatDialog.offsetWidth ? this.chatDialog.offsetWidth : this.chatDialog.clientWidth;
	},

	copyDialogHTML: function() {
		var html = this.parentPanel.getDialogHTML();
		this.chatDialog.innerHTML = html;
		this.chatDialog.scrollTop = this.chatDialog.scrollHeight;
	},

	addMessage: function(source, text, noRecurse) {
		this.parent(source, text);
		if (!noRecurse)
			this.parentPanel.addMessage(source, text, true);
	},
	
	onWindowClose: function() {
		this.parentPanel.onClose();
	},

	onMinimize: function() {
		window.onbeforeunload = null;
		window.onunload = null;
		this.parentPanel.closeChild();
	}

});

var childChatPanel = null;

function xmppChildInit() {
	childChatPanel = new ChatPanelChild(contact, window.opener.xmppClient, titleFlasher, window.opener.chatPanelPlacer.getPanel(contact));
	childChatPanel.copyDialogHTML();
	window.addEvent('resize', childChatPanel.position.create({'bind': childChatPanel, 'event': true}));
}

function xmppChildDeinit() {
	childChatPanel.onWindowClose();
}

var xmppClient = null;
var chatPanelPlacer = null;

function genResource() {
	var chars = "abcdefghijklmnopqrstuvwxyz";
	chars += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	chars += "1234567890";
	res = "talkpad.";
	for(var n = 0; n < 8; n++) {
		i = Math.floor(Math.random() * 61);
		res += chars.charAt(i);
	}
	return res;
}

function xmppInit(userid, siteid) {
	var res = genResource();
	xmppClient = new XMPPClient();
	chatPanelPlacer = new ChatPanelPlacer();

	if (xmppClient && userid && siteid)
		xmppClient.connect(userid + "@talkpad.ru", siteid, res);
}

function xmppDestroy() {
	if (chatPanelPlacer)
		chatPanelPlacer.closeChildren();
	if (xmppClient && xmppClient.connected)
		xmppClient.disconnect();
}


