/*
 * 
 * INSTRUCTIONS:
 * 
 * 1. Create the HTML for the scrollbar, give it an id and place it in DIV
 * with its display set to none.  When the scrollbar is activated by the slider,
 * the HTML will be moved to the slider and processed as necessary.  When the
 * scrollbar is deleted, the HTML will be placed back to the original DIV.
 * 
 * 2. This class needs to know how wide/high to make the bar, hence the
 * third argument in the constructor.  Calculate this number in pixels.  The bar
 * consists of the area in which the button moves, not including the arrows
 * at the top and bottom and the little rounded ends at the end of the bar.
 * 
 * 3. The button is attached to the middle section of the bar and the class
 * does not know and cannot figure out how large the rounded ends are.  In order
 * to position the button correctly, the fourth argument is necessary and specifies
 * relative to the top-left corner of the middle section of the bar how many pixels
 * the button position should be adjusted.
 * 
 * ASSUMPTION:
 * 
 * 	-The little rounded ends at each end of the bar are the same dimension.
 *
 * ENJOY!
 * 
 */

/*
 * Global constants.
 */


/*
 * Global variables.
 */


/*
 * Scrollbar constructor.
 * INPUT:
 * 	-slider: pointer to slider object
 * 	-id: id of the DIV containing the scrollbar HTML
 * 	-pixel: number of pixels width/height of the bar should change
 * 	-offset: at which offset relative to the bar is the button's original position
 */
function Scrollbar(slider,id,pixel,offset,dir,behavior) {
	if(id == null) return;
	if(slider != null) this.direction = slider.direction;
	else if(dir != null) this.direction = dir;
	else this.direction = direction.HORIZONTAL;
	this.slider = slider;
	this.behavior = behavior;
	this.id = id;
	if(offset == null) offset = {"left":0,"top":0};
	this.offset = offset;
	this.Initialize(pixel);
}

/*
 * Scrollbar properties.
 */

Scrollbar.prototype.direction = null;//whether scrollbar is horizontal or vertical
Scrollbar.prototype.slider = null;//pointer to the slider that uses this scrollbar
Scrollbar.prototype.behavior = null;	//pointer to an object containing methods to be executed
										//when certain events act on the scrollbar
										//used in the case where slider is not defined
Scrollbar.prototype.id = "";//string id of the scrollbar
Scrollbar.prototype.offset = null; 	//offset.left = num of pixels to move button horizontally
									//offset.top = num of pixels to move button vertically
Scrollbar.prototype.button = null;//pointer to the scrollbar's button node
Scrollbar.prototype.coor = null;//array with x and y coordinates of the cursor
Scrollbar.prototype.active = false;//whether the scrollbar is currently active
Scrollbar.prototype.pixels = 0;//number of pixels that the button can move
Scrollbar.prototype.original_position = null;//coordinates of button at time of activation
Scrollbar.prototype.original_parent = null;//pointer to the parent where scrollbar was originally attached
Scrollbar.prototype.mousemove_function = null;//called after the button is activated and when the mouse moves

/*
 * Scrollbar methods.
 */

/*
 * Build the scrollbar.
 */
Scrollbar.prototype.Initialize = function(pixel) {
	if(pixel == null) return;
	this.PrepareScrollbar();
	if(this.PrepareButton()) return;
	this.ActivateEventListeners();
	this.SetBarDimension(pixel);
}

/*
 * Set the bar's width/height depending on the orientation.  Also calculate
 * the range of pixels that the button can move.
 */
Scrollbar.prototype.SetBarDimension = function(pixel) {
	var bar = document.getElementById("bar_middle-"+this.id);
	if(this.direction == direction.HORIZONTAL) {
		bar.style.width = pixel+"px";
		this.pixels = pixel-this.button.offsetWidth-2*this.offset.left;
		if(this.slider == null) return; 
		var top = document.getElementById(SLIDER_BODY_ID+this.slider.slider_index).offsetHeight+"px";
		document.getElementById(this.id).style.top = top;
	} else {
		bar.style.height = pixel+"px";
		this.pixels = pixel-this.button.offsetHeight-2*this.offset.top;
		if(this.slider == null) return;
		var left = document.getElementById(SLIDER_BODY_ID+this.slider.slider_index).offsetWidth+"px";
		document.getElementById(this.id).style.left = left;
	}
}

/*
 * Reattach the scrollbar to the slider and intialize the scrollbar.
 */
Scrollbar.prototype.PrepareScrollbar = function() {
	var me = this;
	var scrollbar = document.getElementById(this.id);
	if(this.slider != null) {
		this.original_parent = scrollbar.parentNode;
		this.original_parent.removeChild(scrollbar);
		scrollbar.style.position = "relative";
		scrollbar.onmouseover = function() {me.slider.mouseover = true;};
		scrollbar.onmouseout = function() {me.slider.mouseover = false;};
		this.slider.anchor.appendChild(scrollbar);
	} else scrollbar.parentNode.style.display = "inline";
	scrollbar.onclick = function(e) {me.HandleScrollbarClick(e);};
}

/*
 * Gets a pointer to the button HTML node.
 */
Scrollbar.prototype.PrepareButton = function() {
	this.button = document.getElementById("button-"+this.id);
	if(this.button == null) return true;
	this.button.style.position = "relative";
	document.getElementById("up-"+this.id).offsetWidth;//useless line to deal with IE 7 bug
	this.button.style.left = this.offset.left+"px";
	this.button.style.top = this.offset.top+"px";
	var me = this;
	this.button.background_set = false;
	this.button.onmouseover = function() {if(!me.active) this.style.backgroundPosition = "center";};
	this.button.onmouseout = function() {if(!me.active) this.style.backgroundPosition = "top";};
	this.button.onmouseup = function(e) {
		this.style.backgroundPosition = "center";
		this.background_set = true;};
	return false;
}

/*
 * Sets all the functions that react to user input.
 */
Scrollbar.prototype.ActivateEventListeners = function() {
	var me = this;
	this.button.onmousedown = function(e) {
		if(me.slider != null && me.slider.IsSmallList()) return;
		if(!e) e = window.event;
		me.ActivateButton(e);
	};
	AddEvent(document.body,"mouseup",function() {
		me.DeactivateButton();
	});
	AddEvent(document.body,"mouseout",function(e) {
		//when the mouse leaves the browser window, deactivate scrolling
		if(!e) e = window.event;
		var target = e.relatedTarget || e.toElement;
		try {//did the mouse go outside the browser window?
			if(target == null || target.nodeName == null || target.nodeName == "HTML")
				me.DeactivateButton();
		} catch(e) {me.DeactivateButton();}//error when moving mouse over the browser's scrollbar
	});
	this.mousemove_function = function(e) {
		if(me.slider != null && me.slider.IsSmallList()) return;
		if(!e) e = window.event;
		me.MoveButton(e);
		return false;
	};
	this.ActivateArrows();
}

/*
 * Activates the arrows.
 */
Scrollbar.prototype.ActivateArrows = function() {
	var me = this;
	var up_button = document.getElementById("up-"+this.id);
	var down_button = document.getElementById("down-"+this.id);
	if(this.slider != null) {
		up_button.onmousedown = function() {me.slider.ScrollUp();
											this.style.backgroundPosition = "bottom left";};
		down_button.onmousedown = function() {me.slider.ScrollDown();
											this.style.backgroundPosition = "bottom right";};
		up_button.onmouseout = function() {me.slider.StopScroll();
											this.style.backgroundPosition = "top left";};
		down_button.onmouseout = function() {me.slider.StopScroll();
											this.style.backgroundPosition = "top right";};
		up_button.onmouseup = function() {me.slider.StopScroll();
											this.style.backgroundPosition = "center left";};
		down_button.onmouseup = function() {me.slider.StopScroll();
											this.style.backgroundPosition = "center right";};
	} else if(this.behavior != null) {
		up_button.onmousedown = function() {me.behavior.UpMouseDown(me);
				this.style.backgroundPosition = "bottom left";};
		down_button.onmousedown = function() {me.behavior.DownMouseDown(me);
				this.style.backgroundPosition = "bottom right";};
		up_button.onmouseout = function() {me.behavior.UpMouseOut(me);
				this.style.backgroundPosition = "top left";};
		down_button.onmouseout = function() {me.behavior.DownMouseOut(me);
				this.style.backgroundPosition = "top right";};
		up_button.onmouseup = function() {me.behavior.UpMouseUp(me);
				this.style.backgroundPosition = "center left";};
		down_button.onmouseup = function() {me.behavior.DownMouseUp(me);
				this.style.backgroundPosition = "center right";};
		up_button.onclick = function(e) {me.behavior.UpClick(me,e);}
		down_button.onclick = function(e) {me.behavior.DownClick(me,e);}
	} else {
		up_button.onmousedown = function() {this.style.backgroundPosition = "bottom left";};
		down_button.onmousedown = function() {this.style.backgroundPosition = "bottom right";};
		up_button.onmouseout = function() {this.style.backgroundPosition = "top left";};
		down_button.onmouseout = function() {this.style.backgroundPosition = "top right";};
		up_button.onmouseup = function() {this.style.backgroundPosition = "center left";};
		down_button.onmouseup = function() {this.style.backgroundPosition = "center right";};
	}
	up_button.onmouseover = function() {this.style.backgroundPosition = "center left";};
	down_button.onmouseover = function() {this.style.backgroundPosition = "center right";};
}

/*
 * Turns the button on so that the user can begin to scroll.
 * INPUT:
 * 	-e: event
 */
Scrollbar.prototype.ActivateButton = function(e) {
	//necessary to disable automatic image dragging in firefox
	if(this.slider != null && this.slider.dragging) return;
	this.active = true;
	if(this.slider != null) this.slider.dragging = true;
	if(e.preventDefault) e.preventDefault();
	var me = this;
	this.coor = getMouseCoordinates(e);
	AddEvent(document.body,"mousemove",this.mousemove_function);
	//necessary because when a user moves the button, the browser
	//will select the text wherever the mouse moves
	disableSelection(document.body);
	this.button.style.backgroundPosition = "bottom";
	if(this.slider != null) this.slider.TurnSlidingOff();
	var left = $(this.button).css("left");
	left = parseInt(left.substring(0,left.length-2));
	var top = $(this.button).css("top");
	top = parseInt(top.substring(0,top.length-2));
	this.original_position = {"left":left,"top":top};
}

/*
 * Turns the button off so that it no longer scrolls.
 */
Scrollbar.prototype.DeactivateButton = function() {
	if(!this.active) return;
	this.active = false;
	if(this.slider != null) this.slider.dragging = false;
	RemoveEvent(document.body,"mousemove",this.mousemove_function);
	enableSelection(document.body);
	if(!this.button.background_set) this.button.style.backgroundPosition = "top";
	this.button.background_set = false;
	if(this.slider == null) return;
	if(this.direction == direction.HORIZONTAL) {
		var left = $(this.button).css("left");
		left = parseInt(left.substring(0,left.length-2));
		this.slider.UpdateList(this.original_position.left-left);
	} else {
		var top = $(this.button).css("top");
		top = parseInt(top.substring(0,top.length-2));
		this.slider.UpdateList(this.original_position.top-top);
	}
}

/*
 * Called when the mouse moves and the button is activated.  Gets the mouse
 * coordinates and moves the button.
 * INPUT;
 * 	-e: event
 */
Scrollbar.prototype.MoveButton = function(e) {
	if(!this.InsideScrollbarRegion(e)) return;
	var coor = getMouseCoordinates(e);
	if(this.direction == direction.HORIZONTAL)
		this.MoveButtonHorizontal(coor);
	else
		this.MoveButtonVertical(coor);
	this.coor = coor;
}

/*
 * Calculates the distance the mouse moved in the horizontal direction and moves the button
 * the same amount.  Checks to make sure that the button does not move beyond the ends of the
 * bar.
 * INPUT:
 * 	-coor: array with x and y coordinates of the current mouse position
 */
Scrollbar.prototype.MoveButtonHorizontal = function(coor) {
	var left = $(this.button).css("left");
	left = parseInt(left.substring(0,left.length-2));
	var offset = left+(coor.x-this.coor.x);
	if(offset < this.offset.left) offset = this.offset.left;
	var upper = this.pixels+this.offset.left;
	if(offset > upper) offset = upper;
	this.button.style.left = offset+"px";
	if(this.slider != null) {
		var slider_list_length = document.getElementById(SLIDER_LIST_ID+this.slider.slider_index).offsetWidth;
		this.slider.MoveTo(parseInt(parseFloat(offset-this.offset.left)/parseFloat(this.pixels)*parseFloat(-this.slider.slider_scroll_max)));
	} else if(this.behavior != null) this.behavior.ButtonMoved(offset-this.offset.left,(offset == upper));
}

/*
 * Calculates the distance the mouse moved in the vertical direction and moves the button
 * the same amount.  Checks to make sure that the button does not move beyond the ends of the
 * bar.
 * INPUT:
 * 	-coor: array with x and y coordinates of the current mouse position
 */
Scrollbar.prototype.MoveButtonVertical = function(coor) {
	var top = $(this.button).css("top");
	top = parseInt(top.substring(0,top.length-2));
	var offset = top+(coor.y-this.coor.y);
	if(offset < this.offset.top) offset = this.offset.top;
	var upper = this.pixels+this.offset.top;
	if(offset > upper) offset = upper;
	this.button.style.top = offset+"px";
	if(this.slider != null) { 
		var slider_list_length = document.getElementById(SLIDER_LIST_ID+this.slider.slider_index).offsetHeight;
		this.slider.MoveTo(parseInt(parseFloat(offset-this.offset.top)/parseFloat(this.pixels)*parseFloat(-this.slider.slider_scroll_max)));
	} else if(this.behavior != null) this.behavior.ButtonMoved(offset-this.offset.top,(offset == upper));
}

/*
 * Determines whether the mouse is in a given area defined by the scrollbar.
 * CASE 1: direction.VERTICAL
 * 		-The mouse is inside the scrollbar region when its y-coordinate falls
 * 		between the top and bottom ends of the bar.
 * CASE 2: direction.HORIZONTAL	
 * 		-The mouse is inside the scrollbar region when its x-coordinate falls
 * 		between the left and right ends of the bar.
 * INPUT:
 * 	-e: event
 */
Scrollbar.prototype.InsideScrollbarRegion = function(e) {
	var scrollbar = document.getElementById("bar-"+this.id);
	var s_coor = $(scrollbar).offset();
	var m_coor = this.AdjustInIE(getMouseCoordinates(e));
	if(this.direction == direction.HORIZONTAL) {
		if(m_coor.x > s_coor.left && m_coor.x < s_coor.left+scrollbar.offsetWidth)
			return true;
	} else {
		if(m_coor.y > s_coor.top && m_coor.y < s_coor.top+scrollbar.offsetHeight) 
			return true;
	}
	return false;
}

/*
 * Moves the button to the given pixel position relative to the top/left.
 * INPUT:
 * 	-pixel: place where to position the button
 */
Scrollbar.prototype.MoveButtonTo = function(pixel) {
	if(pixel < 0 || pixel > this.pixels) return;
	if(this.direction == direction.HORIZONTAL)
		this.button.style.left = pixel+this.offset.left+"px";
	else this.button.style.top = pixel+this.offset.top+"px";
}

/*
 * Moves the button by the given number of pixels.
 * INPUT:
 * 	-pixel: number of pixels to move the button
 * OUTPUT:
 * 	-pos: current position of button without offset adjustment
 */
Scrollbar.prototype.MoveButtonBy = function(pixel) {
	var pos = -1;
	if(this.direction == direction.HORIZONTAL) {
		var left = $(this.button).css("left");
		left = parseInt(left.substring(0,left.length-2));
		pos = pixel+left;
		if(pos < 0) pos = 0;
		if(pos > this.pixels) pos = this.pixels;
		this.button.style.left = pos+this.offset.left+"px";
	} else {
		var top = $(this.button).css("top");
		top = parseInt(top.substring(0,top.length-2));
		pos = pixel+top;
		if(pos < 0) pos = 0;
		if(pos > this.pixels) pos = this.pixels;
		this.button.style.top = pos+this.offset.top+"px";
	}
	return pos;
}

/*
 * Initiates the appropriate response when the user clicks the scrollbar.
 * INPUT:
 * 	-e: event object
 */
Scrollbar.prototype.HandleScrollbarClick = function(e) {
	if(!e) e = window.event;
	var side = this.GetSideWhereClickOccurred(e);
	if(side == null) return;
	if(this.slider != null) {
		var index = Math.floor(-this.slider.GetListPosition()/this.slider.width);
		var nindex = -1;
		if(side) {//true = left/top
			nindex = index-3;
			if(nindex < 0) nindex = 0;
		} else {//false = right/bottom
			nindex = index+3;
			if(nindex >= this.slider.list_size) nindex = this.slider.list_size-1;
		}
		this.slider.SlideTo(nindex,true);
	} else if(this.behavior != null) {
		var pixel = -1;
		if(side) {
			if(this.direction == direction.HORIZONTAL) pixel = -this.button.offsetWidth;
			else pixel = -this.button.offsetHeight;
		} else {
			if(this.direction == direction.HORIZONTAL) pixel = this.button.offsetWidth;
			else pixel = this.button.offsetHeight;
		}
		var pos = this.MoveButtonBy(pixel);
		this.behavior.ButtonMoved(pos);
	}
}

/*
 * Decides on which side of the slider the click on the scrollbar
 * occurred.
 * INPUT:
 * 	-e: event object
 * OUTPUT:
 * 	-true: click occurred to the left of the button (HORIZONTAL MODE)
 * 			click occurred above the button (VERTICAL MODE)
 * 	-false: click occurred to the right of the button (HORIZONTAL MODE)
 * 			click occurred below the button (VERTICAL MODE)
 * 	-null: click occurred on the button
 */
Scrollbar.prototype.GetSideWhereClickOccurred = function(e) {
	var scrollbar = document.getElementById("bar-"+this.id);
	var b_coor = $(this.button).offset();
	var m_coor = this.AdjustInIE(getMouseCoordinates(e));
	var s_coor = $(scrollbar).offset();
	if(this.direction == direction.HORIZONTAL) {
		//clicked the arrows
		if(m_coor.x < s_coor.left || m_coor.x > s_coor.left+scrollbar.offsetWidth)
			return null;
		//clicked scrollbar to the left of button
		if(m_coor.x < b_coor.left)
			return true;
		//clicked scrollbar to the right of button
		if(m_coor.x > b_coor.left+this.button.offsetWidth)
			return false;
	} else {
		//clicked the arrows
		if(m_coor.y < s_coor.top || m_coor.y > s_coor.top+scrollbar.offsetHeight)
			return null;
		//clicked scrollbar above button
		if(m_coor.y < b_coor.top)
			return true;
		//clicked scrollbar below button
		if(m_coor.y > b_coor.top+this.button.offsetHeight)
			return false;
	}
	return null;
}

/*
 * Adjusts the mouse coordinates in Internet Explorer.
 */
Scrollbar.prototype.AdjustInIE = function(m_coor) {
	if(jQuery.browser.msie) {
		if(document.documentElement != null) {
			m_coor.x += document.documentElement.scrollLeft;
			m_coor.y += document.documentElement.scrollTop;
		} else {
			m_coor.x += document.body.scrollLeft;
			m_coor.y += document.body.scrollTop;
		}
	}
	return m_coor;
}

/*
 * Removes the scrollbar from the slider and places it back in its original
 * position, in case a new slider will be created and the scrollbar needs to be
 * generated again.
 */
Scrollbar.prototype.Delete = function() {
	var scrollbar = document.getElementById(this.id);
	if(scrollbar == null) return;
	if(this.slider != null) {
		scrollbar.parentNode.removeChild(scrollbar);
		this.original_parent.appendChild(scrollbar);
	} else scrollbar.parentNode.style.display = "none";
}
