Element.implement({
	getScrollMax: function(x,y,offset) {
		if (!offset) var offset = {'x':0, 'y':0};
		var offsetSize = this.getSize(), scrollSize = this.getScrollSize();
		var scroll = this.getScroll(), values = {'x': x, 'y': y};
		for (var z in values){
			var max = scrollSize[z] - offsetSize[z];
			if ($chk(values[z])) {
				values[z] = ($type(values[z]) == 'number') ? values[z].limit(0, max) : max;
			} else {
				values[z] = scroll[z];
			}
			values[z] += offset[z];
		}
		return {now:{'x':scroll.x, 'y':scroll.y}, max:{'x':values.x, 'y':values.y}};
	},
	
	isScrollMaxY: function() {
		var scroll = this.getScrollMax(false,'bottom');
		if (scroll.now.y == scroll.max.y) return true;
	},
	
	isScrollMaxX: function() {
		var scroll = this.getScrollMax('right',false);
		if (scroll.now.x == scroll.max.x) return true;
	}
});


Fx.GridScroll = new Class({
	
	Extends: Fx,
	
	options: {
		offset: {'x': 0, 'y': 0},
		wheelStops: true,
		activateElement: '',
		mode: 'diagonal'
	},
 
	position: {'x':0, 'y':0},
		
	initialize: function(element, options) {
		this.element = this.subject = $(element);
		this.parent(options);
		var cancel = this.cancel.bind(this, false);

		if ($type(this.element) != 'element') this.element = $(this.element.getDocument().body);

		var stopper = this.element;

		if (this.options.wheelStops){
			this.addEvent('start', function(){
				stopper.addEvent('mousewheel', cancel);
			}, true);
			this.addEvent('complete', function(){
				stopper.removeEvent('mousewheel', cancel);
			}, true);
		}
		
		this.setList(this.element, true);
		this.list.each(function(element){
			this.setPosition(element);
		},this);
		if (this.options.activateElement) this.toElement(this.options.activateElement);
		$(element).addEvent('scroll', function() {
			this.position = this.element.getScroll();
		}.bind(this));
		window.addEvent('resize', function() {
			this.list.each(function(element){
				this.setPosition(element);
			},this);
		}.bind(this));
	},
 
	setList: function(element, deleteList){
		if (deleteList) this.list = [];
		if($type(element) == 'element') {
			var el = element.getChildren();
			el.each(function(item) {
				this.list.push(item);
				this.setList($(item), false);
			},this);
		}
		return this;
	},
 
	setPosition: function(element) {
		$(element).store('FxScroll:position', $(element).getPosition(this.element));
		return this;
	},
	
	set: function(){
		var now = Array.flatten(arguments);
		this.element.scrollTo(now[0], now[1]);
	},

	compute: function(from, to, delta){
		var now = [];
		var x = 2;
		x.times(function(i){
			now.push(Fx.compute(from[i], to[i], delta));
		});
		return now;
	},

	start: function(x, y){
		if (!this.check(arguments.callee, x, y)) return this;
		var pos = this.element.getScrollMax(x,y,this.options.offset);
		var scroll = pos.now, values = pos.max;
		return this.parent([scroll.x, scroll.y], [values.x, values.y]);
	},
 
	getPosition: function(el) {
		return $(el).retrieve('FxScroll:position');
	},

	toTop: function(){
		return this.start(false, 0);
	},

	toLeft: function(){
		return this.start(0, false);
	},

	toRight: function(){
		return this.start('right', false);
	},

	toBottom: function(){
		return this.start(false, 'bottom');
	},
	
	toElement: function(el) {
		var position = this.getPosition(el);
		return this[this.options.mode](position);
	},
	
	diagonal: function(position) {
		return this.start(position.x, position.y);
	},
	
	horizontal: function(position) {
		this.chain(
  			function() { (position.x != this.position.x) ? this.start(position.x, false) : this.callChain(); },
  			function() { this.start(false, position.y); }
		);
		return this.callChain();
	},
 
	vertical: function(position) {
		this.chain(
  			function() { (position.y != this.position.y) ? this.start(false, position.y) : this.callChain(); },
			function() { this.start(position.x, false); }
		);
		return this.callChain();
	}
});
