/// Constructor : Reading received options, checking them,
/// creating background for Slider(s), Slider(s), arrows
/// (spins) (if they are user needed), adds events that work 
/// with slider elements
Zapatec.Slider = function(config)
{
	if (typeof config.div == "string") {	
		config.div = document.getElementById (config.div);
	}

	/// assigning configuration for further usage
	this.dragging = false;
	this.orientation = config.orientation;
	this.onChange = config.onChange || function() {};
	this.newPosition = config.newPosition || function() {};
	this.interval = config.onChangeTime || 0;
	this.length = config.length || 0;
	this.dual = config.dual || false;
	this.range = config.range || [0, this.length];
	this.rangeStep = (this.range[1] - this.range[0]) / this.length;
	if (this.dual) {
		this.minDist = Math.floor((config.minDist || this.rangeStep) / this.rangeStep);
		if (this.minDist < 1) {
			this.minDist = 1;
		}
	} else {
		this.minDist = 0;
	}
	
	/// checking step on "correctness"
	if (!this.dual && typeof(config.step) == "number") {
		if (config.step >= (this.range[1] - this.range[0])) {
			this.step = this.range[1] - this.range[0] - this.rangeStep;
		} else {
			this.step = config.step;
		}
	} else {
		this.step = 0;
	}

	/// correcting user errors: 
	/// 	dual is "false"
	///			but secondStart if a number
	if (!this.dual && typeof(config.secondStart) == "number") {
		config.secondStart = this.range[1];
	}
		
	/// correcting start pos#1 and start pos#2, if they are undefined
	if (this.dual) {
		if (typeof(config.secondStart) == "undefined") {
			config.secondStart = this.range[1];
		}
		if (typeof(config.start) == "undefined") {
			config.start = this.range[0];
		}
	}

	/// Creating table that would contain
	/// a Slider and possibly a couple of arrows (spins)
	this.table = Zapatec.Utils.createElement("table", config.div);
	this.table.className = "zpSlider";
	this.table.tbody = Zapatec.Utils.createElement("tbody", this.table);
	this.table.border = 0;
	this.table.cellPadding = 0;
	this.table.cellSpacing = 0;
	
	/// Creating cells
	if (this.orientation == "V") {
		/// if orientation is vertical then  ---
		/// if orientation is vertical then  [1]
		/// if orientation is vertical then  [1]
		/// if orientation is vertical then  [1]
		/// if orientation is vertical then  ---
		this.table.tbody.spinL = Zapatec.Utils.createElement("tr", this.table.tbody);
		this.table.tbody.spinL = Zapatec.Utils.createElement("td", this.table.tbody.spinL);
		
		this.table.tbody.Bg = Zapatec.Utils.createElement("tr", this.table.tbody);
		this.table.tbody.Bg = Zapatec.Utils.createElement("td", this.table.tbody.Bg);

		this.table.tbody.spinR = Zapatec.Utils.createElement("tr", this.table.tbody);
		this.table.tbody.spinR = Zapatec.Utils.createElement("td", this.table.tbody.spinR);
	} else {	
		/// if orientation is horizontal then -  |[1] [1] [1]|
		var row = Zapatec.Utils.createElement("tr", this.table.tbody);

		this.table.tbody.spinL = Zapatec.Utils.createElement("td", row);
		this.table.tbody.Bg = Zapatec.Utils.createElement("td", row);
		this.table.tbody.spinR = Zapatec.Utils.createElement("td", row);
	}

	/// Creating background for Slider
	var div = Zapatec.Utils.createElement("div", this.table.tbody.Bg);
	this.bgDiv = div;
	this.bgDiv.buttonType = "bg";
	this.bgDiv.style.position = "relative";

	/// Creating a Slider (#1), it's obligatory to have at least one
	div = Zapatec.Utils.createElement("div", this.bgDiv);
	this.slider = div;
	this.slider.buttonType = "first_slider";
	this.slider.style.position = "absolute";

	/// Creating an arrows (spins)
	/// According to the orientation of an arrow, we apply requisite CSS class
	if (this.step > 0) {
		if (this.orientation == "H") {
			this.rInc = Zapatec.Utils.createElement("div", this.table.tbody.spinR);
			this.rInc.buttonType = "more";
			this.rInc.className = "zpSliderRightSpin"; ///<right direction
			this.lInc = Zapatec.Utils.createElement("div", this.table.tbody.spinL);
			this.lInc.buttonType = "less";
			this.lInc.className = "zpSliderLeftSpin"; ///<left direction
		} else {		
			this.rInc = Zapatec.Utils.createElement("div", this.table.tbody.spinR);
			this.rInc.buttonType = "more";
			this.rInc.className = "zpSliderDownSpin"; ///<down direction
			this.lInc = Zapatec.Utils.createElement("div", this.table.tbody.spinL);
			this.lInc.buttonType = "less";
			this.lInc.className = "zpSliderUpSpin"; ///<up direction
		}
	}

	/// If a user "needs" second Slider, we create it
	if (this.dual) {
		div = Zapatec.Utils.createElement("div", this.bgDiv);
		this.slider1 = div;
		this.slider1.buttonType = "second_slider";
		this.slider1.style.position = "absolute";
	}
	
	/// According to the orientation  we apply to our Slider(s)
	/// requisite CSS class, make it(them) "draggable"
	/// and set the style of the background of a Slider

	if (this.orientation == 'V') { //<vertical
	  this.bgDiv.className = 'zpSliderBgVertical';
      this.slider.className = 'zpSliderButtonVertical';
      this.bgDiv.style.height =
       ((config.length + this.slider.offsetHeight) + 'px') || '0px';
      var limit = this.getLimit();
      new Zapatec.Utils.Draggable(this.slider, {
        top: limit.min,
        bottom: (limit.min + (Math.round((config.secondStart - this.range[0]) / this.rangeStep) - this.minDist || this.length)),
        vertical: true,
        dragCSS: 'zpSliderButtonVertical'
      });
      if (this.dual) {
        this.slider1.className = this.slider.className;
        new Zapatec.Utils.Draggable(this.slider1, {
          top: (limit.min + (Math.round((config.start - this.range[0]) / this.rangeStep) + this.minDist || 0)),
          bottom: limit.max,
          vertical: true,
          dragCSS : 'zpSliderButtonVertical'
        });
      }
    } else { //<horizontal
      this.bgDiv.className = 'zpSliderBgHorizontal';
      this.slider.className = 'zpSliderButtonHorizontal';
      this.bgDiv.style.width = 100 + 'px';
/*       ((config.length + this.slider.offsetWidth) + 'px') || '0px'; */
      var limit = this.getLimit();
      new Zapatec.Utils.Draggable(this.slider, {
        left: limit.min,
        right: (limit.min + (Math.round((config.secondStart - this.range[0]) / this.rangeStep) - this.minDist || this.length)),
        horizontal: true,
        dragCSS: 'zpSliderButtonHorizontal'
      });
      if (this.dual) {
        this.slider1.className = this.slider.className;
        new Zapatec.Utils.Draggable(this.slider1, {
          left: (limit.min + (Math.round((config.start - this.range[0]) / this.rangeStep) + this.minDist || 0)),
          right: limit.max,
          horizontal: true,
          dragCSS: 'zpSliderButtonHorizontal'
        });
      }
    }
    // set Slider(s) on their positions
	this.setPos(config.start || this.range[0], config.secondStart || this.range[1]);

	/// Customize mouse event handlers
	this.addEvents();
}

/**
 * \internal Returns top offset relative to parent absolute positioned element.
 *
 * \return [number] top offset.
 */
Zapatec.Slider.prototype.getOffsetTop = function () {
  if (typeof this.offsetTop == 'undefined') {
	this.slider.style.top = '0px';
	this.offsetTop = Zapatec.Utils.getAbsolutePos(this.bgDiv).y -
     Zapatec.Utils.getAbsolutePos(this.slider).y;
  }
  return this.offsetTop;
};

/**
 * \internal Returns left offset relative to parent absolute positioned element.
 *
 * \return [number] left offset.
 */
Zapatec.Slider.prototype.getOffsetLeft = function () {
  if (typeof this.offsetLeft == 'undefined') {
    this.slider.style.left = '0px';
    this.offsetLeft = Zapatec.Utils.getAbsolutePos(this.bgDiv).x -
     Zapatec.Utils.getAbsolutePos(this.slider).x;
  }
  return this.offsetLeft;
};

/// Internal setsup the events forSlider
///
Zapatec.Slider.prototype.addEvents = function () {
	var self = this, target = null;
	Zapatec.Utils.addEvent(this.table, "mousedown", function (ev) {
		ev = ev || window.event; 
		target = Zapatec.Utils.getTargetElement(ev);
		while(!target.buttonType && (target != self.table) && (target != document.body)) {
			target = target.parentNode;
		}
		if (!target.buttonType) target = null;
		Zapatec.Slider.mouseDown(ev, self, target);
		if (target) return Zapatec.Utils.stopEvent(ev);
	});
	Zapatec.Utils.addEvent(window.document, "mousemove", function (ev) {
		ev = ev || window.event; 
		Zapatec.Slider.mouseMove(ev, self, target);
		if (target) return Zapatec.Utils.stopEvent(ev);
	});
	Zapatec.Utils.addEvent(window.document, "mouseup", function (ev) {
		ev = ev || window.event; 
		Zapatec.Slider.mouseUp(ev, self, target);
		target = null;
		if (target) return Zapatec.Utils.stopEvent(ev);
	});
}

Zapatec.Slider.mouseDown = function (ev, self, target) {
	if (target) {
		switch (target.buttonType) {
			case "first_slider" : {
				if (self.dual === true) {
					var zIndex1 = parseInt(self.slider.style.Zindex, 10) || 100;
					var zIndex2 = parseInt(self.slider1.style.Zindex, 10) || 100;
					if (zIndex1 == zIndex2) {
						++zIndex1;
					} else if (zIndex1 < zIndex2) {
						zIndex1 = zIndex2 - zIndex1;
						zIndex2 -= zIndex1;
						zIndex1 += zIndex2;
					}
					self.slider.style.zIndex = zIndex1;
					self.slider1.style.zIndex = zIndex2;
				}
				
				break;
			}

			case "second_slider" : {
				if (self.dual === true) {
					var zIndex1 = parseInt(self.slider.style.Zindex, 10) || 100;
					var zIndex2 = parseInt(self.slider1.style.Zindex, 10) || 100;
					if (zIndex1 == zIndex2) {
						++zIndex2;
					} else if (zIndex2 < zIndex1) {
						zIndex1 = zIndex2 - zIndex1;
						zIndex2 -= zIndex1;
						zIndex1 += zIndex2;
					}
					self.slider.style.zIndex = zIndex1;
					self.slider1.style.zIndex = zIndex2;
				}
				
				break;
			}
			
			case "bg" : {
				if (self.dual != true) {
					var shift = 0;
					/// checking and calculating new position
					if (self.orientation == "V") {
						shift = ev.clientY - Zapatec.Utils.getAbsolutePos(self.bgDiv).y - Math.floor(self.slider.offsetHeight / 2);
					} else {
						shift = ev.clientX - Zapatec.Utils.getAbsolutePos(self.bgDiv).x - Math.floor(self.slider.offsetWidth / 2);
					}
					shift = (shift > self.length ? self.length : shift);
					shift = (shift < 0 ? 0 : shift);
					shift = self.range[0] + self.rangeStep * shift;
					/// setting Slider on a new position and retrieving result to user function
					self.setPos(shift);
					self.onChange(shift);
					self.newPosition(shift);
				}
				
				break;
			}

		}
	}
}

Zapatec.Slider.mouseMove = function (ev, self, target) {
	if (target) {
		switch (target.buttonType) {
			case "first_slider" : {
				var pos = self.getPos();
				/// controls onChange call's frequency.
				if (self.onChange && !(self.lastChanged && ((new Date()).getTime() - self.lastChanged.getTime() < self.interval))) {
					self.lastCanged = new Date();
					self.onChange(pos.rangeValue1, pos.rangeValue2);
				}
								
				break;
			}

			case "second_slider" : {		
				var pos = self.getPos();
				/// controls onChange call's frequency.
				if (self.onChange && !(self.lastChanged && ((new Date()).getTime() - self.lastChanged.getTime() < self.interval))) {
					self.lastCanged = new Date();
					self.onChange(pos.rangeValue1, pos.rangeValue2);
				}
				
				break;
			}
		}
	}
}

Zapatec.Slider.mouseUp = function (ev, self, target) {
	if (target) {
		switch (target.buttonType) {
			case "more" : {
				/// getting current position
				var pos = self.getPos();
				/// checking and then calculating
				if (pos.rangeValue1 + self.step < self.range[1]) {
					pos.rangeValue1 += self.step;
				} else {
					pos.rangeValue1 = self.range[1];			
				}
				/// setting Slider on new position and retrieving result to user function
				self.setPos(pos.rangeValue1);
				pos = self.getPos();
				self.onChange(pos.rangeValue1);
				self.newPosition(pos.rangeValue1);
				
				break;
			}

			case "less" : {
				/// getting current position
				var pos = self.getPos();
				/// checking and then calculating
				if (pos.rangeValue1 - self.step > self.range[0]) {
					pos.rangeValue1 -= self.step;
				} else {
					pos.rangeValue1 = self.range[0];
				}
				/// setting Slider on new position and retrieving result to user function
				self.setPos(pos.rangeValue1, 0, true);
				pos = self.getPos();
				self.onChange(pos.rangeValue1);
				self.newPosition(pos.rangeValue1);
				
				break;
			}

			case "first_slider" : case "second_slider" : {
				var pos = self.getPos();
				if ((self.dual === true) && (target.buttonType == "first_slider")) {
					/// setting new limits to another Slider(the one that has not been moved)
					if (self.orientation == "V") {
						self.slider1.Atr.top = self.getOffsetTop() + pos.value1 + self.minDist;
					} else {
						self.slider1.Atr.left = self.getOffsetLeft() + pos.value1 + self.minDist;
					}
				}
				if ((self.dual === true) && (target.buttonType == "second_slider")) {
					/// setting new limits to another Slider(the one that has not been moved)
					if (self.orientation == "V") {
						self.slider.Atr.bottom = self.getOffsetTop() + pos.value2 - self.minDist;
					} else {
						self.slider.Atr.right = self.getOffsetLeft() + pos.value2 - self.minDist;
					}
				}
				self.newPosition(pos.rangeValue1, pos.rangeValue2);
				
				break;
			}
		}
	}
}


/// Sets the Slider(s) on its(their) position
///
/// @param pos1 [number] position of the Slider#1
/// @param pos2 [number] position of the Slider#2
Zapatec.Slider.prototype.setPos = function (pos1, pos2, debug) {
	/// Checks position(s) of the Slider(s) on "correctness"
	//alert(pos1);
	pos1 = parseInt(pos1, 10) || 0;
	pos1 = Math.floor((pos1 - this.range[0]) / this.rangeStep);
	if (this.dual) {
		pos2 = parseInt(pos2, 10) || 0;
		pos2 = Math.floor((pos2 - this.range[0]) / this.rangeStep);
		if (pos2 > this.length) {
			pos2 = this.length;
		}
		if (pos2 <= pos1) {
			pos1 = pos2 - 1;
		}
		if (pos1 < 0) {
			pos1 = 0;
		}
	} else {	
		if (pos1 > this.length) 
		{
			pos1 = this.length;
		}
		if (pos1 < 0) {
			pos1 = 0;
		}
	}
	
	/// set Slider(s) on its(their) position(s)
	if (this.orientation == "V") { ///< if vertical
		this.slider.style.top = this.getOffsetTop() + pos1 + "px"; ///< #1 by Y coordinate 
		if (this.dual) { ///< #2 (if exist)
			this.slider1.style.top = this.getOffsetTop() + pos2 + "px"; ///< by Y coordinate
		}
	} else {
		this.slider.style.left = this.getOffsetLeft() + pos1 + "px"; ///< #1 by X coordinate
		if (this.dual) { ///< #2 (if exist)
			this.slider1.style.left = this.getOffsetLeft() + pos2 + "px"; ///< by X coordinate
		}
	}
}

/// Retrieves the relative position (relative to background of Slider(s)) of a Sliders

/// @return [object] { x, y } containing the limits.
Zapatec.Slider.prototype.getPos = function () {
	var pos = {};
	/// getting absolute position of background and of Slider(s)
	var bg = Zapatec.Utils.getAbsolutePos(this.bgDiv);
	var sl1 = Zapatec.Utils.getAbsolutePos(this.slider);
	if (this.dual) {
		var sl2 = Zapatec.Utils.getAbsolutePos(this.slider1);
	}

	/// calculating position(s)
	if (this.orientation == "V") {
		pos.value1 = sl1.y - bg.y; ///< #1 by Y coordinate
		if (this.dual) { //< #2 (if exist)
			pos.value2 = sl2.y - bg.y; ///< by Y coordinate
		}
	} else {
		pos.value1 = sl1.x - bg.x;///< #1 by X coordinate
		if (this.dual) { //< #2 (if exist)
			pos.value2 = sl2.x - bg.x; ///< by X coordinate
		}
	}
	pos.rangeValue1 = this.range[0] + this.rangeStep * pos.value1; 
	if (pos.value2) {
		pos.rangeValue2 = this.range[0] + this.rangeStep * pos.value2;
	}
	return pos; ///< returning position(s)
}

/// Retrieves the absolute limits (relative to <body>) of a background of Slider(s)

/// @return [object] { x, y } containing the limits.
Zapatec.Slider.prototype.getLimit = function () {
	var limit = {};
	/// calculating minimum
	if (this.orientation == "V") {
		limit.min = this.getOffsetTop();
	} else {
		limit.min = this.getOffsetLeft();		
	}
	limit.max = limit.min + this.length; ///< calculating maximum
	return limit; ///< returning limit
}
;
/* commented by me
Zapatec.Utils.addEvent(window, 'load', Zapatec.Utils.checkActivation);
*/
