var ADMIN_INTERFACE_ID = 1;
var ADV_INTERFACE_ID = 2;
var WEB_INTERFACE_ID = 3;

var reqs = [];
var req_id = 0;
var footer_height = 67;
var header_height = null;
var content_height;

function form2url( f ) {
	var url = f.action;
	for (var i = 0; i < f.elements.length; i++) {
		url = add_param2url(url, f.elements[i].name, f.elements[i].value);
	}
	return url;
}

function add_param2url( url, name, value ){
	if (name) {
		url += (url.match(/\?/) ? '&' : '?') + name + '=' + encodeURIComponent(value);
	}
	return url;
}

function utf2win( s ){
	var i;
	if( s == undefined ){
		return undefined;
	}
	var r = '';
	for( i = 0; i < s.length; i++ ){
		var c = s.charCodeAt( i );
		if( (c >= 1040) && (c <= 1103) ){
			r += String.fromCharCode( c - 848 );
		} else if( c == 1025 ){
			r += String.fromCharCode( 168 );
		} else if( c == 1105 ){
			r += String.fromCharCode( 184 );
		} else{
			r += String.fromCharCode( c );
		}
	}
	return r;
}

function get_ajax_request() {
	var xmlHttp;
	try {
		// Firefox, Opera 8.0+, Safari
		xmlHttp = new XMLHttpRequest();
	} catch( e ){
		// Internet Explorer
		try {
			xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
		} catch( e ){
			try {
					xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
			} catch( e ){
				alert("Your browser does not support AJAX!");
				return false;
			}
		}
	}
	return xmlHttp;
}

function calendar_setup(id) {
	Calendar.setup({
			inputField : id,
			ifFormat   : '%d.%m.%Y',
			firstDay   : 1
	});
}

function change_display(div_id) {
	var div = document.getElementById(div_id);
	div.style.display = div.style.display == 'none' ? 'block' : 'none';
}

function open_win(imgSrc, imgW, imgH, win_name) {
	var w = imgW + 20;
	var h = imgH + 20 + 50;
	var leftPosition = (screen.width) ? (screen.width-w)/2 : 0;
	var topPosition = (screen.height) ? (screen.height-h)/2 : 0;
	var winParams = "left=" + leftPosition + ",top=" + topPosition + ",width=" + w + ",height=" + h;
 	var win = window.open( imgSrc, win_name, "toolbar=0,scrollbars=0,location=0,directories=0,status=0,menubar=0,resizable=0," + winParams );
 	win.focus();
}

function load_into_div(url, div_id) {
	var req = get_ajax_request();
	var id = req_id++;
	reqs[id] = req;
	req.open('POST', url, true);
	req.onreadystatechange = new Function(
		"handle_response('" + id + "', '" + div_id + "');"
	);
	req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
	req.setRequestHeader("Content-length", 0);
	req.setRequestHeader("Connection", "close");
	req.send('');
}

function handle_response( id, div_id ){
	var req = reqs[id];
	if( req.readyState == 4 ){
		document.getElementById(div_id).innerHTML = req.responseText;
		reqs[id] = null;
	}
}

function submitenter( myfield, e ){
	var keycode;
	if( window.event ){
		keycode = window.event.keyCode;
	}else if( e ){
		keycode = e.which;
	}
	if( keycode == 13 ){
		myfield.form.submit();
		return false;
	}
	return true;
}

function fix_page(){
	var content_height_div = $( '#content_height' );
	if( content_height_div[0] ){
		if( content_height_div.height() != content_height ){
			var content_div = document.getElementById( 'content' );
			if( content_div && header_height != null ){
				var sub_content_div = document.getElementById( 'sub_content' );
				var page_height = document.body.offsetHeight;
				content_div.style.height = '';
				if( sub_content_div ){
					sub_content_div.style.height = '';
				}
				var new_content_height = content_div.offsetHeight;
				if( page_height >= header_height + new_content_height + footer_height ){
					var new_content_height_str = (page_height - header_height - footer_height) + 'px';
					content_div.style.height = new_content_height_str;
					if( sub_content_div ){
						sub_content_div.style.height = new_content_height_str;
					}
				}
			}
			content_height = content_height_div.height();
		}
	}
}

function get_dump( obj ){
	var s = '';
	for( var key in obj ){
		s += key + ': ' + obj[key] + "\n";
	}
	return s;
}

function dump( obj ){
	alert( get_dump( obj ) );
}

function trim( str ){
	return str.replace( /^\s\s*/, '' ).replace( /\s\s*$/, '' );
}

function array_map( callback ){
    //
    // +   original by: Andrea Giammarchi (http://webreflection.blogspot.com)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
	var argc = arguments.length, argv = arguments;
    var j = argv[1].length, i = 0, k = 1, m = 0;
    var tmp = [], tmp_ar = [];
    while( i < j ){
        while( k < argc ){
            tmp[m++] = argv[k++][i];
        }
        m = 0;
        k = 1;
        if( callback ){
            tmp_ar[i++] = callback.apply( null, tmp );
        } else {
            tmp_ar[i++] = tmp;
        }
        tmp = [];
    }
    return tmp_ar;
}

function topadvert_create_coords( e ){
    var x = 0;
    var y = 0;
    if( e.pageX || e.pageY ){
        x = e.pageX;
        y = e.pageY;
    } else if( e.clientX || e.clientY ){
        x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
        y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
    }
    if( x < 0 ){
        x = 0;
    }
    if( y < 0 ){
        y = 0;
    }
	return [x, y];
}

function fix_table( id, widths ){
	var calculated_widths = [];
	var full_width = get_content_width();
	var ths = $( '#' + id + ' tr:first th' );
	full_width -= ths.length + 1;
	for( var i1 = 0; i1 < widths.length; i1++ ){
		var width1 = widths[i1];
		var arr1 = /^(\d+)px$/.exec( width1 );
		if( arr1 != null ){
			var v1 = arr1[1];
			var v2 = v1 - parseInt( $( ths[i1] ).css( 'paddingLeft' ) ) - parseInt( $( ths[i1] ).css( 'paddingRight' ) );
			calculated_widths[i1] = v2 + 'px';
			full_width -= v1;
		}
	}
	var last_i = null;
	var last_width = full_width;
	for( var i2 = 0; i2 < widths.length; i2++ ){
		var width2 = widths[i2];
		if( width2 == null ){
			last_i = i2;
		} else {
			var arr2 = /^(\d+)\%$/.exec( width2 );
			if( arr2 != null ){
				var v3 = arr2[1];
				var v5 = parseInt( 0.01*v3*full_width );
				last_width -= v5;
				var v4 = v5 - parseInt( $( ths[i2] ).css( 'paddingLeft' ) ) - parseInt( $( ths[i2] ).css( 'paddingRight' ) );
				calculated_widths[i2] = v4 + 'px';
				last_i = i2;
			}
		}
	}
	calculated_widths[last_i] = (last_width - parseInt( $( ths[last_i] ).css( 'paddingLeft' ) ) - parseInt( $( ths[last_i] ).css( 'paddingRight' ) )) + 'px';
	for( var i3 = 0; i3 < calculated_widths.length; i3++ ){
		$( '#' + id + ' th:nth-child(' + (i3 + 1) + ')' ).css( 'width', calculated_widths[i3] );
		$( '#' + id + ' td:nth-child(' + (i3 + 1) + ')' ).css( 'width', calculated_widths[i3] );
	}
	return calculated_widths;
}

function get_content_width(){
	return document.documentElement.clientWidth - 70;
}

function escapeHTML( s ){
	if( !s ){
		return '';
	}
	return s.replace( /&/g,'&amp;' ).replace( /</g,'&lt;' ).replace( />/g,'&gt;' ).replace( /'/g,'&#39;' ).replace( /"/g,'&quot;' );
}

// div, который выводится при наведении на некоторый элемент - поверх них
// как использовать:
// * создать на странице div(popup-div) с желаемой версткой, напр.:
//   <div id="hint" style="display: none;"></div>
// * создать объект PopUpDiv:
//   var POPUP = new PopUpDiv( $( '#hint' ), function( obj, options ){
//	   return {html: 'Подсказка', width: options.width, height: options.height};
//   } );
// * на элементы, при наведении на которые нужно выводить popup-div поставить обработчики
//   <input type="text" onmouseover="POPUP.mouseover_handler( this, event, {width: 50, height: 20} )" onmouseout="POPUP.mouseout_handler( this )"/>
//
// @param jq_div jQuery - ссылка на div, который будет появляться
// @param get_html function( obj, options ) - функция, возвращающая html для всплывающего div
//   - obj ссылка на объект, вызвавший mouseover_handler
//   - options - объект, который можно передать в mouse_handler последним параметром
// @param options {}
//   options.left Number, options.top Number - смещения всплывающего div относительно курсора; если null, то позиционирование div оставляется за пользователем,
//     который может это сделать в функции get_params
//   options.show_timeout Number - время в мс, через которое появится  всплывающийdiv; default 100
//   options.hide_timeout Number - время в мс, через которое исчезнет всплывающий div; default 300
//   options.is_shared_over_area Boolean - будет сохранять видимость высплывающий div при наведении мышки на него; default false
function PopUpDiv( jq_div, get_html, options ){
	options = if_null( options, {} );
	this.jq_div = jq_div;
	this.get_html = get_html;
	this.left = options.left;
	this.top = options.top;
	this.show_timeout = if_null( options.show_timeout, 100 );
	this.hide_timeout = if_null( options.hide_timeout, 300 );
	this.is_shared_over_area = if_null( options.is_shared_over_area, false );
	this.show_timer_id = null;
	this.hide_timer_id = null;
	this.jq_div.css( 'display', 'none' )
		.css( 'position', 'absolute' )
		.css( 'z-index', '1' );
	this.mouseover_handler = function( obj, e, options ){
		var this_stored = this;
		if( this.is_shared_over_area ){
			this.jq_div[0].onmouseover = function(){
				clearTimeout( this_stored.hide_timer_id );
			};
			this.jq_div[0].onmouseout = function( obj ){
				this_stored.mouseout_handler( obj )
			};
		}
		clearTimeout( this.hide_timer_id );
		var c = topadvert_create_coords( e );
		this.show_timer_id = setTimeout( function(){
			this_stored.jq_div.css( 'visibility', 'hidden' );
			this_stored.jq_div.css( 'display', 'block' );
			this_stored.jq_div.html( this_stored.get_html( obj, options ) );
			var left = this_stored.left;
			var top = this_stored.top;
			var body = $( 'body' );
			var width = this_stored.jq_div.attr( 'offsetWidth' );
			var height = this_stored.jq_div.attr( 'offsetHeight' );
			if( left != null ){
				if( c[0] + width + left > body.attr( 'scrollLeft' ) + body.attr( 'clientWidth' ) ){
					this_stored.jq_div.css( 'left', (c[0] - width - left) + 'px' );
				} else {
					this_stored.jq_div.css( 'left', (c[0] + left) + 'px' );
				}
			}
			if( top != null ){
				if( c[1] + height + top > body.attr( 'scrollTop' ) + body.attr( 'clientHeight' ) ){
					this_stored.jq_div.css( 'top', (c[1] - height - top) + 'px' );
				} else {
					this_stored.jq_div.css( 'top', (c[1] + top) + 'px' );
				}
			}
			this_stored.jq_div.css( 'visibility', 'visible' );
		}, this.show_timeout );
	};
	this.mouseout_handler = function( obj ){
		clearTimeout( this.show_timer_id );
		var this_stored = this;
		this.hide_timer_id = setTimeout( function(){
			this_stored.jq_div.hide();
			this_stored.hide_timer_id = null;
		}, this.hide_timeout );
	};
}

function if_null( value, default_value ){
	return value == null ? default_value : value;
}

function get_scroll_pos( element, relative_element ){
	var pos_x = 0;
	var pos_y = 0;
	while( element != null && element != relative_element ){
		pos_x += element.offsetLeft;
		pos_y += element.offsetTop;
		element = element.offsetParent;
	}
	return [pos_x, pos_y];
}

function scroll_to_element( element ){
	var pos = get_scroll_pos( element, null );
	window.scrollTo( pos[0], pos[1] );
}

function compare_strings( a, b ){
	var a1 = '' + a;
	var b1 = '' + b;
	return a1 > b1 ? 1 : a1 < b1 ? -1 : 0;
}

function compare_numbers( a, b ){
	var a1 = parseFloat( a );
	var b1 = parseFloat( b );
	return a1 > b1 ? 1 : a1 < b1 ? -1 : 0;
}

function compare_booleans( a, b ){
	return a && !b ? 1 : !a && b ? -1 : 0;
}

var cookies_version = 1;

function get_cookies_map(){
	var cookies_map_json = $.cookie( 'map' );
	var cookies_map = null;
	if( cookies_map_json ){
		cookies_map = JSON.parse( cookies_map_json );
		if( cookies_map.version != cookies_version ){
			cookies_map = null;
		}
	}
	if( !cookies_map ){
		cookies_map = {
			version: cookies_version,
			sort_slices: {},
			limit: null
		};
		set_cookies_map( cookies_map );
	}
	return cookies_map;
}

function set_cookies_map( cookies_map ){
	$.cookie( 'map', null, null );
	$.cookie( 'map', JSON.stringify( cookies_map ), {path: '/', expires: 365} );
}

function get_cookie_sort_slice( slice_name ){
	var cookies_map = get_cookies_map();
	var sort_slice = cookies_map.sort_slices[slice_name];
	if( !sort_slice ){
		sort_slice = {
			name: null,
			desc: false
		};
	}
	return sort_slice;
}

function set_cookie_sort_slice( slice_name, name, desc ){
	var cookies_map = get_cookies_map();
	cookies_map.sort_slices[slice_name] = {
		'name': name,
		'desc': desc
	};
	set_cookies_map( cookies_map );
}

function get_cookie_stats_period_info(){
	var cookies_map = get_cookies_map();
	return cookies_map.period_info;
}

function set_cookie_stats_period_info( period, date_min, date_max ){
	var cookies_map = get_cookies_map();
	cookies_map.period_info = {
		period: period,
		date_min: date_min,
		date_max: date_max
	};
	set_cookies_map( cookies_map );
}

function get_cookie_limit(){
	var cookies_map = get_cookies_map();
	return cookies_map.limit;
}

function set_cookie_limit( limit ){
	var cookies_map = get_cookies_map();
	cookies_map.limit = limit;
	set_cookies_map( cookies_map );
}

function DialogSave( id, obj_name, get_is_changed ){
	this.id = id;
	this.obj_name = obj_name;
	this.get_is_changed = get_is_changed;
	this.rd_handler_onclick_str = this.obj_name + '.rd_handler( this.href ); return false;';
	this.rd_handler = function( href ){
		this.rd_url = href;
		if( this.get_is_changed() ){
			this.tdialog.open();
		} else {
			window.location = this.rd_url;
		}
	};
	this.tdialog = null;
	this.init = function(){
		$( '#post_form' ).append( '<input type="hidden" name="rd_url" value=""/>' );
		var this_saved = this;
		this.tdialog = new TDialog( this.id, {
			title: 'Сохранение',
			resizable: false,
			button_handler_backward: function(){
				window.location = this_saved.rd_url;
			},
			button_handler_forward: function(){
				$( '[name=rd_url]' ).val( this_saved.rd_url );
				$( '#save_button' )[0].click();
			},
			width: 500
		} );
		this.tdialog.set_content(
			'<div style="text-align: center; text-valign: center; vertical-align: middle; height: 20px; padding: 5px 0px 0px 0px;" class="standart_font">'
				+ 'Сохранить изменения настроек категорий и рекламной кампании?'
			+ '</div>'
		);
		$( 'a:not([onclick*=rd_handler]):not([role*=button]):not([href*=mailto])' ).click( function(){
			this_saved.rd_handler( this );
			return false;
		} );
	};
}

function dump_var( u_var ){
	alert( get_dump_var( u_var, null ) );
}

function get_dump_var( u_var, n_depth ) {
	var i;
	if( n_depth == null ){
		n_depth = 0;
	}
	var s_tabs = "\n";
	for( i = 0; i < n_depth; i++ ){
		s_tabs += "\t";
	}
	n_depth++;
	if( typeof( u_var ) == 'number' ){
		return u_var;
	} else if (typeof( u_var ) == 'string'){
		return "'" + u_var + "'";
	} else if (typeof( u_var ) == 'boolean'){
		return (u_var ? 'true' : 'false');
	} else if (typeof( u_var ) == 'undefined' || u_var == null){
		return 'null';
	} else if (typeof( u_var ) == 'function'){
		return 'function () {alert(\'function was not saved\')}';
	}
	// detect numeric or symbolic keys
	var b_hash = false;
	for( i in u_var ){
		if( isNaN( Number( i ) ) ){
			b_hash = true;
			break;
		}
	}
	var s_output = '';
	var b_first = true;
	if( b_hash ){
		s_output += "{";
		for( i in u_var ){
			s_output += (b_first ? '' : ",") + s_tabs + "\t'" + i + "': " + get_dump_var( u_var[i], n_depth );
			b_first = false;
		}
		s_output += s_tabs + "}";
	} else {
		s_output += "[";
		for( i = 0; i < u_var.length; i++ ){
			s_output += (b_first ? '' : ",") + s_tabs + "\t" + get_dump_var(u_var[i], n_depth);
			b_first = false;
		}
		s_output += s_tabs + "]";
	}
	return s_output;
}

function set_fix_page_on_interval(){
	$( function(){
		setInterval( function(){
			fix_page();
		}, 200 );
		fix_page();
	} );
}

function extend( Child, Parent ){
	var F = function(){};
	F.prototype = Parent.prototype;
	Child.prototype = new F();
	Child.prototype.constructor = Child;
	Child.superclass = Parent.prototype;
}

function AbstractTTable( settings ){
	this.id = settings.id;
	this.sort_th = settings.sort_th;
	this.sort_name = null;
	this.sort_desc = null;
	this.def_sort_name = settings.def_sort_name;
	this.def_sort_desc = settings.def_sort_desc;
	this.sort_slice = settings.sort_slice;
}

extend( AbstractTTable, Object );

AbstractTTable.prototype.init = function(){
	var th_id;
	var sort_slice;
	var is_exist_sort_name;
	if( this.sort_th ){
		for( th_id in this.sort_th ){
			$( '#' + th_id ).attr( 'class', 'sorting' );
			$( '#' + th_id ).click( function( obj, th_id ){
				return function(){
					obj.set_sort( obj.sort_th[th_id] );
				};
			}( this, th_id ) );
		}
		if( this.sort_slice ){
			sort_slice = get_cookie_sort_slice( this.sort_slice );
			is_exist_sort_name = false;
			for( th_id in this.sort_th ){
				if( this.sort_th[th_id] == sort_slice.name ){
					is_exist_sort_name = true;
				}
			}
			if( is_exist_sort_name ){
				this.sort_name = sort_slice.name;
				this.sort_desc = sort_slice.desc;
			}
		}
	}
	if( !this.sort_name ){
		if( this.def_sort_name ){
			this.sort_name = this.def_sort_name;
		}
		if( this.def_sort_desc ){
			this.sort_desc = this.def_sort_desc;
		}
	}
	this.set_sort_to_ths();
	this.set_cookies();
};

AbstractTTable.prototype.set_sort_to_ths = function(){
	var th_id;
	if( this.sort_th ){
		for( th_id in this.sort_th ){
			$( '#' + th_id ).attr( 'class', this.sort_name == this.sort_th[th_id] ?
				this.sort_desc ? 'sorting_desc' : 'sorting_asc' : 'sorting' );
		}
	}
};

AbstractTTable.prototype.set_sort = function( sort_name ){
	if( sort_name == this.sort_name ){
		this.sort_desc = !this.sort_desc;
	} else {
		this.sort_name = sort_name;
		this.sort_desc = false;
	}
	this.set_sort_to_ths();
};

AbstractTTable.prototype.set_cookies = function(){
	if( this.sort_slice && this.sort_name ){
		set_cookie_sort_slice( this.sort_slice, this.sort_name, this.sort_desc );
	}
	if( this.limit ){
		set_cookie_limit( this.limit );
	}
};

function JsTTable( settings ){
	JsTTable.superclass.constructor.call( this, settings );
	this.columns = settings.columns;
	this.data = [];
	this.count = 0;
}

extend( JsTTable, AbstractTTable );

JsTTable.prototype.init = function(){
	JsTTable.superclass.init.call( this );
};

JsTTable.prototype.draw = function(){
	var i;
	var j;
	var s;
	$( '#' + this.id + ' tbody' ).remove();
	if( this.data.length ){
		s = '<tbody>';
		for( i = 0; i < this.data.length; i++ ){
			s += '<tr>';
			for( j = 0; j < this.columns.length; j++ ){
				s += '<td>' + this.columns[j]( this.data[i], i ) + '</td>';
			}
			s += '</tr>';
		}
		s += '</tbody>';
		$( '#' + this.id ).append( s );
	}
};

function JsJsTTable( settings ){
	JsJsTTable.superclass.constructor.call( this, settings );
	this.data = settings.json_source;
	this.sort_functions = settings.sort_functions;
}

extend( JsJsTTable, JsTTable );

JsJsTTable.prototype.init = function(){
	JsJsTTable.superclass.init.call( this );
	this.re_sort();
	this.draw();
};

JsJsTTable.prototype.set_sort = function( sort_name ){
	JsJsTTable.superclass.set_sort.call( this, sort_name );
	this.re_sort();
	this.draw();
};

JsJsTTable.prototype.re_sort = function(){
	if( this.sort_name && this.sort_functions ){
		var sort_function = this.sort_functions[this.sort_name];
		if( sort_function ){
			var tmp_sort_desc = this.sort_desc;
			this.data = this.data.sort( function( a, b ){
				return (tmp_sort_desc ? -1 : 1)*sort_function( a, b );
			} );
		}
	}
};

function ServerJsTTable( settings ){
	ServerJsTTable.superclass.constructor.call( this, settings );
	this.pages_count = 5;
	this.pagination_limits = [100, 200, 500, 1000];
	this.is_pagination = settings.is_pagination;
	this.server_data = settings.server_data ? settings.server_data : $.getJSON;
	this.ajax_source = settings.ajax_source;
	this.limit = null;
	this.offset = null;
	this.processing_div_id = this.id + '_processing';
	this.pagination_select_id = this.id + '_pagination';
	this.count_id = this.id + '_count';
	this.pages_div_id = this.id + '_pages';
	this.page_max = null;
}

extend( ServerJsTTable, JsTTable );

ServerJsTTable.prototype.init = function(){
	ServerJsTTable.superclass.init.call( this );
	var i;
	var tmp_limit;
	var before_html;
	var after_html;
	if( this.is_pagination ){
		before_html = '<div style="clear: both;"></div>';
		before_html += '<table width="100%"><tr>'
			+ '<td width="150">'
				+ '<span id="' + this.count_id + '" style="font-size: 11px;"> ' + this.get_count_html() + '</span>'
			+ '</td>'
			+ '<td align="center">&nbsp;<div id="' + this.processing_div_id + '" style="display: none;">загрузка...</div>'
			+ '</td>';
		before_html += '<td width="250"><div class="dataTables_length" align="right" style="font-size: 11px;">'
			+ '<nobr>'			
			+ ' показывать по <select id="' + this.pagination_select_id + '" style="font-size: 11px;">';
		for( i = 0; i < this.pagination_limits.length; i++ ){
			tmp_limit = this.pagination_limits[i];
			before_html += '<option value="' + tmp_limit + '">' + tmp_limit + '</option>';
		}
		before_html += '</select></nobr></td></tr></table>';
		$( '#' + this.id ).before( before_html );
		$( '#' + this.pagination_select_id ).val( get_cookie_limit() );
		if( !$( '#' + this.pagination_select_id ).val() ){
			$( '#' + this.pagination_select_id ).val( this.pagination_limits[0] );
		}
		this.limit = $( '#' + this.pagination_select_id ).val();
		this.offset = 0;
		$( '#' + this.pagination_select_id ).change(
			function( obj ){
				return function(){
						obj.offset = 0;
						obj.limit = $( '#' + obj.pagination_select_id ).val();
						obj.load();
				};
			}( this ) );
		after_html = '<div id="' + this.pages_div_id + '" class="paging_full_numbers"></div>';
		$( '#' + this.id ).after( after_html );
	}
	this.load();
};

ServerJsTTable.prototype.get_count_html = function(){
	return 'записей ' + this.count;
}

ServerJsTTable.prototype.load = function(){
	start_processing();
	var request_data = [];
	if( this.sort_name != null ){
		request_data.push( {'name': 'sort_name', 'value': this.sort_name} );
		request_data.push( {'name': 'sort_desc', 'value': this.sort_desc ? '1' : '0'} );
	}
	if( this.is_pagination ){
		request_data.push( {'name': 'limit', 'value': this.limit} );
		request_data.push( {'name': 'offset', 'value': this.offset} );
	}
	this.server_data( this.ajax_source, request_data, function( obj ){
		return function( json ){
			obj.count = json.count;
			obj.data = json.data;
			obj.draw();
			end_processing();
		};
	}( this ) );
	this.set_cookies();
};

ServerJsTTable.prototype.draw = function(){
	ServerJsTTable.superclass.draw.call( this );
	var i;
	if( this.is_pagination ){
		$( '#' + this.count_id ).html( this.get_count_html() );
		var page_max = Math.ceil( this.count / this.limit );
		if( page_max > 1 ){
			var page = this.offset > 0 ? Math.floor( this.offset / this.limit ) + 1 : 1;
			if( page > 0 && page <= page_max ){
				var arr = [];
				arr.push( ['&lt;&lt;', 1, false] );
				arr.push( ['&lt;', page - 1 >= 1 ? page - 1 : 1, false] );
				var p_min = page;
				var p_max = page;
				while( p_max - p_min + 1 < this.pages_count && (p_max < page_max || p_min > 1) ){
					if( p_max < page_max ){
						p_max++;
						if( p_max - p_min + 1 >= this.pages_count ){
							break;
						}
					}
					if( p_min > 1 ){
						p_min--;
					}
				}
				for( var p = p_min; p <= p_max; p++ ){
					arr.push( [p, p, p == page] );
				}
				arr.push( ['&gt;', page + 1 <= page_max ? page + 1 : page_max, false] );
				arr.push( ['&gt;&gt;', page_max, false] );
				var pages_html = '';
				for( i = 0; i < arr.length; i++ ){
					pages_html += '<span id="' + this.id + '_page_' + i + '"'
						+ ' class="' + (arr[i][2] ? 'paginate_active' : 'paginate_button') + '">' + arr[i][0] + '</span>';
				}
				$( '#' + this.pages_div_id ).html( pages_html );
				for( i = 0; i < arr.length; i++ ){
					$( '#' + this.id + '_page_' + i ).click( function( obj, p ){
						return function(){
							obj.offset = (p - 1)*obj.limit;
							obj.load();
						};
					}( this, arr[i][1] ) );
				}
			}
		} else {
			page_max = 1;
			$( '#' + this.pages_div_id ).html( '' );
		}
		this.page_max = page_max;
	}
};

ServerJsTTable.prototype.set_sort = function( sort_name ){
	ServerJsTTable.superclass.set_sort.call( this, sort_name );
	this.load();
};

function HtmlServerTTable( settings ){
	HtmlServerTTable.superclass.constructor.call( this, settings );
}

extend( HtmlServerTTable, AbstractTTable );

HtmlServerTTable.prototype.init = function(){
	HtmlServerTTable.superclass.init.call( this );
};

/*
	@param id str id дива с диалогом
	@param settings hash настройки диалога(необязательные):
		title str заголовок
		width int ширина диалога
		height int высота диалога
		resizable bool может ли пользователь изменять размеры диалога
		button_handler_backward function() обработчик onclick для кнопки backward
		button_handler_cancel function() обработчик onclick для кнопки cancel
		button_handler_forward function() обработчик onclick для кнопки forward
*/
function TDialog( id, settings ){
	var i;
	this.id = id;
	this.settings = settings == null ? {} : settings;
	this.init = function(){
		var button_names = ['forward', 'cancel', 'backward'];
		for( i in button_names ){
			if( this.settings['button_handler_' + button_names[i]] != null ){
				$( '#' + id + '_button_' + button_names[i] ).click( this.settings['button_handler_' + button_names[i]] );
			} else if( button_names[i] == 'cancel' ){
				var obj = this;
				$( '#' + id + '_button_cancel' ).click( function(){
					obj.close();
				} );
			}
		}
		var dialog_settings = {
			autoOpen: false,
			modal: true,
			minHeight: 0,
			buttons: {}
		};
		var other_settings = ['title', 'width', 'height', 'resizable'];
		for( i in other_settings ){
			if( this.settings[other_settings[i]] != null ){
				dialog_settings[other_settings[i]] = this.settings[other_settings[i]];
			}
		}
		$( '#' + this.id ).dialog( dialog_settings );
	};
	this.open = function(){
		$( '#' + this.id ).dialog( 'open' );
	};
	this.close = function(){
		$( '#' + this.id ).dialog( 'close' );
	};
	this.set_title = function( title ){
		$( '#' + this.id ).dialog( 'option', 'title', title );
	};
	this.set_content = function( content ){
		$( '#' + this.id + '_content' ).html( content );
	};
	this.init();
}

function start_processing(){
	var processing_div = _get_processing_div();
	processing_div.css( 'display', 'block' );
}

function end_processing(){
	var processing_div = _get_processing_div();
	processing_div.css( 'display', 'none' );
}

function _get_processing_div(){
	var obj = $( '#processing_div' );
	if( !obj.length ){
		$( 'body' ).append( '<div id="processing_div">Загрузка...</div>' );
		obj = $( '#processing_div' );
	}
	return obj;
}

// @param create_tree_element function( tree, id, parent, has_children, is_lazy, data ) - функция, инстанцирующая объект нужного класса-наследника AbstractTTree
function TTree( var_name, create_tree_element, ajax_source ){
	this.var_name = var_name;
	this.create_tree_element = create_tree_element;
	this.ajax_source = ajax_source;
	this.root = null;
	var map = {};
	this.add = function( id, parent_id, level, has_children, is_lazy, data, is_static ){
		if( !map[id] ){
			var element = this.create_tree_element( this, id, map[parent_id], level, has_children, is_lazy, data, is_static );			
			if( this.root == null ){
				this.root = element;
			}
			map[id] = element;
			if( map[parent_id] ){
				map[parent_id].add_child( element );
			}
			element.render( is_static );
		}
	};
	this.get = function( id ){
		return map[id];
	}
}
TTree.prototype.load = function( scroll_id ){
	this.json( null, scroll_id, null, null );
}
TTree.prototype.json = function( parent_id, scroll_id, jq_obj, collapse_src ){
	var that = this;
	var json_data_add = this.get_json_data_add();
	var json_data = {};
	if( parent_id != null ){
		json_data.parent_id = parent_id;
	}
	if( scroll_id != null ){
		json_data.scroll_id = scroll_id;
	}
	for( var key in json_data_add ){
		json_data[key] = json_data_add[key];
	}
	$.getJSON(
		this.ajax_source,
		json_data,
		function( ret ){
			var elements = ret.elements;
			for( var i in elements ){
				that.add(
					elements[i].id, elements[i].parent_id, elements[i].level, elements[i].has_children, elements[i].is_lazy,
					elements[i].data, 0
				);
			}
			if( jq_obj != null ){
				jq_obj.attr( 'src', collapse_src );
			}
			fix_page();
			that.after_json( ret );
			return false;
		}
	);
};
TTree.prototype.get_json_data_add = function(){
	return {};
}
TTree.prototype.after_json = function( ret ){
	var scroll_to_element = this.get( ret.scroll_id );
	if( scroll_to_element ){
		scroll_to_element.scroll_to();
	}
}

function AbstractTTreeElement( tree, id, parent, level, has_children, is_lazy, data, is_static ){
	this.tree = tree;
	this.id = Number( id );
	this.parent = parent;
	this.level = level;
	this.has_children = Number( has_children );
	this.is_lazy = Number( is_lazy );
	this.data = data;
	this.children = [];
	this.expander = this.has_children ? (this.is_lazy ? this.expander_collapsed : this.expander_expanded) : this.expander_empty;
}
AbstractTTreeElement.prototype.expander_collapsed = 0;
AbstractTTreeElement.prototype.expander_expanded = 1;
AbstractTTreeElement.prototype.expander_waiting = -1;
AbstractTTreeElement.prototype.expander_empty = -2;
AbstractTTreeElement.prototype.collapse_src = '/images/tree/expander-collapse.gif';
AbstractTTreeElement.prototype.expand_src = '/images/tree/expander-expand.gif';
AbstractTTreeElement.prototype.empty_src = '/images/tree/expander-empty.gif';
AbstractTTreeElement.prototype.wait_src = '/images/tree/wait.gif';
AbstractTTreeElement.prototype.get_expander_src = function(){
	var map = {};
	map[AbstractTTreeElement.prototype.expander_collapsed] = AbstractTTreeElement.prototype.expand_src;	
	map[AbstractTTreeElement.prototype.expander_expanded] = AbstractTTreeElement.prototype.collapse_src;	
	map[AbstractTTreeElement.prototype.expander_empty] = AbstractTTreeElement.prototype.empty_src;	
	map[AbstractTTreeElement.prototype.expander_waiting] = AbstractTTreeElement.prototype.wait_src;
	if( this.draw_empty_src || this.has_children ){
		return map[this.expander];
	} else {
		return null;
	}
}
AbstractTTreeElement.prototype.render = function( is_static ){
	var html = this.get_html( is_static );
	if( is_static ){
		document.write( html );
	} else {
		$( '#' + (this.parent ? this.get_id_for( 'div', this.parent.id ) : this.tree.var_name + '__tree') ).append( html );
	}
}
AbstractTTreeElement.prototype.add_child = function( child ){
	this.children.push( child );
};
AbstractTTreeElement.prototype.expander_handler = function( obj ){
	var jq_obj = $( obj );
	if( this.is_lazy ){
		jq_obj.attr( 'src', this.wait_src );
		this.is_lazy = 0;
		this.tree.json( this.id, null, jq_obj, this.collapse_src );
	} else {
		var src = jq_obj.attr( 'src' );
		if( src != this.wait_src && src != this.empty_src ){
			jq_obj.attr( 'src', src == this.expand_src ? this.collapse_src : this.expand_src );
			var div_id = this.get_id_for( 'div', this.id );
			$( '#' + div_id ).css( 'display', $( '#' + div_id ).css( 'display' ) == 'none' ? 'block' : 'none' );
		}
		fix_page( true );
	}
};
AbstractTTreeElement.prototype.expand_to_this = function(){
	this.collapse();
	for( var parent = this.parent, old_parent = this; parent != null; old_parent = parent, parent = parent.parent ){
		for( var i in parent.children ){
			if( parent.children[i].id != old_parent.id ){
				parent.children[i].collapse();
			}
		}
		parent.expand();
	}
}
AbstractTTreeElement.prototype.collapse = function(){
	var jq_expander = $( '#' + this.get_id_for( 'expander', this.id ) );
	if( jq_expander.attr( 'src' ) == this.collapse_src ){
		this.expander_handler( jq_expander[0] );
	}
}
AbstractTTreeElement.prototype.expand = function(){
	var jq_expander = $( '#' + this.get_id_for( 'expander', this.id ) );
	if( jq_expander.attr( 'src' ) == this.expand_src ){
		this.expander_handler( jq_expander[0] );
	}
}
AbstractTTreeElement.prototype.get_html = null;
AbstractTTreeElement.prototype.get_id_for = function( param, id ){
	return this.tree.var_name + '__' + param + '_' + id;
};
AbstractTTreeElement.prototype.get_id_from = function( element ){
	return /\d+$/.exec( $( element ).attr( "id" ) );
}
AbstractTTreeElement.prototype.scroll_to = function(){
	var elem = $( '#' + this.get_id_for( 'tr', this.id ) )[0];
	scroll_to_element( elem );
	this.tr_mouseover_handler( elem );
};
AbstractTTreeElement.prototype.tr_mouseover_handler = function( obj ){
	$( obj ).children().addClass( 'tree_row_marked' );
}
AbstractTTreeElement.prototype.tr_mouseout_handler = function( obj ){
	$( obj ).children().removeClass( 'tree_row_marked' );
}
AbstractTTreeElement.prototype.draw_empty_src = true;
AbstractTTreeElement.prototype._get_src_size = function(){
	return this.draw_empty_src || this.has_children ? 15 : 0;
}
AbstractTTreeElement.prototype.width_left = 0;
AbstractTTreeElement.prototype.width_right = 0;
AbstractTTreeElement.prototype.get_html_left = function(){
	return '';
};
AbstractTTreeElement.prototype.get_html_middle = function(){
	return '';
};
AbstractTTreeElement.prototype.get_html_right = function(){
	return '';
};
AbstractTTreeElement.prototype.get_html = function( is_static ){
	var tree_var_str = this.tree.var_name + '.get(' + this.id + ')';
	var str = (
		'<div style="line-height: 20px; margin-bottom: 1px;"'
			+ ' onmouseover="' + tree_var_str + '.tr_mouseover_handler( this )" onmouseout="' + tree_var_str + '.tr_mouseout_handler( this )"'
			+ ' id="' + this.get_id_for( 'tr', this.id ) + '"'
		+ '>'
			+ '<div style="position: absolute; width: ' + (this._get_src_size() + this.width_left) + 'px;">'
				+ (this.get_expander_src()
					? '<img'
						+ ' src ="' + this.get_expander_src() + '"'
						+ ' id="' + this.get_id_for( 'expander', this.id ) + '"'
						+ ' onclick="' + tree_var_str + '.expander_handler( this )"'
						+ ' style="padding-top: 3px;' + (this.has_children ? 'cursor: pointer;' : '') + '"'
					: '')
				+ this.get_html_left()
			+ '</div>'
			+ '<div style="position: absolute; left: 100%; width: ' + this.width_right + 'px; margin-left: -' + this.width_right + 'px;">'
				+ this.get_html_right()
			+ '</div>'
			+ '<div style="width: 100%;" id="' + this.get_id_for( 'middle', this.id ) + '">'
				+ '<div style="margin: 0 ' + this.width_right + 'px 0 ' + (this._get_src_size() + this.width_left) + 'px;"'
					+ ' id="' + this.get_id_for( 'name', this.id ) + '"'
				+ '>'
					+ this.get_html_middle()
				+ '</div>'
			+ '</div>'
		+ '</div>'
		+ (is_static ? '' :
			'<div style="margin-left: 15px;" id="' + this.get_id_for( 'div', this.id ) + '">'
			+ '</div>'
		)
	);
	return str;
}
