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) {
        var regexp = new RegExp('^(.*?[\\&\\?])' + name + '=[^\\&]*(.*)$');
        var arr = regexp.exec(url);
        if (arr) {
            url = arr[1] + name + '=' + encodeURIComponent(value) + arr[2];
        } else {
            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 ){
    if( a == null ){
        a = '';
    }
    if( b == null ){
        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 = 2;

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();
    this.set_cookies();
};

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;
    this.sort_default_function = settings.sort_default_function;
}

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 ){
                var res = (tmp_sort_desc ? -1 : 1)*sort_function( a, b );
                if( !res && this.sort_default_function ){
                    res = this.sort_default_function( a, b );
                }
                return res;
            } );
        }
    }
};

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.is_counting = settings.is_counting;
    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 ){
        var options_html = '';
        for( i = 0; i < this.pagination_limits.length; i++ ){
            tmp_limit = this.pagination_limits[i];
            options_html += '<option value="' + tmp_limit + '">' + tmp_limit + '</option>';
        }
        before_html = '<div style="clear: both;"></div>' +
            '<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>' +
                    '<td width="150">' +
                        '<div class="dataTables_length" align="right" style="font-size: 11px;">' +
                            '<nobr>' +
                                ' показывать по ' +
                                '<select id="' + this.pagination_select_id + '" style="font-size: 11px;">' +
                                    options_html +
                                '</select>' +
                            '</nobr>' +
                        '</div>' +
                    '</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.is_counting ? 'записей ' + 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 button_names = ['forward', 'cancel', 'backward'];
    var i;
    this.id = id;
    this.settings = settings == null ? {} : settings;
    this.init = function(){
        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.enable_buttons = function() {
        for (var i in button_names) {
            $('#' + id + '_button_' + button_names[i]).attr('disabled', false);
        }
    }
    this.disable_buttons = function() {
        for (var i in button_names) {
            $('#' + id + '_button_' + button_names[i]).attr('disabled', true);
        }
    }
    // call
    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 = '/static/images/tree/expander-collapse.gif';
AbstractTTreeElement.prototype.expand_src = '/static/images/tree/expander-expand.gif';
AbstractTTreeElement.prototype.empty_src = '/static/images/tree/expander-empty.gif';
AbstractTTreeElement.prototype.wait_src = '/static/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;
}

function isString(item) {
    return Object.prototype.toString.call(item) == '[object String]';
}
function isArray(item) {
    return Object.prototype.toString.call(item) == '[object Array]';
}
function isObject(item) {
    return Object.prototype.toString.call(item) == '[object Object]';
}

function escapeHTMLString(string) {
    if (!string) {
        return string;
    }
    return $("<div />").text(string).html();
}
function escapeHTMLObject(obj) {
    var result = new Object();
    for (var i in obj) {
        result[i] = escapeHTML(obj[i]);
    }
    return result;
}
function escapeHTML(item) {
    if (isString(item)) {
        return escapeHTMLString(item);
    }
    if (isArray(item) || isObject(item)) {
        return escapeHTMLArray(item);
    }
    return item;
}

function unescapeHTMLString(string) {
    if (!string) {
        return string;
    }
    return $("<div />").html(string).text();
}
function unescapeHTMLObject(obj) {
    var result = new Object();
    for (var i in obj) {
        result[i] = unescapeHTML(obj[i]);
    }
    return result;
}
function unescapeHTML(item) {
    if (isString(item)) {
        return unescapeHTMLString(item);
    }
    if (isArray(item) || isObject(item)) {
        return unescapeHTMLObject(item);
    }
    return item;
}

