
	MTAnimatedContentChanger = new Class({
		Implements: [Options, Events, Chain],

		options: {
			animationsDuration: 400,
			animationsTransition: Fx.Transitions.Quad.easeOut,
			
			// events
			onChange: $empty, // when old content is hidden and removed and new inserted but not shown yet
			onChangeStart: $empty,
			onChangeComplete: $empty
		},

		element: null,
		
		initialize: function (element, options) {
			// set elements
			this.element = $(element);

			// options
			this.setOptions(options);
		},
		
		change: function (newContent) {
			// check if another is already running
			//  cancel queue and continue with the new ?
			if (this.isRunning()) {
				return;
			}
			
			// chain stages
			var oThis = this;
			this.chain(this.hide);
			this.chain(function(){
				oThis.switchContent(newContent);
			});
			this.chain(this.resize);
			this.chain(this.show);
			this.chain(this.complete);
			
			// start
			this.fireEvent('changeStart', newContent);
			this.callChain();
			
			// retyrb self element for the chain
			return this;
		},
		
		hide: function () {
			new Fx.Tween(this.element, {
				duration: this.options.animationsDuration,
				transition: this.options.animationsTransition,
				property: 'opacity',
			    onComplete: this.callChain.bind(this)
			}).start(0);
		},	
			
		switchContent: function (newContent) {
			// compute cuttent/old hight
			var oldHeight = this.getHeight();
			
			// freeze
			//this.element.setStyles({'height': oldComputedHeight, 'overflow': 'hidden'});
			this.element.setStyle('overflow', 'hidden');
			this.element.setStyle('height', oldHeight);
			this.element.store('oldHeight', oldHeight);
			
			// switch the content
			if ($type(newContent) == 'string') {
				this.element.empty();
				this.element.set('html', newContent);
			}
			else if ($type(newContent) == 'function') {
				var res = newContent.run([], this);
				if (res === false) return;
			}
			else {
				this.element.empty();
				$$(newContent).inject(this.element);
			}
	
			//  fire the chaned event (important)
			this.fireEvent('change', newContent);
			
			this.callChain();
					
			// calc new height
			//var newHeight = this.getHeight();
			
			//this.resize(oldHeight, newHeight);
			
		},
		
		// animated resize 
		resize: function () {
			var newHeight = this.getHeight();
			var oldHeight = this.element.retrieve('oldHeight');
			
			new Fx.Tween(this.element, {
				// calc duration base on dif
				duration: Math.min(Math.abs(oldHeight - newHeight) * (this.options.animationsDuration/100), this.options.animationsDuration * 2),
				onComplete: function () {
					// restore styles
					// this.element.setStyles(oldStyles)
					this.element.setStyles({'height': 'auto', 'overflow': 'auto'});
					this.element.eliminate('oldHeight'); 

					// do next
					this.callChain();
				}.bind(this)
			}).start('height', newHeight);
		},
		
		/**
		* Show page elements
		* @param page page if none-current 
		*/
		show: function () {
			// fade-in & complete
			new Fx.Tween(this.element, {
				property: 'opacity',
				duration: this.options.animationsDuration,
				transition: this.options.animationsTransition,
			    onComplete: function () {
			    	this.callChain();
				}.bind(this)
			}).start(1);
		},
		
		complete: function () {
			this.fireEvent('changeComplete');
			this.callChain();
		},
		
		/**
		 * Get content height
		 * if element has no border and contains children with margin it's not included in the height calculation, so it's not correct
		 * you may fix by style="border:1px transparent; padding -1px" 
		 */
		getHeight: function () {
			//  freeze doc height to avoid glitches
			$body = $(document.body);
			var oldBodyStyles = $body.getStyles('overflow', 'height');
			var oldStyles = this.element.getStyles('overflow', 'height');
			$body.setStyles({overflow: 'hidden', height: $body.getScrollSize().y});
			this.element.setStyles({overflow: 'auto', height: 'auto'});
			var size = this.element.getSize();
			this.element.setStyles(oldStyles);
			$body.setStyles(oldBodyStyles);
			return size.y;
		},
		
		isRunning: function () {
			return this.$chain.length? true : false;
		}
	}); 
	
	// static
	MTAnimatedContentChanger.extend({
		Change: function (element, newContent, options) {
			var changer = new MTAnimatedContentChanger(element, options);
			changer.change(newContent);
			return changer;
		}
	});
	
