/********************************************/
// Scroll-Library
// (c) 2009 Martin Kufner/brainwave innovations 
// All Rights reserved
// mk@brainwaveinnovations.com
//
// USAGE 
// <div id='scrollUpButton'>Up</div>
// <div id='scrollContainerId' style='overflow:hidden;position:relative;'>
//		Content
// </div>
// <div id='scrollDownButton'>Down</div>
//
// Scroll.register('scrollContainerId',{up: 'scrollUpButtonId',down: 'scrollDownButtonId'});
//

Scroll = {
	time: 50,
	elements: [],
	register: function(which,options) {
		which = $(which);
		which.options = options;
		Object.extend(which,this.ElementExtender);
		which.init();
		if(!this.interval) {
			this.interval = window.setInterval(this.step,this.time);
		}
		this.elements.push(which);
	},
	mouseDelta: function(my) {
		if(!this.mouseDelta.values) this.mouseDelta.values = [];
		if(typeof(my) == 'undefined') {
			var values = this.mouseDelta.values.sort(function(a,b){return a-b});
			var p = values[0];
			var m = values.reverse()[0];
			var rv = (Math.abs(p) < Math.abs(m)) ? m : p;
			return rv || 0;
		}
		var delta = my - this.mouseYold;
		this.mouseYold = my
		this.mouseDelta.values.unshift(delta);
		this.mouseDelta.values.splice(4,1);
	},
	step: function() {
		Scroll.mouseDelta(Scroll.mouseY);
		for(var i = 0; i < Scroll.elements.length; i++) {
			Scroll.elements[i].scrollStep();
		}
	},
	up: function(event) {
		var which = this.scrollObject;
		var delta = new Date().getTime() - which.scrollDown;
		which.scrollDown = undefined;
		var dir = this.scroll == 'up' ? -1 : 1;
		if(delta < 100) which.scrollStart(20 * dir);
	},
	down: function(event) {
		var which = this.scrollObject;
		which.scrollDown = new Date().getTime();
		which.scrollSpeed = 0;
		var dir = this.scroll == 'up' ? -1 : 1;
		which.scrollDownSpeed = 10 * dir;
	},
	wheelHandler: function(event) {
		var delta = 0;
		if (event.wheelDelta) {
			delta = event.wheelDelta/120;
			if (window.opera) delta = -delta;
		} else if (event.detail)
			delta = -event.detail/3;
		if (delta) this.wheel(delta);
		if (event.preventDefault) event.preventDefault();
		if (event.stopPropagation) event.stopPropagation();
		event.returnValue = false;
	},
	ElementExtender: {
		init: function() {
			var bounceContainer = $(document.createElement("div")).setStyle({overflow: "hidden", position: "relative"});
			this.setStyle({overflow: "hidden", position: "relative"});
			this.replace(bounceContainer);
			bounceContainer.update(this);
			this.observe('mousedown',this.mouseDown);
			this.observe('mouseup',this.mouseUp);
			this.observe('mousemove',this.mouseMove);
			this.observe('DOMMouseScroll', Scroll.wheelHandler);
			this.observe('mousewheel', Scroll.wheelHandler);
			if(this.options) {
				var up = this.options.up = $(this.options.up);
				up.scroll = 'up';
				up.observe('mousedown',Scroll.down);
				up.observe('mouseup',Scroll.up);
				var down = this.options.down = $(this.options.down);
				down.scroll = 'down';
				down.observe('mousedown',Scroll.down);
				down.observe('mouseup',Scroll.up);
				down.scrollObject = up.scrollObject = this;
				this.onStep = this.options.onStep;
			}
		},
		wheel: function(delta) {
			this.scrollStart(-delta*20);
		},
		scrollStop: function() {
			this.scrollSpeed = 0;
		},
		scrollStart: function(speed,friction) {
			this.scrollFriction = typeof(friction) != 'undefined' ? friction : 0.5;
			this.scrollSpeed = speed ? speed : this.mouseSpeed;
		},
		scrollStep: function() {
			if(this.scrollDown) this.scrollSpeed = this.scrollDownSpeed;
			if(this.scrollBounceY()) this.scrollBounce(-Math.abs(this.scrollBounceY())/this.scrollBounceY()*Math.pow(Math.abs(this.scrollBounceY()),0.8));
			if(!this.scrollSpeed || Math.abs(this.scrollSpeed) < 1) return;
			var sgn = this.scrollSpeed / Math.abs(this.scrollSpeed)
			this.scrollSpeed = (Math.abs(this.scrollSpeed) - this.scrollFriction) * sgn;
			if(isNaN(this.scrollSpeed) || Math.abs(this.scrollSpeed) < 1) {
				this.scrollStop();
				return;
			}
			this.scroll(this.scrollSpeed);
			if(this.onStep) this.onStep(this.scrollTop);
		},
		scroll: function(speed) {
			var bounce = 0;
			var st = this.scrollTop + speed;
			if(st < 0) {
				bounce = st;
				st = 0;
				this.scrollStop();
			} else if(st > (this.scrollHeight - this.height)) {
				var height = this.scrollHeight - this.height;
				bounce = st - height;
				st = height;
				this.scrollStop();
			}
			this.scrollTop = st;
			if(bounce) {
				this.scrollBounce(bounce);
				this.scrollFriction = Math.abs(bounce) / 2;
			}
		},
		scrollBounce: function(bounce) {
			if(Math.abs(bounce) <= 1) bounce = 0;
			this.setStyle({top: - parseInt(bounce) + 'px'});
		},
		scrollBounceY: function() {
			return parseInt(this.getStyle('top'));
		},
		mouseIsDown: false,
		mouseDown: function() {
			this.mouseIsDown = true;
			this.scrollStop();
		},
		mouseUp: function() {
			this.mouseIsDown = false;
			this.scrollStart(-Scroll.mouseDelta()/3);
		},
		mouseMove: function(event) {
			event.stop();
			Scroll.mousemoveYold = Scroll.mouseY; 
			Scroll.mouseY = event.clientY;
			if(this.mouseIsDown) {
				var delta = Scroll.mouseY - Scroll.mousemoveYold;
				if(delta) this.scroll(-delta);
			}
		}
	}
}
