	Fx.Tween.implement({
		 initialize: function(element, options){
			this.element = this.subject = ($type(element) == 'array')? $$(element) : document.id(element);
			this.parent(options);
		}
	});
	Fx.Morph.implement({
		 initialize: function(element, options){
			this.element = this.subject = ($type(element) == 'array')? $$(element) : document.id(element);
			this.parent(options);
		}
	});

	/// ELEMENTS PAGINATOR ///
		
	MTPager = new Class({
		Implements: [Options, Events, Chain],

		options: {
			mode: 'switch',		// 'switch' (show/hides) or 'scroll' (scrolls to element)
			perPage: 10,
			maxLinks: 10,
			autoBuild: true,
			autoHide: true,
			autoHidePrevNext: true,
			animateSizeSwitch: true,
			animationsDuration: 400,
			animationsTransition: Fx.Transitions.Quad.easeOut,
			
			sep: '|',
			prev: false, //'&laquo;',
			next: false, //'&raquo;',
			fw: '&raquo;',
			bw: '&laquo;',
			sepClass: 'sep',
			prevClass: 'prev',
			nextClass: 'next',
			numClass: 'num',
			fwClass: 'fw',
			bwClass: 'bw',

			// events
			onLoad: $empty,
			onChange: $empty,
			onChangeStart: $empty,
			onChangeComplete: $empty
		},

		element: null,
		elements: {
			pages: [],
			prev: null,
			next: null,
			fw: null,
			bw:null
		},
		slidePosition: 1,
		contentElement: null,
		contentElements: null,
		
		changeHandlerCallback: null,
		
		initialize: function (data, pagerElement, changeHandlerCallback, options) {
			// set the data
			this.data = $A(data);
			
			// set elements
			this.element = $(pagerElement); 
			
			// set the callback handler f
			this.changeHandlerCallback = changeHandlerCallback;

			// options
			this.setOptions(options);
			
			// build it
			if (this.options.autoBuild) {
				this.build(this.options);
			}
		},

		_currentPage: null,

		
		rebuild: function (data, options) {
			this.data = $A(data);
			this.build(options);
		},
		
		destroy: function () {
			this.element.empty();
		},
		
		/**
		* Build the pager with specific options
		* options are set here because they take place on build
		*/
		build: function (options) {
			//this.elements = $H({});
			
			// new options ?
			if (options) {
				this.setOptions(options);
			}
			
			// total pages
			this._pagesCount = Math.ceil(this.data.length / this.options.perPage);

			// clean if old
			this.destroy();
			
			// prev
			if (this.options.prev) {
				this.elements.prev = new Element('a', {
					'html': this.options.prev,
					'href': 'javascript: void(0)',
					'class': this.options.prevClass,
					'events': {
						'click': this.gotoPage.bind(this, '-1')
					}
				});
				this.element.grab(this.elements.prev);
			}
			// nums
			for (var pageNum = 1; pageNum <= this._pagesCount; pageNum++) {
				// create
				this.elements.pages[pageNum] = new Element('a', {
					'html': pageNum,
					'href': 'javascript: void(0)',
					'class': this.options.numClass,
					'events': {
						'click': this.gotoPage.bind(this, pageNum)
					}
				});
				// separator
				if (pageNum != 1) {
					this.element.grab(new Element('span', {
						'html': this.options.sep,
						'class': this.options.sepClass
					}));
				}
				// inject
				this.element.grab(this.elements.pages[pageNum]);
			}
			// next
			if (this.options.next) {
				this.elements.next = new Element('a', {
					'html': this.options.next,
					'href': 'javascript: void(0)',
					'class': this.options.nextClass,
					'events': {
						'click': this.gotoPage.bind(this, '+1')
					}
				});
				this.element.grab(this.elements.next);
			}
			
			// FW/BW
			if (this.getPagesCount() > this.options.maxLinks) {
				// forward
				if (this.options.fw) {
					this.elements.fw = new Element('a', {
						'html': this.options.fw,
						'href': 'javascript: void(0)',
						'class': this.options.fwClass,
						'events': {
							'click': this.slide.bind(this, '+1')
						}
					});
					this.element.grab(this.elements.fw);
				}
				// backward
				if (this.options.bw) {
					this.elements.bw = new Element('a', {
						'html': this.options.bw,
						'href': 'javascript: void(0)',
						'class': this.options.bwClass,
						'events': {
							'click': this.slide.bind(this, '-1')
						}
					});
					this.element.grab(this.elements.bw, 'top');
				}
				
				this.slide(this.slidePosition);
				
			}
			
			// not needed - hide
			if (this._pagesCount <= 1) {
				if (this.options.autoHide) {
					this.element.setStyle('display', 'none');
				}
				return;
			}
			// show
			else {
				// show in case hidden
				this.element.setStyle('display', '');
			}
			// loaded
			this.fireEvent('load');
		},
		
		slide: function (slidePosition) {
			// not needed
			if (this.getPagesCount() <= this.options.maxLinks) {
				this.hide(this.elements.fw);
				this.hide(this.elements.bw);
				return;
			}
			
			// calc slide position
			if ($type(slidePosition) == 'string' && slidePosition.test(/^[-+]\d+$/)) {
				slidePosition = this.slidePosition + slidePosition.toInt();
			} 
			if (slidePosition < 1) {
				slidePosition = 1;
			}
			this.slidePosition = slidePosition;

			// current slide range
			var first = this.slidePosition;
			var last = this.slidePosition + this.options.maxLinks;
			if (last > this.getPagesCount()) {
				last = this.getPagesCount();
			}
			
			
			// show/hide pages
			this.show(this.elements.pages.slice(first, last), this.elements.pages[1].getAllNext('[class~=' + this.options.sepClass + ']'));
			
			this.hide(this.elements.pages[first].getAllPrevious('[class~=' + this.options.sepClass + ']'), this.elements.pages[first].getAllPrevious('[class~=' + this.options.numClass + ']'), //this.elements.pages.slice(1, first),
			//this.elements.pages.slice(last),
			this.elements.pages[last - 1].getAllNext('[class~=' + this.options.numClass + ']'), this.elements.pages[last - 1].getAllNext('[class~=' + this.options.sepClass + ']'));
			
			// show/hide slide btns
			if (this.slidePosition + this.options.maxLinks > this.getPagesCount()) {
				this.hide(this.elements.fw);
			}
			else {
				this.show(this.elements.fw);
			}
			if (this.slidePosition == 1) {
				this.hide(this.elements.bw);
			}
			else {
				this.show(this.elements.bw);
			}
			
		},
		
		hide: function () {
			$$($A(arguments).flatten().clean()).setStyle('display', 'none');
		},
		
		show: function () {
			$$($A(arguments).flatten().clean()).setStyle('display', '');
		},

		getCurrentPage: function () {
			return this._currentPage;
		},
		getPagesCount: function () {
			return this._pagesCount;
		},

		
		
		gotoPage: function (numOrElement, onComplete, onCompleteIfNone) {
			// check if another is not already running
			if (this.$chain.length){ 
				return;
				//this.clearChain();
			}

			// get the proper page num 
			var page = this.findPageNum(numOrElement);

			// check page
			if (page > this._pagesCount) {
				page = this._pagesCount;
			} 
			else if (page < 1) {
				page = 1;
			}
			if (! page || this._currentPage == page) {
				if (onCompleteIfNone) {
					onComplete.run();
				}
				return false;
			}
			
			this.chain(
					function () { if (this.changeHandlerCallback.run([page, this.getPageElements(page), this], this)) this.callChain(); },
					function () { this.setPage(page); },
					onComplete
			);
			this.callChain();
			return true;
		},
		
		callChangeHandler: function (page) {
			this.changeHandlerCallback.run([page, this.getPageElements(page), this], this);
		},
		
		setPage: function (page) 
		{
			// proper page
			page = this.findPageNum(page);
			
			// save the page num
			this._currentPage = page;

			// update class states
			this.elements.pages.each( function (el, key) {
				if (! el) return;
				if (key == page) {
					el.addClass('active');
				} else {
					el.removeClass('active');
				}
			}, this);

			// hide perv /next
			if (this.options.autoHidePrevNext) {
				this.elements.prev && this.elements.prev.setStyle('visibility', (page == 1)? 'hidden' : 'visible');
				this.elements.next && this.elements.next.setStyle('visibility', (page == this._pagesCount)? 'hidden' : 'visible');
			}
			
			// center pager slider
			this.slide(this._currentPage - Math.floor(this.options.maxLinks/2));
			
			// finish chain
			this.callChain();
		},
		

		getPageElements: function (page, getOtherElements) {
			if (page) {
				page = this.findPageNum(page);
			} else if (this._currentPage) {
				page = this._currentPage;
			} else {
				return false;
			}

			var range = this.getPageRange(page);
			if (! getOtherElements) { 
				return this.data.slice(range.first, range.last + 1);
			} else {
				var copy = $A(this.data);
				copy.splice(range.first, this.options.perPage);
				return copy;
			}
		},
		
		getPageRange: function (page, countFromOne) {
			return {
				first: (page-1) * this.options.perPage + (countFromOne? 1 : 0),
				last: page * this.options.perPage - (countFromOne? 0 : 1)
			}
		},
		
		findPageNum: function (numOrElement, isElement) {
			// no param - current page
			if (! numOrElement) {
				return this._currentPage; 
			}
			// number
			if ($type(numOrElement) == 'number' && !isElement) {
				return numOrElement;
			}
			// +/- number
			if ($type(numOrElement) == 'string' && numOrElement.test(/^[-+]\d+$/) && !isElement) {
				return  this._currentPage + numOrElement.toInt();
			}
			// element
			else  {
				var pos;
				this.data.each(function (el, i) {
					if (el == numOrElement) {
						pos = i + 1;
					}
				});
				if (pos) {
					return Math.ceil(pos / this.options.perPage);
				}
			}
			// else - error 
			throw "[" + numOrElement + "] is not a valid page"
		}
	}); 

