function Catalog($url_mask, $cookie_prefix, $type) {
	this.type = $type;
	this.CookiePrefix = $cookie_prefix ? $cookie_prefix : '';
	this.BusyRequest = new Array();
	this.URLMask = $url_mask;
	this.Separator = '#separator#';
	this.ParentCategoryID = 0;
	this.OnResponceMethod = null;
	this.TabRegistry = new Array();

	if (window.location.hash && window.location.hash.match(/^#tab-(.*)/)) {
		// get active tab from anchor
		this.ActivePrefix = RegExp.$1;
	}
	else {
		// get active tab from cookie
		this.ActivePrefix = getCookie(this.CookiePrefix + 'active_prefix');
	}

	this.PreviousPrefix = this.ActivePrefix;
	this.TabByCategory = false; // preselect tab by category (used in catalog only)
	$ViewMenus = new Array('c');

	this.searchInfo = {}; // information about search (for each tab)
}

Catalog.prototype.Init = function ($auto_hide_tabs) {
	var $prefix = this.queryTabRegistry('prefix', this.ActivePrefix, 'prefix');
	if ($prefix !== this.ActivePrefix && this.TabRegistry.length > 0) {
		// ActivePrefix not set or has non-existing prefix value
		this.ActivePrefix = this.TabRegistry[0]['prefix'];
	}

	if ($auto_hide_tabs === undefined) {
		$auto_hide_tabs = true;
	}

	if ((this.TabRegistry.length == 1) && $auto_hide_tabs) {
		// only one tab -> hide all tab bar
		$('div.tab-viewport').parents('table:first').hide();
	}

	this.SetAlternativeTabs();
	this.AfterInit();
}

Catalog.prototype.AfterInit = function () {
	this.go_to_cat();
}

Catalog.prototype.SetAlternativeTabs = function () {
	// set alternative grids between all items (catalog is set when tab is loaded via AJAX first time)
	var $i = 0;
	while ($i < this.TabRegistry.length) {
		// run through all prefixes
		var $j = 0;
		while ($j < this.TabRegistry.length) {
			if (this.TabRegistry[$i]['prefix'] == this.TabRegistry[$j]['prefix']) {
				$j++;
				continue;
			}
			// and set alternative to all other prefixes
			$GridManager.AddAlternativeGrid(this.TabRegistry[$i]['prefix'], this.TabRegistry[$j]['prefix']);
			$j++;
		}
		$i++;
	}
}

Catalog.prototype.submit_kernel_form = function($tab_id) {
	var $prefix = 'dummy';
	var $result_div = '';

	if (isset($tab_id)) {
		// responce result + progress are required
		$prefix = this.queryTabRegistry('tab_id', $tab_id, 'prefix');
		$result_div = $tab_id + '_div';
	}

	var $kf = document.getElementById($form_name);

	Request.params = Request.serializeForm($kf);
	Request.method = $kf.method.toUpperCase();

	this.BusyRequest[$prefix] = false;
	Request.makeRequest($kf.action, this.BusyRequest[$prefix], $result_div, this.successCallback, this.errorCallback, $result_div, this);

	$form_name = 'kernel_form'; // restore back to main form with current category id of catalog
}

Catalog.prototype.successCallback = function($request, $params, $object) {
	if (Request.processRedirect($request)) {
		return ;
	}

	var $text = $request.responseText;

	$params = $params.split(',');

	var $js_end = $text.indexOf($object.Separator);
	if ($js_end != -1) {
		// allow to detect if output is permitted by ajax request parameters
		var $request_visible = '$request_visible = ' + ($params[0].length ? 'true' : 'false') + "\n";

		if ($params[0].length) {
//			document.getElementById($params[0]).innerHTML = $text.substring($js_end + $object.Separator.length);
			$( document.getElementById($params[0]) ).html( $text.substring($js_end + $object.Separator.length) );
			eval($request_visible + $text.substring(0, $js_end));
		}
		else {
			// eval JS only & set mark that js should not use HTML as usual in grids
			eval($request_visible + $text.substring(0, $js_end));
		}
	}
	else if ($params[0].length) {
		document.getElementById($params[0]).innerHTML = $text;
	}
	
	// pass catalog object and loaded tab name to event handler
	$('body').trigger('CatalogTabLoad', [$object, $params[0].replace(/_div$/, '')]);
	
	if (typeof($object.OnResponceMethod) == 'function') {
		 $object.OnResponceMethod($object);
		 $object.OnResponceMethod = null;
	}

	if (typeof($Debugger) != 'undefined') {
		$Debugger.Clear();
	}

//	var $tab_id = $params[0].replace(/_div$/, '');
//	var $prefix = $object.queryTabRegistry('tab_id', $tab_id, 'prefix');
}

Catalog.prototype.resizeGrid = function ($prefix) {
	if ($prefix != this.ActivePrefix) {
		// no need to resize, because grid is already visible
		return true;
	}

	var $grid = GridScrollers[$prefix];
	if ($grid) {
		$grid.RefreshPos();
		$grid.Resize( $grid.GetAutoSize('auto') );

		return true;
	}

	return false;
}

Catalog.prototype.trim = function ($string) {
	return $string.replace(/\s*((\S+\s*)*)/, "$1").replace(/((\s*\S+)*)\s*/, "$1");
}

Catalog.prototype.errorCallback = function($request, $params, $object) {
//	$Debugger.ShowProps($request, 'req');
	alert('AJAX Error; class: Catalog; ' + Request.getErrorHtml($request));
}

Catalog.prototype.submit_event = function($prefix_special, $event, $t, $OnResponceMethod) {
	if (typeof($OnResponceMethod) == 'function') {
		this.OnResponceMethod = $OnResponceMethod;
	}

	var $prev_template = get_hidden_field('t');
	if (!isset($prefix_special)) $prefix_special = this.getCurrentPrefix();
	var $tab_id = this.queryTabRegistry('prefix', $prefix_special, 'tab_id');

	$form_name = $tab_id + '_form'; // set firstly, because set_hidden_field uses it
	if (isset($event)) set_hidden_field('events[' + $prefix_special + ']', $event);
	if (isset($t)) set_hidden_field('t', $t);

	this.submit_kernel_form($tab_id);
	set_hidden_field('t', $prev_template);
}


Catalog.prototype.go_to_cat = function($cat_id, $module_prefix) {
	if (!isset($cat_id)) {
		// gets current category
		$cat_id = get_hidden_field('m_cat_id');
	}
	else {
		// sets new category to kernel_form in case if item tab
		// loads faster and will check if it's category is same
		// as parent category of categories list

		if (get_hidden_field('m_cat_id') == $cat_id) {
			// it's the same category, then don't reload category list
			return ;
		}
		set_hidden_field('m_cat_id', $cat_id);
	}

	// 1. make all tabs unselected
	this.resetTabs(false);

	// 2. update grid for current tab
	this.switchTab(this.TabByCategory ? $module_prefix : null); // refresh current item tab

	// 3. update counters for all tabs
	var $prefix = this.TabRegistry[0]['prefix'];

	var $url = this.URLMask.replace('#TEMPLATE_NAME#', 'catalog/catalog_counters').replace('#CATEGORY_ID#', $cat_id);
	this.BusyRequest[$prefix] = false;
	Request.makeRequest($url, this.BusyRequest[$prefix], '', this.updateCounters, this.errorCallback, '', this);
}

Catalog.prototype.updateCounters = function($request, $params, $object) {
	if (Request.processRedirect($request)) {
		return ;
	}

	eval($request.responseText);
}

// set all item tabs counters to "?" before quering catagories
Catalog.prototype.resetTabs = function($reset_content) {
	var $i = 0;
	while ($i < this.TabRegistry.length) {
		this.setItemCount(this.TabRegistry[$i]['prefix'], '?');
		$i++;
	}

	if ($reset_content) {
		// set category for all tabs to -1 (forces reload next time)
		$i = 0;
		while ($i < this.TabRegistry.length) {
			document.getElementById(this.TabRegistry[$i]['tab_id'] + '_div').setAttribute('category_id', -1);
			$i++;
		}
	}
}

Catalog.prototype.switchTab = function($prefix, $force) {
	if (this.queryTabRegistry('prefix', this.ActivePrefix, 'prefix') != this.ActivePrefix) {
		// active prefix is not registred -> cookie left, but not modules installed/enabled at the moment
		return false;
	}

	if (!isset($prefix) || $prefix == '' || this.queryTabRegistry('prefix', $prefix, 'prefix') === false) {
		// prefix not given OR empty prefix OR non-existing tab
		$prefix = this.ActivePrefix;
	}

	if (this.BusyRequest[$prefix]) {
		alert('prefix: ['+$prefix+']; request busy: ['+this.BusyRequest[$prefix]+']');
	}

	if (this.ActivePrefix != $prefix) {
		// hide source tab
		this.PreviousPrefix = this.ActivePrefix;
		$( jq('#' + this.PreviousPrefix + '_tab') ).removeClass('tab-active');

		var $prev_div_id = this.queryTabRegistry('prefix', this.PreviousPrefix, 'tab_id') + '_div';
		document.getElementById($prev_div_id).style.display = 'none';

		var $prev_div_container = $( jq('#' + $prev_div_id + '_container') );
		if ($prev_div_container.length) {
			$prev_div_container.hide();
		}

		this.HideDependentButtons(this.PreviousPrefix);
	}

	// show destination tab
	this.ActivePrefix = $prefix;
	$( jq('#' + this.ActivePrefix + '_tab') ).addClass('tab-active');

	var $div_id = this.queryTabRegistry('prefix', this.ActivePrefix, 'tab_id') + '_div'; // destination tab
	document.getElementById($div_id).style.display = 'block';

	var $div_container = $( jq('#' + $div_id + '_container') );
	if ($div_container.length) {
		$div_container.show();
	}

	this.ShowDependentButtons(this.ActivePrefix);

	this.displaySearch($prefix);
	this.setViewMenu(this.ActivePrefix);

	setCookie(this.CookiePrefix + 'active_prefix', this.ActivePrefix);
	window.location.hash = '#tab-' + this.ActivePrefix;

	this.refreshTab($prefix, $div_id, $force);
}

Catalog.prototype.refreshTab = function($prefix, $div_id, $force) {
	var $cat_id = get_hidden_field('m_cat_id');
	var $tab_cat_id = document.getElementById($div_id).getAttribute('category_id');
	if ($cat_id != $tab_cat_id || $force) {
		// query tab content only in case if not queried or category don't match
		var $url = this.URLMask.replace('#TEMPLATE_NAME#', this.queryTabRegistry('prefix', $prefix, 'view_template'));
		$url = $url.replace('#CATEGORY_ID#', $cat_id);
		$url = $url.replace('#PREFIX#', $prefix);

		this.BusyRequest[$prefix] = false;
		Request.makeRequest($url, this.BusyRequest[$prefix], $div_id, this.successCallback, this.errorCallback, $div_id, this);
	}
	else {
//		alert('refresh disabled = {tab: '+this.ActivePrefix+'; cat_id: '+$cat_id+'; form_name: '+$form_name+'}');
		this.resizeGrid($prefix);
	}
}

// adds information about tab to tab_registry
Catalog.prototype.registerTab = function($tab_id) {
	var $tab = document.getElementById($tab_id + '_div');
	var $index = this.TabRegistry.length;
	this.TabRegistry[$index] = new Array();
	this.TabRegistry[$index]['tab_id'] = $tab_id;
	this.TabRegistry[$index]['prefix'] = $tab.getAttribute('prefix');
	if ($tab_id == 'categories') {
		this.TabRegistry[$index]['module_path'] = 'in-portal/';
	}
	else {
		this.TabRegistry[$index]['module_path'] = $tab.getAttribute('edit_template').substring(0, $tab.getAttribute('edit_template').indexOf('/'));
	}
	this.TabRegistry[$index]['view_template'] = $tab.getAttribute('view_template');
	this.TabRegistry[$index]['edit_template'] = $tab.getAttribute('edit_template');
	this.TabRegistry[$index]['dep_buttons'] = $tab.getAttribute('dep_buttons').length > 0 ? $tab.getAttribute('dep_buttons').split(',') : new Array();
	this.TabRegistry[$index]['index'] = $index;
}

// allows to get any information about tab
Catalog.prototype.queryTabRegistry = function($search_key, $search_value, $return_key) {
	var $i = 0;
//	alert('looking in '+$search_key+' for '+$search_value+' will return '+$return_key)
	while ($i < this.TabRegistry.length) {
		if (this.TabRegistry[$i][$search_key] == $search_value) {
//			alert('got '+this.TabRegistry[$i][$return_key])
			return this.TabRegistry[$i][$return_key];
			break;
		}
		$i++;
	}
	return false;
}

Catalog.prototype.ShowDependentButtons = function($prefix) {
	/*var $tab_id = this.queryTabRegistry('prefix', $prefix, 'tab_id')
	if (!document.getElementById($tab_id + '_form')) {
		// tab form not found => no permission to view -> no permission to do any actions
		alert('no form: ['+$tab_id + '_form'+']');
		return ;
	}
	else {
		alert('has form: ['+$tab_id + '_form'+']');
	}*/

	var $dep_buttons = this.queryTabRegistry('prefix', $prefix, 'dep_buttons');
	var $i = 0;
	while ($i < $dep_buttons.length) {
		a_toolbar.ShowButton($dep_buttons[$i]);
		$i++;
	}
}

Catalog.prototype.HideDependentButtons = function($prefix) {
	var $dep_buttons = this.queryTabRegistry('prefix', $prefix, 'dep_buttons');
	var $i = 0;
	while ($i < $dep_buttons.length) {
		a_toolbar.HideButton($dep_buttons[$i]);
		$i++;
	}
}

Catalog.prototype.setItemCount = function($prefix, $count) {
	setInnerHTML($prefix + '_item_count', $count);
}

Catalog.prototype.saveSearch = function ($prefix, $keyword, $grid_name) {
	this.searchInfo[$prefix] = {
		'keyword': $keyword,
		'grid': $grid_name
	}

	this.displaySearch($prefix);
}

Catalog.prototype.displaySearch = function ($prefix) {
	var $search_input = $('#search_keyword');

	$search_input.attr('PrefixSpecial', $prefix);

	if (this.searchInfo[$prefix]) {
		$search_input
		.val(this.searchInfo[$prefix].keyword)
		.attr('Grid', this.searchInfo[$prefix].grid);

		if (this.searchInfo[$prefix].keyword) {
			// catalog tab found and keyword present
			$search_input.addClass('filter-active');
		}
		else {
			// catalog tab found and keyword missing
			$search_input.removeClass('filter-active');
		}
	}
	else {
		// catalog tab not found
		$search_input.removeClass('filter-active');
	}
}

Catalog.prototype.setCurrentCategory = function($prefix, $category_id) {
	var $tab_id = this.queryTabRegistry('prefix', $prefix, 'tab_id');
//	alert('setting current category for prefix: ['+$prefix+']; tab_id ['+$tab_id+'] = ['+$category_id+']');
	document.getElementById($tab_id + '_div').setAttribute('category_id', $category_id);
}


Catalog.prototype.getCurrentPrefix = function() {
	return this.ActivePrefix;
}

Catalog.prototype.setViewMenu = function($item_prefix) {
	$ViewMenus = isset($item_prefix) ? new Array($item_prefix) : new Array();
}

Catalog.prototype.reflectPasteButton = function($status) {
	a_toolbar.SetEnabled('paste', $status);
	a_toolbar.SetEnabled('clear_clipboard', $status);
}

Catalog.prototype.storeIDs = function ($category_variable) {
	if ($category_variable) {
		var $category_ids = '';

		if (Grids['c'] != undefined) {
			$category_ids = Grids['c'].GetSelected().join(',');
		}

		if (!$category_ids) {
			$category_ids = get_hidden_field('m_cat_id'); // current category
		}

		set_hidden_field($category_variable, $category_ids);
	}

	// get selectes category items
	for (var $i in Grids[this.ActivePrefix].Items) {
		if (Grids[this.ActivePrefix].Items[$i].selected) {
			set_hidden_field(Grids[this.ActivePrefix].Items[$i].CheckBox.id, 'on', false);
		}
	}

}