Ext.namespace('Ext.ux');

Ext.ux.Carousel = function(cfg) {
	if(cfg.orientation) cfg.orientation = cfg.orientation.toLowerCase();
	if(cfg.renderTo){
		cfg.carouselEl = Ext.get(cfg.renderTo);
		delete cfg.renderTo;
	}
	this.addEvents(
		'animationCompleteHandler',
		'loadInitHandler',
		'loadNextHandler',
		'loadPrevHandler',
		'prevButtonStateHandler',
		'nextButtonStateHandler'
	);
	Ext.apply(this, cfg);
	Ext.ux.Carousel.superclass.constructor.call(this);
	this.init();
}

Ext.extend(Ext.ux.Carousel, Ext.util.Observable, {
	UNBOUNDED_SIZE: 1000000, 
	carouselEl: Ext.getBody(), 
	size: 0,
	scrollBeforeAmount: 0, 
	scrollAfterAmount: 0, 
	loadOnStart: true, 
	orientation: 'horizontal', //vertical
	numVisible: 3, 
	firstVisible: 1, 
	scrollInc: 3, 
	animationSpeed: 0.25, 
	animationMethod: 'easeOut', 
	autoPlay: 0, 
	wrap: false, 
	navMargin: 0, 
	revealAmount: 0, 
	prevElement: null, 
	nextElement: null, 
	
	moving: false, 
	allItem: new Array(),
	dataDiv: new Array(), 
	
	//private------------------------------------------------------------------------------>
	init: function(){
		this.allItem = Ext.query('li', this.carouselEl.dom);
		this.size = (this.size) ? this.size : this.allItem.length;
		var indexNum = this.size > this.numVisible ? this.size-this.numVisible : 0;
		this.firstVisible = this.firstVisible.constrain(1, indexNum+1);
		
		this._origFirstVisible = this.firstVisible;
 		this._prevEnabled = true;
 		this._nextEnabled = true;
		this._autoPlayTimer = null;
		this._priorLastVisible = this._priorFirstVisible = this.firstVisible;
		this._lastPrebuiltIdx = 0;
		
		this.carouselEl.addClass('carousel-component');
		this.nextElement = Ext.get(this.nextElement);
		this.prevElement = Ext.get(this.prevElement);
		if(this.nextElement){
			this.nextElement.on('click', this.scrollNext, this);
			this.prevElement.on('click', this.scrollPrev, this);
		}
		
		if(this.carouselEl.dom.innerHTML == '' && dataDiv.length > 0){ 
			return;
		}else{
			this.carouselList = (this.allItem[0]) ? Ext.get(this.allItem[0]).parent() : null;
			if(!this.carouselList) return;
			this.carouselList.addClass('carousel-list');
			this._clipReg = this.carouselList.parent();	
			this._clipReg.addClass('carousel-clip-region');
		}
		//if(this.isVertical()) this.carouselList.addClass('carousel-vertical');
	}, 
	
	isVertical: function(){
		return (this.orientation == 'vertical');
	},
	
	getLastVisible: function(){
		var last = this.firstVisible+this.numVisible-1;
		last = this.size < last ? this.size : last;
		return last;
	},
	
	_isExtraRevealed: function(){
		return (this.revealAmount > 0);
	},
	
	_calculateAllowableScrollExtent: function(){
		var extent = {start: 1-this.scrollBeforeAmount, end: this.size+this.scrollAfterAmount};
		return extent;
	},
	
	_calculateVisibleExtent: function(start, end){
		if(!start){
			start = this.firstVisible;
			end = this.getLastVisible();
		}
		start = start<1 ? 1 : start;
		end = end>this.size ? this.size : end;
		var extent = {start: start, end: end};
		
		this._firstItemRevealed = -1;
		this._lastItemRevealed = -1;
		if(this._isExtraRevealed()) {
			if(start > 1){
				this._firstItemRevealed = start - 1;
				extent.start = this._firstItemRevealed;
			}
			if(end < this.size){
				this._lastItemRevealed = end + 1;
				extent.end = this._lastItemRevealed;
			}
		}
		return extent;
	},
	
	_areAllItemsLoaded: function(first, last){
		var itemsLoaded = true;
		for(var i=first; i<=last; i++) {
			var liElem = this.getItem(i);
			if(!this._isValidObj(liElem)) {
				this._prebuildLiElem(i);
				itemsLoaded = false;
			} else if(this._isValidObj(liElem.placeholder)) {
				itemsLoaded = false;
			}
		}
		return itemsLoaded;
	},
	
	_disablePrev: function(){
		this._prevEnabled = false;
		if(this.nextElement){
			this.prevElement.un('click', this.scrollPrev);
			this.fireEvent('prevButtonStateHandler', this.prevElement, this._prevEnabled);
		}
	},
	
	_enablePrev: function(){
		this._prevEnabled = true;
		if(this.nextElement){
			this.prevElement.un('click', this.scrollPrev);
			this.prevElement.on('click', this.scrollPrev, this);
			this.fireEvent('prevButtonStateHandler', this.prevElement, this._prevEnabled);
		}
	},
	
	_disableNext: function(){
		this._nextEnabled = false;
		if(this.nextElement){
			this.nextElement.un('click', this.scrollNext);
			this.fireEvent('nextButtonStateHandler', this.nextElement, this._nextEnabled);
		}
	},
	
	_enableNext: function(){
		this._nextEnabled = true;
		if(this.nextElement){
			this.nextElement.un('click', this.scrollNext);
			this.nextElement.on('click', this.scrollNext, this);
			this.fireEvent('nextButtonStateHandler', this.nextElement, this._nextEnabled);
		}
	},
	
	_enableDisableControls: function(){
		var firstVisible = this.firstVisible;
		var lastVisible = this.getLastVisible();
		var scrollExtent = this._calculateAllowableScrollExtent();
		if(this._prevEnabled) {
			if(firstVisible === scrollExtent.start) {
				this._disablePrev();
			}
		}
		
		if(this._prevEnabled === false) {
			if(firstVisible > scrollExtent.start) {
				this._enablePrev();
			}
		}
		if(this._nextEnabled) {
			if(lastVisible === scrollExtent.end) {
				this._disableNext();
			}
		}
		
		if(this._nextEnabled === false) {
			if(lastVisible < scrollExtent.end) {
				this._enableNext();
			}
		}	
	},
	
	reload: function(){
		alert('reload');
	},
	
	_position: function(newStart, showAnimation){
		var currStart = this._priorFirstVisible;
		if(newStart > currStart) {
			var inc = newStart - currStart;
			this._scrollNextInc(inc, showAnimation);
		} else {
			var dec = currStart - newStart;
			this._scrollPrevInc(dec, showAnimation);
		}
	},
	
	_scrollPrevInc: function(dec, showAnimation){
		var currStart = this._priorFirstVisible;
		var currEnd = this._priorLastVisible;
		var newStart = currStart - dec;	
		var scrollExtent = this._calculateAllowableScrollExtent();
		newStart = (newStart < scrollExtent.start) ? scrollExtent.start : newStart;
		var newEnd = newStart + this.numVisible - 1;
		if(newEnd > scrollExtent.end) {
			newEnd = scrollExtent.end;
			newStart = newEnd - this.numVisible + 1;
		}
		
		dec = currStart - newStart;
		this.firstVisible = newStart;
		this._priorFirstVisible = newStart;
		this._priorLastVisible = newEnd;	
		
		if(dec > 0){
			if(this.moving) return;
			this.moving = true;
			this.fireEvent('loadPrevHandler', this);
			/*
			if(this._isValidObj(this.cfg.getProperty("loadPrevHandler"))) {	
				var visibleExtent = this._calculateVisibleExtent(newStart, newEnd);
				var cacheEnd = (currStart-1) > visibleExtent.end ? (currStart-1) : visibleExtent.end;						
				var alreadyCached = this._areAllItemsLoaded(visibleExtent.start, cacheEnd);
				
				this._loadPrevHandlerEvt.fire(visibleExtent.start, visibleExtent.end, alreadyCached);
			}
			*/
			
			var way = this.isVertical() ? 'b' : 'r';
			this.moveScroll(way, this.scrollAmountPerInc*dec, showAnimation);
		}

		this._enableDisableControls();
		return false;
	},
	
	_scrollNextInc: function(inc, showAnimation){
		var currStart = this._priorFirstVisible;
		var currEnd = this._priorLastVisible;
		var newStart = currStart + inc;	
		var newEnd = newStart + this.numVisible - 1;
		
		var scrollExtent = this._calculateAllowableScrollExtent();
		if(newEnd > scrollExtent.end) {
			newEnd = scrollExtent.end;
			newStart = newEnd - this.numVisible + 1;
		}
		inc = newStart - currStart;
		/*
		if(this.cfg.getProperty("wrap") && currEnd == scrollExtent.end) {
			this.scrollTo(scrollExtent.start); // might need to check animation is on or not
			return;
		}
		*/
		if(inc > 0){
			if(this.moving) return;
			this.moving = true;
			this.fireEvent('loadNextHandler', this);
			/*
			if(this._isValidObj(this.cfg.getProperty("loadNextHandler"))) {
				var visibleExtent = this._calculateVisibleExtent(newStart, newEnd);
				var cacheStart = (currEnd+1) < visibleExtent.start ? (currEnd+1) : visibleExtent.start;						
				var alreadyCached = this._areAllItemsLoaded(cacheStart, visibleExtent.end);
				this._loadNextHandlerEvt.fire(visibleExtent.start, visibleExtent.end, alreadyCached);
			}
			*/
			
			var way = this.isVertical() ? 't' : 'l';
			this.moveScroll(way, this.scrollAmountPerInc*inc, showAnimation);
		}else{
			if(this.autoPlay > 0){
				this.moving = false;
				this.moveTo(1);
				newStart = 1;
				newEnd = newStart + this.numVisible - 1;
			}
		}

		this.firstVisible = newStart;
		this._priorFirstVisible = newStart;
		this._priorLastVisible = newEnd;
		this._enableDisableControls();
		return false;
	},
	
	moveScroll: function(way, range, isAnimate){
		if(isAnimate){
			this.carouselList.move(way, range, {
				easing: this.animationMethod, 
				duration: this.animationSpeed,
				callback: function(){
					this.moving = false;
					this.fireEvent('animationCompleteHandler', this, this._priorFirstVisible);
				}, 
				scope: this
			});
		}else{
			this.carouselList.move(way, range);
			this.moving = false;
			this.fireEvent('animationCompleteHandler', this, this._priorFirstVisible);
		}
	}, 
	
	toDoAction: function(){
		this.carouselEl.show();
		this.calculateSize();
		this.fireEvent('loadInitHandler', oThis)
		if(this.autoPlay > 0){
			this.startAutoPlay();
		}else{
			this.stopAutoPlay();
		}
	},
	
	onRender: function(){
		oThis = this;
		var firstVisible = this.firstVisible;
		this._priorLastVisible = this.getLastVisible();
		
		/*
		if(this.autoPlay !== 0) {
			this._autoPlayTimer = this.startAutoPlay();
		}	
		*/
		this._enableDisableControls();
		
		var readImg = function(){
			var img = new Image();
			img.onload = function(){
				oThis.firstContentIndex++;
				if(oThis.firstLiContent[oThis.firstContentIndex]){
					readImg();
				}else{
					oThis.toDoAction();
				}
			}
			img.src = oThis.firstLiContent[oThis.firstContentIndex].src;
		}
		var mainBox = this.allItem[this.firstVisible-1];
		this.firstLiContent = Ext.get(mainBox).query('img[src]');
		if(this.firstLiContent.length > 0){
			this.firstContentIndex = 0;
			readImg();
		}else{
			this.toDoAction();
		}
	}, 
	
	getNewItem: function(where, el, html){
		return Ext.DomHelper[where](el, {
			'tag': 'li', 
			'html': html
		});
	},

	_scrollPrev: function(e) {
		//if(e !== null) this.stopAutoPlay();
		/*
		if(carousel._scrollPrevAnim.isAnimated()) {
			return false;
		}
		*/
		this._scrollPrevInc(this.scrollInc, (this.animationSpeed !== 0));
	},
	
	_scrollNext: function(e){
		//if(e !== null) this.stopAutoPlay();
		/*
		if(carousel._scrollNextAnim.isAnimated()) {
			return false; // might be better to set ourself waiting for animation completion and
			// then just do this function. that will allow faster scroll responses.
		}
		*/
		this._scrollNextInc(this.scrollInc, (this.animationSpeed !== 0));
	}, 
	
	//public------------------------------------------------------------------------------>
	setOrientation: function(str){
		if(this.orientation == str) return;
		this.orientation = str.toLowerCase();
		this.reload();
	}, 
	
	setSize: function(v){
		if(this.size == v) return;
		v = parseInt(v, 10);
		this.size = isNaN(v) ? 0 : v;
		this.reload();
	}, 
	
	setNumVisible: function(v){
		if(this.numVisible == v) return;
		v = parseInt(v, 10);
		this.numVisible = isNaN(v) ? 0 : v;
		this.reload();
	}, 
	
	setScrollInc: function(v){
		this.scrollInc = (v > this.numVisible) ? this.numVisible : v;
	}, 
	
	setSpeed: function(str){
		this.animationSpeed = str;
	}, 
	
	setMethod: function(str){
		this.animationMethod = str;
	}, 
	
	setNavMargin: function(str){
		this.navMargin = str;
		this.calculateSize();
	}, 
	
	setRevealAmount: function(str){
		this.revealAmount = str;
		this.reload();
	},
	
	moveTo: function(num, animate){
		var animate = (animate) ? animate : true;
		num = num.constrain(1, this.size);
		this._position(num, animate);
	}, 
	
	addItem: function(html){
		this.insertAfter(this.size, html);
	},
	
	insertAfter: function(inx, html){
		where = 'insertAfter';
		inx = (inx-1).constrain(0, this.size-1);
		var newItem = this.getNewItem(where, Ext.get(this.allItem[inx]), html);
		for(var i=0;i<this.size;i++){
			if(i == inx+1) newArr.push(newItem);
			newArr.push(this.allItem[i]);
		}
		this.size = newArr.length;
		this.allItem = newArr;
		
		this._enableDisableControls();
		return newItem;
	}, 
	
	startAutoPlay: function(num){
		if(num) this.autoPlay = num;
		if(!this._autoPlayTimer){
			var oThis = this;  
			this._autoPlayTimer = setTimeout(function(){
				oThis.scrollNext();
			}, this.autoPlay);
		}
		return this._autoPlayTimer;
	},
	
	stopAutoPlay: function(){
		if (this._autoPlayTimer !== null) {
			clearTimeout(this._autoPlayTimer);
			this._autoPlayTimer = null;
		}
	},
	
	getItem: function(idx) {
		return this.allItem[idx];
	},
	
	scrollPrev: function() {
		this._scrollPrev();
	},
	
	scrollNext: function(){
		this._scrollNext();
		
		this.stopAutoPlay();
		if(this.autoPlay !== 0) {
			this._autoPlayTimer = this.startAutoPlay();
		}
	},
	
	insertBefore: function(inx, html){
		where = 'insertBefore';
		inx = (inx-1).constrain(0, this.size-1);
		var newItem = this.getNewItem(where, Ext.get(this.allItem[inx]), html);
		for(var i=0;i<this.size;i++){
			if(i == inx) newArr.push(newItem);
			newArr.push(this.allItem[i]);
		}
		this.size = newArr.length;
		this.allItem = newArr;
		
		this._enableDisableControls();
		return newItem;
	}, 
	
	calculateSize: function(){
		var li = Ext.get(this.allItem[0]); 
		this.carouselList.removeClass('carousel-horizontal');
		this.carouselList.removeClass('carousel-vertical');
		
		var pl = li.getPadding('l');
		var pr = li.getPadding('r');
		var ml = li.getMargins('l');
		var mr = li.getMargins('r');
		var pt = li.getPadding('t');
		var pb = li.getPadding('b');
		var mt = li.getMargins('t');
		var mb = li.getMargins('b');
		
		if(this.isVertical()){
			this.carouselList.addClass('carousel-vertical');
			var liPaddingMarginWidth = pl + pr + ml + mr;
			var liPaddingMarginHeight = pt + pb + mt + mb;
			
			var upt = this.carouselList.getPadding('t');
			var upb = this.carouselList.getPadding('b');
			var umt = this.carouselList.getMargins('t');
			var umb = this.carouselList.getMargins('b');
			var ulPaddingHeight = upt + upb + umt + umb;
			
			var revealAmt = (this._isExtraRevealed()) ? (this.revealAmount+liPaddingMarginHeight/2) : 0;
			
			var liHeight = li.getHeight(); 
			this.scrollAmountPerInc = (liHeight + liPaddingMarginHeight);
			var liWidth = li.getWidth();
			this.carouselEl.setSize(liWidth + liPaddingMarginWidth, 
				this.scrollAmountPerInc * this.numVisible + revealAmt*2 + this.navMargin*2 + ulPaddingHeight);
			this._clipReg.setHeight(this.scrollAmountPerInc * this.numVisible + revealAmt*2 + ulPaddingHeight);
			var revealTop = this._isExtraRevealed() ? revealAmt - (Math.abs(mt-mb)+Math.abs(pt-pb))/2 : 0;
			this.carouselList.setStyle({'position': 'relative', 'top': revealTop+'px'});
			var currY = this.carouselList.getY();
			this.carouselList.setY(currY - this.scrollAmountPerInc*(this.firstVisible-1));
		}else{
			this.carouselList.addClass('carousel-horizontal');
			
			var upl = this.carouselList.getPadding('l');
			var upr = this.carouselList.getPadding('r');
			var uml = this.carouselList.getMargins('l');
			var umr = this.carouselList.getMargins('r');
			var ulPaddingWidth = upl + upr + uml + umr;
			var liMarginWidth = ml + mr;
			var liPaddingMarginWidth = liMarginWidth + pr + pl;
			
			var revealAmt = (this._isExtraRevealed()) ?	(this.revealAmount+liPaddingMarginWidth/2) : 0;
			
			var liWidth = li.getWidth(); 
			this.scrollAmountPerInc = liWidth + liMarginWidth;
			this._clipReg.setWidth(this.scrollAmountPerInc*this.numVisible + revealAmt*2);
			this.carouselEl.setWidth(this.scrollAmountPerInc*this.numVisible + this.navMargin*2 + revealAmt*2 + ulPaddingWidth);
			var revealLeft = this._isExtraRevealed() ? revealAmt - (Math.abs(mr-ml)+Math.abs(pr-pl))/2 - (uml+upl) : 0;
			this.carouselList.setStyle({'position': 'relative', 'left': revealLeft+'px'});
			var currX = this.carouselList.getX();
			this.carouselList.setX(currX - this.scrollAmountPerInc*(this.firstVisible-1));
		}
	},
	
	show: function(){
		this.onRender();
	}, 
	
	hide: function(){
		this.carouselEl.hide();
	}
});
/*
var common = function(){
	
	return {
		init: function(){
			this.carousel = new Ext.ux.Carousel({
				numVisible: 3,
				animationSpeed: 0.5,
				autoPlay: 3000, 
				scrollInc: 3,
				navMargin: 20, 
				orientation: 'vertical', //vertical
				animationMethod: 'backBoth', 
				renderTo: 'mycarousel', 
				prevElement: "prev-arrow",
				nextElement: "next-arrow"
			});
			var cc = function(a, b){
				//this.moveTo(19);
				//alert('changebuttom');
			}
			this.carousel.on('prevButtonStateHandler', cc, this.carousel);
			this.carousel.show();
		}
	}
}();

Ext.onReady(common.init, common);
*/
