function Table() {
    this._name = null;
    this._source = null;
    this._total = 0;
    this._builder = function() {throw new Error('override _builder using setBuilder!'); };
    this._owner = null;
    this._feed = null;
    this._current = this.getFirstPage();
    this._on_page = 10;
    this._source_opts = {};
    this._sort_column = null;
}

Table.prototype.setName = function(name) {
    this._name = name;
    return this;
}
    
    Table.prototype.getName = function() {
	return this._name;
    };
	
Table.prototype.expandName = function(prepend,append) {
    if(this.getName() == null) {
	throw new Error('Table must have valid name! Use setName(name) function!');
    }
    return prepend + this.getName() + '_' + append;
};

Table.prototype.setSource = function(source) {
    this._source = source;
    return this;
};

Table.prototype.getSource = function() {
    return this._source;
};

Table.prototype.setTotal = function(total) {
    this._total = total;
    return this;
};

Table.prototype.getTotal = function() {
    return this._total;
};

Table.prototype.setBuilder = function(builder) {
    this._builder = builder;
    return this;
};

Table.prototype.getBuilder = function() {
    return this._builder;
};

Table.prototype.setOwner = function(owner) {
    this._owner = owner;
    return this;
};

Table.prototype.getOwner = function() {
    return this._owner;
};

Table.prototype.setCurrent = function(current) {
    this._current = current;
    return this;
};

Table.prototype.getCurrent = function() {
    return this._current;
};

Table.prototype.setOnPage = function(on_page) {
    this._on_page = on_page;
    return this;
};

Table.prototype.getOnPage = function() {
    return this._on_page;
};

Table.prototype.setFeed = function(feed) {
    this._feed = feed;
    return this;
};

Table.prototype.getFeed = function() {
    return this._feed;
};

Table.prototype.postBuild = function() {
    throw new Error("override postBuild");
};

Table.prototype.__buildTable = function() {
    if(this._feed['errors']) {
	$(this._owner).html(
			    p({'class':'error'},"В ходе выполнения запроса произошли следующие ошибки:",
			      ol({},
				 function() {
				     code = '';
				     for(var i in this._feed['errors']) {
					 code += li({},this._feed['errors'][i]);
				     }
				     return code;
				 })));
    }
    $(this._owner).html(this._builder());
    this.postBuild();
};

Table.prototype.denyOperation = function() {
    throw new Error("override denyOperation");
};

Table.prototype.allowOperation = function() {
    throw new Error("override allowOperation");
};

Table.prototype.reload = function() {
    this.__loadFeed();
};

Table.prototype.setSourceOption = function(opt,value) {
    this._source_opts[opt] = value;
    return this;
};

Table.prototype.getSourceOption = function(opt,def) {
    return (this._source_opts[opt] ? this._source_opts[opt] : def);
};

Table.prototype.setSortColumn = function(col) {
    this._sort_column = col;
    return this;
};

Table.prototype.getSortColumn = function() {
    return this._sort_column;
};

Table.prototype.setSourceOption_sortAsc = function(field,noreload) {
    this.setSourceOption(field,'asc').setSortColumn(field);
    if(!noreload) this.reload();
    return this;
};

Table.prototype.setSourceOption_sortDesc = function(field,noreload) {
    this.setSourceOption(field,'desc').setSortColumn(field);
    if(!noreload) this.reload();
    return this;
};

Table.prototype.setSourceOption_switchSort = function(field,ascending,noreload) {
    var sort_col = this.getSortColumn();
    var direction = null;
    
    if(sort_col && sort_col != field) {
	this.removeSourceOption(sort_col);
    }
    
    if(sort_col) {
	if(sort_col == field) {
	    var direction = this.getSourceOption(field);
	    if(direction) {
		direction = (direction == 'asc' ? 'desc' : 'asc'); 
	    } else {
		direction = (ascending ? 'asc' : 'desc');
	    }
	}
    }
    
    if(!direction) {
	direction = (ascending ? 'asc' : 'desc');    
    }
    this.setSortColumn(field).setSourceOption(field,direction);
    if(!noreload) this.reload();

};

Table.prototype.setSourceOption_nextPage = function(noreload) {
    if(this.getCurrent() * this.getOnPage() < this.getTotal()) {
	this.setCurrent(this.getCurrent() + 1);
	this.setSourceOption('page',this.getCurrent());
	if(!noreload) this.reload();
    }
    return this;
};

Table.prototype.setSourceOption_setPage = function(page,noreload) {
    if(page > 0 && page != this.getCurrent()) {
	if((page - 1) * this.getOnPage() < this.getTotal()) {    
	    this.setCurrent(page);
	    this.setSourceOption('page',this.getCurrent());
	    if(!noreload) this.reload();
	}
    }
    return this;
};

Table.prototype.setSourceOption_prevPage = function(noreload) {
    if(this.getCurrent() > this.getFirstPage()) {
	this.setCurrent(this.getCurrent() - 1);
	this.setSourceOption('page',this.getCurrent());
	if(!noreload) this.reload();
    }
    return this;
};

Table.prototype.setSourceOption_onPage = function(on_page,noreload) {
    if(on_page > 0) {
	this.setOnPage(on_page);
	this.setSourceOption('on_page', this.getOnPage());
	this.setSourceOption_setPage(this.getFirstPage(), true); // do not reload after resetting current page to first
	if(!noreload) this.reload();
    }
    return this;
};

Table.prototype.getLastPage = function() {
    var n =  Math.floor(this.getTotal() / this.getOnPage());
    if (n * this.getOnPage() < this.getTotal()) {
	n = n + 1;
    }
    return n;
};

Table.prototype.getFirstPage = function() {
    return 1;
};

Table.prototype.reloadError = function() {
    throw new Error("override reloadError");
};

Table.prototype.initTable = function() {
    feed = this.getFeed();
    this.setTotal(parseInt(feed.total));
    this.setCurrent(parseInt(feed.page));
    this.setOnPage(parseInt(feed.on_page));
    this.loadSourceOptionsFromFeed();
};

Table.prototype.removeSourceOption = function(option) {
    delete this._source_opts[option];
    return this;
};

Table.prototype.loadSourceOptionsFromFeed = function(except) {
    var feed = this.getFeed();    
    for(var i in feed) {
	this.setSourceOption(i,feed[i]);
    }
    this.removeSourceOption('sort_column');
    this.removeSourceOption('items');
    if(except) 
	for(var i = 0 ; i < except.length ; i ++)
	    this.removeSourceOption(except[i]); // we don't need this items
};

Table.prototype.__loadFeed = function() {

    this.denyOperation();
    
    $.ajaxError = function () {
	this.reloadError();
	this.allowOperation();
    };
    
    var table = this;
    
    $.getJSON(this.getSource(), this._source_opts, function(json) {
	    table.setFeed(json);
	    table.initTable();
	    table.__buildTable();
	    table.allowOperation();
	});
};