/*
USES		Core, Console
DESCRIPTION	"Модуль расширения функционала форума MyBB"
AUTHOR		"Berserker"
*/

Bers = {}

Bers.BrowserFault = function()
{
	alert('Sorry, but your browser is not supported.');
	Assert(false);
} // .function Bers.BrowserFault

Bers.Focus = function()
{
	var Memo = $$('textarea'); Memo = Memo[0];
	Memo.focus();
	Bers.Memo = Memo;
	if (Memo.selectionStart === undefined)
	{
		Bers.BrowserFault();
	}
} // .function Bers.Focus

Bers.GetSelection = function ()
{
	var Start = Bers.Memo.selectionStart;
	var End = Bers.Memo.selectionEnd;
	if (End >= Start)
	{
		return {Start: Start, End: End, Range: End - Start};
	}
	else
	{
		return {Start: End, End: Start, Range: Start - End};
	}
} // .function Bers.GetSelection

Bers.Event = function (e)
{
	var E = e || window.event;
	if (E.target === undefined)
	{
		E.target = e.srcElement || null;
	}
	return E;
} // .function Bers.Event

Bers.GotoCustomPage = function (a)
{
	var Page = parseInt(prompt('Enter page #:'));
	if (!isNaN(Page) && (Page >= 0))
	{
		location.href = a + '&page=' + Page;
	}
	return false;
} // .function Bers.GotoCustomPage

Bers.NavigateThread = function (a)
{
	var	Page = parseInt(prompt('Enter page #:'))
		a = a.href,
		End = a.indexOf('&page');
	if(!isNaN(Page) && (Page >= 0))
	{
		if (End === - 1)
		{
			End = a.length;
		}
		a = a.slice(0, End);
		location.href = a + '&page=' + Page;
	}
	return false;
} // .function Bers.NavigateThread

Bers.ButtonPushed = function (e)
{
	var	E = Bers.Event(e),
		Button = E.target;
	Button.style.border = '1px solid black';
	return false;
} // Bers.ButtonPushed

Bers.ButtonReleased = function (e, FuncID)
{
	var E = Bers.Event(e),
		Button = E.target;
	Button.style.border = '0px';
	(Bers.Funcs[FuncID])(E);
	if (Bers.Memo !== undefined)
	{
		Bers.Memo.focus();
	}
	return false;
} // Bers.ButtonButtonReleased

Bers.ButtonDblClick = function ()
{
	return false;
} // Bers.ButtonDblClick

Bers.Funcs = [];

/*
TButton = RECORD
	Name: STRING;
	Hint: STRING;
	Func: FUNCTION(event);
END;

TButton = RECORD
	CustomHtml: STRING;
END; OVERLOAD;
*/

// Bers.NewButton - возвращает html-код кнопки, не обворачивая его в span или div.
Bers.NewButton = function (Button)
{
	var BtnID = Bers.Funcs.length;
	Bers.Funcs.push(Button.Func);
	return ' class="bb" onmousedown="return Bers.ButtonPushed(event);" onmouseup="return Bers.ButtonReleased(event, ' + BtnID + ');" ondblclick="return Bers.ButtonDblClick();" title="' + Button.Hint + '">' + Button.Name;
} // .function Bers.NewButton

Bers.CreateButtons = function (BtnArr)
{
	var	Len = BtnArr.length,
		Buf = [];
	for (var i = 0; i < Len; i++)
	{
		if (BtnArr[i].CustomHtml !== undefined)
		{
			Buf.push(BtnArr[i].CustomHtml);
		}
		else
		{
			Buf.push('<span'+ Bers.NewButton(BtnArr[i]) + '</span>');
		}
	}
	return Buf.join('');
} // Bers.CreateButtons

Bers.SimpleBB = function (Code, ReqInput)
{
	Bers.CloseOpenedMenu();
	Bers.Focus();
	var	Sel = Bers.GetSelection(),
		Memo = Bers.Memo,
		Pos = Code.indexOf('='),
		Tag = Code,
		Txt = '',
		Scroll = 0;
	if (Pos !== -1)
	{
		Tag = Tag.substr(0, Pos);
	}
	Txt = '[' + Code + ']' + Memo.value.substr(Sel.Start, Sel.Range) + '[/' + Tag + ']';
	if (Prototype.Browser.Gecko)
	{
		Scroll = Memo.scrollTop;
	}
	Memo.value = Memo.value.substr(0, Sel.Start) + Txt + Memo.value.substr(Sel.End);
	if (Prototype.Browser.Gecko)
	{
		Memo.scrollTop = Scroll;
	}
	if (ReqInput)
	{
		Memo.selectionStart = Sel.Start + Code.length + 1;
		Memo.selectionEnd = Memo.selectionStart;
	}
	else
	{
		if(Sel.Range > 0)
		{
			Memo.selectionStart = Sel.Start;
			Memo.selectionEnd = Sel.Start + Txt.length;
		}
		else
		{
			Memo.selectionStart = Sel.Start + 2 + Code.length;
			Memo.selectionEnd = Memo.selectionStart;
		}
	}
} // .function Bers.SimpleBB

Bers.InsertBB = function (Code)
{
	Bers.CloseOpenedMenu();
	Bers.Focus();
	var	Sel = Bers.GetSelection(),
		Memo = Bers.Memo,
		Scroll = 0;
	if (Prototype.Browser.Gecko)
	{
		Scroll = Memo.scrollTop;
	}
	Memo.value = Memo.value.substr(0, Sel.End) + Code + Memo.value.substr(Sel.End);
	if (Prototype.Browser.Gecko)
	{
		Memo.scrollTop = Scroll;
	}
	Memo.selectionStart = Sel.Start + Sel.Range + Code.length;
	Memo.selectionEnd = Memo.selectionStart;
} // .function Bers.InsertBB

Bers.CloseOpenedMenu = function ()
{
	var	Elem = $('BersMenu'),
		BtnCount = arguments.callee.BtnCount;
	if (Elem !== null)
	{
		for (var i = 0; i < BtnCount; i++)
		{
			Bers.Funcs.pop();
		}
		Elem.remove();
	}
} // .function Bers.CloseOpenedMenu

Bers.CreateMenu = function (e, Buttons, CustomHTML, Back)
{
	Bers.CloseOpenedMenu();
	Bers.Focus();
	var	E = Bers.Event(e),
		TrigBtn = $(E.target),
		BtnW = TrigBtn.offsetWidth,
		BtnH = TrigBtn.offsetHeight,
		BtnX = 0, BtnY = 0,
		Menu = null,
		BtnCount = Buttons.length + 1,
		Txt = '',
		MenuW = 0, MenuH = 0, MenuX = 0, MenuY = 0;
	if (E.offsetX === undefined)
	{
		E.offsetX = parseInt(BtnW / 2);
	}
	if (E.offsetY === undefined)
	{
		E.offsetY = parseInt(BtnH / 2);
	}
	BtnX = E.pageX - E.offsetX;
	BtnY = E.pageY - E.offsetY;
	Menu = $(document.createElement('div'));
	Menu.id = 'BersMenu';
	Menu.style.cssText = 'position: absolute; left: 0px; top: 0px; color: white; outline: 1px solid black; background-color: ' + Back + '';
	Bers.CloseOpenedMenu.BtnCount = BtnCount;
	Txt = '<div align="center"' + Bers.NewButton({Name: '[-X-]', Hint: 'Close / Закрыть', Func: Bers.CloseOpenedMenu}) + '</div><div align="center">' + Bers.CreateButtons(Buttons) + '</div>';
	Menu.innerHTML = Txt + CustomHTML;
	document.body.appendChild(Menu);
	MenuW = Menu.offsetWidth;
	MenuH = Menu.offsetHeight;
	MenuX = BtnX - parseInt((MenuW - BtnW) / 2);
	if ((MenuX + MenuW) > screen.width)
	{
		MenuX = screen.width - MenuW;
	}
	MenuY = BtnY + BtnH + 5;
	Menu.setStyle
	({
		left: MenuX + 'px',
		top: MenuY + 'px'
	});
} // .function Bers.CreateMenu

Bers.ColorButtons = function (ElemName, Buttons)
{
	var	Count = Buttons.length,
		i = -1,
		result = [];
	for (i = 0; i < Count; i++)
	{

		result.push
		(
			Core.MakeStr('<div style="background-color: %;" onclick="Bers.SimpleBB(\'%=%\', false);Bers.CloseOpenedMenu();">&nbsp;</div>', '%', [Buttons[i], ElemName, Buttons[i]])
		);
	}
	return result.join('');
} // .function Bers.ColorButtons

Bers.Run = function ()
{
	if (!Prototype.Browser.IE)
	{
		$('QuickBB').innerHTML = Bers.CreateButtons
		([
			{Name: '↑↑', Hint: 'Stick panel / Прикрепить панель', Func: function(){
				var	Panel = $('QuickBB2'),
					Txt = '',
					NewPanel = null;
				if (Panel === null)
				{
					Panel = $('QuickBB');
					Txt = Panel.innerHTML;
					Panel.innerHTML = '';
					NewPanel = document.createElement('div');
					NewPanel.id = 'QuickBB2';
					NewPanel.innerHTML = Txt;
					NewPanel.setStyle
					({
						position: 'fixed',
						top: '0px',
						backgroundColor: '#81a2c4',
						left: '0px',
						padding: '6px'
					});
					document.body.appendChild(NewPanel);
					NewPanel.style.left = parseInt((screen.width - NewPanel.offsetWidth) / 2) + 'px';
				}
				else
				{
					$('QuickBB').innerHTML = Panel.innerHTML;
					Panel.remove();
				}
				return false;
			}},
			{Name: ':-)', Hint: 'Smile / Смайлик', Func: function(e) {
				var Txt = '<div align="center"><img style="cursor: pointer;" src="images/smilies/ab.gif" onclick="Bers.InsertBB(\':-)\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/96.gif" onclick="Bers.InsertBB(\':96:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/148.gif" onclick="Bers.InsertBB(\'%]\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/spiteful.gif" onclick="Bers.InsertBB(\':spiteful:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/rolleyes.gif" onclick="Bers.InsertBB(\':rolleyes:\', false);Bers.CloseOpenedMenu();"><br><img style="cursor: pointer;" src="images/smilies/beee.gif" onclick="Bers.InsertBB(\':beee:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/dry.gif" onclick="Bers.InsertBB(\':dry:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/mad.gif" onclick="Bers.InsertBB(\':mad:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/fie.gif" onclick="Bers.InsertBB(\':fie:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/yes.gif" onclick="Bers.InsertBB(\':yes:\', false);Bers.CloseOpenedMenu();"><br><img style="cursor: pointer;" src="images/smilies/132.gif" onclick="Bers.InsertBB(\':132:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/moral.gif" onclick="Bers.InsertBB(\':moral:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/36.gif" onclick="Bers.InsertBB(\':36:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/unsure.gif" onclick="Bers.InsertBB(\':unsure:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/sorry.gif" onclick="Bers.InsertBB(\':sorry:\', false);Bers.CloseOpenedMenu();"><br> <img style="cursor: pointer;" src="images/smilies/stop.gif" onclick="Bers.InsertBB(\':stop:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/dash1.gif" onclick="Bers.InsertBB(\':dash1:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/120.gif" onclick="Bers.InsertBB(\':120:\', false);Bers.CloseOpenedMenu();"><br><img style="cursor: pointer;" src="images/smilies/dance3.gif" onclick="Bers.InsertBB(\':dance3:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/105.gif" onclick="Bers.InsertBB(\':105:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/177.gif" onclick="Bers.InsertBB(\':177:\', false);Bers.CloseOpenedMenu();"><br> <img style="cursor: pointer;" src="images/smilies/103.gif" onclick="Bers.InsertBB(\':103:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/85.gif" onclick="Bers.InsertBB(\':85:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/gigi.gif" onclick="Bers.InsertBB(\':gigi:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/116.gif" onclick="Bers.InsertBB(\':116:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/166.gif" onclick="Bers.InsertBB(\':166:\', false);Bers.CloseOpenedMenu();"><br> <img style="cursor: pointer;" src="images/smilies/102.gif" onclick="Bers.InsertBB(\':102:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/112.gif" onclick="Bers.InsertBB(\':112:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/kap.gif" onclick="Bers.InsertBB(\':kap:\', false);Bers.CloseOpenedMenu();"> <img style="cursor: pointer;" src="images/smilies/4.gif" onclick="Bers.InsertBB(\':4:\', false);Bers.CloseOpenedMenu();"></div>';
				Bers.CreateMenu(e, [], Txt, '#f7f7f7');
				return false;
			}},
			{CustomHtml: '&nbsp;'},
			{Name: 'B ', Hint: 'Bold / Жирный', Func: function() {
				Bers.SimpleBB('b', false);
				return false;
			}},
			{Name: 'I ', Hint: 'Italique / Курсив', Func: function() {
				Bers.SimpleBB('i', false);
				return false;
			}},
			{Name: 'U ', Hint: 'Underlined / Подчёркнуто', Func: function() {
				Bers.SimpleBB('u', false);
				return false;
			}},
			{Name: 'S ', Hint: 'Striked / Зачёркнуто', Func: function() {
				Bers.SimpleBB('s');
				return false;
			}},
			{CustomHtml: '&nbsp;'},
			{Name: 'URL', Hint: 'Link / Ссылка', Func: function() {
				Bers.SimpleBB('url=', true);
				return false;
			}},
			{Name: 'IMG', Hint: 'Image / Ссылка на картинку', Func: function() {
				Bers.SimpleBB('img', false);
				return false;
			}},
			{Name: '«..»', Hint: 'Quote / Цитировать', Func: function() {
				Bers.SimpleBB('quote');
				return false;
			}},
			{Name: 'CODE', Hint: 'Code / Код', Func: function() {
				Bers.SimpleBB('code');
				return false;
			}},
			{Name: 'OFF', Hint: 'Offtop / Оффтопик', Func: function() {
				Bers.SimpleBB('off');
				return false;
			}},
      {Name: 'ERM', Hint: 'ERM / ЕРМ', Func: function() {
				Bers.SimpleBB('erm');
				return false;
			}},
			{CustomHtml: '&nbsp;'},
			{Name: '.T.', Hint: 'Text Align / Выравнивание Текста', Func: function(e) {
				Bers.CreateMenu
				(
					e,
					[
						{Name: 'T..', Hint: 'Left / Лево', Func: function() {
							Bers.SimpleBB('align=left', false);
							return false;
						}},
						{Name: '.T.', Hint: 'Center / По центру', Func: function() {
							Bers.SimpleBB('align=center', false);
							return false;
						}},
						{Name: '..T', Hint: 'Right / Право', Func: function() {
							Bers.SimpleBB('align=right', false);
							return false;
						}},
						{Name: 'TTT', Hint: 'Justify / Выровнять по ширине', Func: function() {
							Bers.SimpleBB('align=justify', false);
							return false;
						}}
					],
					'',
					'black; border-bottom-width: 3px'
				);
				return false;
			}},
			{Name: 'SIZE', Hint: 'Size / Размер', Func: function(e) {
				var Txt = '<div style="font-size: x-small;" onclick="Bers.SimpleBB(\'size=x-small\', false);Bers.CloseOpenedMenu();">Text</div><div style="font-size: large;" onclick="Bers.SimpleBB(\'size=large\', false);Bers.CloseOpenedMenu();">Text</div><div style="font-size: x-large;" onclick="Bers.SimpleBB(\'size=x-large\', false);Bers.CloseOpenedMenu();">Text</div>';
				Bers.CreateMenu(e, [], Txt, 'black');
				return false;
			}},
			{Name: 'COL', Hint: 'Color / Цвет', Func: function(e) {
				var	Txt = Bers.ColorButtons('color', ['red', 'orange', 'yellow', 'green', 'lightblue', 'blue', 'purple', 'black', 'SaddleBrown', 'white']);
				Bers.CreateMenu(e, [], Txt, 'gray');
				return false;
			}},
			{Name: 'SPAN', Hint: 'Background color / Цвет фона', Func: function(e) {
				var	Txt = Bers.ColorButtons('span', ['red', 'orange', 'yellow', 'green', 'lightblue', 'blue', 'purple', 'black', 'white']);
				Bers.CreateMenu(e, [], Txt, 'gray');
				return false;
			}},
			{Name: 'DIV', Hint: 'Color filling / Заливка цветом', Func: function(e) {
				var	Txt = Bers.ColorButtons('div', ['red', 'orange', 'yellow', 'green', 'lightblue', 'blue', 'purple', 'black', 'white']);
				Bers.CreateMenu(e, [], Txt, 'gray');
				return false;
			}},
			{CustomHtml: '&nbsp;'},
			{Name: '(..)', Hint: 'Spoiler / Спойлер', Func: function() {
				Bers.SimpleBB('spoiler');
				return false;
			}},
			{Name: 'LIST', Hint: 'List / Список', Func: function() {
				Bers.InsertBB("[list]\r\n[*]\r\n[/list]");
				return false;
			}},
			{Name: '[*]', Hint: 'List Item / Элемент списка', Func: function() {
				Bers.InsertBB('[*]');
				return false;
			}},
			/*{Name: '@..', Hint: 'Private remark / Личное сообщение', Func: function() {
				Bers.SimpleBB('pr=', true);
				return false;
			}},*/
			{CustomHtml: '&nbsp;'},
			{Name: 'MUZ', Hint: 'Music / Музыка', Func: function() {
				Bers.SimpleBB('muz');
				return false;
			}},
			{Name: 'PS', Hint: 'Post scriptum / Постскриптум', Func: function() {
				Bers.InsertBB("[b]P.S.[/b]");
				return false;
			}},
			{Name: ' &nbsp;', Hint: 'Space / Неразрывный пробел', Func: function() {
				Bers.InsertBB('[ ]');
				return false;
			}},
			{Name: '[..]', Hint: 'Other codes / Другие теги', Func: function(e) {
				Bers.CreateMenu
				(
					e,
					[
						{Name: 'P ', Hint: 'Paragraph / Абзац', Func: function() {
							Bers.InsertBB('[p]');
							return false;
						}},
						{Name: 'BR', Hint: 'Break line / Новая строка', Func: function() {
							Bers.InsertBB('[br]');
							return false;
						}},
						{Name: '___', Hint: 'Line / Линия', Func: function() {
							Bers.InsertBB('[hr]');
							return false;
						}},
						{Name: 'HINT', Hint: 'Hint / Подсказка', Func: function() {
							Bers.SimpleBB('hint=', true);
							return false;
						}},
						{Name: '<span>SUP</span>', Hint: 'Sup / Надстрочный текст', Func: function() {
							Bers.SimpleBB('sup');
							return false;
						}},
						{Name: '<span">SUB</span>', Hint: 'Sub / Подстрочный текст', Func: function() {
							Bers.SimpleBB('sub');
							return false;
						}}
					],
					'',
					'black; border: 0px'
				);
				return false;
			}}
		]);
	} // .if
} // .function Bers.Run

Bers.MakeTabId = function (Prefix, Post, Parent, Tab)
{
	return String(Prefix) + Post + '_' + Parent + '_' + Tab;
} // .function Bers.MakeTabId

Bers.ActTab = function (Post, Parent, Tab)
{
	var	i = 0,
		TabElem = $(Bers.MakeTabId('t', Post, Parent, 1)),
		PageElem = $(Bers.MakeTabId('p', Post, Parent, 1));
	i = 1;
	while (TabElem)
	{
		TabElem.style.border = '';
		i++;
		TabElem = $(Bers.MakeTabId('t', Post, Parent, i));
	}
	i = 1;
	while (PageElem)
	{
		PageElem.style.display = 'none';
		i++;
		PageElem = $(Bers.MakeTabId('p', Post, Parent, i));
	}
	TabElem = $(Bers.MakeTabId('t', Post, Parent, Tab));
	if (TabElem)
	{
		TabElem.style.border = '2px solid red';
	}
	PageElem = $(Bers.MakeTabId('p', Post, Parent, Tab));
	if (PageElem)
	{
		PageElem.style.display = 'block';
	}
} // .function Bers.ActTab

Bers.Threads = {}

/*
TContext = RECORD
	Data:		STRING;		// Текст
	Len:		INTEGER;	// Размер текста
	Line:		INTEGER;	// Номер строки, начиная с 1
	Thread:		TThread;	// Компилируемый поток
	Pos:		INTEGER;	// Позиция в коде, начиная с 0.
	EOT:		BOOLEAN;	// Признак конца текста
	Token:		STRING;		// Текст токена
	TokenType:	TTokenType;	// Тип токена
	Error:		STRING;		// Строка ошибки, если есть.
END;
*/

Bers.NewContext = function (Str, Thread)
{
	var result = 
	{
		Data: Str,
		Len: Str.length,
		Line: 1,
		Thread: Thread,
		Pos: 0,
		EOT: Str.length === 0,
		Token: '',
		TokenType: '',
		Error: ''
	}
	return result;
} // .function Bers.NewContext

Bers.GotoNextPos = function (Context)
{
	var result = !Context.EOT;
	if (result)
	{
		if (Context.Data[Context.Pos] === "\n")
		{
			Context.Line++;
		}
		Context.Pos++;
		if (Context.Pos >= Context.Len)
		{
			Context.EOT = true;
			result = false;
		}
	}
	return result;
} // .function Bers.GotoNextPos

Bers.GotoPos = function (Context, NewPos)
{
	var	result = ((NewPos >= Context.Pos) && (NewPos < Context.Len)),
		Distance = 0,
		i = 0;
	if (result && (NewPos > Context.Pos))
	{
		Distance = NewPos - ContextPos;
		while (i > Distance)
		{
			Bers.GotoNextPos(Context);
			i++;
		}
	}
	return result;
} // .function Bers.GotoPos

Bers.GotoRelPos = function (Context, RelPos)
{
	return Bers.GotoPos(Context.Pos + RelPos);
} // .function Bers.GotoRelPos

Bers.Ord = function (Str)
{
	var result = String(Str).charCodeAt(0);
	Assert(!isNaN(result));
	return result;
} // .function Bers.Ord

Bers.NewSet = function ()
{
	var	Args = arguments,
		Arg = null,
		Len = arguments.length,
		i = 0,
		y = 0,
		result = [],
		Min = 0, Max = 0,
		Byte = 0, Bit = 0,
		b = 0;
	for (i = 0; i < 8; i++)
	{
		result[i] = 0;
	}
	i = 0;
	while (i < Len)
	{
		Arg = arguments[i];
		if (Arg instanceof Array)
		{
			Assert(Arg.length === 2);
			Min = Arg[0]; Max = Arg[1];
			if (typeof(Min) === 'string')
			{
				Min = Bers.Ord(Min);
			}
			if (typeof(Max) === 'string')
			{
				Max = Bers.Ord(Max);
			}
			Assert((Min <= Max) && (Min >= 0) && (Max <= 255));
			for (y = Min; y <= Max; y++)
			{
				Byte = parseInt(y / 32);
				Bit = Math.pow(2, (y % 32));
				result[Byte] = result[Byte] | Bit;
			}
		}
		else
		{
			if (typeof(Arg) === 'string')
			{
				b = Bers.Ord(Arg);
			}
			else if (typeof(Arg) === 'number')
			{
				b = parseInt(Arg);
			}
			else
			{
				Assert(false);
			}
			Assert((b >= 0) && (b <= 255));
			Byte = parseInt(b / 32);
			Bit = Math.pow(2, (b % 32));
			result[Byte] = result[Byte] | Bit;
		}
		i++;
	}
	return result;
} // .function Bers.NewSet

Bers.In = function (Set, c)
{
	var	Min = 0, Max = 0,
		b = 0,
		Byte = 0, Bit = 0;
	if (typeof(c) === 'string')
	{
		b = Bers.Ord(c);
	}
	else
	{
		b = c;
	}
	if (b <= 255)
	{
		Byte = parseInt(b / 32);
		Bit = Math.pow(2, (b % 32));
		return ((Set[Byte] & Bit) !== 0);
	}
	else
	{
		return false;
	}
} // .function Bers.In

Bers.FindChar = function (Context, c)
{
	var result = !Context.EOT;
	if (result)
	{
		while ((Context.Data[Context.Pos] !== c) && (Bers.GotoNextPos(Context))) {}
		result = !Context.EOT;
	}
	return result;
} // .function Bers.FindChar

Bers.FindChars = function (Context, Set)
{
	var result = !Context.EOT;
	if (result)
	{
		while (!Bers.In(Set, Context.Data[Context.Pos]) && (Bers.GotoNextPos(Context))) {}
		result = !Context.EOT;
	}
	return result;
} // .function Bers.FindChars

Bers.SkipChars = function (Context, Set)
{
	var result = !Context.EOT;
	if (result)
	{
		while (Bers.In(Set, Context.Data[Context.Pos]) && (Bers.GotoNextPos(Context))) {}
		result = !Context.EOT;
	}
	return result;
} // .function Bers.SkipChars

Bers.GotoNextLine = function (Context)
{
	return (Bers.FindChar(Context, "\n") && Bers.GotoNextPos(Context));
} // .function Bers.GotoNextLine

Bers.SkipUntilEndQuote = function (Context)
{
	var c = Context.Data[Context.Pos];
	Bers.GotoNextPos(Context);
	while (Bers.FindChar(Context, c) && (Context.Data[Context.Pos + 1] === c))
	{
		Bers.GotoNextPos(Context);
		Bers.GotoNextPos(Context);
	} // .while
	return (Context.Data[Context.Pos] === c);
} // .function Bers.SkipUntilEndQuote

Bers.Space = Bers.NewSet([0, 32]);
Bers.Delims = Bers.NewSet([0, 32], ';');
Bers.Number = Bers.NewSet(['0', '9'], '+', '-');
Bers.Quotes = Bers.NewSet('"', "'");

Bers.Consts =
{
	'true': true,
	'false': false,
	'nl': '',
	'NaN': NaN,
	'INF-': Number.NEGATIVE_INFINITY,
	'INF+': Number.POSITIVE_INFINITY,
	'e': Math.E,
	'pi': Math.PI,
	'log2e': Math.LOG2E,
	'log10e': Math.LOG10E
} // Bers.Consts

/*
TThread = RECORD
	Id:				INTEGER;														// Идентификатор блока кода потока
	State:			('Running', 'Paused', 'Stopped', 'Terminated', 'CompileError');	// Состояние потока
	EAX:			VAR;															// Регистр под временные дела
	EIP:			INTEGER; 														// Указатель на текущую команду
	Code:			ARRAY OF VAR;													// Команды
	Debug:			ARRAY OF INTEGER;												// Массив адресов в Code для каждого номера строки оригинального текста.
	Level:			INTEGER;														// Текущий уровень контекста (отражает уровень процедурных вызовов)
	Vars:			ARRAY Level OF ARRAASSOC OF VAR;								// Переменные для всех контекстов
	Rets:			ARRAY Level OF INTEGER;											// Адреса возвратов. -1 - остановить поток (Stop)
	Labels:			ASSOCARRAY OF INTEGER;											// Массив меток прыжков и вызовов
	Stack:			ARRAY OF VAR;													// Рабочий стёк
	WaitControl:	STRING;															// Идентификатор контрола, ввод которого мы ожидаем. Sleep для паузы. BP - точка останова и т.д.
	Error:			STRING;															// Текст ошибки команды
END;
*/

Bers.NewThread = function (Id)
{
	var result =
	{
		Id: Id,
		State: 'Terminated',
		EAX: null,
		EIP: -1,
		Code: [],
		Debug: [],
		Level: 0,
		Vars: [{}],
		Rets: [],
		Labels: {},
		Stack: [],
		WaitControl: '',
		Error: ''
	}
	return result;
} // .function Bers.NewThread

Bers.GetTokenPart = function (Context)
{
	var OldPos = Context.Pos;
	Bers.FindChars(Context, Bers.Delims);
	Context.Token = Context.Data.slice(OldPos, Context.Pos);
} // .function Bers.GetTokenPart

Bers.GetNextToken = function (Context)
{
	Assert(!Context.EOT);
	var	result = true,
		c,
		OldPos;
	while (Bers.SkipChars(Context, Bers.Space) && (Context.Data[Context.Pos] === ';') && Bers.GotoNextLine(Context)) {}
	result = !Context.EOT;
	if (result)
	{
		c = Context.Data[Context.Pos];
		if (Bers.In(Bers.Number, c))
		{
			Bers.GetTokenPart(Context);
			if (isNaN(parseInt(Context.Token)))
			{
				Context.Error = '2B: Invalid number "' + Context.Token + '"!';
				result = false;
			}
			else
			{
				Context.TokenType = 'int';
			}
		}
		else if (Bers.In(Bers.Quotes, c))
		{
			OldPos = Context.Pos + 1;
			result = Bers.SkipUntilEndQuote(Context);
			if (result)
			{
				Context.Token = Context.Data.slice(OldPos, Context.Pos);
				Context.TokenType = 'str';
				Bers.GotoNextPos(Context);
			}
			else
			{
				Context.Error = '2B: Missing ending quote ' + c + '!';
			}
		}
		else if (c === ':')
		{
			Bers.GotoNextPos(Context);
			result = !Context.EOT;
			if (result)
			{
				Bers.GetTokenPart(Context);
				Context.TokenType = 'label';
			}
			else
			{
				Context.Error = '2B: Missing label name!';
			}
		}
		else
		{
			OldPos = Context.Pos;
			Bers.FindChars(Context, Bers.Delims);
			Context.Token = Context.Data.slice(OldPos, Context.Pos);
			Context.TokenType = 'ident';
		}
	}
	return result;
} // .function Bers.GetNextToken

Bers.Compile = function (ThreadId)
{
	var	Thread = Bers.NewThread(ThreadId),
		Context = Bers.NewContext($(Thread.Id).textContent, Thread),
		Token = '',
		TokenType = '',
		Code = Thread.Code,
		Console = Bers.Console['console_' + Thread.Id.slice(Thread.Id.indexOf('_') + 1)],
		result = Bers.GetNextToken(Context);
	Bers.Threads[ThreadId] = Thread;
	while (result && !Context.EOT)
	{
		Thread.Debug[Code.length] = Context.Line;
		Token = Context.Token;
		TokenType = Context.TokenType;
		if (TokenType === 'str')
		{
			Code.push('push');
			Code.push(Token);
		}
		else if (TokenType === 'int')
		{
			Code.push('push');
			Code.push(parseInt(Token));
		}
		else if (TokenType === 'label')
		{
			result = Thread.Labels[Token] === undefined;
			if (result)
			{
				Thread.Labels[Token] = Code.length;
			}
			else
			{
				Context.Error = '2B: Duplicate label "' + Token + '"!';
			}
		}
		else if (TokenType === 'ident')
		{
			if (Bers.Consts[Token] !== undefined)
			{
				Code.push('push');
				Code.push(Bers.Consts[Token]);
			}
			else
			{
				if ((Bers.Cmds[Token] !== undefined) && (Token !== 'push') && (Token !== 'pushvar'))
				{
					Code.push(Token);
				}
				else
				{
					Code.push('pushvar');
					Code.push(Token);
				}
			}
		}
		else
		{
			Assert(false);
		}
		result = Bers.GetNextToken(Context);
	}
	if (!result && (Context.Error !== ''))
	{
		Thread.State = 'CompileError';
		alert(Context.Error);
	}
	else
	{
		Thread.Debug[Code.length] = Context.Line;
		result = true;
	}
	return result;
} // .function Bers.Compile

Bers.GetLine = function (Str, LineN)
{
	var	Line = 1,
		Len = Str.length,
		i = 0,
		StartPos,
		EndPos,
		result = '';
	while ((i < Len) && (Line != LineN))
	{
		if (Str[i] == "\n")
		{
			Line++;
		}
		i++;
	}
	if (i < Len)
	{
		StartPos = i;
		while ((i < Len) && (Str[i] != "\n"))
		{
			i++;
		}
		if (i < Len)
		{
			EndPos = i - 1;
		}
		result = Str.slice(StartPos, EndPos);
	}
	return result;
} // .function Bers.GetLine

Bers.Show2bError = function (Thread)
{
	var Line = Thread.Debug[Thread.EIP];
	alert(Thread.Error + "\n" + 'Error at address ' + Thread.EIP + '.' + "\nLine: " + Line + '. Context: ' + Bers.GetLine($(Thread.Id).textContent, Line));
} // .function Bers.Show2bError

Bers.Pop = function (Thread)
{
	var result = true;
	if (Thread.Stack.length === 0)
	{
		Thread.Error = '2B: Stack underflow!';
		result = false;
	}
	else
	{
		Thread.EAX = Thread.Stack.pop();
	}
	return result;
} // .function Bers.Pop

Bers.PopCmdArgs = function (Thread, Count)
{
	var	Stack = Thread.Stack,
		result = Stack.length >= Count;
	if (result)
	{
		Thread.EAX = Stack.splice(Stack.length - Count, Count);
	}
	else
	{
		Thread.Error = '2B: Not enough arguments for command "' + Thread.Code[Thread.EIP] + '" (' + (Stack.length) + '/' + Count + ')!'
	}
	return result;
} // .function Bers.PopCmdArgs

Bers.PushParams =  function (Thread, Params)
{
	var	Stack = Thread.Stack,
		Len = Params.length,
		i = -1;
	for (i = 0; i < Len; i++)
	{
		Stack.push(Params[i]);
	}
} // .function Bers.PushParams

Bers.ResumeThread = function (Thread)
{
	var result = true,
		Code = Thread.Code,
		Len = Code.length,
		Cmd = '';
	Thread.EIP++;
	try
	{
		while (result && (Thread.EIP < Len))
		{
			Cmd = Code[Thread.EIP];
			if (Bers.Cmds[Cmd] === undefined)
			{
				Thread.Error = '2B: Unknown command "' + Cmd + '"!';
				result = false;
			}
			else
			{
				result = (Bers.Cmds[Cmd])(Thread);
				if (result)
				{
					Thread.EIP++;
				}
			}
		}
	}
	catch (e)
	{
		Thread.Error = '2B: Exception "' + e.name + '" occured with message "' + e.message + '"!';
		result = false;
	}
	if (Thread.State === 'Running')
	{
		Thread.State = 'Terminated';
		if (!result)
		{
			Bers.Show2bError(Thread);
		}
	}
	return result;
} // .function Bers.ResumeThread

Bers.RunThread = function (Thread, ClearVars, Params)
{
	if (ClearVars)
	{
		Thread.Vars = [{}];
	}
	else
	{
		Thread.Vars = [Thread.Vars[0]];
	}
	Thread.State = 'Running';
	Thread.EIP = -1;
	Thread.Level = 0;
	Thread.Rets = [-1];
	Thread.Error = '';
	Thread.Stack = [];
	Bers.PushParams(Thread, Params);
	Thread.WaitControl = '';
	Bers.ResumeThread(Thread);
} // .function Bers.RunThread

Bers.Trigger = function (ThreadId, Params)
{
	var Thread = Bers.Threads[ThreadId];
	if (Thread === undefined)
	{
		if (Bers.Compile(ThreadId))
		{
			Thread = Bers.Threads[ThreadId];
			Bers.RunThread(Thread, true, Params);
		}
		///SPECIAL!ErrUses.Dump.Dump(Bers.Threads[ThreadId]);
	}
	else
	{
		if (Thread.State === 'Running')
		{
			alert('2B: Thread is already running!');
		}
		else if (Thread.State === 'Terminated')
		{
			Bers.RunThread(Thread, false, Params);
		}
		else if (Thread.State === 'CompileError')
		{
			alert('2B: Thread compilation was not a success!');
		}
		else if (Thread.State === 'Paused')
		{
			alert('2B: Thread is already running!');
			//Bers.ResumeThread(Thread);
		}
		else if (Thread.State = 'Stopped')
		{
			Bers.RunThread(Thread, false, Params);
		}
		else
		{
			Assert(false);
		}
	}
} // .function Bers.Trigger

Bers.GetVar = function (Thread, Addr)
{
	var	Vars = Thread.Vars,
		result = true;
	if (Vars[Thread.Level][Addr] !== undefined)
	{
		Thread.EAX = Vars[Thread.Level][Addr];
	}
	else if (Vars[0][Addr] !== undefined)
	{
		Thread.EAX = Vars[0][Addr];
	}
	else
	{
		Thread.Error = '2B: Unknown var name "' + Addr + '"!';
		result = false;
	}
	return result;
} // .Bers.GetVar

Bers.SetVar = function (Thread, Addr, NewValue)
{
	var	Vars = Thread.Vars,
		result = true;
	if (Vars[Thread.Level][Addr] !== undefined)
	{
		Vars[Thread.Level][Addr] = NewValue;
	}
	else if (Vars[0][Addr] !== undefined)
	{
		Vars[0][Addr] = NewValue;
	}
	else
	{
		Thread.Error = '2B: Unknown var name "' + Addr + '"!';
		result = false;
	}
	return result;
} // .Bers.SetVar

Bers.GetIntArg = function (Thread, Arg)
{
	var	result = true,
		Int = Math.floor(Arg);
	if (!isNaN(Int))
	{
		Thread.EAX = Int;
	}
	else
	{
		Thread.Error = '2B: Invalid command argument "' + Arg + '". Integer is required!';
		result = false;
	}
	return result;
} // .Bers.GetIntArg

Bers.PopArr = function (Thread, Depth)
{
	var	Stack = Thread.Stack;
	Assert(Stack.length >= Depth);
	return Stack.splice(Stack.length - Depth, Depth);
} // .function Bers.PopArr

Bers.CmdVar = function (Thread)
{
	var result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		var Arg = String(Thread.EAX[0]);
		if (Thread.Vars[Thread.Level][Arg] !== undefined)
		{
			Thread.Error = '2B: Duplicate var "' + Arg + '" declaration!';
		}
		else
		{
			Thread.Vars[Thread.Level][Arg] = 0;
			result = true;
		}
	}
	return result;
} // .function Bers.CmdVar

Bers.CmdMov = function (Thread)
{
	return (Bers.PopCmdArgs(Thread, 2) && Bers.SetVar(Thread, String(Thread.EAX[1]), Thread.EAX[0]));
} // .function Bers.CmdMov

Bers.CmdVal = function (Thread)
{
	var result = Bers.PopCmdArgs(Thread, 1) && Bers.GetVar(Thread, String(Thread.EAX[0]));
	if (result)
	{
		Thread.Stack.push(Thread.EAX);
	}
	return result;
} // .function Bers.CmdVal

Bers.CmdGet = function (Thread)
{
	var result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		if ((typeof(Thread.EAX[1]) === 'object') || (typeof(Thread.EAX[1]) === 'string'))
		{
			Thread.Stack.push(Thread.EAX[1][Thread.EAX[0]]);
		}
		else
		{
			Thread.Error = '2B: Object expected!';
			result = false;
		}
	}
	return result;
} // .function Bers.CmdGet

Bers.CmdSet = function (Thread)
{
	var result = Bers.PopCmdArgs(Thread, 3);
	if (result)
	{
		if (typeof(Thread.EAX[2]) === 'object')
		{
			Thread.EAX[2][Thread.EAX[1]] = Thread.EAX[0];
		}
		else
		{
			Thread.Error = '2B: Object expected!';
			result = false;
		}
	}
	return result;
} // .function Bers.CmdSet

Bers.CmdDel = function (Thread)
{
	var result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		if (typeof(Thread.EAX[1]) === 'object')
		{
			delete(Thread.EAX[1][Thread.EAX[0]]);
		}
		else
		{
			Thread.Error = '2B: Object expected!';
			result = false;
		}
	}
	return result;
} // .function Bers.CmdDel

Bers.CmdObj = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 3),
		ArgCount = 0,
		Args = null;
	if (result)
	{
		if ((typeof(Thread.EAX[2]) === 'object') || (typeof(Thread.EAX[2]) === 'string'))
		{
			ArgCount = Math.floor(Thread.EAX[0]);
			if (!isNaN(ArgCount) && (ArgCount >= 0))
			{
				if (Thread.Stack.length >= ArgCount)
				{
					Args = Bers.PopArr(Thread, ArgCount);
					Thread.Stack.push((Thread.EAX[2][Thread.EAX[1]]).apply(Thread.EAX[2], Args));
				}
				else
				{
					Thread.Error = '2B: Stack underflow!';
					result = false;
				}
			}
			else
			{
				Thread.Error = '2B: Invalid arguments count!';
				result = false;
			}
		}
		else
		{
			Thread.Error = '2B: Object expected!';
			result = false;
		}
	}
	return result;
} // .function Bers.CmdObj

Bers.CmdNil = function (Thread)
{
	Thread.Stack.push({});
	return true;
} // .function Bers.CmdNil

Bers.CmdArr = function (Thread)
{
	Thread.Stack.push([]);
	return true;
} // .function Bers.CmdArr

Bers.CmdPush = function (Thread)
{
	Thread.Stack.push(Thread.Code[Thread.EIP + 1]);
	Thread.EIP++;
	return true;
} // .function Bers.CmdPush

Bers.CmdPushVar = function (Thread)
{
	var result = Bers.GetVar(Thread, Thread.Code[Thread.EIP + 1]);
	if (result)
	{
		Thread.Stack.push(Thread.EAX);
		Thread.EIP++;
	}
	return result;
} // .function Bers.CmdPushVar

Bers.CmdDup = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(Thread.EAX[0], Thread.EAX[0]);
	}
	return result;
} // .function Bers.CmdDup

Bers.CmdSwap = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Thread.EAX[1], Thread.EAX[0]);
	}
	return result;
} // .function Bers.CmdSwap

Bers.CmdDrop = function (Thread)
{
	return Bers.Pop(Thread);
} // .function Bers.CmdDrop

Bers.CmdPop = function (Thread)
{
	var	Stack = Thread.Stack,
		result = Bers.PopCmdArgs(Thread, 1) && Bers.GetIntArg(Thread, Thread.EAX[0]),
		i = -1,
		Count = Thread.EAX[0];
	if (result)
	{
		if (Stack.length >= Count)
		{
			for (i = 0; i < Count; i++)
			{
				Stack.pop();
			}
		}
		else
		{
			Thread.Error = '2B: Stack underflow!';
			result = false;
		}
	}
	return result;
} // .function Bers.CmdPop

Bers.CmdCopy = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1) && Bers.GetIntArg(Thread, Thread.EAX[0]),
		Stack = Thread.Stack,
		Offset = -1;
	if (result)
	{
		Offset = Thread.EAX;
		if (Offset < 0)
		{
			Thread.Error = '2B: Negative offset!';
			result = false;
		}
		else if (Offset >= Stack.length)
		{
			Thread.Error = '2B: Stack underflow!';
			result = false;
		}
		else
		{
			Stack.push(Stack[Stack.length - 1 - Offset]);
		}
	}
	return result;
} // .function Bers.CmdCopy

Bers.CmdInc = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(Thread.EAX[0] + 1);
	}
	return result;
} // .function Bers.CmdInc

Bers.CmdDec = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(Thread.EAX[0] - 1);
	}
	return result;
} // .function Bers.CmdDec

Bers.CmdAdd = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Thread.EAX[0] + Thread.EAX[1]);
	}
	return result;
} // .function Bers.CmdAdd

Bers.CmdSub = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Thread.EAX[0] - Thread.EAX[1]);
	}
	return result;
} // .function Bers.CmdSub

Bers.CmdMul = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Thread.EAX[0] * Thread.EAX[1]);
	}
	return result;
} // .function Bers.CmdMul

Bers.CmdDiv = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Thread.EAX[0] / Thread.EAX[1]);
	}
	return result;
} // .function Bers.CmdDiv

Bers.CmdMod = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Thread.EAX[0] % Thread.EAX[1]);
	}
	return result;
} // .function Bers.CmdMod

Bers.CmdPow = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Math.pow(Thread.EAX[0], Thread.EAX[1]));
	}
	return result;
} // .function Bers.CmdPow

Bers.CmdExp = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(Math.exp(Thread.EAX[0]));
	}
	return result;
} // .function Bers.CmdExp

Bers.CmdLn = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(Math.log(Thread.EAX[0]));
	}
	return result;
} // .function Bers.CmdLn

Bers.CmdAbs = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(Math.abs(Thread.EAX[0]));
	}
	return result;
} // .function Bers.CmdAbs

Bers.CmdSqrt = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(Math.sqrt(Thread.EAX[0]));
	}
	return result;
} // .function Bers.CmdSqrt

Bers.CmdFloor = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(Math.floor(Thread.EAX[0]));
	}
	return result;
} // .function Bers.CmdTrunk

Bers.CmdCeil = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(Math.ceil(Thread.EAX[0]));
	}
	return result;
} // .function Bers.CmdCeil

Bers.CmdRound = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(Math.round(Thread.EAX[0]));
	}
	return result;
} // .function Bers.CmdRound

Bers.CmdMin = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Math.min(Thread.EAX[0], Thread.EAX[1]));
	}
	return result;
} // .function Bers.CmdMin

Bers.CmdMax = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Math.max(Thread.EAX[0], Thread.EAX[1]));
	}
	return result;
} // .function Bers.CmdMax

Bers.CmdRand = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Core.Random(Thread.EAX[0], Thread.EAX[1]));
	}
	return result;
} // .function Bers.CmdRand

Bers.CmdSin = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(Math.sin(Thread.EAX[0]));
	}
	return result;
} // .function Bers.CmdSin

Bers.CmdCos = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(Math.cos(Thread.EAX[0]));
	}
	return result;
} // .function Bers.CmdCos

Bers.CmdTan = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(Math.tan(Thread.EAX[0]));
	}
	return result;
} // .function Bers.CmdTan

Bers.CmdASin = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(Math.asin(Thread.EAX[0]));
	}
	return result;
} // .function Bers.CmdASin

Bers.CmdACos = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(Math.acos(Thread.EAX[0]));
	}
	return result;
} // .function Bers.CmdACos

Bers.CmdATan = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(Math.atan(Thread.EAX[0]));
	}
	return result;
} // .function Bers.CmdATan

Bers.Goto = function (Thread, Label)
{
	var Addr = Thread.Labels[Label],
		result = Addr !== undefined;
	if (result)
	{
		Thread.EIP = Addr - 1;
	}
	else
	{
		Thread.Error = '2B: Unknown label "' + Label + '"!';
	}
	return result;
} // .function Bers.Goto

Bers.Invoke = function (Thread, Label)
{
	var Addr = Thread.Labels[Label],
		result = Addr !== undefined;
	if (result)
	{
		Thread.Rets.push(Thread.EIP + 1);
		Thread.Level++;
		Thread.Vars[Thread.Level] = {}
		Thread.EIP = Addr - 1;
	}
	else
	{
		Thread.Error = '2B: Unknown label "' + Label + '"!';
	}
	return result;
} // .function Bers.Invoke

Bers.CmdNumber = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(Number(Thread.EAX[0]));
	}
	return result;
} // .function Bers.CmdNumber

Bers.CmdString = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(String(Thread.EAX[0]));
	}
	return result;
} // .function Bers.CmdString

Bers.CmdEq = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Thread.EAX[0] === Thread.EAX[1]);
	}
	return result;
} // .function Bers.CmdEq

Bers.CmdNotEq = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Thread.EAX[0] !== Thread.EAX[1]);
	}
	return result;
} // .function Bers.CmdNotEq

Bers.CmdIsNaN = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(isNaN(Thread.EAX[0]));
	}
	return result;
} // .function Bers.CmdIsNaN

Bers.CmdAbove = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Thread.EAX[0] > Thread.EAX[1]);
	}
	return result;
} // .function Bers.CmdAbove

Bers.CmdAboveEq = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Thread.EAX[0] >= Thread.EAX[1]);
	}
	return result;
} // .function Bers.CmdAboveEq

Bers.CmdBelow = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Thread.EAX[0] < Thread.EAX[1]);
	}
	return result;
} // .function Bers.CmdBelow

Bers.CmdBelowEq = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Thread.EAX[0] <= Thread.EAX[1]);
	}
	return result;
} // .function Bers.CmdBelowEq

Bers.CmdAnd = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Boolean(Thread.EAX[0]) && Boolean(Thread.EAX[1]));
	}
	return result;
} // .function Bers.CmdAnd

Bers.CmdOr = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Boolean(Thread.EAX[0]) || Boolean(Thread.EAX[1]));
	}
	return result;
} // .function Bers.CmdOr

Bers.CmdXor = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Boolean(Thread.EAX[0]) !== Boolean(Thread.EAX[1]));
	}
	return result;
} // .function Bers.CmdXor

Bers.CmdNot = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(!Boolean(Thread.EAX[0]));
	}
	return result;
} // .function Bers.CmdNot

Bers.CmdBitAnd = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Thread.EAX[0] & Thread.EAX[1]);
	}
	return result;
} // .function Bers.CmdBitAnd

Bers.CmdBitOr = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Thread.EAX[0] | Thread.EAX[1]);
	}
	return result;
} // .function Bers.CmdBitOr

Bers.CmdBitXor = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		Thread.Stack.push(Thread.EAX[0] ^ Thread.EAX[1]);
	}
	return result;
} // .function Bers.CmdBitXor

Bers.CmdBitNot = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		Thread.Stack.push(~Thread.EAX[0]);
	}
	return result;
} // .function Bers.CmdBitNot

Bers.CmdJmp = function (Thread)
{
	return (Bers.PopCmdArgs(Thread, 1) && Bers.Goto(Thread, Thread.EAX[0]));
} // .function Bers.CmdJmp

Bers.CmdJmpTrue = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		if (Boolean(Thread.EAX[0]))
		{
			result = Bers.Goto(Thread, Thread.EAX[1]);
		}
	}
	return result;
} // .function Bers.CmdJmpTrue

Bers.CmdJmpFalse = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		if (!Boolean(Thread.EAX[0]))
		{
			result = Bers.Goto(Thread, Thread.EAX[1]);
		}
	}
	return result;
} // .function Bers.CmdJmpFalse

Bers.CmdCall = function (Thread)
{
	return (Bers.PopCmdArgs(Thread, 1) && Bers.Invoke(Thread, Thread.EAX[0]));
} // .function Bers.CmdCall

Bers.CmdCallTrue = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		if (Boolean(Thread.EAX[0]))
		{
			result = Bers.Invoke(Thread, Thread.EAX[1]);
		}
	}
	return result;
} // .function Bers.CmdCallTrue

Bers.CmdCallFalse = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 2);
	if (result)
	{
		if (!Boolean(Thread.EAX[0]))
		{
			result = Bers.Invoke(Thread, Thread.EAX[1]);
		}
	}
	return result;
} // .function Bers.CmdCallFalse

Bers.CmdRet = function (Thread)
{
	var	Addr = Thread.Rets[Thread.Level],
		result = Addr !== -1;
	if (result)
	{
		delete(Thread.Vars[Thread.Level]);
		Thread.Rets.pop();
		Thread.Level--;
		Thread.EIP = Addr - 1;
	}
	else
	{
		Thread.State = 'Terminated';
	}
	return result;
} // .function Bers.CmdRet

Bers.CmdAlert = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1);
	if (result)
	{
		alert(Thread.EAX[0]);
	}
	return result;
} // .function Bers.CmdAlert

Bers.CmdWrite = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1),
		ID = 'console_' + Thread.Id.slice(Thread.Id.indexOf('_') + 1);
	if (result)
	{
		if (Bers.Console[ID] !== undefined)
		{
			Bers.Console[ID].Write(String(Thread.EAX[0]));
		}
		else
		{
			Thread.Error = '2B: Console does not exist!';
			result = false;
		}
	}
	return result;
} // .function Bers.CmdWrite

Bers.CmdRead = function (Thread)
{
	var	Console = Bers.Console['console_' + Thread.Id.slice(Thread.Id.indexOf('_') + 1)],
		result = Console !== undefined;
	if (result)
	{
		Thread.State = 'Paused';
		Thread.WaitControl = 'Console';
		Console.ReadEx(Call({Thread: Thread}, Bers.CmdReadHandler), -1, -1, 0, 0);
		result = false;
	}
	else
	{
		Thread.Error = '2B: Console does not exist!';
	}
	return result;
} // .function Bers.CmdRead

Bers.CmdReadHandler = function (Txt)
{
	var	Thread = this.Thread;
	Thread.State = 'Running';
	Thread.WaitControl = '';
	Thread.Stack.push(Txt);
	Bers.ResumeThread(Thread);
} // .function Bers.CmdReadHandler

Bers.CmdClearScreen = function (Thread)
{
	var	Console = Bers.Console['console_' + Thread.Id.slice(Thread.Id.indexOf('_') + 1)],
		result = Console !== undefined;
	if (result)
	{
		Console.Clear();
	}
	else
	{
		Thread.Error = '2B: Console does not exist!';
	}
	return result;
} // .function Bers.CmdClearScreen

Bers.CmdSleep = function (Thread)
{
	var	result = Bers.PopCmdArgs(Thread, 1) && Bers.GetIntArg(Thread, Thread.EAX[0]);
	if (result)
	{
		result = false;
		if (Thread.EAX >= 0)
		{
			Thread.State = 'Paused';
			Thread.WaitControl = 'Sleep';
			setTimeout(Call({Thread: Thread}, Bers.CmdSleepHandler), Thread.EAX);
		}
		else
		{
			Thread.Error = '2B: Invalid timeout!';
		}
	}
	return result;
} // .function Bers.CmdSleep

Bers.CmdSleepHandler = function ()
{
	var	Thread = this.Thread;
	Thread.State = 'Running';
	Thread.WaitControl = '';
	Bers.ResumeThread(Thread);
} // .function Bers.CmdSleepHandler

Bers.CmdWindow = function (Thread)
{
	var	Console = Bers.Console['console_' + Thread.Id.slice(Thread.Id.indexOf('_') + 1)],
		i = -1,
		EAX = null,
		result = Console !== undefined;
	if (result)
	{
		result = Bers.PopCmdArgs(Thread, 4);
		EAX = Thread.EAX;
		if (result)
		{
			i = 0;
			while ((i < 4) && result)
			{
				EAX[i] = Math.floor(EAX[i]);
				result = !isNaN(EAX[i]);
				i++;
			}
			if (result)
			{
				Console.Window(EAX[0], EAX[1], EAX[2], EAX[3]);
			}
			else
			{
				Thread.Error = '2B: Invalid window contraints: ' + EAX.join(', ');
			}
		}
	}
	else
	{
		Thread.Error = '2B: Console does not exist!';
	}
	return result;
} // .function Bers.CmdWindow

Bers.CmdWhereX = function (Thread)
{
	var	Console = Bers.Console['console_' + Thread.Id.slice(Thread.Id.indexOf('_') + 1)],
		result = Console !== undefined;
	if (result)
	{
		Thread.Stack.push(Console.x);
	}
	else
	{
		Thread.Error = '2B: Console does not exist!';
	}
	return result;
} // .function Bers.CmdWhereX

Bers.CmdWhereY = function (Thread)
{
	var	Console = Bers.Console['console_' + Thread.Id.slice(Thread.Id.indexOf('_') + 1)],
		result = Console !== undefined;
	if (result)
	{
		Thread.Stack.push(Console.y);
	}
	else
	{
		Thread.Error = '2B: Console does not exist!';
	}
	return result;
} // .function Bers.CmdWhereY

Bers.CmdCol = function (Thread)
{
	var	Console = Bers.Console['console_' + Thread.Id.slice(Thread.Id.indexOf('_') + 1)],
		result = Console !== undefined;
	if (result)
	{
		result = Bers.PopCmdArgs(Thread, 1);
		if (result)
		{
			Console.SetCol(String(Thread.EAX[0]));
		}
	}
	else
	{
		Thread.Error = '2B: Console does not exist!';
	}
	return result;
} // .function Bers.CmdCol

Bers.CmdBack = function (Thread)
{
	var	Console = Bers.Console['console_' + Thread.Id.slice(Thread.Id.indexOf('_') + 1)],
		result = Console !== undefined;
	if (result)
	{
		result = Bers.PopCmdArgs(Thread, 1);
		if (result)
		{
			Console.SetBack(String(Thread.EAX[0]));
		}
	}
	else
	{
		Thread.Error = '2B: Console does not exist!';
	}
	return result;
} // .function Bers.CmdBack

Bers.CmdGotoXY = function (Thread)
{
	var	Console = Bers.Console['console_' + Thread.Id.slice(Thread.Id.indexOf('_') + 1)],
		EAX = null,
		result = Console !== undefined;
	if (result)
	{
		result = Bers.PopCmdArgs(Thread, 2);
		EAX = Thread.EAX;
		if (result)
		{
			EAX[0] = Math.floor(EAX[0]);
			result = !isNaN(EAX[0]) && Core.InRange(EAX[0], 0, Console.WndWidth - 1);
			if (result)
			{
				EAX[1] = Math.floor(EAX[1]);
				result = !isNaN(EAX[1]) && Core.InRange(EAX[1], 0, Console.WndHeight - 1);
				if (result)
				{
					Console.GotoXY(EAX[0], EAX[1]);
				}
				else
				{
					Thread.Error = '2B: Invalid Y coord: ' + EAX[1];
				}
			}
			else
			{
				Thread.Error = '2B: Invalid X coord: ' + EAX[0];
			}
		}
	}
	else
	{
		Thread.Error = '2B: Console does not exist!';
	}
	return result;
} // .function Bers.CmdGotoXY

Bers.Cmds =
{
	// Работа с переменными
	'var':	Bers.CmdVar,			// Объявление переменной (Name)
	mov:	Bers.CmdMov,			// Установить значение переменной (NewValue, VarName)
	val:	Bers.CmdVal,			// Получить значение переменной по её имени (VarName)
	get:	Bers.CmdGet,			// Получить значение свойства объекта (AttrName, Object)
	set:	Bers.CmdSet,			// Установить значение свойства объекта (NewValue, AttrName, Object)
	del:	Bers.CmdDel,			// Удалить свойство у объекта (AttrName, Object)
	obj:	Bers.CmdObj,			// Вызов метода объекта (Arguments..., NumberOfArguments, MethodName, Object)
	nil:	Bers.CmdNil,			// Возвращает пустой объект
	arr:	Bers.CmdArr,			// Возвращает пустой массив
	// Системные
	push:	Bers.CmdPush,			// Положить в стёк (Value), неявный вызов через само значение:	5 'hellow' true
	pushvar:Bers.CmdPushVar,		// Положить в стёк значение переменной (VarName), неявный вызов через имя переменной:	a b c
	// Работа со стёком
	dup:	Bers.CmdDup,			// Продублировать значение на верхушке стёка (Value)
	swap:	Bers.CmdSwap,			// Меняет местами два верхних элемента стёка (Value1, Value2)
	drop:	Bers.CmdDrop,			// Удаляет верхний элемент со стёка (Value)
	copy:	Bers.CmdCopy,			// Положить в стёк копию N-ого элемента стёка, считая от вершины (N), нумерация элементов с нуля
	// Математические
	inc:	Bers.CmdInc,			// Увеличивает на один (Value)
	dec:	Bers.CmdDec,			// Уменьшает на один (Value)
	add:	Bers.CmdAdd,			// Складывает два числа (a, b), => a + b
	sub:	Bers.CmdSub,			// Вычитает два числа (a, b), => a - b
	mul:	Bers.CmdMul,			// Перемножает два числа (a, b) => a * b
	div:	Bers.CmdDiv,			// Делит два числа (a, b) => a / b
	mod:	Bers.CmdMod,			// Возвращает остаток от деления a на b (a, b) => Остаток (a / b)
	pow:	Bers.CmdPow,			// Возводит число в степень (Value, Power) => Value^Power
	exp:	Bers.CmdExp,			// Возвращает e в степени Power (Power) => e^Power
	ln:		Bers.CmdLn,				// Натуральный логарифм от x (x) => ln x
	abs:	Bers.CmdAbs,			// Возвращает модуль числа (x) => |x|
	sqrt:	Bers.CmdSqrt,			// Квадратный корень из числа (x) => x^0.5
	floor:	Bers.CmdFloor,			// Возвращает наибольшее целое, меньшее или равное аргументу
	ceil:	Bers.CmdCeil,			// Округляет число в большую сторону (x):	4.1 => 5, 4.6 => 5, 4.0 = > 4
	round:	Bers.CmdRound,			// Округляет число по правилам математики (x):	4.4 => 4, 4.5 => 5
	min:	Bers.CmdMin,			// Возвращает минимальное из двух чисел (a, b)
	max:	Bers.CmdMax,			// Возвращает максимальное из двух чисел (a, b)
	rand:	Bers.CmdRand,			// Возвращает псевдослучайное число с плавающей запятой в диапазоне 0..1:	0,256875696...
	sin:	Bers.CmdSin,			// Синус числа (x)
	cos:	Bers.CmdCos,			// Косинус числа (x)
	tan:	Bers.CmdTan,			// Тангес числа (x)
	asin:	Bers.CmdASin,			// Арксинус числа (x)
	acos:	Bers.CmdACos,			// Арккосинус числа (x)
	atan:	Bers.CmdATan,			// Арктангенс числа (x)
	// Преобразования и проверки типов
	isnan:	Bers.CmdIsNaN,			// Возвращает true, если значение не является числом
	num:	Bers.CmdNumber,			// Преобразует аргумент к числу. Осторожно, нужна проверка на isnan
	str:	Bers.CmdString,			// Преобразует аргумент в строку
	// Логические операции (возвращают true или false)
	'=':	Bers.CmdEq,				// Возвращает true, если два значения равны и их типы одинаковы (a, b)
	'<>':	Bers.CmdNotEq,			// Возвращает true, если два значения не равны или их типы разные (a, b)
	'<':	Bers.CmdBelow,			// Возвращает true, если a < b (a, b)
	'<=':	Bers.CmdBelowEq,		// Возвращает true, если a <= b (a, b)
	'>':	Bers.CmdAbove,			// Возвращает true, если a > b (a, b)
	'>=':	Bers.CmdAboveEq,		// Возвращает true, если a >= b (a, b)
	// Операции над логическими типами (с автоматическим приведением аргументом к логическому)
	'and':	Bers.CmdAnd,			// Возвращает true, если оба аргумента истинны (a, b)
	'or':	Bers.CmdOr,				// Возвращает true, если хотя бы один из аргументов истинен (a, b)
	'xor':	Bers.CmdXor,			// Возвращает true, если первый аргумент либо второй истинен (a, b)
	'not':	Bers.CmdNot,			// Логическое отрицание аргумента (a):	true => false, false => true
	// Операции над группами бит
	bitand:	Bers.CmdBitAnd,			// AND (a, b)
	bitor:	Bers.CmdBitOr,			// OR (a, b)
	bitxor:	Bers.CmdBitXor,			// XOR (a, b)
	bitnot:	Bers.CmdBitNot,			// NOT (a)
	// Управление потом исполнения
	jmp:	Bers.CmdJmp,			// Осуществляет прыжок на указанную метку (LabelName)
	jt:		Bers.CmdJmpTrue,		// Прыжок на метку, если условие истинно (Condition, LabelName)
	jf:		Bers.CmdJmpFalse,		// Прыжок на метку, если условие ложно (Condition, LabelName)
	call:	Bers.CmdCall,			// Вызов подпрограммы (LabelName)
	callt:	Bers.CmdCallTrue,		// Вызов подпрограммы, если условие истинно (Condition, LabelName)
	callf:	Bers.CmdCallFalse,		// Вызов подпрограммы, если условие ложно (Condition, LabelName)
	ret:	Bers.CmdRet,			// Возврат из подпрограммы или основного кода
	// Диалоги
	alert:	Bers.CmdAlert,			// Отображает сообщение с указанным текстом (Message)
	// Консоль
	clrscr:	Bers.CmdClearScreen,	// Очищает окно консоли
	read:	Bers.CmdRead,			// Считывает строку из консоли
	write:	Bers.CmdWrite,			// Выводит строку в консоль (String)
	sleep:	Bers.CmdSleep,			// Ожидание в миллисекундах (NumMSecons)
	window:	Bers.CmdWindow,			// Устанавливает новые границы текущего окна (x1, y1, x2, y2)
	col:	Bers.CmdCol,			// Устанавливает новый цвет символов (Color)
	back:	Bers.CmdBack,			// Устанавливает новый фон символов (Background)
	gotoxy:	Bers.CmdGotoXY			// Переводит курсор к указанным координатам (x, y)
}; // Bers.Cmds

Bers.Console = {};

Bers.Run2B = function (PostN, UserName)
{
	var	ID = 'console_' + PostN;
	if ((Bers.Console[ID] === undefined) && ($(ID) !== null))
	{
		Bers.Console[ID] = new Console($(ID), 'con' + PostN + '_', Number($(ID).getAttribute('w')), Number($(ID).getAttribute('h')), 'white', 'black', true);
	}
	if ($(ID) !== null)
	{
		$(ID).onclick();
	}
	Bers.Trigger('s2b_' + PostN, [UserName]);
} // .function Bers.Run2B
