peka2tv chat

sc2tv.ru chat with extra features

// ==UserScript==
// @name        peka2tv chat
// @namespace   http://chat.sc2tv.ru
// @description sc2tv.ru chat with extra features
// @author      Winns
// @copyright   27.04.2013, Winns
// @include     http://chat.sc2tv.ru/*
// @include     http://sc2tv.ru/*
// @match 		http://chat.sc2tv.ru/*
// @match 		http://sc2tv.ru/*
// @version     2.0.31
// @grant       GM_addStyle
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_getResourceText
// @resource    peka2tv_chat_css https://raw.githubusercontent.com/Winns/p2tv/master/peka2tv_chat2/peka2tv_chat.css
// @run-at		document-end
// ==/UserScript==
(function () {

GM_addStyle (GM_getResourceText ('peka2tv_chat_css'));

function GM_wait() { 
	if (typeof unsafeWindow.$ == 'undefined')
		setTimeout( GM_wait, 100 ); 
	else
		GM_run();
}
GM_wait();

function GM_run() {
	var $ = unsafeWindow.$;

	var HOST = window.location.host, 
		SUBDOMAIN = HOST.split('.')[0];
		FILE = location.pathname.substring(1);

	if ((SUBDOMAIN === 'chat') && (FILE === 'index.htm')) {
	
		// Run chat
		// ==================================================
		function GM_runChat() {

			/* ====== Variables ====== */
				var cfg = { 
					el: {
						chat:					'#wchat-msgs-wrapper',
						chatInput:				'#wchat-input',
						chatPopUpClose:			'.wchat-menu-popup-close',
						chatPopUp:				'.wchat-menu-popup',
						channelsWrapper:		'#wchat-chanells-wrapper',
						streamerBtn: 			'#wchat-btn-streamer',
						privateSmiles:			'.wchat-smile-private',
						userName: 				'.wchat-nick',
						userMenu: 				'#wchat-usermenu-wrapper',
						userMenuName: 			'.wchat-usermenu-name',
						userMenuClose: 			'#wchat-usermenu-wrapper .wchat-usermenu-close',
						userMenuBan: 			'#wchat-usermenu-wrapper .wchat-usermenu-banmenu .wchat-btn',
						userMenuBanCallback:	'#wchat-usermenu-wrapper .wchat-usermenu-banmenu .wchat-usermenu-callback',
						menuButtons: 			'#wchat-menu-inner-wrapper .wchat-btn',
						menuWrapper: 			'#wchat-menu-wrapper',
						userState:				'#wchat-menu-inner-wrapper .wchat-userstate',
						cfgFriendList: 			'#wchat-cfg-friendlist select',
						cfgFriendListBtn: 		'#wchat-cfg-friendlist .wchat-btn',
						cfgIgnoreList: 			'#wchat-cfg-ignorelist select',
						cfgIgnoreListBtn: 		'#wchat-cfg-ignorelist .wchat-btn',
						cfgSmilesSize: 			'#wchat-cfg-smiles select',
						cfgFontSize: 			'#wchat-cfg-fontsize select',
						cfgMsgsLimit:			'#wchat-cfg-msgslimit select',
						cfgFriendsMsgStyle:		'#wchat-cfg-friendsmsgstyle select',
						cfgForUserMsgStyle:		'#wchat-cfg-forusermsgstyle select',
						cfgNickColor:			'#wchat-cfg-nickcolor input',
						cfgNickIcon:			'#wchat-cfg-nickicon input',

						cfgWrapper: 			'#wchat-cfg-wrapper',
						admWrapper: 			'#wchat-adm-wrapper',
						linksWrapper: 			'#wchat-links-wrapper',
						forYouWrapper: 			'#wchat-foryou-wrapper',
						smilesWrapper: 			'#wchat-smiles-wrapper'
					},
					eventMessages: {
						capsAbuse:		'Слишком много капса.',
						autoBan:		'Что-то не так с вашим сообщением, возможно превышен лимит смайлов.',
						pleaseLogIn:	'Please log in...',
						banned:			'Вы забанены до '
					},

					userMenuUserSetup: {
						name:	null,
						userId:	null,
						msgId:	null
					},
					
					userInfo: null,
					
					chatURL: 'http://chat.sc2tv.ru/',
					chatGate: 'http://chat.sc2tv.ru/gate.php',
					
					channelId: decodeURIComponent((new RegExp('[?|&]channelId=' + '([^&;]+?)(&|#|;|$)').exec(location.search)||[,""])[1].replace(/\+/g, '%20'))||null,
					channelsList: null,
					
					streamerName: '',
					streamTitle: '',
					chatMessagesLimit: GM_getValue('wchat_chatMessagesLimit') || 50,
					chatInterval: null,
					inputCaretPosition: 0,
					
					messages: null,
					smiles: unsafeWindow.smiles,

					friendList: 		JSON.parse( GM_getValue('wchat_friendList') || '{}' ),
					ignoreList: 		JSON.parse( GM_getValue('wchat_ignoreList') || '{}' ),
					smilesSize: 		GM_getValue('wchat_smilesSize') || 1,
					fontSize: 			GM_getValue('wchat_fontSize') || 12,
					friendsMsgStyle: 	GM_getValue('wchat_friendsMsgStyle') || 'wchat-msg-friend-style-default',
					forUserMsgStyle: 	GM_getValue('wchat_forUserMsgStyle') || 'wchat-msg-foruser-style-default',
					nickColor: 			GM_getValue('wchat_nickColor') || true,
					nickIcon: 			GM_getValue('wchat_nickIcon') || true,

					doScroll: true,
					time: {
						scroll: 900,
						newMsg: 900,
						hide: 300,
						show: 300,
						removeMsg: 800,
						userMenu: 180,
						banMsg: 3000,
						toggleChannels: 300,
						ajaxRetryOnError: 1000,
						scrollTimer: 2500, // auto scroll disabled for n seconds after user scroll or mousedown
						chatUpdateInterval: 4000
					}
				}
				var templates = {};
			
			
			
			/* ===== Templates ====== */
				templates.chat = function() {
					var html = '';
					html += '<div id="wchat-wrapper">';
					html += 	'<div id="wchat-chanells-wrapper"></div>';
					html += 	'<div id="wchat-msgs-wrapper"></div>';
					html += 	'<div id="wchat-menu-wrapper">';
					html += 		templates.cfgWrapper();
					html += 		templates.admWrapper();
					html += 		templates.linksWrapper();
					html += 		templates.forYouWrapper();
					html += 		templates.userMenu();
					html += 		'<div id="wchat-menu-inner-wrapper">';
					html += 			'<div class="input-wrapper"><textarea id="wchat-input" placeholder="Сообщение..." maxlength="1024"></textarea>';
					html += 				'<div id="wchat-btn-streamer" class="wchat-btn" title="Написать стримеру">S</div>';
					html += 				'<div id="wchat-btn-smiles" class="wchat-btn" title="Смайлы" data-target="smilesWrapper"></div>';
					html += 			'</div>';
					html += 			templates.userState();
					html += 			'<div id="wchat-menu-control">';
					html += 				'<div class="wchat-btn" title="Настройки" data-target="cfgWrapper">CFG</div>';
					html += 				'<div class="wchat-btn" title="Сообщения от администрации" data-target="admWrapper">ADM</div>';
					html += 				'<div class="wchat-btn" title="Ссылки из чата" data-target="linksWrapper">LINKS</div>';
					html += 				'<div class="wchat-btn disabled" title="Сообщения адресованные вам" data-target="forYouWrapper">4YOU</div>';
					html += 			'</div>';
					html += 		'</div>';
					html += 	'</div>';
					html += '</div>';
					return html;
				}
				templates.chatMSG = function( data ) {
					var html = '', 
						messageStyle = getMessageStyle( data ),
						msgData = 'data-userid="'+ data.uid +'" data-msgid="'+ data.id +'"';

					html += '<div class="wchat-msg '+ messageStyle.msg +'" title="'+ data.date +'">';
					html += 	messageStyle.icons +'<span class="wchat-nick '+ messageStyle.nick +'" '+ msgData +' >'+ data.name +'</span> <span class="wchat-msg-text">'+ msg2html(data.message) +'</span>';
					html += '</div>';
					
					return html;
				}
				templates.smile = function( data ) {
					var style = 'width: '+ Math.floor(data.width * cfg.smilesSize) +'px; height: '+ Math.floor(data.height * cfg.smilesSize)+'px;';
					return '<img src="img/'+ data.img +'" title="'+ data.code +'" style="'+ style +'" />';
				}
				templates.smilesWrapper = function() {
					var html = '', smilesHtml = '', smile, noAccess,
						privateSmile, userLoggedIn = isUserLoggedIn(),
						userRoles = cfg.userInfo.roleIds.slice();

					for (var i=0; i < cfg.smiles.length; i++) {
						smile = cfg.smiles[ i ];
						
						privateSmile = '';
						
						// check if user have access to smile
						if (( userLoggedIn ) && ( smile.private )) {
							noAccess = true;
							
							for (var j=0; j < userRoles.length; j++) {
								if (smile.roles.indexOf( userRoles[ j ] ) !== -1) {
									noAccess = false;
									break;
								}
							}
							
							if (noAccess) { privateSmile = ' class="wchat-smile-private" title="Платные смайлы"'; }
						}

						smilesHtml += '<div'+ privateSmile +'><img src="/img/'+ smile.img +'" title="'+ smile.code +'" /></div>';
					}
				
					html +=		'<div id="wchat-smiles-wrapper" class="wchat-menu-popup">';
					html += 		'<div class="wchat-menu-popup-close"><span>Смайлы</span><div>&#10005;</div></div>';
					html += 		'<div class="wchat-menu-popup-content">'+ smilesHtml +'</div>';
					html += 	'</div>';
					
					return html;
				}
				templates.cfgWrapper = function() {
					var html = '';

					html +=		'<div id="wchat-cfg-wrapper" class="wchat-menu-popup">';
					html += 		'<div class="wchat-menu-popup-close"><span>Настройки</span><div>&#10005;</div></div>';
					html += 		'<div class="wchat-menu-popup-content">';
					html += 			'<ul>';
					
					html += 				'<li id="wchat-cfg-friendlist">';
					html += 					'В друзьях <br><select></select><br><div class="wchat-btn">УДАЛИТЬ</div>';
					html += 				'</li>';
					
					html += 				'<li id="wchat-cfg-ignorelist">';
					html += 					'В игноре <br><select></select><br><div class="wchat-btn">УДАЛИТЬ</div>';
					html += 				'</li>';
					
					html += 				'<li id="wchat-cfg-smiles">';
					html += 					'Размер смайлов ';
					html += 					'<select>';
					html += 						'<option>2</option>';
					html += 						'<option>1.5</option>';
					html += 						'<option>1.25</option>';
					html += 						'<option>1</option>';
					html += 						'<option>0.9</option>';
					html += 						'<option>0.8</option>';
					html += 						'<option>0.7</option>';
					html += 						'<option>0.6</option>';
					html += 						'<option>0.5</option>';
					html += 						'<option title="Выключить смайлы">0</option>';
					html += 					'</select>';
					html += 				'</li>';
					
					html += 				'<li id="wchat-cfg-fontsize">';
					html += 					'Размер шрифта ';
					html += 					'<select>';
					html += 						'<option>40</option><option>36</option><option>32</option><option>28</option>';
					html += 						'<option>24</option><option>20</option><option>18</option><option>16</option>';
					html += 						'<option>15</option><option>14</option><option>13</option><option>12</option>';
					html += 						'<option>11</option><option>10</option><option>9</option><option>8</option>';
					html += 					'</select>';
					html += 				'</li>';
					
					html += 				'<li id="wchat-cfg-nickcolor">';
					html += 					'Цветные ники <input type="checkbox">';
					html += 				'</li>';
					
					html += 				'<li id="wchat-cfg-nickicon">';
					html += 					'Иконки ников <input type="checkbox">';
					html += 				'</li>';
					
					html += 				'<li id="wchat-cfg-msgslimit">';
					html += 					'Лимит сообщений в чате ';
					html += 					'<select>';
					html += 						'<option>250</option>';
					html += 						'<option>200</option>';
					html += 						'<option>150</option>';
					html += 						'<option>100</option>';
					html += 						'<option>50</option>';
					html += 						'<option>20</option>';
					html += 						'<option>10</option>';
					html += 					'</select>';
					html += 				'</li>';
					
					html += 				'<li id="wchat-cfg-friendsmsgstyle">';
					html += 					'Friends msg style ';
					html += 					'<select>';
					html += 						'<option data-class="wchat-msg-friend-style-default">default</option>';
					html += 						'<option data-class="wchat-msg-friend-style-grgray">gray gradient</option>';
					html += 						'<option data-class="wchat-msg-friend-style-grgray3d">gray gradient + 3d</option>';
					html += 					'</select>';
					html += 				'</li>';
					
					html += 				'<li id="wchat-cfg-forusermsgstyle">';
					html += 					'4YOU msg style ';
					html += 					'<select>';
					html += 						'<option data-class="wchat-msg-foruser-style-default">default</option>';
					html += 						'<option data-class="wchat-msg-foruser-style-classic">classic</option>';
					html += 						'<option data-class="wchat-msg-foruser-style-grbrown">brown gradient</option>';
					html += 						'<option data-class="wchat-msg-foruser-style-grbrown3d">brown gradient + 3d</option>';
					html += 						'<option data-class="wchat-msg-foruser-style-grgreen">green gradient</option>';
					html += 						'<option data-class="wchat-msg-foruser-style-grgreen3d">green gradient + 3d</option>';
					html += 					'</select>';
					html += 				'</li>';
					
					html += 				'<li id="wchat-cfg-about">';
					html += 					'<a href="http://chat.sc2tv.ru/history.htm" target="_blank">История сообщений</a>, ';
					html += 					'<a href="http://sc2tv.ru/chat-rules" target="_blank">Правила</a>, ';
					html += 					'<a href="http://chat.sc2tv.ru/automoderation_history.htm" target="_blank">Баны</a>';
					html += 				'</li>';
					
					html += 				'<li id="wchat-cfg-about">';
					html += 					'peka2tv chat <a href="https://github.com/Winns/p2tv/tree/master/peka2tv_chat2" target="_blank">v2.x</a><br>';
					html += 					'Установить старую версию <a href="https://github.com/Winns/p2tv/tree/master/peka2tv_chat" target="_blank">v1.x</a>';
					html += 				'</li>';
					html += 			'</ul>';
					html += 		'</div>';
					html += 	'</div>';
					
					return html;
				}
				templates.channels = function() {
					var html = '', id, text, textHTML,
						title = cfg.streamTitle,
						streamer = cfg.streamerName;

					if ((cfg.streamerName == '') && (cfg.streamTitle == ''))
						text = textHTML = 'Unknown channel';
					else {
						if (cfg.streamerName === undefined)
							text = textHTML = cfg.streamTitle;
						else {
							text = cfg.streamerName +': '+ cfg.streamTitle;
							textHTML = '<em>'+ cfg.streamerName +':</em> '+ cfg.streamTitle;
						}
					}

					html += '<div id="wchat-chanells-title" title="'+ text +'">';
					html += 	textHTML +'<span></span><a href="http://chat.sc2tv.ru/index.htm?channelId='+ cfg.channelId +'" title="Full screen chat" target="_blank">&rarr;</a>';
					html += '</div>';
					html += '<div id="wchat-chanells-list">';
					html += 	'<div class="wchat-select-menu">';
					
						for (var i=0; i < cfg.channelsList.length; i++) {
							id			= cfg.channelsList[ i ].channelId;
							title		= cfg.channelsList[ i ].channelTitle;
							streamer	= cfg.channelsList[ i ].streamerName;

							if (streamer === undefined)
								text = textHTML = title;
							else {
								text = streamer +': '+title;
								textHTML = '<em>'+ streamer +':</em> '+ title;
							}
							
							html += '<div title="'+ text +'" data-chanell-id="'+ id +'" data-streamer="'+ streamer +'">'+ textHTML +'</div>';
						}
						
					html += 	'</div>';
					html += '</div>';
					
					return html;
				}
				templates.admWrapper = function() {
					var html = '';

					html +=		'<div id="wchat-adm-wrapper" class="wchat-menu-popup">';
					html += 		'<div class="wchat-menu-popup-close"><span>Сообщения от администрации</span><div>&#10005;</div></div>';
					html += 		'<div class="wchat-menu-popup-content"></div>';
					html += 	'</div>';
					
					return html;
				}
				templates.linksWrapper = function() {
					var html = '';

					html +=		'<div id="wchat-links-wrapper" class="wchat-menu-popup">';
					html += 		'<div class="wchat-menu-popup-close"><span>Ссылки из чата</span><div>&#10005;</div></div>';
					html += 		'<div class="wchat-menu-popup-content"></div>';
					html += 	'</div>';
					
					return html;
				}
				templates.forYouWrapper = function() {
					var html = '';

					html +=		'<div id="wchat-foryou-wrapper" class="wchat-menu-popup">';
					html += 		'<div class="wchat-menu-popup-close"><span>Сообщения адресованные вам</span><div>&#10005;</div></div>';
					html += 		'<div class="wchat-menu-popup-content"></div>';
					html += 	'</div>';
					
					return html;
				}
				templates.userMenu = function() {
					var html = '';

					html +=		'<div id="wchat-usermenu-wrapper">';
					html += 		'<div class="wchat-usermenu-close"><span class="wchat-usermenu-name"></span><div>&#10005;</div></div>';
					html += 		'<div class="wchat-usermenu-content">';
					html += 			'<ul>';
					html += 				'<li data-action="answer">Ответить</li>';
					html += 				'<li data-action="channel"><a href="" target="_blank">Канал пользователя</a></li>';
					html += 				'<li data-action="add-to-friends">Добавить в друзья</li>';
					html += 				'<li data-action="send-private-msg">Послать ЛС</li>';
					html += 				'<li data-action="banmenu">Забанить</li>';
					html += 				'<li data-action="add-to-ignore">Добавить в игнор</li>';
					html += 			'</ul>';
					html += 		'</div>';
					html += 		'<div class="wchat-usermenu-banmenu">';
					html += 			'<div>Причина бана</div>';
					html += 			'<select>';
					html += 				'<option data-reason-id="1">Мат</option>';
					html += 				'<option data-reason-id="2">Завуалированый мат</option>';
					html += 				'<option data-reason-id="3">Угрозы жизни и здоровью</option>';
					html += 				'<option data-reason-id="4">Лёгкие оскорбления</option>';
					html += 				'<option data-reason-id="5">Серьёзные оскорбления</option>';
					html += 				'<option data-reason-id="6">Национализм, нацизм</option>';
					html += 				'<option data-reason-id="7">Реклама</option>';
					html += 				'<option data-reason-id="8">Спам</option>';
					html += 				'<option data-reason-id="9">Клевета</option>';
					html += 				'<option data-reason-id="10">Негативный троллинг</option>';
					html += 				'<option data-reason-id="11">Транслит, удаффщина, капсы</option>';
					html += 				'<option data-reason-id="12">Вредные ссылки</option>';
					html += 				'<option data-reason-id="13">Вредные флэшмобы</option>';
					html += 				'<option data-reason-id="14">Спойлер</option>';
					html += 			'</select>';
					html += 			'<br><div class="wchat-btn">Забанить</div>';
					html += 			'<div class="wchat-usermenu-callback"></div>';
					html += 		'</div>';
					html += 	'</div>';
					
					return html;
				}
				templates.userState = function() {
					var html = '';
					if ( !isUserLoggedIn() ) {
						html = '<div class="wchat-userstate">'+ cfg.eventMessages.pleaseLogIn +'</div>';
					}
					return html;
				}
			
			
			
			/* ====== Functions ====== */
				/* === jQuery === */
				$.fn.selectRange = function(start, end) {
					if(!end) end = start; 
					return this.each(function() {
						if (this.setSelectionRange) {
							this.focus();
							this.setSelectionRange(start, end);
						} else if (this.createTextRange) {
							var range = this.createTextRange();
							range.collapse(true);
							range.moveEnd('character', end);
							range.moveStart('character', start);
							range.select();
						}
					});
				};
				
				/* === Script === */
				function isUserLoggedIn() {
					if ((cfg.userInfo === '') || (cfg.userInfo === null) || (cfg.userInfo === undefined)) {
						return false;
					} else {
						if (cfg.userInfo.type === 'anon') {
							return false;
						} else {
							return true;
						}
					}
				}
				
				function getBanInfo() {
					var info = { isBanned: false, banExpire: '' };

					if ((cfg.userInfo !== '') || (cfg.userInfo !== null) || (cfg.userInfo !== undefined)) {
						if ((cfg.userInfo.type === 'bannedInChat') || (cfg.userInfo.type === 'bannedOnSite')) {
							info.isBanned = true;
							info.banExpire = new Date( cfg.userInfo.banExpirationTime * 1000 ).toLocaleString();
						}
					}
					
					return info;
				}
				
				function msg2html( data ) {
					// bb codes parser
					var html = [
						'<b>$1</b>',
						'<a href="$1" target="_blank">$2</a>'
					];
					var bb = [
						/\[b\](.*?)\[\/b\]/g,
						/\[url=(.*?)\](.*?)\[\/url\]/g
					];
					for (var i=0; i < bb.length; i++) {
						data = data.replace( bb[i], html[i] );
					}
					
					// url shortener
					data = data.replace( /\[url\](.*?)\[\/url\]/g, function( match, url ) {
						var text;
						if (url.length > 40)
							text = url.substr(0, 26) +'...'+ url.substr(url.length - 11);
						else
							text = url;
							
						text = text.replace(/(http[s]?:\/\/)?(www\.)?/i, '');
						if (text.length < 1) text = 'link';
							
						return '<a href="'+ url +'" target="_blank">'+ text +'</a>';
					});

					// smiles
					data = data.replace( /:s(:[-a-z0-9]{2,}:)/gi, function( match, code ) {
						var smile = '';
						for (var i=0; i < cfg.smiles.length; i++) {
							if (cfg.smiles[i].code == code) {
								smile = templates.smile( cfg.smiles[i] );
							}
						}
						return smile;
					});
					
					return data;
				}

				function readChat( renderCfg ) {
					
					$.getJSON( cfg.chatURL + 'memfs/channel-' + cfg.channelId + '.json', function( messages ){
						if ( messages != undefined ) {
							messages = messages.messages;

							// get new messages
							var newMessages = [];
							if (cfg.messages === null) {
								newMessages = messages;
								renderMessages( newMessages, renderCfg );
							} else {

								var newId, oldId, isOldMsg;
								for (var i=0, lenI = messages.length; i < lenI; i++) {
									newId = messages[i].id;
									isOldMsg = false;
									
									for (var j=0, lenJ = cfg.messages.length; j < lenJ; j++) {
										oldId = cfg.messages[j].id;
										if (newId == oldId) { 
											isOldMsg = true; 
											break;
										}
									}
									
									if (!isOldMsg) { newMessages.push( messages[i] ); }
								}

								renderMessages( newMessages, renderCfg );
							}

							cfg.messages = messages;

							// chat widgets
							if (newMessages.length > 0) {
								widgetChatLinks( newMessages );
								widgetAdmMsgs( newMessages );
								widgetMsgsForYou( newMessages );
							}
						}
					});
				}
				
				function checkMsgCount() {
					var msgsEl = $(cfg.el.chat).find('div');
					if (msgsEl.length > cfg.chatMessagesLimit)
						msgsEl.slice(0, msgsEl.length - cfg.chatMessagesLimit).remove();
				}
				
				function renderMessages( data, renderCfg ) {

					if (data.length < 1) return;
					
					var html = '', 
						oldMsgs = $(cfg.el.chat).find('.wchat-msg'),
						newMsgs;
					
					if (renderCfg === undefined) {
						renderCfg = {scroll: 'animation', fade: true, append: true }; 
					} else {
						if (!renderCfg.hasOwnProperty( 'scroll' )) { renderCfg.scroll = 'animation'; }
						if (!renderCfg.hasOwnProperty( 'fade' ))  { renderCfg.fade = true; }
						if (!renderCfg.hasOwnProperty( 'append' ))  { renderCfg.append = true; }
					}

					// form msgs html
					for (var i=0; i < data.length; i++) {
						html = templates.chatMSG( data[i] ) + html;
					}
					
					// push new msgs
					if ( renderCfg.append )
						$(cfg.el.chat).append( html );
					else
						$(cfg.el.chat).html( html );
					
					// animate new msgs
					newMsgs = $(cfg.el.chat).find('.wchat-msg').not( oldMsgs );

					if ( newMsgs.length ) {
						if ( renderCfg.fade )
							newMsgs.fadeTo( cfg.time.newMsg, 1 );
						else
							newMsgs.css( 'opacity', 1 );

						switch ( renderCfg.scroll ) {
							case 'animation':
								// scroll, del msg over limit
								// if mousedown and scroll = false
								if ( (!$( cfg.el.chat+':active' ).length) && cfg.doScroll ) {
									$(cfg.el.chat).animate({ 
											scrollTop: $(cfg.el.chat)[0].scrollHeight - ($(cfg.el.chat).height()+0.001)
										}, cfg.time.scroll, function(){
											checkMsgCount();
										}
									);
								}
								break;
							case 'instant':
								$(cfg.el.chat).scrollTop( $(cfg.el.chat)[0].scrollHeight - ($(cfg.el.chat).height()+0.001) );
								checkMsgCount();
								break;
						}
					}
				}
				
				function clearChat() {
					cfg.messages = null;
					$(cfg.el.chat).html('');
					readChat( {scroll: 'instant'} );
				}
				
				function getMessageStyle( data ) {
					var o = { msg: '', nick: '', icons: '' };
					
					// If logged in
					if (isUserLoggedIn()) {
						// if @ ignore list
						if ( cfg.ignoreList.hasOwnProperty(data.uid) )
							o.msg += ' wchat-msg-ignore';

						// message for you
						var msgForUserRegExp = new RegExp('\\[b\\]' + escapeData( cfg.userInfo.name ) + '\\[/b\\],','gi');
						if ( data.message.search( msgForUserRegExp ) != -1 ) {
							o.msg += ' wchat-msg-foruser '+ cfg.forUserMsgStyle;
						} else {
							// if @ friend list
							if ( cfg.friendList.hasOwnProperty(data.uid) )
								o.msg += ' wchat-msg-friend '+ cfg.friendsMsgStyle;
						}
					}
					
					switch ( data.uid ) {
						case '-2': 
							o.msg += ' wchat-msg-primetime'; 
							o.nick += ' wchat-user-primetime'; 
							o.icons += '<i class="wchat-icon wchat-icon-primetime"></i> ';
							break; // primetime bot
						case '-1': 
							o.msg += ' wchat-msg-system'; 
							o.nick += ' wchat-user-system'; 
							o.icons += '<i class="wchat-icon wchat-icon-system"></i> ';
							break;	// system message	
					};
					
					// If nick colors ON
					if (cfg.nickColor) {
						switch (data.role) {
							case 'user': 				o.nick += 'wchat-user-default'; break;	
							case 'userstream-editor': 	o.nick += 'wchat-user-userstream-editor'; break;
							case 'moderator':			o.nick += 'wchat-user-moderator'; break;
							case 'editor': 				o.nick += 'wchat-user-editor'; break;
							case 'root': 				o.nick += 'wchat-user-root'; break;
							case 'streamer': 			o.nick += 'wchat-user-streamer'; break;
							case 'prime-streamer':		o.nick += 'wchat-user-primestreamer'; break;
							
							case 'admin':
							case 'color-red': 			o.nick += 'wchat-user-admin'; break;
							
							case 'color-purple':		o.nick += 'wchat-user-purple'; break;
							case 'color-pink':			o.nick += 'wchat-user-pink'; break;

							default: 					o.nick += 'wchat-user-default'; break;
						}

					} else {
						o.nick = 'wchat-user-default';
					}
					
					// If nick icons ON
					if (cfg.nickIcon) {
					
						// top supporter
						if (data.roleIds.indexOf( 24 ) !== -1)
							o.icons += '<i class="wchat-icon wchat-icon-topsupporter"></i> ';

						switch ( data.uid ) {
							case '51245': 
								o.icons += '<i class="wchat-icon wchat-icon-pes"></i> ';
								break;	// pes	
						};
					}
					
					return o;
				}
				
				function sendMessage() {
					var msg = $( cfg.el.chatInput ).val();

					// sanitize user msg
					msg = msg.replace( /[^\u0020-\u007E\u0400-\u045F\u0490\u0491\u0207\u0239\u2012\u2013\u2014]+/g, '' );
					msg = msg.replace( /[\s]+/g, ' ' );
					
					// check for caps abuse
					if ( unsafeWindow.IsStringCapsOrAbuse( msg ) == true ) {
						alert( cfg.eventMessages.capsAbuse ); return;
					}
					
					// fix smiles
					msg = fixSmileCode( msg );
					// fix url
					msg = unsafeWindow.AddUrlBBCode( msg );
					
					// check for auto ban
					if ( unsafeWindow.CheckForAutoBan( msg ) == true ) {
						alert( cfg.eventMessages.autoBan ); return;
					}

					$( cfg.el.chatInput ).attr( 'readonly', 'readonly' );
					// post message
					$.post( cfg.chatGate,
						{ task: 'WriteMessage', message: msg, channel_id: cfg.channelId, token: cfg.userInfo.token },
						function( data ) {
							var jsonData = $.parseJSON( data );
							if( jsonData.error == '' ) {
								$( cfg.el.chatInput ).val('');
								readChat();
							} else {
								// error
							}

							$( cfg.el.chatInput ).removeAttr( 'readonly' );
						}
					);
				}
				
				function escapeData( data ) {
					return data.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
				}
				
				function fixSmileCode( data ) {
					var smilePattern;
					for( i=0; i < cfg.smiles.length; i++) {
						smilePattern = new RegExp( escapeData( cfg.smiles[ i ].code ), 'gi' );
						data = data.replace( smilePattern, ':s' + cfg.smiles[ i ].code  );
					}
					return data;
				}
				
				function saveCaretePosition() {
					cfg.inputCaretPosition = $(cfg.el.chatInput)[0].selectionStart;
				}

				
				
				/* === User menu === */
				function userMenuShow() {
					$( cfg.el.userMenu ).animate( {right: -$( cfg.el.userMenu ).width()}, cfg.time.userMenu );
				}
				function userMenuHide() {
					$( cfg.el.userMenu ).animate( {right: 0}, cfg.time.userMenu );
				}
				
				function voteBan( reasonId ) {
					$.post( cfg.chatGate, {
							task:		'CitizenVoteForUserBan',
							messageId:	cfg.userMenuUserSetup.msgId,
							banUserId:	cfg.userMenuUserSetup.userId,
							userName:	cfg.userMenuUserSetup.name,
							reasonId:	reasonId,
							token:		cfg.userInfo.token
						}, function( data ) {
							$( cfg.el.userMenuBanCallback ).html( $.parseJSON( data ).result );
							setTimeout(function() {
								userMenuHide();
							}, cfg.time.banMsg);
						}
					);
				}
				
				function addToFriends( id, name ) {
					cfg.friendList[ id ] = name;
					GM_setValue('wchat_friendList', JSON.stringify( cfg.friendList ) );
					
					if (isIgnored( id ))
						removeIgnore( id );

					$( cfg.el.userName +'[data-userid="'+ id +'"]' ).parent()
						.addClass('wchat-msg-friend '+ cfg.friendsMsgStyle);
				}
				function removeFriend( id ) {
					delete cfg.friendList[ id ];
					GM_setValue('wchat_friendList', JSON.stringify( cfg.friendList ) );
					
					$( cfg.el.userName +'[data-userid="'+ id +'"]' ).parent()
						.removeClass('wchat-msg-friend')
						.removeClass(function (index, elClass) {
							return (elClass.match (/\wchat-msg-friend-style\S+/g) || []).join(' ');
						});
				}
				function isFriend( id ) {
					if ( cfg.friendList.hasOwnProperty( id ) )
						return true;
					else
						return false;
				}
				
				function addToIgnore( id, name ) {
					cfg.ignoreList[ id ] = name;
					GM_setValue('wchat_ignoreList', JSON.stringify( cfg.ignoreList ) );
					
					if (isFriend( id ))
						removeFriend( id );
					
					$( cfg.el.userName +'[data-userid="'+ id +'"]' ).parent().addClass('wchat-msg-ignore');
				}
				function removeIgnore( id ) {
					delete cfg.ignoreList[ id ];
					GM_setValue('wchat_ignoreList', JSON.stringify( cfg.ignoreList ) );
					$( cfg.el.userName +'[data-userid="'+ id +'"]' ).parent().removeClass('wchat-msg-ignore');	
				}
				function isIgnored( id ) {
					if ( cfg.ignoreList.hasOwnProperty( id ) )
						return true;
					else
						return false;
				}
				
				
				
				
				
				function getUserInfo() {
					$.ajax({
						url: cfg.chatGate + '?task=GetUserInfo&ref=' + document.referrer,
						dataType: 'json',
						success: function( data ) { 
							cfg.userInfo = data; 
							onUserInfoUpdate();
						},
						error: function() {
							setTimeout(function() { getUserInfo(); }, cfg.time.ajaxRetryOnError);
						}
					});
				}
					function onUserInfoUpdate() {
						if ( isUserLoggedIn() ) {
							// if user banned
							if ( getBanInfo().isBanned ) {
								$( cfg.el.userState ).html( cfg.eventMessages.banned + getBanInfo().banExpire );
								return;
							}
							// hide user state overlay
							$( cfg.el.userState ).fadeOut( cfg.time.hide );
							// activate '4YOU' button
							$('.wchat-btn.disabled[data-target="forYouWrapper"]').removeClass('disabled');
							// append smiles window
							if ( !$( cfg.el.smilesWrapper ).length ) {
								$( cfg.el.menuWrapper ).prepend( templates.smilesWrapper() );
							}
						}
					}
				
				function getChannelsInfo() {
					$.ajax({
						url: cfg.chatURL + 'memfs/channels.json',
						dataType: 'json',
						success: function( jsonData ) { 
							cfg.channelsList = jsonData.channel;
							onChannelsInfoUpdate();
						},
						error: function() {
							setTimeout(function() { getChannelsInfo(); }, cfg.time.ajaxRetryOnError);
						}
					});
				}
					function onChannelsInfoUpdate() {
						for (var i=0; i < cfg.channelsList.length; i++) {
							if ( cfg.channelsList[ i ].channelId == cfg.channelId ) {
								cfg.streamerName = cfg.channelsList[ i ].streamerName;
								cfg.streamTitle = cfg.channelsList[ i ].channelTitle;
							}
						}
						
						// add channels to cfg menu
						$( cfg.el.channelsWrapper ).html( templates.channels() );
					}
				
				function toggleChannels() {
					var el = $('#wchat-chanells-list');
							
					el.toggleClass('active');
					if ( !el.hasClass('active') ) {
						el.stop(true, false).animate({top: '-183px' }, cfg.time.toggleChannels );
					} else {
						el.stop(true, false).animate({top: '12px' }, cfg.time.toggleChannels );
					}
				}
				
				
				
			/* ====== Chat widgets ====== */
				/* === Links === */
				function widgetChatLinks( data ) {
					var msg, hasLink, html = '', newMsgs, duplicate;

					for (var i=0; i < data.length; i++ ) {
						msg = data[ i ].message;
						hasLink = msg.match( /\[url\](.*?)\[\/url\]/g );
						
						if ( hasLink ) {
							// check for duplicate
							duplicate = $( cfg.el.linksWrapper ).find('.wchat-nick[data-msgid="'+ data[ i ].id +'"]').length;
							if ( !duplicate )
								html = templates.chatMSG( data[ i ] ) + html;
						}
					}
					
					$( cfg.el.linksWrapper ).find('.wchat-menu-popup-content').append( html );
					
					var messages = $( cfg.el.linksWrapper ).find('.wchat-msg');
					messages.css({opacity: 1});

					// remove messages over limit
					if (messages.length > cfg.chatMessagesLimit) {
						messages.slice(0, messages.length-cfg.chatMessagesLimit).remove();
					}
				}
				
				/* === Adm === */
				function widgetAdmMsgs( data ) {
					var html = '', duplicate;
					
					for (var i=0; i < data.length; i++) {
						if ( data[ i ].role != 'user' ) {
							// check for duplicate
							duplicate = $( cfg.el.admWrapper ).find('.wchat-nick[data-msgid="'+ data[ i ].id +'"]').length;
							if ( !duplicate )
								html = templates.chatMSG( data[ i ] ) + html;
						}
					}
					$( cfg.el.admWrapper ).find('.wchat-menu-popup-content').append( html );
					
					var messages = $( cfg.el.admWrapper ).find('.wchat-msg');
					messages.css({opacity: 1});

					// remove messages over limit
					if (messages.length > cfg.chatMessagesLimit) {
						messages.slice(0, messages.length-cfg.chatMessagesLimit).remove();
					}
				}
				
				/* === 4You === */
				function widgetMsgsForYou( data ) {
					var html = '', duplicate,
						msgForUserRegExp = new RegExp('\\[b\\]' + escapeData( cfg.userInfo.name ) + '\\[/b\\],','gi');

					for (var i=0; i < data.length; i++ ) {
						if ( data[ i ].message.search( msgForUserRegExp ) != -1 ) {
							// check for duplicate
							duplicate = $( cfg.el.forYouWrapper ).find('.wchat-nick[data-msgid="'+ data[ i ].id +'"]').length;
							if ( !duplicate )
								html = templates.chatMSG( data[ i ] ) + html;
						}
					};
					
					$( cfg.el.forYouWrapper ).find('.wchat-menu-popup-content').append( html );
					
					var messages = $( cfg.el.forYouWrapper ).find('.wchat-msg');
					messages.css({opacity: 1});
					
					// remove messages over limit
					if (messages.length > cfg.chatMessagesLimit) {
						messages.slice(0, messages.length-cfg.chatMessagesLimit).remove();
					}
				}
				
				/* === Cfg === */
				function updateCfgFrame() {
					var html = '';
					// friend list
					for (var key in cfg.friendList) {
						html += '<option data-id="'+ key +'">'+ cfg.friendList[ key ] +'</option>';
					}
					$( cfg.el.cfgFriendList ).html( html );
					
					// ignore list
					html = '';
					for (var key in cfg.ignoreList) {
						html += '<option data-id="'+ key +'">'+ cfg.ignoreList[ key ] +'</option>';
					}
					$( cfg.el.cfgIgnoreList ).html( html );
					
					$( cfg.el.cfgSmilesSize ).val( cfg.smilesSize );
					$( cfg.el.cfgFontSize ).val( cfg.fontSize );
					
					$( cfg.el.cfgNickColor ).prop('checked', cfg.nickColor);
					$( cfg.el.cfgNickIcon ).prop('checked', cfg.nickIcon);
					
					$( cfg.el.cfgMsgsLimit ).val( cfg.chatMessagesLimit );
					$( cfg.el.cfgFriendsMsgStyle ).find('option[data-class="'+ cfg.friendsMsgStyle +'"]').attr('selected', 'selected');
					$( cfg.el.cfgForUserMsgStyle).find('option[data-class="'+ cfg.forUserMsgStyle +'"]').attr('selected', 'selected');
				}
				function setSmilesSize( size ) {
					GM_setValue( 'wchat_smilesSize', size.toString() );
					cfg.smilesSize = size;
					renderMessages( cfg.messages, {scroll: 'off', fade: false, append: false} );
				}
				function setFontSize( size ) {
					GM_setValue( 'wchat_fontSize', size.toString() );
					cfg.fontSize = size;
					
					$( cfg.el.chat ).css('font-size', cfg.fontSize +'px');
				}
				function setNickColor( val ) {
					GM_setValue( 'wchat_nickColor', val );
					cfg.nickColor = val;
					
					renderMessages( cfg.messages, {scroll: 'off', fade: false, append: false} );
				}
				function setNickIcon( val ) {
					GM_setValue( 'wchat_nickIcon', val );
					cfg.nickIcon = val;
					
					renderMessages( cfg.messages, {scroll: 'off', fade: false, append: false} );
				}
				function setMsgsLimit( limit ) {
					GM_setValue( 'wchat_chatMessagesLimit', limit.toString() );
					cfg.chatMessagesLimit = limit;
					
					renderMessages( cfg.messages, {scroll: 'instant', fade: false, append: false} );
				}
				function setFriendsMsgStyle( style ) {
					GM_setValue( 'wchat_friendsMsgStyle', style );
					
					cfg.friendsMsgStyle = style;
					
					$('.wchat-msg-friend')
						.removeClass(function (index, elClass) {
							return (elClass.match (/\wchat-msg-friend-style\S+/g) || []).join(' ');
						})
						.addClass( cfg.friendsMsgStyle );
				}
				function setForUserMsgStyle( style ) {
					GM_setValue( 'wchat_forUserMsgStyle', style );
					
					cfg.forUserMsgStyle = style;
					
					$('.wchat-msg-foruser')
						.removeClass(function (index, elClass) {
							return (elClass.match (/\wchat-msg-foruser-style\S+/g) || []).join(' ');
						})
						.addClass( cfg.forUserMsgStyle );
				}

				
				
				/* ====== Init ====== */
				function init() {
					/* === Render === */
					$('body').append( templates.chat() );

					/* === Elements === */
						// stop jquery scroll animation on user scroll
						$(cfg.el.chat).on('wheel mousedown', function(){
							$(cfg.el.chat).stop();

							cfg.doScroll = false;
							clearTimeout( cfg.sctollTimer );
							cfg.sctollTimer = setTimeout(function() {
								cfg.doScroll = true;
							}, cfg.time.scrollTimer);
						});
						
						// fix scroll animation on window resize
						$(window).on('resize', function() {
							var el = $(cfg.el.chat);
							
							if (el.scrollTop() >= el[0].scrollHeight - el.height()) {
								el.scrollTop( el[0].scrollHeight - (el.height()+1) );
							}
						});

					/* === Channels === */
						// set new channel
						$( document ).on('click', cfg.el.channelsWrapper +' .wchat-select-menu div', function() {			
							cfg.channelId = $(this).attr('data-chanell-id');

							clearChat();
							toggleChannels();
							onChannelsInfoUpdate();
						});
						// open channel menu
						$( document ).on('click', '#wchat-chanells-title span', function() {
							toggleChannels();
						});

					/* === Chat input === */
						// submit message on "enter"
						$( cfg.el.chatInput ).on('keypress', function(e){
							if (e.which == 13) {
								e.preventDefault();
								sendMessage();
								return false; 
							}
						});
						
						// fix pageUp, pageDown @ textarea bug (chrome)
						$( cfg.el.chatInput ).on('keydown', function(e){
							if ((e.which == 33) || (e.which == 34)) {
								e.stopPropagation();
								e.preventDefault();
								return false;
							}
						});

						// save caret position
						$( cfg.el.chatInput ).on('mouseup input paste change blur keypress', function(e){
							saveCaretePosition();
						});

					/* === Chat popup === */
						// close
						$ ( document ).on('click', cfg.el.chatPopUpClose, function() {
							$(this).parent().toggleClass('active');
							$(this).parent().animate({top: 0 }, cfg.time.hide );
						});

					/* === Streamer === */
						// handle streamer button
						$( cfg.el.streamerBtn ).on('click', function() {
							if ( (cfg.streamerName != '') && (cfg.streamerName != undefined) ){
								var streamer = '[b]'+ cfg.streamerName +'[/b], ',

								msg = $(cfg.el.chatInput).val();
								if (msg.substr(0, streamer.length) != streamer) {
									$(cfg.el.chatInput).val( streamer + msg );
								}
								$(cfg.el.chatInput).selectRange( streamer.length + msg.length );
							}
						});


					/* === handle popup show === */
						$( document ).on('click', cfg.el.menuButtons +':not(.disabled)', function(){
							var target = $(this).attr('data-target');

							if (target) {
								switch (target) {
									case 'cfgWrapper': updateCfgFrame(); break;
								}

								// hide other frames
								$( cfg.el.chatPopUp ).not( $(cfg.el[target]) )
									.removeClass('active')
									.animate({top: '0' }, cfg.time.hide );

								$( cfg.el[ target ] ).toggleClass('active');
								if ($( cfg.el[ target ] ).hasClass('active')) {
									$( cfg.el[ target ] ).animate({top: -$(cfg.el[ target ]).height() }, cfg.time.show );
								} else {
									$( cfg.el[ target ] ).animate({top: '0' }, cfg.time.hide );
								}
							}
						});

					/* === Smiles === */
						// handle smile post
						$( document ).on('click', cfg.el.smilesWrapper + ' .wchat-menu-popup-content div:not(.wchat-smile-private)', function() {
							var smile			= ' '+ $(this).find('img').attr('title') +' ',
								inputLength		= $( cfg.el.chatInput ).val().length,
								textBeforeCaret = $( cfg.el.chatInput ).val().substring( 0, cfg.inputCaretPosition ),
								textAfterCaret	= $( cfg.el.chatInput ).val().substring( cfg.inputCaretPosition, inputLength );

							// insert smile
							$(cfg.el.chatInput).val( textBeforeCaret + smile + textAfterCaret );
							// close window
							$( cfg.el.smilesWrapper ).removeClass('active').animate({top: '0' }, cfg.time.hide );
							// restor cursor position
							$(cfg.el.chatInput).selectRange( cfg.inputCaretPosition + smile.length );
						});
						// on private smile click
						$( document ).on('click', cfg.el.privateSmiles, function() {
							window.open('http://prime.sc2tv.ru/donate','_blank');
						});

					/* === User menu (on username click) === */
						// show
						$( document ).on('click', cfg.el.userName, function() {
							if ( isUserLoggedIn() ){
								var userMenu			= $( cfg.el.userMenu ),
									userMenuHeight		= userMenu.outerHeight(),
									chatHeight			= $(cfg.el.chat).outerHeight(),
									msgPosition			= $(this).position(),
									chatMenuPosition	= $(cfg.el.menuWrapper).position(),
									top					= -(chatHeight - msgPosition.top),
									text 				= '';

								if (-top < userMenuHeight) {
									top =  -userMenu.outerHeight();
								}
								userMenu.css('top', top );
								userMenu.find( cfg.el.userMenuName ).html( $(this).text() );

								// hide ban menu, show default menu
								userMenu.find('.wchat-usermenu-banmenu').hide();
								userMenu.find('.wchat-usermenu-content').show();

								// save data to variable
								cfg.userMenuUserSetup = {
									name: $(this).text(),
									userId: $(this).attr('data-userid'),
									msgId: $(this).attr('data-msgid')
								}
								
								// handle add/remove friend text
								if (isFriend( cfg.userMenuUserSetup.userId )) {
									text = 'Удалить из друзей';
								} else {
									text = 'Добавить в друзья';
								}
								$( cfg.el.userMenu ).find('.wchat-usermenu-content ul li[data-action="add-to-friends"]').text( text );
								
								// handle add/remove ignore text
								if (isIgnored( cfg.userMenuUserSetup.userId )) {
									text = 'Удалить из игнора';
								} else {
									text = 'Добавить в игнор';
								}
								$( cfg.el.userMenu ).find('.wchat-usermenu-content ul li[data-action="add-to-ignore"]').text( text );
								
								// Create user channel url
								var nick = $( cfg.el.userMenuName ).text();
								nick = 	$.trim( nick )
										.toLowerCase()
										.replace(/[`~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '')
										.replace(/ /g, '-');
										
								$( cfg.el.userMenu )
									.find('.wchat-usermenu-content ul li[data-action="channel"] a')
									.attr( 'href', 'http://sc2tv.ru/channel/'+ nick );
								
								userMenuShow();
							}
						});
						
						// hide
						$( cfg.el.userMenuClose ).on('click', function() { userMenuHide(); });
						
						// on menu click
						$( cfg.el.userMenu ).find('ul li[data-action]').on('click', function() {
							var action = $(this).attr('data-action');
							switch( action ) {
								case 'answer':
									var nick = '[b]'+ $( cfg.el.userMenuName ).text() +'[/b], ';
									$( cfg.el.chatInput ).val( nick );
									$( cfg.el.chatInput ).selectRange( nick.length );
									userMenuHide();
									break;
								case 'banmenu': // hide default menu, show ban menu
									$( cfg.el.userMenu ).find('.wchat-usermenu-content').hide();
									$( cfg.el.userMenuBanCallback ).html('');
									$( cfg.el.userMenu ).find('.wchat-usermenu-banmenu').show();
									break;
								case 'send-private-msg':
									var id = cfg.userMenuUserSetup.userId;
									window.open('http://sc2tv.ru/messages/new/'+ id,'_blank'); 
									break;
								case 'add-to-friends':
									var id = cfg.userMenuUserSetup.userId,
										nick = $( cfg.el.userMenuName ).text();
									if (isFriend( cfg.userMenuUserSetup.userId )) {
										removeFriend( id );
									} else {
										addToFriends( id, nick );
									}
									userMenuHide();
									break;
								case 'add-to-ignore':
									var id = cfg.userMenuUserSetup.userId,
										nick = $( cfg.el.userMenuName ).text();
									if (isIgnored( cfg.userMenuUserSetup.userId )) {
										removeIgnore( id );
									} else {
										addToIgnore( id, nick );
									}
									userMenuHide();
									break;	
								
							}
						});
						
						// ban menu
						$( cfg.el.userMenuBan ).on('click', function() {
							var reasonId = $(this).parent().find('option:selected').attr('data-reason-id');
							voteBan( reasonId );
						});

					/* ====== Cfg ====== */
						/* === Cfg init === */
						setFontSize( cfg.fontSize );
						
						/* === Friend list === */
						$( cfg.el.cfgFriendListBtn ).on('click', function() {
							removeFriend( $( cfg.el.cfgFriendList ).find(':selected').attr('data-id') );
							updateCfgFrame();
						});
						
						/* === Ignore list === */
						$( cfg.el.cfgIgnoreListBtn ).on('click', function() {
							removeIgnore( $( cfg.el.cfgIgnoreList ).find(':selected').attr('data-id') );
							updateCfgFrame();
						});
						
						/* === Smiles size === */
						$( cfg.el.cfgSmilesSize ).on('change', function() {
							setSmilesSize( $(this).val() );
						});
						/* === Font size === */
						$( cfg.el.cfgFontSize ).on('change', function() {
							setFontSize( $(this).val() );
						});
						/* === Nick color === */
						$( cfg.el.cfgNickColor ).on('change', function() {
							setNickColor( this.checked );
						});
						/* === Nick icon === */
						$( cfg.el.cfgNickIcon).on('change', function() {
							setNickIcon( this.checked );
						});
						/* === Messages limit === */
						$( cfg.el.cfgMsgsLimit ).on('change', function() {
							setMsgsLimit( $(this).val() );
						});
						/* === Friends msgs style === */
						$( cfg.el.cfgFriendsMsgStyle ).on('change', function() {
							setFriendsMsgStyle( $(this).find(':selected').attr('data-class') );
						});
						/* === 4YOU msgs style === */
						$( cfg.el.cfgForUserMsgStyle ).on('change', function() {
							setForUserMsgStyle( $(this).find(':selected').attr('data-class') );
						});

					/* === Script === */
						// shutdown original chat
						unsafeWindow.StopChat();
		
						// get chat data from server
						setTimeout(function() { readChat( {scroll: 'instant'} ); }, 250);

						// updata chat data on interval
						if (cfg.chatInterval != null) {
							clearInterval( cfg.chatInterval );
						}
						cfg.chatInterval = setInterval(function() { 
							readChat() 
						}, cfg.time.chatUpdateInterval);
				}

				getUserInfo();
				getChannelsInfo();
				init();
		}
		// END Run chat
		
		function GM_waitChat() { 
			if ((typeof unsafeWindow.StopChat == 'undefined') ||
				(typeof unsafeWindow.smiles == 'undefined') ||
				(typeof unsafeWindow.AddUrlBBCode == 'undefined'))
				setTimeout( GM_waitChat, 100 ); 
			else
				GM_runChat();
		}
		GM_waitChat();

	} else { // END (if SUBDOMAIN = chat)
	
		// add wrapper to iframe, to fix fullscreen button
		$( 'html' ).css( 'min-height', '100%' );
		$( 'body' ).css( 'min-height', '100%' );
		
		$( '#tab_chat iframe' )
			.wrap( '<div id="wchat-iframe-fix" style="display: inline-block; position: relative; min-height: 100%;"></div>' );

		$( '#wchat-iframe-fix' )
			.append( '<div id="wchat-fullscreen" onclick="change_screen(); return false;"></div>' );
		
		$( '#chat-switch-screen-btn' )
			.appendTo( '#wchat-iframe-fix' ).hide();
	}
}

})();