document.UIElements = new Array();

function scrollBar(el, index, has_buttons) {
	this.parent = el.parentNode;
	this.has_buttons = (has_buttons == null) ? false : true;

	//and now we'll build the scrollbar:

	this.toscroll = el;
	this.canscroll = false;
		
	this.scrollwell = document.createElement("div");
	//this.scrollwell.className = "scrollwell";
	var buttons_class = "";
	if (this.has_buttons != null) buttons_class = " " + has_buttons + "_buttons";
	this.scrollwell.setAttribute("class", "scrollwell" + buttons_class);

	//the end caps and middle:
	this.top_end_cap = document.createElement("div");
	this.top_end_cap.className = "top_end_cap";
	
	this.scrollwell_middle = document.createElement("div");
	this.scrollwell_middle.className = "scrollwell_middle";

	this.bottom_end_cap = document.createElement("div");
	this.bottom_end_cap.className = "bottom_end_cap";

	this.scrollwell.appendChild(this.top_end_cap);	
	this.scrollwell.appendChild(this.bottom_end_cap);
	this.scrollwell.appendChild(this.scrollwell_middle);
	
	//the scroll tab:
	this.scrolltab = document.createElement("div");
	this.scrolltab.className = "scrolltab";
	
	this.scrolltab_top_cap = document.createElement("div");
	this.scrolltab_top_cap.className = "scrolltab_top_cap";
		
	this.scrolltab_middle = document.createElement("div");
	this.scrolltab_middle.className = "scrolltab_middle";

	this.scrolltab_bottom_cap = document.createElement("div");
	this.scrolltab_bottom_cap.className = "scrolltab_bottom_cap";
	
	this.scrolltab.appendChild(this.scrolltab_top_cap);
	this.scrolltab.appendChild(this.scrolltab_bottom_cap);
	this.scrolltab.appendChild(this.scrolltab_middle);
	
	//now the buttons if desired
	
	if (this.has_buttons) {
		this.button_up = document.createElement("div");
		this.button_up.className = "button_up_norm";
		
		this.button_down = document.createElement("div");
		this.button_down.className = "button_down_norm";
		
		this.scrollwell.appendChild(this.button_up);
		this.scrollwell.appendChild(this.button_down);
	}
	
	//this.scrollwell.appendChild(this.scrolltab);
	this.scrollwell_middle.appendChild(this.scrolltab);

	this.parent.appendChild(this.scrollwell);

//Variables	
	this.init_y = 0;
	
	this.top = 0;
	this.SCROLL_RATIO = 1;

	//rather than excessive DOM calls, values are set in setScrollBarDimensions:	
	this.TO_SCROLL_HEIGHT = 0;			//the offset height of what we're scrolling
	this.TO_SCROLL_SCROLLHEIGHT = 0;	//the scroll height of what we're scrolling
	this.SCROLLTAB_HEIGHT = 0;			//the offset height of the scroll tab

	this.scroll_timer;
	
	var self = this;

	/***************************************************************\
	|*	Set up the functions for actually handling scrolling       *|
	\***************************************************************/


	this.handleScroll = function() {
		var max = (this.SCROLLWELL_MIDDLE_HEIGHT - this.SCROLLTAB_HEIGHT);

		if (this.top < 0) this.top = 0;
		if (this.top > max) this.top = max;

		this.toscroll.scrollTop = Math.round(this.top * this.SCROLL_RATIO);
		this.scrolltab.style.top = this.top + "px";
	}
	
//scrolling using the buttons:
	this.scroll = function(dir, interval) {
	//let's add in some acceleration, like in OS X
	//obviously, play around with initial intervals and decrement to get it
	//as smooth as possible...
	
		if (dir == "up") {
			this.top -= 1;
			this.button_up.className = "button_up_press";
		} else {
			this.top += 1;
			this.button_down.className = "button_down_press";
		}

		this.handleScroll();
		
		if (interval > 20) interval -= 20;
		
		this.scroll_timer = setTimeout(function(){self.scroll(dir, interval);}, interval);
	}
	
	this.finishScroll = function(dir) {
		if (dir == "up") 
			this.button_up.className = "button_up_norm";
		else
			this.button_down.className = "button_down_norm";
		
		clearTimeout(this.scroll_timer);
	}
	
//clicking on the scroll well
	this.handleScrollClick = function(e) {
		if (e.target == this.scrollwell_middle) {
			if (this.scroll_obj.init_y == 0) this.scroll_obj.init_y = getTotalDistanceFromTop(this.scrolltab_middle);
			this.scroll_obj.top = (e.clientY - this.scroll_obj.init_y - (this.SCROLLTAB_HEIGHT/2));
			
			this.scroll_obj.controller(e);
		}
	}
	
//using a mouse's wheel
	this.handleScrollWheel = function(e) {
		this.scroll_obj.top -= e.wheelDelta / 12; //it's 10 times the number of clicks (goes plus or minus 120, I think - no time to check it out)
		
		this.scroll_obj.controller(e);	
	}
	
//handling clicking on scroll buttons:	
	this.scrollDownButtonDown = function(e) {
		this.scroll('down', 200);
		
		document.addEventListener("mouseup", self.scrollDownButtonUp, false);
		e.preventDefault();
	}

	this.scrollUpButtonDown = function(e) {
		this.scroll('up', 200);
		
		document.addEventListener("mouseup", self.scrollUpButtonUp, false);
		e.preventDefault();
	}
	
	this.scrollDownButtonUp = function(e) {
		self.finishScroll('down');
		document.removeEventListener("mouseup", self.scrollDownButtonUp, false);
	}

	this.scrollUpButtonUp = function(e) {
		self.finishScroll('up');
		document.removeEventListener("mouseup", self.scrollUpButtonUp, false);
	}
	
	this.getScrollBarDimensions = function() {
	//to_scroll stuff
		var ts_dims = getComputedStyleValues(this.toscroll, 'offsetHeight', 'scrollHeight');
		this.TO_SCROLL_HEIGHT = ts_dims[0];
		this.TO_SCROLL_SCROLLHEIGHT = ts_dims[1];
		
		this.SCROLLWELL_MIDDLE_HEIGHT = getComputedStyleValues(this.scrollwell_middle, 'offsetHeight')[0];
		this.SCROLL_RATIO = this.TO_SCROLL_SCROLLHEIGHT/this.SCROLLWELL_MIDDLE_HEIGHT;
	}

	this.setScrollBarDimensions = function() {
		this.getScrollBarDimensions();
		/*
		We can't do this because there's crashes unless an alert is in place
		or we wait till everything's finished (fault in getComputedStyleValues)
		
		alert('something');
		
		if (this.TO_SCROLL_SCROLLHEIGHT <= this.TO_SCROLL_HEIGHT) {
			this.scrollwell.style.display = "none";
			this.toscroll.className = "scrollable_element";
			this.toscroll.scrollTop = 0;
			this.top = 0;
		} else {
			this.scrollwell.style.display = "block";
			
			if (this.toscroll.className != "scrollable_element_overflow") {
			//we've gone from non scrollable content, to scrollable
				this.toscroll.className = "scrollable_element_overflow";
				
				//need to re-get the scrollheight, as it will have changed (now that
				//we're thinner)

				this.getScrollBarDimensions();
			}
			
			this.SCROLLTAB_HEIGHT = (this.TO_SCROLL_HEIGHT / this.SCROLL_RATIO); //this.actual_tab_height; //Math.max(this.min_tab_height, this.actual_tab_height);			
			this.scrolltab.style.height = this.SCROLLTAB_HEIGHT + "px";
			
			this.handleScroll();				
		}*/
		
		setTimeout( function () {
			if (self.TO_SCROLL_SCROLLHEIGHT <= self.TO_SCROLL_HEIGHT) {
				self.scrollwell.style.display = "none";
				self.toscroll.className = "scrollable_element";
				self.toscroll.scrollTop = 0;
				self.top = 0;
			} else {
				self.scrollwell.style.display = "block";
				if (self.toscroll.className != "scrollable_element_overflow") {
				//we've gone from non scrollable content, to scrollable
					self.toscroll.className = "scrollable_element_overflow";
					
					//need to re-get the scrollheight, as it will have changed (now that
					//we're thinner)
	
					self.getScrollBarDimensions();
				}
	
				self.SCROLLTAB_HEIGHT = (self.TO_SCROLL_HEIGHT / self.SCROLL_RATIO);
				self.scrolltab.style.height = self.SCROLLTAB_HEIGHT + "px";
				
				self.handleScroll();				
			}
		}, 0);
	}
	
	//setTimeout(function(){self.setScrollBarDimensions();}, 0);
	this.setScrollBarDimensions();
	
//And now add the event listeners	
	this.scrollwell.addEventListener("click",function(e){self.handleScrollClick(e);e.preventDefault();}, false);
	this.toscroll.addEventListener("mousewheel", function(e){self.handleScrollWheel(e);e.preventDefault();}, false);
	this.toscroll.addEventListener("keypress", function(e) {
		alert(e.keyCode);
	}, false);
	
	if (this.has_buttons) {
		this.button_down.addEventListener("mousedown", function(e){self.scrollDownButtonDown(e);}, false);
		this.button_up.addEventListener("mousedown",function(e){self.scrollUpButtonDown(e);}, false);
	}
	
//Make the scroll tab draggable:
	this.scroll_obj = new DraggableElement(this.scrolltab, 'scroller_'+index, this, scrollMouseDown, scrollHandler, null);

/*
Firefox doesn't handle mutation events so don't bother with these for the web version
	//and now we listen for mutation events in what we're scrolling, so that
	//our scrollbar will react dynamically to any DOM changes to it.
	//Dashboard client crashes if we do this synchronously...
	
	setTimeout( function() {
		self.toscroll.addEventListener("DOMSubtreeModified",
			function(e) {
				//alert('modified subtree');
				self.setScrollBarDimensions();
			}, false);

		self.toscroll.addEventListener("DOMAttrModified",
			function(e) {
				//alert('modified attribute');
				self.setScrollBarDimensions();
			}, false);

		self.toscroll.addEventListener("resize",
			function(e) {
				alert('modified size');
				//self.setScrollBarDimensions();
			}, false);
	}, 0);
*/
}

function scrollMouseDown(e) {
	this.init_top = this.element.offsetTop;
	this.init_left = 0;
	
	this.init_y = (e.clientY - this.init_top);
	this.init_x = (e.clientX - this.init_left);
}

function scrollHandler(e) {
	this.parent.top = this.top;
	this.parent.handleScroll();
}

function scrollButtonDown(obj) {
	//obj.
}

function scrollHandler(e) {
	this.parent.top = this.top;
	this.parent.handleScroll();
}
/*
scrollBar.prototype.__defineGetter__(
       "dimensions",
       function() { return "hello"; }
   );
 */

function scrollableElement(el, index, has_buttons) {
	this.parent = el.parentNode;

	this.id = el.id;

	this.container = document.createElement("div");
	this.container.className = "scroll_container";
	
	if (this.id) {
		this.container.id = this.id;
		el.removeAttribute("id");
	}
	
	this.toscroll = el.cloneNode(true);
	this.toscroll.removeAttribute("class");
	this.toscroll.className = "scrollable_element";
	
	this.container.appendChild(this.toscroll);
	
	this.parent.replaceChild(this.container, el);
	
	//var button_string = (has_buttons != null) ? "buttons=yes" : "";

	document.UIElements.push(new scrollBar(this.toscroll, document.UIElements.length, has_buttons));
	this.scrollbar = document.UIElements[document.UIElements.length-1];
	
//Functions:
	this.setContent = function (content) {
		this.toscroll.innerHTML = content;
	}
	
	//overriding appendChild...
	this.appendChild = function(node) {
		this.toscroll.appendChild(node);
	}
}

function makeScrollbars() {
	var elems = document.getElementsByTagName("*");

	for (var i=0; i<elems.length; i++) {
		if (/\bscrollable\b/.exec(elems[i].className)) {
	//no buttons
			document.UIElements.push(new scrollableElement(elems[i], document.UIElements.length, null));
		} else if (/\bscrollable_buttons_bottom\b/.exec(elems[i].className)) {
	//buttons at the bottom
			//document.UIElements.push(new scrollBar(elems[i], document.UIElements.length, "buttons=yes, bottom_offset=27"));
			document.UIElements.push(new scrollableElement(elems[i], document.UIElements.length, "bottom"));
		} else if (/\bscrollable_buttons_top\b/.exec(elems[i].className)) {
	//buttons at the top
			document.UIElements.push(new scrollableElement(elems[i], document.UIElements.length, "top"));
		} else if (/\bscrollable_buttons_separate\b/.exec(elems[i].className)) {
	//buttons at top and bottom
			document.UIElements.push(new scrollableElement(elems[i], document.UIElements.length, "separate"));
		}
	}
}

document.getUIElementById = function(id) {
	for (var i=0; i < document.UIElements.length; i++) {
		if (document.UIElements[i].id == id)
			return document.UIElements[i];
	}
	
	return null;
}

document.getUIElementsByName = function(name) {
	var ar = [];
	
	for (var i=0; i < document.UIElements.length; i++) {
		if (document.UIElements[i].name && document.UIElements[i].name == name)
			ar.push(document.UIElements[i]);
	}
	
	return ar;
}


//elements who don't have a display (or their ancestors don't) return null
//when getting their computed style. this is a workaround.

function computedStyleWorkAround(el) {
	var ar = [];
	var bod = document.getElementsByTagName("body")[0];
	
	if (el != bod && el.nodeType == 1) {
		do {
			if (document.defaultView.getComputedStyle(el, '') == null) {
				ar.push(new Element(el, el.style.display));
				el.style.display = "block";
			}
			el = el.parentNode;
		} while (el != bod);
	}
	
	return ar.reverse(); //so we run top down when putting them back
}

function Element(el, display) {
	this.element = el;
	this.display = display;
}

function getComputedStyleValues(el) {
	//so you can pass an id of an element, or the element...
	el = (el == new String(el)) ? document.getElementById(el) : el;
	
	var ar = [], put_back = false;

	var c_s = document.defaultView.getComputedStyle(el, '');
	
	if (c_s == null) {
		var els = computedStyleWorkAround(el);
		c_s = document.defaultView.getComputedStyle(el, '');
		put_back = true;
	}

	for (var i=1; i<arguments.length; i++) {
		var val;
		switch(arguments[i]) {
			case "offsetTop":
				val = el.offsetTop;
				break;
			case "offsetLeft":
				val = el.offsetLeft;
				break;
			case "offsetHeight":
				val = el.offsetHeight;
				break;
			case "offsetWidth":
				val = el.offsetWidth;
				break;
			case "scrollHeight":
				val = el.scrollHeight;
				break;
			case "scrollWidth":
				val = el.scrollWidth;
				break;
			default:
				val = c_s.getPropertyValue(arguments[i]);
		}
		
		ar.push(val);
	}
	
	if (put_back) {
		//now put them back
		//note that the bug where the widget would crash seems to have been
		//fixed in the 10.4.2 update
		for (elem in els) {
			els[elem].element.style.display = els[elem].display;
		}
	}
	
	return ar;
}

function DraggableElement(what, id, parent, startFunction, controller, endFunction) {
	this.element = (what == new String(what)) ? document.getElementById(what) : what;

	this.instance = "drag_" + id;
	eval(this.instance + " = this"); //<-- mildly minging, but it lets us set up the objects without worry about global vars
	
	this.is_moving = false;
	
	this.parent = parent;
	
	this.startFunction = startFunction;
	this.controller = controller;
	this.endFunction = endFunction;
	
	this.canmove = false;
	this.init_y = 0;
	this.init_x = 0;
	this.init_top = 0;
	this.init_left = 0;
	
	this.top = 0;
	this.left = 0;
	
	this.current_tile = 0; //the index of the tile we're dragging

	this.mousemove = function(e) {
		if (this.canmove) {
			this.top = e.clientY - this.init_y;		//always defined in the parent object
			this.left = e.clientX - this.init_x;	//always defined in the parent object
			this.is_moving = true;
		}			
					
		//so, controller is actually the controlling function. This
		//means we can use this 'class' for multiple things,
		//such as scrollbars or sliders etc.
		
		if (this.controller != null) this.controller(e);
	}
	
	this.mousedown = function(e) {
		this.init_top = this.element.offsetTop + this.element.parentNode.offsetTop + this.element.parentNode.parentNode.offsetTop; //getOffsetTop(this.element); 
		
		this.init_left = this.element.offsetLeft + this.element.parentNode.offsetLeft;
		
		this.init_y = (e.clientY - this.init_top);
		this.init_x = (e.clientX - this.init_left);
		this.canmove = true;

	//the following seems rather 'hacky' doesn't it?
		cur_drag_obj = this;
		document.addEventListener("mousemove", dragging, false);
		document.addEventListener("mouseup", finishDragging, false);

		if (this.startFunction != null) this.startFunction(e);
		//document.removeEventListener("mousemove", handleDockMagnify, false);
		e.preventDefault();
	}
	
	this.mouseup = function(e) {
		this.canmove = false;
		this.is_moving = false;
		
		if (this.endFunction != null) this.endFunction();

		//and remove the event listeners:		
		document.removeEventListener("mousemove", dragging, false);
		document.removeEventListener("mouseup", finishDragging, false);
	}
	
	this.element.setAttribute("onmousedown", this.instance + ".mousedown(event)");
	//this.element.setAttribute("onclick", this.instance + ".click(event)");
}

/*
	note that I had to actually make these to as proper functions for the event
	listeners, otherwise they weren't being removed (when using something like
	function(e){cur_drag_obj.mousemove(e);} instead).
 */
function dragging(e) {
	cur_drag_obj.mousemove(e);
}

function finishDragging(e) {
	cur_drag_obj.mouseup(e);
}

function initialiseScrollbars() {
	//include style:
	//document.write('<link rel="StyleSheet" href="Scrollbar/scrollbar.css" type="text/css" />');
	makeScrollbars();
}

window.addEventListener("load", initialiseScrollbars, false);
