jsLibrary.js

以前看犀牛书收藏和组合别人的库。

; (function () {
    'use strict';

    if (!Date.now)
        Date.now = function () { return new Date().getTime(); };

    var vendors = ['webkit', 'moz'];
    for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {
        var vp = vendors[i];
        window.requestAnimationFrame = window[vp + 'RequestAnimationFrame'];
        window.cancelAnimationFrame = (window[vp + 'CancelAnimationFrame']
                                   || window[vp + 'CancelRequestAnimationFrame']);
    }
    if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) // iOS6 is buggy
        || !window.requestAnimationFrame || !window.cancelAnimationFrame) {
        var lastTime = 0;
        window.requestAnimationFrame = function (callback) {
            var now = Date.now();
            var nextTime = Math.max(lastTime + 16, now);
            return setTimeout(function () { callback(lastTime = nextTime); },
                              nextTime - now);
        };
        window.cancelAnimationFrame = clearTimeout;
    }
}());

; (function(win, doc) {

     function JSLibrary(arg) {

         //用来保存选中的元素, elements是一个真正的数组, 不是HTMLCollection对象
         this.elements = [];

         switch (typeof arg) {
             case 'function':
                 _methodSets.ready(arg);
                 break;
             case 'string':
                 switch (arg.charAt(0)) {
                     case '#': //ID
                         var obj = doc.querySelector(arg);
                         this.elements.push(obj);
                         break;
                     case '.': //class
                         this.elements = _methodSets.getByClass(doc, arg.substring(1));
                         break;
                     default: //tagName
                         this.elements = _methodSets.convertToArray(doc.getElementsByTagName(arg));
                 }
                 break;
             case 'object':
                 this.elements.push(arg);
         }
     };

    // 用来存放内部调用的方法
     JSLibrary.methodSets = {      

        //文档加载完毕执行js脚本
         ready: (function() {
            var funcs = []; //当获得事件时,要运行的函数
            var bReady = false; //当触发事件处理程序时,切换到true

            //当文档准备就绪时,调用事件处理程序
            function handler(e) {
                //如果已经运行过一次, 只需要返回
                if (bReady) return;

                //如果发生readystatechange事件,但是其状态不是'complete'的话, 那么文档尚未准备好
                if (e.type === 'readystatechange' && doc.readyState !== 'complete') return;

                //运行所有注册函数, 注意每次都要计算funcs.length, 以防止这些函数的调用可能会导致注册更多的函数
                for (var i = 0; i < funcs.length; i++) {
                    funcs[i].call(doc);
                }

                //现在设置ready标识为true, 并移除所有函数
                bReady = true;
                funcs = null;
            }

            //为接收到任何事件注册处理程序
            if (doc.addEventListener) {
                doc.addEventListener('DomContentLoaded', handler, false);
                doc.addEventListener('readystatechange', handler, false);
                win.addEventListener('load', handler, false);

            } else if (doc.attachEvent) {
                doc.attachEvent('onreadystatechange', handler);
                win.attachEvent('onload', handler);
            }

            //返回whenReady()函数
            return function whenReady(f) {
                if (bReady) f.call(doc); //若准备完毕, 只需要运行它
                else funcs.push(f); //否则, 加入列队等候
            }
        })(),

        //把NodeList数组对象转换成数组
        convertToArray: function(nodes) { 
            var arrayOfNodes = null;

            try {
                arrayOfNodes = Array.prototype.slice.call(nodes, 0);
            } catch (ex) { //兼容ie8及以前的版本
                arrayOfNodes = new Array();
                for (var i = 0; i < nodes.length; i++) {
                    arrayOfNodes.push(nodes[i]);
                }

            };

            return arrayOfNodes;
        },

         myAddEvent: function(obj, sEv, fn) {
             if (obj.attachEvent) {
                 obj.attachEvent('on' + sEv, function() {
                     if (false == fn.call(obj)) {
                         event.cancelBubble = true;
                         return false;
                     }
                 });
             } else {
                 obj.addEventListener(sEv, function(ev) {
                     if (false == fn.call(obj)) {
                         ev.cancelBubble = true;
                         ev.preventDefault();
                     }
                 }, false);
             }
         },

         getByClass: function(oParent, sClass) {
             var aEle = oParent.getElementsByTagName('*');
             var aResult = [];
             var i = 0;

             for (i = 0; i < aEle.length; i++) {
                 if (aEle[i].className == sClass) {
                     aResult.push(aEle[i]);
                 }
             }

             return aResult;
         },

         getStyle: function(obj, attr) {
             if (obj.currentStyle) {
                 return obj.currentStyle[attr];
             } else {
                 return getComputedStyle(obj, false)[attr];
             }
         }
         
     };

     var _methodSets = JSLibrary.methodSets;


     /********************* 以下JSLibrary 原型方法   开始 ************************/
     JSLibrary.prototype = {
         constructor: JSLibrary,

        /**
         *  这个点击事件是针对传统pc的
         *  手机端使用tap事件
         */
         click: function(callback) {

             this.elements.forEach(function(item) {
                 _methodSets.myAddEvent(item, 'click', callback);
             });

             return this;
         },

         _setEvent: function(evtName, callback) {

            var oFinger = null, currentDom = null;
            
            for (var i = 0, len = this.elements.length; i < len; i += 1) {
                currentDom = this.elements[i];
                if (currentDom.oFinger) {

                    currentDom.oFinger.on(evtName, callback);
                } else {

                    oFinger = new jsLib.Finger(currentDom, {});
                    oFinger.on(evtName, callback);
                    currentDom.oFinger = oFinger;
                };
            };
        },

        _removeEvent: function(evtName, callback) {

            var currentDom = null;

            for (var i = 0, len = this.elements.length; i < len; i += 1) {
                currentDom = this.elements[i];
                if (currentDom.oFinger) {
                    currentDom.oFinger.off(evtName, callback);
                };
            };
        },

        /**
         *  移动端事件
         *  @use
         *
            var aLi = jsLib('#ul1').find('li').swipe(swipe);
            function swipe(ev) {
                console.log(ev.direction);
            }
            aLi.tap(tap);
            function tap(ev) {
                console.log('tap');
                jsLib(ev.target).removeSwipe(swipe);
            }
            aLi.doubleTap(doubleTap);
            function doubleTap(ev) {
                console.log('doubleTap');
                jsLib(ev.target).removeTap(tap);
            };
            aLi.longTap(longTap);
            function longTap(ev) {
                console.log('longTap');
                jsLib(ev.target).removeDTap(doubleTap);
            };

            aLi.destroy();    //销毁dom上的一切事件
         */

        tap: function(callback) {

            this._setEvent('tap', callback);

            return this;
        },

        removeTap: function(callback) {

            this._removeEvent('tap', callback);

            return this;
        },


        swipe: function(callback) {
            // console.log(ev.direction);

            this._setEvent('swipe', callback);

            return this;
        },

        removeSwipe: function(callback) {

            this._removeEvent('swipe', callback);

            return this;
        },

        
        longTap: function(callback) {

            this._setEvent('longTap', callback);

            return this;
        },

        removeLTap: function(callback) {

            this._removeEvent('longTap', callback);

            return this;
        },

        doubleTap: function(callback) {

            this._setEvent('doubleTap', callback);

            return this;
        },

        removeDTap: function(callback) {

            this._removeEvent('doubleTap', callback);

            return this;
        },

        destroy: function() {

            var currentDom = null;

            for (var i = 0, len = this.elements.length; i < len; i += 1) {
                currentDom = this.elements[i];
                if (currentDom.oFinger) {
                    currentDom.oFinger.destroy();
                };
            };

            return this;
        },

        transform: function() {

            for (var i = 0, len = this.elements.length; i < len; i += 1) {
                jsLib.Transform(this.elements[i]);
            };

            return this.toDom();
        },

        /*
         * 据父节点查找其子孩子
         * @param { String } 可以是标签名或.calss名
         */
         find: function(str) {
             var i = 0;
             var aResult = [];

             for (i = 0; i < this.elements.length; i++) {
                 switch (str.charAt(0)) {
                     case '.': //class
                         var aEle = _methodSets.getByClass(this.elements[i], str.substring(1));

                         aResult = aResult.concat(aEle);
                         break;
                     default: //标签
                         var aEle = this.elements[i].getElementsByTagName(str);

                         aResult = _methodSets.convertToArray(aEle);
                 }
             }

             var newJSLib = jsLib();

             newJSLib.elements = aResult;

             return newJSLib;
         },

         /*
          * 获取点击时它在其兄弟节点的索引位置
          */
         index: function() {
             var _this = this;
             return (function(_this) {
                 var obj = _this.elements[0];
                 var aBrother = obj.parentNode.children;
                 var i = 0;

                 for (i = 0; i < aBrother.length; i++) {
                     if (aBrother[i] == obj) {
                         return i;
                     }
                 }
             }(_this));
         },

         /*
          * 获取第几个dom对象(含原型方法)
          * @param { Number } 从0开始
          */
         eq: function(n) {
             return jsLib(this.elements[n]);
         },

         /*
          * 返回元素的length长度
          */
         length: function() {
             return this.elements.length;
         },

         /*
          * 单纯地获取dom对象(不含原型方法)
          * @param { Number } 从0开始
          */
         toDom: function() {
             if (this.elements.length === 1) {
                 return this.elements[0];
             } else {
                 return this.elements;
             }
         },

         show: function() {
             this.elements.forEach(function(item) {
                 item.style.display = 'block';
             });
         },

         hide: function() {
             this.elements.forEach(function(item) {
                 item.style.display = 'none';
             });
         },

         /*
          * 给节点元素添加样式(多个或者单个)
          * @param { Object } { '100px', height: '100px', background: '#ccc', opacity: 30}
          * @use: setStyle([oDiv,oDiv2], { '100px', height: '100px', background: '#ccc', opacity: 30});
          * @use: setStyle(oDiv, { 100, height: 100, background: '#ccc', opacity: 30});
          */
         setStyle: function(json) {
             (function setDomStyle(obj, json) {
                 if (obj.length) { //对象数组

                     // for (var i = 0; i < obj.length; i++) arguments.callee(obj[i], json);
                     for (var i = 0; i < obj.length; i++) setDomStyle(obj[i], json);

                 } else {
                     if (arguments.length == 2) {

                         // for (var attr in json) arguments.callee(obj, attr, json[attr]);
                         for (var attr in json) setDomStyle(obj, attr, json[attr]);

                     } else {
                         switch (arguments[1].toLowerCase()) {
                             case 'opacity':
                                 obj.style.filter = 'alpha(opacity:' + arguments[2] + ')';
                                 obj.style.opacity = arguments[2] / 100;
                                 break;
                             default:
                                 if (typeof arguments[2] == 'number') {
                                     obj.style[arguments[1]] = arguments[2] + 'px';
                                 } else {
                                     obj.style[arguments[1]] = arguments[2];
                                 }
                                 break;
                         }
                     }
                 }
             })(this.elements, json);
         },

        /*
         * 获取/设置dom样式
         * @param { String } 样式名
         * @param { String } 样式值
         */
         css: function(attr, value) {
            if (arguments.length == 2) {    //设置样式
                var i = 0;

                for (i = 0; i < this.elements.length; i++) {
                    this.elements[i].style[attr] = value;
                }
            } else {    //获取样式
                if (typeof attr == 'string') {
                    return _methodSets.getStyle(this.elements[0], attr);
                } else {
                    for (i = 0; i < this.elements.length; i++) {
                        var k = '';

                        for (k in attr) {
                            this.elements[i].style[k] = attr[k];
                        }
                    }
                }
            }

            return this;
         },

         /*
          * 获取/设置dom属性
          * @param { String } dom属性名
          * @param { String } dom属性值
          */
         attr: function(attr, value) {
             if (arguments.length == 2) {
                 var i = 0;

                 for (i = 0; i < this.elements.length; i++) {
                     this.elements[i][attr] = value;
                 }
             } else {
                 return this.elements[0][attr];
             }

             return this;
         },

         removeAttr: function(name) {
             this.elements.forEach(function(item) {
                 item.removeAttribute(name);
             });

             return this;
         },

         /*
          * 给元素节点设置Css3样式
          * @param { String } name 属性名
          * @param { String } value 属性值
          */
         setStyle3: function(name, value) {
             this.elements.forEach(function(item) {
                 item.style['Webkit' + name.charAt(0).toUpperCase() + name.substring(1)] = value;
                 item.style['Moz' + name.charAt(0).toUpperCase() + name.substring(1)] = value;
                 item.style['ms' + name.charAt(0).toUpperCase() + name.substring(1)] = value;
                 item.style['O' + name.charAt(0).toUpperCase() + name.substring(1)] = value;
                 item.style[name] = value;
             });
         },

         /*
          * 给元素节点添加class
          * @param { String } sClass class名
          */
         addClass: function(sClass) {
             var re = new RegExp('\b' + sClass + '\b');
            
            this.elements.forEach(function(item) {
                if (re.test(item.className)) return;
                item.className = (item.className + ' ' + sClass).match(/S+/g).join(' ');
            });

            return this;
         },

         /*
          * 移除某元素节点的class
          * @param { String } sClass class名
          */
         removeClass: function(sClass) {
             var re = new RegExp('\b' + sClass + '\b', 'g');

             this.elements.forEach(function(item) {
                 item.className = item.className.replace(re, '').replace(/^s+|s+$/g, '').replace(/s+/g, ' ');
             });

            return this;
         },

         /*
          * 设置dom文本
          */
         html: function(str) {
            this.elements.forEach(function(item) {
                item.innerHTML = str;
            });
         },

        /*
         * 扩张方法,把一些好的方法扩张到JSLibrary原型上
         * @param { String } 方法名
         * @param { Function } 函数
         */
         extend: function(name, fn) {
             JSLibrary.prototype[name] = fn;
         },

        /**
         * [功能: 返回元素e的第几层祖先元素, 如果不存在此类祖先或祖先不是Element,
         *  则返回NUll]
         * @param  {Object Dom} e 指定的元素
         * @param  {Number} n 第n层祖先元素
         * @return {Object Dom}   返回其祖父元素
         */
        parent: function(n) {
            if (this.elements.length > 1) return;    //多个元素直接返回
            var e = this.elements[0];

            if (n === undefined) n = 1;
            while(n -- && e)
                e = e.parentNode;
            if (!e || e.nodeType !== 1)
                return null;

            // return e;
            return jsLib(e);
        },

        /**
         * [功能: 返回元素e的第几个兄弟元素, n为正,返回后续的第n个兄弟元素,
         *  n为负,返回前面的第n个兄弟元素, n为0, 返回e本身]
         * @param  {Object Dom} e 指定的元素
         * @param  {Number} n 第几个兄弟节点
         * @return {Object Dom}   返回第几个兄弟节点
         */
        sibling: function(n) {
            if (this.elements.length > 1) return;    //多个元素直接返回
            var e = this.elements[0];

            while(e && n !== 0) {    //如果e未定义, 立即返回它

                if (n > 0) {    //查找后续的兄弟元素
                    if (e.nextElementSibling) {
                        e = e.nextElementSibling;
                    } else {
                        for (e = e.nextSibling; e && e.nodeType !== 1; e = e.nextSibling)
                            /* 空循环 */;
                    };
                    n --;
                } else {    //查找前面的兄弟元素
                    if (e.previousElementSibing) {
                        e = e.previousElementSibing;
                    } else {
                        for (e = e.previousSibling; e && e.nodeType !== 1; e = e.previousSibling)
                            /* 空循环 */;
                    };
                    n ++;
                }
            }

            // return e;
            return jsLib(e);
        },

        /**
         * [功能: 返回元素e的第n代子元素,如果不存在则为NUll,
         * 负值n代表从后往前计数. 0表示第一个子元素, -1代表最后一个, -2代表倒数第二个,以此类推.(和children功能一样, 从0开始)]
         * @param  {Object Dom} e 指定的元素
         * @param  {Number} n 第几个
         * @return {Object Dom}   返回第n代子元素
         */
        child: function(n) {
            if (this.elements.length > 1) return;    //多个元素直接返回
            var e = this.elements[0];
            if (!e) return;

            if (e.children) {                   // 如果children数组存在
                if (n < 0)                      // 转换负的n为数组索引
                    n += e.children.length;     
                if (n < 0)                      // 如果它仍然为负, 说明没有子元素
                    return null;
                // return e.children[n];           //返回指定的子元素
                return jsLib(e.children[n]);           //返回指定的子元素
            }

            //如果e没有children数组, 找到第一个子元素并向前数, 或找到最后一个子元素并往回数
            if (n >= 0) {
                //找到元素的第一个子元素
                if (e.firstElementChild) {
                    e = e.firstElementChild;
                } else {
                    for (e = e.firstChild; e && e.nodeType !== 1; e = e.nextSibling)
                        /* 空循环 */;
                }
                // return this.sibling(e, n);    //返回第一个子元素的第n个兄弟元素
                return jsLib(this.sibling(e, n));    //返回第一个子元素的第n个兄弟元素
            } else {    // n为负数
                if (e.lastElementChild) {
                    e.lastElementChild;
                } else {
                    for (e = e.lastChild; e && e.nodeType !== 1; e = e.previousSibling)
                        /* 空循环 */;
                }
                // return this.sibling(e, n+1);
                return jsLib(this.sibling(e, n+1));
            }
        }
     };

     /********************* 以上JSLibrary 原型方法   结束 ************************/


     /************** 以下是一些常用的方法挂在到jsLib(类方法)   开始 **************/


     /**
      *  一些实用工具方法挂载到jsLib.utils
      */

     jsLib.utils = {};
    
    /**
     *  检测一个值是否是NaN 
     */
    jsLib.utils.isReallyNaN = function(x) {
        return x !== x;
    };

    /**
     *  检测一个对象是否为空
     */
    jsLib.utils.isEmptyObj = function(obj){
        for(var i in obj){
            if(obj.hasOwnProperty(i)){
                return false;
            }
        }
        return true;
    };

    /**
     *  检测一个对象是否为数组
     */
    jsLib.utils.isArray = function (arr) {
        if (Array.isArray) {
            return Array.isArray(arr);
        } else {
            return Object.prototype.toString.call(arg) === '[object Array]';
        }
    };

    /**
     *
     * @param parent    要拷贝的对象
     * @param child    返回浅拷贝的对象
     *
     * 如果parent对象中属性也是对象或者数组,那么浅拷贝的对象是引用parent这个对象
     */
    jsLib.utils.extend = function(parent, child) {
        var i;
        child = child || {};
        for (i in parent) {
            if (parent.hasOwnProperty(i)) {
                child[i] = parent[i];
            };
        };
        return child;
    };

    /**
     *
     * @param parent
     * @param child
     * 深拷贝以后,parent对象和child对象就不相等了,都是独立的了
     */
    jsLib.utils.extendDeep = function(parent, child) {
        var i,
            toStr = Object.prototype.toString,
            astr = "[object Array]";

        child = child || {};

        for (i in parent) {
            if (parent.hasOwnProperty(i)) {
                if (typeof parent[i] === 'object') {
                    child[i] = (toStr.call(parent[i]) === astr) ? [] : {};
                    this.extendDeep(parent[i], child[i]);
                } else {
                    child[i] = parent[i];
                };
            };
        };
        return child;
    };

    /**
     *  从数组中随机取几个不重复的元素
     */
    jsLib.utils.getArrayItems = function(arr, num) {
        var temp_array = new Array();
        for (var index in arr) {
            temp_array.push(arr[index]);
        }
        var return_array = new Array();
        for (var i = 0; i<num; i++) {
            if (temp_array.length>0) {
                var arrIndex = Math.floor(Math.random()*temp_array.length);
                return_array[i] = temp_array[arrIndex];
                temp_array.splice(arrIndex, 1);
            } else {
                break;
            };
        };
        return return_array;
    }

    /**
     *  判断点在多边形内
     *  eg:  var pointCenter = {x: gV.box_w/2, y: gV.box_h/2};
     *     var polygon = [
     *         {x:aClientLeftPoint[0], y:aClientLeftPoint[1]},
     *         {x:aClientRightPoint[0], y:aClientRightPoint[1]},
     *         {x:aClientRightunderPoint[0], y:aClientRightunderPoint[1]},
     *         {x:aClientLeftunderPoint[0], y:aClientLeftunderPoint[1]},
     *         {x:aClientLeftPoint[0], y:aClientLeftPoint[1]}
     *     ];
     *   jsLib.utils.pointInPolygon(pointCenter, polygon);
     */
    jsLib.utils.pointInPolygon = function(curPoint, points) {
        var counter = 0;
        for (var i = 0, p1, p2; i < points.length; i++) {
            p1 = points[i];
            p2 = points[(i + 1) % points.length];
            if (p1.y == p2.y) {
                continue;
            }
            if (curPoint.y <= Math.min(p1.y, p2.y)) {
                continue;
            }
            if (curPoint.y >= Math.max(p1.y, p2.y)) {
                continue;
            }
            var x = (curPoint.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x;
            if (x > curPoint.x) counter++;
        };

        if (counter % 2 == 0) {
            return false;
        } else {
            return true;
        };
    };

    /**
     * [功能: 作为一个对象的w和h属性返回视口的尺寸(视口坐标)]
     * @param  {Object} w 指定的窗口
     * @return {Object}   { x: 屏幕的宽度, y: 屏幕的高度 }
     */
    jsLib.utils.getViewportSize = function(w) {
        //使用指定的窗口, 如果不带参数则使用当前窗口
        var w = w || window;

        //出了ie8及更早的版本以外, 其他浏览器都能用
        if (w.innerWidth != null)
            return { w: w.innerWidth, h: w.innerHeight };

        //对标准下的ie (或任何浏览器)
        var d = w.document;
        if (document.compatMode == 'CSS1Compat')
            return {
                w: d.documentElement.clientWidth,
                h: d.documentElement.clientHeight
            };

        //对怪异模式下的浏览器
        return { w: d.body.clientWidth, h: d.body.clientHeight };
    };

    /**
     * [功能: 查询窗口滚动条的位置]
     * @param  {Object} w 指定的窗口
     * @return {Object}   { x: 滚动条的x, y: 滚动条的y }
     */
    jsLib.utils.getScrollOffset = function(w) {
        //使用指定的窗口, 如果不带参数则使用当前窗口
        var w = w || window;

        //除了ie8及更早的版本, 其他浏览器都能用
        if (w.pageXOffset != null)
            return { x: w.pageXOffset, y: w.pageYOffset }

        //对于标准下的ie(或任意浏览器)
        var d = w.document;
        if (document.compatMode == 'CSS1Compat')
            return { x: d.documentElement.scrollLeft, y: d.documentElement.scrollTop }

        //对于怪异模式下的浏览器
        return { x: d.body.scrollLeft, y: d.body.scrollTop }
    };


    /**
     * [功能: 文档如果有滚动条的话,就不行了(文档坐标,含滚动条)]
     * @param  {Object Dom} e dom节点
     * @return {Object}   返回节点坐标(含滚动条)
     */
    jsLib.utils.getElementPosition = function(e) {
        var x = 0,
            y = 0;
        while(e != null) {
            x += e.offsetLeft;
            y += e.offsetTop;
            e = e.offsetParent;
        }

        return { x: x, y: y };
    };

    /**
     * [功能: 增强版(视口坐标)  与 dom.getBoundingClientRect()对象中left和top相等,并且getBoundingClientRect方法效率高]
     * @param  {Object Dom} elt dom节点
     * @return {Object}     返回节点坐标(不含滚动条)
     */
    jsLib.utils.getElementPos = function(elt) {
        var x = 0, y = 0;

        //循环以累加偏移量
        for (var e = elt; e != null; e = e.offsetParent) {
            x += e.offsetLeft;
            y += e.offsetTop;
        }

        //在此循环所有的祖先元素,减去滚动的偏移量
        //这也减去了主滚动条, 并转化为视口坐标
        for (var e = elt.parentNode; e != null && e.nodeType == 1; e = e.parentNode) {
            x -= e.scrollLeft;
            y -= e.scrollTop;
        }

        return { x: x, y: y };
    };

    //函数赋值
    jsLib.utils.getByClass = _methodSets.getByClass;
    jsLib.utils.convertToArray = _methodSets.convertToArray;


     /*
      * 仅仅简单的dom Id选择器
      */
     jsLib.getEle = function(id) {
         return doc.querySelector(id);
     };

      
    /*  ajax 的封装, 含跨域 jsonp
     *
      *  参数         默认值              描述                  可选值
     *    url             “”                 请求的链接              string
     *    type         get             请求的方法              get,post
     *    data         null             请求的数据              object,string
     *    contentType     “”                 请求头                  string
     *    dataType     “”                 请求的类型              jsonp
     *    async         true             是否异步              blooean
     *    timeOut         undefined         超时时间              number
     *    before         function(){}     发送之前执行的函数      function
     *    error         function(){}     请求报错执行的函数      function
     *    success         function(){}     请求成功的回调函数      function
     *
     *    @use
     *    ajax({
     *        type:"post",
     *        dataType: 'jsonp',
     *        url:"http://wx.indoorun.com/wx/getUnitsOfFloor.html", //添加自己的接口链接
     *        data: {'regionId':'14428254382730015', 'floorId':'14428254382890016'},
     *        timeOut:5000,
     *        before:function(){
     *          console.log("before");  
     *        },
     *        success:function(str){
     *            console.log(str);
     *        },
     *        error:function(){
     *            console.log("error");
     *        }
     *    });
      */
     jsLib.ajax = function(options) {
         //编码数据
         function setData() {
             var name, value;
             if (data) {
                 if (typeof data === "string") {
                     data = data.split("&");
                     for (var i = 0, len = data.length; i < len; i++) {
                         name = data[i].split("=")[0];
                         value = data[i].split("=")[1];
                         data[i] = encodeURIComponent(name) + "=" + encodeURIComponent(value);
                     }
                     data = data.replace("/%20/g", "+");
                 } else if (typeof data === "object") {
                     var arr = [];
                     for (var name in data) {
                         if (typeof data[name] !== 'undefined') {
                            var value = data[name].toString();
                            name = encodeURIComponent(name);
                            value = encodeURIComponent(value);
                            arr.push(name + "=" + value);
                        }

                     }
                     data = arr.join("&").replace("/%20/g", "+");
                 }
                 //若是使用get方法或JSONP,则手动添加到URL中
                 if (type === "get" || dataType === "jsonp") {
                     url += url.indexOf("?") > -1 ? (url.indexOf("=")>-1 ? "&"+data : data ): "?" + data;
                 }
             }
         }
         // JSONP
         function createJsonp() {

             var script = document.createElement("script"),
                 timeName = new Date().getTime() + Math.round(Math.random() * 1000),
                 callback = "JSONP_" + timeName;

             window[callback] = function(data) {
                 clearTimeout(timeout_flag);
                 document.body.removeChild(script);
                 success(data);
             }
             script.src = url +  (url.indexOf("?") > -1 ? "&" : "?") + "callback=" + callback;
             script.type = "text/javascript";
             document.body.appendChild(script);
             setTime(callback, script);
         }
         //设置请求超时
         function setTime(callback, script) {
             if (timeOut !== undefined) {
                 timeout_flag = setTimeout(function() {
                     if (dataType === "jsonp") {
                         // delete window[callback];
                         document.body.removeChild(script);

                     } else {
                         timeout_bool = true;
                         xhr && xhr.abort();
                     }
                     console.log("timeout");

                 }, timeOut);
             }
         }
         // XHR
         function createXHR() {
             //由于IE6的XMLHttpRequest对象是通过MSXML库中的一个ActiveX对象实现的。
             //所以创建XHR对象,需要在这里做兼容处理。
             function getXHR() {
                 if (window.XMLHttpRequest) {
                     return new XMLHttpRequest();
                 } else {
                     //遍历IE中不同版本的ActiveX对象
                     var versions = ["Microsoft", "msxm3", "msxml2", "msxml1"];
                     for (var i = 0; i < versions.length; i++) {
                         try {
                             var version = versions[i] + ".XMLHTTP";
                             return new ActiveXObject(version);
                         } catch (e) {}
                     }
                 }
             }
             //创建对象。
             xhr = getXHR();
             xhr.open(type, url, async);
             //设置请求头
             if (type === "post" && !contentType) {
                 //若是post提交,则设置content-Type 为application/x-www-four-urlencoded
                 xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
             } else if (contentType) {
                 xhr.setRequestHeader("Content-Type", contentType);
             }
             //添加监听
             xhr.onreadystatechange = function() {
                 if (xhr.readyState === 4) {
                     if (timeOut !== undefined) {
                         //由于执行abort()方法后,有可能触发onreadystatechange事件,
                         //所以设置一个timeout_bool标识,来忽略中止触发的事件。
                         if (timeout_bool) {
                             return;
                         }
                         clearTimeout(timeout_flag);
                     }
                     if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {

                         success(xhr.responseText);
                     } else {
                          error(xhr.status, xhr.statusText);
                     }
                 }
             };
             //发送请求
             xhr.send(type === "get" ? null : data);
             setTime(); //请求超时
         }

         var url = options.url || "", //请求的链接
             type = (options.type || "get").toLowerCase(), //请求的方法,默认为get
             data = options.data || null, //请求的数据
             contentType = options.contentType || "", //请求头
             dataType = options.dataType || "", //请求的类型
             async = options.async === undefined && true, //是否异步,默认为true.
             timeOut = options.timeOut, //超时时间。 
             before = options.before || function() {}, //发送之前执行的函数
             error = options.error || function() {}, //错误执行的函数
             success = options.success || function() {}; //请求成功的回调函数
         var timeout_bool = false, //是否请求超时
             timeout_flag = null, //超时标识
             xhr = null; //xhr对角
         setData();
         before();
         if (dataType === "jsonp") {
             createJsonp();
         } else {
             createXHR();
         }
     };

     /*
      * 把url后面的参数放入到一个对象中去
      * 返回这个对象
      */
     jsLib.getQueryString = function() {

         var str = location.search.length > 0 ? location.search.substring(1) : "";
         var items = str.length ? str.split("&") : [];

         var args = {}, item = null, name = null, value = null;

         for (var i = 0, len = items.length; i < len; i++) {
             item = items[i].split("=");
             name = decodeURIComponent(item[0]);
             value = decodeURIComponent(item[1]);
             if (name.length) {
                 args[name] = value;
             }
         };

         return args;
     };

     /*
      * 绑定和解绑事件的方法
      */
     jsLib.EventUtil = {

         //事件绑定  EventUtil.addHandler()
         addHandler: function(element, type, handler) { //要绑定的元素, 事件类型, 发生事件的函数
             if (element.addEventListener) {
                 element.addEventListener(type, handler, false); // false为事件冒泡 (w3c标准下)
             } else if (element.attachEvent) {
                 element.attachEvent('on' + type, handler); //  只有事件冒泡 (ie下)
             } else {
                 element['on' + type] = handler;
             }
         },

         //事件移除 
         removeHandler: function(element, type, handler) {
             if (element.removeEventListener) {
                 element.removeEventListener(type, handler, false);
             } else if (element.detachEvent) {
                 element.detachEvent('on' + type, handler);
             } else {
                 element['on' + type] = null;
             }
         },

         //获取事件对象 
         getEvent: function(event) {
             return event ? event : win.event;
         },

         //获取事件目标 
         getTarget: function(event) {
             var oEvent = jsLib.EventUtil.getEvent(event);
             return oEvent.target || oEvent.srcElement; //标准或ie下
         },

         //取消默认事件 
         preventDefault: function(event) {
             var oEvent = jsLib.EventUtil.getEvent(event);
             oEvent.preventDefault ? oEvent.preventDefault() : oEvent.returnValue = false;
         },

         //阻止事件冒泡和事件捕获 
         stopPropagation: function(event) {
             var oEvent = jsLib.EventUtil.getEvent(event);
             oEvent.stopPropagation ? oEvent.stopPropagation() : oEvent.cancelBubble = true;
         }
     };

     /************** 以上是一些常用的方法挂在到jsLib(类方法)   结束 **************/




     /************** 以下一些方法(插件)扩展到JSLibrary原型上    开始 **************/

     /*
      * 任意dom节点运动方法
      * @param { Object } 运动的属性
      * @param { Function } 回调函数
      * @user jsLib('#id').animate({left: '200', top: '200', 'opacity': 30});
      */
    jsLib().extend('animate', function(json, fn, time) {

        var time = time || 30;

        this.elements.forEach(function(item, index, array) {
            startMove(item, json, fn);
        });

        function getStyle(obj, attr) {
            if (obj.currentStyle) {
                return obj.currentStyle[attr];
            } else {
                return getComputedStyle(obj, false)[attr];
            }
        }
        //运动
        function startMove(obj, json, fn) {
            clearInterval(obj.timer);
            obj.timer = setInterval(function() {
                var attr = '';
                var iStop = true; //假设所有值都到达了,定时器里一轮的运动结束了
                for (attr in json) {
                    //1.计算当前值
                    var iCurr = 0;
                    if (attr == 'opacity') {
                        iCurr = parseInt(parseFloat(getStyle(obj, attr)) * 100);
                    } else {
                        iCurr = parseInt(getStyle(obj, attr));
                    }
                    //2.计算速度
                    var speed = (json[attr] - iCurr) / 8;
                    speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
                    //3.检测停止
                    if (iCurr != json[attr]) {
                        iStop = false;
                    };
                    if (attr == 'opacity') {
                        obj.style.opacity = (iCurr + speed) / 100;
                        obj.style.filter = 'alpha(opacity:' + (iCurr + speed) + ')';
                    } else {
                        obj.style[attr] = iCurr + speed + 'px';
                    };
                };
                if (iStop) { //所有属性都到达了目标,那就关闭定时器
                    clearInterval(obj.timer);
                    fn && fn();
                };
            }, time);
        }
    });


    /**
     * 任意运动, 传入回调函数, 分批执行 
     * @return {_stopMove} 可以停止运动
     * 可以单独使用
     * @user 
     * var stop = new Move.elastic([0, 1], 800, function(v){
     *     console.log(v);    // v是速度在 0,1之间以各种运动变化, 每变化一次就执行一次函数
     * }
     *
     * stop();  //通过调用,可以立即停止
     */
    ; (function(exports) {

        var PI = Math.PI,
            sin = Math.sin,
            cos = Math.cos,
            pow = Math.pow,
            abs = Math.abs,
            sqrt = Math.sqrt;

        var request = window.requestAnimationFrame,
            stopRequest = window.cancelAnimationFrame;
        var _move, _stopMove;    // 都是函数, 不支持requestAnimationFrame的浏览器就使用定时器

        //初始化运动函数和停止函数  
        if (request) {
            _move = function(fn, timer) {    // fn: 匿名函数, timer: 不同的空对象
                var step = function() {
                    if (!fn()) {    // fn函数返回值为假值则调用requestAnimationFrame方法(true代表运动结束)
                        timer.id = request(step);
                    };
                };
                step();    // 函数调用
            };
        } else {
            _move = function(fn, timer) {
                timer.id = setInterval(fn, 16);    // 采用定时器, 时间间隔不能低于16
            };
        };
        if (stopRequest) {
            _stopMove = function(timer) {
                stopRequest(timer.id);    // 停止动画调用
            };
        } else {
            _stopMove = function(timer) {
                clearInterval(timer.id);    // 关闭定时器
            };
        };

        var Move = function() {    // Move构造函数
            this.aCurve = [];    // 曲线动画函数名集合
            this.init();
        };    


        var curve = Move.prototype = {    // Move原型
            // 初始化动画曲线 
            init: function() {
                this.extends({
                    //定义域和值域均为[0, 1], 传入自变量x返回对应值y
                    //先加速后减速
                    ease: function(x) {
                        // return -0.5*cos(PI * (2 - x)) + 0.5;
                        if (x <= 0.5) return 2 * x * x;
                        else if (x > 0.5) return -2 * x * x + 4 * x - 1;
                    },

                    // 初速度为0 ,一直加速
                    easeIn: function(x) {
                        return x * x;
                    },

                    //先慢慢加速1/3, 然后突然大提速, 最后减速
                    ease2: function(x) {
                        return x < 1 / 3 ? x * x : -2 * x * x + 4 * x - 1;
                    },

                    //初速度较大, 一直减速, 缓冲动画
                    easeOut: function(x) {
                        return pow(x, 0.8);
                    },

                    //碰撞动画
                    collision: function(x) {
                        var a, b; //a, b代表碰撞点的横坐标
                        for (var i = 1, m = 20; i < m; i++) {
                            a = 1 - (4 / 3) * pow(0.5, i - 1);
                            b = 1 - (4 / 3) * pow(0.5, i);
                            if (x >= a && x <= b) {
                                return pow(3 * (x - (a + b) / 2), 2) + 1 - pow(0.25, i - 1);
                            }
                        }
                    },

                    //弹性动画
                    elastic: function(x) {
                        return -pow(1 / 12, x) * cos(PI * 2.5 * x * x) + 1;
                    },

                    //匀速动画
                    linear: function(x) {
                        return x;
                    },

                    //断断续续加速减速
                    wave: function(x) {
                        return (1 / 12) * sin(5 * PI * x) + x;
                    },

                    //先向反方向移动一小段距离, 然后正方向移动, 并超过终点一小段, 然后回到终点
                    opposite: function(x) {
                        return (sqrt(2) / 2) * sin((3 * PI / 2) * (x - 0.5)) + 0.5;
                    },

                    // 相反的三次贝塞尔
                    reverseEase: function (x) {    
                        return 1 - Math.sqrt(1 - x * x);
                    }
                });
            },

            // 随机选择一个动画方法名
            getRd: function () {
                var preItem = null;

                return function () {
                    var arr = this.aCurve;
                    var index = Math.floor(Math.random() * arr.length),
                        item = arr[index],
                        result;

                    if (preItem != item) {
                        preItem = item;
                        result = item;
                    } else {
                        result = this.getRd(arr);
                    };

                    return result;
                };
            }(),

            // 扩张曲线动画
            extends: function(obj) {
                for (var k in obj) {
                    if (k in curve) {
                        console.warn('扩张的方法名' + k + ': 已经存在, 换个方法名吧!' );
                        return;
                    };
                    this.aCurve.push(k);
                    curve[k] = (function(moveType) {    // 给Move原型添加 动画曲线 方法
                        return function() {
                            return _doMove.call(this, arguments, moveType);    // 每个动画曲线方法实际调用_doMove函数
                        };
                    })(obj[k]);
                };
            }
        };


        /**
         * 开始动画函数
         * arg: 用户要传的([0, 1000], 500, function(v){ ... }, fnEnd)
         * moveType: 曲线动画函数
         */
        function _doMove(arg, moveType) {
            var r,    // r => 过渡范围, 例如[0, 1000]   (必须传, 且传数组)
                d,    // d => 过渡时间, ms,             (可不传, 默认500) 
                fn,    // fn => 每一帧的回调函数, 传入当前过渡值v   (必须传) 
                fnEnd;    // fnEnd => 动画结束时回调               (可不传)    

            // 严格限制传入参数, 且传入的参数可以没有顺序
            for (var i = 0; i < 4; i++) {
                if (typeof arg[i] === 'object' && !r) r = arg[i];
                else if (typeof arg[i] === 'number' && !d) d = arg[i];
                else if (typeof arg[i] === 'function' && !fn) fn = arg[i];
                else if (typeof arg[i] === 'function' && !fnEnd) fnEnd = arg[i];
            };

            if (!r instanceof Array || !fn) return;    // 如果r不是数组或者fn不是函数(真值)就return掉

            d = d || 500;    // 过渡时间默认500ms

            var from = +new Date, //起始时间
                x = 0,    
                y,
                a = r[0],    // 过渡范围的起点
                b = r[1];    // 过度范围的终点

            var timer = 't' + Math.random();    // 随机数

            var self = this;    // 存一下Move的实例

            //用于保存定时器ID的对象, requestAnimation递归调用必须传入对象(给实例添加timer属性值为{})
            this[timer] = {};

            // 优先使用requestAnimationFrame否则setInterval定时器
            _move(function() {
                x = (+new Date - from) / d;

                if (x >= 1) {    // 动画结束
                    fn(b);    // 调用外部动画的回调函数且把过度范围的终点值作为参数传过去
                    if (fnEnd) fnEnd();    // 如果有动画结束回调函数就执行回调函数
                    return true;    // 返回真值停止调用requestAnimationFrame方法
                } else {    // 动画进行中
                    y = moveType(x);    // 调用动画曲线中的函数返回运动数字
                    fn(a + (b - a) * y);    // 调用外部动画的回调函数传参为 a + (b - a) * y
                };
            }, self[timer]);

            return function() {
                _stopMove(self[timer]);    // 调用cancelAnimationFrame方法停止动画
                return a + (b - a) * y;    // 返回动画停止后的运动数字
            };
        };

        // 抛出去
        exports.Move = Move;    // Move构造函数抛出去
    
    })(jsLib);

    ; (function(exports) {
        // 一些要使用的内部工具函数

        // 2点之间的距离 (主要用来算pinch的比例的, 两点之间的距离比值求pinch的scale)
        function getLen(v) {
            return Math.sqrt(v.x * v.x + v.y * v.y);
        };

        // dot和getAngle函数用来算两次手势状态之间的夹角, cross函数用来算方向的, getRotateAngle函数算手势真正的角度的
        function dot(v1, v2) {
            return v1.x * v2.x + v1.y * v2.y;
        };

        // 求两次手势状态之间的夹角
        function getAngle(v1, v2) {
            var mr = getLen(v1) * getLen(v2);
            if (mr === 0) return 0;
            var r = dot(v1, v2) / mr;
            if (r > 1) r = 1;
            return Math.acos(r);
        };

        // 利用cross结果的正负来判断旋转的方向(大于0为逆时针, 小于0为顺时针)
        function cross(v1, v2) {
            return v1.x * v2.y - v2.x * v1.y;
        };

        // 如果cross大于0那就是逆时针对于屏幕是正角,对于第一象限是负角,所以 角度 * -1, 然后角度单位换算
        function getRotateAngle(v1, v2) {
            var angle = getAngle(v1, v2);
            if (cross(v1, v2) > 0) {
                angle *= -1;
            };
            return angle * 180 / Math.PI;
        };

        // HandlerAdmin构造函数
        var HandlerAdmin = function(el) {
            this.handlers = [];    // 手势函数集合
            this.el = el;    // dom元素
        };

        // HandlerAdmin原型方法

        // 把fn添加到实例的 handlers数组中
        HandlerAdmin.prototype.add = function(handler) {
            this.handlers.push(handler); 
        };

        // 删除 handlers数组中的函数
        HandlerAdmin.prototype.del = function(handler) {
            if(!handler) this.handlers = [];    // handler为假值,handlers则赋值为[](参数不传undefined,其实不管this.handlers有没有成员函数,都得置空)

            for(var i = this.handlers.length; i >= 0; i--) {
                if(this.handlers[i] === handler) {    // 如果函数一样
                    this.handlers.splice(i, 1);    // 从handler中移除该函数(改变了原数组)
                };
            };
        };

        // 执行用户的回调函数
        HandlerAdmin.prototype.dispatch = function() {
            for(var i=0, len=this.handlers.length; i<len; i++) {
                var handler = this.handlers[i];    
                if(typeof handler === 'function') handler.apply(this.el, arguments);    // 执行回调this为dom元素, 把触发的事件对象作为参数传过去了
            };
        };

        function wrapFunc(el, handler) {
            var handlerAdmin = new HandlerAdmin(el);    // 实例化一个对象
            handlerAdmin.add(handler);

            return handlerAdmin;
        };

        // AlloyFinger构造函数
        var AlloyFinger = function (el, option) {    // el: dom元素/id, option: 各种手势的集合对象

            this.element = typeof el == 'string' ? document.querySelector(el) : el;    // 获取dom元素

            // 绑定原型上start, move, end, cancel函数的this对象为 AlloyFinger实例
            this.start = this.start.bind(this);
            this.move = this.move.bind(this);
            this.end = this.end.bind(this);
            this.cancel = this.cancel.bind(this);

            // 给dom元素 绑定原生的 touchstart, touchmove, touchend, touchcancel事件, 默认冒泡
            this.element.addEventListener("touchstart", this.start, false);
            this.element.addEventListener("touchmove", this.move, false);
            this.element.addEventListener("touchend", this.end, false);
            this.element.addEventListener("touchcancel", this.cancel, false);

            this.preV = { x: null, y: null };    // 开始前的坐标
            this.pinchStartLen = null;    // start()方法开始时捏的长度
            this.scale = 1;    // 初始缩放比例为1
            this.isDoubleTap = false;    // 是否双击, 默认为false

            var noop = function () { };    // 空函数(把用户没有绑定手势函数默认赋值此函数)

            // 提供了14种手势函数. 根据option对象, 分别创建一个 HandlerAdmin实例 赋值给相应的this属性
            this.rotate = wrapFunc(this.element, option.rotate || noop);
            this.touchStart = wrapFunc(this.element, option.touchStart || noop);
            this.multipointStart = wrapFunc(this.element, option.multipointStart || noop);
            this.multipointEnd = wrapFunc(this.element, option.multipointEnd || noop);
            this.pinch = wrapFunc(this.element, option.pinch || noop);
            this.swipe = wrapFunc(this.element, option.swipe || noop);
            this.tap = wrapFunc(this.element, option.tap || noop);
            this.doubleTap = wrapFunc(this.element, option.doubleTap || noop);
            this.longTap = wrapFunc(this.element, option.longTap || noop);
            this.singleTap = wrapFunc(this.element, option.singleTap || noop);
            this.pressMove = wrapFunc(this.element, option.pressMove || noop);
            this.touchMove = wrapFunc(this.element, option.touchMove || noop);
            this.touchEnd = wrapFunc(this.element, option.touchEnd || noop);
            this.touchCancel = wrapFunc(this.element, option.touchCancel || noop);

            this.delta = null;    // 差值 变量增量
            this.last = null;    // 最后数值
            this.now = null;    // 开始时的时间戳
            this.tapTimeout = null;    // tap超时
            this.singleTapTimeout = null;    // singleTap超时
            this.longTapTimeout = null;    // longTap超时(定时器的返回值)
            this.swipeTimeout = null;    // swipe超时
            this.x1 = this.x2 = this.y1 = this.y2 = null;    // start()时的坐标x1, y1, move()时的坐标x2, y2 (相对于页面的坐标)
            this.preTapPosition = { x: null, y: null };    // 用来保存start()方法时的手指坐标
        };

        // AlloyFinger原型对象
        AlloyFinger.prototype = {

            start: function (evt) {
                if (!evt.touches) return;    // 如果没有TouchList对象, 直接return掉 (touches: 位于屏幕上的所有手指的列表)

                this.now = Date.now();    // 开始时间戳
                this.x1 = evt.touches[0].pageX;    // 相对于页面的 x1, y1 坐标
                this.y1 = evt.touches[0].pageY;
                this.delta = this.now - (this.last || this.now);    // 时间戳差值

                this.touchStart.dispatch(evt);    // 调用HandlerAdmin实例this.touchStart上的dispatch方法(用户的touchStart回调就在此调用的)

                if (this.preTapPosition.x !== null) {    // 开始前tap的x坐标不为空的话(一次没点的时候必然是null了)
                    this.isDoubleTap = (this.delta > 0 && this.delta <= 250 && Math.abs(this.preTapPosition.x - this.x1) < 30 && Math.abs(this.preTapPosition.y - this.y1) < 30);
                };
                this.preTapPosition.x = this.x1;    // 把相对于页面的 x1, y1 坐标赋值给 this.preTapPosition
                this.preTapPosition.y = this.y1;
                this.last = this.now;    // 把开始时间戳赋给 this.last
                var preV = this.preV,    // 把开始前的坐标赋给 preV变量
                    len = evt.touches.length;    // len: 手指的个数

                if (len > 1) {    // 一根手指以上
                    this._cancelLongTap();    // 取消长按定时器
                    this._cancelSingleTap();    // 取消SingleTap定时器

                    var v = {    // 2个手指坐标的差值
                        x: evt.touches[1].pageX - this.x1, 
                        y: evt.touches[1].pageY - this.y1 
                    };
                    preV.x = v.x;    // 差值赋值给PreV对象
                    preV.y = v.y;

                    this.pinchStartLen = getLen(preV);    // start()方法中2点之间的距离
                    this.multipointStart.dispatch(evt);    // (用户的multipointStart回调就在此调用的)
                };

                this.longTapTimeout = setTimeout(function () {
                    this.longTap.dispatch(evt);    // (用户的longTap回调就在此调用的)
                }.bind(this), 750);
            },

            move: function (evt) {
                if (!evt.touches) return;    // 如果没有TouchList对象, 直接return掉 (touches: 位于屏幕上的所有手指的列表)

                var preV = this.preV,    // 把start方法保存的2根手指坐标的差值xy赋给preV变量
                    len = evt.touches.length,    // 手指个数
                    currentX = evt.touches[0].pageX,    // 第一根手指的坐标(相对于页面的 x1, y1 坐标)
                    currentY = evt.touches[0].pageY;
                this.isDoubleTap = false;    // 移动过程中不能双击了

                if (len > 1) {    // 2根手指以上(处理捏pinch和旋转rotate手势)

                    var v = {    // 第二根手指和第一根手指坐标的差值
                        x: evt.touches[1].pageX - currentX, 
                        y: evt.touches[1].pageY - currentY 
                    };

                    if (preV.x !== null) {    // start方法中保存的this.preV的x不为空的话

                        if (this.pinchStartLen > 0) {    // 2点间的距离大于0
                            evt.scale = getLen(v) / this.pinchStartLen;    // move中的2点之间的距离除以start中的2点的距离就是缩放比值
                            this.pinch.dispatch(evt);    // scale挂在到evt对象上 (用户的pinch回调就在此调用的)
                        };

                        evt.angle = getRotateAngle(v, preV);    // 计算angle角度
                        this.rotate.dispatch(evt);    // (用户的pinch回调就在此调用的)
                    };

                    preV.x = v.x;    // 把move中的2根手指中的差值赋值给preV, 当然也改变了this.preV
                    preV.y = v.y;

                } else {    // 一根手指(处理拖动pressMove手势)

                    if (this.x2 !== null) {    // 一根手指第一次必然为空,因为初始化赋值为null, 下面将会用x2, y2保存上一次的结果

                        evt.deltaX = currentX - this.x2;    // 拖动过程中或者手指移动过程中的差值(当前坐标与上一次的坐标)
                        evt.deltaY = currentY - this.y2;

                    } else {
                        evt.deltaX = 0;    // 第一次嘛, 手指刚移动,哪来的差值啊,所以为0呗
                        evt.deltaY = 0;
                    };
                    this.pressMove.dispatch(evt);    // deltaXY挂载到evt对象上,抛给用户(用户的pressMove回调就在此调用的)
                };

                this.touchMove.dispatch(evt);    // evt对象因if语句而不同,挂载不同的属性抛出去给用户 (用户的touchMove回调就在此调用的)

                this._cancelLongTap();    // 取消长按定时器

                this.x2 = currentX;    // 存一下本次的pageXY坐标, 为了下次做差值
                this.y2 = currentY;

                if (len > 1) {    // 2个手指以上就阻止默认事件
                    evt.preventDefault();
                };
            },

            end: function (evt) {
                if (!evt.changedTouches) return;    // 位于该元素上的所有手指的列表, 没有TouchList也直接return掉

                this._cancelLongTap();    // 取消长按定时器

                var self = this;    // 存个实例
                if (evt.touches.length < 2) {    // 手指数量小于2就触发 (用户的多点结束multipointEnd回调函数)
                    this.multipointEnd.dispatch(evt);
                };

                this.touchEnd.dispatch(evt);    // 触发(用户的touchEnd回调函数)

                //swipe 滑动
                if ((this.x2 && Math.abs(this.x1 - this.x2) > 30) || (this.y2 && Math.abs(this.preV.y - this.y2) > 30)) {

                    evt.direction = this._swipeDirection(this.x1, this.x2, this.y1, this.y2);    // 获取上下左右方向中的一个

                    this.swipeTimeout = setTimeout(function () {
                        self.swipe.dispatch(evt);    // 立即触发,加入异步队列(用户的swipe事件的回调函数)
                    }, 0);

                } else {   // 以下是tap, singleTap, doubleTap事件派遣

                    this.tapTimeout = setTimeout(function () {

                        self.tap.dispatch(evt);    // 触发(用户的tap事件的回调函数)
                        // trigger double tap immediately
                        if (self.isDoubleTap) {    // 如果满足双击的话

                            self.doubleTap.dispatch(evt);    // 触发(用户的doubleTap事件的回调函数)
                            clearTimeout(self.singleTapTimeout);    // 清除singleTapTimeout定时器

                            self.isDoubleTap = false;    // 双击条件重置

                        } else {
                            self.singleTapTimeout = setTimeout(function () {
                                self.singleTap.dispatch(evt);    // 触发(用户的singleTap事件的回调函数)
                            }, 250);
                        };

                    }, 0);    // 加入异步队列,主线程完成立马执行
                };

                this.preV.x = 0;    // this.preV, this.scale, this.pinchStartLen, this.x1 x2 y1 y2恢复初始值
                this.preV.y = 0;
                this.scale = 1;
                this.pinchStartLen = null;
                this.x1 = this.x2 = this.y1 = this.y2 = null;
            },

            cancel: function (evt) {
                //清除定时器
                clearTimeout(this.singleTapTimeout);
                clearTimeout(this.tapTimeout);
                clearTimeout(this.longTapTimeout);
                clearTimeout(this.swipeTimeout);
                // 执行用户的touchCancel回调函数,没有就执行一次noop空函数
                this.touchCancel.dispatch(evt);
            },

            _cancelLongTap: function () {    // 取消长按定时器
                clearTimeout(this.longTapTimeout);
            },

            _cancelSingleTap: function () {    // 取消延时SingleTap定时器
                clearTimeout(this.singleTapTimeout);
            },

            // 2点间x与y之间的绝对值的差值作比较,x大的话即为左右滑动,y大即为上下滑动,x的差值大于0即为左滑动,小于0即为右滑动
            _swipeDirection: function (x1, x2, y1, y2) {    // 判断用户到底是从上到下,还是从下到上,或者从左到右、从右到左滑动
                return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down');
            },

            // 给dom添加14种事件中的一种
            on: function(evt, handler) {    
                if(this[evt]) {    // 看看有没有相应的事件名
                    this[evt].add(handler);    // HandlerAdmin实例的add方法
                };
            },

            // 移除手势事件对应函数
            off: function(evt, handler) {
                if(this[evt]) {
                    this[evt].del(handler);    // 从数组中删除handler方法
                };
            },

            destroy: function() {

                // 关闭所有定时器
                if(this.singleTapTimeout) clearTimeout(this.singleTapTimeout);
                if(this.tapTimeout) clearTimeout(this.tapTimeout);
                if(this.longTapTimeout) clearTimeout(this.longTapTimeout);
                if(this.swipeTimeout) clearTimeout(this.swipeTimeout);

                // 取消dom上touchstart, touchmove, touchend, touchcancel事件
                this.element.removeEventListener("touchstart", this.start);
                this.element.removeEventListener("touchmove", this.move);
                this.element.removeEventListener("touchend", this.end);
                this.element.removeEventListener("touchcancel", this.cancel);

                // 把14个HandlerAdmin实例的this.handlers置为空数组
                this.rotate.del();
                this.touchStart.del();
                this.multipointStart.del();
                this.multipointEnd.del();
                this.pinch.del();
                this.swipe.del();
                this.tap.del();
                this.doubleTap.del();
                this.longTap.del();
                this.singleTap.del();
                this.pressMove.del();
                this.touchMove.del();
                this.touchEnd.del();
                this.touchCancel.del();

                // 实例成员的变量全部置为null
                this.preV = this.pinchStartLen = this.scale = this.isDoubleTap = this.delta = this.last = this.now = this.tapTimeout = this.singleTapTimeout = this.longTapTimeout = this.swipeTimeout = this.x1 = this.x2 = this.y1 = this.y2 = this.preTapPosition = this.rotate = this.touchStart = this.multipointStart = this.multipointEnd = this.pinch = this.swipe = this.tap = this.doubleTap = this.longTap = this.singleTap = this.pressMove = this.touchMove = this.touchEnd = this.touchCancel = null;

                return null;
            }
        };

        // 抛出去
        exports.Finger = AlloyFinger;
        
    })(jsLib);

    ; (function(exports) {

        var DEG_TO_RAD =  0.017453292519943295;

        // 三维矩阵
        var Matrix3D = function(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) {
            this.elements = window.Float32Array ? new Float32Array(16) : [];
            var te = this.elements;
            te[0] = (n11 !== undefined) ? n11 : 1;
            te[1] = n21 || 0;
            te[2] = n31 || 0;
            te[3] = n41 || 0;

            te[4] = n12 || 0;
            te[5] = (n22 !== undefined) ? n22 : 1;
            te[6] = n32 || 0;
            te[7] = n42 || 0;

            te[8] = n13 || 0;
            te[9] = n23 || 0;
            te[10] = (n33 !== undefined) ? n33 : 1;
            te[11] = n43 || 0;

            te[12] = n14 || 0;
            te[13] = n24 || 0;
            te[14] = n34 || 0;
            te[15] = (n44 !== undefined) ? n44 : 1;
        };

        Matrix3D.prototype = {
            constructor: Matrix3D,

            set: function(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) {
                var te = this.elements;
                te[0] = n11; te[4] = n12; te[8] = n13; te[12] = n14;
                te[1] = n21; te[5] = n22; te[9] = n23; te[13] = n24;
                te[2] = n31; te[6] = n32; te[10] = n33; te[14] = n34;
                te[3] = n41; te[7] = n42; te[11] = n43; te[15] = n44;
                return this;
            },
            identity: function() {
                this.set(
                    1, 0, 0, 0,
                    0, 1, 0, 0,
                    0, 0, 1, 0,
                    0, 0, 0, 1
                );
                return this;
            },
            appendTransform: function(x, y, z, scaleX, scaleY, scaleZ, rotateX, rotateY, rotateZ, skewX, skewY, originX, originY, originZ) {
                
                var rx = rotateX * DEG_TO_RAD;
                var cosx = this._rounded(Math.cos(rx));
                var sinx = this._rounded(Math.sin(rx));
                var ry = rotateY * DEG_TO_RAD;
                var cosy = this._rounded(Math.cos(ry));
                var siny = this._rounded(Math.sin(ry));
                var rz = rotateZ * DEG_TO_RAD;
                var cosz = this._rounded(Math.cos(rz * -1));
                var sinz = this._rounded(Math.sin(rz * -1));

                this.multiplyMatrices(this, this._arrayWrap([
                    1, 0, 0, x,
                    0, cosx, sinx, y,
                    0, -sinx, cosx, z,
                    0, 0, 0, 1
                ]));

                this.multiplyMatrices(this, this._arrayWrap([
                    cosy, 0, siny, 0,
                    0, 1, 0, 0,
                    -siny, 0, cosy, 0,
                    0, 0, 0, 1
                ]));

                this.multiplyMatrices(this, this._arrayWrap([
                    cosz * scaleX, sinz * scaleY, 0, 0,
                    -sinz * scaleX, cosz * scaleY, 0, 0,
                    0, 0, 1 * scaleZ, 0,
                    0, 0, 0, 1
                ]));

                if (skewX || skewY) {
                    this.multiplyMatrices(this, this._arrayWrap([
                        this._rounded(Math.cos(skewX * DEG_TO_RAD)), this._rounded(Math.sin(skewX * DEG_TO_RAD)), 0, 0,
                        -1 * this._rounded(Math.sin(skewY * DEG_TO_RAD)), this._rounded(Math.cos(skewY * DEG_TO_RAD)), 0, 0,
                        0, 0, 1, 0,
                        0, 0, 0, 1
                    ]));
                };

                if (originX || originY || originZ) {
                    this.elements[12] -= originX * this.elements[0] + originY * this.elements[4] + originZ * this.elements[8];
                    this.elements[13] -= originX * this.elements[1] + originY * this.elements[5] + originZ * this.elements[9];
                    this.elements[14] -= originX * this.elements[2] + originY * this.elements[6] + originZ * this.elements[10];
                };

                return this;
            },
            // 矩阵相乘
            multiplyMatrices: function(a, be) {
                var ae = a.elements;
                var te = this.elements;

                var a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12];
                var a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13];
                var a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14];
                var a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15];

                var b11 = be[0], b12 = be[1], b13 = be[2], b14 = be[3];
                var b21 = be[4], b22 = be[5], b23 = be[6], b24 = be[7];
                var b31 = be[8], b32 = be[9], b33 = be[10], b34 = be[11];
                var b41 = be[12], b42 = be[13], b43 = be[14], b44 = be[15];

                te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
                te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
                te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
                te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;

                te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
                te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
                te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
                te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;

                te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
                te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
                te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
                te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;

                te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
                te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
                te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
                te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;

                return this;
            },
            // 解决角度为90的整数倍导致Math.cos得到极小的数,其实是0。导致不渲染
            _rounded: function(value, i) {
                i = Math.pow(10, i || 15);
                return Math.round(value * i) / i;
            },
            _arrayWrap: function(arr) {
                return window.Float32Array ? new Float32Array(arr) : arr;
            }
        };

        // 主入口函数
        function Transform(obj, notPerspective) {
            var observeProps = ['translateX', 'translateY', 'translateZ', 'scaleX', 'scaleY', 'scaleZ', 'rotateX', 'rotateY', 'rotateZ', 'skewX', 'skewY', 'originX', 'originY', 'originZ'],
                objIsElement = isElement(obj);
            if (!notPerspective) {
                observeProps.push('perspective');
            };

            obj.matrix3d = new Matrix3D();
            observe(
                obj, 
                observeProps, 
                function() {
                    var mtx = obj.matrix3d.identity().appendTransform(obj.translateX, obj.translateY, obj.translateZ, obj.scaleX, obj.scaleY, obj.scaleZ, obj.rotateX, obj.rotateY, obj.rotateZ, obj.skewX, obj.skewY, obj.originX, obj.originY, obj.originZ);
                    var transform = (notPerspective ? '' : 'perspective(' + obj.perspective + 'px) ') + 'matrix3d(' + Array.prototype.slice.call(mtx.elements).join(',') + ')';
                    if (objIsElement) {
                        obj.style.transform = obj.style.msTransform = obj.style.OTransform = obj.style.MozTransform = obj.style.webkitTransform = transform;
                    } else {
                        obj.transform = transform;
                    };
                });

            if (!notPerspective) {
                obj.perspective = 500;    // 景深默认值
            };
            obj.scaleX = obj.scaleY = obj.scaleZ = 1;
            obj.translateX = obj.translateY = obj.translateZ = obj.rotateX = obj.rotateY = obj.rotateZ = obj.skewX = obj.skewY = obj.originX = obj.originY = obj.originZ = 0;
        };

        // 工具函数
        function isElement(obj) {
            return (
                typeof HTMLElement === 'object' ? obj instanceof HTMLElement : //DOM2
                obj && typeof obj === 'object' && obj !== null && obj.nodeType === 1 && typeof obj.nodeName === 'string'
            );
        };

        function observe(target, props, callback) {
            for (var i = 0, len = props.length; i < len; i += 1) {
                var prop = props[i];
                watch(target, prop, callback);
            };
        };

        // 每一次改变那15个属性中的任意一个,都会执行回调
        function watch(target, prop, callback) {
            Object.defineProperty(target, prop, {
                get: function() {
                    return this['_' + prop];
                },
                set: function(value) {
                    if (value !== this['_' + prop]) {
                        this['_' + prop] = value;
                        callback();
                    };
                }
            });
        };

        // 抛出去
        exports.Transform = Transform;

    })(jsLib);


    /************** 上面一些方法(插件)扩展到JSLibrary原型上    结束 **************/


    // 把jsLib抛出去
     function jsLib(arg) {
         return new JSLibrary(arg);
     };

     if (typeof module !== 'undefined' && typeof exports === 'object') {
        module.exports = jsLib;
    } else {
         if (!win.jsLib) win.jsLib = jsLib;        
    };
}(window, document));
原文地址:https://www.cnblogs.com/sorrowx/p/8675384.html