var Aheuh = {
	Version: '1.0',
	intialize: function () {}
}
Aheuh.intialize();Aheuh.Scroller = Class.create({
	
	_baseHtmlClass: { mask:'scrollerMask', content:'scrollerContent' },
	_buttons: { Left:-1, Right:1 ,Top:-1, Bottom:1 },
	_axis: { Left:'x', Right:'x', Top:'y', Bottom:'y' },
	
	initialize: function (o) {
		if ($(o.root)) {
			
			for (var i in this._baseHtmlClass) {
				var check = $$('#'+o.root+' .'+this._baseHtmlClass[i]);
				if (check.length==0) { alert('Html Class required : '+this._baseHtmlClass[i]); return; }
				else { this['_'+i] = check[0]; }
			}
			
			this._root = o.root;
			this.speed = o.speed || 2;
			this.speedInterval = o.speedInterval || 2;
			this._wheelSpeed = o.wheelSpeed|| 10;
			this._interval = null;			
			this._cursors = { scrollx:false, scrolly:false };
			this._tools = {};
			this._wheelFactor = 0;
			
			if ($$('#'+o.root+' .scrollerTools')) {
				for (var i in this._buttons) {
					$$('#'+o.root+' .scroller'+i).each( this._setButtonsEvents.bind(this,this._axis[i],this._buttons[i]) );
				}
				this.reload();
				Aheuh.Axis.each( this._setBg.bind(this) );
			}
			
		}
		else { alert('Root Scroller Html ID required'); }
	},
	
	_setButtonsEvents: function (axis,move,o) {
		Event.observe(o,'mousedown',this._setInterval.bind(this,axis,move));
		Event.observe(o,'mouseup',this._stop.bind(this));
	},
	
	_scrolling: function (k) {
		var tools = $$('#'+this._root+' .scrollerTools'+k.key.toUpperCase());
		var visibility = '';
		if (this._mask[k.scrollscale]>this._mask[k.offsetscale]) {
			this._cursors['scroll'+k.key] = true;
			visibility = 'visible';
			this._mask[k.scrollpos] = 0;
			if (k.key == 'y'&&!this._cursors['element'+k.key]) {
				Event.observe(this._mask, document.all ?'mousewheel' :'DOMMouseScroll', this._onMouseWheel.bindAsEventListener(this));
			}
		}
		else {
			this._cursors['scroll'+k.key] = false;
			visibility = 'hidden';
		}
		if (tools.length>0) {
			this._tools['element'+k.key] = tools[0];
			this._viewScrollTools(visibility,k.key);
		}
	},
	
	_setCursor: function (k) {
		if (this._cursors['scroll'+k.key]) {
			var o = $$('#'+this._root+' .scrollerCursor'+Aheuh.Axis.pos[k.key]);			
			if (o.length>0) {
				if (!this._cursors['element'+k.key]) {				
					this._cursors['start'+k.key] = o[0][k.offsetpos];
					this._cursors['size'+k.key] = o[0][k.offsetscale];
					this._cursors['element'+k.key] = o[0];
					this._cursors['params'+k.key] = {target:o[0], xmin:0, xmax:0, ymin:0, ymax:0, onMoveFunction:this._onDrag.bind(this,k) };
					this._cursors['params'+k.key][k.min] = o[0][k.offsetpos];
					this._cursors['params'+k.key][k.max] = o[0][k.offsetpos]+o[0][k.offsetscale];
					this._cursors['drag'+k.key] = new Aheuh.Drag(this._cursors['params'+k.key]);
				}			
				this._cursors['fact'+k.key] = this._getFactor(k);
				if (k.key == 'y') {	this._setWheelFactor(); }
				o[0].style[k.scale] = this._cursors['size'+k.key]*this._cursors['fact'+k.key]+'px';
				this._cursors['drag'+k.key].reload(this._cursors['params'+k.key]);				
			}
		}
	},
	
	_getFactor: function (k) {
		return (this._mask[k.offsetscale]-(this._mask[k.offsetscale]-this._cursors['size'+k.key]))/(this._mask[k.scrollscale]-(this._mask[k.offsetscale]-this._cursors['size'+k.key]));
	},
	
	_setWheelFactor: function () {
		this._wheelFactor = this._wheelSpeed - this._cursors['facty'];
	},
	
	_setInterval: function (axis,move) {
		if (!this._interval) {
			this._interval = new PeriodicalExecuter(this._scroll.bind(this,axis,move),this.speedInterval/1000);
		}
		else {
			this._interval.callback = this._scroll.bind(this,axis,move);
			this._interval.registerCallback();
		}
	},
	
	_scroll: function (axis,move) {
		this._mask[Aheuh.Axis[axis].scrollpos] += this.speed*move;
		this._moveCursor(axis);
	},
	
	_moveCursor: function (axis) {
		if (this._cursors['element'+axis]) {
			this._cursors['element'+axis].style[Aheuh.Axis[axis].pos] = this._cursors['start'+axis] + this._mask[Aheuh.Axis[axis].scrollpos] * this._cursors['fact'+axis] + "px";
		}
	},
	
	_onMouseWheel: function (e) {
		e.stop();
		this._scroll('y',Event.Wheel(e)*-1*this._wheelFactor);
	},
	
	_stop: function () {
		this._interval.stop();
	},
	
	_setBg: function (k) {
		if (this._cursors['scroll'+k.key]) {
			var o = $$('#'+this._root+' .scrollerCursorBg'+Aheuh.Axis.pos[k.key]);
			if (o.length>0) {
				Event.observe(o[0], "click", this._onBgClick.bindAsEventListener(this,k));
			}
		}
	},
	
	_onDrag: function (k) {
		this._mask[k.scrollpos] = (this._cursors['element'+k.key][k.offsetpos]-this._cursors['start'+k.key])/this._cursors['fact'+k.key];
	},
	
	_onBgClick: function (e,k) {
		var move = (Event[k.pointer](e)>this._cursors['element'+k.key].cumulativeOffset()[Aheuh.Axis.id[k.key]]+this._cursors['element'+k.key][k.offsetscale]) ?1 :-1;		
		this._scroll(k.key,this._cursors['element'+k.key][k.offsetscale]*move);		
	},
	
	moveTo: function (o) {
		Aheuh.Axis.each(function (k){
			this._mask[k.scrollpos] = $(o)[k.offsetpos];
			this._moveCursor(k.key);
		}.bind(this));
	},
	
	_viewScrollTools: function (visibility,k) {
		this._tools['element'+k].setStyle({ visibility:visibility });
	},
	
	forceScrollTools: function (visibility,k) {
		if (this._cursors['scroll'+k]) {
			this._viewScrollTools(visibility,k);
		}
	},
	
	reload: function () {
		Aheuh.Axis.each( this._scrolling.bind(this) );
		Aheuh.Axis.each( this._setCursor.bind(this) );
	}
	
});Aheuh.ScrollerCursor = Class.create(Aheuh.Scroller, {
	
	initialize: function ($super,o) {
		this._cursorSizeX = o.cursorSizeX || 0;
		this._cursorSizeY = o.cursorSizeY || 0;
		$super(o);
	},
	
	_setCursor: function (k) {
		if (this._cursors['scroll'+k.key]) {
			var o = $$('#'+this._root+' .scrollerCursor'+Aheuh.Axis.pos[k.key]);			
			if (o.length>0) {
				if (!this._cursors['element'+k.key]) {				
					this._cursors['start'+k.key] = o[0][k.offsetpos];
					this._cursors['size'+k.key] = o[0][k.offsetscale];
					this._cursors['element'+k.key] = o[0];
					this._cursors['params'+k.key] = {target:o[0], xmin:0, xmax:0, ymin:0, ymax:0, onMoveFunction:this._onDrag.bind(this,k) };
					this._cursors['params'+k.key][k.min] = o[0][k.offsetpos];
					this._cursors['params'+k.key][k.max] = o[0][k.offsetpos]+o[0][k.offsetscale];
					this._cursors['drag'+k.key] = new Aheuh.Drag(this._cursors['params'+k.key]);
				}				
				o[0].style[k.scale] = this['_cursorSize'+k.key.toUpperCase()]+'px';
				this._cursors['fact'+k.key] = this._getFactor(k);
				if (k.key == 'y') {	this._setWheelFactor(); }
				this._cursors['drag'+k.key].reload(this._cursors['params'+k.key]);				
			}
		}
	},
	
	_getFactor: function (k) {
		return (this._cursors['size'+k.key]-this._cursors['element'+k.key][k.offsetscale])/(this._mask[k.scrollscale]-this._mask[k.offsetscale]);
	}
	
});Aheuh.Axis = {
    initialize: function() {
        this.each( function(k,r){ this.base[k] = this.base[k]||this.keys(k); }.bind(this) );
    },
    base:{x:null,y:null},
    pos:{x:'Left',y:'Top'},
    scale:{x:'Width',y:'Height'},
    id:{x:0,y:1},
    keys: function(key){
         this[key] = this[key] ||
            {
                key:key,          
                min:key+'min',
                max:key+'max',
                axis:key+'axis',
                limit:key+'limit',
                factor:key+'factor',
                pos:this.pos[key].toLowerCase(),
                scale:this.scale[key].toLowerCase(),
                scrollpos:'scroll'+this.pos[key],
                scrollscale:'scroll'+this.scale[key],
                offsetpos:'offset'+this.pos[key],
                offsetscale:'offset'+this.scale[key],
                clientscale:'client'+this.scale[key],
				mouse:key+'mouse',
				pointer:'pointer'+key.toUpperCase()
            };
        return this[key];
    },
    from: function(o)
    {
        return {x:o[0],y:o[1]};
    },
    each: function(iterator,memo)
    {
        for(var key in this.base)
            memo = iterator((this[key]||key),memo);
        return memo;
    }
}
Aheuh.Axis.initialize();Aheuh.Drag = Class.create({
	
	initialize: function (o) {
		this._target = o.target;
		this._mouseOldPos = {};
		this._setLimit(o.xmin,o.xmax,o.ymin,o.ymax);
		this.onMoveFunction = o.onMoveFunction;		
		this.draggable = false;
		this._targetDown = this.setDraggable.bind(this,true);
		this._docMouseUp = this.setDraggable.bind(this,false);
		this._docMouseMove = this._setMove.bindAsEventListener(this);
		Event.observe(this._target,"mousedown",this._targetDown);
		Event.observe(document,"mouseup",this._docMouseUp);
		Event.observe(document,"mousemove",this._docMouseMove);
	},
	
	_setLimit: function (xmin,xmax,ymin,ymax) {
		Aheuh.Axis.each( function (k){
			this[k.min] = eval(k.min); this[k.max] = eval(k.max);
			this['target'+[k.max]] = this[k.max]-this._target[k.offsetscale];
			this._mouseOldPos[k.mouse] = null;
		}.bind(this));
	},
	
	setDraggable: function (draggable) {
		this.draggable = draggable;
		Aheuh.Axis.each( function (k){ this._mouseOldPos[k.mouse] = null; }.bind(this));
	},
	
	_setMove: function (e) {
		Aheuh.Axis.each( this._move.bind(this,e) );
	},
	
	_move: function (e,k) {
		if (this.draggable) {			
			var mouse = Event['pointer'+k.key.toUpperCase()](e);
			if (this._mouseOldPos[k.mouse]) {	
				if (this[k.max]>this[k.min]) {
					var n = this._target[k.offsetpos]-(this._mouseOldPos[k.mouse]-mouse);					
					if (n<this[k.min]) { n = this[k.min]; }
					else if (n+this._target[k.offsetscale]>this[k.max]) { n = this['target'+[k.max]]; }
					this._target.style[k.pos] = n+"px";
					if (this.onMoveFunction) { this.onMoveFunction(); }
				}
			}
			Event.SelectionClear();
			this._mouseOldPos[k.mouse] = mouse;
		}
		e.stop();
	},
	
	reload: function (o) {
		this._setLimit(o.xmin,o.xmax,o.ymin,o.ymax);
	},
	
	kill: function () {
		Event.stopObserving(this._target,"mousedown",this._targetDown);
		Event.stopObserving(document,"mouseup",this._docMouseUp);
		Event.stopObserving(document,"mousemove",this._docMouseMove);
	}

});Object.extend(Event, {
	Wheel: function (event){
		var delta = 0;
		if (!event) event = window.event;
		if (event.wheelDelta) {
			delta = event.wheelDelta/120; 
			if (window.opera) delta = -delta;
		} else if (event.detail) { delta = -event.detail/3;	}
		return Math.round(delta);
	}
});

Object.extend(Event, {
	SelectionClear: function () {
		if (document.execCommand&&navigator.userAgent.indexOf("Firefox")==-1) { document.execCommand("Unselect"); }
		else if (window.getSelection&&window.getSelection().removeAllRanges) {			
			window.getSelection().removeAllRanges();
			window.getSelection().addRange(document.createRange());
		}
	}
});

Object.extend(Array, {
    sortOnNumber: function (a) { return a.sort( function(n1,n2) { return (n1-n2) } ) }
});
