/* компоненты */

// быстрый переход
var quickGo = Class.create();
quickGo.prototype = {
	initialize: function(params) {
	    var that = this;
	    // слой показывается слева
	    this.pos = 'l';
	    Object.extend(this, params);
	    this.requests = [];
	    this.element = $(this.element);
	    this.contentEl = this.element.getElementsBySelector('[id="content"]')[0];
	    this.defaultHTML = this.contentEl.innerHTML;
	    this.oldQuery = '';
	    Event.observe(this.element, 'mouseover', this.mouseOver.bindAsEventListener(this), false);
	    Event.observe(this.element, 'mouseout', this.mouseOut.bindAsEventListener(this), false);
	},
	
	startTime: function() {
		var that = this;
		this.time = setTimeout(function() { that.hide.call(that); }, 5000);
	},
	
	stopTime: function() {
		var that = this;
		clearTimeout(that.time);
	},
	
    show: function(caller) {
		var query = this.form.getQuery();
		if (this.oldQuery == query)
			return;
		this.stopTime();
		this.oldQuery = query;
		var that = this;
		var formPos = Position.cumulativeOffset(this.form.holderDiv);
		
		var callerPos = Position.cumulativeOffset(caller);
		var callerDim = caller.getDimensions();
		Position.absolutize(this.element);
		this.element.show();
		var goDim = this.element.getDimensions();
		var formDim = this.form.holderDiv.getDimensions();
		if (this.pos == 'l')
			var x = formPos[0]-goDim.width;
		else
			var x = formPos[0]+formDim.width;
		var y = callerPos[1]-Math.round(goDim.height/2)+Math.round(callerDim.height/2);
		if (x < 0) x = 0;
		if (y < 0) y = 0;
		this.element.style.left = x+'px';
		this.element.style.top = y+'px';
		this.loadContent(query);
		this.startTime();
    },
    
    hide: function() {
    	this.stopTime();
    	this.element.hide();
    },
    
    mouseOver: function(event) {
    	this.stopTime();
    },
    
    mouseOut: function(event) {
    	this.startTime();
    },
    
    reset: function() {
    	this.hide();
    	this.oldQuery = '';
    },
    
    loadContent: function(query) {
    	var that = this;
    	this.contentEl.update(this.defaultHTML);
        // прибить все незавершенные запросы
    	if (Ajax.activeRequestCount > 0) {
    		Ajax.Responders.responders.each(function(resp, key) {
    			if ((resp.options) && (resp.options.name) && (resp.options.name == 'quickGo')) {
    				resp.transport.abort();
    				that.stopTime();
    			}
            });
    	}
    	var req = new Ajax.Request(this.form.backendScript+'?'+query, {
    		name : 'quickGo',
            method: 'post',
            parameters: {'query':query},
            onFailure: function(tr) {
                //alert('Ошибка передачи данных');
                that.element.hide();
                that.stopTime();
            },
            onSuccess: function(tr) {
            	that.stopTime();
            	that.contentEl.update(tr.responseText);
            	that.startTime();
            	Ajax.Responders.unregister(req);
            	if (that.form.selectButtons) {
            		that.form.selectButtons.each(function(butt) {
            			if ($(butt)) {
	            			if (tr.responseJS.total != '0') {
	            				$(butt).disabled = false;
	            				$(butt).value = 'Показать ('+tr.responseJS.total+')';
	            			} else {
	            				$(butt).disabled = true;
	            				$(butt).value = 'Не найдено';
	            			}
            			}
            		});
            	}
            }
        });
    	Ajax.Responders.register(req);
    }
};

// открыть/закрыть слой (-/+)
var toggleHolder = Class.create();
toggleHolder.prototype = {
	initialize: function(params) {
	    var that = this;
	    Object.extend(this, params);
	    this.createControl();
	},
	
	createControl: function() {
		var that = this;
		// this.tplElementId - отсюда взять html для контрола
		// this.place - место размещения контрола на странице
		this.place = $(this.place);
    	var newDiv = document.createElement('div');
    	this.place = this.place.appendChild(newDiv);
		var tpl = new Template($(this.tplElement).innerHTML.replace(/#%7B([a-zA-Z0-9_-]+)%7D/gi, "#{$1}"));
		var html = tpl.evaluate(this.data);
		$(this.place).update(html);
		// картинка
		this.imgSign = this.place.getElementsBySelector('img[id="imgSign"]')[0];
		// активная область переключателя
		this.toggleEl = this.place.getElementsBySelector('[id="toggle"]')[0];
		this.contentEl = this.place.getElementsBySelector('[id="content"]')[0];
		if (this.innerElement)
			this.contentEl.appendChild(this.innerElement);
		Event.observe(this.toggleEl, 'click', this.toggle.bindAsEventListener(this));
    },
    
    toggle: function() {
    	this.contentEl.visible() ? this.close() : this.open();
    },
    
    open: function() {
		this.contentEl.show();
		if (this.imgSign)
			this.imgSign.src = this.imgClose;
    },
    
    close: function() {
		this.contentEl.hide();
		if (this.imgSign)
			this.imgSign.src = this.imgOpen;
    }
};

/* характеристики */

var singleSelect = Class.create();
singleSelect.prototype = {
    initialize: function(params) {
        var that = this;
        Object.extend(this, params);
        this.items = [];
        this.value = -1;
        this.createControl();
		this.select(this.value);
    },
    
    createControl: function() {
    	var that = this;
    	var tmpDiv = document.createElement('div');
    	tmpDiv = $(tmpDiv);
    	tmpDiv.update(this.html);
    	// получить шаблон элемента
    	this.normalTpl = new Template(tmpDiv.getElementsBySelector('div[id="normal"]')[0].innerHTML);
    	this.selectTpl = new Template(tmpDiv.getElementsBySelector('div[id="selected"]')[0].innerHTML);
    	this.control = tmpDiv.getElementsBySelector('div[id="control"]')[0];
    	var itemsPlaces = this.control.getElementsBySelector('[id="items"]');
    	itemsPlaces.each(function(item, key){
    		that.items.push({place: item, html: item.innerHTML});
    		Event.observe(item, 'click', that.click.bindAsEventListener(this, that, key), false);
    	});
    	// получить выбранного шаблон элемента
		$(this.holder).appendChild(this.control);
    },
    
    select: function(itemNumber) {
    	var that = this;
    	this.value = -1;
    	var element = null;
    	this.items.each(function(item, key){
    		if (key == itemNumber) {
    			item.place.update(that.selectTpl.evaluate({item:item.html}));
    			that.value = key;
    			element = item.place;
    		} else
    			item.place.update(that.normalTpl.evaluate({item:item.html}));
    	});
    	return element;
    },

    click: function(e, that, num) {
    	var el = that.select(num);
    	that.form.onCmpChange(el);
    },

    getValue: function() {
    	return this.value;
    },

	getTxtValue: function() {
    	return this.value;
	},

    setValue: function(val) {
    	this.select(val);
    },

	resetValue: function() {
		this.setValue(-1);
	},

    getQuery: function() {
    	var res = "";
        var val = this.getValue();
        if (val == 0)
        	return false;
		if (this.attrId)
			res = this.attrId + '~' + val;
    	return res;
    }
};

// "да/нет/не важно"
var yesNoSelect = Class.create();
yesNoSelect.prototype = {
    initialize: function(params) {
        var that = this;
        Object.extend(this, params);
        this.createControl();
        Event.observe(this.checkbox, 'click', that.click.bindAsEventListener(this, that), false);
    },

    getValue: function() {
    	var res = 0;
    	if (this.checkbox.checked)
    		res = 1;
    	return res;
    },
 
	getTxtValue: function() {
    	var res = "";
		if (this.checkbox.checked) {
			var attrData =this.form.data.attrs[this.catId].attrs[this.attrId];
	    	var res = attrData.title + ": Да";
		}
    	return res;
	},

    getQuery: function() {
    	var res = "";
    	var val = this.getValue();
    	if (!((val == 1) || (val == 2)))
    	    return false;
    	res = val;
		if (this.attrId)
			res = this.attrId + '~' + val;
		return res;
    },

	createControl: function() {
    	this.control = document.createElement('div');
    	$(this.control).update(this.html);
		$(this.holder).appendChild(this.control);
		this.checkbox = this.control.getElementsBySelector('input[type="checkbox"]')[0];
    },
 
    createFold: function() {
    	this.fold = new toggleHolder({
    		data           : {title: this.foldTitle},
    		tplElement     : 'attrsByCat',
    		place          : this.holder,
    		imgOpen        : this.form.attSignPlus,
    		imgClose       : this.form.attSignMinus,
    		innerElement   : this.control
    	});
    	this.control = this.fold.place;
    },

    setValue: function(value) {
    	this.checkbox.checked = false;
    	if (value == this.checkbox.value)
    		this.checkbox.checked = true;
    },

	resetValue: function() {
		this.setValue(0);
	},

    click: function(e, that) {
    	var el = Event.element(e);
    	that.form.onCmpChange(el);
    }
};

// выбор нескольких (галочки)
var multiSelect = Class.create();
multiSelect.prototype = {
	initialize: function(params) {
		var that = this;
		this.cols = 2;
		this.shortListLimit = 12;
		this.items = {};
		this.data = {};
	    Object.extend(this, params);
	    this.createControl();
	    Event.observe(this.fullList, 'click', this.showFull.bindAsEventListener(this), false);
	    Event.observe(this.shortList, 'click', this.showShort.bindAsEventListener(this), false);
	    this.cells = new Array();
	    this.checkboxes = {};
	    this.value = {};
	    this.showShort();
	    this.control.show(false, that);
    },

    sortData: function(data, field, limit) {
    	var res = {};
    	switch (field)
    	{
	    	case 'cnt':
	    		var nd = $H(data).sortBy(function(s) { return -parseInt(s.value.cnt);});
	    		break;
	    	case 'str':
	    		var nd = $H(data).sortBy(function(s) { return s.value.str;});
    			break;
    	}
    	$H(this.value).each(function(item){
    		res[item.key] = data[item.key];
    	});
    	var i = $H(res).size();
    	nd.each(function(item){
    		if (!res[item.key]) {
    			res[item.key] = item.value;
    			i++;
    		}
    		if ((limit) && (i >= limit))
    			throw $break;
    	});
    	return res;
    },

    showFull: function(event) {
    	if ($H(this.data).size() > this.shortListLimit) {
    		this.fullList.hide();
    		this.shortList.show();
    	}
        var data = this.sortData(this.data, 'str', 0);
	    this.fill(data);
    },

    showShort: function(event) {
    	if ($H(this.data).size() > this.shortListLimit) {
    		this.fullList.show();
    		this.shortList.hide();
    	}
        var data = this.sortData(this.data, 'cnt', this.shortListLimit);
        data = this.sortData(data, 'str', 0);
        this.fill(data);
    },

    // заполняем ячейки массивом данных
    fill: function(data) {
        var that = this;
        var tpl = new Template(this.cellTpl.innerHTML.replace(/#%7B([a-zA-Z0-9_-]+)%7D/gi, "#{$1}"));
        var i = 0;
        // удаляем все ячейки
        this.cells.clear();
        $H(data).each(function(item){
            var newCell = that.cellTpl.cloneNode(true);
            that.cells.push(newCell);
            Object.extend(item.value, {counter: parseInt(i)+1});
            newCell.update(tpl.evaluate(item.value));
            var checkbox = newCell.getElementsBySelector('input[type="checkbox"]').first();
    	    Event.observe(checkbox, 'click', that.updateValue.bindAsEventListener(this, that, checkbox, item.value.id), false);
            if (that.value[item.value.id])
            	checkbox.checked = true;
            that.checkboxes[item.value.id] = {title: item.value.attrVal, checkbox: checkbox};
            i++;
        });
        this.build();
    },

    // строим таблицу
    build: function() {
        this.control.hide();
        if (!this.cells.length)
            return;
        var that = this;
        // удаляем строки таблицы
        this.table.getElementsBySelector('tr').invoke('remove');
        var cellsCount = this.cells.length;
        var fullCells = this.cols*Math.ceil(cellsCount/this.cols);
        var newTr, rowCount = 0, i = 0;
        this.cells.each(function(item){
             // вставить строку
            if (Math.ceil(i/that.cols) == (i/that.cols)) {
                newTr = that.table.insertRow(rowCount);
                rowCount++;
            }
            newTr.appendChild(item);
            i++;
        });
        // добавить пустые ячейки, если необходимо
        while (i < fullCells) {
            var newCell = this.cells[0].cloneNode(true);
            newTr.appendChild(newCell);
            newCell.update('&nbsp;');
            i++;
        }
        this.control.show();
    },
 
	setValue: function(attrValIds) {
    	var that = this;
    	var arr = attrValIds.split(',');
    	arr.each(function(id){
    		id = (isNaN(parseInt(id))) ? 0 : id;
    		that.value[id] = true;
    	});
		this.showShort();
	},

	getValue: function() {
		if ($H(this.value).size())
			return this.value;
		return false;
	},
	
	resetValue: function() {
		var that = this;
    	$H(this.value).each(function(d){
    		that.checkboxes[d.key].checkbox.checked = false;
    	});
		this.value = {};
		this.showShort();
	},

	getTxtValue: function() {
		var that = this;
		var res = "";
		var arr = [];
		var attrData = {};
		if ($H(this.value).size()) {
			$H(this.value).each(function(item){
				var valId = item[0];
				arr.push(that.data[valId].str);
			});
			if (that.catId && that.attrId)
				attrData =that.form.data.attrs[that.catId].attrs[that.attrId];
			if (attrData.title)
				res += attrData.title+': ';
			res += arr.join(', ');
		}
		return res;
	},

	getQuery: function() {
		var res = "";
		if (!$H(this.value).size())
			return false;
		res = $H(this.value).keys().join(',');
		if (this.attrId)
			res = this.attrId + '~' + res;
		return res;
    },

    // клик по чекбоксу
	updateValue: function(event, that, checkbox, attrValId) {
    	var el = Event.element(event); 
		if (checkbox.checked)
			that.value[attrValId] = true;
		else
			delete that.value[attrValId];
		that.form.onCmpChange(el);
	},

	createControl: function() {
        this.control = document.createElement('span');
        $(this.control).update(this.html);
        this.table = this.control.getElementsBySelector('[id="multiSelect"]')[0];
        this.fullList = this.control.getElementsBySelector('[id="fullList"]')[0];
        this.shortList = this.control.getElementsBySelector('[id="shortList"]')[0];
        this.cellTpl = $(this.table).getElementsBySelector('td')[0];
        // удаляем строки таблицы
        this.table.getElementsBySelector('tr').invoke('remove');
        if (this.foldTitle)
        	this.createFold();
	    this.holder.appendChild(this.control);
    },
    
    createFold: function() {
    	this.fold = new toggleHolder({
    		data           : {title: this.foldTitle},
    		tplElement     : 'attrsByCat',
    		place          : this.holder,
    		imgOpen        : this.form.attSignPlus,
    		imgClose       : this.form.attSignMinus,
    		innerElement   : this.control
    	});
    	this.control = this.fold.place;
    }
};

// диапазон значений
var doubleTracker = Class.create();
doubleTracker.prototype = {
	initialize: function(params) {
	    var that = this;
		this.fingerOffset = 0;
		this.formatNumbers = false;
		this.trackWidth = 250;
		this.min = 2;
		this.max = 12;
		this.minSpace = 0;
		this.roundTo = 1;
		this.margins = 0;
		this.hairLine = 0;
		this.allowedValues = false;
		this.loadSet = false;
	    Object.extend(this, params);
	    this.setup();
	},

	setup: function() {
		var that = this;
		this.min *= 1;
		this.max *= 1;
		this.minSpace *= 1;
		this.roundTo *= 1;
		this.hairLine *= 1;
		if (this.min >= this.max)
			this.max = this.min + 1;
		this.minPos = this.min;
		this.maxPos = this.max;
		if (this.max - this.min < this.roundTo)
			this.roundTo =  this.max - this.min;
		this.logScale = false;
		// если значений больше 500, то применять лог.шкалу
		if ((this.max - this.min)/this.roundTo > 500)
			this.logScale = true;
		this.createControl();
		this.track.style.width =this.trackWidth + 'px';
		this.trackerLeft = 0;
		this.updateTracker(this.trackWidth + this.fingerOffset);
		this.autoHairline(this.hairLine);
    	this.onTrackMouseDown = this.trackMouseDown.bindAsEventListener(this, that);
	    this.onDocumentMouseMove = this.documentMouseMove.bindAsEventListener(this, that);
	    this.onDocumentMouseUp = this.documentMouseUp.bindAsEventListener(this, that);
	    var onInputChange = this.inputChange.bindAsEventListener(this);
	    Event.observe(this.trackLeftInput, 'change', onInputChange, false);
	    Event.observe(this.trackRightInput, 'change', onInputChange, false);
	    Event.observe(this.trackLeftInput, 'keyup', onInputChange, false);
	    Event.observe(this.trackRightInput, 'keyup', onInputChange, false);
	    Event.observe(this.track, 'mousedown', this.onTrackMouseDown, false);
		this.control.show();
	},

	// создает трекер в заданном элементе
	createControl: function() {
        this.control = document.createElement('span');
        $(this.control).update(this.html);
		this.track = this.control.getElementsBySelector('[id="doubleTrack"]')[0];
		this.tracker = this.control.getElementsBySelector('[id="doubleTrackTracker"]')[0];
		this.trackLeftInput = this.control.getElementsBySelector('[id="doubleTrackLeftInput"]')[0];
		this.trackRightInput = this.control.getElementsBySelector('[id="doubleTrackRightInput"]')[0];
        if (this.foldTitle)
        	this.createFold();
	    this.holder.appendChild(this.control);
	},
    
    createFold: function() {
    	this.fold = new toggleHolder({
    		data           : {title: this.foldTitle},
    		tplElement     : 'attrsByCat',
    		place          : this.holder,
    		imgOpen        : this.form.attSignPlus,
    		imgClose       : this.form.attSignMinus,
    		innerElement   : this.control
    	});
    	this.control = this.fold.place;
    },

	trackMouseDown: function(event, that) {
		this.trackerLeft = this.tracker.offsetLeft - this.margins;
		this.trackerRight = this.trackerLeft + this.tracker.offsetWidth;
		this.trackerOffsets = this.getOffsets(this.track);
		var X = event.clientX + document.documentElement.scrollLeft;
		X -= this.trackerOffsets[0];
		this.left = Math.abs(this.trackerLeft-X+this.margins) <= Math.abs(this.trackerRight-X+this.margins);
		this.updateTracker(X);
	    Event.observe(document, 'mousemove', that.onDocumentMouseMove, false);
	    Event.observe(document, 'mouseup', that.onDocumentMouseUp, false);
	    Event.stop(event);
	},

	documentMouseMove: function(event, that) {
		that.updateTracker(event.clientX + document.documentElement.scrollLeft - this.trackerOffsets[0]);
	    Event.stop(event);
	},

	documentMouseUp: function(event, that) {
    	//var target = Event.element(event); 
		that.form.onCmpChange($(that.tracker));
		Event.stopObserving(document, 'mousemove', that.onDocumentMouseMove);
		Event.stopObserving(document, 'mouseup', that.onDocumentMouseUp);
	    Event.stop(event);
	},

	inputChange: function(event) {
		if (event.keyCode)
	        if (event.keyCode != Event.KEY_RETURN)
	            return;
		if (!this.setValue(this.trackLeftInput.value, this.trackRightInput.value))
			return;
		this.tracker.style.width = (this.trackerRight - this.trackerLeft) + 'px';
		this.tracker.style.left = (this.margins + this.trackerLeft) + 'px';
		this.tracker.style.backgroundPosition = -this.trackerLeft + 'px center';
		this.form.onCmpChange(this.track);
	},

	setValue: function(min, max) {
     	min = (isNaN(parseFloat(min))) ? this.min : min;
		max = (isNaN(parseFloat(max))) ? this.max : max;
        if (min > this.max)
        	min = this.min;
        if (max < this.min)
        	max = this.max;
		var logicWidth = this.trackWidth - this.margins*2 - 1;
		var minPos = parseFloat(min);
		if (minPos)
			this.minPos = minPos;
		else
			this.minPos = this.min;
		if (this.minPos < this.min)
		    this.minPos = this.min;
		
		if (this.logScale)
			this.trackerLeft = Math.round(this.logVal(0, logicWidth, this.minPos, 1));
		else
			this.trackerLeft = Math.round(((this.minPos - this.min)/(this.max - this.min))*logicWidth);

		var maxPos = parseFloat(max);
		if (maxPos)
			this.maxPos = maxPos;
		else
			this.maxPos = this.max;
		if (this.maxPos > this.max)
			this.maxPos = this.max;
		
		if (this.logScale)
			this.trackerRight = Math.round(this.logVal(0, logicWidth, this.maxPos, 1))+1;
		else
			this.trackerRight = Math.round(((this.maxPos - this.min)/(this.max - this.min))*logicWidth)+1;

		this.tracker.style.width = (this.trackerRight - this.trackerLeft) + 'px';
		this.tracker.style.left = (this.margins + this.trackerLeft) + 'px';
		this.tracker.style.backgroundPosition = -this.trackerLeft + 'px center';
		this.updateInputs();
		return true;
	},

	resetValue: function() {
		this.setValue(this.min, this.max);
	},
	
	getValue: function() {
        var min = 0, max = 0;
      	min = parseFloat(this.trackLeftInput.value.replace(",","."));
      	min = (isNaN(min)) ? 0 : min;
      	max = parseFloat(this.trackRightInput.value.replace(",","."));
      	max = (isNaN(max)) ? 0 : max;
      	if (!min & ! max)
      		return false;
      	return [min, max];
	},

	getTxtValue: function() {
		var val;
		var res = "";
		if (val = this.getValue()) {
			var attrData = {};
			if (this.catId && this.attrId)
				attrData =this.form.data.attrs[this.catId].attrs[this.attrId];
			if (attrData.title)
				res += attrData.title;
			if (val[0])
				res += ' от '+val[0];
			if (val[1])
				res += ' до '+val[1];
			if (attrData.unit)
				res += ' ('+attrData.unit+')';
		}
    	return res;
	},


	getQuery: function() {
		var res = ""; 
		var val = this.getValue();
		if (!val)
			return false;
		res = val[0] + '_' + val[1];
		if (this.attrId)
			res = this.attrId + '~' + res;
		return res;
	},

	updateInputs: function() {
		var fix = 0;
		if (this.roundTo < 1) {
		    var x = this.roundTo;
			while(x < 1) {
				x *= 10; 
				fix++;
			}
		}
		if (this.min == this.minPos.toFixed(fix))
			this.trackLeftInput.value = "";
		else
			this.trackLeftInput.value = this.minPos.toFixed(fix)*1;

		if (this.max == this.maxPos.toFixed(fix))
			this.trackRightInput.value = "";
		else
			this.trackRightInput.value = this.maxPos.toFixed(fix)*1;
	},

	updateTracker: function(X) {
		var logicWidth = this.trackWidth - this.margins*2 - 1;
		X -= this.margins;
		if (this.left) {
			X += this.fingerOffset;
			this.trackerLeft = Math.max(0, Math.min(this.trackerRight - 1, X));
			
			if (!this.logScale) {
				// линейная шкала
				this.minPos = this.min + this.trackerLeft*(this.max-this.min)/logicWidth;
			} else {
				// логарифмическая шкала
				this.minPos = this.logVal(0, logicWidth, this.trackerLeft);
				// round
				if (this.minPos.toFixed(3) == this.min.toFixed(3))
					this.minPos = this.min;
				else {
					var round = this.getRound(this.minPos);
					this.minPos = Math.round(this.minPos / round) * round;
				}
			}
		} else {
			X -= this.fingerOffset;
			this.trackerRight = Math.max(this.trackerLeft + 1 , Math.min(logicWidth + 1, X));
			if (!this.logScale) {
			// линейная шкала
			this.maxPos = this.min + (this.trackerRight-1)*(this.max-this.min)/logicWidth;
			} else {
			    // логарифмическая шкала
				this.maxPos = this.logVal(0, logicWidth, this.trackerRight-1);
				// round
				if (this.maxPos.toFixed(3) == this.max.toFixed(3))
					this.maxPos = this.max;
				else {
					var round = this.getRound(this.maxPos);
					this.maxPos = Math.round(this.maxPos / round) * round;
				}
			}
		}
		this.tracker.style.width = (this.trackerRight - this.trackerLeft) + 'px';
		this.tracker.style.left = (this.margins + this.trackerLeft) + 'px';
		this.tracker.style.backgroundPosition = -this.trackerLeft + 'px center';
		this.updateInputs();
	},

	getOffsets: function(element) {
	    var valueT = 0, valueL = 0;
	    do {
			valueT += element.offsetTop  || 0;
			valueL += element.offsetLeft || 0;
			element = element.offsetParent;
	    } while (element);
	    return [valueL, valueT];
	},

	addHairline: function (pos) {
		var touch = this.track.appendChild(document.createElement('div'));
		var logicWidth = this.trackWidth - this.margins*2 - 1;
        if (this.logScale)
            touch.style.left = this.margins + this.logVal(0, logicWidth, pos, 1) + 'px';
        else
        	touch.style.left = this.margins + logicWidth/(this.max-this.min)*(pos-this.min) + 'px';
		touch.className = 'touch';
		pos = pos.toFixed(3)*1;
		touch.innerHTML = "<span>" + pos.toString().replace(/(\d)(?=(\d\d\d)+([^\d]|$))/g, '$1&nbsp;') + "</span>";
	},

	autoHairline: function(num) {
		var logicWidth = this.trackWidth - this.margins*2 - 1;
		if (num >= 1)
			this.addHairline(this.min);
		if (num >= 2)
			this.addHairline(this.max);
		if (num >= 3) {
			var diff = this.max - this.min;
            for (var i=1; i<num; i++) {
            	var val;
                if (this.logScale)
                	val = this.logVal(0, logicWidth, logicWidth/num*i);
                else
                    val = this.min + diff/num*i;
                var round = this.getRound(val);
                if (round < this.roundTo) {
                	round = this.roundTo;
                }
                val = Math.round(val / round) * round;
                this.addHairline(val);
            }
		}
	},

    logVal: function(scaleMin, scaleMax, val, toScale) {
	  	  var minv = Math.log(this.min);
	  	  var maxv = Math.log(this.max);
	  	  // calculate adjustment factor
	  	  var scale = (maxv-minv) / (scaleMax-scaleMin);
	  	  if (toScale)
	  		  return (Math.log(val) - minv)/scale + scaleMin;
	  	  return Math.exp(minv + scale*(val-scaleMin));
	},

	getRound: function(val) {
		for (i = -10; i <= 10; i++) {
	    	var min = Math.pow(10, i);
	    	var max = min*10;
	    	if ((val >= min) && (val < max))
				return min/10;
	    }
    }
};

var selForm = Class.create();
selForm.prototype = {
    initialize: function(params) {
        var that = this;
        Object.extend(this, params);
        this.holderDiv = $(this.holderDiv);
        this.templatesDiv = $(this.templatesDiv);
        this.attrsComponents = {};
        this.attrsCatFolders = {};
        this.attrsFolders = {};
        // шаблоны компонентов
        this.cmpTemplates = this.getTemplates([
           'attrsCatTitle', // название раздела характеристик
           'attrsByCat',    // характеристика (общий шаблон)
           'typeSelect',
           'varcharType',
           'enumType',
           'numberType',
           'brands',        // производители    
           'price'          // цена    
        ]);
        this.viewsTemplates = this.getTemplates([
	       'shortView',
	       'fullView'
        ]);                                        
        this.loadForm();
        this.buttonsDefaultCaption = new Array;
    	if (this.selectButtons) {
    		this.selectButtons.each(function(butt) {
    			var bt = $(butt);
    			if (bt)
        			that.buttonsDefaultCaption.push(bt.value);
    		});
    	}
    },

    // получаем шаблоны элементов формы
    getTemplates: function(tplTitles) {
        var that = this;
        var res = {};
        tplTitles.each(function(tpl) {
        	var element = that.templatesDiv.getElementsBySelector('[id="'+tpl+'"]')[0];
        	var html = element.innerHTML.replace(/#%7B([a-zA-Z0-9_-]+)%7D/gi, "#{$1}");
        	res[tpl] = html;
        });
        return res;
    },

    // загрузка данных формы
    loadForm: function() {
    	var that = this;
    	var params = {'catId':this.catId};
        var r = new Ajax.Request(this.backendScript, {
            method: 'post',
            parameters: params,
            onFailure: function(tr) {
                alert('Ошибка передачи данных');
            },
            onSuccess: function(tr) {
            	that.data = tr.responseJS.data;
            	that.openView('shortView');
                // установка значений в компоненты формы
            	that.setValues();
            	if ($(that.formLoading))
            		$(that.formLoading).hide();
            	if (that.onFormLoad)
            		eval(that.onFormLoad);
            	that.setValuesToCookie();
            }
        });
    },

    openView: function(viewType) {
    	var that = this;
    	if (this.openViewCode)
    		eval(this.openViewCode);
    	this.holderDiv.hide();

    	if (this.typeSelect) {
	    	var tmpDiv = document.createElement('div');
		    tmpDiv.appendChild(this.typeSelect.control);
    	}
    	if (this.price) {
    		var tmpDiv = document.createElement('div');
    		tmpDiv.appendChild(this.price.control);
    	}
    	if (this.brands) {
    		var tmpDiv = document.createElement('div');
    		tmpDiv.appendChild(this.brands.control);
    	}
    	if (this.attrsComponents)
	    	$H(this.attrsComponents).each(function(cmp) {
	    		var tmpDiv = document.createElement('div');
	    		tmpDiv.appendChild(cmp.value.control);
	    	});
    	
    	this.holderDiv.update(this.viewsTemplates[viewType]);

    	var holder;
    	holder = $(this.holderDiv).getElementsBySelector('[id="typeSelectHolder"]')[0];
    	if (this.typeSelect)
    		$(holder).appendChild(this.typeSelect.control);
    	else
     		this.createAttr('typeSelect', 0, holder);
    	// цена
    	holder = $(this.holderDiv).getElementsBySelector('[id="priceHolder"]')[0];
    	if (this.price)
    		$(holder).appendChild(this.price.control);
    	else
     		this.createAttr('price', this.data.price, holder);
    	// производители
    	holder = $(this.holderDiv).getElementsBySelector('[id="brandsHolder"]')[0];
    	if (this.brands)
    		holder.appendChild(this.brands.control);
    	else
    		this.createAttr('brands', this.data.brands, holder);

    	switch (viewType)
    	{
    		case 'fullView':
    			var attrsHolder = $(this.holderDiv).getElementsBySelector('[id="attrsHolder"]')[0];
    			if (attrsHolder)
			        $H(this.data.attrs).each(function(catData) {
						var attr = {catId:catData.key, catTitle:catData.value.title, count:$H(catData.value.attrs).size()};
						var attrHolder = that.newCategory(attr, attrsHolder);
	                    $H(catData.value.attrs).each(function(attrData) {
	                    	Object.extend(attr, attrData.value);
	                    	attr.id = attrData.key;
	                    	var attrCmp = that.attrsComponents[attr.id];
	                    	if (attrCmp) {
	                    		attrHolder.appendChild(attrCmp.control);
	                        	if (attrCmp.getValue())
	                        		that.attrsCatFolders[attrCmp.catId].open();
	                    	} else {
	                    		that.createAttr(attr.type, attr, attrHolder);
	                    	}
	                    });	
	                });
			        break;
    		case 'shortView':
    			var addAttrsHolder = $(that.holderDiv).getElementsBySelector('[id="addAttrs"]')[0];
    			var attrsIds = this.getAllAttrsId();
		        $H(that.data.attrs).each(function(catData) {
					var attr = {catId:catData.key, catTitle:catData.value.title, count:$H(catData.value.attrs).size()};
                    $H(catData.value.attrs).each(function(attrData) {
                    	Object.extend(attr, attrData.value);
                    	attr.id = attrData.key;
                    	var attrHolder = $(that.holderDiv).getElementsBySelector('[id="attr'+attr.id+'"]')[0];
                    	
                    	var attrCmp = that.attrsComponents[attr.id];
                    	if (attrHolder) {
                    		if (attrCmp)
	                    		attrHolder.appendChild(attrCmp.control);
                    		else {
                    			that.createAttr(attr.type, attr, attrHolder);
                    		}
                    	} else {
                    		if (!attrCmp && attrsIds[attr.id])
                    			that.createAttr(attr.type, attr, addAttrsHolder);
                    		if (attrCmp && attrCmp.getValue())
                    			addAttrsHolder.appendChild(attrCmp.control);
                    	}
                    	
                    });	
                });
		    break;
    	};
    	that.holderDiv.show();
    },
    
    createAttr: function(type, data, holder) {
    	switch (type)
		{
			case 'typeSelect':
				this.typeSelect = new singleSelect({
					form   : this,
				    html   : this.cmpTemplates['typeSelect'],
				    holder : holder
				});
				break;
			case 'price':
		    	if (!this.priceTrackWidth)
		    		this.priceTrackWidth = 250;
	    		this.price = new doubleTracker({
	            	form         : this,
	            	trackWidth   : this.priceTrackWidth,
	    			holder       : holder,
	    			html         : this.cmpTemplates['price'],
	    			fingerOffset : 8,
	    			minSpace     : 0,
	    			min          : data.min*1,
	    			max          : data.max*1,
	    			roundTo      : 10,
	    			margins      : 15,
	    			hairLine     : 4
	    		});
	    		break;
			case 'brands':
		    	if (!this.brandsCols)
		    		this.brandsCols = 3;
		    	if (!this.brandsLimit)
		    		this.brandsLimit = 9;
		        this.brands = new multiSelect({
		        	form           : this,
					holder         : holder,
					shortListLimit : this.brandsLimit,
					html           : this.cmpTemplates['brands'],
					cols           : this.brandsCols,
					data           : data
				});	
		        break;
			case 'varchar_value':
		        var html = this.cmpTemplates['varcharType'];
		        var cols = 1;
		        if ($H(data.values).size() > 9)
		        	cols = 2;
		        this.attrsComponents[data.id] = new multiSelect({
		        	form      : this,
					holder    : holder,
					html      : html,
					cols      : cols,
					foldTitle : data.title,
					data      : data.values,
					attrId    : data.id,
					type      : type,
					catId     : data.catId
				});
				break;
			case 'enum_value':
		        var tpl = new Template(this.cmpTemplates['enumType']);
		        var html = tpl.evaluate(data);
		        this.attrsComponents[data.id] = new yesNoSelect({
					form   : this,
					holder : holder,
					html   : html,
					attrId : data.id,
					type   : type,
					catId  : data.catId
				});
				break;
			case 'number_value':
		    	var tpl = new Template(this.cmpTemplates['numberType']);
		        var html = tpl.evaluate(data);
				this.attrsComponents[data.id] = new doubleTracker({
					form         : this,
					holder       : holder,
					html         : html,
					fingerOffset : 8,
					minSpace     : 0,
					min          : data.values.min,
					max          : data.values.max,
					roundTo      : data.values.roundTo,
					margins      : 15,
					hairLine     : 4,
					attrId       : data.id,
					foldTitle    : data.title,
					type         : type,
					catId        : data.catId
				}); 
				break;
		}
    },

	newCategory: function(data, holder) {
    	this.attrsCatFolders[data.catId] = new toggleHolder({
    		data       : data,
    		tplElement : 'attrsCatTitle',
    		place      : holder,
    		imgOpen    : this.catSignPlus,
    		imgClose   : this.catSignMinus
    	});
    	return this.attrsCatFolders[data.catId].contentEl;
	},

	// получить id всех характеристик, которые присутствуют в запросе
	getAllAttrsId: function() {
		var res = {};
		var qParams = this.getQParams();
        if (qParams.at) {
            var attrVals = qParams.at.split('*');
            attrVals.each(function(atVal, index) {
                var a = atVal.split('~');
                res[a[0]*1] = true;
            });
       }
       return res;
    },

    // при изменении значения компонента формы
    onCmpChange: function(el) {
    	if (!this.cmpChangeCode)
    		return;
    	this.setValuesToCookie();
    	eval(this.cmpChangeCode);
    },

    setValues: function() {
		var that = this;
		// установка значений в элементы формы
		var qParams = this.getQParams();
    	if (qParams.pr) {
        	var price = qParams.pr.split('_');
        	this.price.setValue(price[0], price[1]);
        }
        if (qParams.sel)
            this.typeSelect.setValue(qParams.sel);
        if (qParams.br && (qParams.br != '0'))
        	this.brands.setValue(qParams.br);
        // характеристики
        if (qParams.at) {
            var attrVals = qParams.at.split('*');
            attrVals.each(function(atVal, index) {
                var a = atVal.split('~');
                var attrId = a[0];
                var attrVal = a[1];
                var attrCmp = that.attrsComponents[attrId];
                if (attrCmp) {
                	switch (attrCmp.type)
                	{
	                	case 'enum_value':
	                		attrCmp.setValue(attrVal);
	                		break;
	                	case 'varchar_value':
	                		attrCmp.setValue(attrVal);
	                		break;
                		case 'number_value':
                	        var val = attrVal.split('_');
                	        if (val.length != 2)
                	            break;
                			attrCmp.setValue(parseFloat(val[0]), parseFloat(val[1]));
                			break;
                	}
                	if (attrCmp.fold)
                		attrCmp.fold.open();
                	if (that.attrsCatFolders[attrCmp.catId])
                		that.attrsCatFolders[attrCmp.catId].open();
                }
            });
       }
    },

    getQParams: function () {
    	var urlParams = decodeURIComponent(unescape(window.location.search));
    	var prm = urlParams.toQueryParams();
    	if (prm.clr && (prm.clr == 1))
    		return prm;
        var formValues = this.getValuesFromCookie();
        if (!formValues[this.catId])
        	return urlParams;
        else
        	return formValues[this.catId].toQueryParams();
    },
    
    getQuery: function() {
        var query = [];
        if (this.catId)
            query.push('cid='+this.catId);
        var priceQuery = this.price.getQuery();
        if (priceQuery)
        	query.push('pr=' + priceQuery);
        var brandsQuery = this.brands.getQuery();
        if (brandsQuery)
        	query.push('br=' + brandsQuery);
        var attrs = [];
        $H(this.attrsComponents).each(function(val, index) {
        	var attrQuery = val.value.getQuery();
        	if (attrQuery)
        		attrs.push(attrQuery);
        });
        if (attrs.size())
        	query.push('at=' + attrs.join('*'));
        var selTypeQuery = this.typeSelect.getValue();
        if (selTypeQuery > -1)
        	query.push('sel=' + selTypeQuery);
        if (query.length)
            return query.join('&');
        return false;
    },

    setValuesToCookie: function()
    {
    	var name = "formsValues";
    	var expires = 1; // дней
    	
    	expires = expires * 1000 * 60 * 60 * 24;
    	var formValues = this.getValuesFromCookie();
    	formValues[this.catId] = this.getQuery();
    	var cookieVal = $H(formValues).toJSON();
        var today = new Date();
        today.setTime( today.getTime() );
        var expires_date = new Date( today.getTime() + (expires) );
        document.cookie = name+'='+escape(cookieVal) + (';expires='+expires_date.toGMTString()) + (';path=/');
    },

    getValuesFromCookie: function()
    {
        var name = "formsValues";
    	var start = document.cookie.indexOf(name + "=");
        var len = start + name.length + 1;
        if ((!start) && (name != document.cookie.substring(0, name.length)))
            return {};
        if (start == -1)
        	return {};
        var end = document.cookie.indexOf( ';', len );
        if (end == -1)
        	end = document.cookie.length;
        var fV = unescape(document.cookie.substring(len, end));
    	if (!fV)
    		formsValues = {};
    	else
    		formsValues = fV.evalJSON();
    	return formsValues;    	
    },

    go: function(blank) {
    	var q;
    	if (q = this.getQuery()) {
    		if (!blank)
    			window.location = this.location+'?'+q;
    		else
    			window.open(this.location+'?'+q,'_blank');
    	}
    },
    
    resetForm: function() {
    	var that = this;
    	if (this.typeSelect)
    		this.typeSelect.resetValue();
    	if (this.brands)
    		this.brands.resetValue();
    	if (this.price)
    		this.price.resetValue();
    	if (this.attrsComponents)
	    	$H(this.attrsComponents).each(function(cmp) {
	    		var c = cmp.value;
	    		c.resetValue();
            	if (c.fold)
            		c.fold.close();
            	if (that.attrsCatFolders[c.catId])
            		that.attrsCatFolders[c.catId].close();
	    	});
    	if (this.selectButtons) {
    		var captions = this.buttonsDefaultCaption.slice();
    		this.selectButtons.each(function(butt) {
    			var bt = $(butt);
    			if (bt)
        			bt.value = captions.shift();
    		});
    	}
    	this.setValuesToCookie();
    }
};
