var FX = {
	go:function( el, attr, to, opts ){
		FX.stop( el, attr );
		opts = FX.options.apply( FX, arguments );
		el['fx-'+attr] = setInterval( animate, opts.intv );
		animate();

		function animate(){
			if(( to - opts.from ) * opts.dir <= 0 ){
				opts.from = to;
				FX.stop(el, attr);
				opts.after.call( el, attr, to );
			}
			FX.set( el, attr, opts.from, opts.ext );
			opts.from += opts.step;
		};
	},
	stop:function( el, attr ){
		clearInterval( el['fx-'+attr] );
	},
	set:function( el, attr, value, ext ){
		el.style[attr] = value + ext;
	},
	get:function( el, attr ){
		return parseFloat(el.style[attr]) || 0;
	},
	ext:function( attr ){
		switch( attr ){
			case 'top': case 'left': return 'px';
			case 'opacity':	default: return 0;
		}
	},
	options:function( el, attr, to, opts ){
		opts = opts || { };
		opts.from = opts.from || FX.get( el, attr );
		opts.ext = opts.ext || FX.ext( attr );
		opts.dir = opts.from <= to ? 1 : -1;
		opts.step = Math.abs(opts.step||1) * opts.dir;
		opts.intv = opts.intv || 100;
		opts.after = opts.after || function(){};
		return opts;
	}
};

var Opacity = {
	go:function( el, to, opts ){
		var from = parseFloat(el.style.opacity);
		from = isNaN(from) ? 1 : from;
		opts = DOM.extend({
			step:0.2,
			intv:100,
			from:from
		},opts);
		FX.go( el, 'opacity', to, opts );
	},
	show:function( el ){
		return Opacity.go( el, 1 );
	},
	hide:function( el ){
		return Opacity.go( el, 0 );
	}
};