/* Copyright (c) 2006 Tibor Claassen
 *
 * JavaScript class library
 *
 * @author	Tibor Claassen
 * @author	tibor@imagerator.com
 * @date	2006-05-28
 */

/* Prepare namespace simulation */
if (!com) var com = new Object();
if (!com.imagerator) com.imagerator = new Object();


/*
 * Creates classes, manages inheritance.
 */
com.imagerator.Class = {
	
	create: function(constructor, prototype, staticFields) {
		
		return com.imagerator.Class.extend(Function, constructor, prototype, staticFields);
		
	},
	
	extend: function(superclass, constructor, prototype, staticFields) {
		
		if (superclass instanceof Function) {
			
			var c = (constructor instanceof Function) ? constructor : function() { superclass.apply(this, arguments) };
			c.prototype = new superclass();
			c.prototype.constructor = c;
			for (var p in prototype) c.prototype[p] = prototype[p];
			for (p in staticFields) c[p] = staticFields[p];
			return c;
			
		} else throw new Error("Cannot inherit from " + superclass + ". Invalid argument.");
	}
};

/*
 * Basic class for GUI elements.
 */
com.imagerator.View = com.imagerator.Class.create(
	
	function(image, width, height, alt) {
		
		if(arguments.length == 0) return;

		var img = this.createElement("img");
		img.src = image;
		img.alt = (alt != undefined) ? alt : "";
		
		this.width = width;
		this.height = height;
		this.setStyle({
			width: width + "px",
			height: height + "px"
		})
		
	}, {
		
		getView: function() {
			
			return (this._view != undefined) ? this._view : null;
		},
		
		createElement: function(name) {
			
			var e = document.createElement(name);
			com.imagerator.View.resetStyleFor(e);
			
			var v = this.getView();
			if (v != null) v.appendChild(e);
			else this._view = e;
			
			return e;
		},
		
		moveTo: function(x, y, positioning)	{
			
			var p = (positioning != undefined) ? p : "absolute";
			
			this.setStyle({
				
				position: p,
				left: x + "px",
				top: y + "px"
			});
		},
		
		setStyle: function(styleObject) {
			
			com.imagerator.View.setStyleFor(this.getView(), styleObject);
		},
		
		bubbleEvent: function (eventName, eventObject) {
			
			if (this[eventName] instanceof Function) {
				
				this[eventName](eventObject);
			}
		}
	}, {
		
		setStyleFor: function(element, styleObject) {
			
			
			for (var p in styleObject) element.style[p] = styleObject[p];
		},
		
		resetStyleFor: function(element) {
			
			com.imagerator.View.setStyleFor(element, {
				
				margin: "0",
				padding: "0",
				border: "none",
				background: "none",
				textAlign: "left",
				display: "block",
				border: "none"				
			})
		}
	}
); 

/*
 * Clickable GUI element with roll-over effect.
 */
com.imagerator.Button = com.imagerator.Class.extend (com.imagerator.View,
	
	function(image, imageOver, width, height, alt) {
		
		com.imagerator.View.call(this, image, width, height, alt);
		
		(new Image()).src = imageOver;
		
		var self = this;
		var img = this.getView();
		img.onmouseover = function(e) { this.src = imageOver; self.bubbleEvent("unmouseover", e) };
		img.onmouseout = function(e) { this.src = image; self.bubbleEvent("onmouseout", e) };
		img.onmousedown = function(e) { self.bubbleEvent("onmousedown", e) };
		img.onmouseup = function(e) { self.bubbleEvent("onmouseup", e) };
		img.onclick = function(e) { self.bubbleEvent("onclick", e) };
		img.oncontextmenu = function(e) { com.imagerator.Button.cancelEvent(e) };
	}, null,
	{
		cancelEvent: function(eventObj) {
			
			var e = (eventObj != undefined) ? eventObj : event;
			e.cancelBubble = true;
			return false;
		}
	}
);

/*
 * GUI element controlling ScrollPane instances.
 */
com.imagerator.ScrollBar = com.imagerator.Class.extend (com.imagerator.View,
	
	function(upButton, downButton, scrollBarImage, bgImage, height) {
		
		var v = this.createElement("div");
		
		v.appendChild(upButton.getView());
		v.appendChild(downButton.getView());
		this.upButton = upButton;
		this.downButton = downButton;
		this.width = (this.upButton.width > this.downButton.width) ? this.upButton.width : this.downButton.width;
		this.setStyle({ 
			background: "url(" + bgImage + ") repeat-y",
		    width: this.width + "px"
		}); 
		
		var bar = this._scrollbar = this.createElement("div");
		com.imagerator.View.setStyleFor(bar, {
			background: "url(" + scrollBarImage + ") repeat-y",
			width: this.width + "px"
		});
		
		this.upButton.moveTo(0, 0);
		var h = (height != undefined) ? height : 300;
 		this.setHeight(h);
		
		var self = this;
    	this.upButton.onmousedown = function(e) { self.onScroll("Up", e) };
		this.downButton.onmousedown = function(e) { self.onScroll("Down", e) }; 
		this.upButton.onmouseup = this.downButton.onmouseup = function(e) { self.stopScroll(self) };
		bar.oncontextmenu = function(e) { com.imagerator.Button.cancelEvent(e) };
		bar.onmousedown = function (e) { self.startDrag(self, self.upButton.height, self.height - self.downButton.height - self._scrollBarHeight, e) };
	}, {
		
			setHeight: function(newHeight) {
			
				this.height = newHeight;
				this.downButton.moveTo(0, newHeight - this.downButton.height);
				this.setStyle({ height: newHeight + "px" });
			},
		
			onScroll: function(direction, e) {
			
		  	 	this.bubbleEvent("onScroll" + direction, e);
                
				__self = this;
				__direction = direction;
				__scrollID = setInterval(this.startScroll, 500); 
			}, 
			
			startScroll: function() {
				
				clearInterval(__scrollID);
				__scrollID = setInterval(__self.scroll, 50);
			},
			
			scroll: function() { 
				
				__self.bubbleEvent("onScroll" + __direction);
			},
		
			stopScroll: function() {
			
				clearInterval(__scrollID); 
			},
			
			resizeScrollBar: function(wrapperHeight, contentHeight) {
				
				this._wrapperHeight = wrapperHeight;
				this._contentHeight = contentHeight;
				
				var ratio = this._wrapperHeight / this._contentHeight;
				if (ratio >= 1) this.setStyle({ visibility: "hidden" });
				
				this._scrollTrackHeight = Math.round(this.height - this.upButton.height - this.downButton.height);
				this._scrollBarHeight = Math.round(this._scrollTrackHeight * ratio);
				
				com.imagerator.View.setStyleFor( this._scrollbar, {
					height: this._scrollBarHeight + "px",
					position: "relative",
					top: this.upButton.height + "px"
				});
			},
			
			update: function(newOffset) {
				
				var maxOffset = -(this._contentHeight - this._wrapperHeight);
				var scrollFactor = newOffset / maxOffset;
				var maxScrollOffset = this._scrollTrackHeight - this._scrollBarHeight;
				this.scrollOffset = Math.round(maxScrollOffset * scrollFactor + this.upButton.height);
				
				com.imagerator.View.setStyleFor( this._scrollbar, {
					top: this.scrollOffset + "px"
				});
			},
			
			startDrag: function(self, minY, maxY, eventObject) {
				
				var e = (eventObject != undefined) ? eventObject : window.event;
				var bar = self._scrollbar;
				
				var lastY = e.clientY;
				
				if (document.addEventListener) {
					
					document.addEventListener("mousemove", drag, true);
					document.addEventListener("mouseup", stopDrag, true);
				}
				else if (document.attachEvent) {
					
					document.attachEvent("onmousemove", drag);
					document.attachEvent("onmouseup", stopDrag);
				} else {
					
					document.onmousemove = drag;
					document.onmouseup = stopDrag;
				}
				
				if (e.stopPropagation) e.stopPropagation();
				else e.cancelBubble = true;
				if (e.preventDefault) e.preventDefault();
				else e.returnValue = false;
				
				function drag(eventObject) {
					
					var e = (eventObject != undefined) ? eventObject : window.event;
					var offset = e.clientY - lastY;
					lastY = e.clientY;
					
					var t = parseInt(bar.style.top) + offset;
					if (t < minY) t = minY;
					else if (t > maxY) t = maxY;
					com.imagerator.View.setStyleFor( bar, { top: t + "px" });
					
					self.bubbleEvent("onScrolled", e);
					
					if (e.stopPropagation) e.stopPropagation();
					else e.cancelBubble = true;
				}
				
				function stopDrag(eventObject) {
					
					var e = (eventObject != undefined) ? eventObject : window.event;
					
					if (document.removeEventListener) {
						
						document.removeEventListener("mousemove", drag, true);
						document.removeEventListener("mouseup", stopDrag, true);
					}
					else if (document.detachEvent) {
						
						document.detachEvent("onmousemove", drag);
						document.detachEvent("onmouseup", stopDrag);
					} else {
						
						document.onmouseup = null;
						document.onmousemove = null;
					}
					
					if (e.stopPropagation) e.stopPropagation();
					else e.cancelBubble = true;
				}
			},
			
			getScrollFactor: function() {
				
				var maxScroll = this.height - this.downButton.height - this.upButton.height - this._scrollBarHeight;
				var scroll = parseInt(this._scrollbar.style.top) - this.upButton.height;
				
				return scroll / maxScroll;
			}
	}
);

/*
 * View that is scrollable with a ScrollBar instance.
 */

com.imagerator.ScrollPane = com.imagerator.Class.extend (com.imagerator.View,

	function(parentId, wrapperId, contentId, scrollbar, width, height, startOffset) {
         
		this.width = width;
		this.height = height;
		
		var v = this._view = document.getElementById(parentId);
		this.setStyle({ position: "relative" });
		
		var w = this._wrapper = document.getElementById(wrapperId);
		com.imagerator.View.setStyleFor(this._wrapper,{ height: this.height + "px" });
		
		var c = this._content = document.getElementById(contentId);
		this.contentHeight = (c.scrollHeight != undefined) ? c.scrollHeight + startOffset : c.offsetHeight + startOffset;
		this.startOffset = this.scrollOffset = startOffset; 
		this.scrollAmount = 16;
		com.imagerator.View.setStyleFor(this._content,{ top: this.startOffset + "px" });
		
		this._scrollbar = scrollbar;
		v.appendChild(scrollbar.getView());
		scrollbar.moveTo(this.width - scrollbar.width, 0);
		scrollbar.setHeight(height);
		scrollbar.resizeScrollBar(this.height, this.contentHeight);
		
		var self = this;
		scrollbar.onScrollUp = function(e) { self.scrollUp() };
		scrollbar.onScrollDown = function(e) { self.scrollDown() };
		scrollbar.onScrolled = function(e) { self.updateView() };
		
		this._wrapper.onmousewheel = function(eventObject) {
			
			var e = (eventObject) ? eventObject : window.event;
			self.scrollBy(e.wheelDelta / 20);
			e.cancelBubble = true;
			return false;
		};
	},{
		
		scrollDown: function() {
			
			this.scrollBy(-this.scrollAmount);
		},
		
		scrollUp: function() {
			
			this.scrollBy(this.scrollAmount);
		},
		
		scrollBy: function(distance) {
			
			var targetScroll = this.scrollOffset + distance;
			if (targetScroll > this.startOffset) this.scrollOffset = this.startOffset;
			else if (targetScroll < -(this.contentHeight - this.height - this.startOffset)) this.scrollOffset = -(this.contentHeight - this.height - this.startOffset);
			else this.scrollOffset += distance;
			
			this._content.style.top = this.scrollOffset + "px";
			this._scrollbar.update(this.scrollOffset - this.startOffset);
		},
		
		updateView: function(scrollFactor, eventObject) {
			
			var maxScrollOffset = -(this.contentHeight - this.height);
			this.scrollOffset = maxScrollOffset * this._scrollbar.getScrollFactor() + this.startOffset;
			
			this._content.style.top = this.scrollOffset + "px";
		}
	}
);

