/**
 * TouchSlider v1.2.4
 * By qiqiboy, http://www.qiqiboy.com, http://weibo.com/qiqiboy, 2012/12/05
 */
(function(window, undefined){
	
	"use strict";
	
	var hasTouch=("createTouch" in document) || ('ontouchstart' in window),
		testStyle=document.createElement('div').style,
		testVendor=(function(){
			var cases={
				'OTransform':['-o-','otransitionend'],
				'WebkitTransform':['-webkit-','webkitTransitionEnd'],
				'MozTransform':['-moz-','transitionend'],
				'msTransform':['-ms-','MSTransitionEnd'],
				'transform':['','transitionend']
			},prop;
			for(prop in cases){
				if(prop in testStyle)return cases[prop];
			}
			return false;
		})(),
		sg=[['width','left','right'],['height','top','bottom']],
		cssVendor=testVendor&&testVendor[0],
		toCase=function(str){
			return (str+'').replace(/^-ms-/, 'ms-').replace(/-([a-z]|[0-9])/ig, function(all, letter){
				return (letter+'').toUpperCase();
			});
		},
		testCSS=function(prop){
			var _prop=toCase(cssVendor+prop);
			return (prop in testStyle)&& prop || (_prop in testStyle)&& _prop;
		},
		parseArgs=function(arg,dft){
			for(var key in dft){
				if(typeof arg[key]=='undefined'){
					arg[key]=dft[key];
				}
			}
			return arg;
		},
		children=function(elem){
			var children=elem.children||elem.childNodes,
				_ret=[],i=0;
			for(;i<children.length;i++){
				if(children[i].nodeType===1){
					_ret.push(children[i]);
				}
			}
			return _ret;
		},
		each=function(arr,func){
			var i=0,j=arr.length;
			for(;i<j;i++){
				if(func.call(arr[i],i,arr[i])===false){
					break;
				}
			}
		},
		returnFalse=function(evt){
			evt=TouchSlider.fn.eventHook(evt);
			evt.preventDefault();
		},
		startEvent=hasTouch ? "touchstart" : "mousedown",
		moveEvent=hasTouch ? "touchmove" : "mousemove",
		endEvent=hasTouch ? "touchend" : "mouseup",
		transitionend=testVendor[1]||'',
		
		TouchSlider=function(id,cfg){
			if(!(this instanceof TouchSlider)){
				return new TouchSlider(id,cfg);
			}
			
			if(typeof id!='string' && !id.nodeType){
				cfg=id;
				id=cfg.id;
			}
			if(!id.nodeType){
				id=document.getElementById(id);
			}
			this.cfg=parseArgs(cfg||{},this._default);
			this.element=id;
			if(this.element){
				this.container=this.element.parentNode||document.body;
				this.setup();
			}
		}

	TouchSlider.fn=TouchSlider.prototype={
		//默认配置
		_default: {
			'id':'slider', //幻灯容器的id
			'begin':0,
			'auto':true, //是否自动开始，负数表示非自动开始，0,1,2,3....表示自动开始以及从第几个开始
			'speed':600, //动画效果持续时间 ms
			'timeout':5000,//幻灯间隔时间 ms,
			'direction':'left', //left right up down
			'align':'center',
			'fixWidth':true,
			'mouseWheel':false,
			'before':new Function,
			'after':new Function
		},
		//设置OR获取节点样式
		css:function(elem,css){
			if(typeof css=='string'){
				var style=document.defaultView && document.defaultView.getComputedStyle && getComputedStyle(elem, null) || elem.currentStyle || elem.style || {};
				return style[toCase(css)];
			}else{
				var prop,
					propFix;
				for(prop in css){
					if(prop=='float'){
						propFix=("cssFloat" in testStyle) ? 'cssFloat' : 'styleFloat';
					}else{
						propFix=toCase(prop);
					}
					elem.style[propFix]=css[prop];
				}
			}
		},
		//绑定事件
		addListener:function(e, n, o, u){
			if(e.addEventListener){
				e.addEventListener(n, o, u);
				return true;
			} else if(e.attachEvent){
				e.attachEvent('on' + n, o);
				return true;
			}
			return false;
		},
		removeListener:function(e, n, o, u){
			if(e.addEventListener){
				e.removeEventListener(n, o, u);
				return true;
			} else if(e.attachEvent){
				e.detachEvent('on' + n, o);
				return true;
			}
			return false;
		},
		eventHook:function(origEvt){
			var evt={},
				props="changedTouches touches scale target view which clientX clientY fromElement offsetX offsetY pageX pageY toElement".split(" ");
			origEvt=origEvt||window.event;
			each(props,function(){
				evt[this]=origEvt[this];
			});
			evt.target=origEvt.target||origEvt.srcElement||document;
			if(evt.target.nodeType===3){
				evt.target=evt.target.parentNode;
			}
			evt.preventDefault=function(){
				origEvt.preventDefault && origEvt.preventDefault();
				evt.returnValue=origEvt.returnValue=false;
			}
			evt.stopPropagation=function(){
				origEvt.stopPropagation && origEvt.stopPropagation();
				evt.cancelBubble=origEvt.cancelBubble=true;
			}
			if(hasTouch&&evt.touches.length){
				evt.pageX=evt.touches.item(0).pageX;
				evt.pageY=evt.touches.item(0).pageY;
			}else if(typeof origEvt.pageX=='undefined'){
				var doc=document.documentElement,
					body=document.body;
				evt.pageX=origEvt.clientX+(doc&&doc.scrollLeft || body&&body.scrollLeft || 0)-(doc&&doc.clientLeft || body&&body.clientLeft || 0);
				evt.pageY=origEvt.clientY+(doc&&doc.scrollTop  || body&&body.scrollTop  || 0)-(doc&&doc.clientTop  || body&&body.clientTop  || 0);
			}
			evt.origEvent=origEvt;
			return evt;
		},
		//修正函数作用环境
		bind:function(func, obj){
			return function(){
				return func.apply(obj, arguments);
			}
		},
		//初始化
		setup: function(){
			this.slides=children(this.element);
			this.length=this.slides.length;
			this.cfg.timeout=parseInt(this.cfg.timeout);
			this.cfg.speed=parseInt(this.cfg.speed);
			this.cfg.begin=parseInt(this.cfg.begin);
			this.cfg.auto=!!this.cfg.auto;
			this.cfg.timeout=Math.max(this.cfg.timeout,this.cfg.speed);
			this.touching=!!hasTouch;
			this.css3transition=!!testVendor; 
			this.index=this.cfg.begin<0||this.cfg.begin>=this.length ? 0 : this.cfg.begin;
			
			if(this.length<1)return false;
			
			switch(this.cfg.direction){
				case 'up':
				case 'down':this.direction=this.cfg.direction; this.vertical=1; break;
				case 'right':this.direction='right';
				default:this.direction=this.direction||'left'; this.vertical=0; break;
			}

			this.addListener(this.element,startEvent,this.bind(this._start,this),false);
			this.addListener(document,moveEvent,this.bind(this._move,this),false);
			this.addListener(document,endEvent,this.bind(this._end,this),false);
			this.addListener(document,'touchcancel',this.bind(this._end,this),false);
			this.addListener(this.element,transitionend,this.bind(this.transitionend,this),false);
			
			this.addListener(window,'resize',this.bind(function(){
				clearTimeout(this.resizeTimer);
				this.resizeTimer=setTimeout(this.bind(this.resize,this),100);
			},this),false);
			
			if(this.cfg.mouseWheel){
				this.addListener(this.element,'mousewheel',this.bind(this.mouseScroll,this),false);
				this.addListener(this.element,'DOMMouseScroll',this.bind(this.mouseScroll,this),false);
			}
			this.playing=this.cfg.auto;
			this.resize();
		},
		getSum:function(type,start,end){
			var sum=0,i=start,
				_type=toCase('-'+type);
			for(;i<end;i++){
				sum+=this['getOuter'+_type](this.slides[i]);
			}
			return sum;
		},
		getPos:function(type,index){
			var _type=toCase('-'+type),
				myWidth=this.getSum(type,index,index+1),
				sum=this.getSum(type,0,index)+this['getOuter'+_type](this.element)/2-this['get'+_type](this.element)/2;
			switch(this.cfg.align){
				case 'left':
					return -sum;
				case 'right':
					return this[type]-myWidth-sum;
				default:return (this[type]-myWidth)/2-sum;
			}
		},
		resize:function(){
			clearTimeout(this.aniTimer);
			var _this=this,css,type=sg[this.vertical][0],_type=toCase('-'+type),
				pst=this.css(this.container,'position');
			this.css(this.container,{'overflow':'hidden','visibility':'hidden','listStyle':'none','position':pst=='static'?'relative':pst});
			this[type]=this['get'+_type](this.container);
			css={float:this.vertical?'none':'left',display:'block'};
			each(this.slides,function(){
				if(_this.cfg.fixWidth){
					css[type]=_this[type]-_this['margin'+_type](this)-_this['padding'+_type](this)-_this['border'+_type](this)+'px';
				}
				_this.css(this,css);
			});
			this.total=this.getSum(type,0,this.length);
			css={position:'relative',overflow:'hidden'};
			css[cssVendor+'transition-duration']='0ms';
			css[type]=this.total+'px';
			css[sg[this.vertical][1]]=this.getPos(type,this.index)+'px';
			this.css(this.element,css);
			this.css(this.container,{'visibility':'visible'});
			this.playing && this.play();
			return this;
		},
		slide:function(index, speed){
			var direction=sg[this.vertical][1],
				type=sg[this.vertical][0],
				transition=testCSS('transition'),
				nowPos=parseFloat(this.css(this.element,direction))||0,
				endPos,css={},change,size=this.getSum(type,index,index+1);
			index=Math.min(Math.max(0,index),this.length-1);
			speed=typeof speed=='undefined' ? this.cfg.speed : parseInt(speed);
			endPos=this.getPos(type,index);
			change=endPos-nowPos, //变化量
			speed=Math.abs(change)<size?Math.ceil(Math.abs(change)/size*speed):speed;
			if(transition){
				css[transition]=direction+' ease '+speed+'ms';
				css[direction]=endPos+'px';
				this.css(this.element,css);
			}else{
				var _this=this,
					begin=0, //动画开始时间
					time=speed/10,//动画持续时间
					animate=function(t,b,c,d){ //缓动效果计算公式
						return -c * ((t=t/d-1)*t*t*t - 1) + b;
					},
					run=function(){
						if(begin<time){
							begin++;
							_this.element.style[direction]=Math.ceil(animate(begin,nowPos,change,time))+'px';
							_this.aniTimer=setTimeout(run,10);
						}else{
							_this.element.style[direction]=endPos+'px';
							_this.transitionend({propertyName:direction});
						}
					}
				clearTimeout(this.aniTimer);
				run();
			}
			this.cfg.before.call(this,index,this.slides[this.index]);
			this.index=index;
			return this;
		},
		play:function(){
			clearTimeout(this.timer);
			this.playing=true;
			this.timer=setTimeout(this.bind(function(){
				this.direction=='left'||this.direction=='up' ? this.next() : this.prev();
			},this), this.cfg.timeout);
			return this;
		},
		pause:function(){
			clearTimeout(this.timer);
			this.playing=false;
			return this;
		},
		stop:function(){
			this.pause();
			return this.slide(0);
		},
		prev:function(offset,sync){
			clearTimeout(this.timer);
			var index=this.index;
			offset=typeof offset == 'undefined'?offset=1:offset%this.length;
			index-=offset;
			if(sync===false){
				index=Math.max(index,0);
			}else{
				index=index<0?this.length+index:index;
			}
			return this.slide(index);
		},
		next:function(offset,sync){
			clearTimeout(this.timer);
			var index=this.index;
			if(typeof offset=='undefined')offset=1;
			index+=offset;
			if(sync===false){
				index=Math.min(index,this.length-1);
			}else{
				index%=this.length
			}
			return this.slide(index);
		},
		_start:function(evt){
			evt=this.eventHook(evt);
			if(!this.touching)evt.preventDefault();
			this.removeListener(this.element,'click',returnFalse);
			this.startPos=[evt.pageX,evt.pageY];
			this.element.style[toCase(cssVendor+'transition-duration')]='0ms';
			this.startTime=+new Date;
			this._pos=parseFloat(this.css(this.element,sg[this.vertical][1]))||0;
		},
		_move:function(evt){
			if(!this.startPos || evt.scale&&evt.scale!==1)return;
			evt=this.eventHook(evt);
			this.stopPos=[evt.pageX,evt.pageY];
			var direction=sg[this.vertical][1],
				type=sg[this.vertical][0],
				offset=this.stopPos[this.vertical]-this.startPos[this.vertical];
			if(this.scrolling || typeof this.scrolling=='undefined'&&Math.abs(offset)>=Math.abs(this.stopPos[1-this.vertical]-this.startPos[1-this.vertical])){
				evt.preventDefault();
				offset=offset/((!this.index&&offset>0 || this.index==this.length-1&&offset<0) ? (Math.abs(offset)/this[type]+1) : 1);
				this.element.style[direction]=this._pos+offset+'px';
				if(offset&&typeof this.scrolling=='undefined'){
					this.scrolling=true;//标记拖动（有效触摸）
					clearTimeout(this.timer);//暂停幻灯
					clearTimeout(this.aniTimer);//暂停动画
				}
			}else this.scrolling=false;
		},
		_end:function(evt){
			if(this.startPos){
				if(this.scrolling){
					var type=sg[this.vertical][0],
						direction=sg[this.vertical][1],
						offset=this.stopPos[this.vertical]-this.startPos[this.vertical],
						absOff=Math.abs(offset),
						sub=absOff/offset,
						myWidth,curPos,tarPos,
						next=this.index,off=0;
					this.addListener(this.element,'click',returnFalse);
					if(absOff>20){//有效移动距离
						curPos=parseFloat(this.css(this.element,sg[this.vertical][1]));
						do{
							if(next>=0 && next<this.length){
								tarPos=this.getPos(type,next);
								myWidth=this.getSum(type,next,next+1);
							}else{
								next+=sub;
								break;
							}
						}while(Math.abs(tarPos-curPos)>myWidth/2 && (next-=sub));
						off=Math.abs(next-this.index);
						if(!off && +new Date-this.startTime<250){
							off=1;
						}
					}
					offset>0?this.prev(off,false):this.next(off,false);
					
					this.playing && this.play();
				}
				delete this._pos;
				delete this.stopPos;
				delete this.startPos;
				delete this.scrolling;
				delete this.startTime;
			}
		},
		mouseScroll:function(evt){
			if(this.cfg.mouseWheel){
				evt=this.eventHook(evt);
				evt.preventDefault();
				var _e=evt.origEvent;
				var wheelDelta=_e.wheelDelta || _e.detail && _e.detail*-1 || 0,
					flag=wheelDelta/Math.abs(wheelDelta);
				wheelDelta>0?this.prev(1,false):this.next(1,false);
			}
		},
		transitionend:function(evt){
			if(evt.propertyName==sg[this.vertical][1]){
				this.cfg.after.call(this, this.index, this.slides[this.index]);
				this.playing && this.play();
			}
		}
	}

	each(['Width','Height'],function(i,type){
		var _type=type.toLowerCase();
		each(['margin','padding','border'],function(j,name){
			TouchSlider.fn[name+type]=function(elem){
				return parseFloat(this.css(elem,name+'-'+sg[i][1]+(name=='border'?'-width':'')))+parseFloat(this.css(elem,name+'-'+sg[i][2]+(name=='border'?'-width':'')));
			}
		});
		TouchSlider.fn['get'+type]=function(elem){
			return elem['offset'+type]-this['padding'+type](elem)-this['border'+type](elem);
		}
		TouchSlider.fn['getOuter'+type]=function(elem){
			return elem['offset'+type]+this['margin'+type](elem);
		}
	});
	
	window.TouchSlider=TouchSlider;
})(window);