function TemplateManager ($edit_url, $browse_url, $save_layout_url, $edting_mode) {
	this._editUrl = $edit_url;
	this.browseUrl = $browse_url;
	this._saveLayoutUrl = $save_layout_url;
	this.editingMode = $edting_mode; // from {1 - browse, 2 - content, 3 - design}

	this._blocks = {};
	this._blockOrder = Array ();
	this.inDrag = false; // don't process mouse over/out events while in drag mode

	var $template_manager = this;

	$(document).ready(
		function() {
			$template_manager.searchBlocks();

			if (!$template_manager.editingMode) {
				return ;
			}

			// show special toolbar when in any of 3 browse modes
			var $head_frame = getFrame('head');
			var $extra_toolbar = $head_frame.$('div.front-extra-toolbar').clone(); // clone to keep original untouched

			$('a', $extra_toolbar).each(
				function() {
					// cut from end, because IE7 adds base_href to beginning of href
					var $editing_mode = $(this).attr('href');
					$editing_mode = $editing_mode.substr($editing_mode.length - 1, 1);

					$(this).attr('href', $template_manager.browseUrl.replace('#EDITING_MODE#', $editing_mode));

					if ($editing_mode == $template_manager.editingMode) {
						$(this).parents('td:first').addClass('button-active').prevAll('td:first').addClass('button-active');
					}
				}
			);

			$head_frame.$('#extra_toolbar').html( $extra_toolbar.html() );

			var $hover_effect = [];

			if ($template_manager.editingMode > 1) {
				// all modes except for "Browse Mode"
				$hover_effect.push('div.cms-section-properties-btn:first');
			}

			if ($template_manager.editingMode == 2) {
				// Content Mode
				$hover_effect.push('div.cms-edit-btn');

				// make all spans with phrases clickable
				$template_manager.setupEditTranslationButtons(document);
			}

			if ($template_manager.editingMode == 3) {
				// Design Mode
//				$hover_effect.push('div.cms-save-layout-btn:first, div.cms-cancel-layout-btn:first');

				$template_manager.renumberMovableElements();

				$('div.movable-area').sortable(
					{
						placeholder: 'move-helper',
						handle: '.movable-header',
						items: 'div.movable-element',
						connectWith: ['div.movable-area'],
						tolerance: 'pointer',
						start: function(e, ui) {
							$template_manager.inDrag = true;
							ui.placeholder.height( ui.item.height() );
						},
						stop: function(e, ui) {
							$template_manager.inDrag = false;

							// mouseout doesn't happen while in drag, so compensate it here
							var $header = $('.movable-header', ui.item);
							$('div.block-edit-block-btn-container', $header).mouseout();
						},
						change: function(e, ui) {
							$('div.cms-layout-btn-container').show();
						}
					}
				);
			}

			// make requested elements fully visible on mouseover
			$($hover_effect.join(', '))
			.mouseover(
				function(e) {
					$(this).css('opacity', 1);
				}
			)
			.mouseout(
				function(e) {
					$(this).css('opacity', 0.5);
				}
			);
		}
	);
}

TemplateManager.prototype.setupEditTranslationButtons = function ($container) {
	$("span[name='cms-translate-phrase']", $container).each(
		function() {
			var $me = $(this);
			var $parent_link = $me.parents('a:first');

			if ($parent_link.length == 0) {
				// span in not inside "a" tag

				$me.prepend('<div class="cms-edit-btn"><div class="cms-btn-text">Edit</div></div>');
				$('div.cms-edit-btn:first', $me).click(TemplateManager.prototype.translatePhrase);
				$me.dblclick(
					function ($e) {
						$('div.cms-edit-btn:first', this).click();
						return false;
					}
				);

				var $effect_element = $me;
			}
			else {
				// span is inside "a" tag
				var $clone = $me.clone();
				$clone.empty().attr('title', '');

				// in case if "a" tag is "display: block", then make "span" the same
				$clone.css('display', $parent_link.css('display'));

				$parent_link.html( $me.html() ).wrap($clone);
				$parent_link.before('<div class="cms-edit-btn" title="' + $me.attr('title') + '"><div class="cms-btn-text">Edit</div></div>');
				$parent_link.prev('div.cms-edit-btn:first').click(TemplateManager.prototype.translatePhrase);

				var $effect_element = $parent_link.parents("span[name='cms-translate-phrase']:first");
			}

			$effect_element
			.mouseover(
				function($e) {
					$('div.cms-edit-btn', this).css('display', 'inline');
				}
			)
			.mouseout(
				function($e) {
					$('div.cms-edit-btn', this).hide();
				}
			);
		}
	);
}

TemplateManager.prototype.translatePhrase = function ($e) {
	var $translate_url = $(this).parents("span[name='cms-translate-phrase']:first").attr('href');

	if ($translate_url.match(/javascript:(.*)/)) {
		eval(RegExp.$1);
	}
	else {
		window.location.href = $translate_url;
	}

	return false;
}

TemplateManager.prototype.renumberMovableElements = function () {
	var $area_index = 0;
	// 1. dynamically assign IDs to all movable elements
	$('div.movable-area').each(
		function() {
			var $element_index = 0;
			$('div.movable-element', this).each(
				function() {
					$(this).attr('id', 'target_order_a' + $area_index + 'e' + $element_index);
					$element_index++;
				}
			);

			$area_index++;
		}
	);
}

TemplateManager.prototype.saveLayout = function () {
	// prepare order string
	var $sort_order = [];
	$('div.movable-area').each(
		function($area_index) {
			var $order = $(this).sortable('serialize').replace(/target_order\[\]/g, 'target_order[' + $area_index + '][]');
			if ($order) {
				$sort_order.push($order);
			}
		}
	);
	$sort_order = $sort_order.join('&');

	// save order string
	var $me = this;

	var $settings = {
		url: this._saveLayoutUrl + '&' + $sort_order + '&width=200&height=70&modal=true',
		caption: 'Layout Saving Result',
		onDataReceived: function ($data) {
			var $message = '';

			if ($data == 'OK') {
				$message = 'New Layout Saved';
				$('div.cms-layout-btn-container').hide();
				$me.renumberMovableElements();
			}
			else {
				$message = 'Failed to Save New Layout';
			}

			$data = '<div style="text-align: center;">' + $message + '<br/><br/><input type="button" class="button" value="OK" onclick="TB.remove();"/></div>';

			return $data;
		}
	};

	TB.setWindowTitle( window.top.document.title.replace(main_title + ' :: ', '') );
	TB.show($settings);
}

TemplateManager.prototype.cancelLayout = function () {
	window.location.href = window.location.href;
}

TemplateManager.prototype.onBtnClick = function ($e, $element) {
	var $id = $element.id.replace(/_btn$/, '');
	var $block_info = this._blocks[$id];
	var $url = this._editUrl.replace('#BLOCK#', $block_info.block_name + ':' + $block_info.function_name).replace('#EVENT#', 'OnLoadBlock');

	direct_edit('theme-file', $url);

	$e.stopPropagation();
}

TemplateManager.prototype.onMouseOver = function ($e, $element) {
	if (this.inDrag) {
		return ;
	}

	$element = $($element);

	if ($element.hasClass('block-edit-design-btn-container')) {
		$($element).addClass('block-edit-design-btn-container-over');

		var $button_group = $('div.cms-edit-design-btn-container:first', $element);
		if ($button_group.length) {
			$button_group.show();
		}
		else {
			$('div.cms-edit-design-btn:first', $element).show();
		}
	}
	else {
		$($element).addClass('block-edit-block-btn-container-over');
		$('div.cms-edit-block-btn:first', $element).show();
	}

	$e.stopPropagation();
}

TemplateManager.prototype.onMouseOut = function ($e, $element) {
	if (this.inDrag) {
		return ;
	}

	$element = $($element);

	if ($element.hasClass('block-edit-design-btn-container')) {
		$($element).removeClass('block-edit-design-btn-container-over');

		var $button_group = $('div.cms-edit-design-btn-container:first', $element);
		if ($button_group.length) {
			$button_group.hide();
		}
		else {
			$('div.cms-edit-design-btn:first', $element).hide();
		}
	}
	else {
		$($element).removeClass('block-edit-block-btn-container-over');
		$('div.cms-edit-block-btn:first', $element).hide();
	}

	$e.stopPropagation();
}

TemplateManager.prototype.searchBlocks = function () {
	var $design_containers = $('div.block-edit-design-btn-container');
	var $block_containers = $('div.block-edit-block-btn-container');

	$design_containers.each(
		function() {
			var $block_container = $('div.block-edit-block-btn-container:first', this);

			if ($block_container.length) {
				$block_containers = $block_containers.not($block_container);

				// place "Edit Block" button near "Edit Design" button
				var $edit_design_btn = $('div.cms-edit-design-btn:first', this);
				var $edit_block_btn = $('div.cms-edit-block-btn:first', $block_container);

				$edit_design_btn
				.wrap('<div class="cms-edit-design-btn-container"></div>')
				.before( $edit_block_btn.clone() );

				$edit_block_btn.remove();

				// make "hint" from "Edit Block" button container main
				$(this).attr('title', $block_container.attr('title'));
				$block_container.attr('title', '');

				TemplateManager.prototype.registerBlock.call(aTemplateManager, $block_container.get(0), ['hover']);
			}

			TemplateManager.prototype.registerBlock.call(aTemplateManager, this, ['dblclick']);
		}
	);

	$block_containers.each(
		function() {
			TemplateManager.prototype.registerBlock.call(aTemplateManager, this);
		}
	);

//	$('div').each (
//		function () {
//			/*var $id = $(this).attr('id');
//
//			if (!$id || $id.match(/parser_block\[.*\].*_btn$/) || !$id.match(/parser_block\[.*\]/)) {
//				// skip other divs
//				return true;
//			}*/
//
//
//			TemplateManager.prototype.registerBlock.call(aTemplateManager, this);
//		}
//	);
}

TemplateManager.prototype.registerBlock = function ($element, $skip_events) {
	var $params = $element.getAttribute('params').split(':');

	this._blocks[$element.id] = {
		block_name: $params[0],
		function_name: $params[1]
	};

	var $btn = document.getElementById($element.id + '_btn');

	$($btn).click(
		function(ev) {
			TemplateManager.prototype.onBtnClick.call(aTemplateManager, ev, this);
		}
	);

	if ($skip_events === undefined) {
		$skip_events = [];
	}

	if (!in_array('dblclick', $skip_events)) {
		$($element)
		.dblclick(
			function(ev) {
				TemplateManager.prototype.onBtnClick.call(aTemplateManager, ev, this);
			}
		)
	}

	if (!in_array('hover', $skip_events)) {
		$($element)
		.mouseover(
			function(ev) {
				TemplateManager.prototype.onMouseOver.call(aTemplateManager, ev, this);
			}
		)
		.mouseout(
			function(ev) {
				TemplateManager.prototype.onMouseOut.call(aTemplateManager, ev, this);
			}
		);
	}

	this._blockOrder.push($element.id);
}