

/*
Script: Slider.js
	Class for creating horizontal and vertical slider controls.

License:
	MIT-style license.
*/

var Slider = new Class({

	Implements: [Events, Options],

	options: {/*
		onChange: $empty,
		onComplete: $empty,*/
		onTick: function(position,mx){
			if(mx==null) {
				if(this.options.snap) position = this.toPosition(this.step);
				this.knob.setStyle(this.property, position);
				this.drag.value.now[this.axis] = position;
				//console.log('min');
			} else {
				if(this.options.snap) position = this.toPosition(this.maxstep);
				this.maxknob.setStyle(this.property, position);
				this.maxdrag.value.now[this.axis] = position;
				//console.log('max');
			}
		},
		snap: false,
		offset: 0,
		range: false,
		wheel: false,
		steps: 100,
		mode: 'horizontal'
	},
	//initialize: function(element, knob, options){
	initialize: function(element, knob, options, maxknob){
		this.setOptions(options);
		this.element = $(element);
		this.knob = $(knob);
		this.previousChange = this.previousEnd = this.step = -1;
		//modif
		if(maxknob!=null)
			this.maxknob = $(maxknob);
		else
			this.element.addEvent('mousedown', this.clickedElement.bind(this));
		//endmodif
		if (this.options.wheel) this.element.addEvent('mousewheel', this.scrolledElement.bindWithEvent(this));
		var offset, limit = {}, modifiers = {'x': false, 'y': false};
		switch (this.options.mode){
			case 'vertical':
				this.axis = 'y';
				this.property = 'top';
				offset = 'offsetHeight';
				break;
			case 'horizontal':
				this.axis = 'x';
				this.property = 'left';
				offset = 'offsetWidth';
		}
		this.half = this.knob[offset] / 2;
		this.full = this.element[offset] - this.knob[offset] + (this.options.offset * 2);
		this.min = $chk(this.options.range[0]) ? this.options.range[0] : 0;
		this.max = $chk(this.options.range[1]) ? this.options.range[1] : this.options.steps;
		this.range = this.max - this.min;
		this.steps = this.options.steps || this.full;
		this.stepSize = Math.abs(this.range) / this.steps;
		this.stepWidth = this.stepSize * this.full / Math.abs(this.range) ;
		
		this.knob.setStyle('position', 'relative').setStyle(this.property, - this.options.offset);
		if(maxknob != null) {
			this.maxPreviousChange = -1;
			this.maxPreviousEnd = -1;
			this.maxstep = this.options.end;
			this.maxknob.setStyle('position', 'relative').setStyle(this.property, + this.max - this.options.offset).setStyle('bottom', this.options.knobheight);
		}
		modifiers[this.axis] = this.property;
		limit[this.axis] = [- this.options.offset, this.full - this.options.offset];
		this.drag = new Drag(this.knob, {
			snap: 0,
			limit: limit,
			modifiers: modifiers,
			onDrag: function(){ this.draggedKnob() }.bind(this),
			onStart: function(){ this.draggedKnob() }.bind(this),
			onComplete: function(){
				this.draggedKnob();
				this.end();
			}.bind(this)
		});
		if (this.options.snap) {
			this.drag.options.grid = Math.ceil(this.stepWidth);
			this.drag.options.limit[this.axis][1] = this.full;
		}
		if(maxknob != null) {  
			
			this.maxdrag = new Drag(this.maxknob, {
				limit: limit,
				modifiers: modifiers,
				snap: 0, 
				onStart: function(){
					this.draggedKnob(1);
				}.bind(this),
				onDrag: function(){
					this.draggedKnob(1);
				}.bind(this),
				onComplete: function(){
					this.draggedKnob(1);
					this.end();
				}.bind(this)
			});
			// [snap] The following "if" block was added
			if (this.options.snap) {
				this.maxdrag.options.grid = Math.ceil(this.stepWidth);
				this.maxdrag.options.limit[this.axis][1] = this.full;
			}
			//
		}
	},
	
	set: function(step){
		if (!((this.range > 0) ^ (step < this.min))) step = this.min;
		if (!((this.range > 0) ^ (step > this.max))) step = this.max;
		
		this.step = Math.round(step);
		this.checkStep();
		this.end();
		this.fireEvent('tick', this.toPosition(this.step));
		return this;
	},
	setMin: function(stepMin){
		this.step = stepMin.limit(this.options.start, this.options.end);
		this.checkStep();
		this.end();
		this.moveKnob = this.knob;
		this.fireEvent('tick', this.toPosition(this.step));
		return this;
	},

	setMax: function(stepMax){
		this.maxstep = stepMax.limit(this.options.start, this.options.end);
		this.checkStep(1);
		this.end();
		this.moveKnob = this.maxknob;
		this.fireEvent('tick', [this.toPosition(this.maxstep), 1]);
		return this;
	},

	clickedElement: function(event){
		var dir = this.range < 0 ? -1 : 1;
		var position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half;
		position = position.limit(-this.options.offset, this.full -this.options.offset);
		
		this.step = Math.round(this.min + dir * this.toStep(position));
		this.checkStep();
		this.end();
		this.fireEvent('tick', position);
	},
	
	scrolledElement: function(event){
		var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) : (event.wheel > 0);
		this.set(mode ? this.step - this.stepSize : this.step + this.stepSize);
		event.stop();
	},

	draggedKnob: function(mx){
		// [no_overlap] The following 'if' block was added
		if (this.maxknob!=null && this.options.no_overlap) {
			(function(drag, maxvalue){
				if (drag.value.now[this.axis] > maxvalue) {
					drag.value.now[this.axis] = maxvalue;
					//update the min knob when the max drags it down
					this.step = this.toStep(this.drag.value.now[this.axis]);
					this.checkStep();
					//
					if (drag.options.grid[this.axis]) drag.value.now[this.axis] -= (drag.value.now[this.axis] % drag.options.grid[this.axis]);
					if (drag.options.style) {
						drag.element.setStyle(drag.options.modifiers[this.axis], drag.value.now[this.axis] + drag.options.unit);
					}
					else drag.element[drag.options.modifiers[this.axis]] = drag.value.now[this.axis];
				}
			}.bind(this))(this.drag, this.maxdrag.value.now[this.axis]);
		}
		//
		//var dir = this.range < 0 ? -1 : 1;
		//var position = (mx==null) ? this.drag.value.now[this.axis] : this.maxdrag.value.now[this.axis];
		//console.log(this.drag.value.now[this.axis])
		//position = position.limit(-this.options.offset, this.full -this.options.offset);
		if(mx==null) {
			this.step = this.toStep(this.drag.value.now[this.axis]);
			this.checkStep();
			//this.step = Math.round(this.min + dir * this.toStep(position));
			//this.checkStep();
		}
		else {  
			this.maxstep = this.toStep(this.maxdrag.value.now[this.axis]);
			this.checkStep(1);
			//this.maxstep = Math.round(this.max + dir * this.toStep(position));
			//this.checkStep(1);
		}
		
	},

	checkStep: function(mx){
		if(mx==null) {
			if (this.previousChange != this.step){
				this.previousChange = this.step;
			}
		}
		else {  
			if (this.maxPreviousChange != this.maxstep){
				this.maxPreviousChange = this.maxstep;
			}
		}
		if(this.maxknob!=null) {
			// [no_overlap] Condition 'this.options.no_overlap' added below
			if(this.options.no_overlap || this.step < this.maxstep)
				this.fireEvent('change', { minpos: this.step, maxpos: this.maxstep });
			else    
				this.fireEvent('change', { minpos: this.maxstep, maxpos: this.step });
		}
		else {  
			this.fireEvent('change', this.step);
		}
	},

	end: function(){
		if (this.previousEnd !== this.step || (this.maxknob != null && this.maxPreviousEnd != this.maxstep)) {
			this.previousEnd = this.step;
			if(this.maxknob != null) {
				this.maxPreviousEnd = this.maxstep;

				if(this.step < this.maxstep)
					this.fireEvent('complete', { minpos: this.step + '', maxpos: this.maxstep + '' });
				else    
					this.fireEvent('complete', { minpos: this.maxstep + '', maxpos: this.step + '' });
			}
			else {  
				this.fireEvent('complete', this.step + '');
			}
		}
	},

	toStep: function(position){
		var step = (position + this.options.offset) * this.stepSize / this.full * this.steps;
		return this.options.steps ? Math.round(step -= step % this.stepSize) : step;
	},

	toPosition: function(step){
		return (this.full * Math.abs(this.min - step)) / (this.steps * this.stepSize) - this.options.offset;
	}

});
