/*!
 * jCarousel - Riding carousels with jQuery
 *   http://sorgalla.com/jcarousel/
 *
 * Copyright (c) 2006 Jan Sorgalla (http://sorgalla.com)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 * Built on top of the jQuery library
 *   http://jquery.com
 *
 * Inspired by the "Carousel Component" by Bill Scott
 *   http://billwscott.com/carousel/
 */

(function($){
	$.fn.jcarousel=function(o){
		if(typeof o=='string'){
			var instance=$(this).data('jcarousel') , args=Array.prototype.slice.call(arguments,1);
			return instance[o].apply(instance,args);
		}else
			return this.each(function(){
				$(this).data('jcarousel',new $jc(this,o));
			});
		};
		
		var defaults={
			vertical:false,
			start:1,
			offset:1,
			size:null,
			scroll:3,
			visible:null,
			animation:'normal',
			easing:'swing',
			auto:0,
			wrap:null,
			initCallback:null,
			reloadCallback:null,
			itemLoadCallback:null,
			itemFirstInCallback:null,
			itemFirstOutCallback:null,
			itemLastInCallback:null,
			itemLastOutCallback:null,
			itemVisibleInCallback:null,
			itemVisibleOutCallback:null,
			buttonNextHTML:'<div></div>',
			buttonPrevHTML:'<div></div>',
			buttonNextEvent:'click',
			buttonPrevEvent:'click',
			buttonNextCallback:null,
			buttonPrevCallback:null
		};
		
		$.jcarousel=function(e,o){
			this.options=$.extend({},defaults,o||{});
			this.locked=false;
			this.container=null;
			this.clip=null;
			this.list=null;
			this.buttonNext=null;
			this.buttonPrev=null;
			this.wh=!this.options.vertical?'width':'height';
			this.lt=!this.options.vertical?'left':'top';
			
			var skin='',split=e.className.split(' ');
			
			for(var i=0;i<split.length;i++){
				if(split[i].indexOf('jcarousel-skin')!=-1){
					$(e).removeClass(split[i]);skin=split[i];break;
				}
			}
			
			if(e.nodeName=='UL'||e.nodeName=='OL'){
				this.list=$(e);
				this.container=this.list.parent();
				
				if(this.container.hasClass('jcarousel-clip')){
					if(!this.container.parent().hasClass('jcarousel-container'))
						this.container=this.container.wrap('<div></div>');
					
					this.container=this.container.parent();
				}else if(!this.container.hasClass('jcarousel-container'))
					this.container=this.list.wrap('<div></div>').parent();
			}else{
				this.container=$(e);
				this.list=this.container.find('ul,ol').eq(0);
			}
			
			if(skin!=''&&this.container.parent()[0].className.indexOf('jcarousel-skin')==-1)
				this.container.wrap('<div class=" '+skin+'"></div>');
				this.clip=this.list.parent();
				
				if(!this.clip.length||!this.clip.hasClass('jcarousel-clip'))
					this.clip=this.list.wrap('<div></div>').parent();
					
				this.buttonNext=$('.jcarousel-next',this.container);
				
				if(this.buttonNext.size()==0&&this.options.buttonNextHTML!=null)
					this.buttonNext=this.clip.after(this.options.buttonNextHTML).next();
				
				this.buttonNext.addClass(this.className('jcarousel-next'));
				this.buttonPrev=$('.jcarousel-prev',this.container);
				
				if(this.buttonPrev.size()==0&&this.options.buttonPrevHTML!=null)
					this.buttonPrev=this.clip.after(this.options.buttonPrevHTML).next();
				
				this.buttonPrev.addClass(this.className('jcarousel-prev'));
				this.clip.addClass(this.className('jcarousel-clip')).css({overflow:'hidden',position:'relative'});
				this.list.addClass(this.className('jcarousel-list')).css({overflow:'hidden',
																		  position:'relative',
																		  top:0,
																		  left:0,
																		  margin:0,
																		  padding:0
																		});
																		
				this.container.addClass(this.className('jcarousel-container')).css({position:'relative'});
				
				var di=this.options.visible!=null?Math.ceil(this.clipping()/this.options.visible):null;
				var li=this.list.children('li');
				var self=this;
				
				if(li.size()>0){
					var wh=0,i=this.options.offset;li.each(function(){
						self.format(this,i++);
						wh+=self.dimension(this,di);
					});this.list.css(this.wh,wh+'px');
					
					if(!o||o.size===undefined)
						this.options.size=li.size();
				}
				
				this.container.css('display','block');
				this.buttonNext.css('display','block');
				this.buttonPrev.css('display','block');
				this.funcNext=function(){
					self.next();
				};
				this.funcPrev=function(){
					self.prev();
				};
				this.funcResize=function(){
					self.reload();
				};
				
				if(this.options.initCallback!=null)
					this.options.initCallback(this,'init');
					
				if($.browser.safari){
					this.buttons(false,false);
					$(window).bind('load.jcarousel',function(){
						self.setup();
					});
				}else
					this.setup();
			};
			
			var $jc=$.jcarousel;$jc.fn=$jc.prototype={jcarousel:'0.2.4'};
			
			$jc.fn.extend=$jc.extend=$.extend;
			$jc.fn.extend({
				setup:function(){
					this.first=null;
					this.last=null;
					this.prevFirst=null;
					this.prevLast=null;
					this.animating=false;
					this.timer=null;
					this.tail=null;
					this.inTail=false;
					
					if(this.locked)return;
					
					this.list.css(this.lt, this.pos(this.options.offset)+'px');
					
					var p=this.pos(this.options.start);
					
					this.prevFirst=this.prevLast=null;
					this.animate(p,false);
					$(window).unbind('resize.jcarousel',this.funcResize).bind('resize.jcarousel',this.funcResize);
				},
				reset:function(){
					this.list.empty();
					this.list.css(this.lt,'0px');
					this.list.css(this.wh,'10px');
					if(this.options.initCallback!=null)
						this.options.initCallback(this,'reset');
						this.setup();
				},
				reload:function(){
					if(this.tail!=null&&this.inTail)
						this.list.css(this.lt,$jc.intval(this.list.css(this.lt))+this.tail);
						
						this.tail=null;
						this.inTail=false;
						if(this.options.reloadCallback!=null)
						this.options.reloadCallback(this);
						
						if(this.options.visible!=null){
							var self=this;
							var di=Math.ceil(this.clipping()/this.options.visible),wh=0,lt=0;
							
							$('li',this.list).each(function(i){
								wh+=self.dimension(this,di);
								if(i+1<self.first)lt=wh;}
							);
							
							this.list.css(this.wh,wh+'px');
							this.list.css(this.lt,-lt+'px');
						}
						
						this.scroll(this.first,false);
				},
				lock:function(){
					this.locked=true;this.buttons();
				},
				unlock:function(){
					this.locked=false;this.buttons();
				},
				size:function(s){
					if(s!=undefined){
						this.options.size=s;
						if(!this.locked)
							this.buttons();
					}
					return this.options.size;
				},
				has:function(i,i2){
					if(i2==undefined||!i2)
						i2=i;
						
					if(this.options.size!==null&&i2>this.options.size)
						i2=this.options.size;
							
					for(var j=i;j<=i2;j++){
						var e=this.get(j);
						if(!e.length||e.hasClass('jcarousel-item-placeholder'))
							return false;
					}
					
					return true;
				},
				get:function(i){
					return $('.jcarousel-item-'+i,this.list);},add:function(i,s){
						var e=this.get(i),old=0,add=0;
						
						if(e.length==0){
							var c,e=this.create(i),j=$jc.intval(i);
							
							while(c=this.get(--j)){
								if(j<=0||c.length){
									j<=0?this.list.prepend(e):c.after(e);break;
								}
							}
						}else
							old=this.dimension(e);
							
						e.removeClass(this.className('jcarousel-item-placeholder'));
						typeof s=='string'?e.html(s):e.empty().append(s);
						
						var di=this.options.visible!=null?Math.ceil(this.clipping()/this.options.visible):null;
						var wh=this.dimension(e,di)-old;
						
						if(i>0&&i<this.first)
							this.list.css(this.lt,$jc.intval(this.list.css(this.lt))-wh+'px');
						
						this.list.css(this.wh,$jc.intval(this.list.css(this.wh))+wh+'px');
						
						return e;
					},
					remove:function(i){
						var e=this.get(i);
						
						if(!e.length||(i>=this.first&&i<=this.last))
							return;
							
						var d=this.dimension(e);
						
						if(i<this.first)
							this.list.css(this.lt,$jc.intval(this.list.css(this.lt))+d+'px');
							
						e.remove();
						
						this.list.css(this.wh,$jc.intval(this.list.css(this.wh))-d+'px');
					},
					next:function(){
						this.stopAuto();
						
						if(this.tail!=null&&!this.inTail)
							this.scrollTail(false);
						else
							this.scroll(((this.options.wrap=='both'||this.options.wrap=='last')&&this.options.size!=null&&this.last==this.options.size)?1:this.first+this.options.scroll);
					},
					prev:function(){
						this.stopAuto();
						if(this.tail!=null&&this.inTail)
							this.scrollTail(true);
						else
							this.scroll(((this.options.wrap=='both'||this.options.wrap=='first')&&this.options.size!=null&&this.first==1)?this.options.size:this.first-this.options.scroll);
					},
					scrollTail:function(b){
						if(this.locked||this.animating||!this.tail)
							return;
						
						var pos=$jc.intval(this.list.css(this.lt));!b?pos-=this.tail:pos+=this.tail;
						
						this.inTail=!b;
						
						this.prevFirst=this.first;
						this.prevLast=this.last;
						this.animate(pos);
					},
					scroll:function(i,a){
						if(this.locked||this.animating)
							return;
							
						this.animate(this.pos(i),a);
					},
					pos:function(i){
						var pos=$jc.intval(this.list.css(this.lt));
						
						if(this.locked||this.animating)
							return pos;
						
						if(this.options.wrap!='circular')
							i=i<1?1:(this.options.size&&i>this.options.size?this.options.size:i);
							
						var back=this.first>i;
						var f=this.options.wrap!='circular'&&this.first<=1?1:this.first;
						var c=back?this.get(f):this.get(this.last);
						var j=back?f:f-1;
						var e=null,l=0,p=false,d=0,g;
						while(back?--j>=i:++j<i){
							e=this.get(j);
							p=!e.length;
							if(e.length==0){
								e=this.create(j).addClass(this.className('jcarousel-item-placeholder'));
								c[back?'before':'after'](e);
								if(this.first!=null&&this.options.wrap=='circular'&&this.options.size!==null&&(j<=0||j>this.options.size)){
									g=this.get(this.index(j));
									
									if(g.length)
										this.add(j,g.children().clone(true));
								}
							}
							
							c=e;
							d=this.dimension(e);
							if(p)l+=d;
							if(this.first!=null&&(this.options.wrap=='circular'||(j>=1&&(this.options.size==null||j<=this.options.size))))
								pos=back?pos+d:pos-d;
						}
						
						var clipping=this.clipping();
						var cache=[];
						var visible=0,j=i,v=0;
						var c=this.get(i-1);
						while(++visible){
							e=this.get(j);
							p=!e.length;
							if(e.length==0){
								e=this.create(j).addClass(this.className('jcarousel-item-placeholder'));
								c.length==0?this.list.prepend(e):c[back?'before':'after'](e);
								
								if(this.first!=null&&this.options.wrap=='circular'&&this.options.size!==null&&(j<=0||j>this.options.size)){
									g=this.get(this.index(j));
									
									if(g.length)
										this.add(j,g.find('>*').clone(true));
								}
							}
							
							c=e;
							var d=this.dimension(e);
							
							if(d==0){
								alert('jCarousel: No width/height set for items. This will cause an infinite loop. Aborting...');
								return 0;
							}
							
							if(this.options.wrap!='circular'&&this.options.size!==null&&j>this.options.size)
								cache.push(e);
							else if(p)
								l+=d;v+=d;
								
							if(v>=clipping)
								break;
							
							j++;
						}
						
						for(var x=0;x<cache.length;x++)
							cache[x].remove();
							
						if(l>0){
							this.list.css(this.wh,this.dimension(this.list)+l+'px');
						
							if(back){
								pos-=l;
								this.list.css(this.lt,$jc.intval(this.list.css(this.lt))-l+'px');
							}
						}
						
						var last=i+visible-1;
						
						if(this.options.wrap!='circular'&&this.options.size&&last>this.options.size)
							last=this.options.size;
							
							if(j>last){
								visible=0,j=last,v=0;
								
								while(++visible){
									var e=this.get(j--);
									
									if(!e.length)
										break;
										
									v+=this.dimension(e);
									
									if(v>=clipping)
										break;
								}
							}
							
							var first=last-visible+1;
							
							if(this.options.wrap!='circular'&&first<1)
								first=1;
								
							if(this.inTail&&back){
								pos+=this.tail;
								this.inTail=false;
							}
							
							this.tail=null;
							if(this.options.wrap!='circular'&&last==this.options.size&&(last-visible+1)>=1){
								var m=$jc.margin(this.get(last),!this.options.vertical?'marginRight':'marginBottom');
								
								if((v-m)>clipping)
									this.tail=v-clipping-m;
							}
							
							while(i-->first)
								pos+=this.dimension(this.get(i));
								
							this.prevFirst=this.first;
							this.prevLast=this.last;
							this.first=first;
							this.last=last;
							
							return pos;
					},
					animate:function(p,a){
						if(this.locked||this.animating)
							return;
							
						this.animating=true;
						
						var self=this;
						var scrolled=function(){
							self.animating=false;
							
							if(p==0)
								self.list.css(self.lt,0);
								
							if(self.options.wrap=='circular'||self.options.wrap=='both'||self.options.wrap=='last'||self.options.size==null||self.last<self.options.size)
								self.startAuto();
							
							self.buttons();
							self.notify('onAfterAnimation');
						};
						
						this.notify('onBeforeAnimation');
						if(!this.options.animation||a==false){
							this.list.css(this.lt,p+'px');
							scrolled();
						}else{
							var o=!this.options.vertical?{'left':p}:{'top':p};
							this.list.animate(o,this.options.animation,this.options.easing,scrolled);
						}
					},
					startAuto:function(s){
						if(s!=undefined)
							this.options.auto=s;
							
						if(this.options.auto==0)
							return this.stopAuto();
							
						if(this.timer!=null)
							return;
							
						var self=this;
						this.timer=setTimeout(function(){
							self.next();
						},this.options.auto*1000);
					},
					stopAuto:function(){
						if(this.timer==null)
							return;
							
						clearTimeout(this.timer);
						this.timer=null;
					},
					buttons:function(n,p){
						if(n==undefined||n==null){
							var n=!this.locked&&this.options.size!==0&&((this.options.wrap&&this.options.wrap!='first')||this.options.size==null||this.last<this.options.size);
						
							if(!this.locked&&(!this.options.wrap||this.options.wrap=='first')&&this.options.size!=null&&this.last>=this.options.size)
								n=this.tail!=null&&!this.inTail;
						}
						
						if(p==undefined||p==null){
							var p=!this.locked&&this.options.size!==0&&((this.options.wrap&&this.options.wrap!='last')||this.first>1);
							
							if(!this.locked&&(!this.options.wrap||this.options.wrap=='last')&&this.options.size!=null&&this.first==1)
								p=this.tail!=null&&this.inTail;
						}
						
						var self=this;
						
						this.buttonNext[n?'bind':'unbind'](this.options.buttonNextEvent+'.jcarousel',this.funcNext)[n?'removeClass':'addClass'](this.className('jcarousel-next-disabled')).attr('disabled',n?false:true);
						this.buttonPrev[p?'bind':'unbind'](this.options.buttonPrevEvent+'.jcarousel',this.funcPrev)[p?'removeClass':'addClass'](this.className('jcarousel-prev-disabled')).attr('disabled',p?false:true);
						
						if(this.buttonNext.length>0&&(this.buttonNext[0].jcarouselstate==undefined||this.buttonNext[0].jcarouselstate!=n)&&this.options.buttonNextCallback!=null){
							this.buttonNext.each(function(){
													self.options.buttonNextCallback(self,this,n);
												});
												
							this.buttonNext[0].jcarouselstate=n;
						}
						
						if(this.buttonPrev.length>0&&(this.buttonPrev[0].jcarouselstate==undefined||this.buttonPrev[0].jcarouselstate!=p)&&this.options.buttonPrevCallback!=null){
							this.buttonPrev.each(function(){
													self.options.buttonPrevCallback(self,this,p);
												});
												
							this.buttonPrev[0].jcarouselstate=p;
						}
					},notify:function(evt){
						var state=this.prevFirst==null?'init':(this.prevFirst<this.first?'next':'prev');
						
						this.callback('itemLoadCallback',evt,state);
						
						if(this.prevFirst!==this.first){
							this.callback('itemFirstInCallback',evt,state,this.first);
							this.callback('itemFirstOutCallback',evt,state,this.prevFirst);
						}
						
						if(this.prevLast!==this.last){
							this.callback('itemLastInCallback',evt,state,this.last);
							this.callback('itemLastOutCallback',evt,state,this.prevLast);
						}
						
						this.callback('itemVisibleInCallback',evt,state,this.first,this.last,this.prevFirst,this.prevLast);
						this.callback('itemVisibleOutCallback',evt,state,this.prevFirst,this.prevLast,this.first,this.last);
					},callback:function(cb,evt,state,i1,i2,i3,i4){
						if(this.options[cb]==undefined||(typeof this.options[cb]!='object'&&evt!='onAfterAnimation'))
							return;
							
						var callback=typeof this.options[cb]=='object'?this.options[cb][evt]:this.options[cb];
						
						if(!$.isFunction(callback))	
							return;
							
						var self=this;
						
						if(i1===undefined)
							callback(self,state,evt);
						else if(i2===undefined)
							this.get(i1).each(function(){
								callback(self,this,i1,state,evt);
							});
						else{
							for(var i=i1;i<=i2;i++)
								if(i!==null&&!(i>=i3&&i<=i4))
									this.get(i).each(function(){
										callback(self,this,i,state,evt);
									});
							}
					},
					create:function(i){
						return this.format('<li></li>',i);
					},
					format:function(e,i){
						var $e=$(e).addClass(this.className('jcarousel-item')).addClass(this.className('jcarousel-item-'+i)).css({'float':'left','list-style':'none'});
						$e.attr('jcarouselindex',i);
						return $e;
					},
					className:function(c){
						return c+' '+c+(!this.options.vertical?'-horizontal':'-vertical');
					},
					dimension:function(e,d){
						var el=e.jquery!=undefined?e[0]:e;
						var old=!this.options.vertical?el.offsetWidth+$jc.margin(el,'marginLeft')+$jc.margin(el,'marginRight'):el.offsetHeight+$jc.margin(el,'marginTop')+$jc.margin(el,'marginBottom');
						
						if(d==undefined||old==d)
							return old;
							
						var w=!this.options.vertical?d-$jc.margin(el,'marginLeft')-$jc.margin(el,'marginRight'):d-$jc.margin(el,'marginTop')-$jc.margin(el,'marginBottom');
					
						$(el).css(this.wh,w+'px');
						
						return this.dimension(el);
					},
					clipping:function(){
						return!this.options.vertical?this.clip[0].offsetWidth-$jc.intval(this.clip.css('borderLeftWidth'))-$jc.intval(this.clip.css('borderRightWidth')):this.clip[0].offsetHeight-$jc.intval(this.clip.css('borderTopWidth'))-$jc.intval(this.clip.css('borderBottomWidth'));
					},
					index:function(i,s){
						if(s==undefined)
							s=this.options.size;
						
						return Math.round((((i-1)/s)-Math.floor((i-1)/s))*s)+1;
					}
				}
			);
			
			$jc.extend({defaults:function(d){
					return $.extend(defaults,d||{});
				},
				margin:function(e,p){
					if(!e)
						return 0;
						
					var el=e.jquery!=undefined?e[0]:e;
					
					if(p=='marginRight'&&$.browser.safari){
						var old={'display':'block','float':'none','width':'auto'},oWidth,oWidth2;
						
						$.swap(el,old,function(){
							oWidth=el.offsetWidth;
						});
						
						old['marginRight']=0;
						
						$.swap(el,old,function(){
							oWidth2=el.offsetWidth;
						});
						
						return oWidth2-oWidth;
					}
					
					return $jc.intval($.css(el,p));
					
				},
				intval:function(v){
					v=parseInt(v);
					
					return isNaN(v)?0:v;
				}
			});
		})(jQuery);
