rainer.adelstonegmail.com / Kommentar make-up

// ==UserScript==
// @name        Kommentar make-up
// @namespace   germany.ru
// @version     2.0.10
// @match       https://*.germany.ru/newreply*
// @match       https://*.germany.ru/editpost*
// @match       https://*.germany.ru/newpost*
// @match       https://*.germany.ru/dialog/*
// @match       https://*.germany.ru/report.pl*
// @match       https://*.germany.ru/cgi-bin/my/dialog.cgi*
// @match       https://*.germany.ru/cgi-bin/my/sendprivate.cgi*
// @grant       GM.xmlHttpRequest
// @grant       unsafeWindow
// @license     MIT
// ==/UserScript==

/**------------------------------------------------- Globals ------------------------------------------------*/

Function.prototype._memorise = function ( args, value )
{
	const fn = this;

	fn._values = fn._values || {};

	if ( fn._values[ JSON.stringify( args ) ] === undefined ) {
		fn._values[ JSON.stringify( args ) ] = value;
	}
};

Function.prototype._memorised = function ( args )
{
	const fn = this;

	if ( fn._values && fn._values[ JSON.stringify( args ) ] !== undefined ) {
		return fn._values[ JSON.stringify( args ) ];
	}
};

Function.prototype._resolve = function ( callback )
{
	const fn        = this,
		  args      = callback && typeof callback === 'function' ? [ ...arguments ].slice( 1 ) : [ ...arguments ],
		  memorised = fn._memorised.bind( fn, args )();

	if ( memorised === undefined ) {

		if ( !fn._pending ) {

			fn._pending     = true;
			let promise_obj = new Promise( ( resolve ) =>
										   {
											   resolve( fn.apply( fn, args ) );
										   }
			);

			promise_obj.then( ( value ) =>
							  {
								  fn._pending = false;
								  fn._memorise.bind( fn, args, value )();

								  if ( callback && typeof callback === 'function' ) {
									  callback( value );
								  }
							  }, () =>
							  {
								  fn._pending = false;
							  } );
		}
	}
	else {
		if ( callback && typeof callback === 'function' ) {
			callback( memorised );
		}

		return memorised;
	}
};

( function ()
	{

		/**------------------------------------------- Util functions -------------------------------------------*/

		let getElements = ( name ) =>
		{
			getElements.cache = getElements.cache || {};

			return getElements.cache[ name ] = getElements.cache[ name ] ||
											   ( document.getElementsByClassName( name ).length > 0 ? document.getElementsByClassName( name ) :
												 document.getElementsByTagName( name ).length > 0 ? document.getElementsByTagName( name ) :
												 document.getElementsByName( name ).length > 0 ? document.getElementsByName( name ) : undefined );
		};

		let getElement = ( id ) =>
		{
			getElement.cache = getElement.cache || {};

			return getElement.cache[ id ] = getElement.cache[ id ] || document.getElementById( id );
		};

		let styleElement = ( element, style_obj ) =>
		{
			if ( element && element instanceof Element ) {

				if ( style_obj && typeof style_obj === 'object' ) {

					element._initial_styles = element._initial_styles || {};

					for ( const key in style_obj ) {

						if ( style_obj.hasOwnProperty( key ) && element.style.hasOwnProperty( key ) ) {

							if ( !element._initial_styles[ key ] && element._initial_styles[ key ] !== "" ) {
								element._initial_styles[ key ] = element.style[ key ];
							}

							element.style[ key ] = style_obj[ key ];
						}
					}
				}
				else if ( element._initial_styles ) {

					for ( const key in element._initial_styles ) {
						element.style[ key ] = element._initial_styles[ key ];
					}
				}
			}
		};

		/**--------------------------- CSS definitions, new DOM elements & scope vars ---------------------------*/

		const css = ' .no-user-selection { opacity: 0.6 }' +
					' .no-user-selection:hover { opacity: 0.9 }' +
					' .user-selection { opacity: 1 }' +
					' .user-selection-disabled { opacity: 0.6 }' +
					' textarea { height: 30vh; min-width: 90vw }' +
					' textarea::selection { background: #3838BB; color: #FFFFFF; }' +
					' textarea::-moz-selection { background: #3838BB; color: #FFFFFF; }' +
					' .text-area-full { position:fixed; top:47px; left:1px; height:95vh; width:99.8vw; font-size:16px; padding-right:17px; overflow-x:hidden; z-index:99999 }' +
					' .blockheader { display:none }' +
					' span > .td-50{ display:none }' +
					' .td-20 { display:none }' +
					' .td-80 { width:100% }' +
					' .float-left-mobile{ float:none; }' +
					' #img { width: 0; height: 0; opacity: 0; overflow: hidden; position: absolute; z-index: -1 }' +
					' #img + label { background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAQAAABLCVATAAABJklE' +
					'QVR4Ae3VPYrbUBRA4a+RJ1VSJswvGG8mhYsswRhtxT/YmNEuhtEiAmpcJjBJOQsYyyBMsF0qXcjDjNF7pAnJOd0tTqN3r/yLfPCsFfrsvUj' +
					'e+ao91RdvRfBGpX3Fzy6c4UKh0Ua6U+gJKLSJFgKa5FAjoE23W6hRWpp4UKeHNnIZgMzIS0rom1shXHqKDW3cgoHC2tq9PriyiQvl4JPjr8' +
					'nBEIxjQo0MA8dguneHTN09VL7ySBfgsXtoCdYnoQpMu4cmZ0Oz7qEHcH8SmoOye6iWoe8QTH+4QWYb8/lHYGj/W+YjyOPe0YtLcGehUpm7A' +
					'dfq2BV5coWQa9/TlnYcLG2uTj8jtUdTM6VtxBlJNGCXnGn+1PFfCegl/Y4aKz1/Lf/5CShXgVKz4A8DAAAAAElFTkSuQmCC")' +
					'no-repeat center; vertical-align: text-bottom; width: 24px; height: 24px; opacity: 0.6; cursor: pointer;' +
					'-webkit-user-select: none; -moz-user-select: none;  background-size: 24px 24px; display: inline-block}' +
					' #img + label:hover{ opacity: 0.9;}' +
					' #modal-symbols { box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19); display: none; background-color: #fefefe; margin: 15% 18%;' +
					'padding: 20px; border: 1px solid #888; height: auto; width:65%; max-width: 65%; -webkit-user-select: none; -moz-user-select: none }' +
					' #modal-symbols > div, #modal-preview > div{ width=100%; margin-top:-15px; margin-right: -5px;}' +
					' #modal-symbols > div > span:first-child, #modal-preview > div > span:first-child { position:absolute; width:auto; background-color:#fefefe}' +
					' #modal-symbols-close { text-align:right; background-color:#fefefe; cursor: pointer } ' +
					' #modal-preview-close { position:absolute; right:7%; background-color:#fefefe; padding:0px 5px; border-radius:11px; cursor: pointer } ' +
					' #modal-preview { display: none; background-color: #fefefe; margin: 5% 5%; padding: 20px; border: 1px solid #888; height: 85%; width:90%; max-width: 90%; overflow-y: auto;' +
					'-webkit-user-select: none; -moz-user-select: none}' +
					' #modal-dialogue { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.4) }' +
					' #font { float:right; padding-top:7px; width:26px; display:none; cursor:pointer } ' +
					' #font, #resize{ -webkit-user-select: none; -moz-user-select: none; }' +
					' #resize { float:right; margin-top:-5px; padding-left: 16px; display: inline; width: auto; cursor:pointer}' +
					' .resize-el-full { position:fixed; top:15px; right:-20px; z-index:99999; margin-right: 40px}' +
					' #preview { float:right; cursor:pointer }' +
					' .preview-full { position:fixed; top:15px; right:35px; z-index:99999;}' +
					' #upload { font-size: 1.6em; margin: 0px 6px 0px 0px }' +
					' .sml-1 { float: left; font-size:1.2em; padding: 10px; cursor: pointer }' +
					' .sml-2 { float: left; padding: 10px; cursor:pointer}' +
					' #smiley { opacity: 0.7; border: none; background-color: inherit; font-size:1.7em }' +
					' #smiley:hover { opacity: 1.0}' +
					' #trfrm { display:inline-block}' +
					' #markup { margin-top:7px; padding-left:1px; padding-bottom: 2px; width:100% }' +
					' .markup-full { position:fixed; top:0; left:0; margin-top:-5px !important; height:100vh !important; width:100vw !important;  padding-top:12px; padding-left:2px !important;' +
					'background-color:#FFFFFF; font-size:17px; z-index:9999 }' +
					' #answer { font-size:18px; background-color:#FFFFFF; cursor:pointer; display:none }' +
					' .answer-full { display:inline !important; position:fixed; top:95vh !important; right:5vw}' +
					' .answer-opened { display:inline !important; position:fixed; top:66.5vh !important; right:5vw}' +
					' .comment-to-answer-full { position: fixed; top:94vh !important; left:0; height:100vh !important; width:99.8vw; overflow:hidden !important; border:1px solid #CCC5BE;' +
					'margin: 2px 8px 0px 2px; z-index:9999; padding-top:7px}' +
					' .comment-to-answer-opened { position: fixed; top:66vh !important; left:0; height:34vh !important; width:99.8vw; overflow-y:auto !important; overflow-x:hidden !important;' +
					'border:1px solid #CCC5BE; margin: 2px 8px 0px 2px; z-index:9999}' +
					' input[name="Subject"] { width:auto; float:left; padding:0 }' +
					' #subj { float:left; width:101px }' +
					' #avatar-spacer { display:inline-block; width:10px }' +
					'.redactor-toolbar-tooltip { display:none !important }' +
					'.font-btn { line-height:inherit; color:#999999; width:30px }' +
					'.quote-btn { line-height:inherit; color:#999999; width:42px }' +
					'.color-btn { line-height:inherit; width:20px; height: 20px; border: none }' +
					'#error { margin-top:-2px; padding-left:2%; color:#bb3838 }' +
					'#redo, #undo { line-height:inherit; color:#999999; font-weight:bold; width:30px }' +
					'#clear { line-height:inherit; width:auto; color:#999999 }' +
					'#translate + label + span { float: right; margin-right:5px; display: inline }';

		const misc_symbols  = [
				  //funny
				  "🙊", "🙉", "🙈", "🎅", "👹", "😈", "😇",
				  //emoticons
				  "😴", "😷", "😤", "😠", "😬", "😵", "😮", "😭", "😥", "😩", "😫", "😖", "😣", "😒", "😕", "😟", "😞", "😔", "😳", "😑",
				  "😐", "😎", "😉", "😏", "😌", "😊", "😙", "😚", "😘", "😍", "😝", "😜", "😋", "😅", "😂", "😆", "😄", "😃", "😁", "😀",
				  //flowers
				  "🌷", "🌹", "🌻",
				  //hearts
				  "💔", "💕", "💖",
				  //meal & drinks
				  "🍎", "🍭", "🎂", "🍷", "🍺",
				  //gestures
				  "👌", "👏", "✋", "👊", "✊", "✌",
				  //misc
				  "©", "≠", "►", "—", "№", "#"
			  ],

			  smileys_links = [
				  "https://tt.germany.ru/wwwthreads/images/icons/smile.gif",
				  "https://tt.germany.ru/wwwthreads/images/icons/frown.gif",
				  "https://tt.germany.ru/wwwthreads/images/icons/blush.gif",
				  "https://tt.germany.ru/wwwthreads/images/icons/cool.gif",
				  "https://tt.germany.ru/wwwthreads/images/icons/crazy.gif",
				  "https://tt.germany.ru/wwwthreads/images/icons/laugh.gif",
				  "https://tt.germany.ru/wwwthreads/images/icons/mad.gif",
				  "https://tt.germany.ru/wwwthreads/images/icons/shocked.gif",
				  "https://tt.germany.ru/wwwthreads/images/icons/tongue.gif",
				  "https://tt.germany.ru/wwwthreads/images/icons/wink.gif",
				  "https://tt.germany.ru/wwwthreads/images/icons/up.gif",
				  "https://tt.germany.ru/wwwthreads/images/icons/down.gif",
				  "https://tt.germany.ru/wwwthreads/images/icons/kiss.gif",
				  "https://tt.germany.ru/wwwthreads/images/icons/flower.gif",
				  "https://tt.germany.ru/wwwthreads/images/icons/glass.gif",
				  "https://tt.germany.ru/wwwthreads/images/icons/heart.gif"
			  ],

			  colorize_btns = [ "pre", "l-quote", "strike", "underline", "italic", "bold", "quote" ],

			  color_btns    = [ "red", "green", "blue", "black" ],

			  lat           = ( '/E_/e_/O_/o_Шh_Йo_Зh_Цh_Сh_Йe_Йu_Йa_Ыo_Ыu_Ыa_ШH_ЙO_ЗH_ЦH_СH_ЙE_ЙU_ЙA_ЫO_ЫU_ЫA_A_B_V_G_D_E_Z_I_J_K_L_M_N_O_P_R_S_T_U_F_X_C_ъ#_Y_ь\'_H_W_Q_шh_йo_зh_цh_сh_йe_йu_йa_ыo_ыa_a_b_v_g_d_e_z_i_j_k_l_m_n_o_p_r_s_t_u_f_x_c_#_y_\'_h_w_q_' +
								String.fromCharCode( 220 ) + '_' + String.fromCharCode( 214 ) + '_' + String.fromCharCode( 196 ) + '_' + String.fromCharCode( 252 ) + '_' + String.fromCharCode(
					  246 ) + '_' + String.fromCharCode( 228 ) ).split( '_' ),
			  rus           = ( 'E_e_O_o_Щ_Ё_Ж_Ч_Ш_Э_Ю_Я_Ё_Ю_Я_Щ_Ё_Ж_Ч_Ш_Э_Ю_Я_Ё_Ю_Я_А_Б_В_Г_Д_Е_З_И_Й_К_Л_М_Н_О_П_Р_С_Т_У_Ф_Х_Ц_Ъ_Ы_Ь_Х_Щ_Я_щ_ё_ж_ч_ш_э_ю_я_ё_я_а_б_в_г_д_е_з_и_й_к_л_м_н_о_п_р_с_т_у_ф_х_ц_ъ_ы_ь_х_щ_я_Ю_Ё_Э_ю_ё_э' ).split(
				  '_' ),

			  buttons       = '<div id="markup">' +
							  '<span style="padding-right:4px"><input type="button" class="font-btn" id="pre" value="{ }"></span>' +
							  '<span style="padding-right:4px"><input type="button" class="font-btn" id="strike" value="а" style="text-decoration:line-through;"></span>' +
							  '<span style="padding-right:4px"><input type="button" class="font-btn" id="underline" value="a" style="text-decoration:underline;"></span>' +
							  '<span style="padding-right:4px"><input type="button" class="font-btn" id="italic" value="к" style="font-style:italic;"></span>' +
							  '<span style="padding-right:4px"><input type="button" class="font-btn" id="bold" value="ж" style="font-weight:bold;"></span>' +
							  '<span style="padding-right:4px"><input type="button" class="quote-btn" id="l-quote" value="«a»"></span>' +
							  '<span style="padding-right:16px"><input type="button" class="quote-btn" id="quote" value="&#8222;a&#8220;"></span>' +
							  '<span style="padding-right:4px"><input type="button" class="color-btn no-user-selection" id="red" style="background-color:#bb3838;"></span>' +
							  '<span style="padding-right:4px"><input type="button" class="color-btn no-user-selection" id="green" style="background-color:#38BB38;"></span>' +
							  '<span style="padding-right:4px"><input type="button" class="color-btn no-user-selection" id="blue" style="background-color:#3838bb;"></span>' +
							  '<span><input type="button" class="color-btn no-user-selection" id="black" style="background-color:#161619;"></span>' +
							  '<span><input type="button" id="smiley" value="☺"></span>' +
							  '<span id="upload"><input type="file" name="img" id="img"><label for="img"></label></span>' +
							  '<span id="error"></span>' +
							  '<span style="float:right; margin-top: 5px"><input type="button" id="redo" value="&#x21AA;"></span>' +
							  '<span style="float:right; margin-top: 5px; padding-left:15px; padding-right:4px;"><input type="button" id="undo" value="&#x21A9;"></span>' +
							  '<span style="float:right; margin-top: 5px; padding-left:15px;"><input type="button" id="clear" value="Убрать тэги" disabled></span>' +
							  '<span id="font">&#x21a8;<span>&nbsp;a</span></span>' +
							  '</div>',

			  checkboxes    = '<input type="checkbox" id="translate"><label for="translate">&nbsp;Включить транслит</label>' +
							  '<span><a href="http://groups.germany.ru/showmessage.pl?Number=29559602&Board=1015169" target="_blank">' +
							  '<img src="https://tt.germany.ru/wwwthreads/images/icons/exclamation.gif" alt="Справка"/></a></span><br>' +
							  '<input type="checkbox" id="short-links"><label for="short-links">&nbsp;Сокращать ссылки</label><br>';

		let text_area,
			comment_to_answer,
			container_well,
			container_wrap,
			selected_decorators,
			text_history = [],
			undo_clicks  = 0,
			intervals    = 100;

		/**-------------------------------------------- Append styles -------------------------------------------*/

		const style = document.createElement( 'style' );

		style.type = 'text/css';
		style.appendChild( document.createTextNode( css ) );

		document.head.appendChild( style );

		/**------------------------------------- Editor's set up & clean up -------------------------------------*/

		window.onunload = ( event ) =>
		{
			delFlag( event );
		};

		window.onclick = ( event ) =>
		{
			const modal = getElement( 'modal-dialogue' );

			if ( event.target === modal ) {
				hideModal();
			}
		};

		let setUp = () =>
		{
			text_area         = getElement( 'trtxt' ) || getElement( 'trtxt-message' );
			comment_to_answer = getElement( 'previewedit' );
			container_well    = getElements( 'well-alt' );
			container_wrap    = getElements( 'wrap' );

			if ( !text_area ) {
				setTimeout( () =>
							{
								setUp();
							}, 50 );
			}
			else {
				setTimeout( function removeNativeEditor()
							{
								const redactor_box = getElements( 'redactor-box' );

								if ( redactor_box && redactor_box.length > 0 ) {

									text_area.style.display = "";
									redactor_box[ 0 ].insertAdjacentElement( 'beforebegin', text_area );
									redactor_box[ 0 ].remove();
									updateTextArea( [ replaceTags, delNewlines, delWhitespaces ] );
									saveState();
								}
								else if ( intervals > 0 ) {

									intervals--;
									removeNativeEditor();
								}
							}, 500 );

				/** Initial HTML manipulations: modify editor's block styling */

				const resize_el = document.createElement( 'SPAN' );

				resize_el.setAttribute( 'id', 'resize' );
				resize_el.appendChild( document.createTextNode( "↗" ) );

				const preview = document.createElement( 'SPAN' );

				preview.setAttribute( 'id', 'preview' );
				preview.appendChild( document.createElement( "I" ) );
				preview.childNodes[ 0 ].classList.add( "iconlarge-views" );
				text_area.parentElement.insertBefore( resize_el, text_area );
				text_area.parentElement.insertBefore( preview, text_area );
				text_area.parentElement.insertBefore( document.createElement( 'BR' ), text_area );
				text_area.insertAdjacentHTML( 'beforebegin', buttons );
				text_area.insertAdjacentHTML( 'afterend', checkboxes );

				const modal_box = document.createElement( 'DIV' );

				modal_box.setAttribute( 'id', 'modal-dialogue' );
				modal_box.appendChild( document.createElement( 'DIV' ) );
				modal_box.childNodes[ 0 ].setAttribute( 'id', 'modal-symbols' );
				modal_box.childNodes[ 0 ].appendChild( document.createElement( 'DIV' ) );
				modal_box.childNodes[ 0 ].childNodes[ 0 ].appendChild( document.createElement( 'DIV' ) );
				modal_box.childNodes[ 0 ].childNodes[ 0 ].childNodes[ 0 ].setAttribute( 'id', 'modal-symbols-close' );
				modal_box.childNodes[ 0 ].childNodes[ 0 ].childNodes[ 0 ].appendChild( document.createTextNode( "✕" ) );

				modal_box.appendChild( document.createElement( 'DIV' ) );
				modal_box.childNodes[ 1 ].setAttribute( 'id', 'modal-preview' );
				modal_box.childNodes[ 1 ].appendChild( document.createElement( 'DIV' ) );
				modal_box.childNodes[ 1 ].childNodes[ 0 ].appendChild( document.createElement( 'SPAN' ) );
				modal_box.childNodes[ 1 ].childNodes[ 0 ].appendChild( document.createElement( 'SPAN' ) );
				modal_box.childNodes[ 1 ].childNodes[ 0 ].childNodes[ 0 ].appendChild( document.createTextNode( "Предварительный просмотр сообщения:" ) );
				modal_box.childNodes[ 1 ].childNodes[ 0 ].childNodes[ 1 ].setAttribute( 'id', 'modal-preview-close' );
				modal_box.childNodes[ 1 ].childNodes[ 0 ].childNodes[ 1 ].appendChild( document.createTextNode( "✕" ) );
				modal_box.childNodes[ 1 ].childNodes[ 0 ].appendChild( document.createElement( 'BR' ) );
				modal_box.childNodes[ 1 ].childNodes[ 0 ].appendChild( document.createElement( 'BR' ) );
				modal_box.childNodes[ 1 ].childNodes[ 0 ].appendChild( document.createElement( 'DIV' ) );
				modal_box.childNodes[ 1 ].childNodes[ 0 ].childNodes[ 4 ].setAttribute( 'id', 'preview-body' );

				text_area.parentElement.insertBefore( modal_box, getElement( 'translate' ) );

				const user_pic_pattern         = /^<div class="td-\d+">\s*<img/,
					  avatar_pattern           = /name="Avatar"/,
					  new_topic_avatar_pattern = /<\/select>\s*<br>\s*<script/,
					  no_avatar_pattern        = /club\.germany\.ru/;

				if ( container_well ) {

					for ( let i = 0; i < container_well.length; i++ ) {

						if ( user_pic_pattern.test( container_well[ i ].innerHTML.trim() ) ) {
							container_well[ i ].firstElementChild.remove();
						}

						if ( avatar_pattern.test( container_well[ i ].innerHTML.trim() ) ) {

							if ( container_well[ i ].firstElementChild.classList.contains( 'td-80' ) ) {
								container_well[ i ].firstElementChild.classList.remove( 'td-80' );
							}

							if ( container_well[ i ].firstElementChild.tagName !== 'SCRIPT' ) {
								container_well[ i ].firstElementChild.style.display = 'inline';
								container_well[ i ].firstElementChild.style.padding = '.75em 1em';
							}

							if ( getElements( 'Subject' ) && getElements( 'Subject' )[ 0 ].getAttribute( 'type' ) === 'hidden' ) {
								container_well[ i ].firstElementChild.style.paddingLeft = '0';
							}
						}

						if ( new_topic_avatar_pattern.test( container_well[ i ].innerHTML.trim() ) ) {

							getElement( 'subj' ).nextSibling.remove();
							getElement( 'subj' ).nextElementSibling.remove();

							if ( getElements( 'Icon' ) ) {

								getElements( 'Icon' )[ 0 ].nextElementSibling.remove();

								const spacer = document.createElement( 'span' );

								spacer.setAttribute( 'id', 'avatar-spacer' );
								getElements( 'Icon' )[ 0 ].insertAdjacentElement( 'afterend', spacer );

								container_well[ i ].firstElementChild.style.padding = '0';
							}
						}

						if ( no_avatar_pattern.test( container_well[ i ].innerHTML.trim() ) ) {
							container_well[ i ].firstElementChild.remove();
						}
					}

					let avatar = getElements( "Avatar" );

					if ( avatar ) {
						avatar[ 0 ].parentElement.innerHTML = avatar[ 0 ].parentElement.innerHTML.replace( "onchange=\"showimage()\"", "" );
					}
				}

				if ( getElements( 'Subject' ) && getElements( 'Subject' )[ 0 ].previousElementSibling ) {

					const clear = getElements( 'Subject' )[ 0 ].previousElementSibling.getAttribute( 'class' );

					if ( clear && clear.indexOf( 'clear' ) !== -1 ) {
						getElements( 'Subject' )[ 0 ].previousElementSibling.remove();
					}

					if ( getElements( 'Subject' )[ 0 ].nextElementSibling.tagName === 'BR' ) {
						getElements( 'Subject' )[ 0 ].nextElementSibling.remove();
					}
				}

				if ( comment_to_answer ) {

					comment_to_answer.innerHTML = comment_to_answer.innerHTML.replace( /br>(\s|\S)*?(<br>)/, '/b><br>' );

					let answer = document.createElement( 'span' );

					answer.setAttribute( 'id', 'answer' );
					comment_to_answer.insertBefore( answer, comment_to_answer.firstElementChild );
				}

				const modal_close = getElement( 'modal-symbols-close' );

				for ( let i = 0; i < misc_symbols.length; i++ ) {

					let sml_1 = document.createElement( 'span' );

					sml_1.setAttribute( 'id', 'sml-1-' + i );
					sml_1.setAttribute( 'class', 'sml-1' );
					sml_1.appendChild( document.createTextNode( misc_symbols[ i ] ) );

					modal_close.parentElement.insertBefore( sml_1, modal_close.nextSibling );
				}

				for ( let i = smileys_links.length - 1; i >= 0; i-- ) {

					let sml_2     = document.createElement( 'span' );
					let sml_2_img = document.createElement( 'img' );

					sml_2.setAttribute( 'id', 'sml-2-' + i );
					sml_2.setAttribute( 'class', 'sml-2' );
					sml_2.appendChild( sml_2_img );
					sml_2_img.setAttribute( 'src', smileys_links[ i ] );
					sml_2_img.setAttribute( 'height', '17px' );

					modal_close.parentElement.insertBefore( sml_2, modal_close.nextSibling );
				}

				const sibling_elements = [
					getElements( 'mail' ) ? getElements( 'mail' )[ 0 ].previousElementSibling.previousElementSibling : null,
					getElement( 'markup' ).nextElementSibling
				];

				for ( let i = 0; i < sibling_elements.length; i++ ) {

					if ( sibling_elements[ i ] && sibling_elements[ i ].tagName === 'BR' ) {
						sibling_elements[ i ].remove();
					}
				}

				if ( getElements( 'preview' ) ) {

					if ( getElements( 'preview' )[ 0 ].nextElementSibling && getElements( 'preview' )[ 0 ].nextElementSibling.tagName !== 'BR' ) {
						getElements( 'preview' )[ 0 ].nextElementSibling.insertAdjacentHTML( 'beforebegin', '<br>' );
					}
					else if ( getElements( 'preview' )[ 0 ].parentNode.nextElementSibling && getElements( 'preview' )[ 0 ].parentNode.nextElementSibling.tagName !== 'BR' ) {
						getElements( 'preview' )[ 0 ].parentNode.nextElementSibling.insertAdjacentHTML( 'beforebegin', '<br>' );
					}
				}

				/** Convert tags into mark-up */

				updateTextArea( [ replaceTags, delNewlines, delWhitespaces ] );
				saveState();

				/** Set translate & short links options on */

				checkCookies();

				/** Events handling */

				/** Text area */
				text_area.addEventListener( 'keypress', translateLetter, false );
				text_area.addEventListener( 'input', checkMarkup, true );
				text_area.addEventListener( 'keyup', decolorizeButtons, false );
				text_area.addEventListener( 'focus', delFlag, false );
				text_area.addEventListener( 'click', decolorizeButtons, true );
				text_area.addEventListener( 'select', colorizeButtons, true );
				text_area.addEventListener( 'paste', mkHyperlinks, false );

				/** Decorators replace-function */

				colorize_btns.concat( color_btns )
							 .forEach( ( elem ) =>
									   {
										   getElement( elem ).addEventListener( 'click', decorate, false );
									   } );

				/** Image upload */

				getElement( 'img' ).addEventListener( 'change', fileMonitor, false );
				getElement( 'upload' ).addEventListener( 'click', mkFlag, false );

				/** Smileys handling */

				getElement( 'smiley' ).addEventListener( 'click', mkFlag, false );
				getElement( 'smiley' ).addEventListener( 'click', showSmileys, false );
				getElement( 'preview' ).addEventListener( 'click', showPreview, false );
				getElement( 'modal-symbols-close' ).addEventListener( 'click', defaultState, false );
				getElement( 'modal-preview-close' ).addEventListener( 'click', defaultState, false );

				for ( let i = 0; i < misc_symbols.length; i++ ) {
					getElement( 'sml-1-' + i ).addEventListener( 'click', insertSmiley1, false );
				}

				for ( let i = 0; i < smileys_links.length; i++ ) {
					getElement( 'sml-2-' + i ).addEventListener( 'click', insertSmiley2, false );
				}

				/** Sizing */

				resize_el.addEventListener( 'click', fullSize, false );
				getElement( 'font' ).addEventListener( 'click', resizeFont, false );

				const answer = getElement( 'answer' );

				if ( answer ) {
					answer.addEventListener( 'click', showPrevious, false );
				}

				/** Clear decorators */

				getElement( 'clear' ).addEventListener( 'click', delDecorators, false );

				/** Undo - redo */

				getElement( 'undo' ).addEventListener( 'click', undo, false );
				getElement( 'redo' ).addEventListener( 'click', redo, false );

				/** Submit */

				if ( getElement( 'trtxt' ) ) {
					getElement( 'trfrm' ).addEventListener( 'submit', normalizeTags, false );
				}
				else {
					getElement( 'trfrm-dialog' ).addEventListener( 'submit', normalizeTags, false );
				}

				/** Translate & short links */

				getElement( 'translate' ).addEventListener( 'change', checkBoxVal, false );
				getElement( 'short-links' ).addEventListener( 'change', checkBoxVal, false );
			}

		};

		/**----------------------------------------- Editor's functions -----------------------------------------*/

		let checkCookies = () =>
		{
			if ( getCookie( 'translate' ) !== "" && getCookie( 'translate' ) !== 'off' ) {
				getElement( 'translate' ).checked = true;
			}

			if ( getCookie( 'short-links' ) !== "" && getCookie( 'short-links' ) !== 'off' ) {
				getElement( 'short-links' ).checked = true;
			}
		};


		let setCookie = ( cname, cvalue ) =>
		{
			const currentYear = new Date().getFullYear(),
				  nextYear    = currentYear + 1,
				  expires     = "expires=" + new Date().toUTCString().replace( currentYear.toString(), nextYear.toString() );

			document.cookie = cname + "=" + cvalue + "; " + expires + "; domain=.germany.ru; path=/";
		};


		let getCookie = ( cname ) =>
		{
			const name = cname + "=",
				  ca   = document.cookie.split( ';' );

			for ( let i = 0; i < ca.length; i++ ) {

				let c = ca[ i ];

				while ( c.charAt( 0 ) === ' ' ) {
					c = c.substring( 1 );
				}

				if ( c.indexOf( name ) === 0 ) {
					return c.substring( name.length, c.length );
				}
			}

			return "";
		};


		let checkBoxVal = ( event ) =>
		{
			if ( event ) {

				const check_box = getElement( event.target.id );

				if ( check_box.checked ) {
					setCookie( event.target.id, 'on' );
				}
				else {
					setCookie( event.target.id, 'off' );
				}
			}
		};


		let checkMarkup = () =>
		{
			let corrupted_index = -1, pair_index, corrupted_length, pair_length;

			saveState();

			updateTextArea( [
								( text ) =>
								{
									const links_all = /\[[^\s]*?⇄.*?]/g,
										  links     = /\[\u200c[^\s]*?⇄.*?]\u200c/g,
										  img       = /\[[^\s]*?\.(jpg|png|gif|jpeg|bmp)⇄img]((\(.*?\))[\u200b]|[\u200b])/g,
										  vid       = /\[[^\s]*?⇄vid]/g,
										  text_hist = text_history[ text_history.length - 2 ];

									if ( text_hist.match( links_all ) ) {

										const img_count_hist   = text_hist.match( img ) ? text_hist.match( img ).length : 0,
											  vid_count_hist   = text_hist.match( vid ) ? text_hist.match( vid ).length : 0,
											  links_count_hist = text_hist.match( links ) ? text_hist.match( links ).length : 0,
											  img_count        = text.match( img ) ? text.match( img ).length : 0,
											  vid_count        = text.match( vid ) ? text.match( vid ).length : 0,
											  links_count      = text.match( links ) ? text.match( links ).length : 0;

										let replace_links = ( all, index ) =>
										{
											if ( /\[[^\s]*?⇄vid]/.test( all ) ||
												 /\[\u200c[^\s]*?⇄.*?]\u200c/.test( all ) ||
												 /\[[^\s]*?\.(jpg|png|gif|jpeg|bmp)⇄img](\(.*?\)[\u200b]|[\u200b])/.test( all ) ) {

												return all;
											}

											corrupted_index  = index;
											corrupted_length = all.length;

											return "";
										};

										let text_c = text;

										if ( img_count_hist !== img_count ) {

											text_c = text_c.replace( /\[(?:.|\s)*?\.(?:jpg|png|gif|jpeg|bmp)⇄img](?:\((?:.|\s)*?\)?[\u200b]|[\u200b])/g, replace_links );
											text_c = text_c === text ?
													 text_c.replace( /\[(?:.|\s)*?\.(?:jpg|png|gif|jpeg|bmp)⇄img](?:\(?(?:.|\s)*?\)[\u200b]|[\u200b])/g, replace_links ) : text_c;
											text_c === text ?
											text_c = text_c.replace( /\[(?:.|\s)*?\.(?:jpg|png|gif|jpeg|bmp)⇄?img](?:\((?:.|\s)*?\)[\u200b]|[\u200b])/g, replace_links ) : text_c;
											text_c === text ?
											text_c = text_c.replace( /\[(?:.|\s)*?\.(?:jpg|png|gif|jpeg|bmp)⇄(?:.|\s)*?](?:\((?:.|\s)*?\)[\u200b]|[\u200b])/g, replace_links ) : text_c;
											text_c = text_c === text ?
													 text_c.replace( /\[?(?:.|\s)*?\.(?:jpg|png|gif|jpeg|bmp)⇄img](?:\((?:.|\s)*?\)[\u200b]|[\u200b])/g, replace_links ) : text_c;
											text_c = text_c === text ?
													 text_c.replace( /\[(?:.|\s)*?\.(?:jpg|png|gif|jpeg|bmp)⇄img]?(?:\((?:.|\s)*?\)[\u200b]|[\u200b])/g, replace_links ) : text_c;
											text_c = text_c === text ?
													 text_c.replace( /\[(?:.|\s)*?\.(?:jpg|png|gif|jpeg|bmp)⇄img](?:\((?:.|\s)*?\)[\u200b]?|[\u200b]?)/g, replace_links ) : text_c;
											text_c === text ?
											text_c.replace( /\[(?:.|\s)*?⇄img](?:\((?:.|\s)*?\)[\u200b]|[\u200b])/g, replace_links ) : text_c;
										}
										else if ( vid_count_hist !== vid_count ) {

											text_c = text_c.replace( /\[(?:.|\s)*?⇄?vid]/g, replace_links );
											text_c = text_c === text ?
													 text_c.replace( /\[?(?:.|\s)*?⇄vid]/g, replace_links ) : text_c;
											text_c = text_c === text ?
													 text_c.replace( /\[(?:.|\s)*?⇄(?:.|\s)*?](?!\((?:.|\s)*?\)[\u200b]|[\u200b]|[\u200c])/g, replace_links ) : text_c;
											text_c === text ?
											text_c.replace( /\[(?:.|\s)*?⇄vid]?/g, replace_links ) : text_c;
										}
										else if ( links_count_hist !== links_count ) {

											text_c = text_c.replace( /\[\u200c(?:.|\s)*?⇄(?:.|\s)*?]\u200c?/g, replace_links );
											text_c = text_c === text ?
													 text_c.replace( /\[\u200c(?:.|\s)*?⇄?(?:.|\s)*?]\u200c/g, replace_links ) : text_c;
											text_c = text_c === text ?
													 text_c.replace( /\[\u200c(?:.|\s)*?⇄(?:.|\s)*?\u200c/g, replace_links ) : text_c;
											text_c = text_c === text ?
													 text_c.replace( /\[?\u200c(?:.|\s)*?⇄(?:.|\s)*?]\u200c/g, replace_links ) : text_c;
											text_c = text_c === text ?
													 text_c.replace( /\[[^\u200c]*?\u200c(?:.|\s)*?⇄(?:.|\s)*?]\u200c/g, replace_links ) : text_c;
											text_c === text ?
											text_c.replace( /\[[^\u200c]*?⇄(?:.|\s)*?]\u200c/g, replace_links ) : text_c;
										}

										if ( corrupted_index !== -1 ) {

											return text.substring( 0, corrupted_index ) +
												   text.substring( corrupted_index + corrupted_length );
										}
									}

									const single_letter_opening      = /\[[\u200b]([suib])[\u200b]]/g,
										  single_letter_closing      = /\[[\u200b]\/([suib])[\u200b]]/g,
										  single_letter_opened_count = text.match( single_letter_opening ) ? text.match( single_letter_opening ).length : 0,
										  single_letter_closed_count = text.match( single_letter_closing ) ? text.match( single_letter_closing ).length : 0,
										  multi_letter_opening       = /\[(red|blue|black|green|quote|pre)]/g,
										  multi_letter_closing       = /\[\/(red|blue|black|green|quote|pre)]/g,
										  multi_letter_opened_count  = text.match( multi_letter_opening ) ? text.match( multi_letter_opening ).length : 0,
										  multi_letter_closed_count  = text.match( multi_letter_closing ) ? text.match( multi_letter_closing ).length : 0;

									if ( single_letter_opened_count === single_letter_closed_count && multi_letter_opened_count === multi_letter_closed_count ) {
										return text;
									}

									let corrupted, pair_is_closing;

									if ( single_letter_opened_count !== single_letter_closed_count || multi_letter_opened_count !== multi_letter_closed_count ) {

										const decorators = [ 's', 'u', 'i', 'b', 'red', 'blue', 'black', 'green', 'quote', 'pre' ];

										for ( let i = 0; i < decorators.length; i++ ) {

											let opening_all, closing_all, opening_and_closing, opening, closing;

											if ( decorators[ i ].length === 1 ) {
												opening_all         = new RegExp( "\\[\u200b" + decorators[ i ] + "\u200b]", "g" );
												closing_all         = new RegExp( "\\[\u200b\\/" + decorators[ i ] + "\u200b]", "g" );
												opening_and_closing = new RegExp( "\\[\u200b\\/?" + decorators[ i ] + "\u200b]", "g" );
												opening             = new RegExp( "\\[\u200b" + decorators[ i ] + "\u200b]" );
												closing             = new RegExp( "\\[\u200b\\/" + decorators[ i ] + "\u200b]" );
											}
											else {
												opening_all         = new RegExp( "\\[" + decorators[ i ] + "]", "g" );
												closing_all         = new RegExp( "\\[\\/" + decorators[ i ] + "]", "g" );
												opening_and_closing = new RegExp( "\\[\\/?" + decorators[ i ] + "]", "g" );
												opening             = new RegExp( "\\[" + decorators[ i ] + "]" );
												closing             = new RegExp( "\\[\\/" + decorators[ i ] + "]" );
											}

											const opened = text.match( opening_all ) ? text.match( opening_all ).length : 0,
												  closed = text.match( closing_all ) ? text.match( closing_all ).length : 0;

											if ( opened === closed ) {
												continue;
											}

											let match, ii = 0,
												pair_ids  = [];

											while ( ( match = opening_and_closing.exec( text ) ) !== null && pair_index === undefined ) {

												pair_ids.push( match.index );

												if ( opened - closed === 2 ) {

													if ( opening.test( match[ 0 ] ) && ii % 2 !== 0 ) {

														corrupted_index  = match.index;
														pair_index       = pair_ids[ pair_ids.length - 2 ];
														corrupted_length = decorators[ i ].length > 2 ? decorators[ i ].length + 2 : 5;
														pair_length      = corrupted_length;
														break;

													}
												}
												else if ( opening.test( match[ 0 ] ) && ii % 2 !== 0 ) {

													pair_index  = pair_ids[ pair_ids.length - 2 ];
													pair_length = decorators[ i ].length > 2 ? decorators[ i ].length + 2 : 5;
													break;
												}
												else if ( closing.test( match[ 0 ] ) && ii % 2 === 0 ) {

													pair_index  = pair_ids[ pair_ids.length - 1 ];
													pair_length = decorators[ i ].length > 2 ? decorators[ i ].length + 3 : 6;
													break;
												}

												ii++;
											}

											if ( pair_index === undefined && pair_ids.length > 0 ) {
												pair_index = pair_ids[ pair_ids.length - 1 ];
											}

											if ( opened > closed ) {

												corrupted = '/' + decorators[ i ];

												if ( !pair_length ) {
													pair_length = decorators[ i ].length > 2 ? decorators[ i ].length + 2 : 5;
												}
												break;
											}
											else if ( closed > opened ) {

												pair_is_closing = true;
												corrupted       = decorators[ i ];

												if ( !pair_length ) {
													pair_length = decorators[ i ].length > 2 ? decorators[ i ].length + 3 : 6;
												}
												break;
											}
										}
									}

									let replace_decorators = ( all, index ) =>
									{
										if ( new RegExp( "(\\[\u200b" + corrupted.replace( '/', '' ) + "\u200b])" ).test( all ) ||
											 new RegExp( "(\\[\u200b\\/" + corrupted.replace( '/', '' ) + "\u200b])" ).test( all ) ||
											 new RegExp( "(\\[" + corrupted.replace( '/', '' ) + "])" ).test( all ) ||
											 new RegExp( "(\\[\\/" + corrupted.replace( '/', '' ) + "])" ).test( all ) ) {

											return all;
										}

										corrupted_index  = index;
										corrupted_length = all.length;

										return "";
									};

									if ( corrupted_index === -1 ) {

										let text_c = text;

										if ( corrupted.length <= 2 ) {
											text_c = text_c.replace( new RegExp( "\\[[^\u200b]*?\u200b" + corrupted + "\u200b]", "g" ), replace_decorators );
											text_c = text_c === text ?
													 text_c.replace( new RegExp( "\\[[^\u200b]*?" + corrupted + "\u200b]", "g" ), replace_decorators ) : text_c;
											text_c = text_c === text ?
													 text_c.replace( new RegExp( "\\[\u200b[^s|u|i|b]*?" + corrupted + "\u200b]", "g" ), replace_decorators ) : text_c;
											text_c = text_c === text ?
													 text_c.replace( new RegExp( "\\[\u200b\\/[^s|u|i|b]*?" + corrupted.replace( '/', '' ) + "\u200b]", "g" ), replace_decorators ) : text_c;
											text_c = text_c === text ?
													 text_c.replace( new RegExp( "\\[\u200b" + corrupted + "[^\u200b]*?\u200b]", "g" ), replace_decorators ) : text_c;
											text_c = text_c === text ?
													 text_c.replace( new RegExp( "\\[\u200b" + corrupted + "[^\u200b]*?]", "g" ), replace_decorators ) : text_c;
											text_c = text_c === text ?
													 text_c.replace( new RegExp( "\\[\u200b" + corrupted + "\u200b]?", "g" ), replace_decorators ) : text_c;
											text_c = text_c === text ?
													 text_c.replace( new RegExp( "\\[?\u200b" + corrupted + "\u200b]", "g" ), replace_decorators ) : text_c;
											text_c = text_c === text ?
													 text_c.replace( new RegExp( "\\[\u200b\u200b]", "g" ), replace_decorators ) : text_c;
											text_c = text_c === text ?
													 text_c.replace( new RegExp( "\\[\u200b\\/\u200b]", "g" ), replace_decorators ) : text_c;
											text_c === text ?
											text_c.replace( new RegExp( "\\[\u200b" + corrupted + "[\u200b]]?", "g" ), replace_decorators ) : text_c;
										}
										else {
											if ( !pair_is_closing ) {
												corrupted = corrupted.replace( '/', '' );
											}

											let i = corrupted.length - 1;

											while ( i >= 1 && text_c === text ) {
												text_c = text_c.replace(
													new RegExp( "\\[\\/?" + corrupted.replace( corrupted.charAt( i ), "(?:.|\\s)*?" ) + "]", "g" ), replace_decorators );
												i--;
											}

											text_c = text_c === text ?
													 text_c.replace( new RegExp( "\\[\\/?[^" + corrupted.charAt( 0 ) + "]{1,3}" + corrupted.charAt( 0 ) + "?" + corrupted.substr( 1 ) + "]", "g" ),
																	 replace_decorators ) : text_c;
											text_c = text_c === text ?
													 text_c.replace( new RegExp( "\\[[^\\/]*?\\/?" + corrupted + "]?", "g" ), replace_decorators ) : text_c;
											text_c = text_c === text ?
													 text_c.replace( new RegExp( "\\[\\/?" + corrupted + "]?", "g" ), replace_decorators ) : text_c;
											text_c === text ?
											text_c.replace( new RegExp( "\\[?\\/?" + corrupted + "]", "g" ), replace_decorators ) : text_c;
										}
									}

									if ( corrupted_index === -1 ) {

										if ( pair_index === undefined ) {
											return text;
										}
										else {
											return text.substring( 0, pair_index ) +
												   text.substring( pair_index + pair_length );
										}

									}
									else if ( pair_is_closing ) {
										return text.substring( 0, corrupted_index ) +
											   text.substring( corrupted_index + corrupted_length, pair_index ) +
											   text.substring( pair_index + pair_length );
									}
									else {
										return text.substring( 0, pair_index ) +
											   text.substring( pair_index + pair_length, corrupted_index ) +
											   text.substring( corrupted_index + corrupted_length );
									}
								}
							] );

			const previous = text_history[ text_history.length - 1 ],
				  diff     = previous.length - text_area.value.length;

			if ( diff !== 0 ) {

				text_history.splice( text_history.length - 1 );
				saveState();

				for ( let i = text_area.value.length - 1; i >= 0; i-- ) {
					if ( text_area.value.charAt( i ) !== previous.charAt( i + diff ) ) {
						corrupted_index = i + 1;
						break;
					}
				}
			}

			setCursor( corrupted_index );
		};


		let updateTextArea = ( callback ) =>
		{
			let text         = text_area.value,
				text_initial = text;
			const args       = [ ...callback ];

			for ( let i = 0; i < args.length; i++ ) {

				if ( typeof args[ i ] === 'function' ) {
					text = args[ i ].call( args[ i ], text.toString() );
				}
			}

			if ( text_initial !== text ) {
				text_area.value = text;
			}
		};


		let replaceTags = ( text ) =>
		{
			for ( let i = 0; i < smileys_links.length; i++ ) {

				const smiley           = smileys_links[ i ].substring( 46, smileys_links[ i ].length - 4 ),
					  smiley_link_patt = new RegExp( '<img src=\\"http[s]?:\\/\\/tt\\.germany\\.ru\\/wwwthreads\\/images\\/icons\\/' + smiley + '\\.gif\\"\\s\\/>', 'gi' );

				text = text.replace( smiley_link_patt, '[' + smiley + ']' );
			}

			return text
				.replace( /<font color="(red|blue|black|green)">((?:.|\s)*?)<\/font>/gi, '[$1]$2[/$1]' )
				.replace( /<blockquote>(<q>)?/gi, '[quote]' )
				.replace( /(<\/q>)?<\/blockquote>(<br \/>)?/gi, '[/quote]\n' )
				.replace( /<a(?=.*href)/gi, '[\u200c' )
				.replace( /"?\s?><\/iframe>/gi, '⇄vid]' )
				.replace( /"\s?>(?=.*<\/a>)/gi, '⇄' )
				.replace( /<\/a>/gi, ']\u200c' )
				.replace( /target="_\w+"?|href="?|src="?|width="\d{3}"?(?!(%|px))|height="\d{3}"?(?!(%|px))|frameborder="\d+"?/gi, '' )
				.replace( /(<img|<iframe)/gi, '[' )
				.replace( /\[(?![\u200c])\s+(?![\u200c])(?=http.*|ftp.*)/gi, '[' )
				.replace( /\[[\u200c]?\s+[\u200c]?(?=http.*|ftp.*)/gi, '[\u200c' )
				.replace( /<(br \/|br|p)>/gi, '\n' )
				.replace( /<\/p>/gi, '' )
				.replace( /"\swidth="(?=(\d+(%|px)))/gi, '⇄img](' )
				.replace( /px"\s?\/?>/g, 'px)\u200b' )
				.replace( /%"\s?\/?>/g, '%)\u200b' )
				.replace( /"\s?\/?>/g, '⇄img](auto)\u200b' )
				.replace( /</gi, '[\u200b' )
				.replace( />/gi, '\u200b]' )
				.replace( /"\s⇄/g, '⇄' );
		};


		let normalizeTags = ( event ) =>
		{
			if ( getElements( 'Subject' ) && getElements( 'Subject' )[ 0 ].value.trim() === "" ) {

				if ( getElement( 'subj' ) ) {
					getElement( 'subj' ).classList.remove( 'error' );
				}

				event.stopPropagation();
				event.preventDefault();
			}

			updateTextArea( [ delNewlines, delWhitespaces, normalize ] );
		};


		let normalize = ( text ) =>
		{
			text = text
				.replace( /\[(red|blue|black|green)]/gi, '<font color="$1">' )
				.replace( /\[\/(red|blue|black|green)]/gi, '</font>' )
				.replace( /\[quote]/gi, '<blockquote><q>' )
				.replace( /\[\/quote]\n?/gi, '</q></blockquote>' )
				.replace( /\[(\/?pre)]/gi, '<$1>' )
				.replace( /(\[https:\/\/player\.vimeo\.com\/video\/)/gi, '<iframe width="560" height="315" frameborder="0" src="https://player.vimeo.com/video/' )
				.replace( /(\[https:\/\/www\.youtube\.com\/embed\/)/gi, '<iframe width="560" height="315" frameborder="0" src="https://www.youtube.com/embed/' )
				.replace( /(\[https:\/\/ok\.ru\/videoembed\/)/gi, '<iframe width="560" height="315" frameborder="0" src="https://ok.ru/videoembed/' )
				.replace( /⇄vid]/gi, '"></iframe>' )
				.replace( /\[(?=.*\.(jpg|png|gif|jpeg|bmp))(?![\u200c])/gi, '<img src="' )
				.replace( /⇄img]\((\d+(%|px))\)[\u200b]/gi, '" width="$1">' )
				.replace( /⇄img]\(.*?\)[\u200b]/gi, '">' )
				.replace( /⇄img][\u200b]/gi, '">' )
				.replace( /\[[\u200c]/g, '<a target="_blank" href="' )
				.replace( /⇄(.*?)]\u200c/g, '">$1</a>' )
				.replace( /⇄[\s].*?]\u200c/g, '">link</a>' )
				.replace( /><\/a>/g, '">link</a>' )
				.replace( /\[[\u200b]/g, '<' )
				.replace( /[\u200b]]/g, '>' )
				.replace( /\n/gi, '<br>' )
				.replace( /⚐/g, '' );

			for ( let i = 0; i < smileys_links.length; i++ ) {

				const smiley_patt = new RegExp( '\\[' + smileys_links[ i ].substring( 46, smileys_links[ i ].length - 4 ) + '\\]', 'gi' );
				text              = text.replace( smiley_patt, '<img src="' + smileys_links[ i ] + '" />' );
			}

			return text;
		};


		let fullSize = () =>
		{
			const resize_state = getElement( 'resize' ).innerHTML;

			if ( resize_state === "↗" ) {

				text_area.classList.add( 'text-area-full' );
				getElement( 'markup' ).classList.add( 'markup-full' );
				getElement( 'resize' ).classList.add( 'resize-el-full' );
				getElement( 'preview' ).classList.add( 'preview-full' );
				getElement( 'resize' ).innerHTML = "&#x2199;";
				styleElement( getElement( 'font' ), { 'display': 'inline' } );
				styleElement( getElement( 'font' ).children[ 0 ], { 'fontSize': '16px' } );
				styleElement( getElement( 'redo' ).parentElement, { 'marginRight': '85px' } );
				getElements( "FOOTER" ) ? styleElement( getElements( "FOOTER" )[ 0 ], { 'display': 'none' } ) : null;
				getElements( "BODY" ) ? styleElement( getElements( "BODY" )[ 0 ], { 'overflowX': 'hidden' } ) : null;

				const answer = getElement( 'answer' );

				if ( answer && comment_to_answer ) {

					answer.innerHTML = "&#x25B2;";
					answer.classList.add( 'answer-full' );
					comment_to_answer.classList.add( 'comment-to-answer-full' );
					styleElement( text_area, { 'height': '88vh' } );
				}
				else {
					styleElement( text_area, { 'height': '94vh' } );
				}

				const native_buttons = getElements( 'btn' );

				if ( native_buttons ) {

					for ( let i = 0; i < native_buttons.length; i++ ) {
						styleElement( native_buttons[ i ], { 'position': 'static' } );
					}
				}

				if ( container_well && container_wrap ) {

					styleElement( container_well[ container_well.length - 1 ], { 'position': 'fixed', 'top': '0', 'left': '0', 'backgroundColor': '#fff', 'height': '100vw' } );
					styleElement( container_wrap[ container_wrap.length - 2 ], { 'position': 'fixed' } );
				}
			}
			else {
				normalSize();
			}

			setCursor();
		};


		let normalSize = () =>
		{
			text_area.classList.remove( 'text-area-full' );
			styleElement( text_area );
			getElement( 'markup' ).classList.remove( 'markup-full' );
			getElement( 'preview' ).classList.remove( 'preview-full' );
			getElement( 'resize' ).classList.remove( 'resize-el-full' );
			getElement( 'resize' ).innerHTML = "&#x2197";

			styleElement( getElement( 'font' ) );
			styleElement( getElement( 'font' ).children[ 0 ] );
			styleElement( getElement( 'redo' ).parentElement );
			getElements( "FOOTER" ) ? styleElement( getElements( "FOOTER" )[ 0 ] ) : null;
			getElements( "BODY" ) ? styleElement( getElements( "BODY" )[ 0 ] ) : null;

			if ( comment_to_answer ) {

				comment_to_answer.classList.remove( 'comment-to-answer-full', 'comment-to-answer-opened' );
				getElement( 'answer' ).classList.remove( 'answer-full', 'answer-opened' );
			}

			if ( container_well && container_wrap ) {

				styleElement( container_well[ container_well.length - 1 ] );
				styleElement( container_well[ container_well.length - 2 ] );
			}
		};


		let showPrevious = () =>
		{
			const previous_state = getElement( 'answer' ).innerHTML,
				  answer         = getElement( 'answer' );

			if ( previous_state === '▲' ) {

				text_area.style.height = '60vh';
				answer.innerHTML       = "&#x25BC;";
				answer.classList.replace( 'answer-full', 'answer-opened' );
				comment_to_answer.classList.replace( 'comment-to-answer-full', 'comment-to-answer-opened' );
			}
			else {
				text_area.style.height = '88vh';
				answer.innerHTML       = "&#x25B2;";
				answer.classList.replace( 'answer-opened', 'answer-full' );
				comment_to_answer.classList.replace( 'comment-to-answer-opened', 'comment-to-answer-full' );
			}

			setCursor();
		};


		let resizeFont = () =>
		{
			const font_size = window.getComputedStyle( text_area, null ).getPropertyValue( 'font-size' );

			switch ( parseFloat( font_size ) ) {
				case 16:
					styleElement( text_area, { 'fontSize': '18px' } );
					styleElement( getElement( 'font' ).children[ 0 ], { 'fontSize': '18px' } );
					break;
				default:
					styleElement( text_area, { 'fontSize': '16px' } );
					styleElement( getElement( 'font' ).children[ 0 ], { 'fontSize': '16px' } );
			}

			setCursor();
		};


		let decorate = ( event ) =>
		{
			const button_id = event.target.getAttribute( 'id' );

			if ( button_id ) {

				if ( !isSelected() ) {

					errorMsg( "no_text" );
				}
				else {

					const identical_pattern    = /^(red|blue|green|black|quote|pre)$/,
						  first_letter_pattern = /^(strike|underline|italic|bold)$/;

					if ( identical_pattern.test( button_id ) ) {

						replace( '[' + button_id + ']', '[/' + button_id + ']' );
					}
					else if ( first_letter_pattern.test( button_id ) ) {

						replace( '[\u200b' + button_id.substr( 0, 1 ) + '\u200b]', '[\u200b/' + button_id.substr( 0, 1 ) + '\u200b]' );
					}
					else if ( button_id === 'l-quote' ) {

						replace( '«', '»' );
					}
				}
			}
		};


		let isSelected = () =>
		{
			let selected = false;

			if ( text_area.selectionStart !== null ) {

				const start_pos = text_area.selectionStart,
					  end_pos   = text_area.selectionEnd;

				if ( end_pos - start_pos > 0 && text_area.value.substr( start_pos, end_pos ).trim() !== '' ) {
					selected = true;
				}
			}

			return selected;
		};


		let errorMsg = ( msg ) =>
		{
			const error = getElement( 'error' );

			switch ( msg ) {
				case "no_text":
					error.innerHTML = "Выделите сначала текст!";
					break;
				case "false_img_file":
					error.innerHTML = "Неподходящее изображение!";
					break;
				case "upload_broken":
					error.innerHTML = "Ошибка загрузки изображения :(";
					break;
				case "wrong_cursor_position":
					error.innerHTML = "Курсор внутри спец. разметки!";
					break;
				default:
					error.innerHTML = "Что-то пошло не так :(";
			}

			setTimeout( () =>
						{
							error.innerHTML = "";
						}, 4000 );

		};


		function isVID( str )
		{
			const youtube = /^https:\/\/www\.youtube\.com\/watch\?v=([a-zA-Z0-9-_]){10,11}(&.*)?$/,
				  vimeo   = /^https:\/\/vimeo.com\/\d{4,}$/,
				  ok      = /^https:\/\/ok\.ru\/video\/\d{4,}$/;

			return youtube.test( str ) || vimeo.test( str ) || ok.test( str );
		}


		function isIMG( str )
		{
			const imgRegex = /^(?:http[s]?:\/\/(?:www\.)?|ftp:\/\/(?:www\.)?|www\.)(?:[0-9A-Za-z-.@:%_+~#=]+)+(?:(?:\.[a-zA-Z]{2,3})+)(?:.*)?(?:\.(?:jpg|png|gif|jpeg|bmp))$/i;

			return str.length < 2083 && imgRegex.test( str );
		}


		function isURL( str )
		{
			const urlRegex = /^(?:http[s]?:\/\/(?:www\.)?|ftp:\/\/(?:www\.)?|www\.)(?:[0-9A-Za-z-.@:%_+~#=]+)+(?:(?:\.[a-zA-Z]{2,3})+)(?:.*)?/i;

			return str.length < 2083 && urlRegex.test( str );
		}


		function mkShortUrl( callback, inputValue )
		{
			const fn = this;

			if ( typeof callback === 'function' ) {

				if ( inputValue.length > 25 && !fn._memorised.bind( fn, inputValue )() ) {

					const service = 'http://is.gd/api.php?longurl=%URL%',

						  _uri    = service.replace( '%URL%', encodeURIComponent( inputValue ) );

					GM.xmlHttpRequest( {
										   method : "GET",
										   url    : _uri,
										   headers: {
											   'User-Agent': 'Mozilla/4.0 (compatible) Greasemonkey',
											   'Accept'    : 'application/atom+xml,application/xml,text/xml'
										   },

										   onload: ( response ) =>
										   {
											   callback( response.responseText );
										   },

										   onerror: () =>
										   {
											   callback( inputValue );
										   }

									   } );
				}
				else {
					callback( fn._memorised.bind( fn, inputValue )() || inputValue );
				}
			}
		}


		let mkHyperlinks = ( event ) =>
		{
			if ( event ) {

				const inputValue = event.clipboardData.getData( 'Text' ).trim();

				if ( inputValue ) {

					isIMG._resolve.bind( isIMG, ( is_img ) =>
					{
						if ( is_img ) {

							event.preventDefault();

							insert( '[' + inputValue + '⇄img](auto)\u200b', event );
						}
						else {
							isVID._resolve.bind( isVID, ( is_vid ) =>
							{
								if ( is_vid ) {

									event.preventDefault();

									const start = inputValue.indexOf( '=' ),
										  end   = inputValue.indexOf( '&' );

									if ( end !== -1 ) {
										insert( '[https://www.youtube.com/embed/' + inputValue.substr( start + 1, ( end - start - 1 ) ) + '⇄vid]', event );
									}
									else if ( start !== -1 ) {
										insert( '[https://www.youtube.com/embed/' + inputValue.substr( start + 1 ) + '⇄vid]', event );
									}
									else if ( /vimeo/.test( inputValue ) ) {
										insert( '[https://player.vimeo.com/video/' + inputValue.substr( 18 ) + '⇄vid]', event );
									}
									else if ( /ok\.ru/.test( inputValue ) ) {
										insert( '[https://ok.ru/videoembed/' + inputValue.substr( 20 ) + '⇄vid]', event );
									}
								}
								else {
									isURL._resolve.bind( isURL, ( is_url ) =>
									{
										if ( is_url ) {

											event.preventDefault();

											if ( getElement( 'short-links' ).checked ) {

												mkShortUrl.bind( mkShortUrl, ( url ) =>
												{
													mkShortUrl._memorise.bind( mkShortUrl, inputValue, url )();

													if ( isSelected() ) {
														replace( '[\u200c' + ( isURL( url ) ? url : inputValue ) + '⇄', ']\u200c' );
													}
													else {
														insert( '[\u200c' + ( isURL( url ) ? url : inputValue ) + '⇄link]\u200c', event );
													}
												}, inputValue )();
											}
											else {

												if ( isSelected() ) {
													replace( '[\u200c' + inputValue + '⇄', ']\u200c' );
												}
												else {
													insert( '[\u200c' + inputValue + '⇄link]\u200c', event );
												}
											}
										}
									}, inputValue )();
								}
							}, inputValue )();
						}
					}, inputValue )();
				}
			}
		};


		let fileMonitor = ( event ) =>
		{
			try {
				const file_obj = getElement( "img" ),
					  formData = new FormData( getElement( 'upload' ) );

				if ( formData && file_obj && file_obj.files.length > 0 && ( /image\/jpg|png|gif|jpeg|bmp/i ).test( file_obj.files[ 0 ].type ) ) {

					formData.append( "file", file_obj.files[ 0 ] );

					upload( formData, event );
				}
				else {
					errorMsg( "false_img_file" );
				}
			}
			catch ( e ) {
				errorMsg( "upload_broken" );
			}
		};


		let upload = ( image, event ) =>
		{
			try {
				GM.xmlHttpRequest( {
									   method: "POST",
									   url   : "https://h.germany.ru/abogat/r/upload.php?hash=" + getCookie( 'session' ),
									   data  : image,

									   onload: ( response ) =>
									   {
										   const msg_obj = JSON.parse( response.responseText ),
												 msg     = msg_obj.filelink.toString()
																  .replace( '/\\/g', '' )
																  .replace( '/"/g', '' );
										   msg ? insert( '[' + msg + '⇄img](auto)\u200b', event ) : errorMsg( "upload_broken" );
									   },

									   onerror: () =>
									   {
										   errorMsg( "upload_broken" );
									   }
								   } );
			}
			catch ( e ) {
				errorMsg( "upload_broken" );
			}
		};


		let undo = () =>
		{
			let cursor = text_area.selectionEnd;

			if ( text_history.length - 1 > undo_clicks ) {

				undo_clicks++;

				getElement( 'redo' ).style.color = 'inherit';
				getElement( 'redo' ).disabled    = false;

				if ( undo_clicks === text_history.length - 1 ) {
					getElement( 'undo' ).style.color = '#999999';
					getElement( 'undo' ).disabled    = true;
				}

				updateTextArea( [
									() =>
									{
										const previous = text_history[ text_history.length - undo_clicks - 1 ],
											  diff     = text_area.value.length - previous.length;

										for ( let i = previous.length - 1; i >= 0; i-- ) {
											if ( previous.charAt( i ) !== text_area.value.charAt( i + diff ) ) {
												cursor = i + 1;
												break;
											}
										}

										return previous;
									}
								] );
			}

			setCursor( cursor );
		};


		let redo = () =>
		{
			let cursor = text_area.selectionEnd;

			if ( undo_clicks > 0 ) {

				undo_clicks--;

				getElement( 'undo' ).style.color = 'inherit';
				getElement( 'undo' ).disabled    = false;

				if ( undo_clicks === 0 ) {
					getElement( 'redo' ).style.color = '#999999';
					getElement( 'redo' ).disabled    = true;
				}

				updateTextArea( [
									() =>
									{
										const previous = text_history[ text_history.length - undo_clicks - 1 ],
											  diff     = text_area.value.length - previous.length;

										for ( let i = previous.length - 1; i >= 0; i-- ) {
											if ( previous.charAt( i ) !== text_area.value.charAt( i + diff ) ) {
												cursor = i + 1;
												break;
											}
										}

										return previous;
									}
								] );
			}

			setCursor( cursor );
		};


		let colorizeButtons = () =>
		{
			if ( isSelected() ) {

				const selected_text      = text_area.value.substring( text_area.selectionStart, text_area.selectionEnd ),
					  after_selection    = text_area.value.substring( text_area.selectionEnd ),
					  before_selection   = text_area.value.substring( 0, text_area.selectionStart ),
					  decorator_patterns = [
						  /\[[\u200b]\/?([suib])[\u200b]]/g,
						  /\[\/?(red|blue|black|green|quote|pre)]/g,
						  /\[(smile|frown|blush|cool|crazy|laugh|mad|shocked|tongue|wink|up|down|kiss|flower|glass|heart)]/g,
						  /\[.*?\.(jpg|png|gif|jpeg|bmp)⇄img]((\(((\d+(%|px))|auto)\))[\u200b]|[\u200b])/g,
						  /\[.*?⇄.*?]/g
					  ];
				let colorize             = true;

				selected_decorators = new Map();

				let noteDeletableDecorators = ( letter_pattern, outer_before, outer_after, partial_outer ) =>
				{
					let match, index, length;

					while ( ( match = letter_pattern.exec( text_area.value ) ) !== null ) {

						if ( outer_before ) {

							if ( match.index <= text_area.selectionStart ) {

								index  = match.index;
								length = match[ 0 ].length;
							}
							else {
								break;
							}
						}
						else if ( outer_after ) {

							if ( match.index >= text_area.selectionEnd ) {

								index  = match.index;
								length = match[ 0 ].length;

								break;
							}
						}
						else if ( partial_outer ) {

							if ( match.index <= text_area.selectionEnd ) {

								index  = match.index;
								length = match[ 0 ].length;
							}
							else {
								break;
							}
						}
						else {

							if ( match.index >= text_area.selectionStart && match.index <= text_area.selectionEnd ) {

								index  = match.index;
								length = match[ 0 ].length;
							}
						}
					}

					if ( index !== undefined && length !== undefined ) {

						getElement( 'clear' ).disabled    = false;
						getElement( 'clear' ).style.color = 'inherit';

						selected_decorators.set( index, length );
					}
				};

				const decorators = [ 's', 'u', 'i', 'b', 'red', 'blue', 'black', 'green', 'quote', 'pre' ];

				for ( let i = 0; i < decorators.length; i++ ) {

					let opening_all, closing_all, opening_and_closing, opening, closing;

					if ( decorators[ i ].length === 1 ) {
						opening_all         = new RegExp( "\\[\u200b" + decorators[ i ] + "\u200b]", "g" );
						closing_all         = new RegExp( "\\[\u200b\\/" + decorators[ i ] + "\u200b]", "g" );
						opening_and_closing = new RegExp( "\\[\u200b\\/?" + decorators[ i ] + "\u200b]", "g" );
						opening             = new RegExp( "\\[\u200b" + decorators[ i ] + "\u200b]" );
						closing             = new RegExp( "\\[\u200b\\/" + decorators[ i ] + "\u200b]" );
					}
					else {
						opening_all         = new RegExp( "\\[" + decorators[ i ] + "]", "g" );
						closing_all         = new RegExp( "\\[\\/" + decorators[ i ] + "]", "g" );
						opening_and_closing = new RegExp( "\\[\\/?" + decorators[ i ] + "]", "g" );
						opening             = new RegExp( "\\[" + decorators[ i ] + "]" );
						closing             = new RegExp( "\\[\\/" + decorators[ i ] + "]" );
					}

					const opened_inner = selected_text.match( opening_all ) ? selected_text.match( opening_all ).length : 0,
						  closed_inner = selected_text.match( closing_all ) ? selected_text.match( closing_all ).length : 0;

					if ( opened_inner === closed_inner && opened_inner !== 0 ) {

						noteDeletableDecorators( opening_all );
						noteDeletableDecorators( closing_all );
					}
					else if ( opened_inner > closed_inner ) {
						noteDeletableDecorators( opening_all );
						noteDeletableDecorators( closing_all, false, true );
					}
					else if ( opened_inner < closed_inner ) {
						noteDeletableDecorators( closing_all );
					}

					const opened_outer_before = before_selection.match( opening_all ) ? before_selection.match( opening_all ).length : 0,
						  closed_outer_before = before_selection.match( closing_all ) ? before_selection.match( closing_all ).length : 0,
						  opened_outer_after  = after_selection.match( opening_all ) ? after_selection.match( opening_all ).length : 0,
						  closed_outer_after  = after_selection.match( closing_all ) ? after_selection.match( closing_all ).length : 0;

					if ( opened_outer_before !== 0 ) {

						if ( ( opened_outer_before + closed_outer_before ) % 2 !== 0 ) {
							noteDeletableDecorators( opening_all, true );
						}
					}

					if ( closed_outer_after !== 0 ) {

						if ( ( opened_outer_after + closed_outer_after ) % 2 !== 0 ) {
							noteDeletableDecorators( closing_all, false, true );
						}
					}
				}


				for ( let i = 0; i < decorator_patterns.length; i++ ) {

					const all_count    = text_area.value.match( decorator_patterns[ i ] ) ? text_area.value.match( decorator_patterns[ i ] ).length : 0,
						  before_count = before_selection.match( decorator_patterns[ i ] ) ? before_selection.match( decorator_patterns[ i ] ).length : 0,
						  within_count = selected_text.match( decorator_patterns[ i ] ) ? selected_text.match( decorator_patterns[ i ] ).length : 0,
						  after_count  = after_selection.match( decorator_patterns[ i ] ) ? after_selection.match( decorator_patterns[ i ] ).length : 0;

					if ( ( before_count + within_count + after_count ) !== all_count ) {

						if ( i < 2 ) {

							noteDeletableDecorators( decorator_patterns[ i ], true );
							noteDeletableDecorators( new RegExp( decorator_patterns[ i ].source, "g" ), false, false, true );
						}

						colorize = false;
						break;
					}
				}

				if ( !colorize ) {

					for ( let i = 0; i < colorize_btns.length; i++ ) {
						getElement( colorize_btns[ i ] ).style.color = '#999999';
						getElement( colorize_btns[ i ] ).disabled    = true;
					}

					for ( let i = 0; i < color_btns.length; i++ ) {
						getElement( color_btns[ i ] ).classList.remove( 'no-user-selection', 'user-selection' );
						getElement( color_btns[ i ] ).classList.add( 'user-selection-disabled' );
						getElement( color_btns[ i ] ).disabled = true;
					}

					getElement( 'smiley' ).disabled = true;
					getElement( 'img' ).disabled    = true;

					return;
				}

				for ( let i = 0; i < colorize_btns.length; i++ ) {

					const single_letter     = new RegExp( "\\[\u200b\\/?" + colorize_btns[ i ].substr( 0, 1 ) + "\u200b]" ),
						  multi_letter      = new RegExp( "\\[\\/?" + colorize_btns[ i ] + "]" ),
						  single_letter_all = new RegExp( "\\[\u200b\\/?" + colorize_btns[ i ].substr( 0, 1 ) + "\u200b]", "g" ),
						  multi_letter_all  = new RegExp( "\\[\\/?" + colorize_btns[ i ] + "]", "g" ),
						  single_before     = before_selection.match( single_letter_all ) ? before_selection.match( single_letter_all ).length : 0,
						  single_after      = after_selection.match( single_letter_all ) ? after_selection.match( single_letter_all ).length : 0,
						  multi_before      = before_selection.match( multi_letter_all ) ? before_selection.match( multi_letter_all ).length : 0,
						  multi_after       = after_selection.match( multi_letter_all ) ? after_selection.match( multi_letter_all ).length : 0;

					if ( !single_letter.test( selected_text ) && !multi_letter.test( selected_text ) &&
						 ( single_before + multi_before ) % 2 === 0 &&
						 ( single_after + multi_after ) % 2 === 0 ) {

						getElement( colorize_btns[ i ] ).style.color = 'inherit';
						getElement( colorize_btns[ i ] ).disabled    = false;
					}
					else {
						getElement( colorize_btns[ i ] ).style.color = '#999999';
						getElement( colorize_btns[ i ] ).disabled    = true;
					}
				}

				const multi_letter     = /\[\/?(red|green|blue|black)]/,
					  multi_letter_all = /\[\/?(red|green|blue|black)]/g,
					  multi_before     = before_selection.match( multi_letter_all ) ? before_selection.match( multi_letter_all ).length : 0,
					  multi_after      = after_selection.match( multi_letter_all ) ? after_selection.match( multi_letter_all ).length : 0;

				for ( let i = 0; i < color_btns.length; i++ ) {

					if ( !multi_letter.test( selected_text ) && ( multi_before % 2 === 0 ) && ( multi_after % 2 === 0 ) ) {

						getElement( color_btns[ i ] ).classList.remove( 'no-user-selection', 'user-selection-disabled' );
						getElement( color_btns[ i ] ).classList.add( 'user-selection' );
						getElement( color_btns[ i ] ).disabled = false;
					}
					else {
						getElement( color_btns[ i ] ).classList.remove( 'no-user-selection', 'user-selection' );
						getElement( color_btns[ i ] ).classList.add( 'user-selection-disabled' );
						getElement( color_btns[ i ] ).disabled = true;
					}
				}
			}
		};


		let decolorizeButtons = () =>
		{
			for ( let i = 0; i < colorize_btns.length; i++ ) {
				getElement( colorize_btns[ i ] ).style.color = '#999999';
			}

			for ( let i = 0; i < color_btns.length; i++ ) {
				getElement( color_btns[ i ] ).classList.remove( 'user-selection', 'user-selection-disabled' );
				getElement( color_btns[ i ] ).classList.add( 'no-user-selection' );
			}

			getElement( 'smiley' ).disabled   = false;
			getElement( 'img' ).disabled      = false;
			getElement( 'clear' ).disabled    = true;
			getElement( 'clear' ).style.color = '#999999';
		};


		let delDecorators = () =>
		{
			saveState();

			updateTextArea( [
								( text ) =>
								{
									let replaced_chars = 0;

									const indexes = [ ...selected_decorators.keys() ].sort( ( a, b ) =>
																							{
																								return a - b;
																							} );
									indexes.forEach( ( index ) =>
													 {
														 let length = selected_decorators.get( index );

														 text = text.substring( 0, ( index - replaced_chars ) ) + text.substr( index - replaced_chars + length );
														 replaced_chars += length;
													 } );

									return text;
								}
							] );

			const previous   = text_history[ text_history.length - 1 ],
				  diff       = previous.length - text_area.value.length;
			let cursor_index = -1;

			if ( diff !== 0 ) {

				saveState();

				for ( let i = text_area.value.length - 1; i >= 0; i-- ) {

					if ( text_area.value.charAt( i ) !== previous.charAt( i + diff ) ) {
						cursor_index = i + 1;
						break;
					}
				}
			}

			setCursor( cursor_index );
		};


		let defaultState = ( event ) =>
		{
			decolorizeButtons();

			if ( event && event.target.getAttribute( 'id' ) !== "" ) {
				hideModal();
			}
		};


		let saveState = () =>
		{
			if ( text_history[ text_history.length - 1 ] !== text_area.value.trim() ) {

				text_history.splice( ( text_history.length - undo_clicks ), undo_clicks + 1, text_area.value );

				undo_clicks                      = 0;
				getElement( 'redo' ).style.color = '#999999';
				getElement( 'redo' ).disabled    = true;

				if ( text_history.length > 1 ) {
					getElement( 'undo' ).style.color = 'inherit';
					getElement( 'undo' ).disabled    = false;
				}
			}
		};


		let replace = ( open_tag, close_tag, event ) =>
		{
			const e = event || window.event;

			if ( isSelected() ) {

				saveState();
				text_area.focus();

				const start_pos        = text_area.selectionStart,
					  end_pos          = text_area.selectionEnd,
					  text             = text_area.value,
					  before_selection = text.substring( 0, start_pos ),
					  after_selection  = text.substring( end_pos, text.length );

				if ( text.substr( start_pos, 1 ) === ' ' ) {
					open_tag = ' ' + open_tag;
				}

				if ( text.substr( end_pos - 1, 1 ) === ' ' ) {
					close_tag = close_tag + ' ';
				}

				const selection = open_tag + text.substring( start_pos, end_pos ).trim() + close_tag;

				updateTextArea( [
									() =>
									{
										return before_selection + selection + after_selection;
									}
								] );
				saveState();

				text_area
					.setSelectionRange( before_selection.length + open_tag.length, before_selection.length + open_tag.length + text
						.substring( start_pos, end_pos ).trim().length );

				defaultState( e );
			}
		};


		let insert = ( input, event ) =>
		{
			if ( input !== "⚐" ) {

				delFlag( event );
				saveState();
			}

			const before_selection   = text_area.value.substring( 0, text_area.selectionStart ),
				  after_selection    = text_area.value.substring( text_area.selectionStart ),
				  new_text           = before_selection + input + after_selection,
				  decorator_patterns = [
					  /\[[\u200b]\/?([suib])[\u200b]]/g,
					  /\[\/?(red|blue|black|green|quote|pre)]/g,
					  /\[(smile|frown|blush|cool|crazy|laugh|mad|shocked|tongue|wink|up|down|kiss|flower|glass|heart)]/g,
					  /\[.*?\.(jpg|png|gif|jpeg|bmp)⇄img]((\(((\d+(%|px))|auto)\))[\u200b]|[\u200b])/g,
					  /\[.*?⇄.*?]/g
				  ];
			let insertable           = true;

			for ( let i = 0; i < decorator_patterns.length; i++ ) {

				const all_count    = text_area.value.match( decorator_patterns[ i ] ) ? text_area.value.match( decorator_patterns[ i ] ).length : 0,
					  before_count = before_selection.match( decorator_patterns[ i ] ) ? before_selection.match( decorator_patterns[ i ] ).length : 0,
					  after_count  = after_selection.match( decorator_patterns[ i ] ) ? after_selection.match( decorator_patterns[ i ] ).length : 0;

				if ( ( i < 2 && ( before_count + after_count ) % 2 !== 0 ) || ( i > 1 && ( before_count + after_count ) !== all_count ) ) {
					insertable = false;
					break;
				}
			}

			if ( !insertable ) {
				event.preventDefault();
				event.stopPropagation();
				hideModal();
				errorMsg( "wrong_cursor_position" );
				return;
			}

			updateTextArea( [
								() =>
								{
									return new_text;
								}
							] );

			if ( input !== "⚐" ) {

				saveState();
				setCursor( new_text.length - after_selection.length );
			}
		};


		let setCursor = ( cursor ) =>
		{
			let cursor_pos_start = cursor || text_area.value.indexOf( "⚐" ),
				cursor_pos_end   = cursor_pos_start;

			if ( cursor_pos_start === -1 ) {
				cursor_pos_start = text_area.selectionStart;
				cursor_pos_end   = text_area.selectionEnd;

			}

			if ( cursor_pos_start ) {
				text_area.setSelectionRange( cursor_pos_start, cursor_pos_end );
			}

			text_area.focus();
		};


		let mkFlag = ( event ) =>
		{
			const cursor = text_area.value.indexOf( "⚐" );

			if ( cursor === -1 ) {
				insert( "⚐", event );
			}
		};


		let delFlag = ( event ) =>
		{
			const cursor = text_area.value.indexOf( "⚐" );

			if ( cursor !== -1 ) {

				text_area.value = text_area.value.replace( /⚐/g, '' );

				if ( event ) {
					setCursor( cursor );
				}
			}

			defaultState( event );
		};


		let delNewlines = ( text ) =>
		{
			return text.trim().replace( /\n{4,}/g, '\n\n\n' );
		};


		let delWhitespaces = ( text ) =>
		{
			let replaceable = true;

			text = text
				.replace( /[\u200b][s][\u200b]]\s+(?!\[)/g, '\u200bs\u200b]' )
				.replace( /[\u200b][u][\u200b]]\s+(?!\[)/g, '\u200bu\u200b]' )
				.replace( /[\u200b][i][\u200b]]\s+(?!\[)/g, '\u200bi\u200b]' )
				.replace( /[\u200b][b][\u200b]]\s+(?!\[)/g, '\u200bb\u200b]' )
				.replace( /\s+\[[\u200b]\//g, '[\u200b/' )
				.replace( /(\/?pre[\u200b])|(\n(?!\n)\s+)/g, ( all, first ) =>
				{
					if ( first && !/\/pre\u200b/.test( first ) ) {

						replaceable = false;

					}
					else if ( first && /\/pre\u200b/.test( first ) ) {

						replaceable = true;
						return first;
					}

					if ( replaceable ) {
						return '\n';
					}
					else {
						return all;
					}

				} )
				.replace( /\[quote]\s+/g, '[quote]' )
				.replace( /\s+\[\/quote]/g, '[/quote]' );

			replaceable = false;

			text = text
				.replace( /(\/?quote)|(\n){2,}/g, ( all, first ) =>
				{
					if ( first && !/\/quote/.test( first ) ) {

						replaceable = true;
						return first;

					}
					else if ( first && /\/quote/.test( first ) ) {

						replaceable = false;
					}

					if ( replaceable ) {
						return '\n';
					}
					else {
						return all;
					}

				} );

			return text;
		};


		let showSmileys = () =>
		{
			getElement( 'modal-dialogue' ).style.display = 'block';
			getElement( 'modal-dialogue' ).style.zIndex  = '99999';
			getElement( 'modal-preview' ).style.display  = 'none';
			getElement( 'modal-symbols' ).style.display  = 'inline-block';
		};


		let hideModal = () =>
		{
			getElement( 'modal-dialogue' ).style.display = 'none';
			getElement( 'modal-symbols' ).style.display  = 'none';
			getElement( 'modal-preview' ).style.display  = 'none';
		};


		let showPreview = () =>
		{
			getElement( 'modal-dialogue' ).style.display = 'block';
			getElement( 'modal-dialogue' ).style.zIndex  = '99999';
			getElement( 'modal-symbols' ).style.display  = 'none';
			getElement( 'modal-preview' ).style.display  = 'block';
			getElement( 'preview-body' ).innerHTML       = normalize( text_area.value );
		};


		let insertSmiley1 = ( event ) =>
		{
			const symbol = event.target.innerHTML;

			insert( symbol, event );
		};


		let insertSmiley2 = ( event ) =>
		{
			try {
				const smiley_tag = event.target.innerHTML.substring( 56, event.target.innerHTML.indexOf( ".gif" ) ) ? event.target.innerHTML.substring( 56, event.target.innerHTML.indexOf(
					".gif" ) ) : event.target.outerHTML.substring( 56, event.target.outerHTML.indexOf( ".gif" ) );
				insert( '[' + smiley_tag + ']', event );
			}
			catch ( e ) {
			}
		};


		let translateLetter = ( event ) =>
		{
			if ( getElement( 'translate' ).checked ) {

				const input = event.key;

				if ( !event.which || rus.indexOf( input ) !== -1 ) {
					return;
				}

				if ( input && ( !( event.ctrlKey || event.altKey ) ) ) {

					event.preventDefault();

					const pre_text        = text_area.value.substring( 0, text_area.selectionStart ),
						  translated_text = translateSymbolToCyrillic( pre_text + input ),
						  post_text       = text_area.value.substr( text_area.selectionEnd ),
						  result          = translated_text + post_text;

					if ( text_area.value !== result ) {

						updateTextArea( [
											() =>
											{
												return translated_text + post_text;
											}
										] );

						saveState();
						setCursor( translated_text.length );
					}
				}
			}
		};


		let translateSymbolToCyrillic = ( text ) =>
		{
			let pos = 0;

			for ( let i = 0; i < lat.length; i++ ) {

				pos = text.length > lat[ i ].length ? ( text.length - lat[ i ].length ) : 0;

				if ( lat[ i ] === text.substr( pos, text.length - pos ) ) {
					return text.substr( 0, text.length - lat[ i ].length ) + rus[ i ];
				}
			}

			return text;
		};

		setUp();
	}
)();