/*

 * TouchSlider

 * @author qiqiboy

 * @github https://github.com/qiqiboy/touchslider

 */

;

(function(ROOT, struct, undefined){



    "use strict";



    var VERSION='2.0.1';

    var lastTime=0,

        nextFrame=ROOT.requestAnimationFrame            ||

                ROOT.webkitRequestAnimationFrame        ||

                ROOT.mozRequestAnimationFrame           ||

                ROOT.msRequestAnimationFrame            ||

                function(callback){

                    var currTime=+new Date,

                        delay=Math.max(1000/60,1000/60-(currTime-lastTime));

                    lastTime=currTime+delay;

                    return setTimeout(callback,delay);

                },

        cancelFrame=ROOT.cancelAnimationFrame           ||

                ROOT.webkitCancelAnimationFrame         ||

                ROOT.webkitCancelRequestAnimationFrame  ||

                ROOT.mozCancelRequestAnimationFrame     ||

                ROOT.msCancelRequestAnimationFrame      ||

                clearTimeout,

        DOC=ROOT.document,

        divstyle=DOC.createElement('div').style,

        cssVendor=function(){

            var tests="-webkit- -moz- -o- -ms-".split(" "),

                prop;

            while(prop=tests.shift()){

                if(camelCase(prop+'transform') in divstyle){

                    return prop;

                }

            }

            return '';

        }(),

        transition=cssTest('transition'),

        toString=Object.prototype.toString,

        slice=[].slice,

        class2type={},

        event2type={},

        event2code={

            click:4,

            mousewheel:5,

            dommousescroll:5,

            keydown:6,

            resize:7

        },

        POINTERTYPES={

            2:'touch',

            3:'pen',

            4:'mouse',

            pen:'pen'

        },

        STARTEVENT=[],

        MOVEEVENT=[],

        EVENT=function(){

            var ret={},

                states={

                    start:1,

                    down:1,

                    move:2,

                    end:3,

                    up:3,

                    cancel:3

                };

            each("mouse touch pointer MSPointer-".split(" "),function(prefix){

                var _prefix=/pointer/i.test(prefix)?'pointer':prefix;

                ret[_prefix]=ret[_prefix]||{};

                POINTERTYPES[_prefix]=_prefix;

                each(states,function(endfix,code){

                    var ev=camelCase(prefix+endfix);

                    ret[_prefix][ev]=code;

                    event2type[ev.toLowerCase()]=_prefix;

                    event2code[ev.toLowerCase()]=code;

                    if(code==1){

                        STARTEVENT.push(ev);

                    }else{

                        MOVEEVENT.push(ev);

                    }

                });

            });

            each("otransitionend oTransitionEnd webkitTransitionEnd mozTransitionEnd MSTransitionEnd transitionend".split(" "),function(ev){

                STARTEVENT.push(ev);

                event2code[ev.toLowerCase()]=8;

            });

            return ret;

        }(),

        POINTERS={

            touch:{},

            pointer:{},

            mouse:{}

        };



    each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(name){

        class2type["[object "+name+"]"]=name.toLowerCase();

    });



    function type(obj){

        if(obj==null){

            return obj+"";

        }

        

        return typeof obj=='object'||typeof obj=='function' ? class2type[toString.call(obj)]||"object" :

            typeof obj;

    }

	

    function isArrayLike(elem){

        var tp=type(elem);

        return !!elem && tp!='function' && tp!='string' && (elem.length===0 || elem.length && (elem.nodeType==1 || (elem.length-1) in elem));

    }



    function camelCase(str){

        return (str+'').replace(/^-ms-/, 'ms-').replace(/-([a-z]|[0-9])/ig, function(all, letter){

            return (letter+'').toUpperCase();

        });

    }



    function cssTest(name){

        var prop=camelCase(name),

            _prop=camelCase(cssVendor+prop);

        return (prop in divstyle) && prop || (_prop in divstyle) && _prop || '';

    }



    function isFunction(func){

        return type(func)=='function';

    }



    function pointerLength(obj){

        var len=0,key;

        if(type(obj.length)=='number'){

            len=obj.length;

        }else if('keys' in Object){

            len=Object.keys(obj).length;

        }else{

            for(key in obj){

                if(obj.hasOwnProperty(key)){

                    len++;

                }

            }

        }

        return len;

    }



    function pointerItem(obj,n){

        return 'item' in obj?obj.item(n):function(){

            var i=0,key;

            for(key in this){

                if(i++==n){

                    return this[key];

                }

            }

        }.call(obj,n);

    }

    

    function each(arr, iterate){

        if(isArrayLike(arr)){

            if(type(arr.forEach)=='function'){

                return arr.forEach(iterate);

            }

            var i=0,len=arr.length,item;

            for(;i<len;i++){

                item=arr[i];

                if(type(item)!='undefined'){

                    iterate(item,i,arr);

                }

            }

        }else{

            var key;

            for(key in arr){

                iterate(key,arr[key],arr);

            }

        }

    }



    function children(elem){

        var ret=[];

        each(elem.children||elem.childNodes,function(elem){

            if(elem.nodeType==1){

                ret.push(elem);

            }

        });

        return ret;

    }



    function getStyle(elem,prop){

        var style=ROOT.getComputedStyle&&ROOT.getComputedStyle(elem,null)||elem.currentStyle||elem.style;

        return style[prop];

    }



    function setStyle(elem,props){

        each(props,function(name,value){

            var prop;

            switch(name){

                case 'float':

                    prop=cssTest('cssFloat')?'cssFloat':'styleFloat';

                    break;

                default:

                    prop=camelCase(name);

            }

            try{

                elem.style[prop]=value;

            }catch(e){}

        });

    }



    function addListener(elem,evstr,handler){

        if(type(evstr)=='object'){

            return each(evstr,function(evstr,handler){

                addListener(elem,evstr,handler);

            });

        }

        each(evstr.split(" "),function(ev){

            if(elem.addEventListener){

                elem.addEventListener(ev,handler,false);

            }else if(elem.attachEvent){

                elem.attachEvent('on'+ev,handler);

            }else elem['on'+ev]=handler;

        });

    }



    function offListener(elem,evstr,handler){

        if(type(evstr)=='object'){

            return each(evstr,function(evstr,handler){

                offListener(elem,evstr,handler);

            });

        }

        each(evstr.split(" "),function(ev){

            if(elem.removeEventListener){

                elem.removeEventListener(ev,handler,false);

            }else if(elem.detachEvent){

                elem.detachEvent('on'+ev,handler);

            }else elem['on'+ev]=null;

        });

    }



    function removeRange(){

        var range;

        if(ROOT.getSelection){

            range=getSelection();

            if('empty' in range)range.empty();

            else if('removeAllRanges' in range)range.removeAllRanges();

        }else{

            DOC.selection.empty();

        }

    }



    function EASE(t,b,c,d){

        return -c * ((t=t/d-1)*t*t*t - 1) + b;

    }



    function filterEvent(oldEvent){

        var ev={},

            which=oldEvent.which,

            button=oldEvent.button,

            pointers,pointer;



        each("wheelDelta detail which keyCode".split(" "),function(prop){

            ev[prop]=oldEvent[prop];

        });



        ev.oldEvent=oldEvent;

        

        ev.type=oldEvent.type.toLowerCase();

        ev.eventType=event2type[ev.type]||ev.type;

        ev.eventCode=event2code[ev.type]||0;

        ev.pointerType=POINTERTYPES[oldEvent.pointerType]||oldEvent.pointerType||ev.eventType;



        ev.target=oldEvent.target||oldEvent.srcElement||DOC.documentElement;

        if(ev.target.nodeType===3){

            ev.target=ev.target.parentNode;

        }



        ev.preventDefault=function(){

            oldEvent.preventDefault && oldEvent.preventDefault();

            ev.returnValue=oldEvent.returnValue=false;

        }



        if(pointers=POINTERS[ev.eventType]){

            switch(ev.eventType){

                case 'mouse':

                case 'pointer':

                    var id=oldEvent.pointerId||0;

                    ev.eventCode==3?delete pointers[id]:pointers[id]=oldEvent;

                    break;

                case 'touch':

                    POINTERS[ev.eventType]=pointers=oldEvent.touches;

                    break;

            }



            if(pointer=pointerItem(pointers,0)){

                ev.clientX=pointer.clientX;

                ev.clientY=pointer.clientY;

            }

            

            ev.button=which<4?Math.max(0,which-1):button&4&&1||button&2; // left:0 middle:1 right:2

            ev.length=pointerLength(pointers);

        }



        return ev;

    }



    struct.prototype={

        version:VERSION,

        constructor:struct,

        latestTime:0,

        init:function(config){

            var self=this,

                handler=this.handler=function(ev){

                    self.handleEvent(ev);

                }



            this.events={};

            this.duration=isNaN(parseInt(config.duration))?600:parseInt(config.duration);

            this.direction=parseInt(config.direction)==0?0:1;

            this.current=parseInt(config.start)||0;

            this.mouse=config.mouse==null?true:!!config.mouse;

            this.mousewheel=!!config.mousewheel;

            this.interval=parseInt(config.interval)||5000;

            this.playing=config.autoplay==null?true:!!config.autoplay;

            this.arrowkey=!!config.arrowkey;

            this.fullsize=config.fullsize==null?true:!!config.fullsize;

            this.align=config.align||'center';

            this.pages=children(this.container);

            this.length=this.pages.length;



            this.pageData=[];



            addListener(this.container,STARTEVENT.join(" ")+" click"+(this.mousewheel?" mousewheel DOMMouseScroll":""),handler);

            addListener(DOC,MOVEEVENT.join(" ")+(this.arrowkey?" keydown":""),handler);

            addListener(ROOT,'resize',handler);

            

            each(this.pages,function(page){

                self.pageData.push({

                    cssText:page.style.cssText||''

                });

            });

            this.pageData.container=this.container.style.cssText||'';



            this.on({

                before:function(){clearTimeout(this.playTimer);},

                dragStart:function(){clearTimeout(this.playTimer);removeRange();},

                after:this.firePlay

            }).firePlay();



            this.comment=document.createComment(' Powered by TouchSlider v'+this.version+'  https://github.com/qiqiboy/touchslider ');

            this.container.appendChild(this.comment);



            this.resize();

        },

        resize:function(){

            var self=this,

                type=this.direction?'height':'width',

                pst=getStyle(this.container,'position'),

                css;



            this.size=this.getSize(this.offsetParent=this.container[pst=='absolute'||pst=='fixed'?'offsetParent':'parentNode']||DOC.body);



            css={

                float:'left',

                display:'inline'

            }

            each(this.pages,function(page){

                if(self.fullsize){

                    css[type]=self.size-self.getMarginSize(page)-self.getPaddingSize(page)-self.getBorderSize(page)+'px';

                }

                if(type=='height'){

                    css['clear']='both';

                }

                setStyle(page,css);

            });



            this.total=this.getSum(0,this.length);

            

            css={};

            if(pst=='static'){

                css={position:'relative'};

            }

            css[transition]='none';

            css[type]=this.total+'px';

            css[this.direction?'top':'left']=this.getPos(this.current)+'px';

            cancelFrame(this.timer);

            setStyle(this.container,css);



            return this;

        },

        on:function(ev,callback){

            var self=this;

            if(type(ev)=='object'){

                each(ev,function(ev,callback){

                    self.on(ev,callback);

                });

            }else{

                if(!this.events[ev]){

                    this.events[ev]=[];

                }

                this.events[ev].push(callback);

            }

            return this;

        },

        fire:function(ev){

            var self=this,

                args=slice.call(arguments,1);

            each(this.events[ev]||[],function(func){

                if(isFunction(func)){

                    func.apply(self,args);

                }

            });

            return this;

        },

        isStatic:function(){

            return !this.timer && !this.drag;

        },

        prev:function(){

            return this.slide((this.current-1+this.length)%this.length);

        },

        next:function(){

            return this.slide((this.current+1)%this.length);

        },

        play:function(){

            this.playing=true;

            return this.firePlay();

        },

        firePlay:function(){

            var self=this;

            if(this.playing){

                this.playTimer=setTimeout(function(){

                    self.next();

                },this.interval);

            }

            return this;

        },

        pause:function(){

            this.playing=false;

            clearTimeout(this.playTimer);

            return this;

        },

        slide:function(_index){

            var self=this,

                dir=this.direction,

                stime=+new Date,

                duration=this.duration,

                current=this.current,

                index=Math.min(Math.max(0,_index),this.length-1),

                curSize=this.getSum(index,index+1),

                curPos=parseFloat(getStyle(this.container,dir?'top':'left'))||0,

                type=dir?'top':'left',

                css={},tarPos;

        

            tarPos=this.getPos(index);

            duration*=Math.min(1,Math.abs(tarPos-curPos)/curSize)||10;

            this.current=index;

            this.latestTime=stime+duration;

            this.fire('before',current,index);

            this.end=function(){

                delete self.timer;

                self.fire('after',index,current);

            }

            if(transition){

                this.timer=1;

                css[transition]=type+' '+duration+'ms ease';

                css[type]=tarPos+'px';

                setStyle(this.container,css);

            }else{

                cancelFrame(this.timer);

                ani();

            }



            function ani(){

                var offset=Math.min(duration,+new Date-stime),

                    s=EASE(offset,0,1,duration)

                    cp=(tarPos-curPos)*s+curPos;

                self.container.style[type]=cp+'px';

                if(offset==duration){

                    self.end();

                }else{

                    self.timer=nextFrame(ani);

                }

            }

        },

        handleEvent:function(oldEvent){

            var ev=filterEvent(oldEvent),

                canDrag=ev.button<1&&ev.length<2&&(!this.pointerType||this.pointerType==ev.eventType)&&(this.mouse||ev.pointerType!='mouse');



            switch(ev.eventCode){

                case 2:

                    if(canDrag&&this.rect){

                        var index=this.current,

                            dir=this.direction,

                            rect=[ev.clientX,ev.clientY],

                            _rect=this.rect,

                            offset=rect[dir]-_rect[dir];

                        if(this.drag==null && _rect.toString()!=rect.toString()){

                            this.drag=Math.abs(offset)>=Math.abs(rect[1-dir]-_rect[1-dir]);

                            this.drag && this.fire('dragStart',ev);

                        }

                        if(this.drag){

                            if(!this.pages[index+(offset>0?-1:1)]){

                                offset/=Math.abs(offset)/this.size+2;

                            }

                            this.container.style[dir?'top':'left']=this.startPos+offset+'px';

                            this.fire('dragMove',ev);

                            this._offset=offset;

                            ev.preventDefault();

                        }

                    }

                    break;



                case 1:

                case 3:

                    if(canDrag){

                        var self=this,

                            index=this.current,

                            type=this.direction?'top':'left',

                            isDrag,offset,tm,nn,sub,curPos,tarPos,myWidth;

                        if(ev.length&&(ev.eventCode==1||this.drag)){

                            nn=ev.target.nodeName.toLowerCase();

                            clearTimeout(this.eventTimer);

                            if(!this.pointerType){

                                this.pointerType=ev.eventType;

                            }

                            this.startPos=parseFloat(getStyle(this.container,type))||0;

                            if(transition){

                                this.container.style[transition]='none';

                            }else if(this.timer){

                                cancelFrame(this.timer);

                                delete this.timer;

                            }

                            this.rect=[ev.clientX,ev.clientY];

                            this.time=+new Date;

                            this.container.style[type]=this.startPos+'px';

                            if(ev.eventType!='touch' && (nn=='a' || nn=='img')){

                                ev.preventDefault();

                            }

                        }else if(tm=this.time){

                            offset=this._offset||0;

                            isDrag=this.drag;

                            curPos=this.startPos+offset;

                            tarPos=this.getPos(index);



                            each("rect drag time startPos _offset".split(" "),function(prop){

                                delete self[prop];

                            });



                            if(isDrag){

                                sub=offset>0?1:-1;

                                while(sub*(curPos-tarPos)>this.getOuterSize(this.pages[index])/2&&this.pages[index-sub]){

                                    tarPos=this.getPos(index-=sub);

                                }



                                if(Math.abs(offset)>20&&+new Date-tm<500){

                                    index-=sub;

                                }

                                

                                this.fire('dragEnd',ev);

                                ev.preventDefault();

                            }



                            if(curPos!=tarPos){

                                this.slide(index);

                            }



                            this.eventTimer=setTimeout(function(){

                                delete self.pointerType;

                            },30);

                        }

                    }

                    break;



                case 4:

                    if(this.timer){

                        ev.preventDefault();

                    }

                    break;



                case 5:

                    ev.preventDefault();

                    if(this.isStatic() && +new Date-this.latestTime>Math.max(1000-this.duration,0)){

                        var wd=ev.wheelDelta||-ev.detail;

                        Math.abs(wd)>=3 && this[wd>0?'prev':'next']();

                    }

                    break;



                case 6:

                    var nn=ev.target.nodeName.toLowerCase();

                    if(this.isStatic() && nn!='input' && nn!='textarea' && nn!='select'){

                        switch(ev.keyCode||ev.which){

                            case 33:

                            case 37:

                            case 38:

                                this.prev();

                                break;

                            case 32:

                            case 34:

                            case 39:

                            case 40:

                                this.next();

                                break;

                            case 35:

                                this.slide(this.length-1);

                                break;

                            case 36:

                                this.slide(0);

                                break;

                        }

                    }

                    break;



                case 7:

                    this.resize();

                    break;



                case 8:

                    if(oldEvent.propertyName==(this.direction?'top':'left')){

                        this.container.style[transition]='none';

                        this.end();

                    }

                    break;

            }

        },

        getSum:function(from,to){

            var sum=0;

            while(from<to){

                sum+=this.getOuterSize(this.pages[from++],true);

            }

            return sum;   

        },

        getPos:function(index){

            var type=this.direction?'Top':'Left',

                myWidth=this.getOuterSize(this.pages[index],true),

                sum=this.getSum(0,index)+this['getMargin'+type+'Size'](this.container)+this['getBorder'+type+'Size'](this.container);



            switch(this.align){

                case 'top':

                case 'left':

                    return -sum;

                case 'bottom':

                case 'right':

                    return this.size-myWidth-sum;

                default:

                    return (this.size-myWidth)/2-sum;

            }

        },

        getOuterSize:function(elem,withMargin){

            return elem[this.direction?'offsetHeight':'offsetWidth']+(withMargin?this.getMarginSize(elem):0);

        },

        getInnerSize:function(elem){

            return this.getOuterSize(elem)-this.getBorderSize(elem);

        },

        getSize:function(elem){

            return elem[this.direction?'offsetHeight':'offsetWidth']-this.getPaddingSize(elem)-this.getBorderSize(elem);

        },

        destroy:function(){

            var pageData=this.pageData;



            offListener(this.container,STARTEVENT.join(" ")+" click"+(this.mousewheel?" mousewheel DOMMouseScroll":""),this.handler);

            offListener(DOC,MOVEEVENT.join(" ")+(this.arrowkey?" keydown":""),this.handler);

            offListener(ROOT,'resize',this.handler);



            each(this.pages,function(page,index){

                page.style.cssText=pageData[index].cssText;

            });



            this.container.style.cssText=pageData.container;

            this.container.removeChild(this.comment);

            this.length=0;

            

            return this.pause();

        },

        refresh:function(){

            this.pages=children(this.container);

            this.length=this.pages.length;

            this.current=Math.max(Math.min(this.length-1,this.current),0);

            return this.resize();

        },

        append:function(elem,index){

            if(null==index){

                index=this.pages.length;

            }

            this.pageData.splice(index,0,{

                cssText:elem.style.cssText||''

            });

            this.container.insertBefore(elem,this.pages[index]||null);



            return this.refresh();

        },

        prepend:function(elem){

            return this.append(elem,0);

        },

        insertBefore:function(elem,index){

            return this.append(elem,index-1);

        },

        insertAfter:function(elem,index){

            return this.append(elem,index+1);

        },

        remove:function(index){

            this.container.removeChild(this.pages[index]);

            this.pageData.splice(index,1);



            return this.refresh();

        }

    }





    each("margin padding border".split(" "),function(type){

        each("Top Left Right Bottom".split(" "),function(dir){

            var prop=type+dir;

            struct.prototype[camelCase('get-'+prop)+'Size']=function(elem){

                return parseFloat(getStyle(elem,prop+(type=='border'?'Width':'')))||0;

            }

        });

        struct.prototype[camelCase('get-'+type)+'Size']=function(elem){

            return this[camelCase('get-'+type)+(this.direction?'Top':'Left')+'Size'](elem)+this[camelCase('get-'+type)+(this.direction?'Bottom':'Right')+'Size'](elem);

        }

    });



    ROOT.TouchSlider=struct;



})(window, function(wrap,config){

    if(!(this instanceof arguments.callee)){

        return new arguments.callee(wrap,config);

    }



    this.container=typeof wrap=='string'?document.getElementById(wrap):wrap;

    this.init(config||{});

});