/*
 * @package AJAX_Chat
 * @author Sebastian Tschan
 * @copyright (c) Sebastian Tschan
 * @license GNU Affero General Public License
 * @link https://blueimp.net/ajax/
 * 
 * The SELFHTML documentation has been used throughout this project:
 * http://selfhtml.org
 * 
 * Stylesheet and cookie methods have been inspired by Paul Sowden (A List Apart):
 * http://www.alistapart.com/stories/alternate/
 */

// AJAX Chat client side logic:
var ajaxChat = {

	settingsInitiated: null,
	styleInitiated: null,
	initializeFunction: null,
	finalizeFunction: null,
	loginChannelID: null,
	loginChannelName: null,
	timerRate: null,
	timer: null,
	ajaxURL: null,
	baseURL: null,
	regExpMediaUrl: null,
	dirs: null,
	startChatOnLoad: null,
	chatStarted: null,
	domIDs: null,
	dom: null,
	settings: null,
	nonPersistentSettings: null,
	unusedSettings: null,
	bbCodeTags: null,
	colorCodes: null,
	emoticonCodes: null,
	emoticonFiles: null,
	soundFiles: null,
	sounds: null,
	soundTransform: null,
	sessionName: null,
	cookieExpiration: null,
	cookiePath: null,
	cookieDomain: null,
	cookieSecure: null,
	chatBotName: null,
	chatBotID: null,
	allowUserMessageDelete: null,
	inactiveTimeout: null,
	privateChannelDiff: null,
	privateMessageDiff: null,
	showChannelMessages: null,
	messageTextMaxLength: null,
	socketServerEnabled: null,
	socketServerHost: null,
	socketServerPort: null,
	socketServerChatID: null,
	socket: null,
	socketIsConnected: null,
	socketTimerRate: null,
	socketReconnectTimer: null,
	socketRegistrationID: null,
	userID: null,
	userName: null,
	userRole: null,
	channelID: null,
	channelName: null,
	channelSwitch: null,
	usersList: null,
	userNamesList: null,
	userGmtTimesList: null,
	// nikolp: 27.03.2009
	userGroupList: null,
	userMenuCounter: null,
	encodedUserName: null,
	userNodeString: null,
	ignoredUserNames: null,
	lastID: null,
	msgID: null,
	localID: null,
	lang: null,
	langCode: null,
	baseDirection: null,
	originalDocumentTitle: null,
	blinkInterval: null,
	httpRequest: null,
	oldHistoryMessageCount: null,
	historyWindow: null,
	openerWindow: null,
	viewTimeSemicol: null,
	isHistoryLogs: null,
	bannedIPList: null,
	currentMsg: null,
	// объект для информации пользователя
	userObject: null,
	// список стилей по группам
	userGroupStyleList: null,
	// параметр для группы пользователя, который послал сообщение
	messageSenderGroupID: null,
	// флаг обновления контента: nikolp
	contentIsChanges: null,
        //timestamp сервера
        serverTimestamp: null,
        serverTimezone: null,
	
	
	init: function(config, lang, initSettings, initStyle, initialize, initializeFunction, finalizeFunction) {	
		this.httpRequest		   = new Object();
		this.usersList			   = new Array();
		this.userNamesList	           = new Array();
	        // флаг обновления контента: nikolp
	        this.contentIsChanges              = false;
   	        // nikolp: 27.03.2009
		this.userGmtTimesList              = new Array();
		this.userGroupList	           = new Array();
		this.bannedIPList		   = new Array();
		this.userGroupStyleList		   = new Array();
		this.timerRate		           = 2000;
		this.userMenuCounter	           = 0;
		this.lastID			   = 0;
		this.msgID			   = 0;
		this.localID			   = 0;
		this.oldHistoryMessageCount	   = 0;
		this.viewTimeSemicol               = true;
		this.openerWindow                  = null;
		this.lang		           = lang;
		this.isHistoryLogs                 = false;
		this.initConfig(config);
		this.initDirectories();		
		if(initSettings) {
			this.initSettings();
		}
		if(initStyle) {
			this.initStyle();
		}
		this.initializeFunction = initializeFunction;
		this.finalizeFunction = finalizeFunction;
		if(initialize) {
			this.setLoadHandler();
		}
	},
	
	initConfig: function(config) {
		this.loginChannelID			= config['loginChannelID'];
		this.loginChannelName		        = config['loginChannelName'];
		this.timerRate				= config['timerRate'];
		this.ajaxURL				= config['ajaxURL'];
		this.baseURL				= config['baseURL'];
		this.regExpMediaUrl			= config['regExpMediaUrl'];
		this.startChatOnLoad		        = config['startChatOnLoad'];
		this.domIDs			        = config['domIDs'];
		this.settings				= config['settings'];
		this.nonPersistentSettings	        = config['nonPersistentSettings'];
		this.bbCodeTags				= config['bbCodeTags'];
		this.colorCodes				= config['colorCodes'];
		this.emoticonCodes			= config['emoticonCodes'];
		this.emoticonFiles			= config['emoticonFiles'];
		this.soundFiles				= config['soundFiles'];
		this.sessionName			= config['sessionName'];
		this.cookieExpiration		        = config['cookieExpiration'];
		this.cookiePath				= config['cookiePath'];
		this.cookieDomain			= config['cookieDomain'];
		this.cookieSecure			= config['cookieSecure'];
		this.chatBotName			= config['chatBotName'];
		this.chatBotID				= config['chatBotID'];
		this.allowUserMessageDelete	        = config['allowUserMessageDelete'];
		this.inactiveTimeout		        = config['inactiveTimeout'];
		this.privateChannelDiff		        = config['privateChannelDiff'];
		this.privateMessageDiff		        = config['privateMessageDiff'];
		this.showChannelMessages	        = config['showChannelMessages'];
		this.messageTextMaxLength	        = config['messageTextMaxLength'];
		this.socketServerEnabled	        = config['socketServerEnabled'];
		this.socketServerHost		        = config['socketServerHost'];
		this.socketServerPort		        = config['socketServerPort'];
		this.socketServerChatID		        = config['socketServerChatID'];
	},

	initDirectories: function() {
		this.dirs = new Object();
		this.dirs['emoticons'] 	= this.baseURL+'img/emoticons/';
		this.dirs['sounds']		= this.baseURL+'sounds/';
		this.dirs['flash']		= this.baseURL+'flash/';
	},
	
	initSettings: function() {
		this.settingsInitiated = true;
		this.unusedSettings = new Object();
		var cookie = this.readCookie(this.sessionName + '_settings');
		if(cookie) {
			var settingsArray = cookie.split('&');
			var setting,key,value,number;
			for(var i=0; i<settingsArray.length; i++) {
				setting = settingsArray[i].split('=');
				if(setting.length == 2) {
					key = setting[0];
					value = this.decodeText(setting[1]);
					switch(value) {
						case 'true':
							value = true;
							break;
						case 'false':
							value = false;
							break;
						case 'null':
							value = null;
							break;
						default:
							number = parseFloat(value);
							if(!isNaN(number)) {
								if(parseInt(number) == number) {
									value = parseInt(number);
								} else {
									value = number;
								}
							}
					}
					if(this.inArray(this.nonPersistentSettings, key)) {
						// The setting is not used, store it for the persistSettings method:
						this.unusedSettings[key] = value;
					}else {
						this.settings[key] = value;
					}
				}
			}
		}
	},

	persistSettings: function() {
		if(this.settingsInitiated) {
			var settingsArray = new Array();
			for(var property in this.settings) {
				if(this.inArray(this.nonPersistentSettings, property)) {
					if(this.unusedSettings && this.unusedSettings[property]) {
						// Store the unusedSetting previously stored:
						this.settings[property] = this.unusedSettings[property];	
					} else {
						continue;
					}
				}
				settingsArray.push(property + '=' + this.encodeText(this.settings[property]));
			}
			this.createCookie(this.sessionName + '_settings', settingsArray.join('&'), this.cookieExpiration);	
		}
	},
	
	getSettings: function() {
		return this.settings;
	},
	
	getSetting: function(key) {
		// Only return null if setting is null or undefined, not if it is false:
		for(var property in this.settings) {
			if(property == key) {
				return this.settings[key];
			}
		}
		return null;
	},
	
	setSetting: function(key, value) {
		this.settings[key] = value;
	},
	
	initializeSettings: function() {
		if(this.settings['persistFontColor'] && this.settings['fontColor']) {
			// Set the inputField font color to the font color:
			if(this.dom['inputField']) {
				this.dom['inputField'].style.color = this.settings['fontColor'];
			}
		}
	},
	
	initialize: function() {	
		this.setUnloadHandler();
		this.initializeDocumentNodes();
		this.loadPageAttributes();
		this.initEmoticons();
		this.initEmoticonsHelpContainer();
		this.initColorCodes();
		this.initializeSettings();		
		this.setSelectedStyle();
		this.customInitialize();
		if(typeof this.initializeFunction == 'function') {
			this.initializeFunction();
		}
		if(!this.isCookieEnabled()) {
			this.addChatBotMessageToChatList('/error CookiesRequired');
		} else {
			if(this.startChatOnLoad) {
				this.startChat();
			} else {
				this.setStartChatHandler();
				this.requestTeaserContent();
			}
		}
	},

	requestTeaserContent: function() {
		var params = '&view=teaser';
		params += '&getInfos=' + this.encodeText('userID,userName,userRole');
		if(!isNaN(parseInt(this.loginChannelID))) {
			params += '&channelID='+this.loginChannelID;
		} else if(this.loginChannelName !== null) {
			params += '&channelName='+this.encodeText(this.loginChannelName);
		}
		this.updateChat(params);
	},
	
	setStartChatHandler: function() {
		if(this.dom['inputField']) {
			this.dom['inputField'].onfocus = function() {
				ajaxChat.startChat();
				// Reset the onfocus event on first call:
				ajaxChat.dom['inputField'].onfocus = '';
			}			
		}
	},
	
	startChat: function() {
		this.chatStarted = true;
		if(this.dom['inputField'] && this.settings['autoFocus']) {
			this.dom['inputField'].focus();
		}
		this.loadFlashInterface();
		this.startChatUpdate();
	},

	loadPageAttributes: function() {
		var htmlTag			= document.getElementsByTagName('html')[0];
		this.langCode		= htmlTag.getAttribute('lang')	? htmlTag.getAttribute('lang')	: 'en';
		this.baseDirection	= htmlTag.getAttribute('dir')	? htmlTag.getAttribute('dir')	: 'ltr';		
	},

	setLoadHandler: function() {
		// Make sure initialize() is called on page load:
  		var onload = window.onload;
		if(typeof onload != 'function') {
			window.onload = function() {
				ajaxChat.initialize();
			}
		}else {
			window.onload = function() {
				onload();
				ajaxChat.initialize();
			}
		}		
	},
	
	setUnloadHandler: function() {
		// Make sure finalize() is called on page unload:
  		var onunload = window.onunload;
		if(typeof onunload != 'function') {
			window.onunload = function() {
				ajaxChat.finalize();
			}
		} else {
			window.onunload = function() {
				ajaxChat.finalize();
				onunload();
			}
		}
	},

	updateDOM: function(id, str, prepend, overwrite) {
		var domNode = this.dom[id] ? this.dom[id] : document.getElementById(id);
		if(!domNode) {
			return;
		}
		try {
			// Test for validity before adding the string to the DOM:
			// TODO: FOR history - this.isHistoryLogs
			if(this.isHistoryLogs == false)
			   domNode.cloneNode(false).innerHTML = str;
			   
			if(overwrite) {
				domNode.innerHTML = str;
			} else if(prepend) {
				domNode.innerHTML = str + domNode.innerHTML;
			} else {
			if(this.userID ==1)
			  {
			  //alert(str)
			  }
				domNode.innerHTML += str;
			}
		} catch(e) {
			this.addChatBotMessageToChatList('/error DOMSyntax '+id);
			this.updateChatlistView();
		}
	},
	
	initializeDocumentNodes: function() {
		this.dom = new Object();
		for(var key in this.domIDs) {
			this.dom[key] = document.getElementById(this.domIDs[key]);
		}
	},

	initEmoticons: function() {
		for(var i=0; i<this.emoticonCodes.length; i++) {
			// Replace specials characters in emoticon codes:
			this.emoticonCodes[i] = this.encodeSpecialChars(this.emoticonCodes[i]);
			if(this.dom['emoticonsContainer']) {
					this.updateDOM(
						'emoticonsContainer',
						'<a href="javascript:ajaxChat.insertText(\''
						+ this.scriptLinkEncode(this.emoticonCodes[i])
						+ '\');"><img src="'
						+ this.dirs['emoticons']
						+ this.emoticonFiles[i]
						+ '" alt="'
						+ this.emoticonCodes[i]
						+ '" title="'
						+ this.emoticonCodes[i]
						+ '"/></a>'
					);
			}
		}
	},

	initEmoticonsHelpContainer: function() {
		for(var i=0; i<this.emoticonCodes.length; i++) {
			// Replace specials characters in emoticon codes:
			this.emoticonCodes[i] = this.encodeSpecialChars(this.emoticonCodes[i]);
			if(this.dom['emoticonsHelpContainer']) {
					this.updateDOM(
						'emoticonsHelpContainer',
						'<div>'
						+ this.emoticonCodes[i]
						+ ' = ' 
					    + '<a href="javascript:ajaxChat.insertText(\''
						+ this.scriptLinkEncode(this.emoticonCodes[i])
						+ '\');"><img src="'
						+ this.dirs['emoticons']
						+ this.emoticonFiles[i]
						+ '" alt="'
						+ this.emoticonCodes[i]
						+ '" title="'
						+ this.emoticonCodes[i]
						+ '"/></a></div>'   
					);
			}
		}
	},
	
	initColorCodes: function() {
		if(this.dom['colorCodesContainer']) {
			for(var i=0; i<this.colorCodes.length; i++) {
				this.updateDOM(
					'colorCodesContainer',
					'<a href="javascript:ajaxChat.setFontColor(\''
					+ this.colorCodes[i]
					+ '\');" style="background-color:'
					+ this.colorCodes[i]
					+ ';" title="'
					+ this.colorCodes[i]
					+ '"></a>'
					+ "\n"
				);
			}			
		}
	},

	startChatUpdate: function() {
		// Start the chat update and retrieve current user and channel info and set the login channel:
		var infos = 'userID,userName,userRole,channelID,channelName';
		if(this.socketServerEnabled) {
			infos += ',socketRegistrationID';
		}
		var params = '&getInfos=' + this.encodeText(infos);
		if(!isNaN(parseInt(this.loginChannelID))) {
			params += '&channelID='+this.loginChannelID;
		} else if(this.loginChannelName !== null) {
			params += '&channelName='+this.encodeText(this.loginChannelName);
		}
		this.updateChat(params);
	},
	
	updateChat: function(paramString) {
		var requestUrl = this.ajaxURL
						+ '&lastID='
						+ this.lastID;
		if(paramString) {
			requestUrl += paramString;
		}
		this.makeRequest(requestUrl,'GET',null);
	},
	
	loadFlashInterface: function() {
	   if(this.userID ==1){
	    //alert('loadFlashInterface');
	   }
		if(this.dom['flashInterfaceContainer']) {
			this.updateDOM(
				'flashInterfaceContainer',
				'<object id="ajaxChatFlashInterface" style="position:absolute; left:-100px;" '
				+'classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" '
				+'codebase="'
				+ window.location.protocol
				+'//download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" '
				+'height="1" width="1">'
				+'<param name="flashvars" value="bridgeName=ajaxChat"/>'
				+'<param name="src" value="'+this.dirs['flash']+'FABridge.swf"/>'
				+'<embed name="ajaxChatFlashInterface" pluginspage="'
				+ window.location.protocol
				+'//www.macromedia.com/go/getflashplayer" '
				+'src="'+this.dirs['flash']+'FABridge.swf" height="1" width="1" flashvars="bridgeName=ajaxChat"/>'
				+'</object>'
			);
			FABridge.addInitializationCallback('ajaxChat', this.flashInterfaceLoadCompleteHandler);
		}
	},
	
	flashInterfaceLoadCompleteHandler: function() {
		ajaxChat.initializeFlashInterface();
	},

	initializeFlashInterface: function() {
		if(this.socketServerEnabled) {
			this.socketTimerRate = (this.inactiveTimeout-1)*60*1000;
			this.socketConnect();
		}
		this.loadSounds();
		this.initializeCustomFlashInterface();
	},

	socketConnect: function() {
		if(!this.socketIsConnected) {
			try {
				if(!this.socket && FABridge.ajaxChat) {
					this.socket = FABridge.ajaxChat.create('flash.net.XMLSocket');
					this.socket.addEventListener('connect', this.socketConnectHandler);
					this.socket.addEventListener('close', this.socketCloseHandler);
					this.socket.addEventListener('data', this.socketDataHandler);
					this.socket.addEventListener('ioError', this.socketIOErrorHandler);
					this.socket.addEventListener('securityError', this.socketSecurityErrorHandler);
				}
				this.socket.connect(this.socketServerHost, this.socketServerPort);
			} catch(e) {
				//alert(e);
			}
		}
		clearTimeout(this.socketReconnectTimer);
		this.socketReconnectTimer = null;
	},
	
	socketConnectHandler: function(event) {
		ajaxChat.socketIsConnected = true;
		// setTimeout is needed to avoid calling the flash interface recursively:
		setTimeout('ajaxChat.socketRegister()', 0);
	},

	socketCloseHandler: function(event) {
		ajaxChat.socketIsConnected = false;
		if(ajaxChat.socket) {
			clearTimeout(ajaxChat.timer);
			ajaxChat.updateChat(null);
		}
	},
	
	socketDataHandler: function(event) {
		ajaxChat.socketUpdate(event.getData());
	},

	socketIOErrorHandler: function(event) {
		// setTimeout is needed to avoid calling the flash interface recursively (e.g. sound on new messages):
		setTimeout('ajaxChat.addChatBotMessageToChatList(\'/error SocketIO\')', 0);
		setTimeout('ajaxChat.updateChatlistView()', 1);
	},

	socketSecurityErrorHandler: function(event) {
		// setTimeout is needed to avoid calling the flash interface recursively (e.g. sound on new messages):
		setTimeout('ajaxChat.addChatBotMessageToChatList(\'/error SocketSecurity\')', 0);
		setTimeout('ajaxChat.updateChatlistView()', 1);
	},

	socketRegister: function() {
		if(this.socket && this.socketIsConnected) {
			try {
				this.socket.send(
					'<register chatID="'
					+this.socketServerChatID
					+'" userID="'
					+this.userID
					+'" regID="'
					+this.socketRegistrationID
					+'"/>'
				);
			} catch(e) {
				//alert(e);
			}
		}
	},
	
	loadXML: function(str) {
		if(!arguments.callee.parser) {
			try {
				// DOMParser native implementation (Mozilla, Opera):
				arguments.callee.parser = new DOMParser();
			} catch(e) {
				var customDOMParser = function() {}
				if(navigator.appName == 'Microsoft Internet Explorer') {
					// IE implementation:
					customDOMParser.prototype.parseFromString = function(str, contentType) {
						if(!arguments.callee.XMLDOM) {
							arguments.callee.XMLDOM = new ActiveXObject('Microsoft.XMLDOM');
						}
						arguments.callee.XMLDOM.loadXML(str);
						return arguments.callee.XMLDOM;	
					}
				} else {
					// Safari, Konqueror:
					customDOMParser.prototype.parseFromString = function(str, contentType) {
						if(!arguments.callee.httpRequest) {
							arguments.callee.httpRequest = new XMLHttpRequest();
						}
						arguments.callee.httpRequest.open(
							'GET',
							'data:text/xml;charset=utf-8,'+encodeURIComponent(str),
							false
						);
						arguments.callee.httpRequest.send(null);
						return arguments.callee.httpRequest.responseXML;
					}
				}
				arguments.callee.parser = new customDOMParser();
			}
		}
		return arguments.callee.parser.parseFromString(str, 'text/xml');
	},
	
	socketUpdate: function(data) {
		var xmlDoc = this.loadXML(data);
		if(xmlDoc) {
			this.handleOnlineUsers(xmlDoc.getElementsByTagName('user'));
			// If the root node has the attribute "mode" set to "1" it is a channel message:
			if((this.showChannelMessages || xmlDoc.firstChild.getAttribute('mode') != '1') && !this.channelSwitch) {
				var channelID = xmlDoc.firstChild.getAttribute('channelID');
				if(channelID == this.channelID ||
					parseInt(channelID) == parseInt(this.userID)+this.privateMessageDiff
					) {
					this.handleChatMessages(xmlDoc.getElementsByTagName('message'));
				}
			}
		}
	},

	setAudioVolume: function(volume) {
		volume = parseFloat(volume);
		if(!isNaN(volume)) {
			if(volume < 0) {
				volume = 0.0;
			} else if(volume > 1) {
				volume = 1.0;
			}
			this.settings['audioVolume'] = volume;
			try {
				if(!this.soundTransform) {
					this.soundTransform = FABridge.ajaxChat.create('flash.media.SoundTransform');					
				}
				this.soundTransform.setVolume(volume);
			} catch(e) {
				//alert(e);
			}
		}
	},
	
	loadSounds: function() {
		try {
			this.setAudioVolume(this.settings['audioVolume']);
			this.sounds = new Object();
			var sound,urlRequest;
			for(var key in this.soundFiles) {
				sound = FABridge.ajaxChat.create('flash.media.Sound');
				sound.addEventListener('complete', this.soundLoadCompleteHandler);
				sound.addEventListener('ioError', this.soundIOErrorHandler);
				urlRequest = FABridge.ajaxChat.create('flash.net.URLRequest');
				urlRequest.setUrl(this.dirs['sounds']+this.soundFiles[key]);
				sound.load(urlRequest);
			}
		} catch(e) {
			//alert(e);
		}
	},
	
	soundLoadCompleteHandler: function(event) {
		var sound = event.getTarget();
		for(var key in ajaxChat.soundFiles) {
			// Get the sound key by matching the sound URL with the sound filename:
			if((new RegExp(ajaxChat.soundFiles[key])).test(sound.getUrl())) {
				// Add the loaded sound to the sounds list:
				ajaxChat.sounds[key] = sound;
			}
		}
	},

	soundIOErrorHandler: function(event) {
		// setTimeout is needed to avoid calling the flash interface recursively (e.g. sound on new messages):
		setTimeout('ajaxChat.addChatBotMessageToChatList(\'/error SoundIO\')', 0);
		setTimeout('ajaxChat.updateChatlistView()', 1);
	},
	
	soundPlayCompleteHandler: function(event) {
		// soundChannel event 'soundComplete'
	},

	playSound: function(soundID) {
		if(this.sounds && this.sounds[soundID]) {
			try {
				// play() parameters are
				// startTime:Number (default = 0),
				// loops:int (default = 0) and
				// sndTransform:SoundTransform  (default = null)
				return this.sounds[soundID].play(0, 0, this.soundTransform);
			} catch(e) {
				//alert(e);
			}
		}
		return null;
	},
	
	playSoundOnNewMessage: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) {
		if(this.settings['audio'] && this.sounds && this.lastID && !this.channelSwitch) {
			switch(userID) {
				case this.chatBotID:
					var messageParts = messageText.split(' ', 1);
					switch(messageParts[0]) {
						case '/login':
						case '/channelEnter':
							this.playSound(this.settings['soundEnter']);
							break;
						case '/logout':
						case '/channelLeave':
						case '/kick':
							this.playSound(this.settings['soundLeave']);
							break;
						case '/error':
							this.playSound(this.settings['soundError']);
							break;
						default:
							this.playSound(this.settings['soundChatBot']);
					}
					break;
				case this.userID:
					this.playSound(this.settings['soundSend']);
					break;
				default:
					this.playSound(this.settings['soundReceive']);
					break;
			}
		}
	},

	fillSoundSelection: function(selectionID, selectedSound) {
		var selection = document.getElementById(selectionID);
		// Skip the first, empty selection:
		var i = 1;
		for(var key in this.soundFiles) {
			selection.options[i] = new Option(key, key);
			if(key == selectedSound){
				selection.options[i].selected = true;
			}
			i++;
		}
	},
	
	getHttpRequest: function(identifier) {
		if(!this.httpRequest[identifier]) {
			if (window.XMLHttpRequest) {
				this.httpRequest[identifier] = new XMLHttpRequest();
				if (this.httpRequest[identifier].overrideMimeType) {
					this.httpRequest[identifier].overrideMimeType('text/xml');
				}
			} else if (window.ActiveXObject) {
				try {
					this.httpRequest[identifier] = new ActiveXObject("Msxml2.XMLHTTP");
				} catch (e) {
					try {
						this.httpRequest[identifier] = new ActiveXObject("Microsoft.XMLHTTP");
					}catch (e) {
					}
				}
			}
		}
		return this.httpRequest[identifier];
	},
	
	makeRequest: function(url, method, data) {
		try {
			var identifier;
			if(data) {
				// Create up to 50 HTTPRequest objects:
				if(!arguments.callee.identifier || arguments.callee.identifier > 50) {
					arguments.callee.identifier = 1;
				} else {
					arguments.callee.identifier++;
				}
				identifier = arguments.callee.identifier;
			} else {
				identifier = 0;
			}
			this.getHttpRequest(identifier).open(method, url, true);
			this.getHttpRequest(identifier).onreadystatechange = function() {
				try {
					ajaxChat.handleResponse(identifier);
				} catch(e) {
				   //alert('makeRequest / catch block!');
					try {
						clearTimeout(this.timer);
						//clearTimeout(ajaxChat.timer);
					} catch(e) {
						//alert(e);
					}
					try {
						if(data) {
							ajaxChat.addChatBotMessageToChatList('/error ConnectionTimeout');
							ajaxChat.updateChatlistView();
							//alert('makeRequest/error ConnectionTimeout I')
						}
					} catch(e) {
						alert(e);
					}
					try {				
						ajaxChat.timer = setTimeout('ajaxChat.updateChat(null);', ajaxChat.timerRate);
					} catch(e) {
						alert(e);
					}
				}
			};
			if(method == 'POST') {
				this.getHttpRequest(identifier).setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
			}
			this.getHttpRequest(identifier).send(data);
		} catch(e) {
			clearTimeout(this.timer);
			if(data) {
				this.addChatBotMessageToChatList('/error ConnectionTimeout');
				this.updateChatlistView();
			   //alert('makeRequest/error ConnectionTimeout II')
			}
         //alert('makeRequest/error ConnectionTimeout III')
			this.timer = setTimeout('ajaxChat.updateChat(null);', this.timerRate);
			/* индикатор состояния: added by nikolp */
         this.setConnectionIconStatus(false);
		}
	},
		
	handleResponse: function(identifier) {
	   var xmlDoc = null;
		if (this.getHttpRequest(identifier).readyState == 4) {
			if (this.getHttpRequest(identifier).status == 200) {
            
            //alert(this.currentMsg)				
            // очищаем текущее сообщение если оно успешно отправлено на сервер
            this.currentMsg = null;
		
				/* индикатор состояния: added by nikolp */
            this.setConnectionIconStatus(true);
            				
				xmlDoc = this.getHttpRequest(identifier).responseXML;
			} else {
			   //alert('ConnectionStatus')
   			clearTimeout(this.timer);

				this.addChatBotMessageToChatList('/error ConnectionStatus '+this.getHttpRequest(identifier).status);
				this.updateChatlistView();				
				
				// индикатор состояния: added by nikolp
				this.setConnectionIconStatus(false);
				
				// добавлено для сохраниния сообщения в окне ввода: added by nikolp
				if(this.currentMsg)
				{
	            this.dom['inputField'].value = this.currentMsg;
	            this.dom['inputField'].focus();
               this.updateMessageLengthCounter();
               this.currentMsg = null;
            }
            
            // реанимирование подключения чата...
            this.timer = setTimeout('ajaxChat.updateChat(null);', this.timerRate);

				return false;
			}
		}
		if(!xmlDoc) {
			return false;
		}
		this.handleXML(xmlDoc);

		return true;
	},
	
	handleXML: function(xmlDoc) {
		this.handleInfoMessages(xmlDoc.getElementsByTagName('info'));
		this.handleOnlineUsers(xmlDoc.getElementsByTagName('user'));
		this.handleChatMessages(xmlDoc.getElementsByTagName('message'));
      // делаем обновление для контекстного меню: nikolp 31.03.09
      if(this.contentIsChanges) {
         this.setContextMenuForIP();
	      this.setAllContextMenuIII();
		   this.contentIsChanges = false;
		}

		this.channelSwitch = null;
		this.setChatUpdateTimer();
	},

	setChatUpdateTimer: function() {
		clearTimeout(this.timer);
		if(this.chatStarted) {
			var timeout;
			if(this.socketIsConnected) {
				timeout = this.socketTimerRate;
			} else {
				timeout = this.timerRate;
				if(this.socketServerEnabled && !this.socketReconnectTimer) {
					// If the socket connection fails try to reconnect once in a minute:
					this.socketReconnectTimer = setTimeout('ajaxChat.socketConnect();', 60000);
				}
			}
			this.timer = setTimeout('ajaxChat.updateChat(null);', timeout);			
		}
	},
	
	handleInfoMessages: function(infoNodes) {
		var infoType, infoData;
		for(var i=0; i<infoNodes.length; i++) {
			infoType = infoNodes[i].getAttribute('type');
			infoData = infoNodes[i].firstChild ? infoNodes[i].firstChild.nodeValue : '';
			this.handleInfoMessage(infoType, infoData);
		}
	},
	
	handleInfoMessage: function(infoType, infoData) {
		switch(infoType) {
			case 'channelSwitch':
				this.clearChatList();
				this.clearOnlineUsersList();
				this.setSelectedChannel(infoData);
				this.channelName = infoData;
				this.channelSwitch = true;
				break;			
			case 'channelName':
				this.setSelectedChannel(infoData);
				this.channelName = infoData;
				break;
			case 'channelID':
				this.channelID = infoData;
				break;
			case 'userID':
				this.userID = infoData;
				break;			
			case 'userName':
				this.userName = infoData;
				this.encodedUserName = this.scriptLinkEncode(this.userName);
				this.userNodeString = null;
				break;
			case 'userRole':
				this.userRole = infoData;
				break;				
			case 'oldHistoryViewStat':
			    this.oldHistoryMessageCount = infoData;
				break;				
			case 'logout':
				this.handleLogout(infoData);
				return;
			case 'socketRegistrationID':
				this.socketRegistrationID = infoData;
				this.socketRegister();
			default:
				this.handleCustomInfoMessage(infoType, infoData);
		}
	},

	handleOnlineUsers: function(userNodes) {
		if(userNodes.length) {
			var index,userID,userName,userRole, userGmtTime;
			// флаг для установки обновления: nikolp - 31.03.09
			this.contentIsChanges = false;
			var onlineUsers = new Array();
			for(var i=0; i<userNodes.length; i++) {
				userID = userNodes[i].getAttribute('userID');
				userName = userNodes[i].firstChild ? userNodes[i].firstChild.nodeValue : '';
				userRole = userNodes[i].getAttribute('userRole');
				userGmtTime = userNodes[i].getAttribute('userGmtTime');
				// группа пользователя
				userGroup = userNodes[i].getAttribute('userGroup');
				onlineUsers.push(userID);
								
				index = this.arraySearch(userID, this.usersList);
				if(index === false) {
					this.addUserToOnlineList(
						userID,
						userName,
						userRole,
						userGmtTime,
						userGroup
					);
      			// флаг для установки обновления: nikolp - 31.03.09
					this.contentIsChanges = true;

				} else if(this.userNamesList[index] != userName) {
					this.removeUserFromOnlineList(userID, index);
					this.addUserToOnlineList(
						userID,
						userName,
						userRole,
						userGmtTime,
						userGroup
					);
      			// флаг для установки обновления: nikolp - 31.03.09
					this.contentIsChanges = true;
				} else if (this.userGroupList[index] != userGroup) {
				   // меняем пользователя если изменилась группа: - 01.04.09
					this.removeUserFromOnlineList(userID, index);
					this.addUserToOnlineList(
						userID,
						userName,
						userRole,
						userGmtTime,
						userGroup
					);
      			// флаг для установки обновления: nikolp - 31.03.09
					this.contentIsChanges = true;
				}
			}
			// Clear the offline users from the online users list:
			for(var i=0; i<this.usersList.length; i++) {
				if(!this.inArray(onlineUsers, this.usersList[i])) {
					this.removeUserFromOnlineList(this.usersList[i], i);
      			// флаг для установки обновления: nikolp - 31.03.09
					this.contentIsChanges  = true;
				}
			}	
			if (this.contentIsChanges) 
			{
			   this.setOnlineListRowClasses();
		      this.setAllContextMenuIII('onlineListContainer');
		      this.contentIsChanges = false;
   	   }
		}	
	},

	handleChatMessages: function(messageNodes) {
		if(messageNodes.length) {
			var userNode,userName,textNode,messageText;		
			for(var i=0; i<messageNodes.length; i++) {
				userNode = messageNodes[i].getElementsByTagName('username')[0];
				userName = userNode.firstChild ? userNode.firstChild.nodeValue : '';
				textNode = messageNodes[i].getElementsByTagName('text')[0];
				messageText = textNode.firstChild ? textNode.firstChild.nodeValue : '';
				
				if(!this.showChannelMessages && messageNodes[i].getAttribute('userID') == this.chatBotID)
				{
				    continue;
				}
         	// устанавливаем группу пользователя, который послал сообщение- nikolp: 27.03.2009
            this.messageSenderGroupID = messageNodes[i].getAttribute('senderGroupID');
            
			   this.msgID = messageNodes[i].getAttribute('id');		
				this.addMessageToChatList(
						new Date(messageNodes[i].getAttribute('dateTime')),
						messageNodes[i].getAttribute('userID'),
						userName,
						messageNodes[i].getAttribute('userRole'),
						messageNodes[i].getAttribute('id'),
						messageText,
						messageNodes[i].getAttribute('channelID'),
						messageNodes[i].getAttribute('ip')
				);
				if (!this.contentIsChanges) {
				   this.contentIsChanges = true;
				}
				this.msgID = 0;
			}
			this.updateChatlistView();		
			this.lastID = messageNodes[messageNodes.length-1].getAttribute('id');
		}
	},
	
	setSelectedChannel: function(channel) {
		if(this.dom['channelSelection']) {
			// Replace the entities in the channel name with their character equivalent:
			channel = this.decodeSpecialChars(channel);
			var channelSelected = false;
			for(var i=0; i<this.dom['channelSelection'].options.length; i++) {
				if(this.dom['channelSelection'].options[i].value == channel) {
					this.dom['channelSelection'].options[i].selected = true;
					channelSelected = true;
					break;
				}
			}
			// The given channel is not in the list, add it:
			if(!channelSelected) {
				var option = document.createElement('option');
				var text = document.createTextNode(channel);
				option.appendChild(text);
				option.setAttribute('value', channel);
				option.setAttribute('selected', 'selected');			
				this.dom['channelSelection'].appendChild(option);
			}
		}
	},

	removeUserFromOnlineList: function(userID, index) {
		this.usersList.splice(index, 1);
		this.userNamesList.splice(index, 1);	
      // delete for user local time - nikolp: 19.01.2009
		this.userGmtTimesList.splice(index, 1);		
      // удаляем группу пользователя - nikolp: 19.01.2009
		this.userGroupList.splice(index, 1);		
		
		if(this.dom['onlineList']) {
			this.dom['onlineList'].removeChild(this.getUserNode(userID));
		}
	},
   // добавлен параметр userGroup - nikolp: 27.03.2009
	addUserToOnlineList: function(userID, userName, userRole, userGmtTime, userGroup) {
		this.usersList.push(userID);
		this.userNamesList.push(userName);	
      // локальное время пользователя - nikolp: 19.01.2009
      this.userGmtTimesList.push(userGmtTime);
      // группа пользователя - nikolp: 27.03.2009
      this.userGroupList.push(userGroup);

		if(this.dom['onlineList']) {
			this.updateDOM(
				'onlineList',
				this.getUserNodeString(userID, userName, userRole),
				(this.userID == userID)
			);			
		}
	},

	getUserNodeString: function(userID, userName, userRole) {
		if(this.userNodeString && userID == this.userID) {
			return this.userNodeString;
		} else {
			var encodedUserName = this.scriptLinkEncode(userName);
			// add variable for gmt time differences; nikolp: 21.01.2009
			var gmtNum = this.getUserGmtTimeFromUserID(userID);
			gmtNum = (parseInt(gmtNum) >= 0 ? '+' : '') + gmtNum;
			
			var str	= '<div id="'
					+ this.getUserDocumentID(userID)
					+ '">'
					+ '<a href="#' + this.getUserMenuDocumentID(userID) + '_anchor" '
					+ 'onclick="ajaxChat.toggleUserMenu(\''
					+ this.getUserMenuDocumentID(userID)
					+ '\', \''
					+ encodedUserName
					+ '\', '
					+ userID
					+ '); ajaxChat.toggleUserMenuImage(\'' + this.getUserMenuDocumentID(userID) + '\');"  title="'
					+ this.lang['toggleUserMenu'].replace(/%s/, userName)
					+ '"/>'
					+ '<img class="' + ((userID == this.userID) ? 'off' : 'button')
					+ '" src="img/pixel.gif" id="' + this.getUserMenuDocumentID(userID) + 'Image" />'
					+ '</a>&nbsp;'
					+ this.replaceUserNameWithLink(userName, userRole).replace('uid=""', 'uid="' + userID + '"')
					+ '<div class="userClock"><span id="userClock' + userID + '"></span>&nbsp;' 
					+ '(GMT' + gmtNum  + ')'
					+ '</div>  '
					+ '<ul class="userMenu" id="'
					+ this.getUserMenuDocumentID(userID)
					+ '"'
					+ ((userID == this.userID) ?
						'>'+this.getUserNodeStringItems(encodedUserName, userID, userName) :
						' style="display:none;">')
					+ '</ul>'
					+ '<a name="' + this.getUserMenuDocumentID(userID) + '_anchor" '
					+ 'id="' + this.getUserMenuDocumentID(userID) + '_anchor" />'
					+ '</div>';
										
			if(userID == this.userID) {
				this.userNodeString = str;
			}
			return str;	
		}
	},

	toggleUserMenu: function(menuID, userName, userID) {
		if(!document.getElementById(menuID).firstChild) {
			this.updateDOM(
				menuID,
				this.getUserNodeStringItems(
					this.encodeText(this.addSlashes(this.getScriptLinkValue(userName))),
					userID,
					userName					
				),
				false,
				true
			)
		}
		this.showHide(menuID);
	},
	
	toggleUserMenuImage: function(menuID){
        var nodeMenu = document.getElementById(menuID);
        var nodeImage = document.getElementById(menuID + 'Image');

        if(nodeImage) {
            this.setClass(nodeImage, (nodeMenu.style.display == 'none' ? 'button' : 'button off'))
        }
	},
	
	getUserNodeStringItems: function(encodedUserName, userID, userName) {
		var menu;
		/*
		* added new link (profile link): nikolp 16.01.2009
		*
		*/
		if(encodedUserName != this.encodedUserName) {
		    encodedUserName = this.scriptLinkDecode(userName);
		    menu 	= '<li><a href="javascript:ajaxChat.insertTextAbove(\'/msg '
					+ encodedUserName
					+ ' \');">'
					+ this.lang['userMenuSendPrivateMessage']
					+ '</a></li>'
					+ '<li><a href="../member.php?action=profile&uid=' + userID + '" target="blank">'
					+ this.lang['userMenuViewProfile']
					+ '</a></li>'
					+ '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/query '
					+ encodedUserName
					+ '\');">'
					+ this.lang['userMenuOpenPrivateChannel']
					+ '</a></li>'
					+ '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/query\');">'
					+ this.lang['userMenuClosePrivateChannel']
					+ '</a></li>'
					+ '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/invite '
					+ encodedUserName
					+ '\');">'
					+ this.lang['userMenuInvite']
					+ '</a></li>'
					+ '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/uninvite '
					+ encodedUserName
					+ '\');">'
					+ this.lang['userMenuUninvite']
					+ '</a></li>'
					+ '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/ignore '
					+ encodedUserName
					+ '\');">'
					+ this.lang['userMenuIgnore']
					+ '</a></li>'
					+ '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/whereis '
					+ encodedUserName
					+ '\');">'
					+ this.lang['userMenuWhereis']
					+ '</a></li>';	
					
					/* removed action by nikolp: 18.02.2009.
               * + '<li><a href="javascript:ajaxChat.insertMessageWrapper(\'/describe '
               * + encodedUserName
               * + ' \');">'
               * + this.lang['userMenuDescribe']
               * + '</a></li>'
					*/		
			if(this.userRole == 2 || this.userRole == 3) {
				menu	+= '<li><a href="javascript:ajaxChat.insertMessageWrapper(\'/kick '
						+ encodedUserName
						+ ' \');">'
						+ this.lang['userMenuKick']
						+ '</a></li>'
						+ '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/whois '
						+ encodedUserName
						+ '\');">'
						+ this.lang['userMenuWhois']
						+ '</a></li>';
			}
		} else {
			menu 	= '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/quit\');">'
					+ this.lang['userMenuLogout']
					+ '</a></li>'
					+ (userID ==1 ? '<li><a href="javascript:ajaxChat.getTest()">Get test </a></li>' : '')
					+ '<li><a href="../member.php?action=profile&uid=' + userID + '" target="blank">'
					+ this.lang['userMenuViewProfile']
					+ '</a></li>'
					+ '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/who\');">'
					+ this.lang['userMenuWho']
					+ '</a></li>'
					+ '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/ignore\');">'
					+ this.lang['userMenuIgnoreList']
					+ '</a></li>'
					+ '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/list\');">'
					+ this.lang['userMenuList']
					+ '</a></li>'
					+ '<li><a href="javascript:ajaxChat.insertMessageWrapper(\'/roll \');">'
					+ this.lang['userMenuRoll']
					+ '</a></li>'
					+ '<li><a href="javascript:ajaxChat.insertMessageWrapper(\'/nick \');">'
					+ this.lang['userMenuNick']
					+ '</a></li>';
					
					/* removed action by nikolp: 18.02.2009.
               * + '<li><a href="javascript:ajaxChat.insertMessageWrapper(\'/action \');">'
					* + this.lang['userMenuAction']
					* + '</a></li>'
					*/

			if(this.userRole == 1 || this.userRole == 2 || this.userRole == 3) {
				menu	+= '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/join\');">'
						+ this.lang['userMenuEnterPrivateRoom']
						+ '</a></li>';
				if(this.userRole == 2 || this.userRole == 3) {
					menu	+= '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/bans\');">'
							+ this.lang['userMenuBans']
							+ '</a></li>';
				}
				if(this.userRole == 2 || this.userRole == 3) {
					menu	+= '<li><a href="javascript:ajaxChat.popupWindow(\'?view=newLogs\')">'
							+ this.lang['userMenuLogsTitle']
							+ '</a></li>';
					/* Old history 
					'<li><a href="javascript:ajaxChat.popupWindow(\'?view=logs\')">'
							+ this.lang['userMenuLogsTitle']
							+ '</a></li>'
							+ 
					*/
				}
			}
		}
		menu += this.getCustomUserMenuItems(encodedUserName, userID);
		return menu;
	},
	
    popupWindow: function(url)
    {        
        if (this.historyWindow == null || this.historyWindow.closed) {        
            // set window size
            var height = 600; //screen.height ? (screen.height - 300) : 600;
            var width = 850;//screen.width ? (screen.width - 300)  : 800;
            // set window position
            var positionX = screen.availWidth-width;
            var positionY = 0;

            this.historyWindow = window.open(url,'historyLog','resizable=1,width=' + width + ',height=' + height + ',top=0,screenY='+positionY+',left='+positionX+',screenX='+positionX);
        }
                
        if (window.focus) {
            this.historyWindow.focus();
        }
    }, 
    
    viewMessageWithPopup: function(messageID, url, date){ 
      // set text into parent window input field
	   if(this.historyWindow != null && this.historyWindow.ajaxChat != undefined){   
	      // replace searced from date value with current message date....
	      if(this.historyWindow.document != undefined && 
	         (dateSelection = this.historyWindow.document.getElementById('dateSelection')) != undefined &&
	         date != undefined){
	         dateSelection.value = date.replace('/', '-').replace('/', '-').substr(0, 10);
	      }
	      
	      this.historyWindow.ajaxChat.viewMessageWithPopup(messageID);
	      this.historyWindow.focus();
	   }
      else{			   
         //this.msgID = messageID;
         this.popupWindow(url + '&msgID=' + messageID);
         //setTimeout("ajaxChat.this.msgID=0", 3000);   
      }
    },
    
    handleHistoryWindow: function(){
        window.close();
    },
    
	setOnlineListRowClasses: function() {
		if(this.dom['onlineList']) {
			var node = this.dom['onlineList'].firstChild;			
			var rowEven = false;
			while(node) {
				this.setClass(node, (rowEven ? 'rowEven' : 'rowOdd'))
				node = node.nextSibling;
				rowEven = !rowEven;
			}
		}
	},
	
	clearChatList: function() {
		while(this.dom['chatList'].hasChildNodes()) {
			this.dom['chatList'].removeChild(this.dom['chatList'].firstChild);
		}
	},

	clearOnlineUsersList: function() {
		this.usersList = new Array();
		this.userNamesList = new Array();
		this.userGmtTimesList = new Array();
      this.userGroupList = new Array();

		if(this.dom['onlineList']) {
			while(this.dom['onlineList'].hasChildNodes()) {
				this.dom['onlineList'].removeChild(this.dom['onlineList'].firstChild);
			}
		}
	},

	getEncodedChatBotName: function() {
		if(typeof arguments.callee.encodedChatBotName == 'undefined') {
			arguments.callee.encodedChatBotName = this.encodeSpecialChars(this.chatBotName);
		}
		return arguments.callee.encodedChatBotName;
	},
	
	addChatBotMessageToChatList: function(messageText) {
		this.addMessageToChatList(
			new Date(),
			this.chatBotID,
			this.getEncodedChatBotName(),
			4,
			null,
			messageText,
			null
		);
	},
	
	addMessageToChatList: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) {
		// Prevent adding the same message twice:
      // TODO: FOR history - this.isHistoryLogs
      if(this.isHistoryLogs == false){
		   if(this.getMessageNode(messageID)) {
			   return;
		   }		
		   if(!this.onNewMessage(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip)) {
			   return;
		   }
	   }
		this.updateDOM(
			'chatList',
			this.getChatListMessageString(
				dateObject, userID, userName, userRole, messageID, messageText, channelID, ip
			)
		);
	},

	getChatListMessageString: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) {
		var rowClass = 	this.dom['chatList'].lastChild && this.getClass(this.dom['chatList'].lastChild) == 'rowOdd'
						? 'rowEven' : 'rowOdd';
		var userClass = this.getRoleClass(userRole);
		// added for privmessage title
		var userTitle = '';
		var userCommand = '->';
		var colon;
		if(messageText.indexOf('/action') == 0 || messageText.indexOf('/privaction') == 0) {
			userClass += ' action';
			colon = ' ';
		} else if(messageText.indexOf('/privmsg') == 0){
		   /*userTitle = ' title="'
			+ this.lang['sendprivmsgto'].replace(/%s/, userName)
			+ '"';*/
         
         //userCommand = '/msg';
		   colon = ': ';
		}
		else {
			colon = ': ';
		}
		//alert(messageText)
		var dateTime = this.settings['dateFormat'] ? '<span class="dateTime">'
						+ this.formatDate(this.settings['dateFormat'], dateObject) + '</span> ' : '';
	   
	   // создаем переменную для отображения имени пользователя
	   var styledUserName = userName;
	   // устанавливаем стиль для группы пользователя, который посылает сообщение
	   if (this.messageSenderGroupID) {
	      var groupStyle = this.getUserGroupStyle(this.messageSenderGroupID);
         if(groupStyle) {
            //if(this.userID == 1)
               styledUserName = groupStyle.replace('{username}', styledUserName).replace('uid=""', 'uid="'+userID+'"');
         }
	   } 
	   
	   
	   
		return	'<div id="'
				+ this.getMessageDocumentID(messageID)
				+ '" class="'
				+ rowClass
				+ '">'
				+ this.getDeletionLink(messageID, userID, userRole, channelID)
				+ this.getMessageOperationLink(messageID)
				+ this.getParentLink(messageID, this.formatDate(this.settings['dateFormat'], dateObject))
				+ dateTime
				+ '<span class="'
				+ userClass
				+ '"'
				+ this.getChatListUserNameTitle(userID, userName, userRole, ip)
				+ ' dir="'
				+ this.baseDirection
				+ '" onclick="ajaxChat.insertTextAbove(\''+ userCommand + ' ' + userName + ' \');" onmouseover="this.className=\'' 
				+ userClass + '_active\';" onmouseout="this.className=\'' + userClass + '\';">'
				+ styledUserName
				+ '</span>'
				+ this.getReplacedCommands(messageText)
				+ colon
				+ this.replaceText(messageText)
				+ '</div>';
	},
	
	getChatListUserNameTitle: function(userID, userName, userRole, ip) {
		return (ip != null) ? ' title="IP: ' + ip + '"' : '';		
	},
	
	getMessageDocumentID: function(messageID) {
		return ((messageID === null) ? 'ajaxChat_lm_'+(this.localID++) : 'ajaxChat_m_'+messageID);
	},
	
	getMessageNode: function(messageID) {
		return ((messageID === null) ? null : document.getElementById(this.getMessageDocumentID(messageID)));
	},
	
	getUserDocumentID: function(userID) {
		return 'ajaxChat_u_'+userID;
	},
	
	getUserNode: function(userID) {
		return document.getElementById(this.getUserDocumentID(userID));
	},

	getUserMenuDocumentID: function(userID) {
		return 'ajaxChat_um_'+userID;
	},
	
	getInlineUserMenuDocumentID: function(menuID, index) {
		return 'ajaxChat_ium_'+menuID+'_'+index;
	},
	
	getDeletionLink: function(messageID, userID, userRole, channelID) {
		if(messageID !== null && this.isAllowedToDeleteMessage(messageID, userID, userRole, channelID)) {
			if(!arguments.callee.deleteMessage) {
				arguments.callee.deleteMessage = this.encodeSpecialChars(this.lang['deleteMessage']);
			}
			return	'<a class="delete" title="'
					+ arguments.callee.deleteMessage
					+ '" href="javascript:ajaxChat.deleteMessage('
					+ messageID
					+ ');"> </a>' // Adding a space - without any content Opera messes up the chatlist display
		}
		return '';
	},
	
	getParentLink: function(messageID, messageDate){
			if(!arguments.callee.parentMessage) {
				arguments.callee.parentMessage = this.encodeSpecialChars(this.lang['parentMessage']);
			}
			if(messageID !== null && messageID > 0 && messageDate != undefined){
		      return	'<a class="parent" title="'
				      + arguments.callee.parentMessage
				      + '" href="javascript:ajaxChat.parentMessage('
				      + messageID + ', \'' + messageDate + '\'' 
				      + ');"> </a>' // Adding a space - without any content Opera messes up the chatlist display
         }
         else{
            return '';
         }
	},

	getMessageOperationLink: function(messageID){
	      if(this.userRole != 3) {
	         return '';
	      }
	      
			if(!arguments.callee.messageOperation) {
				arguments.callee.messageOperation = this.encodeSpecialChars(this.lang['messageOperation']);
			}
			if(messageID !== null && messageID > 0){
		      return	'<span class="messageOperation" title="'
				      + arguments.callee.messageOperation + '"'
				      + (this.userID == 1 
				         ? ' onclick="ajaxChat.viewMessageDetails(' + messageID + ')"' 
				         : ' onclick="ajaxChat.viewMessageDetails(' + messageID + ')"' 
				        )
				      + '>&nbsp;&nbsp;</span>' // Adding a space - without any content Opera messes up the chatlist display
         }
         else{
            return '';
         }
	},
	
	isAllowedToDeleteMessage: function(messageID, userID, userRole, channelID) {
		if((((this.userRole == 1 && this.allowUserMessageDelete && (userID == this.userID ||
			parseInt(channelID) == parseInt(this.userID)+this.privateMessageDiff ||
			parseInt(channelID) == parseInt(this.userID)+this.privateChannelDiff)) ||
			this.userRole == 2) && userRole != 3 && userRole != 4) || this.userRole == 3) {
			return true;
		}
		return false;
	},
	
	onNewMessage: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) {
		if(!this.customOnNewMessage(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip)) {
			return false;
		}
		if(this.ignoreMessage(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip)) {
			return false;
		}
		if(this.parseDeleteMessageCommand(messageText)) {
			return false;
		}
		this.blinkOnNewMessage(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip);
		this.playSoundOnNewMessage(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip);
		return true;
	},

	parseDeleteMessageCommand: function(messageText) {
		if(messageText.indexOf('/delete') == 0) {
			var messageID = messageText.substr(8);
			var messageNode = this.getMessageNode(messageID);
			if(messageNode) {
				var nextSibling = messageNode.nextSibling;
				try {
					this.dom['chatList'].removeChild(messageNode);
					if(nextSibling) {
						this.updateChatListRowClasses(nextSibling);
					}
				} catch(e) {
				}
			}
			return true;
		}
		return false;
	},
	
	blinkOnNewMessage: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) {
		if(this.settings['blink'] && this.lastID && !this.channelSwitch && userID != this.userID) {
			clearInterval(this.blinkInterval);
			this.blinkInterval = setInterval(
				'ajaxChat.blinkUpdate(\''+this.addSlashes(this.decodeSpecialChars(userName))+'\')',
				this.settings['blinkInterval']
			);
		}
	},
	
	blinkUpdate: function(blinkStr) {
		if(!this.originalDocumentTitle) {
			this.originalDocumentTitle = document.title;
		}
		if(!arguments.callee.blink) {
			document.title = '[@ ] '+blinkStr+' - '+this.originalDocumentTitle;
			arguments.callee.blink = 1;
		} else if(arguments.callee.blink > this.settings['blinkIntervalNumber']) {
			clearInterval(this.blinkInterval);
			document.title = this.originalDocumentTitle;
			arguments.callee.blink = 0;
		} else {
			if(arguments.callee.blink % 2 != 0) {
				document.title = '[@ ] '+blinkStr+' - '+this.originalDocumentTitle;
			} else {
				document.title = '[ @] '+blinkStr+' - '+this.originalDocumentTitle;
			}
			arguments.callee.blink++;
		}
	},
	
	updateChatlistView: function() {		
		if(this.settings['maxMessages'] && this.dom['chatList'].childNodes) {
			while(this.dom['chatList'].childNodes.length > this.settings['maxMessages']) {
				this.dom['chatList'].removeChild(this.dom['chatList'].firstChild);
			}
		}
		
		if(this.settings['autoScroll']) {
			this.dom['chatList'].scrollTop = this.dom['chatList'].scrollHeight;
		}
	},
	
	encodeText: function(text) {
		return encodeURIComponent(text);
	},

	decodeText: function(text) {
		return decodeURIComponent(text);
	},

	utf8Encode: function(plainText) {
		var utf8Text = '';
		for(var i=0; i<plainText.length; i++) {
			var c=plainText.charCodeAt(i);
			if(c<128) {
				utf8Text += String.fromCharCode(c);
			} else if((c>127) && (c<2048)) {
				utf8Text += String.fromCharCode((c>>6)|192);
				utf8Text += String.fromCharCode((c&63)|128);
			} else {
				utf8Text += String.fromCharCode((c>>12)|224);
				utf8Text += String.fromCharCode(((c>>6)&63)|128);
				utf8Text += String.fromCharCode((c&63)|128);
			}
		}
		return utf8Text;
	},

	utf8Decode: function(utf8Text) {
		var plainText = '';
		var c,c2,c3;
		var i=0;
		while(i<utf8Text.length) {
			c = utf8Text.charCodeAt(i);
			if(c<128) {
				plainText += String.fromCharCode(c);
				i++;
			} else if((c>191) && (c<224)) {
				c2 = utf8Text.charCodeAt(i+1);
				plainText += String.fromCharCode(((c&31)<<6) | (c2&63));
				i+=2;
			} else {
				c2 = utf8Text.charCodeAt(i+1);
				c3 = utf8Text.charCodeAt(i+2);
				plainText += String.fromCharCode(((c&15)<<12) | ((c2&63)<<6) | (c3&63));
				i+=3;
			}
		}
		return plainText;
	},

	encodeSpecialChars: function(text) {
		if (!arguments.callee.regExp) {
			arguments.callee.regExp = new RegExp('[&<>\'"]', 'g');
		}
		
		return text.replace(
			arguments.callee.regExp,
			this.encodeSpecialCharsCallback
		);
	},
	
	encodeSpecialCharsCallback: function(str) {
		switch(str) {
			case '&':
				return '&amp;';
			case '<':
				return '&lt;';
			case '>':
				return '&gt;';
			case '\'':
				// As &apos; is not supported by IE, we use &#39; as replacement for ('):
				return '&#39;';
			case '"':
				return '&quot;';
			default:
				return str;
		}
	},

	decodeSpecialChars: function(text) {
		if (!arguments.callee.regExp) {
			arguments.callee.regExp = new RegExp('(&amp;)|(&lt;)|(&gt;)|(&#39;)|(&quot;)', 'g');
		}
		
		return text.replace(
			arguments.callee.regExp,
			this.decodeSpecialCharsCallback
		);
	},
	
	decodeSpecialCharsCallback: function(str) {
		switch(str) {
			case '&amp;':
				return '&';
			case '&lt;':
				return '<';
			case '&gt;':
				return '>';
			case '&#39;':
				return '\'';
			case '&quot;':
				return '"';
			default:
				return str;
		}
	},
	
	inArray: function(haystack, needle) {
		var i = haystack.length;
		while(i--) {
			if(haystack[i] === needle) {
				return true;
			}
		}
		return false;
	},

	arraySearch: function(needle, haystack) {
		var i = haystack.length;
		while(i--) {
			if(haystack[i] === needle) {
				return i;
			}
		}
	    return false;
	},

	stripTags: function(str) {
		if (!arguments.callee.regExp) {
			arguments.callee.regExp = new RegExp('<\\/?[^>]+?>', 'g');
		}
		
		return str.replace(arguments.callee.regExp, '');
	},

	stripBBCodeTags: function(str) {
		if (!arguments.callee.regExp) {
			arguments.callee.regExp = new RegExp('\\[\\/?[^\\]]+?\\]', 'g');
		}
		
		return str.replace(arguments.callee.regExp, '');
	},	

	escapeRegExp: function(text) {
		if (!arguments.callee.regExp) {
			var specials = new Array(
				'^', '$', '*', '+', '?', '.', '|', '/',
				'(', ')', '[', ']', '{', '}', '\\'
			);
			arguments.callee.regExp = new RegExp(
				'(\\' + specials.join('|\\') + ')', 'g'
			);
		}
		return text.replace(arguments.callee.regExp, '\\$1');
	},
	
	addSlashes: function(text) {
		// Adding slashes in front of apostrophs and backslashes to ensure a valid JavaScript expression:
		return text.replace(/\\/g, '\\\\').replace(/\'/g, '\\\'');
	},

	removeSlashes: function(text) {
		// Removing slashes added by calling addSlashes(text) previously:
		return text.replace(/\\\\/g, '\\').replace(/\\\'/g, '\'');
	},

	formatDate: function(format, date) {
		date = (date == null) ? new date() : date;
		// Added by nikolp, for Moskow time: 13.10.2008
		tHours = date.getHours();
		tHours = tHours >= 24 ? 0 : tHours;

		return format
		.replace(/%Y/g, parseInt(date.getFullYear()))
		.replace(/%m/g, this.addLeadingZero(date.getMonth()+1))
		.replace(/%d/g, this.addLeadingZero(date.getDate()))
		.replace(/%H/g, this.addLeadingZero(tHours))
		.replace(/%i/g, this.addLeadingZero(date.getMinutes()))
		.replace(/%s/g, this.addLeadingZero(date.getSeconds()));
	},
	
	addLeadingZero: function(number) {
		number = number.toString();
		if(number.length < 2) {
			number = '0'+number;
		}
		return number;
	},

	getUserIDFromUserName: function(userName) {
		var index = this.arraySearch(userName, this.userNamesList);
		if(index !== false) {
			return this.usersList[index];
		}
		return null;
	},

	getUserNameFromUserID: function(userID) {
		var index = this.arraySearch(userID, this.usersList);
		if(index !== false) {
			return this.userNamesList[index];
		}
		return null;
	},
	
	// return GMT number for userID
	getUserGmtTimeFromUserID: function(userID) {
		var index = this.arraySearch(userID, this.usersList);
		if(index !== false) {
		   return parseInt(this.userGmtTimesList[index]);
		}
		// GMT + 3 Moscow time
		return 3;
	},
	
	// метод возращает группу пользователя по ид из списка онлайн - nikolp:27.03.2009
	getUserGroupFromUserID: function(userID) {
		var index = this.arraySearch(userID, this.usersList);
		if(index !== false) {
		   return parseInt(this.userGroupList[index]);
		}
		return 0;
	},
	
	// метод возращает группу пользователя по имени из списка онлайн - nikolp:27.03.2009
	getUserGroupFromUserName: function(userName) {
		var index = this.arraySearch(userName, this.userNamesList);
		if(index !== false) {
		   return parseInt(this.userGroupList[index]);
		}
		return 0;
	},
	
	// метод возращает группы пользователя по ид - nikolp:27.03.2009
	getUserGroupStyle: function(groupID) {
		if(this.userGroupStyleList[groupID] != undefined) {
		   return this.userGroupStyleList[groupID];
		}
		
		return null;
	},
	
	// метод возращает форматированное имя пользователя - nikolp:27.03.2009
	getStyledUserName: function(userName) {
	   var styledUserName = userName;
	   // устанавливаем группу пользователя из списка онлайн
	   var userGroup = this.getUserGroupFromUserName(styledUserName);
	   // если группа установлена, то устаналиваем стиль для пользователя
	   if (userGroup) {
	      var groupStyle = this.getUserGroupStyle(userGroup);
         if(groupStyle) {
            //if(this.userID == 1)
               styledUserName = groupStyle.replace('{username}', styledUserName);
         }
	   } 
		
		return styledUserName;
	},
	
	
	
   getUserNameFromUserID: function(userID) {
      var index = this.arraySearch(userID, this.usersList);
      if(index !== false) {
         return this.userNamesList[index];
      }
      return null;
   },
	

	getRoleClass: function(roleID) {
		switch(parseInt(roleID)) {
			case 0:
				return 'guest';
			case 1:
				return 'user';
			case 2:
				return 'moderator';
			case 3:
				return 'admin';
			case 4:
				return 'chatBot';
			default:
				return 'default';
		}
	},
	
	handleInputFieldKeyPress: function(event) {
		if(event.keyCode == 13 && !event.shiftKey) {
			this.sendMessage();
			try {
				event.preventDefault();
			} catch(e) {
				event.returnValue = false; // IE
			}
			return false;
		}
		return true;
	},

	handleInputFieldKeyUp: function(event) {
		this.updateMessageLengthCounter();
	},
	
	updateMessageLengthCounter: function() {
		if(this.dom['messageLengthCounter']) {
			this.updateDOM(
				'messageLengthCounter',
				this.dom['inputField'].value.length	+ '/' + this.messageTextMaxLength,
				false,
				true
			)
		}
	},
	
	sendMessage: function(text) {
		text = text ? text : this.dom['inputField'].value;
		if(!text) {
			return;
		}
		text = this.parseInputMessage(text);
		if(text) {
			clearTimeout(this.timer);
			
			this.currentMsg = text;
			
			var message = 	'lastID='
							+ this.lastID
							+ '&text='
							+ this.encodeText(text);				
			
			this.makeRequest(this.ajaxURL,'POST',message);
		}
		/* добавлено для сохраниния сообщения в окне ввода: added by nikolp*/
		//if(this.currentMsg == null) 
		this.dom['inputField'].value = '';
		this.dom['inputField'].focus();
      this.updateMessageLengthCounter();
	},
	
	parseInputMessage: function(text) {
		if(text.charAt(0) == '/') {
			var textParts = text.split(' ');
			switch(textParts[0]) {
				case '/ignore':
					text = this.parseIgnoreInputCommand(text, textParts);
					break;
				default:
					text = this.parseCustomInputCommand(text, textParts);
			}
			if(text && this.settings['persistFontColor'] && this.settings['fontColor']) {
				text = this.assignFontColorToCommandMessage(text, textParts);
			}
		} else {
			text = this.parseCustomInputMessage(text);
			if(text && this.settings['persistFontColor'] && this.settings['fontColor']) {
				text = this.assignFontColorToMessage(text);
			}
		}
		return text;
	},
	
	assignFontColorToMessage: function(text) {
		return '[color='+this.settings['fontColor']+']'+text+'[/color]';
	},

	assignFontColorToCommandMessage: function(text, textParts) {
		switch(textParts[0]) {
			case '/msg':
			case '/describe':
				if(textParts.length > 2) {
					return	textParts[0]+' '+textParts[1]+' '
							+ '[color='+this.settings['fontColor']+']'
							+ textParts.slice(2).join(' ')
							+ '[/color]';
				}
				break;
			case '/action':
				if(textParts.length > 1) {
					return	textParts[0]+' '
							+ '[color='+this.settings['fontColor']+']'
							+ textParts.slice(1).join(' ')
							+ '[/color]';
				}
				break;
		}
		return text;
	},
	
	parseIgnoreInputCommand: function(text, textParts) {
		var ignoredUserNames = this.getIgnoredUserNames();
		if(textParts.length > 1) {
			var userName = this.encodeSpecialChars(textParts[1]);
			// Prevent adding the chatBot or current user to the list:
			if(userName == this.userName || userName == this.getEncodedChatBotName()) {
				// Display the list of ignored users instead:
				return this.parseIgnoreInputCommand(null, new Array('/ignore'));
			}
			if(ignoredUserNames.length > 0) {
				var i = ignoredUserNames.length;
				while(i--) {
					if(ignoredUserNames[i] === userName) {
						ignoredUserNames.splice(i,1);
						this.addChatBotMessageToChatList('/ignoreRemoved '+userName);
						this.setIgnoredUserNames(ignoredUserNames);
						this.updateChatlistView();
						return null;
					}
				}
			}
			ignoredUserNames.push(userName);
			this.addChatBotMessageToChatList('/ignoreAdded '+userName);
			this.setIgnoredUserNames(ignoredUserNames);
		} else {
			if(ignoredUserNames.length == 0) {
				this.addChatBotMessageToChatList('/ignoreListEmpty -');
			} else {
				this.addChatBotMessageToChatList('/ignoreList '+ignoredUserNames.join(' '));
			}
		}
		this.updateChatlistView();
		return null;
	},

	getIgnoredUserNames: function() {
		if(!this.ignoredUserNames) {
			var ignoredUserNamesString = this.getSetting('ignoredUserNames');
			if(ignoredUserNamesString) {
				this.ignoredUserNames = ignoredUserNamesString.split(' ');
			} else {
				this.ignoredUserNames = new Array();
			}
		}
		return this.ignoredUserNames;
	},
	
	setIgnoredUserNames: function(ignoredUserNames) {
		this.ignoredUserNames = ignoredUserNames;
		this.setSetting('ignoredUserNames', ignoredUserNames.join(' '));
	},
	
	ignoreMessage: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) {
		if(userID == this.chatBotID && messageText.charAt(0) == '/') {
			var textParts = messageText.split(' ');
			if(textParts.length > 1) {
				switch(textParts[0]) {
					case '/invite':
					case '/uninvite':
					case '/roll':
						userName = textParts[1];
						break;
				}	
			}
		}
		if(this.inArray(this.getIgnoredUserNames(), userName)) {
			return true;
		}
		return false;
	},

	deleteMessage: function(messageID) {
		var messageNode = this.getMessageNode(messageID);
		if(messageNode) {
			var originalClass = this.getClass(messageNode);
			this.setClass(messageNode, originalClass+' deleteSelected');
			if(confirm(this.lang['deleteMessageConfirm'])) {
				var nextSibling = messageNode.nextSibling;
				try {
					this.dom['chatList'].removeChild(messageNode);
					if(nextSibling) {
						this.updateChatListRowClasses(nextSibling);
					}
					this.updateChat('&delete='+messageID);
				} catch(e) {
					this.setClass(messageNode, originalClass);
				}
			} else {
				this.setClass(messageNode, originalClass);
			}
		}
	},
	
	parentMessage: function(messageID, commentTitle) {
		var messageNode = this.getMessageNode(messageID);
		if(messageNode) {
			// var originalClass = this.getClass(messageNode);
			// this.setClass(messageNode, originalClass+' parentSelected');
			// if(confirm(this.lang['parentMessageConfirm'])) {
         // this.setClass(messageNode, originalClass);
		   if(this.openerWindow != null && this.openerWindow.ajaxChat != undefined){
		      // set text into opener input field
            this.openerWindow.focus();
		      this.openerWindow.ajaxChat.insert('[parent=' +  messageID + ']' + this.trim(commentTitle) + '[/parent] ', '');				
		   }
         else{			   
            this.insert('[parent=' +  messageID + ']' + this.trim(commentTitle) + '[/parent] ', '');				
         }
			// } 
			// else {
			//	this.setClass(messageNode, originalClass);
			// }
		}
	},

	updateChatListRowClasses: function(node) {
		if(!node) {
			node = this.dom['chatList'].firstChild;
		}
		if(node) {
			var previousNode = node.previousSibling;
			var rowEven = (previousNode && this.getClass(previousNode) == 'rowOdd') ? true : false;
			while(node) {
				this.setClass(node, (rowEven ? 'rowEven' : 'rowOdd'))
				node = node.nextSibling;
				rowEven = !rowEven;
			}
		}
	},
	
	getClass: function(node) {
		if(typeof node.className != 'undefined') {
			return node.className; // IE
		} else {
			return node.getAttribute('class');
		}
	},
	
	setClass: function(node, className) {
		if(typeof node.className != 'undefined') {
			node.className = className; // IE
		} else {
			node.setAttribute('class', className);
		}
	},

	scriptLinkEncode: function(text) {
		return this.encodeText(this.addSlashes(this.decodeSpecialChars(text)));
	},
	
	scriptLinkDecode: function(text) {
		return this.encodeSpecialChars(this.removeSlashes(this.decodeText(text)));
	},

	getScriptLinkValue: function(value) {
		// This method returns plainText encoded values from javascript links
		// The value has to be utf8Decoded for MSIE and Opera:
		if(typeof arguments.callee.utf8Decode == 'undefined') {
			switch(navigator.appName) {
				case 'Microsoft Internet Explorer':
				case 'Opera':
				    // --> commented by nikolp: cyrillic problem (05.12.2008)
					//arguments.callee.utf8Decode = true;
					//return this.utf8Decode(value);
				    // -->commented by nikolp: cyrillic problem (05.12.2008)
				    // --> added by nikolp: cyrillic problem (05.12.2008)
					arguments.callee.utf8Decode = false;
					return value;
				    // --> added by nikolp: cyrillic problem (05.12.2008)
				default:
					arguments.callee.utf8Decode = false;
					return value;
			}	
		} else if(arguments.callee.utf8Decode) {
			return this.utf8Decode(value);	
		} else {
			return value;
		}
	},

	sendMessageWrapper: function(text) {
		this.sendMessage(this.getScriptLinkValue(text));
	},

	insertMessageWrapper: function(text) {
		this.insertText(this.getScriptLinkValue(text), true);
	},
	
	switchShowChannelMessages: function (){
		this.clearChatList();
        this.requestTeaserContent();
	},
	
	switchChannel: function(channel) {
		if(!this.chatStarted) {
			this.clearChatList();
			this.channelSwitch = true;
			this.loginChannelID = null;
			this.loginChannelName = channel;
			this.requestTeaserContent();
			return;
		}
		clearTimeout(this.timer);	
		var message = 	'lastID='
						+ this.lastID
						+ '&channelName='
						+ this.encodeText(channel);		
		this.makeRequest(this.ajaxURL,'POST',message);
		if(this.dom['inputField'] && this.settings['autoFocus']) {
			this.dom['inputField'].focus();
		}
	},

	logout: function() {
		clearTimeout(this.timer);
		var message = 'logout=true';
		this.makeRequest(this.ajaxURL,'POST',message);
	},
	
	handleLogout: function(url) {
		window.location.href = url;
	},

	toggleSetting: function(setting, buttonID) {
		this.setSetting(setting, !this.getSetting(setting));
		if(buttonID) {
			this.updateButton(setting, buttonID);
		}
	},
    
	toggleShowChannelMessages: function(buttonID) {
	    this.lastID = 0;
		this.showChannelMessages = !this.showChannelMessages;
		
		ajaxChat.switchShowChannelMessages();
		
		if(buttonID) {
			var node = document.getElementById(buttonID);
		    if(node) {
			    this.setClass(node, (this.showChannelMessages ? 'button' : 'button off'))
		    }
		}
	},
    
    refreshWindow: function(){
        this.lastID = 0;
		this.switchShowChannelMessages();
    },
    
	updateButton: function(setting, buttonID) {
		var node = document.getElementById(buttonID);
		if(node) {
			this.setClass(node, (this.getSetting(setting) ? 'button' : 'button off'))
		}
	},
	
	showHide: function(id, styleDisplay, displayInline) {
		var node = document.getElementById(id);
		if(node) {
			if(styleDisplay) {
				node.style.display = styleDisplay;
			} else {
				if(node.style.display == 'none') {
					node.style.display = (displayInline ? 'inline' : 'block'); 
				} else {
					node.style.display = 'none';
				}
			}	
		}
	},

	setPersistFontColor: function(bool) {
		this.settings['persistFontColor'] = bool;		
		if(!this.settings['persistFontColor']) {
			this.settings['fontColor'] = null;
			if(this.dom['inputField']) {
				this.dom['inputField'].style.color = '';
			}
		}
	},

	setFontColor: function(color) {
		if(this.settings['persistFontColor']) {
			this.settings['fontColor'] = color;
			if(this.dom['inputField']) {
				this.dom['inputField'].style.color = color;
			}
			if(this.dom['colorCodesContainer']) {
				this.dom['colorCodesContainer'].style.display = 'none';
				if(this.dom['inputField']) {
					this.dom['inputField'].focus();
				}
			}
		} else {
			this.insert('[color=' + color + ']', '[/color]');
		}
	},
	
	insertText: function(text, clearInputField) {
		if(clearInputField) {
			this.dom['inputField'].value = '';
		}
		this.insert(text, '');
	},
	
	insertBBCode: function(bbCode) {
		switch(bbCode) {			
			case 'url':
				var url = prompt(this.lang['urlDialog'], 'http://');
				if(url)
					this.insert('[url=' + url + ']', '[/url]');
				else
					this.dom['inputField'].focus();
				break;
			default:
				this.insert('[' + bbCode + ']', '[/' + bbCode + ']');		
		}
	},
	
    insertTextAbove: function(text) {
		this.dom['inputField'].focus();
      var pos = this.dom['inputField'].value.length;
		
		// bug nr. 24; nikolp: 23.01.2009
		if(this.dom['inputField'].value.indexOf('->') > -1 || this.dom['inputField'].value.indexOf('/msg') > -1){
         var textParts = this.dom['inputField'].value.split(' ');
         var insertTextParts = text.split(' ');
         
         if(textParts[0] != insertTextParts[0]){
            this.dom['inputField'].value = this.dom['inputField'].value.replace(textParts[0], insertTextParts[0]);
         }
         if(textParts[1] != undefined && textParts[1] != insertTextParts[1]){
            if(this.dom['inputField'].value.substr((textParts[1].length + textParts[0].length +1), 1) != ' ') {
               insertTextParts[1] += ' ';
            }
            this.dom['inputField'].value = this.dom['inputField'].value.replace(textParts[1], insertTextParts[1]);
         }
         else if(textParts[1] != undefined && this.dom['inputField'].value.substr((textParts[1].length + textParts[0].length +1), 1) != ' '){
            this.dom['inputField'].value = this.dom['inputField'].value.replace(textParts[1], textParts[1] + ' ');            
         }
		}
      else{
	      this.dom['inputField'].value = text
            + this.dom['inputField'].value.substr(0, pos)
		      + this.dom['inputField'].value.substr(pos);
	   }
    },
    
	insert: function(startTag, endTag) {
		this.dom['inputField'].focus();
		// Internet Explorer:
		if(typeof document.selection != 'undefined') {
			// Insert the tags:
			var range = document.selection.createRange();
			var insText = range.text;
			range.text = startTag + insText + endTag;
			// Adjust the cursor position:
			range = document.selection.createRange();
			if (insText.length == 0) {
				range.move('character', -endTag.length);
			} else {
				range.moveStart('character', startTag.length + insText.length + endTag.length);			
			}
			range.select();
		}
		// Firefox, etc. (Gecko based browsers):
		else if(typeof this.dom['inputField'].selectionStart != 'undefined') {
			// Insert the tags:
			var start = this.dom['inputField'].selectionStart;
			var end = this.dom['inputField'].selectionEnd;
			var insText = this.dom['inputField'].value.substring(start, end);
			this.dom['inputField'].value = 	this.dom['inputField'].value.substr(0, start)
											+ startTag
											+ insText
											+ endTag
											+ this.dom['inputField'].value.substr(end);
			// Adjust the cursor position:
			var pos;
			if (insText.length == 0) {
				pos = start + startTag.length;
			} else {
				pos = start + startTag.length + insText.length + endTag.length;
			}
			this.dom['inputField'].selectionStart = pos;
			this.dom['inputField'].selectionEnd = pos;
		}
		// Other browsers:
		else {
			var pos = this.dom['inputField'].value.length;
			this.dom['inputField'].value = 	this.dom['inputField'].value.substr(0, pos)
											+ startTag
											+ endTag
											+ this.dom['inputField'].value.substr(pos);
		}
	},
	
	replaceText: function(text) {
		try{
			text = this.replaceLineBreaks(text);
			if(text.charAt(0) == '/' || text.charAt(0) == '-') {
				text = this.replaceCommands(text);
			} else {
				text = this.replaceBBCode(text);
				text = this.replaceHyperLinks(text);
				text = this.replaceEmoticons(text);
			}
			text = this.breakLongWords(text);		
			text = this.replaceCustomText(text);
		} catch(e){
			//alert(e);
		}
		return text;
	},
	
	getReplacedCommands: function(text){
		try {
		    if(text.charAt(0) != '/' && text.charAt(0) != '-') {
			    return "";
		    }	
		    
		    var textParts = text.split(' ');				
		    
		    switch(textParts[0]) {
                case '/privmsg':
					return this.getCommandPrivMsg(textParts);
				case '/privmsgto':
					return this.getCommandPrivMsgTo(textParts);
				case '-&gt;':
				    return this.getAddressedMessage(textParts[1]);

				default:
					return "";
			}
		} catch(e) {
			//alert(e);
		}
		return "";
	},
	
	replaceCommands: function(text) {
		try {
		    //alert(text);
			if(text.charAt(0) != '/' && text.charAt(0) != '-') {
				return text;
			}
			var textParts = text.split(' ');				
			switch(textParts[0]) {
				case '/login':
					return this.replaceCommandLogin(textParts);
				case '/logout':
					return this.replaceCommandLogout(textParts);
				case '/channelEnter':
					return this.replaceCommandChannelEnter(textParts);
				case '/channelLeave':
					return this.replaceCommandChannelLeave(textParts);
				case '/privmsg':
					return this.replaceCommandPrivMsg(textParts);
				case '/privmsgto':
					return this.replaceCommandPrivMsgTo(textParts);
				case '/privaction':
					return this.replaceCommandPrivAction(textParts);
				case '/privactionto':
					return this.replaceCommandPrivActionTo(textParts);
				case '/action':
					return this.replaceCommandAction(textParts);
				case '/invite':
					return this.replaceCommandInvite(textParts);
				case '/inviteto':
					return this.replaceCommandInviteTo(textParts);
				case '/uninvite':
					return this.replaceCommandUninvite(textParts);
				case '/uninviteto':
					return this.replaceCommandUninviteTo(textParts);
				case '/queryOpen':
					return this.replaceCommandQueryOpen(textParts);
				case '/queryClose':
					return this.replaceCommandQueryClose(textParts);
				case '/ignoreAdded':
					return this.replaceCommandIgnoreAdded(textParts);
				case '/ignoreRemoved':
					return this.replaceCommandIgnoreRemoved(textParts);
				case '/ignoreList':
					return this.replaceCommandIgnoreList(textParts);
				case '/ignoreListEmpty':
					return this.replaceCommandIgnoreListEmpty(textParts);
				case '/kick':
					return this.replaceCommandKick(textParts);
				case '/who':
					return this.replaceCommandWho(textParts);
				case '/whoChannel':
					return this.replaceCommandWhoChannel(textParts);
				case '/whoEmpty':
					return this.replaceCommandWhoEmpty(textParts);
				case '/list':
					return this.replaceCommandList(textParts);
				case '/bans':
					return this.replaceCommandBans(textParts);
				case '/bansEmpty':
					return this.replaceCommandBansEmpty(textParts);
				case '/unban':
					return this.replaceCommandUnban(textParts);
				case '/whois':
					return this.replaceCommandWhois(textParts);
				case '/whereis':
					return this.replaceCommandWhereis(textParts);
				case '/roll':
					return this.replaceCommandRoll(textParts);
				case '/nick':
					return this.replaceCommandNick(textParts);
				case '/error':
					return this.replaceCommandError(textParts);
				case '/message':
				   // added by nikolp for connection message
					return this.replaceCommandMessage(textParts);
				case '-&gt;':
					return this.replaceAddressedMessage(textParts, textParts[1]);
				default:
					return this.replaceCustomCommands(text, textParts);
			}
		} catch(e) {
			//alert(e);
		}
		return text;
	},

   /*
   * changed for replace user name into linked user name
   *
   * nikolp: 21.01.2009
   *
   */
	replaceCommandLogin: function(textParts) {
	   
	   var userName = textParts[1];
	   userName = this.replaceUserNameWithLink(userName);
		
		return '<span class="chatBotMessage">'
				+ this.lang['login'].replace(/%s/, userName)
				+ '</span>';		
	},
   
   /*
   * added NEW function for replace user name into linked user name
   *
   * nikolp: 21.01.2009
   *
   **/
   replaceUserNameWithLink: function (userName, userRole){
      
      var cssClass = (userRole ? 'class="' + this.getRoleClass(userRole)+ '" ' : '');
      var styledUserName = userName;
      var _userID=this.getUserIDFromUserName(userName);
      if(_userID) {
         var _groupID=this.getUserGroupFromUserID(_userID);
         if (_groupID) {
            var groupStyle = this.getUserGroupStyle(_groupID).replace('uid=""', 'uid="'+_userID+'"');
            if(groupStyle) {
               //if(this.userID == 1)
                  styledUserName = groupStyle.replace('{username}', styledUserName);
            }
         }
      }
      
      return '<a href="javascript:ajaxChat.insertTextAbove(\'-> ' + userName + ' \');" '
            + cssClass
            +'title="'
				+ this.lang['sendaddressedmsgto'].replace(/%s/, userName)
				+ '">'
				+ styledUserName
				+ '</a>';
   },
   
	replaceCommandLogout: function(textParts) {
		var type = '';
		if(textParts.length == 3)
			type = textParts[2];
		
		return '<span class="chatBotMessage">'
				+ this.lang['logout' + type].replace(/%s/, textParts[1])
				+ '</span>';		
	},
	
	replaceCommandChannelEnter: function(textParts) {
		return	'<span class="chatBotMessage">'
				+ this.lang['channelEnter'].replace(/%s/, textParts[1])
				+ '</span>';		
	},
	
	replaceCommandChannelLeave: function(textParts) {
		return	'<span class="chatBotMessage">'
				+ this.lang['channelLeave'].replace(/%s/, textParts[1])
				+ '</span>';		
	},
	
	replaceCommandPrivMsg: function(textParts) {
		var privMsgText = textParts.slice(1).join(' ');
		privMsgText = this.replaceBBCode(privMsgText);
		privMsgText = this.replaceHyperLinks(privMsgText);
		privMsgText = this.replaceEmoticons(privMsgText);
		return	'<span class="privmsgto">'
				+ privMsgText
				+ '</span>';
	},

	getCommandPrivMsg: function(textParts) {
		return	'<span class="privmsg">'
				+ this.lang['privmsg']
				+ '</span>';
	},
	
	replaceCommandPrivMsgTo: function(textParts) {
		var privMsgText = textParts.slice(2).join(' ');
		privMsgText = this.replaceBBCode(privMsgText);
		privMsgText = this.replaceHyperLinks(privMsgText);
		privMsgText = this.replaceEmoticons(privMsgText);
		return privMsgText;
	},

	getCommandPrivMsgTo: function(textParts) {
	   // получаем идент. номер пользователя если он в списке онлайн
	   var usedID = this.getUserIDFromUserName(textParts[1]);
	   // устанавливаем стиль для пользователя - nikolp: 27.03.2009
	   var styledUserName = 
	      this.getStyledUserName(
	         textParts[1]).replace(
	            'uid=""', 
	            (usedID ? 'uid="' + usedID  + '"' : ''));
	   
	   // added by nikolp: 16.01.2009
      userName = '<a href="javascript:ajaxChat.insertTextAbove(\'/msg ' + textParts[1] + ' \');" '
			+ ' title="'
			+ this.lang['sendprivmsgto'].replace(/%s/, textParts[1])
			+ '">'
			+ styledUserName
		   + '</a>';
      
		return	'<span class="privmsg">'
				+ this.lang['privmsgto'].replace(/%s/, userName)
				+ '</span>';
	},
	
	replaceCommandPrivAction: function(textParts) {
		var privActionText = textParts.slice(1).join(' ');
		privActionText = this.replaceBBCode(privActionText);
		privActionText = this.replaceHyperLinks(privActionText);
		privActionText = this.replaceEmoticons(privActionText);
		return	'<span class="action">'
				+ privActionText
				+ '</span> <span class="privmsg">'
				+ this.lang['privmsg']
				+ '</span> ';
	},
	
	replaceCommandPrivActionTo: function(textParts) {
	   // устанавливаем стиль для пользователя - nikolp: 27.03.2009
	   var styledUserName = this.getStyledUserName(textParts[1]);
	
		var privActionText = textParts.slice(2).join(' ');
		privActionText = this.replaceBBCode(privActionText);
		privActionText = this.replaceHyperLinks(privActionText);
		privActionText = this.replaceEmoticons(privActionText);
		return	'<span class="action">'
				+ privActionText
				+ '</span> <span class="privmsg">'
				+ this.lang['privmsgto'].replace(/%s/, styledUserName)
				+ '</span> ';		
	},
	
	replaceCommandAction: function(textParts) {
		var actionText = textParts.slice(1).join(' ');
		actionText = this.replaceBBCode(actionText);
		actionText = this.replaceHyperLinks(actionText);
		actionText = this.replaceEmoticons(actionText);
		return	'<span class="action">'
				+ actionText
				+ '</span>';		
	},
	
	replaceCommandInvite: function(textParts) {
		var inviteText = this.lang['invite']
							.replace(/%s/, textParts[1])
							.replace(
								/%s/,
								'<a href="javascript:ajaxChat.sendMessageWrapper(\'/join '
								+ this.scriptLinkEncode(textParts[2])
								+ '\');" title="'
								+ this.lang['joinChannel'].replace(/%s/, textParts[2])
								+ '">'
								+ textParts[2]
								+ '</a>'
							);
		return	'<span class="chatBotMessage">'
				+ inviteText
				+ '</span>';		
	},
	
	replaceCommandInviteTo: function(textParts) {
		var inviteText = this.lang['inviteto']
							.replace(/%s/, textParts[1])
							.replace(/%s/, textParts[2]);
		return	'<span class="chatBotMessage">'
				+ inviteText
				+ '</span>';		
	},
	
	replaceCommandUninvite: function(textParts) {
		var uninviteText = this.lang['uninvite']
							.replace(/%s/, textParts[1])
							.replace(/%s/, textParts[2]);
		return	'<span class="chatBotMessage">'
				+ uninviteText
				+ '</span>';		
	},
	
	replaceCommandUninviteTo: function(textParts) {
		var uninviteText = this.lang['uninviteto']
							.replace(/%s/, textParts[1])
							.replace(/%s/, textParts[2]);
		return	'<span class="chatBotMessage">'
				+ uninviteText
				+ '</span>';		
	},
	
	replaceCommandQueryOpen: function(textParts) {
		return	'<span class="chatBotMessage">'
				+ this.lang['queryOpen'].replace(/%s/, textParts[1])
				+ '</span>';		
	},
		
	replaceCommandQueryClose: function(textParts) {
		return	'<span class="chatBotMessage">'
				+ this.lang['queryClose'].replace(/%s/, textParts[1])
				+ '</span>';		
	},
		
	replaceCommandIgnoreAdded: function(textParts) {
		return	'<span class="chatBotMessage">'
				+ this.lang['ignoreAdded'].replace(/%s/, textParts[1])
				+ '</span>';		
	},
		
	replaceCommandIgnoreRemoved: function(textParts) {
		return	'<span class="chatBotMessage">'
				+ this.lang['ignoreRemoved'].replace(/%s/, textParts[1])
				+ '</span>';		
	},
		
	replaceCommandIgnoreList: function(textParts) {
		return	'<span class="chatBotMessage">'
				+ this.lang['ignoreList'] + ' '
				+ this.getInlineUserMenu(textParts.slice(1))
				+ '</span>';		
	},
		
	replaceCommandIgnoreListEmpty: function(textParts) {
		return	'<span class="chatBotMessage">'
				+ this.lang['ignoreListEmpty']
				+ '</span>';			
	},
		
	replaceCommandKick: function(textParts) {
		return	'<span class="chatBotMessage">'
				+ this.lang['logoutKicked'].replace(/%s/, textParts[1])
				+ '</span>';		
	},
		
	replaceCommandWho: function(textParts) {
		return	'<span class="chatBotMessage">'
				+ this.lang['who'] + ' '
				+ this.getInlineUserMenu(textParts.slice(1))
				+ '</span>';		
	},

	replaceCommandWhoChannel: function(textParts) {
		return	'<span class="chatBotMessage">'
				+ this.lang['whoChannel'].replace(/%s/, textParts[1]) + ' '
				+ this.getInlineUserMenu(textParts.slice(2))
				+ '</span>';		
	},
	
	replaceCommandWhoEmpty: function(textParts) {
		return	'<span class="chatBotMessage">'
				+ this.lang['whoEmpty']
				+ '</span>';		
	},
		
	replaceCommandList: function(textParts) {
		var channels = textParts.slice(1);
		var listChannels = new Array();
		var channelName;
		for(var i=0; i<channels.length; i++) {
			channelName = (channels[i] == this.channelName) ? '<b>'+channels[i]+'</b>' : channels[i];
			listChannels.push(
				'<a href="javascript:ajaxChat.sendMessageWrapper(\'/join '
				+ this.scriptLinkEncode(channels[i])
				+ '\');" title="'
				+ this.lang['joinChannel'].replace(/%s/, channels[i])
				+ '">'
				+ channelName
				+ '</a>'
			);
		}
		return	'<span class="chatBotMessage">'
				+ this.lang['list'] + ' '
				+ listChannels.join(', ')
				+ '</span>';		
	},
		
	replaceCommandBans: function(textParts) {
		var users = textParts.slice(1);
		var listUsers = new Array();
		for(var i=0; i<users.length; i++) {
			listUsers.push(
				'<a href="javascript:ajaxChat.sendMessageWrapper(\'/unban '
				+ this.scriptLinkEncode(users[i])
				+ '\');" title="'
				+ this.lang['unbanUser'].replace(/%s/, users[i])
				+ '">'
				+ users[i]
				+ '</a>'
			);
		}
		return	'<span class="chatBotMessage">'
				+ this.lang['bans'] + ' '
				+ listUsers.join(', ')
				+ '</span>';		
	},
		
	replaceCommandBansEmpty: function(textParts) {
		return	'<span class="chatBotMessage">'
				+ this.lang['bansEmpty']
				+ '</span>';		
	},
		
	replaceCommandUnban: function(textParts) {
		return	'<span class="chatBotMessage">'
				+ this.lang['unban'].replace(/%s/, textParts[1])
				+ '</span>';		
	},
	
	replaceCommandWhois: function(textParts) {
		return	'<span class="chatBotMessage">'
				+ this.lang['whois'].replace(/%s/, textParts[1]) + ' '
				+ textParts[2]
				+ '</span>';		
	},

	replaceCommandWhereis: function(textParts) {
		return	'<span class="chatBotMessage">'
				+ this.lang['whereis'].replace(/%s/, textParts[1]).replace(
								/%s/,
								'<a href="javascript:ajaxChat.sendMessageWrapper(\'/join '
								+ this.scriptLinkEncode(textParts[2])
								+ '\');" title="'
								+ this.lang['joinChannel'].replace(/%s/, textParts[2])
								+ '">'
								+ textParts[2]
								+ '</a>'
							)
				+ '</span>';		
	},
	
	/*
   * changed for replace user name into linked user name
   *
   * nikolp: 21.01.2009
   *
   */
   replaceCommandRoll: function(textParts) {
	   var userName = textParts[1];
      userName = this.replaceUserNameWithLink(userName);

		var rollText = this.lang['roll'].replace(/%s/, userName);
		rollText = rollText.replace(/%s/, textParts[2]);
		rollText = rollText.replace(/%s/, textParts[3]);
		return	'<span class="chatBotMessage">'
				+ rollText
				+ '</span>';		
	},
		
	replaceCommandNick: function(textParts) {
		return	'<span class="chatBotMessage">'
				+ this.lang['nick'].replace(/%s/, textParts[1]).replace(/%s/, textParts[2])
				+ '</span>';		
	},
		
   /*
    * метод возвращает сообщение ошибки по коду ошибки....
    *
   */
	replaceCommandError: function(textParts) {
		var errorMessage = this.lang['error'+textParts[1]];
		if(!errorMessage) {
			errorMessage = 'Error: Unknown.';
		} else if(textParts.length > 2) {
			errorMessage = errorMessage.replace(/%s/, textParts.slice(2).join(' '));
		}
		return	'<span class="chatBotErrorMessage">'
				+ errorMessage
				+ '</span>';		
	},
	
	replaceCommandMessage: function(textParts) {
		var message = this.lang['message'+textParts[1]];
		if(!message) {
			message = 'Message: Unknown.';
		} else if(textParts.length > 2) {
			message = message.replace(/%s/, textParts.slice(2).join(' '));
		}
		return	'<span class="chatBotMessage">'
				+ message
				+ '</span>';		
	},

	getInlineUserMenu: function(users) {
		var menu = '';
		for(var i=0; i<users.length; i++) {
			if(i>0) {
				menu += ', ';
			}
			menu	+= '<a href="javascript:ajaxChat.toggleUserMenu(\''
					+ this.getInlineUserMenuDocumentID(this.userMenuCounter, i)
					+ '\', \''
					+ this.scriptLinkEncode(users[i])
					+ '\', null);" title="'
					+ this.lang['toggleUserMenu'].replace(/%s/, users[i])
					+ '" dir="'
					+ this.baseDirection
					+ '">'
					+ ((users[i] == this.userName) ? '<b>'+users[i]+'</b>' : users[i])
					+ '</a>'
					+ '<ul class="inlineUserMenu" id="'
					+ this.getInlineUserMenuDocumentID(this.userMenuCounter, i)
					+ '" style="display:none;">'
					+ '</ul>';
		}
		this.userMenuCounter++;
		return menu;
	},
	
	containsUnclosedTags: function(str) {
		if (!arguments.callee.regExpOpenTags || !arguments.callee.regExpCloseTags) {
			arguments.callee.regExpOpenTags		= new RegExp('<[^>\\/]+?>', 'gm');
			arguments.callee.regExpCloseTags	= new RegExp('<\\/[^>]+?>', 'gm');
		}	
		var openTags	= str.match(arguments.callee.regExpOpenTags);
		var closeTags	= str.match(arguments.callee.regExpCloseTags);
		// Return true if the number of tags doesn't match:
		if((!openTags && closeTags) ||
			(openTags && !closeTags) ||
			(openTags && closeTags && (openTags.length != closeTags.length))) {
			return true;
		}
		return false;
	},
		
	breakLongWords: function(text) {
		if(!this.settings['wordWrap'])
			return text;
		var newText = '';
		var charCounter = 0;
		var currentChar, withinTag, withinEntity;
		
		for(var i=0; i<text.length; i++) {
			currentChar = text.charAt(i);
			
			// Check if we are within a tag or entity:
			if(currentChar == '<') {
				withinTag = true;
				// Reset the charCounter after newline tags (<br/>):
				if(i>5 && text.substr(i-5,4) == '<br/')
					charCounter = 0;				
			} else if(withinTag && i>0 && text.charAt(i-1) == '>') {
				withinTag = false;
				// Reset the charCounter after newline tags (<br/>):
				if(i>4 && text.substr(i-5,4) == '<br/')
					charCounter = 0;
			} else if(currentChar == '&') {
				withinEntity = true;
			} else if(withinEntity && i>0 && text.charAt(i-1) == ';') {
				withinEntity = false;
				// We only increase the charCounter once for the whole entiy:
				charCounter++;
			}
				
			if(!withinTag && !withinEntity) {
				// Reset the charCounter if we encounter a word boundary:
				if(currentChar == ' ' || currentChar == '\n' || currentChar == '\t') {
					charCounter = 0;
				} else {
					// We are not within a tag or entity, increase the charCounter:
					charCounter++;
				}
				if(charCounter > this.settings['maxWordLength']) {
					// maxWordLength has been reached, break here and reset the charCounter:
					newText += this.getBreakString();
					charCounter = 0;
				}
			}		
			// Add the current char to the text:
			newText += currentChar;
		}
		
		return newText;
	},
	
	getBreakString: function() {
		// Returns the character sequence used to wrap long words
		if(typeof arguments.callee.breakString == 'undefined') {
			arguments.callee.breakString = '&#8203;';
		}
		return arguments.callee.breakString;
	},
	
	replaceBBCode: function(text) {
  		//if(this.userID ==1 )	alert( text)

		if(!this.settings['bbCode']) {
			// If BBCode is disabled, just strip the text from BBCode tags:
			if (!arguments.callee.regExpStripBBCode) {
				arguments.callee.regExpStripBBCode = new RegExp(
					'\\[(?:\\/)?(\\w+)(?:=([^<>]*?))?\\]',
					'gm'
				);
			}		
			return text.replace(
				arguments.callee.regExpStripBBCode,
				''
			);
		}
		// Remove the BBCode tags:
		if (!arguments.callee.regExp) {
			arguments.callee.regExp = new RegExp(
				'\\[(\\w+)(?:=([^<>]*?))?\\](.+?)\\[\\/\\1\\]',
				'gm'
			);
		}		
		return text.replace(
			arguments.callee.regExp,
			this.replaceBBCodeCallback
		);
	},
	
	replaceBBCodeCallback: function(str, p1, p2, p3) {
//		if(this.userID == 1)	alert('replaceBBCodeCallback:' .  str + '/'+ p1 + '/' + p2 + '/' + p3)
		// Only replace predefined BBCode tags:
		if(!ajaxChat.inArray(ajaxChat.bbCodeTags, p1)) {
			return str;
		}	
		// Avoid invalid XHTML (unclosed tags):
		if(ajaxChat.containsUnclosedTags(p3)) {
			return str;
		}		
		
		switch(p1) {
			case 'color':
				return ajaxChat.replaceBBCodeColor(p3, p2);
			case 'url':
				return ajaxChat.replaceBBCodeUrl(p3, p2);
			case 'parent':
				return ajaxChat.replaceBBCodeParentUrl(p3, p2);
			case 'img':
				return ajaxChat.replaceBBCodeImage(p3);
			case 'quote':
				return ajaxChat.replaceBBCodeQuote(p3, p2);
			case 'code':
				return ajaxChat.replaceBBCodeCode(p3);
			case 'u':
				return ajaxChat.replaceBBCodeUnderline(p3);
			default:
				return ajaxChat.replaceCustomBBCode(p1, p2, p3);
		}	
	},

	replaceBBCodeColor: function(content, attribute) {
		if(this.settings['bbCodeColors']) {
			// Only allow predefined color codes:
			if(!attribute || !this.inArray(ajaxChat.colorCodes, attribute))
				return content;								
			return 	'<span style="color:'
					+ attribute + ';">'
					+ this.replaceBBCode(content)
					+ '</span>';
		}
		return content;
	},
	
	replaceBBCodeUrl: function(content, attribute) {
		var url;
		var params;
		
		if(attribute)
			url = attribute.replace(/\s/gm, this.encodeText(' '));
		else
			url = this.stripBBCodeTags(content.replace(/\s/gm, this.encodeText(' ')));
		if (!arguments.callee.regExpUrl) {
			arguments.callee.regExpUrl = new RegExp(
				'^(?:(?:http)|(?:https)|(?:ftp)|(?:irc)):\\/\\/',
				''
			);
		}
		if(!url || !url.match(arguments.callee.regExpUrl))
			return content;
			
	   // проверяем наличие параметров
	   var paramName = '&cparams=';
	   if ((paramPos = url.indexOf(paramName)) > 0) {
	         if(this.userID == 1) {
	            //alert(url.substring(paramPos + paramName.length));
	         }
	         params = url.substring(paramPos + paramName.length).split(',');
	         url = url.substring(0, paramPos);
	   }
		
	   if(this.userRole == 3 && (ipPos = url.indexOf('member.php?action=profile')) >= 0) {
			// возвращаем линк профайла...
			var uid = '';
			var gir = '';
			
         if(params != null && params.length == 2) {
            var uidArr = params[0].split('=');
            var ugroupArr = params[1].split('=');
            if(uidArr && ugroupArr) {
               uid = uidArr[1];
               gid = ugroupArr[1];
               
               var groupStyle = this.getUserGroupStyle(gid);
               if(groupStyle) {
                  content = groupStyle.replace('{username}', content).replace('uid=""', 'uid="'+uid+'"');
               }
            }
         }
			
		   return 	'<a href="'
				   + url
				   + '" onclick="window.open(this.href); return false;" class="userProfile">'
				   + this.replaceBBCode(content)
				   + '</a>';
		}
      else if(this.userRole == 3 && (ipPos = url.indexOf('http://nic.ru/whois/?query=')) >= 0)
		{
		   if(content.indexOf('--bann--') >= 0 && !this.arraySearch(content, this.bannedIPList))
            this.bannedIPList.push(content);	
         
         var ipType = 'ip';
         if(params != null && params.length) {
            ipType = 'bannedIP';
         }

			return '<a href="' + url + '" onclick="window.open(this.href); return false;" msgid="' + 
			   this.msgID + '" type="' + ipType + '" ip="' + content.replace('--bann--', '')  + '">' +
				this.replaceBBCode(content).replace('--bann--', '') + 
				'</a>&nbsp;-&nbsp;' + 
				(content.indexOf('--bann--') < 0 
				   ? '<a href="#" onclick="ajaxChat.setBannedIP(\''+ url.replace('http://nic.ru/whois/?query=', '') + 
				   '\', ' + this.msgID + ')">' + this.lang['label_ban_this_ip'] + '</a>'
				   : this.lang['label_this_ip_is_banned']
				);			
		}
      else{
		   return 	'<a href="'
				   + url
				   + '" onclick="window.open(this.href); return false;">'
				   + this.replaceBBCode(content)
				   + '</a>';
		}
	},
	
	replaceBBCodeParentUrl: function(content, attribute) {
		var messageID;
		
		if(attribute)
			messageID = attribute.replace(/\s/gm, this.encodeText(' '));
		else
			messageID = this.stripBBCodeTags(content.replace(/\s/gm, this.encodeText(' ')));
			
		return 	'<a class="parentlink" href="#"'
				+ ' onclick="ajaxChat.viewMessageWithPopup(' + messageID  + ', \'?view=newLogs\', \'' + content + '\'); return false;">'
				+ this.replaceBBCode(content)
				+ '</a>';
	},
	
	replaceBBCodeImage: function(url) {
		if(this.settings['bbCodeImages']) {
			if (!arguments.callee.regExpUrl) {
				arguments.callee.regExpUrl = new RegExp(
					this.regExpMediaUrl,
					''
				);
			}
			if(!url || !url.match(arguments.callee.regExpUrl))
				return url;
			url = url.replace(/\s/gm, this.encodeText(' '));
			var maxWidth = this.dom['chatList'].offsetWidth-50;
			var maxHeight = this.dom['chatList'].offsetHeight-50;
			return	'<a href="'
					+url
					+'" onclick="window.open(this.href); return false;">'
					+'<img class="bbCodeImage" style="max-width:'
					+maxWidth
					+'px; max-height:'
					+maxHeight
					+'px;" src="'
					+url
					+'" alt=""/></a>';
		}
		return url;
	},

	replaceBBCodeQuote: function(content, attribute) {
		if(attribute)
			return	'<span class="quote"><cite>'
					+ this.lang['cite'].replace(/%s/, attribute)
					+ '</cite><q>'
					+ this.replaceBBCode(content)
					+ '</q></span>';
		return 	'<span class="quote"><q>'
				+ this.replaceBBCode(content)
				+ '</q></span>';
	},

	replaceBBCodeCode: function(content) {
		// Replace vertical tabs and multiple spaces with two non-breaking space characters:
		return 	'<code>'
				+ this.replaceBBCode(content.replace(/\t|(?:  )/gm, '&#160;&#160;'))
				+ '</code>';
	},
	
	replaceBBCodeUnderline: function(content) {
		return 	'<span style="text-decoration:underline;">'
				+ this.replaceBBCode(content)
				+ '</span>';
	},
	
	replaceHyperLinks: function(text) {
		if(!this.settings['hyperLinks']) {
			return text;
		}
		if(!arguments.callee.regExp) {
			arguments.callee.regExp = new RegExp(
				'(^|\\s|>)((?:(?:http)|(?:https)|(?:ftp)|(?:irc)):\\/\\/[^\\s<>]+)(<\\/a>)?',
				'gm'
			);
		}
		return text.replace(
			arguments.callee.regExp,
			// Specifying an anonymous function as second parameter:
			function(str, p1, p2, p3) {
				// Do not replace URL's inside URL's:
				if(p3) {
					return str;
				}
				return 	p1
						+ '<a href="'
						+ p2
						+ '" onclick="window.open(this.href); return false;">'
						+ p2
						+ '</a>';
			}
		);
	},

	replaceLineBreaks: function(text) {
		if (!arguments.callee.regExp) {
			arguments.callee.regExp = new RegExp('\\n',	'g');
		}
		if(!this.settings['lineBreaks']) {
			return text.replace(arguments.callee.regExp, ' ');
		} else {
			return text.replace(arguments.callee.regExp, '<br/>');
		}
	},

	replaceEmoticons: function(text) {
		if(!this.settings['emoticons']) {
			return text;
		}
		if(!arguments.callee.regExp) {
			var regExpStr = '^(.*)(';
			for(var i=0; i<this.emoticonCodes.length; i++) {
				if(i!=0)
					regExpStr += '|';
				regExpStr += '(?:' + this.escapeRegExp(this.emoticonCodes[i]) + ')';
			}
			regExpStr += ')(.*)$';
			arguments.callee.regExp = new RegExp(regExpStr, 'gm');
		}
		return text.replace(
			arguments.callee.regExp,			
			this.replaceEmoticonsCallback
		);
	},
	
	replaceEmoticonsCallback: function(str, p1, p2, p3) {
		if (!arguments.callee.regExp) {
			arguments.callee.regExp = new RegExp('(="[^"]*$)|(&[^;]*$)', '');
		}
		// Avoid replacing emoticons in tag attributes or XHTML entities:
		if(p1.match(arguments.callee.regExp)) {
			return str;
		}	
		if(p2) {
			var index = ajaxChat.arraySearch(p2, ajaxChat.emoticonCodes);							
			return 	ajaxChat.replaceEmoticons(p1)
				+	'<img src="'
				+	ajaxChat.dirs['emoticons']
				+	ajaxChat.emoticonFiles[index]
				+	'" alt="'
				+	p2
				+	'" />'
				+ 	ajaxChat.replaceEmoticons(p3);
		}
		return str;
	},
	
	getActiveStyle: function() {
		var cookie = this.readCookie(this.sessionName + '_style');
		var style = cookie ? cookie : this.getPreferredStyleSheet();
		return style;		
	},

	initStyle: function() {
		this.styleInitiated = true;
		this.setActiveStyleSheet(this.getActiveStyle());
	},
	
	persistStyle: function() {
		if(this.styleInitiated) {
			this.createCookie(this.sessionName + '_style', this.getActiveStyleSheet(), this.cookieExpiration);
		}
	},
	
	setSelectedStyle: function() {
		if(this.dom['styleSelection']) {
			var style = this.getActiveStyle();
			var styleOptions = this.dom['styleSelection'].getElementsByTagName('option');
			for(var i=0; i<styleOptions.length; i++) {
				if(styleOptions[i].value == style) {
					styleOptions[i].selected = true;
					break;
				}
			}
		}
	},
	
	getSelectedStyle: function() {
		var styleOptions = this.dom['styleSelection'].getElementsByTagName('option');
		if(this.dom['styleSelection'].selectedIndex == -1) {
			return styleOptions[0].value;
		} else {
			return styleOptions[this.dom['styleSelection'].selectedIndex].value;
		}
	},
	
	setActiveStyleSheet: function(title) {
		var i, a, main;
		var titleFound = false;
		for(i=0; (a = document.getElementsByTagName('link')[i]); i++) {
			if(a.getAttribute('rel').indexOf('style') != -1 && a.getAttribute('title')) {
				a.disabled = true;
				if(a.getAttribute('title') == title) {
	                a.disabled = false;
	                titleFound = true;
				}
			}
		}
		if(!titleFound && title != null) {
		   this.setActiveStyleSheet(this.getPreferredStyleSheet());
		}
	},
	
	getActiveStyleSheet: function() {
		var i, a;
		for(i=0; (a = document.getElementsByTagName('link')[i]); i++) {
			if(a.getAttribute('rel').indexOf('style') != -1 && a.getAttribute('title') && !a.disabled) {
				return a.getAttribute('title');
			}
		}
		return null;
	},
	
	getPreferredStyleSheet: function() {
		var i,a;
		for(i=0; (a = document.getElementsByTagName('link')[i]); i++) {
			if(a.getAttribute('rel').indexOf('style') != -1
				&& a.getAttribute('rel').indexOf('alt') == -1
				&& a.getAttribute('title')
				) {
				return a.getAttribute('title');
			}
		}
		return null;
	},

	switchLanguage: function(langCode) {
		window.location.search = '?lang='+langCode;
	},
	
	createCookie: function(name,value,days) {
		var expires = '';
		if(days) {
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			expires = '; expires='+date.toGMTString();
		}
		var path = '; path='+this.cookiePath;
		var domain = this.cookieDomain ? '; domain='+this.cookieDomain : '';
		var secure = this.cookieSecure ? '; secure' : '';
		document.cookie = name+'='+encodeURIComponent(value)+expires+path+domain+secure;
	},
	
	readCookie: function(name) {
		if(!document.cookie)
		   return null;
		var nameEQ = name + '=';
		var ca = document.cookie.split(';');
		for(var i=0; i<ca.length; i++) {
			var c = ca[i];
			while(c.charAt(0) == ' ') {
				c = c.substring(1, c.length);
			}
			if(c.indexOf(nameEQ) == 0) {
				return decodeURIComponent(c.substring(nameEQ.length, c.length));
			}
		}
		return null;
	},

	isCookieEnabled: function() {
		this.createCookie(this.sessionName + '_cookie_test', true, 1);
		var cookie = this.readCookie(this.sessionName + '_cookie_test');
		if(cookie) {
			// Unset the test cookie:
			this.createCookie(this.sessionName + '_cookie_test', true, -1);
			// Cookie test successfull, return true:
			return true;
		}
		return false;
	},
	
	finalize: function() {	
		if(typeof this.finalizeFunction == 'function') {
			this.finalizeFunction();
		}
		// Ensure the socket connection is closed on unload:
		if(this.socket) {
			try {
				this.socket.close();
				this.socket = null;
			} catch(e) {
				//alert(e);
			}
		}	
		this.persistSettings();
		this.persistStyle();		
		this.customFinalize();
	},

	// Override to perform custom actions on flash initialization:
	initializeCustomFlashInterface: function() {	
	},
	
	// Override to handle custom info messages
	handleCustomInfoMessage: function(infoType, infoData) {
	},

	// Override to add custom initialization code
	// This method is called on page load
	customInitialize: function() {		
	},

	// Override to add custom finalization code
	// This method is called on page unload
	customFinalize: function() {	
	},

	// Override to add custom user menu items:
	// Return a string with list items ( <li>menuItem</li> )
	// encodedUserName contains the userName ready to be used for javascript links
	// userID is only available for the online users menu - not for the inline user menu
	// use (encodedUserName == this.encodedUserName) to check for the current user
	getCustomUserMenuItems: function(encodedUserName, userID) {
		return '';
	},

	// Override to parse custom input messages:
	// Return replaced text
	// text contains the whole message
	parseCustomInputMessage: function(text) {
		return text;
	},
	
	// Override to parse custom input commands:
	// Return parsed text
	// text contains the whole message, textParts the message split up as words array
	parseCustomInputCommand: function(text, textParts) {
		return text;
	},
	
	// Override to replace custom text:
	// Return replaced text
	// text contains the whole message
	replaceCustomText: function(text) {
		return text;
	},
	
	replaceAddressedMessage: function(textParts, userName){
	    var privMsgText = textParts.slice(2).join(' ');
		privMsgText = this.replaceBBCode(privMsgText);
		privMsgText = this.replaceHyperLinks(privMsgText);
		privMsgText = this.replaceEmoticons(privMsgText);

	    if(this.userName == userName){
		    return	'<span class="privmsgto">'
				    + privMsgText
				    + '</span> ';
	    }
	    else{
		    return privMsgText;
	    }
	},
	
	// Return replced text for custom command
	getAddressedMessage: function(userName){
	   //if(this.userName == userName){
      userName = this.replaceUserNameWithLink(userName);
					
	    return	'<span class="privmsg">'
			    + this.lang['addressedmsgto'].replace(/%s/, userName)
			    + '</span>';
				    
	    /*}
	    else{
		    return	'<span class="privmsg">'
				    + this.lang['addressedmsgto'].replace(/%s/, userName)
				    + '</span> ';
	    }*/
	},
	
	// Override to replace custom commands:
	// Return replaced text for custom commands
	// text contains the whole message, textParts the message split up as words array
	replaceCustomCommands: function(text, textParts) {
		return text;
	},

	// Override to replace custom BBCodes:
	// Return replaced text and call replaceBBCode recursively for the content text
	// tag contains the BBCode tag, attribute the BBCode attribute and content the content text
	// This method is only called for BBCode tags which are in the bbCodeTags list
	replaceCustomBBCode: function(tag, attribute, content) {
		return '<' + tag + '>' + this.replaceBBCode(content) + '</' + tag + '>';
	},
	
	// Override to perform custom actions on new messages:
	// Return true if message is to be added to the chatList, else false
	customOnNewMessage: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) {
		return true;
	},
	
	// return current time for all online users
	// added by nikolp
	// date: 19.01.2009
   getUsersTime: function(){
      var dt = new Date();
      var def = dt.getTimezoneOffset()/60;
      var gmt = (dt.getHours() + def);
      var endingWithSecons = ":" + this.IfZero(dt.getMinutes()) + ":" +  this.IfZero(dt.getSeconds());
      var ending = (this.viewTimeSemicol ? ":" : ":") + this.IfZero(dt.getMinutes());
      
      if(this.usersList.length != undefined && this.usersList.length > 0){
         // set moscow time
         var serverClockObject = document.getElementById('globalServerClock');
         var serverTime =  this.check24(gmt+3);
         
         if(serverClockObject != undefined){
            serverClockObject.innerHTML = this.IfZero(serverTime ) + endingWithSecons;
         }
          
         // set users locale time
         for(var i=0; i < this.usersList.length; i ++){
         
            var _userID = this.usersList[i];
            var _userGmtTimeNumber = this.getUserGmtTimeFromUserID(_userID);
            var _nowHours = _userGmtTimeNumber + gmt;
         
            if(_userID > 0){
         
               var userClockObject = document.getElementById('userClock' + _userID);
               var gmtTime = this.check24(_nowHours);// > 24 ? (_nowHours - 24) : _nowHours);
               
               if(userClockObject != undefined){
                  userClockObject.innerHTML = (this.IfZero(gmtTime ) + ending);
               }
               else{
                  //alert(this.IfZero(gmtTime ) + ending);
               }
            }
         }
      }
      
      this.viewTimeSemicol = !this.viewTimeSemicol;
      setTimeout("ajaxChat.getUsersTime()", 1000);   
   },

	// return 0X number
	// added by nikolp
	// date: 19.01.2009
   IfZero: function(num) {
      return ((num <= 9) ? ("0" + num) : num);
   },
   
	// return hours number
	// added by nikolp
	// date: 19.01.2009
   check24: function(hour) {
      return (hour >= 24) ? hour - 24 : (hour < 0 ? (24+hour) : hour);
   },
   
   // function for javasript test
   getTest: function()
   {
      if (this.userGroupStyleList.length == undefined || !this.userGroupStyleList.length) {
         this.userGroupStyleList = new Array();
         this.userGroupStyleList[2] = 'Style 2';
         this.userGroupStyleList[22] = 'Style 22';
         this.userGroupStyleList[56] = 'Style 56';
         this.userGroupStyleList[89] = 'Style 89';
      }
      
      for (x in this.userGroupStyleList)
         alert(this.userGroupStyleList[x]);
//      if(this.userID==1)      alert('function test: ' + strNow)
   },
   
   trim: function(string)
   {
      return string.replace(/(^\s+)|(\s+$)/g, "");
   },
      
   viewConfirmBannedIP: function(ip, messageID){
      this.setBannedIP(ip, messageID);
   },
   
   /*
    * Метод устанавливает иконку соединения... 
    *
   */
   setConnectionIconStatus: function(isConnected){
      var connectionIconObject = null;
      if($('#imgConnectionStatus'))
      {
         connectionIconObject = $('#imgConnectionStatus');
      }
      
      if(connectionIconObject != null)
      {
         if(!isConnected){
            //$('#connectionStatusLabel').addClass('off');
            $('#reconnect').removeClass('off');
            connectionIconObject.addClass('off');
         }
         else{
            //$('#connectionStatusLabel').removeClass('off');
            if(connectionIconObject.hasClass('off')){
               connectionIconObject.removeClass('off');
               // отсылаем сообщение в чат
 				   this.addChatBotMessageToChatList('/message ConnectionStatus');
				   this.updateChatlistView();				
				   
				   // run effect for ConnectionStatusLabel
				   //runEffectForConnectionStatusLabel();
				   $('#connectionStatusLabel').removeClass('off');
			      
			      //callback function to bring a hidden box back
               setTimeout(function(){
		   	      $("#connectionStatusLabel").addClass('off');
			         }, 2000);
           }
           
           $('#reconnect').addClass('off');
            
         }
      }
   },
   
   createConnection: function(){
		clearTimeout(ajaxChat.timer);
		ajaxChat.updateChat(null);
  },
  
  /*
   * метод добавляет пользователя в группу - читатель
   *
  */
  addReaderRoleToUser: function(userObject){
      var userID = userObject.attr('uid');

      if(userID > 0 && confirm(this.lang['confirmAddReaderRole']))
      {            
         var userID = userObject.attr('uid');
         var messageID = userObject.attr('msgid');
         var userName = userObject.html();
         var urlTxt = '?ajax=true&view=userAction&action=addReaderRole&userID=' + userID + '&msgID=' + messageID;
         var isNotCompleted = true;
         var options = '';
         var log = $('#ajaxChat_m_' + messageID).addClass('ajax-loading');

         // делаем запрос на блокирование ИП адреса
         $.ajax({
            type: "GET",
            url: urlTxt,
            success: function(html){
               log.removeClass('ajax-loading');
               log.html('');
               log.html(html);
               isNotCompleted = false;
            }
         });

         return isNotCompleted;
      }
  },  

  /*
   * метод меняет группу - читатель на группу дух
   *
  */
  deleteReaderRoleFromUser: function(userObject){
      var userID = userObject.attr('uid');
      
      if(userID && confirm(this.lang['confirmDeleteReaderRole']))
      {            
         var userID = userObject.attr('uid');
         var messageID = userObject.attr('msgid');
         var userName = userObject.html();
         var urlTxt = '?ajax=true&view=userAction&action=deleteReaderRole&userID=' + userID + '&msgID=' + messageID;
         var isNotCompleted = true;
         var options = '';
         var log = $('#ajaxChat_m_' + messageID).addClass('ajax-loading');

         // делаем запрос на блокирование ИП адреса
         $.ajax({
            type: "GET",
            url: urlTxt,
            success: function(html){
               log.removeClass('ajax-loading');
               log.html('');
               log.html(html);
               isNotCompleted = false;
            }
         });

         return isNotCompleted;
      }
  },  

  /*
   * метод добавляет бан на пользователя
   *
  */
  addBanToUser: function(userObject, reason, bantime, mdForm){
      var userName = userObject.html();
      //if(confirm(this.lang['confirmSetBannedUser'].replace(/%s/, userName)))
      //{            
      //$("#md_confirm_banning").dialog('open');
      var userID = userObject.attr('uid');
      var messageID = userObject.attr('msgid');
      var userName = userObject.html();
      var urlTxt = '?ajax=true&view=userAction&action=banning_by_username&userID=' + userID + 
         '&msgID=' + messageID + '&reason=' + reason + '&bantime=' + bantime;
      var isNotCompleted = true;
      var options = '';
      var log = $('#ajaxChat_m_' + messageID).addClass('ajax-loading');
      
      //alert(urlTxt);
      // делаем запрос на блокирование ИП адреса
      $.ajax({
         type: "GET",
         url: urlTxt,
         success: function(html){
            log.removeClass('ajax-loading');
            log.html('');
            log.html(html);
            isNotCompleted = false;
         }
      });
      
      mdForm.removeClass('ajax-loading');
      mdForm.dialog('close');
      

      return isNotCompleted;
      //}
  },
  
  /*
   * метод уберает бан с пользователя
   *
  */
  deleteBanFromUser: function(userObject){
      var userName = userObject.html();
      if(confirm(this.lang['confirmUnSetBannedUser'].replace(/%s/, userName)))
      {            
         var userID = userObject.attr('uid');
         var messageID = userObject.attr('msgid');
         var userName = userObject.html();
         var urlTxt = '?ajax=true&view=userAction&action=unbanning_by_username&userID=' + userID + '&msgID=' + messageID;
         var isNotCompleted = true;
         var options = '';
         var log = $('#ajaxChat_m_' + messageID).addClass('ajax-loading');

         // делаем запрос на блокирование ИП адреса
         $.ajax({
            type: "GET",
            url: urlTxt,
            success: function(html){
               log.removeClass('ajax-loading');
               log.html('');
               log.html(html);
               isNotCompleted = false;
            }
         });

         return isNotCompleted;
      }
  },
  
  /*
   * метод открывает страницу для изменения групп
   *
  */
  changeGroup: function(userObject){
      var userID = userObject.attr('uid');
      
      if(userID) {
         window.open(
            '../admin/index.php?module=user/users&action=edit&uid=' + userID + '#tab_profile',
            null,
            'resizable=1,fullscreen=yes,menubar=yes,scrollbars=yes,titlebar=yes,status=yes,toolbar=yes');
      }
  },
   /*
    * метод выводит модальное окно на подтверждение блокировки ИП адреса
    *
    * @author: nikolp
   */
   viewConfirmBannedIP: function(ip, messageID){
      ajaxChat.banIP = ip;
      ajaxChat.banMessageID = messageID;
      //$('#ajaxChat_m_' + ajaxChat.banMessageID).addClass('ajax-loading');
      
      var openMDConfirm = $("#md_confirm_ip_banning");
      openMDConfirm.html(this.lang['confirmSetBannedIP'].replace(/%s/, ajaxChat.banIP));
      if(typeof openMDConfirm.dialog == 'function')
         openMDConfirm.dialog('open');
   },
   /*
    * метод блокирует выбранный ИП адресс
    *
    * @author: nikolp
   */
   setBannedIP: function(ip, messageID){
      var urlTxt = '?ajax=true&view=banning&action=add&msgID=' + messageID + '&ip=' + ip;
      var isNotCompleted = true;
      var options = '';
      var log = $('#ajaxChat_m_' + messageID);

      // делаем запрос на блокирование ИП адреса
      $.ajax({
         type: "GET",
         url: urlTxt,
         success: function(html){
            //log.removeClass('ajax-loading');
            log.html('');
            log.html(html);
            isNotCompleted = false;
            
            // обновляем контекстное меню для всех объектов
            ajaxChat.setContextMenuForIP(null, null, log);
         }
      });

      return isNotCompleted;
   },
   /*
    * метод снимает блокировку с ИП адреса
    *
    * @author: nikolp
   */
   setUnBannedIP: function(ip, messageID){
      if(confirm(this.lang['confirmSetUnBannedIP'].replace(/%s/, ip)))
      {            
         var urlTxt = '?ajax=true&view=unbanning&action=add&msgID=' + messageID + '&ip=' + ip;
         var isNotCompleted = true;
         var options = '';
         var log = $('#ajaxChat_m_' + messageID).addClass('ajax-loading');

         // делаем запрос на блокирование ИП адреса
         $.ajax({
            type: "GET",
            url: urlTxt,
            success: function(html){
               log.removeClass('ajax-loading');
               log.html('');
               log.html(html);
               isNotCompleted = false;
               
               // обновляем контекстное меню для всех объектов
               ajaxChat.setContextMenuForIP(null, null, log);
            }
         });

         return isNotCompleted;
      }
      else{
         return false;
      }
   },
   
   /*
    * метод обнавляет контекстные меню для всех объектов
    *
    * @author: nikolp
   */
   setAllContextMenu: function(inputID, attributes){
      if (this.userID == 1) {
		   //alert('setAllContextMenu()');
			//alert(this.userRole);
		} 
	   // только для избранных
      if (this.userRole == 3 || this.userRole == null) {
         // обновляем контекстное меню для всех пользователей
         $('.userContext').contextMenu(
               contextMenuForUserProfile, 
               {
                  theme: 'vista'
               }
            );     
         // обновляем контекстное меню для пользователей, которые состоят в группе босс
         $('.bossContext').contextMenu(
               contextMenuViewProfile, 
               {
                  theme: 'vista'
               }
            );     
         // обновляем контекстное меню для пользователей, которые ожидают активацию
         $('.awaitingContext').contextMenu(
               contextMenuForAwaitingProfile, 
               {
                  theme: 'vista'
               }
            );     
         // обновляем контекстное меню для свободных IP адресов
         $('.IPMenu').contextMenu(
            contextMenuForIP, 
            {
               theme: 'vista'
            }
         );     
         // обновляем контекстное меню для заблокированных IP адресов
         $('.bannedIPMenu').contextMenu(
               contextMenuForBannedIP, 
               {
                  theme: 'vista'
               }
            );     
         // обновляем контекстное меню для пользователей, которые состоят в группе читатель
         $('.readerContext').contextMenu(
               contextMenuForReaderUserProfile, 
               {
                  theme: 'vista'
               }
            );     
         // обновляем контекстное меню для заблокированный пользователей
         $('.bannedContext').contextMenu(
               contextMenuForBannedUserProfile, 
               {
                  theme: 'vista'
               }
            ); 
         
         // TODO устанавливаем контекстное меню
         $("[type='boss']").contextMenu(
            contextMenuViewProfile, 
            {
               theme: 'vista'
            }
         );  
         // TODO
        $("[type='admin']").contextMenu(
            contextMenuForUserProfile, 
            {
               theme: 'vista'
            }
         ); 
         // TODO для своего
        $("[type='svoj']").contextMenu(
            contextMenuForUserProfile, 
            {
               theme: 'vista'
            }
         ); 
         // TODO для духов
        $("[type='dyx']").contextMenu(
            contextMenuForUserProfile, 
            {
               theme: 'vista'
            }
         ); 
         // TODO для читателей
        $("[type='reader']").contextMenu(
            contextMenuForReaderUserProfile, 
            {
               theme: 'vista'
            }
         ); 
         // TODO для заблокированных
        $("[type='banned']").contextMenu(
            contextMenuForBannedUserProfile, 
            {
               theme: 'vista'
            }
         ); 
      }
      else if(this.userRole == 2) {
         // TODO устанавливаем контекстное меню
         $("[type='boss']").contextMenu(
            contextMenuViewProfile, 
            {
               theme: 'vista'
            }
         );  
         // TODO
        $("[type='admin']").contextMenu(
            contextMenuViewProfile, 
            {
               theme: 'vista'
            }
         ); 
         // TODO для своего
        $("[type='svoj']").contextMenu(
            contextMenuViewProfile, 
            {
               theme: 'vista'
            }
         ); 
         // TODO для духов
        $("[type='dyx']").contextMenu(
            contextMenuViewProfile, 
            {
               theme: 'vista'
            }
         ); 
      
      }
   },  
   /*
    * метод обнавляет контекстные меню для всех объектов
    *
    * @author: nikolp
   */
   setAllContextMenuII: function(inputID, attributes){
      if (this.userID == 1) {
		   //alert('setAllContextMenu(): ' + inputID);
			//alert(this.userRole);
		} 
	   // только для избранных
      if (this.userRole == 3) {
         // обновляем контекстное меню для свободных IP адресов
         /*$('.IPMenu').contextMenu(
            contextMenuForIP, 
            {
               theme: 'vista'
            }
         );     
         // обновляем контекстное меню для заблокированных IP адресов
         $('.bannedIPMenu').contextMenu(
               contextMenuForBannedIP, 
               {
                  theme: 'vista'
               }
            );     
         */
         // TODO устанавливаем контекстное меню
         $((inputID ? "#" + inputID + " " : "") + "[type='boss']").contextMenu(
            contextMenuViewProfile, 
            {
               theme: 'vista'
            }
         );  
         // TODO
        $((inputID ? "#" + inputID + " " : "") + "[type='admin']").contextMenu(
            contextMenuForUserProfile, 
            {
               theme: 'vista'
            }
         ); 
         // TODO для своего
        $((inputID ? "#" + inputID + " " : "") + "[type='svoj']").contextMenu(
            contextMenuForUserProfile, 
            {
               theme: 'vista'
            }
         ); 
         // TODO для духов
        $((inputID ? "#" + inputID + " " : "") + "[type='dyx']").contextMenu(
            contextMenuForUserProfile, 
            {
               theme: 'vista'
            }
         ); 
         // TODO для читателей
        $((inputID ? "#" + inputID + " " : "") + "[type='reader']").contextMenu(
            contextMenuForReaderUserProfile, 
            {
               theme: 'vista'
            }
         ); 
         // TODO для заблокированных
        $((inputID ? "#" + inputID + " " : "") + "[type='banned']").contextMenu(
            contextMenuForBannedUserProfile, 
            {
               theme: 'vista'
            }
         ); 
      }
      else if(this.userRole == 2) {
         // TODO устанавливаем контекстное меню
         $((inputID ? "#" + inputID + " " : "") + "[type='boss']").contextMenu(
            contextMenuViewProfile, 
            {
               theme: 'vista'
            }
         );  
         // TODO
        $((inputID ? "#" + inputID + " " : "") + "[type='admin']").contextMenu(
            contextMenuViewProfile, 
            {
               theme: 'vista'
            }
         ); 
         // TODO для своего
        $((inputID ? "#" + inputID + " " : "") + "[type='svoj']").contextMenu(
            contextMenuViewProfile, 
            {
               theme: 'vista'
            }
         ); 
         // TODO для духов
        $((inputID ? "#" + inputID + " " : "") + "[type='dyx']").contextMenu(
            contextMenuViewProfile, 
            {
               theme: 'vista'
            }
         ); 
      }
   },  
   /*
    * метод обнавляет контекстные меню для IP объектов
    *
    * @author: nikolp
   */
   setContextMenuForIP: function(inputID, attributes, object){
      // TODO for test: nikolp
      //if(this.userID != 1)
      //   return null;
         
	   // только для избранных
      if (this.userRole == 3) {

         if(this.userID == 1) {
            //alert('setContextMenuForIP: ' + ' / ' + inputID + ' / ' + attributes);
         }
         //alert(object);
         inputID = (inputID ? "#" + inputID + " " : "");
         attributes = (inputID ? " " + attributes : "");
         // обновляем контекстное меню для свободных IP адресов
         var objectIP = object 
            ? (object.attr('type') == 'ip' ? object : $(inputID + "[type='ip']" + attributes)) 
            : $(inputID + "[type='ip']" + attributes);

         objectIP.contextMenu('contextMenuForIP', {
              bindings: {
                'viewIP': function(t) {
                  var newwindow = window.open($(t).attr('href'));
                  newwindow.focus();
                },
                'bannIP': function(t) {
                  ajaxChat.viewConfirmBannedIP($(t).html(), $(t).attr('msgid'));               
                }
              },
              onShowMenu: function(e, menu) {
                  $('#unbannIP', menu).remove()
                  return menu;
              }
            });			
         // обновляем контекстное меню для заблокированных IP адресов
         var objectBannedIP = object 
            ? (object.attr('type') == 'bannedIP' ? object : $(inputID + "[type='bannedIP']" + attributes)) 
            : $(inputID + "[type='bannedIP']" + attributes);

         objectBannedIP.contextMenu('contextMenuForIP', {
              bindings: {
                'viewIP': function(t) {
                  var newwindow = window.open($(t).attr('href'));
                  newwindow.focus();
                },
                'unbannIP': function(t) {
                   ajaxChat.setUnBannedIP($(t).html(), $(t).attr('msgid'));
                }
              },
              onShowMenu: function(e, menu) {
                  $('#bannIP', menu).remove()
                  return menu;
              }
            });			
      }
   },  
   /*
    * метод обнавляет контекстные меню для всех объектов
    *
    * @author: nikolp
   */
   setAllContextMenuIII: function(inputID, attributes){
      // только для избранных
      if (this.userRole == 3) {
         if(this.userID == 1) {
            //alert('setContextMenuForIP: ' + ' / ' + inputID + ' / ' + attributes);
         }
         inputID = (inputID ? "#" + inputID + " " : "");
         // меню для админ пользователей
         $(inputID + "[type='admin']").contextMenu('contextMenuForUserProfile', {
              bindings: {
                'viewProfile': function(t) {
                  ajaxChat.viewProfile($(t));
                },
                'changeGroup': function(t) {
                  ajaxChat.changeGroup($(t));
                },
                'addToReader': function(t) {
                  ajaxChat.addReaderRoleToUser($(t));
                },
                'addToBanned': function(t) {
                  // получаем объект пользователя
                  ajaxChat.userObject = $(t);
                  // вызываем форму подтверждения
                  $("#md_confirm_banning").dialog('open');
                }
              },
              onShowMenu: function(e, menu) {
                  $(' #deleteReaderRoleFromUser, #deleteBanFromUser, #activateUser', menu).remove()
                  return menu;
              }
            });			

         // меню своих пользователей
         $(inputID + "[type='svoj']").contextMenu('contextMenuForUserProfile', {
              bindings: {
                'viewProfile': function(t) {
                  ajaxChat.viewProfile($(t));
                },
                'changeGroup': function(t) {
                  ajaxChat.changeGroup($(t));
                },
                'addToReader': function(t) {
                  ajaxChat.addReaderRoleToUser($(t));
                },
                'addToBanned': function(t) {
                  // получаем объект пользователя
                  ajaxChat.userObject = $(t);
                  // вызываем форму подтверждения
                  $("#md_confirm_banning").dialog('open');
                }
              },
              onShowMenu: function(e, menu) {
                  $(' #deleteReaderRoleFromUser, #deleteBanFromUser, #activateUser', menu).remove()
                  return menu;
              }
            });			
         // меню dyx пользователей
         $(inputID + "[type='dyx']").contextMenu('contextMenuForUserProfile', {
              bindings: {
                'viewProfile': function(t) {
                  ajaxChat.viewProfile($(t));
                },
                'changeGroup': function(t) {
                  ajaxChat.changeGroup($(t));
                },
                'addToReader': function(t) {
                  ajaxChat.addReaderRoleToUser($(t));
                },
                'addToBanned': function(t) {
                  // получаем объект пользователя
                  ajaxChat.userObject = $(t);
                  // вызываем форму подтверждения
                  $("#md_confirm_banning").dialog('open');
                }
              },
              onShowMenu: function(e, menu) {
                  $(' #deleteReaderRoleFromUser, #deleteBanFromUser, #activateUser', menu).remove()
                  return menu;
              }
            });			

         // меню для boss пользователей
         $(inputID + "[type='boss']").contextMenu('contextMenuForUserProfile', {
              bindings: {
                'viewProfile': function(t) {
                  ajaxChat.viewProfile($(t));
                }
              },
              onShowMenu: function(e, menu) {
                  $('#changeGroup, #addToReader, #deleteReaderRoleFromUser, #addToBanned, #deleteBanFromUser, #activateUser', menu).remove()
                  return menu;
              }
            });			

         // меню для reader пользователей
         $(inputID + "[type='reader']").contextMenu('contextMenuForUserProfile', {
              bindings: {
                'viewProfile': function(t) {
                  ajaxChat.viewProfile($(t));
                },
                'deleteReaderRoleFromUser': function(t) {
                  ajaxChat.deleteReaderRoleFromUser($(t));
                },
                'addToBanned': function(t) {
                  // получаем объект пользователя
                  ajaxChat.userObject = $(t);
                  // вызываем форму подтверждения
                  $("#md_confirm_banning").dialog('open');
                }
              },
              onShowMenu: function(e, menu) {
                  $('#changeGroup, #addToReader, #deleteBanFromUser, #activateUser', menu).remove()
                  return menu;
              }
            });			

         // меню для banned пользователей
         $(inputID + "[type='banned']").contextMenu('contextMenuForUserProfile', {
              bindings: {
                'viewProfile': function(t) {
                  ajaxChat.viewProfile($(t));
                },
                'deleteBanFromUser': function(t) {
                  // получаем объект пользователя
                  ajaxChat.deleteBanFromUser($(t));
                }
              },
              onShowMenu: function(e, menu) {
                  $('#changeGroup, #addToReader, #deleteReaderRoleFromUser, #addToBanned, #activateUser', menu).remove()
                  return menu;
              }
            });			
         // меню для awaiting пользователей
         $(inputID + "[type='awaiting']").contextMenu('contextMenuForUserProfile', {
              bindings: {
                'viewProfile': function(t) {
                  ajaxChat.viewProfile($(t));
                },
                'activateUser': function(t) {
                  ajaxChat.activateUser($(t));
                }
              },
              onShowMenu: function(e, menu) {
                  $('#changeGroup, #addToReader, #deleteReaderRoleFromUser, #addToBanned,  #deleteBanFromUser', menu).remove()
                  return menu;
              }
            });			
		} 
   },  
  /*
    * метод открывает профайл пользователя в новом окне
    *
    * @author: nikolp
   */
   viewProfile: function(userObject) {
      if((uid = userObject.attr('uid')) != undefined && parseInt(uid) > 0) {
         var newwindow = window.open('../member.php?action=profile&uid=' + uid);
         newwindow.focus();
      }
      else if(
         confirm(
            'Идентификационный номер пользователя ('
            + userObject.html() 
            + ') не установлен! Хотите продолжить поиск по имени пользователя?')
         ) {
         var newwindow = window.open('../memberlist.php?username=' + userObject.html());
         newwindow.focus();
      }
   },
  /*
    * метод посылает запрос на активацию пользователя
    *
    * @author: nikolp
   */
   activateUser: function(userObject){
      var userName = userObject.html();
      if(confirm(this.lang['confirmActivateUser'].replace(/%s/, userName))){
         var userID = userObject.attr('uid');
         var messageID = userObject.attr('msgid');
         var urlTxt = '?ajax=true&view=userAction&action=activate_user&userID=' + userID + '&msgID=' + messageID;
         var isNotCompleted = true;
         var options = '';
         var log = $('#ajaxChat_m_' + messageID).addClass('ajax-loading');

         // делаем запрос на блокирование ИП адреса
         $.ajax({
            type: "GET",
            url: urlTxt,
            success: function(html){
               log.removeClass('ajax-loading');
               log.html('');
               log.html(html);
               isNotCompleted = false;
            }
         });

         return isNotCompleted;
      }
   },
  /*
    * метод посылает запрос на сообщение и показывает детали сообщения
    *
    * @author: nikolp
   */
   viewMessageDetails: function(messageID){
      // делаем запрос на контекст сообщения
      var urlTxt = '?ajax=true&view=userAction&action=get_message&msgID=' + messageID;

      messageFrmDialog.html('').addClass('ajax-loading').dialog('open');
      
      $.ajax({
         type: "GET",
         url: urlTxt,
         success: function(html){
            messageFrmDialog.removeClass('ajax-loading');
            messageFrmDialog.html(html);
         }
      });
   },


   /*
    *   Метод получает точно время сервера
    *
    *   @author: dXd
    */
   getServerTime: function(){
       var objSrvTime     = document.getElementById('globalServerClock');
       if(objSrvTime == undefined) return;

       var request = false;

       if (window.XMLHttpRequest){
           request = new window.XMLHttpRequest();
       } else if (window.ActiveXObject){
           try{
               request = new ActiveXObject('Msxml2.XMLHTTP');
           }catch(e){}
           try{
               request = new ActiveXObject('Microsoft.XMLHTTP');
           }catch(e){}
       }

       if(request == false){
           objSrvTime.innerHTML = 'NaN';
           return;
       }

       request.open('POST', 'getServerTime.php', true);
       request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
       request.send('clock=1&ajax=1');
       request.onreadystatechange = function(){
           if(request.readyState == 4 && request.status == 200){
               var tmp = request.responseText;
               var tm  = tmp.split(':', 2);
               serverTimestamp = tm[0]-0;
               serverTimezone  = tm[1]-0;
               var dts  = new Date(serverTimestamp*1000);
               var st  = ajaxChat.check24(dts.getUTCHours()+(serverTimezone/3600));
               var str = ajaxChat.zeroFeel(st)+":"+ajaxChat.zeroFeel(dts.getMinutes())+" (GMT+"+(serverTimezone/3600)+")";
               objSrvTime.innerHTML = str;
           }
       }

       setTimeout('ajaxChat.getServerTime()', 5000); // Получаем время сервера.
   },

   /*
    *   Метод устанавливает отображение времени пользователя на основе времеени
    *   сервера и timezone из профиля.
    *
    *   @author: dXd
    */
   setUserTime: function(){

       if(this.usersList.length != undefined && this.usersList.length > 0){
           for(var i=0; i < this.usersList.length; i ++){
               var _userID = this.usersList[i];
               if(_userID > 0){
                   var _userGmtTimeNumber = this.getUserGmtTimeFromUserID(_userID);
                   if(_userGmtTimeNumber == undefined) _userGmtTimeNumber = 0;

                   var userClockObject = document.getElementById('userClock' + _userID);
                   if(userClockObject != undefined){
                       var dt = new Date(serverTimestamp*1000);
                       var uh = ajaxChat.check24((dt.getUTCHours()+_userGmtTimeNumber));
                       var str = ajaxChat.zeroFeel(uh)+":"+ajaxChat.zeroFeel(dt.getMinutes());
                       userClockObject.innerHTML = str;
                   } else {
                       
                   }
               }
           }
       }

       setTimeout("ajaxChat.setUserTime()", 1000);
   },

   zeroFeel: function(num){
       return ((num <= 9) ? ("0" + num) : num);
   }
}

