zepto源码注释

zepto源码注释

Zepto是一个轻量级的针对现代高级浏览器的JavaScript库, 它与jquery有着类似的api。 如果你会用jquery,那么你也会用zepto。这段时间公司的事情比较少,所以就把它的源码看了下,觉得写的挺好的,所以就有了给它写注释的想法。当然,这里面的注释只是我读代码时对它的理解,并不一定正确,如果有错误还请指正,先谢谢了。另外,敬请期待另一个JS大牛(果果)的JS库(then.js)的源码注释。

复制代码
   1 /* Zepto v1.0-1-ga3cab6c - polyfill zepto detect event ajax form fx - zeptojs.com/license */
   2 
   3 
   4 ;(function(undefined){
   5   if (String.prototype.trim === undefined) // fix for iOS 3.2
   6     String.prototype.trim = function(){ return this.replace(/^\s+|\s+$/g, '') }
   7 
   8   // For iOS 3.x
   9   // from https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/reduce
  10   if (Array.prototype.reduce === undefined)
  11     Array.prototype.reduce = function(fun){
  12       if(this === void 0 || this === null) throw new TypeError()
  13       var t = Object(this), len = t.length >>> 0, k = 0, accumulator
  14       if(typeof fun != 'function') throw new TypeError()
  15       if(len == 0 && arguments.length == 1) throw new TypeError()
  16 
  17       if(arguments.length >= 2)
  18        accumulator = arguments[1]
  19       else
  20         do{
  21           if(k in t){
  22             accumulator = t[k++]
  23             break
  24           }
  25           if(++k >= len) throw new TypeError()
  26         } while (true)
  27 
  28       while (k < len){
  29         if(k in t) accumulator = fun.call(undefined, accumulator, t[k], k, t)
  30         k++
  31       }
  32       return accumulator
  33     }
  34 
  35 })()
  36 
  37 var Zepto = (function() {
  38   var undefined, key, $, classList, emptyArray = [], slice = emptyArray.slice, filter = emptyArray.filter,
  39     document = window.document,
  40     elementDisplay = {}, classCache = {},
  41     getComputedStyle = document.defaultView.getComputedStyle,
  42     //设置CSS时,不用加px单位的属性
  43     cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1,'opacity': 1, 'z-index': 1, 'zoom': 1 },
  44     //HTML代码片断的正则
  45     fragmentRE = /^\s*<(\w+|!)[^>]*>/,
  46     //匹配非单独一个闭合标签的标签,类似将<div></div>写成了<div/>
  47     tagExpanderRE = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
  48     //根节点
  49     rootNodeRE = /^(?:body|html)$/i,
  50 
  51     //需要提供get和set的方法名
  52     // special attributes that should be get/set via method calls
  53     methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'],
  54     //相邻节点的一些操作
  55     adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ],
  56     table = document.createElement('table'),
  57     tableRow = document.createElement('tr'),
  58     containers = {
  59       'tr': document.createElement('tbody'),
  60       'tbody': table, 'thead': table, 'tfoot': table,
  61       'td': tableRow, 'th': tableRow,
  62       '*': document.createElement('div')
  63     },
  64     //当DOM ready的时候,document会有以下三种状态的一种
  65     readyRE = /complete|loaded|interactive/,
  66     //class选择器的正则
  67     classSelectorRE = /^\.([\w-]+)$/,
  68     //id选择器的正则
  69     idSelectorRE = /^#([\w-]*)$/,
  70     //DOM标签正则
  71     tagSelectorRE = /^[\w-]+$/,
  72     class2type = {},
  73     toString = class2type.toString,
  74     zepto = {},
  75     camelize, uniq,
  76     tempParent = document.createElement('div');
  77 
  78    //判断一个元素是否匹配给定的选择器
  79   zepto.matches = function(element, selector) {
  80     if (!element || element.nodeType !== 1) return false
  81     //引用浏览器提供的MatchesSelector方法
  82     var matchesSelector = element.webkitMatchesSelector || element.mozMatchesSelector ||
  83                           element.oMatchesSelector || element.matchesSelector
  84     if (matchesSelector) return matchesSelector.call(element, selector);
  85     //如果浏览器不支持MatchesSelector方法,则将节点放入一个临时div节点,
  86     //再通过selector来查找这个div下的节点集,再判断给定的element是否在节点集中,如果在,则返回一个非零(即非false)的数字
  87     // fall back to performing a selector:
  88     var match, parent = element.parentNode, temp = !parent
  89     //当element没有父节点,那么将其插入到一个临时的div里面
  90     if (temp) (parent = tempParent).appendChild(element)
  91     //将parent作为上下文,来查找selector的匹配结果,并获取element在结果集的索引,不存在时为-1,再通过~-1转成0,存在时返回一个非零的值
  92     match = ~zepto.qsa(parent, selector).indexOf(element)
  93     //将插入的节点删掉
  94     temp && tempParent.removeChild(element)
  95     return match
  96   }
  97   
  98   //获取对象类型 
  99   function type(obj) {
 100     //obj为null或者undefined时,直接返回'null'或'undefined'
 101     return obj == null ? String(obj) : class2type[toString.call(obj)] || "object"
 102   }
 103 
 104   function isFunction(value) { return type(value) == "function" }
 105   function isWindow(obj)     { return obj != null && obj == obj.window }
 106   function isDocument(obj)   { return obj != null && obj.nodeType == obj.DOCUMENT_NODE }
 107   function isObject(obj)     { return type(obj) == "object" }
 108   //判断给定的参数是否是由Object构造器生成的对象
 109   //可参考http://snandy.iteye.com/blog/663245
 110   function isPlainObject(obj) {
 111     return isObject(obj) && !isWindow(obj) && obj.__proto__ == Object.prototype
 112   }
 113   function isArray(value) { return value instanceof Array }
 114   //类数组,比如nodeList,这个只是做最简单的判断,如果给一个对象定义一个值为数据的length属性,它同样会返回true
 115   function likeArray(obj) { return typeof obj.length == 'number' }
 116 
 117   //清除给定的参数中的null或undefined,注意0==null,'' == null为false
 118   function compact(array) { return filter.call(array, function(item){ return item != null }) }
 119   //类似得到一个数组的副本
 120   function flatten(array) { return array.length > 0 ? $.fn.concat.apply([], array) : array }
 121   //将字符串转成驼峰式的格式
 122   camelize = function(str){ return str.replace(/-+(.)?/g, function(match, chr){ return chr ? chr.toUpperCase() : '' }) }
 123   //将字符串格式化成-拼接的形式,一般用在样式属性上,比如border-width
 124   function dasherize(str) {
 125     return str.replace(/::/g, '/') //将::替换成/
 126            .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') //在大小写字符之间插入_,大写在前,比如AAAbb,得到AA_Abb
 127            .replace(/([a-z\d])([A-Z])/g, '$1_$2') //在大小写字符之间插入_,小写或数字在前,比如bbbAaa,得到bbb_Aaa
 128            .replace(/_/g, '-') //将_替换成-
 129            .toLowerCase() //转成小写
 130   }
 131   //数组去重
 132   uniq = function(array){ return filter.call(array, function(item, idx){ return array.indexOf(item) == idx }) }
 133 
 134   //将给定的参数生成正则
 135   function classRE(name) {
 136     //classCache,缓存正则
 137     return name in classCache ?
 138       classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)'))
 139   }
 140   //给需要的样式值后面加上'px'单位,除了cssNumber里面的指定的那些
 141   function maybeAddPx(name, value) {
 142     return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value
 143   }
 144   //获取节点的默认display属性
 145   function defaultDisplay(nodeName) {
 146     var element, display
 147     if (!elementDisplay[nodeName]) {//缓存里不存在
 148       element = document.createElement(nodeName)
 149       document.body.appendChild(element)
 150       display = getComputedStyle(element, '').getPropertyValue("display") 
 151       element.parentNode.removeChild(element)
 152       display == "none" && (display = "block") //当display等于none时,设置其值为block,搞不懂为毛要这样
 153       elementDisplay[nodeName] = display //缓存元素的默认display属性
 154     }
 155     return elementDisplay[nodeName]
 156   }
 157    //获取指定元素的子节点(不包含文本节点),Firefox不支持children,所以只能通过筛选childNodes
 158   function children(element) {
 159     return 'children' in element ?
 160       slice.call(element.children) :
 161       $.map(element.childNodes, function(node){ if (node.nodeType == 1) return node })
 162   }
 163 
 164   // `$.zepto.fragment` takes a html string and an optional tag name
 165   // to generate DOM nodes nodes from the given html string.
 166   // The generated DOM nodes are returned as an array.
 167   // This function can be overriden in plugins for example to make
 168   // it compatible with browsers that don't support the DOM fully.
 169   zepto.fragment = function(html, name, properties) {
 170     //将类似<div class="test"/>替换成<div class="test"></div>,算是一种修复吧
 171     if (html.replace) html = html.replace(tagExpanderRE, "<$1></$2>")
 172     //当不指定name的时候,给name取标签名
 173     if (name === undefined) name = fragmentRE.test(html) && RegExp.$1
 174     //当name不等于tr,tbody,thead,tfoot,th,td时,赋值*
 175     if (!(name in containers)) name = '*'
 176 
 177     var nodes, dom, container = containers[name] //创建容器
 178     container.innerHTML = '' + html //将html代码片断放入容器
 179     //取容器的子节点,这样就直接把字符串转成DOM节点了
 180     dom = $.each(slice.call(container.childNodes), function(){
 181       container.removeChild(this)//逐个删除
 182     })
 183     //如果properties是对象
 184     if (isPlainObject(properties)) {
 185       nodes = $(dom)  //将dom转成zepto对象,为了方便下面调用zepto上的方法
 186       //遍历对象,设置属性
 187       $.each(properties, function(key, value) {
 188         //如果设置的是'val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset',则调用zepto上相对应的方法
 189         if (methodAttributes.indexOf(key) > -1) nodes[key](value)
 190         else nodes.attr(key, value)
 191       })
 192     }
 193     return dom
 194   }
 195 
 196   // `$.zepto.Z` swaps out the prototype of the given `dom` array
 197   // of nodes with `$.fn` and thus supplying all the Zepto functions
 198   // to the array. Note that `__proto__` is not supported on Internet
 199   // Explorer. This method can be overriden in plugins.
 200   zepto.Z = function(dom, selector) {
 201     dom = dom || []
 202     dom.__proto__ = $.fn //通过给dom设置__proto__属性指向$.fn来达到继承$.fn上所有方法的目的
 203     dom.selector = selector || ''
 204     return dom
 205   }
 206 
 207   // `$.zepto.isZ` should return `true` if the given object is a Zepto
 208   // collection. This method can be overriden in plugins.
 209   //判断给定的参数是否是Zepto集
 210   zepto.isZ = function(object) {
 211     return object instanceof zepto.Z
 212   }
 213 
 214   // `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and
 215   // takes a CSS selector and an optional context (and handles various
 216   // special cases).
 217   // This method can be overriden in plugins.
 218   zepto.init = function(selector, context) {
 219     // If nothing given, return an empty Zepto collection
 220     if (!selector) return zepto.Z() //没有参数,返回空数组
 221     // If a function is given, call it when the DOM is ready
 222     else if (isFunction(selector)) return $(document).ready(selector)
 223     // If a Zepto collection is given, juts return it
 224     else if (zepto.isZ(selector)) return selector
 225     else {
 226       var dom
 227       // normalize array if an array of nodes is given
 228       if (isArray(selector)) dom = compact(selector)
 229       // Wrap DOM nodes. If a plain object is given, duplicate it.
 230       else if (isObject(selector))
 231         dom = [isPlainObject(selector) ? $.extend({}, selector) : selector], selector = null
 232       // If it's a html fragment, create nodes from it
 233       else if (fragmentRE.test(selector))
 234         dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null
 235       // If there's a context, create a collection on that context first, and select
 236       // nodes from there
 237       else if (context !== undefined) return $(context).find(selector)
 238       // And last but no least, if it's a CSS selector, use it to select nodes.
 239       else dom = zepto.qsa(document, selector)
 240       // create a new Zepto collection from the nodes found
 241       return zepto.Z(dom, selector)
 242     }
 243   }
 244 
 245   // `$` will be the base `Zepto` object. When calling this
 246   // function just call `$.zepto.init, which makes the implementation
 247   // details of selecting nodes and creating Zepto collections
 248   // patchable in plugins.
 249   $ = function(selector, context){
 250     return zepto.init(selector, context)
 251   }
 252 
 253   //扩展,deep表示是否深度扩展
 254   function extend(target, source, deep) {
 255     for (key in source)
 256       //如果深度扩展
 257       if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
 258         //如果要扩展的数据是对象且target相对应的key不是对象
 259         if (isPlainObject(source[key]) && !isPlainObject(target[key]))
 260           target[key] = {}
 261         //如果要扩展的数据是数组且target相对应的key不是数组
 262         if (isArray(source[key]) && !isArray(target[key]))
 263           target[key] = []
 264         extend(target[key], source[key], deep)
 265       }
 266       else if (source[key] !== undefined) target[key] = source[key]
 267   }
 268 
 269   // Copy all but undefined properties from one or more
 270   // objects to the `target` object.
 271   $.extend = function(target){
 272     var deep, args = slice.call(arguments, 1)
 273     if (typeof target == 'boolean') { //当第一个参数为boolean类型的值时,表示是否深度扩展
 274       deep = target
 275       target = args.shift() //target取第二个参数
 276     }
 277     //遍历后面的参数,全部扩展到target上
 278     args.forEach(function(arg){ extend(target, arg, deep) })
 279     return target
 280   }
 281 
 282   // `$.zepto.qsa` is Zepto's CSS selector implementation which
 283   // uses `document.querySelectorAll` and optimizes for some special cases, like `#id`.
 284   // This method can be overriden in plugins.
 285   zepto.qsa = function(element, selector){
 286     var found
 287     //当element为document,且selector为ID选择器时
 288     return (isDocument(element) && idSelectorRE.test(selector)) ?
 289         //直接返回document.getElementById,RegExp.$1为ID的值,当没有找节点时返回[]
 290       ( (found = element.getElementById(RegExp.$1)) ? [found] : [] ) :
 291       //当element不为元素节点或者document时,返回[]
 292       (element.nodeType !== 1 && element.nodeType !== 9) ? [] :
 293       //否则将获取到的结果转成数组并返回
 294       slice.call(
 295         //如果selector是标签名,直接调用getElementsByClassName
 296         classSelectorRE.test(selector) ? element.getElementsByClassName(RegExp.$1) :
 297         //如果selector是标签名,直接调用getElementsByTagName
 298         tagSelectorRE.test(selector) ? element.getElementsByTagName(selector) :
 299         //否则调用querySelectorAll
 300         element.querySelectorAll(selector)
 301       )
 302   }
 303 
 304   //在结果中进行过滤
 305   function filtered(nodes, selector) {
 306     return selector === undefined ? $(nodes) : $(nodes).filter(selector)
 307   }
 308   //判断parent是否包含node
 309   $.contains = function(parent, node) {
 310     return parent !== node && parent.contains(node)
 311   }
 312 
 313   function funcArg(context, arg, idx, payload) {
 314     return isFunction(arg) ? arg.call(context, idx, payload) : arg
 315   }
 316   
 317   function setAttribute(node, name, value) {
 318     //如果设置的值为null或undefined,则相当于删除该属性,否则设置name属性为value
 319     value == null ? node.removeAttribute(name) : node.setAttribute(name, value)
 320   }
 321 
 322   // access className property while respecting SVGAnimatedString
 323   function className(node, value){
 324     var klass = node.className,
 325         svg   = klass && klass.baseVal !== undefined
 326 
 327     if (value === undefined) return svg ? klass.baseVal : klass
 328     svg ? (klass.baseVal = value) : (node.className = value)
 329   }
 330 
 331   // "true"  => true
 332   // "false" => false
 333   // "null"  => null
 334   // "42"    => 42
 335   // "42.5"  => 42.5
 336   // JSON    => parse if valid
 337   // String  => self
 338   function deserializeValue(value) {
 339     var num
 340     try {
 341       return value ?
 342         value == "true" ||
 343         ( value == "false" ? false :
 344           value == "null" ? null :
 345           !isNaN(num = Number(value)) ? num :
 346           /^[\[\{]/.test(value) ? $.parseJSON(value) :
 347           value )
 348         : value
 349     } catch(e) {
 350       return value
 351     }
 352   }
 353 
 354   $.type = type
 355   $.isFunction = isFunction
 356   $.isWindow = isWindow
 357   $.isArray = isArray
 358   $.isPlainObject = isPlainObject
 359 
 360   //空对象
 361   $.isEmptyObject = function(obj) {
 362     var name
 363     for (name in obj) return false
 364     return true
 365   }
 366   
 367   //获取指定的值在数组中的位置
 368   $.inArray = function(elem, array, i){
 369     return emptyArray.indexOf.call(array, elem, i)
 370   }
 371   //将字符串转成驼峰式的格式
 372   $.camelCase = camelize
 373   //去字符串头尾空格
 374   $.trim = function(str) { return str.trim() }
 375 
 376   // plugin compatibility
 377   $.uuid = 0
 378   $.support = { }
 379   $.expr = { }
 380 
 381   //遍历elements,将每次放入callback里处理,保存处理结果不为null的项
 382   $.map = function(elements, callback){
 383     var value, values = [], i, key
 384     if (likeArray(elements))
 385       for (i = 0; i < elements.length; i++) {
 386         value = callback(elements[i], i)
 387         if (value != null) values.push(value)
 388       }
 389     else
 390       for (key in elements) {
 391         value = callback(elements[key], key)
 392         if (value != null) values.push(value)
 393       }
 394     return flatten(values)
 395   }
 396 
 397   $.each = function(elements, callback){
 398     var i, key 
 399     if (likeArray(elements)) {
 400       for (i = 0; i < elements.length; i++)
 401         if (callback.call(elements[i], i, elements[i]) === false) return elements
 402     } else {
 403       for (key in elements)
 404         if (callback.call(elements[key], key, elements[key]) === false) return elements
 405     }
 406 
 407     return elements
 408   }
 409   //过滤
 410   $.grep = function(elements, callback){
 411     return filter.call(elements, callback)
 412   }
 413 
 414   if (window.JSON) $.parseJSON = JSON.parse
 415 
 416   // Populate the class2type map
 417   //填充class2type的值
 418   $.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
 419     class2type[ "[object " + name + "]" ] = name.toLowerCase()
 420   })
 421 
 422   //针对DOM的一些操作
 423   // Define methods that will be available on all
 424   // Zepto collections
 425   $.fn = {
 426     // Because a collection acts like an array
 427     // copy over these useful array functions.
 428     forEach: emptyArray.forEach,
 429     reduce: emptyArray.reduce,
 430     push: emptyArray.push,
 431     sort: emptyArray.sort,
 432     indexOf: emptyArray.indexOf,
 433     concat: emptyArray.concat,
 434 
 435     // `map` and `slice` in the jQuery API work differently
 436     // from their array counterparts
 437     map: function(fn){
 438       return $($.map(this, function(el, i){ return fn.call(el, i, el) }))
 439     },
 440     slice: function(){
 441       return $(slice.apply(this, arguments))
 442     },
 443     //DOM Ready
 444     ready: function(callback){
 445       if (readyRE.test(document.readyState)) callback($)
 446       else document.addEventListener('DOMContentLoaded', function(){ callback($) }, false)
 447       return this
 448     },
 449     //取集合中对应指定索引的值,如果idx小于0,则idx等于idx+length,length为集合的长度
 450     get: function(idx){
 451       return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length]
 452     },
 453     //将集合转换为数组
 454     toArray: function(){ return this.get() },
 455     //获取集合长度
 456     size: function(){
 457       return this.length
 458     },
 459     //将集合从dom中删除
 460     remove: function(){
 461       return this.each(function(){
 462         if (this.parentNode != null)
 463           this.parentNode.removeChild(this)
 464       })
 465     },
 466     //遍历集合,将集合中的每一项放入callback中进行处理,去掉结果为false的项
 467     each: function(callback){
 468       emptyArray.every.call(this, function(el, idx){
 469         return callback.call(el, idx, el) !== false
 470       })
 471       return this
 472     },
 473     //过滤集合,返回处理结果为true的记录
 474     filter: function(selector){
 475        //this.not(selector)取到需要排除的集合,第二次再取反(这个时候this.not的参数就是一个集合了),得到想要的集合
 476       if (isFunction(selector)) return this.not(this.not(selector))
 477       //filter收集返回结果为true的记录
 478       return $(filter.call(this, function(element){
 479         return zepto.matches(element, selector)//当element与selector匹配,则收集
 480       }))
 481     },
 482     //将由selector获取到的结果追加到当前集合中
 483     add: function(selector,context){
 484       return $(uniq(this.concat($(selector,context))))//追求并去重
 485     },
 486     //返回集合中的第1条记录是否与selector匹配
 487     is: function(selector){
 488       return this.length > 0 && zepto.matches(this[0], selector)
 489     },
 490     //排除集合里满足条件的记录,接收参数为:字符串选择器,function, DOM,DOM集合
 491     not: function(selector){
 492       var nodes=[]
 493       //当selector为函数时
 494       if (isFunction(selector) && selector.call !== undefined){
 495         this.each(function(idx){
 496           //注意这里收集的是selector.call(this,idx)返回结果为false的时候记录
 497           if (!selector.call(this,idx)) nodes.push(this)
 498         })
 499       }else {
 500         //当selector为字符串的时候,对集合进行筛选,也就是筛选出集合中满足selector的记录
 501         var excludes = typeof selector == 'string' ? this.filter(selector) :
 502            //当selector为数组时执行slice.call(selector),当selector为节点时,执行$(selector)
 503           (likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector)
 504         this.forEach(function(el){
 505             //筛选出不在excludes集合里的记当,达到排除的目的
 506           if (excludes.indexOf(el) < 0) nodes.push(el)
 507         })
 508       }
 509       return $(nodes)
 510     },
 511     /*
 512         接收node和string作为参数,给当前集合筛选出包含selector的集合
 513         isObject(selector)是判断参数是否是node,因为typeof node == 'object'
 514         当参数为node时,只需要判读当前记当里是否包含node节点即可
 515         当参数为string时,则在当前记录里查询selector,如果长度为0,则为false,filter函数就会过滤掉这条记录,否则保存该记录
 516     */
 517     has: function(selector){
 518       return this.filter(function(){
 519         return isObject(selector) ? $.contains(this, selector) : $(this).find(selector).size()
 520       })
 521     },
 522     /* 
 523         选择集合中指定索引的记录,当idx为-1时,取最后一个记录
 524     */
 525     eq: function(idx){
 526       return idx === -1 ? this.slice(idx) : this.slice(idx, + idx + 1)
 527     },
 528     /* 
 529         取集合中的第一条记录
 530     */
 531     first: function(){
 532       var el = this[0] //取集合中的第一条记录
 533       //如果el为node,则isObject(el)会为true,需要转成zepto对象
 534       return el && !isObject(el) ? el : $(el)
 535     },
 536     /* 
 537         取集合中的最后一条记录
 538     */
 539     last: function(){
 540       var el = this[this.length - 1] //取集合中的最后一条记录
 541       //如果el为node,则isObject(el)会为true,需要转成zepto对象
 542       return el && !isObject(el) ? el : $(el)
 543     },
 544     /* 
 545         在当前集合中查找selector,selector可以是集合,选择器,以及节点
 546     */
 547     find: function(selector){
 548       var result, $this = this
 549       //如果selector为node或者zepto集合时
 550       if (typeof selector == 'object')
 551         //遍历selector,筛选出父级为集合中记录的selector
 552         result = $(selector).filter(function(){
 553           var node = this
 554           //如果$.contains(parent, node)返回true,则emptyArray.some也会返回true,外层的filter则会收录该条记录
 555           return emptyArray.some.call($this, function(parent){
 556             return $.contains(parent, node)
 557           })
 558         })
 559         //如果当前集合长度为1时,调用zepto.qsa,将结果转成zepto对象
 560       else if (this.length == 1) result = $(zepto.qsa(this[0], selector))
 561       //如果长度大于1,则调用map遍历
 562       else result = this.map(function(){ return zepto.qsa(this, selector) })
 563       return result
 564     },
 565     //取集合中第一记录的最近的父级元素
 566     closest: function(selector, context){
 567       var node = this[0], collection = false
 568       if (typeof selector == 'object') collection = $(selector)
 569       //当selector是node或者zepto集合时,如果node不在collection集合中时需要取node.parentNode进行判断
 570       //当selector是字符串选择器时,如果node与selector不匹配,则需要取node.parentNode进行判断
 571       while (node && !(collection ? collection.indexOf(node) >= 0 : zepto.matches(node, selector)))
 572         //当node 不是context,document的时候,取node.parentNode
 573         node = node !== context && !isDocument(node) && node.parentNode
 574       return $(node)
 575     },
 576     //取集合所有父级元素
 577     parents: function(selector){
 578       var ancestors = [], nodes = this
 579       //通过遍历nodes得到所有父级,注意在while里nodes被重新赋值了
 580       //本函数的巧妙之处在于,不停在获取父级,再遍历父级获取父级的父级
 581       //然后再通过去重,得到最终想要的结果,当到达最顶层的父级时,nodes.length就为0了
 582       while (nodes.length > 0)
 583         //nodes被重新赋值为收集到的父级集合
 584         nodes = $.map(nodes, function(node){
 585           //遍历nodes,收集集合的第一层父级
 586           //ancestors.indexOf(node) < 0用来去重复
 587           if ((node = node.parentNode) && !isDocument(node) && ancestors.indexOf(node) < 0) {
 588             ancestors.push(node) //收集已经获取到的父级元素
 589             return node
 590           }
 591         })
 592       //上面还只是取到了所有的父级元素,这里还需要对其进行筛选从而得到最终想要的结果
 593       return filtered(ancestors, selector) 
 594     },
 595     //获取集合的父节点
 596     parent: function(selector){
 597       return filtered(uniq(this.pluck('parentNode')), selector)
 598     },
 599     children: function(selector){
 600       return filtered(this.map(function(){ return children(this) }), selector)
 601     },
 602     contents: function() {
 603       return this.map(function() { return slice.call(this.childNodes) })
 604     },
 605     siblings: function(selector){
 606       return filtered(this.map(function(i, el){
 607         //先获取该节点的父节点中的所有子节点,再排除本身
 608         return filter.call(children(el.parentNode), function(child){ return child!==el })
 609       }), selector)
 610     },
 611     empty: function(){
 612       return this.each(function(){ this.innerHTML = '' })
 613     },
 614     //根据属性来获取当前集合的相关集合
 615     pluck: function(property){
 616       return $.map(this, function(el){ return el[property] })
 617     },
 618     show: function(){
 619       return this.each(function(){
 620         //清除元素的内联display="none"的样式
 621         this.style.display == "none" && (this.style.display = null)
 622         //当样式表里的该元素的display样式为none时,设置它的display为默认值
 623         if (getComputedStyle(this, '').getPropertyValue("display") == "none")
 624           this.style.display = defaultDisplay(this.nodeName)//defaultDisplay是获取元素默认display的方法
 625       })
 626     },
 627     replaceWith: function(newContent){
 628       //将要替换的内容插入到被替换的内容前面,然后删除被替换的内容
 629       return this.before(newContent).remove()
 630     },
 631     wrap: function(structure){
 632       var func = isFunction(structure)
 633       if (this[0] && !func)
 634           //如果structure是字符串,则将其转成DOM
 635         var dom   = $(structure).get(0),
 636             //如果structure是已经存在于页面上的节点或者被wrap的记录不只一条,则需要clone dom
 637             clone = dom.parentNode || this.length > 1
 638 
 639       return this.each(function(index){
 640         $(this).wrapAll(
 641           func ? structure.call(this, index) :
 642             clone ? dom.cloneNode(true) : dom
 643         )
 644       })
 645     },
 646     wrapAll: function(structure){
 647       if (this[0]) {
 648         //将要包裹的内容插入到第一条记录的前面,算是给structure定位围置
 649         $(this[0]).before(structure = $(structure))
 650         var children
 651         // drill down to the inmost element
 652         //取structure里的第一个子节点的最里层
 653         while ((children = structure.children()).length) structure = children.first()
 654         //将当前集合插入到最里层的节点里,达到wrapAll的目的
 655         $(structure).append(this)
 656       }
 657       return this
 658     },
 659     //在匹配元素里的内容外包一层结构
 660     wrapInner: function(structure){
 661       var func = isFunction(structure)
 662       return this.each(function(index){
 663         //原理就是获取节点的内容,然后将structure将内容包起来,如果内容不存在,则直接将structure append到该节点
 664         var self = $(this), contents = self.contents(),
 665             dom  = func ? structure.call(this, index) : structure
 666         contents.length ? contents.wrapAll(dom) : self.append(dom)
 667       })
 668     },
 669     unwrap: function(){
 670       //用子元素替换掉父级
 671       this.parent().each(function(){
 672         $(this).replaceWith($(this).children())
 673       })
 674       return this
 675     },
 676     //clone node
 677     clone: function(){
 678       return this.map(function(){ return this.cloneNode(true) })
 679     },
 680     //隐藏集合
 681     hide: function(){
 682       return this.css("display", "none")
 683     },
 684     toggle: function(setting){
 685       return this.each(function(){
 686         var el = $(this)
 687         /* 
 688             这个setting取得作用就是控制显示与隐藏,并不切换,当它的值为true时,一直显示,false时,一直隐藏
 689             这个地方的判断看上去有点绕,其实也简单,意思是说,当不给toogle参数时,根据元素的display是否等于none来决定显示或者隐藏元素
 690             当给toogle参数,就没有切换效果了,只是简单的根据参数值来决定显示或隐藏。如果参数true,相当于show方法,false则相当于hide方法
 691         */
 692         ;(setting === undefined ? el.css("display") == "none" : setting) ? el.show() : el.hide()
 693       })
 694     },
 695     prev: function(selector){ return $(this.pluck('previousElementSibling')).filter(selector || '*') },
 696     next: function(selector){ return $(this.pluck('nextElementSibling')).filter(selector || '*') },
 697     //当有参数时,设置集合每条记录的HTML,没有参数时,则为获取集合第一条记录的HTML,如果集合的长度为0,则返回null
 698     html: function(html){
 699       return html === undefined ?
 700         //参数html不存在时,获取集合中第一条记录的html
 701         (this.length > 0 ? this[0].innerHTML : null) :
 702         //否则遍历集合,设置每条记录的html
 703         this.each(function(idx){
 704           //记录原始的innerHTMl
 705           var originHtml = this.innerHTML
 706           //如果参数html是字符串直接插入到记录中,
 707           //如果是函数,则将当前记录作为上下文,调用该函数,且传入该记录的索引和原始innerHTML作为参数
 708           $(this).empty().append( funcArg(this, html, idx, originHtml) )
 709         })
 710     },
 711     text: function(text){
 712       return text === undefined ?
 713         (this.length > 0 ? this[0].textContent : null) :
 714         this.each(function(){ this.textContent = text })
 715     },
 716     attr: function(name, value){
 717       var result
 718       //当只有name且为字符串时,表示获取第一条记录的属性
 719       return (typeof name == 'string' && value === undefined) ?
 720         //集合没有记录或者集合的元素不是node类型,返回undefined
 721         (this.length == 0 || this[0].nodeType !== 1 ? undefined :
 722             //如果取的是input的value
 723           (name == 'value' && this[0].nodeName == 'INPUT') ? this.val() :
 724           //注意直接定义在node上的属性,在标准浏览器和ie9,10中用getAttribute取不到,得到的结果是null
 725           //比如div.aa = 10,用div.getAttribute('aa')得到的是null,需要用div.aa或者div['aa']这样来取
 726           (!(result = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : result
 727         ) :
 728         //有两个参数时为设置
 729         this.each(function(idx){
 730           if (this.nodeType !== 1) return
 731           if (isObject(name)) for (key in name) setAttribute(this, key, name[key])
 732           else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name)))
 733         })
 734     },
 735     removeAttr: function(name){
 736       return this.each(function(){ this.nodeType === 1 && setAttribute(this, name) })
 737     },
 738     prop: function(name, value){
 739       return (value === undefined) ?
 740         (this[0] && this[0][name]) :
 741         this.each(function(idx){
 742           this[name] = funcArg(this, value, idx, this[name])
 743         })
 744     },
 745     data: function(name, value){
 746       var data = this.attr('data-' + dasherize(name), value)
 747       return data !== null ? deserializeValue(data) : undefined
 748     },
 749     val: function(value){
 750       return (value === undefined) ?
 751         //如果是多选的select,则返回一个包含被选中的option的值的数组
 752         (this[0] && (this[0].multiple ?
 753            $(this[0]).find('option').filter(function(o){ return this.selected }).pluck('value') :
 754            this[0].value)
 755         ) :
 756         this.each(function(idx){
 757           this.value = funcArg(this, value, idx, this.value)
 758         })
 759     },
 760     offset: function(coordinates){
 761       if (coordinates) return this.each(function(index){
 762         var $this = $(this),
 763             //coordinates为{}时直接返回,为函数时返回处理结果给coords
 764             coords = funcArg(this, coordinates, index, $this.offset()),
 765             //取父级的offset
 766             parentOffset = $this.offsetParent().offset(),
 767             //计算出它们之间的差,得出其偏移量  
 768             props = {
 769               top:  coords.top  - parentOffset.top,
 770               left: coords.left - parentOffset.left
 771             }
 772         //注意元素的position为static时,设置top,left是无效的
 773         if ($this.css('position') == 'static') props['position'] = 'relative'
 774         $this.css(props)
 775       })
 776       //取第一条记录的offset,包括offsetTop,offsetLeft,offsetWidth,offsetHeight
 777       if (this.length==0) return null
 778       var obj = this[0].getBoundingClientRect()
 779       //window.pageYOffset就是类似Math.max(document.documentElement.scrollTop||document.body.scrollTop)
 780       return {
 781         left: obj.left + window.pageXOffset,
 782         top: obj.top + window.pageYOffset,
 783          Math.round(obj.width),
 784         height: Math.round(obj.height)
 785       }
 786     },
 787     css: function(property, value){
 788       //获取指定的样式
 789       if (arguments.length < 2 && typeof property == 'string')
 790         return this[0] && (this[0].style[camelize(property)] || getComputedStyle(this[0], '').getPropertyValue(property))
 791       //设置样式
 792       var css = ''
 793       if (type(property) == 'string') {
 794         if (!value && value !== 0)//当value的值为非零的可以转成false的值时,删掉property样式
 795           this.each(function(){ this.style.removeProperty(dasherize(property)) })
 796         else
 797           css = dasherize(property) + ":" + maybeAddPx(property, value)
 798       } else {
 799         //当property是对象时
 800         for (key in property)
 801           if (!property[key] && property[key] !== 0)
 802             //当property[key]的值为非零的可以转成false的值时,删掉key样式
 803             this.each(function(){ this.style.removeProperty(dasherize(key)) })
 804           else
 805             css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';'
 806       }
 807       //设置
 808       return this.each(function(){ this.style.cssText += ';' + css })
 809     },
 810     index: function(element){
 811       //这里的$(element)[0]是为了将字符串转成node,因为this是个包含node的数组
 812       //当不指定element时,取集合中第一条记录在其父节点的位置
 813       //this.parent().children().indexOf(this[0])这句很巧妙,和取第一记录的parent().children().indexOf(this)相同
 814       return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0])
 815     },
 816     hasClass: function(name){
 817       return emptyArray.some.call(this, function(el){
 818         //注意这里的this是classRE(name)生成的正则
 819         return this.test(className(el))
 820       }, classRE(name))
 821     },
 822     addClass: function(name){
 823       return this.each(function(idx){
 824         classList = []
 825         var cls = className(this), newName = funcArg(this, name, idx, cls)
 826         //处理同时多个类的情况,用空格分开
 827         newName.split(/\s+/g).forEach(function(klass){
 828           if (!$(this).hasClass(klass)) classList.push(klass)
 829         }, this)
 830         classList.length && className(this, cls + (cls ? " " : "") + classList.join(" "))
 831       })
 832     },
 833     removeClass: function(name){
 834       return this.each(function(idx){
 835         if (name === undefined) return className(this, '')
 836         classList = className(this)
 837         funcArg(this, name, idx, classList).split(/\s+/g).forEach(function(klass){
 838           classList = classList.replace(classRE(klass), " ")
 839         })
 840         className(this, classList.trim())
 841       })
 842     },
 843     toggleClass: function(name, when){
 844       return this.each(function(idx){
 845         var $this = $(this), names = funcArg(this, name, idx, className(this))
 846         names.split(/\s+/g).forEach(function(klass){
 847           (when === undefined ? !$this.hasClass(klass) : when) ?
 848             $this.addClass(klass) : $this.removeClass(klass)
 849         })
 850       })
 851     },
 852     scrollTop: function(){
 853       if (!this.length) return
 854       return ('scrollTop' in this[0]) ? this[0].scrollTop : this[0].scrollY
 855     },
 856     position: function() {
 857       if (!this.length) return
 858 
 859       var elem = this[0],
 860         // Get *real* offsetParent
 861         offsetParent = this.offsetParent(),
 862         // Get correct offsets
 863         offset       = this.offset(),
 864         parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset()
 865 
 866       // Subtract element margins
 867       // note: when an element has margin: auto the offsetLeft and marginLeft
 868       // are the same in Safari causing offset.left to incorrectly be 0
 869       offset.top  -= parseFloat( $(elem).css('margin-top') ) || 0
 870       offset.left -= parseFloat( $(elem).css('margin-left') ) || 0
 871 
 872       // Add offsetParent borders
 873       parentOffset.top  += parseFloat( $(offsetParent[0]).css('border-top-width') ) || 0
 874       parentOffset.left += parseFloat( $(offsetParent[0]).css('border-left-width') ) || 0
 875 
 876       // Subtract the two offsets
 877       return {
 878         top:  offset.top  - parentOffset.top,
 879         left: offset.left - parentOffset.left
 880       }
 881     },
 882     offsetParent: function() {
 883       return this.map(function(){
 884         var parent = this.offsetParent || document.body
 885         while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static")
 886           parent = parent.offsetParent
 887         return parent
 888       })
 889     }
 890   }
 891 
 892   // for now
 893   $.fn.detach = $.fn.remove
 894 
 895   // Generate the `width` and `height` functions
 896   ;['width', 'height'].forEach(function(dimension){
 897     $.fn[dimension] = function(value){
 898       var offset, el = this[0],
 899         //将width,hegiht转成Width,Height,用于取window或者document的width和height
 900         Dimension = dimension.replace(/./, function(m){ return m[0].toUpperCase() })
 901       //没有参数为获取,获取window的width和height用innerWidth,innerHeight
 902       if (value === undefined) return isWindow(el) ? el['inner' + Dimension] :
 903         //获取document的width和height时,用offsetWidth,offsetHeight
 904         isDocument(el) ? el.documentElement['offset' + Dimension] :
 905         (offset = this.offset()) && offset[dimension]
 906       else return this.each(function(idx){
 907         el = $(this)
 908         el.css(dimension, funcArg(this, value, idx, el[dimension]()))
 909       })
 910     }
 911   })
 912 
 913   function traverseNode(node, fun) {
 914     fun(node)
 915     for (var key in node.childNodes) traverseNode(node.childNodes[key], fun)
 916   }
 917 
 918   // Generate the `after`, `prepend`, `before`, `append`,
 919   // `insertAfter`, `insertBefore`, `appendTo`, and `prependTo` methods.
 920   adjacencyOperators.forEach(function(operator, operatorIndex) {
 921     var inside = operatorIndex % 2 //=> prepend, append
 922 
 923     $.fn[operator] = function(){
 924       // arguments can be nodes, arrays of nodes, Zepto objects and HTML strings
 925       var argType, nodes = $.map(arguments, function(arg) {
 926             argType = type(arg)
 927             return argType == "object" || argType == "array" || arg == null ?
 928               arg : zepto.fragment(arg)
 929           }),
 930           parent, copyByClone = this.length > 1 //如果集合的长度大于集,则需要clone被插入的节点
 931       if (nodes.length < 1) return this
 932 
 933       return this.each(function(_, target){
 934         parent = inside ? target : target.parentNode
 935 
 936         //通过改变target将after,prepend,append操作转成before操作,insertBefore的第二个参数为null时等于appendChild操作
 937         target = operatorIndex == 0 ? target.nextSibling :
 938                  operatorIndex == 1 ? target.firstChild :
 939                  operatorIndex == 2 ? target :
 940                  null
 941 
 942         nodes.forEach(function(node){
 943           if (copyByClone) node = node.cloneNode(true)
 944           else if (!parent) return $(node).remove()
 945             
 946           //插入节点后,如果被插入的节点是SCRIPT,则执行里面的内容并将window设为上下文
 947           traverseNode(parent.insertBefore(node, target), function(el){
 948             if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' &&
 949                (!el.type || el.type === 'text/javascript') && !el.src)
 950               window['eval'].call(window, el.innerHTML)
 951           })
 952         })
 953       })
 954     }
 955 
 956     // after    => insertAfter
 957     // prepend  => prependTo
 958     // before   => insertBefore
 959     // append   => appendTo
 960     $.fn[inside ? operator+'To' : 'insert'+(operatorIndex ? 'Before' : 'After')] = function(html){
 961       $(html)[operator](this)
 962       return this
 963     }
 964   })
 965 
 966   zepto.Z.prototype = $.fn
 967 
 968   // Export internal API functions in the `$.zepto` namespace
 969   zepto.uniq = uniq
 970   zepto.deserializeValue = deserializeValue
 971   $.zepto = zepto
 972 
 973   return $
 974 })()
 975 
 976 window.Zepto = Zepto
 977 '$' in window || (window.$ = Zepto)
 978 
 979 ;(function($){
 980   function detect(ua){
 981     var os = this.os = {}, browser = this.browser = {},
 982       webkit = ua.match(/WebKit\/([\d.]+)/),
 983       android = ua.match(/(Android)\s+([\d.]+)/),
 984       ipad = ua.match(/(iPad).*OS\s([\d_]+)/),
 985       iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/),
 986       webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/),
 987       touchpad = webos && ua.match(/TouchPad/),
 988       kindle = ua.match(/Kindle\/([\d.]+)/),
 989       silk = ua.match(/Silk\/([\d._]+)/),
 990       blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/),
 991       bb10 = ua.match(/(BB10).*Version\/([\d.]+)/),
 992       rimtabletos = ua.match(/(RIM\sTablet\sOS)\s([\d.]+)/),
 993       playbook = ua.match(/PlayBook/),
 994       chrome = ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/),
 995       firefox = ua.match(/Firefox\/([\d.]+)/)
 996 
 997     // Todo: clean this up with a better OS/browser seperation:
 998     // - discern (more) between multiple browsers on android
 999     // - decide if kindle fire in silk mode is android or not
1000     // - Firefox on Android doesn't specify the Android version
1001     // - possibly devide in os, device and browser hashes
1002 
1003     if (browser.webkit = !!webkit) browser.version = webkit[1]
1004 
1005     if (android) os.android = true, os.version = android[2]
1006     if (iphone) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.')
1007     if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.')
1008     if (webos) os.webos = true, os.version = webos[2]
1009     if (touchpad) os.touchpad = true
1010     if (blackberry) os.blackberry = true, os.version = blackberry[2]
1011     if (bb10) os.bb10 = true, os.version = bb10[2]
1012     if (rimtabletos) os.rimtabletos = true, os.version = rimtabletos[2]
1013     if (playbook) browser.playbook = true
1014     if (kindle) os.kindle = true, os.version = kindle[1]
1015     if (silk) browser.silk = true, browser.version = silk[1]
1016     if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true
1017     if (chrome) browser.chrome = true, browser.version = chrome[1]
1018     if (firefox) browser.firefox = true, browser.version = firefox[1]
1019 
1020     os.tablet = !!(ipad || playbook || (android && !ua.match(/Mobile/)) || (firefox && ua.match(/Tablet/)))
1021     os.phone  = !!(!os.tablet && (android || iphone || webos || blackberry || bb10 ||
1022       (chrome && ua.match(/Android/)) || (chrome && ua.match(/CriOS\/([\d.]+)/)) || (firefox && ua.match(/Mobile/))))
1023   }
1024 
1025   detect.call($, navigator.userAgent)
1026   // make available to unit tests
1027   $.__detect = detect
1028 
1029 })(Zepto)
1030 
1031 /* 
1032 事件处理部份
1033  */
1034 ;(function($){
1035   var $$ = $.zepto.qsa, handlers = {}, _zid = 1, specialEvents={},
1036       hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' }
1037 
1038   specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents'
1039 
1040   //取element的唯一标示符,如果没有,则设置一个并返回
1041   function zid(element) {
1042     return element._zid || (element._zid = _zid++)
1043   }
1044   //查找绑定在元素上的指定类型的事件处理函数集合
1045   function findHandlers(element, event, fn, selector) {
1046     event = parse(event)
1047     if (event.ns) var matcher = matcherFor(event.ns)
1048     return (handlers[zid(element)] || []).filter(function(handler) {
1049       return handler
1050         && (!event.e  || handler.e == event.e)//判断事件类型是否相同
1051         && (!event.ns || matcher.test(handler.ns))//判断事件命名空间是否相同
1052         //注意函数是引用类型的数据zid(handler.fn)的作用是返回handler.fn的标示符,如果没有,则给它添加一个,
1053         //这样如果fn和handler.fn引用的是同一个函数,那么fn上应该也可相同的标示符,
1054         //这里就是通过这一点来判断两个变量是否引用的同一个函数
1055         && (!fn       || zid(handler.fn) === zid(fn))
1056         && (!selector || handler.sel == selector)
1057     })
1058   }
1059   //解析事件类型,返回一个包含事件名称和事件命名空间的对象
1060   function parse(event) {
1061     var parts = ('' + event).split('.')
1062     return {e: parts[0], ns: parts.slice(1).sort().join(' ')}
1063   }
1064   //生成命名空间的正则
1065   function matcherFor(ns) {
1066     return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)')
1067   }
1068   //遍历events
1069   function eachEvent(events, fn, iterator){
1070     if ($.type(events) != "string") $.each(events, iterator)
1071     else events.split(/\s/).forEach(function(type){ iterator(type, fn) })
1072   }
1073   //通过给focus和blur事件设置为捕获来达到事件冒泡的目的
1074   function eventCapture(handler, captureSetting) {
1075     return handler.del &&
1076       (handler.e == 'focus' || handler.e == 'blur') ||
1077       !!captureSetting
1078   }
1079   
1080   //修复不支持mouseenter和mouseleave的情况
1081   function realEvent(type) {
1082     return hover[type] || type
1083   }
1084 
1085   //给元素绑定监听事件,可同时绑定多个事件类型,如['click','mouseover','mouseout'],也可以是'click mouseover mouseout'
1086   function add(element, events, fn, selector, getDelegate, capture){
1087     var id = zid(element), set = (handlers[id] || (handlers[id] = [])) //元素上已经绑定的所有事件处理函数
1088     eachEvent(events, fn, function(event, fn){
1089       var handler   = parse(event)
1090       //保存fn,下面为了处理mouseenter, mouseleave时,对fn进行了修改
1091       handler.fn    = fn
1092       handler.sel   = selector
1093       // 模仿 mouseenter, mouseleave
1094       if (handler.e in hover) fn = function(e){
1095         /* 
1096             relatedTarget为事件相关对象,只有在mouseover和mouseout事件时才有值
1097             mouseover时表示的是鼠标移出的那个对象,mouseout时表示的是鼠标移入的那个对象
1098             当related不存在,表示事件不是mouseover或者mouseout,mouseover时!$.contains(this, related)当相关对象不在事件对象内
1099             且related !== this相关对象不是事件对象时,表示鼠标已经从事件对象外部移入到了对象本身,这个时间是要执行处理函数的
1100             当鼠标从事件对象上移入到子节点的时候related就等于this了,且!$.contains(this, related)也不成立,这个时间是不需要执行处理函数的
1101         */
1102         var related = e.relatedTarget
1103         if (!related || (related !== this && !$.contains(this, related)))
1104           return handler.fn.apply(this, arguments)
1105       }
1106       //事件委托
1107       handler.del   = getDelegate && getDelegate(fn, event)
1108       var callback  = handler.del || fn
1109       handler.proxy = function (e) {
1110         var result = callback.apply(element, [e].concat(e.data))
1111         //当事件处理函数返回false时,阻止默认操作和冒泡
1112         if (result === false) e.preventDefault(), e.stopPropagation()
1113         return result
1114       }
1115       //设置处理函数的在函数集中的位置
1116       handler.i = set.length
1117       //将函数存入函数集中
1118       set.push(handler)
1119       element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
1120     })
1121   }
1122   //删除绑定在元素上的指定类型的事件监听函数,可同时删除多种事件类型指定的函数,用数组或者还空格的字符串即可,同add
1123   function remove(element, events, fn, selector, capture){
1124     var id = zid(element)
1125     eachEvent(events || '', fn, function(event, fn){
1126       findHandlers(element, event, fn, selector).forEach(function(handler){
1127         delete handlers[id][handler.i]
1128         element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
1129       })
1130     })
1131   }
1132 
1133   $.event = { add: add, remove: remove }
1134 
1135   //设置代理
1136   $.proxy = function(fn, context) {
1137     if ($.isFunction(fn)) {
1138       //如果fn是函数,则申明一个新的函数并用context作为上下文调用fn
1139       var proxyFn = function(){ return fn.apply(context, arguments) }
1140       //引用fn标示符
1141       proxyFn._zid = zid(fn)
1142       return proxyFn
1143     } else if (typeof context == 'string') {
1144       return $.proxy(fn[context], fn)
1145     } else {
1146       throw new TypeError("expected function")
1147     }
1148   }
1149 
1150   $.fn.bind = function(event, callback){
1151     return this.each(function(){
1152       add(this, event, callback)
1153     })
1154   }
1155   $.fn.unbind = function(event, callback){
1156     return this.each(function(){
1157       remove(this, event, callback)
1158     })
1159   }
1160   //绑定一次性事件监听函数
1161   $.fn.one = function(event, callback){
1162     return this.each(function(i, element){
1163       //添加函数,然后在回调函数里再删除绑定。达到一次性事件的目的
1164       add(this, event, callback, null, function(fn, type){
1165         return function(){
1166           var result = fn.apply(element, arguments) //这里执行绑定的回调
1167           remove(element, type, fn)  //删除上面的绑定
1168           return result
1169         }
1170       })
1171     })
1172   }
1173 
1174   var returnTrue = function(){return true},
1175       returnFalse = function(){return false},
1176       ignoreProperties = /^([A-Z]|layer[XY]$)/,
1177       eventMethods = {
1178         preventDefault: 'isDefaultPrevented', //是否调用过preventDefault方法
1179             //取消执行其他的事件处理函数并取消事件冒泡.如果同一个事件绑定了多个事件处理函数, 在其中一个事件处理函数中调用此方法后将不会继续调用其他的事件处理函数.
1180         stopImmediatePropagation: 'isImmediatePropagationStopped', //是否调用过stopImmediatePropagation方法,
1181         stopPropagation: 'isPropagationStopped' //是否调用过stopPropagation方法
1182       }
1183   //创建事件代理
1184   function createProxy(event) {
1185     var key, proxy = { originalEvent: event } //保存原始event
1186     for (key in event)
1187       if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key] //复制event属性至proxy
1188 
1189     //将preventDefault,stopImmediatePropagatio,stopPropagation方法定义到proxy上
1190     $.each(eventMethods, function(name, predicate) {
1191       proxy[name] = function(){
1192         this[predicate] = returnTrue
1193         return event[name].apply(event, arguments)
1194       }
1195       proxy[predicate] = returnFalse
1196     })
1197     return proxy
1198   }
1199 
1200   // emulates the 'defaultPrevented' property for browsers that have none
1201   //event.defaultPrevented返回一个布尔值,表明当前事件的默认动作是否被取消,也就是是否执行了 event.preventDefault()方法.
1202   function fix(event) {
1203     if (!('defaultPrevented' in event)) {
1204       event.defaultPrevented = false //初始值false
1205       var prevent = event.preventDefault // 引用默认preventDefault
1206       event.preventDefault = function() {//重写preventDefault
1207         this.defaultPrevented = true
1208         prevent.call(this)
1209       }
1210     }
1211   }
1212   //事件委托
1213   $.fn.delegate = function(selector, event, callback){
1214     return this.each(function(i, element){
1215       add(element, event, callback, selector, function(fn){
1216         return function(e){
1217           //如果事件对象是element里的元素,取与selector相匹配的
1218           var evt, match = $(e.target).closest(selector, element).get(0)
1219           if (match) {
1220             //evt成了一个拥有preventDefault,stopImmediatePropagatio,stopPropagation方法,currentTarge,liveFiredn属性的对象,另也有e的默认属性
1221             evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element})
1222             return fn.apply(match, [evt].concat([].slice.call(arguments, 1)))
1223           }
1224         }
1225       })
1226     })
1227   }
1228   //取消事件委托
1229   $.fn.undelegate = function(selector, event, callback){
1230     return this.each(function(){
1231       remove(this, event, callback, selector)
1232     })
1233   }
1234 
1235   $.fn.live = function(event, callback){
1236     $(document.body).delegate(this.selector, event, callback)
1237     return this
1238   }
1239   $.fn.die = function(event, callback){
1240     $(document.body).undelegate(this.selector, event, callback)
1241     return this
1242   }
1243 
1244   //on也有live和事件委托的效果,所以可以只用on来绑定事件
1245   $.fn.on = function(event, selector, callback){
1246     return !selector || $.isFunction(selector) ?
1247       this.bind(event, selector || callback) : this.delegate(selector, event, callback)
1248   }
1249   $.fn.off = function(event, selector, callback){
1250     return !selector || $.isFunction(selector) ?
1251       this.unbind(event, selector || callback) : this.undelegate(selector, event, callback)
1252   }
1253   //主动触发事件
1254   $.fn.trigger = function(event, data){
1255     if (typeof event == 'string' || $.isPlainObject(event)) event = $.Event(event)
1256     fix(event)
1257     event.data = data
1258     return this.each(function(){
1259       // items in the collection might not be DOM elements
1260       // (todo: possibly support events on plain old objects)
1261       if('dispatchEvent' in this) this.dispatchEvent(event)
1262     })
1263   }
1264 
1265   // triggers event handlers on current element just as if an event occurred,
1266   // doesn't trigger an actual event, doesn't bubble
1267   //触发元素上绑定的指定类型的事件,但是不冒泡
1268   $.fn.triggerHandler = function(event, data){
1269     var e, result
1270     this.each(function(i, element){
1271       e = createProxy(typeof event == 'string' ? $.Event(event) : event)
1272       e.data = data
1273       e.target = element
1274       //遍历元素上绑定的指定类型的事件处理函数集,按顺序执行,如果执行过stopImmediatePropagation,
1275       //那么e.isImmediatePropagationStopped()就会返回true,再外层函数返回false
1276       //注意each里的回调函数指定返回false时,会跳出循环,这样就达到的停止执行回面函数的目的
1277       $.each(findHandlers(element, event.type || event), function(i, handler){
1278         result = handler.proxy(e)
1279         if (e.isImmediatePropagationStopped()) return false
1280       })
1281     })
1282     return result
1283   }
1284 
1285   // shortcut methods for `.bind(event, fn)` for each event type
1286   ;('focusin focusout load resize scroll unload click dblclick '+
1287   'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+
1288   'change select keydown keypress keyup error').split(' ').forEach(function(event) {
1289     $.fn[event] = function(callback) {
1290       return callback ?
1291         //如果有callback回调,则认为它是绑定
1292         this.bind(event, callback) :
1293         //如果没有callback回调,则让它主动触发
1294         this.trigger(event)
1295     }
1296   })
1297 
1298   ;['focus', 'blur'].forEach(function(name) {
1299     $.fn[name] = function(callback) {
1300       if (callback) this.bind(name, callback)
1301       else this.each(function(){
1302         try { this[name]() }
1303         catch(e) {}
1304       })
1305       return this
1306     }
1307   })
1308 
1309   //根据参数创建一个event对象
1310   $.Event = function(type, props) {
1311     //当type是个对象时
1312     if (typeof type != 'string') props = type, type = props.type
1313     //创建一个event对象,如果是click,mouseover,mouseout时,创建的是MouseEvent,bubbles为是否冒泡
1314     var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true
1315     //确保bubbles的值为true或false,并将props参数的属性扩展到新创建的event对象上
1316     if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name])
1317     //初始化event对象,type为事件类型,如click,bubbles为是否冒泡,第三个参数表示是否可以用preventDefault方法来取消默认操作
1318     event.initEvent(type, bubbles, true, null, null, null, null, null, null, null, null, null, null, null, null)
1319     //添加isDefaultPrevented方法,event.defaultPrevented返回一个布尔值,表明当前事件的默认动作是否被取消,也就是是否执行了 event.preventDefault()方法.
1320     event.isDefaultPrevented = function(){ return this.defaultPrevented }
1321     return event
1322   }
1323 
1324 })(Zepto)
1325 
1326 /**
1327   Ajax处理部份
1328 **/
1329 ;(function($){
1330   var jsonpID = 0,
1331       document = window.document,
1332       key,
1333       name,
1334       rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
1335       scriptTypeRE = /^(?:text|application)\/javascript/i,
1336       xmlTypeRE = /^(?:text|application)\/xml/i,
1337       jsonType = 'application/json',
1338       htmlType = 'text/html',
1339       blankRE = /^\s*$/
1340 
1341   // trigger a custom event and return false if it was cancelled
1342   function triggerAndReturn(context, eventName, data) {
1343     var event = $.Event(eventName)
1344     $(context).trigger(event, data)
1345     return !event.defaultPrevented
1346   }
1347 
1348   // trigger an Ajax "global" event
1349   //触发 ajax的全局事件
1350   function triggerGlobal(settings, context, eventName, data) {
1351     if (settings.global) return triggerAndReturn(context || document, eventName, data)
1352   }
1353 
1354   // Number of active Ajax requests
1355   $.active = 0
1356 
1357   //settings.global为true时表示需要触发全局ajax事件
1358   //注意这里的$.active++ === 0很巧妙,用它来判断开始,因为只有$.active等于0时$.active++ === 0才成立
1359   function ajaxStart(settings) {
1360     if (settings.global && $.active++ === 0) triggerGlobal(settings, null, 'ajaxStart')
1361   }
1362   //注意这里的 !(--$.active)同上面的异曲同工,--$.active为0,则表示$.active的值为1,这样用来判断结束,也很有意思
1363   function ajaxStop(settings) {
1364     if (settings.global && !(--$.active)) triggerGlobal(settings, null, 'ajaxStop')
1365   }
1366 
1367   // triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable
1368   //触发全局ajaxBeforeSend事件,如果返回false,则取消此次请求
1369   function ajaxBeforeSend(xhr, settings) {
1370     var context = settings.context
1371     if (settings.beforeSend.call(context, xhr, settings) === false ||
1372         triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false)
1373       return false
1374 
1375     triggerGlobal(settings, context, 'ajaxSend', [xhr, settings])
1376   }
1377   function ajaxSuccess(data, xhr, settings) {
1378     var context = settings.context, status = 'success'
1379     settings.success.call(context, data, status, xhr)
1380     triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data])
1381     ajaxComplete(status, xhr, settings)
1382   }
1383   // type: "timeout", "error", "abort", "parsererror"
1384   function ajaxError(error, type, xhr, settings) {
1385     var context = settings.context
1386     settings.error.call(context, xhr, type, error)
1387     triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error])
1388     ajaxComplete(type, xhr, settings)
1389   }
1390   // status: "success", "notmodified", "error", "timeout", "abort", "parsererror"
1391   function ajaxComplete(status, xhr, settings) {
1392     var context = settings.context
1393     settings.complete.call(context, xhr, status)
1394     triggerGlobal(settings, context, 'ajaxComplete', [xhr, settings])
1395     ajaxStop(settings)
1396   }
1397 
1398   // Empty function, used as default callback
1399   function empty() {}
1400   //可参考http://zh.wikipedia.org/zh-cn/JSONP
1401   $.ajaxJSONP = function(options){
1402     if (!('type' in options)) return $.ajax(options)
1403 
1404     var callbackName = 'jsonp' + (++jsonpID), //创建回调函数名
1405       script = document.createElement('script'),
1406       //js文件加载完毕
1407       cleanup = function() {
1408         clearTimeout(abortTimeout) //清除下面的timeout事件处理
1409         $(script).remove() //移除创建的script标签,因为该文件的JS内容已经解析过了
1410         delete window[callbackName] //清除掉指定的回调函数
1411       },
1412       //取消加载
1413       abort = function(type){
1414         cleanup()
1415         // In case of manual abort or timeout, keep an empty function as callback
1416         // so that the SCRIPT tag that eventually loads won't result in an error.
1417         //这里通过将回调函数重新赋值为空函数来达到看似阻止加载JS的目的,实际上给script标签设置了src属性后,请求就已经产生了,并且不能中断
1418         if (!type || type == 'timeout') window[callbackName] = empty
1419         ajaxError(null, type || 'abort', xhr, options)
1420       },
1421       xhr = { abort: abort }, abortTimeout
1422 
1423     if (ajaxBeforeSend(xhr, options) === false) {
1424       abort('abort')
1425       return false
1426     }
1427     //成功加载后的回调函数
1428     window[callbackName] = function(data){
1429       cleanup()
1430       ajaxSuccess(data, xhr, options)
1431     }
1432 
1433     script.onerror = function() { abort('error') }
1434     //将回调函数名追加到请求地址,并赋给script,至此请求产生
1435     script.src = options.url.replace(/=\?/, '=' + callbackName)
1436     $('head').append(script)
1437     
1438     //如果设置了超时处理
1439     if (options.timeout > 0) abortTimeout = setTimeout(function(){
1440       abort('timeout')
1441     }, options.timeout)
1442 
1443     return xhr
1444   }
1445   
1446   //ajax全局设置
1447   $.ajaxSettings = {
1448     // Default type of request
1449     type: 'GET',
1450     // Callback that is executed before request
1451     beforeSend: empty,
1452     // Callback that is executed if the request succeeds
1453     success: empty,
1454     // Callback that is executed the the server drops error
1455     error: empty,
1456     // Callback that is executed on request complete (both: error and success)
1457     complete: empty,
1458     // The context for the callbacks
1459     context: null,
1460     // Whether to trigger "global" Ajax events
1461     global: true,
1462     // Transport
1463     xhr: function () {
1464       return new window.XMLHttpRequest()
1465     },
1466     // MIME types mapping
1467     accepts: {
1468       script: 'text/javascript, application/javascript',
1469       json:   jsonType,
1470       xml:    'application/xml, text/xml',
1471       html:   htmlType,
1472       text:   'text/plain'
1473     },
1474     // Whether the request is to another domain
1475     crossDomain: false,
1476     // Default timeout
1477     timeout: 0,
1478     // Whether data should be serialized to string
1479     processData: true,
1480     // Whether the browser should be allowed to cache GET responses
1481     cache: true,
1482   }
1483 
1484   //根据MIME返回相应的数据类型,用作ajax参数里的dataType用,设置预期返回的数据类型
1485   //如html,json,scirpt,xml,text
1486   function mimeToDataType(mime) {
1487     if (mime) mime = mime.split(';', 2)[0]
1488     return mime && ( mime == htmlType ? 'html' :
1489       mime == jsonType ? 'json' :
1490       scriptTypeRE.test(mime) ? 'script' :
1491       xmlTypeRE.test(mime) && 'xml' ) || 'text'
1492   }
1493   //将查询字符串追加到URL后面
1494   function appendQuery(url, query) {
1495     //注意这里的replace,将第一个匹配到的&或者&&,&?,? ?& ??替换成?,用来保证地址的正确性
1496     return (url + '&' + query).replace(/[&?]{1,2}/, '?')
1497   }
1498 
1499   // serialize payload and append it to the URL for GET requests
1500   //序列化发送到服务器上的数据,如果是GET请求,则将序列化后的数据追加到请求地址后面
1501   function serializeData(options) {
1502     //options.processData表示对于非Get请求,是否自动将 options.data转换为字符串,前提是options.data不是字符串
1503     if (options.processData && options.data && $.type(options.data) != "string")
1504       //options.traditional表示是否以$.param方法序列化
1505       options.data = $.param(options.data, options.traditional)
1506     if (options.data && (!options.type || options.type.toUpperCase() == 'GET'))
1507       //如果是GET请求,将序列化后的数据追加到请求地址后面
1508       options.url = appendQuery(options.url, options.data)
1509   }
1510 
1511   $.ajax = function(options){
1512     //注意这里不能直接将$.ajaxSettings替换掉$.extend的第一个参数,这样会改变 $.ajaxSettings里面的值
1513     //这里的做法是创建一个新对象
1514     var settings = $.extend({}, options || {})
1515     //如果它没有定义$.ajaxSettings里面的属性的时候,才去将$.ajaxSettings[key] 复制过来
1516     for (key in $.ajaxSettings) if (settings[key] === undefined) settings[key] = $.ajaxSettings[key]
1517     //执行全局ajaxStart
1518     ajaxStart(settings)
1519 
1520     //通过判断请求地址和当前页面地址的host是否相同来设置是跨域
1521     if (!settings.crossDomain) settings.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(settings.url) &&
1522       RegExp.$2 != window.location.host
1523     //如果没有设置请求地址,则取当前页面地址
1524     if (!settings.url) settings.url = window.location.toString();
1525     //将data进行转换
1526     serializeData(settings);
1527     //如果不设置缓存
1528     if (settings.cache === false) settings.url = appendQuery(settings.url, '_=' + Date.now())
1529     
1530     //如果请求的是jsonp,则将地址栏里的=?替换为callback=?,相当于一个简写
1531     var dataType = settings.dataType, hasPlaceholder = /=\?/.test(settings.url)
1532     if (dataType == 'jsonp' || hasPlaceholder) {
1533       if (!hasPlaceholder) settings.url = appendQuery(settings.url, 'callback=?')
1534       return $.ajaxJSONP(settings)
1535     }
1536 
1537     var mime = settings.accepts[dataType],
1538         baseHeaders = { },
1539         //如果请求地址没有定请求协议,则与当前页面协议相同
1540         protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol,
1541         xhr = settings.xhr(), abortTimeout
1542     //如果没有跨域
1543     if (!settings.crossDomain) baseHeaders['X-Requested-With'] = 'XMLHttpRequest'
1544     if (mime) {
1545       baseHeaders['Accept'] = mime
1546       if (mime.indexOf(',') > -1) mime = mime.split(',', 2)[0]
1547       xhr.overrideMimeType && xhr.overrideMimeType(mime)
1548     }
1549     //如果不是GET请求,设置发送信息至服务器时内容编码类型
1550     if (settings.contentType || (settings.contentType !== false && settings.data && settings.type.toUpperCase() != 'GET'))
1551       baseHeaders['Content-Type'] = (settings.contentType || 'application/x-www-form-urlencoded')
1552     settings.headers = $.extend(baseHeaders, settings.headers || {})
1553 
1554     xhr.onreadystatechange = function(){
1555       if (xhr.readyState == 4) {
1556         xhr.onreadystatechange = empty;
1557         clearTimeout(abortTimeout)
1558         var result, error = false
1559         //根据状态来判断请求是否成功
1560         //状态>=200 && < 300 表示成功
1561         //状态 == 304 表示文件未改动过,也可认为成功
1562         //如果是取要本地文件那也可以认为是成功的,xhr.status == 0是在直接打开页面时发生请求时出现的状态,也就是不是用localhost的形式访问的页面的情况
1563         if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:')) {
1564           //获取返回的数据类型
1565           dataType = dataType || mimeToDataType(xhr.getResponseHeader('content-type'))
1566           result = xhr.responseText
1567 
1568           try {
1569             // http://perfectionkills.com/global-eval-what-are-the-options/
1570             if (dataType == 'script')    (1,eval)(result) //如果返回的数据类型是JS
1571             else if (dataType == 'xml')  result = xhr.responseXML
1572             else if (dataType == 'json') result = blankRE.test(result) ? null : $.parseJSON(result)
1573           } catch (e) { error = e }
1574           //如果解析出错,则执行全局parsererror事件
1575           if (error) ajaxError(error, 'parsererror', xhr, settings)
1576           //否则执行ajaxSuccess
1577           else ajaxSuccess(result, xhr, settings)
1578         } else {
1579           //如果请求出错,则根据xhr.status来执行相应的错误处理函数
1580           ajaxError(null, xhr.status ? 'error' : 'abort', xhr, settings)
1581         }
1582       }
1583     }
1584 
1585     var async = 'async' in settings ? settings.async : true
1586     xhr.open(settings.type, settings.url, async)
1587     //设置请求头信息
1588     for (name in settings.headers) xhr.setRequestHeader(name, settings.headers[name])
1589 
1590     //如果ajaxBeforeSend函数返回的false,则取消此次请示
1591     if (ajaxBeforeSend(xhr, settings) === false) {
1592       xhr.abort()
1593       return false
1594     }
1595 
1596     //当设置了settings.timeout,则在超时后取消请求,并执行timeout事件处理函数
1597     if (settings.timeout > 0) abortTimeout = setTimeout(function(){
1598         xhr.onreadystatechange = empty
1599         xhr.abort()
1600         ajaxError(null, 'timeout', xhr, settings)
1601       }, settings.timeout)
1602 
1603     // avoid sending empty string (#319)
1604     xhr.send(settings.data ? settings.data : null)
1605     return xhr
1606   }
1607 
1608   // handle optional data/success arguments
1609   //将参数转换成ajax函数指定的参数格式
1610   function parseArguments(url, data, success, dataType) {
1611     var hasData = !$.isFunction(data) //如果data是function,则认为它是请求成功后的回调
1612     return {
1613       url:      url,
1614       data:     hasData  ? data : undefined, //如果data不是function实例
1615       success:  !hasData ? data : $.isFunction(success) ? success : undefined,
1616       dataType: hasData  ? dataType || success : success
1617     }
1618   }
1619   
1620   //简单的get请求
1621   $.get = function(url, data, success, dataType){
1622     return $.ajax(parseArguments.apply(null, arguments))
1623   }
1624 
1625   $.post = function(url, data, success, dataType){
1626     var options = parseArguments.apply(null, arguments)
1627     options.type = 'POST'
1628     return $.ajax(options)
1629   }
1630 
1631   $.getJSON = function(url, data, success){
1632     var options = parseArguments.apply(null, arguments)
1633     options.dataType = 'json'
1634     return $.ajax(options)
1635   }
1636   
1637   //这里的url可以是http://www.xxxx.com selector这种形式,就是对加载进来的HTML对行一个筛选
1638   $.fn.load = function(url, data, success){
1639     if (!this.length) return this
1640     //将请求地址用空格分开
1641     var self = this, parts = url.split(/\s/), selector,
1642         options = parseArguments(url, data, success),
1643         callback = options.success
1644     if (parts.length > 1) options.url = parts[0], selector = parts[1]
1645     //要对成功后的回调函数进行一个改写,因为需要将加载进来的HTML添加进当前集合
1646     options.success = function(response){
1647       //selector就是对请求到的数据就行一个筛选的条件,比如只获取数据里的类名为.test的标签
1648       self.html(selector ?
1649         $('<div>').html(response.replace(rscript, "")).find(selector)
1650         : response)
1651       //这里才是你写的回调
1652       callback && callback.apply(self, arguments)
1653     }
1654     $.ajax(options)
1655     return this
1656   }
1657 
1658   var escape = encodeURIComponent
1659 
1660   function serialize(params, obj, traditional, scope){
1661     var type, array = $.isArray(obj) 
1662     $.each(obj, function(key, value) {
1663       type = $.type(value)
1664       //scope用作处理value也是object或者array的情况
1665       //traditional表示是否以传统的方式拼接数据,
1666       //传统的意思就是比如现有一个数据{a:[1,2,3]},转成查询字符串后结果为'a=1&a=2&a=3'
1667       //非传统的的结果则是a[]=1&a[]=2&a[]=3
1668       if (scope) key = traditional ? scope : scope + '[' + (array ? '' : key) + ']'
1669       // handle data in serializeArray() format
1670       //当处理的数据为[{},{},{}]这种情况的时候,一般指的是序列化表单后的结果
1671       if (!scope && array) params.add(value.name, value.value)
1672       // recurse into nested objects
1673       //当value值是数组或者是对象且不是按传统的方式序列化的时候,需要再次遍历value
1674       else if (type == "array" || (!traditional && type == "object"))
1675         serialize(params, value, traditional, key)
1676       else params.add(key, value)
1677     })
1678   }
1679   //将obj转换为查询字符串的格式,traditional表示是否转换成传统的方式,至于传统的方式的意思看上面的注释
1680   $.param = function(obj, traditional){
1681     var params = []
1682     //注意这里将add方法定到params,所以下面serialize时才不需要返回数据
1683     params.add = function(k, v){ this.push(escape(k) + '=' + escape(v)) }
1684     serialize(params, obj, traditional)
1685     return params.join('&').replace(/%20/g, '+')
1686   }
1687 })(Zepto)
1688 
1689 ;(function ($) {
1690   //序列化表单,返回一个类似[{name:value},{name2:value2}]的数组
1691   $.fn.serializeArray = function () {
1692     var result = [], el
1693     //将集合中的第一个表单里的所有表单元素转成数组后进行遍历
1694     $( Array.prototype.slice.call(this.get(0).elements) ).each(function () {
1695       el = $(this)
1696       var type = el.attr('type')
1697       //判断其type属性,排除fieldset,submi,reset,button以及没有被选中的radio和checkbox
1698       if (this.nodeName.toLowerCase() != 'fieldset' &&
1699         !this.disabled && type != 'submit' && type != 'reset' && type != 'button' &&
1700         //注意这里的写法,当元素既不是radio也不是checkbox时,直接返回true,
1701         //当元素是radio或者checkbox时,会执行后面的this.checked,当radio或者checkbox被选中时this.checked得到true值
1702         //这样就可以筛选中被选中的radio和checkbox了
1703         ((type != 'radio' && type != 'checkbox') || this.checked))
1704         result.push({
1705           name: el.attr('name'),
1706           value: el.val()
1707         })
1708     })
1709     return result
1710   }
1711   //将表单的值转成name1=value1&name2=value2的形式
1712   $.fn.serialize = function () {
1713     var result = []
1714     this.serializeArray().forEach(function (elm) {
1715       result.push( encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value) )
1716     })
1717     return result.join('&')
1718   }
1719   //表单提交
1720   $.fn.submit = function (callback) {
1721     if (callback) this.bind('submit', callback)
1722     else if (this.length) {
1723       var event = $.Event('submit')
1724       this.eq(0).trigger(event)
1725       if (!event.defaultPrevented) this.get(0).submit()
1726     }
1727     return this
1728   }
1729 
1730 })(Zepto)
1731 
1732 //CSS3动画
1733 ;(function($, undefined){
1734   var prefix = '', eventPrefix, endEventName, endAnimationName,
1735     vendors = { Webkit: 'webkit', Moz: '', O: 'o', ms: 'MS' },
1736     document = window.document, testEl = document.createElement('div'),
1737     supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i,
1738     transform,
1739     transitionProperty, transitionDuration, transitionTiming,
1740     animationName, animationDuration, animationTiming,
1741     cssReset = {}
1742   //将驼峰式的字符串转成用-分隔的小写形式,如borderWidth ==> border-width
1743   function dasherize(str) { return downcase(str.replace(/([a-z])([A-Z])/, '$1-$2')) }
1744   function downcase(str) { return str.toLowerCase() }
1745   //用于修正事件名
1746   function normalizeEvent(name) { return eventPrefix ? eventPrefix + name : downcase(name) }
1747 
1748   //根据浏览器的特性设置CSS属性前轻辍和事件前辍,比如浏览器内核是webkit
1749   //那么用于设置CSS属性的前辍prefix就等于'-webkit-',用来修正事件名的前辍eventPrefix就是Webkit
1750   $.each(vendors, function(vendor, event){
1751     if (testEl.style[vendor + 'TransitionProperty'] !== undefined) {
1752       prefix = '-' + downcase(vendor) + '-'
1753       eventPrefix = event
1754       return false
1755     }
1756   })
1757 
1758   transform = prefix + 'transform'
1759   cssReset[transitionProperty = prefix + 'transition-property'] =
1760   cssReset[transitionDuration = prefix + 'transition-duration'] =
1761   cssReset[transitionTiming   = prefix + 'transition-timing-function'] =
1762   cssReset[animationName      = prefix + 'animation-name'] =
1763   cssReset[animationDuration  = prefix + 'animation-duration'] =
1764   cssReset[animationTiming    = prefix + 'animation-timing-function'] = ''
1765 
1766   $.fx = {
1767     off: (eventPrefix === undefined && testEl.style.transitionProperty === undefined),
1768     speeds: { _default: 400, fast: 200, slow: 600 },
1769     cssPrefix: prefix,
1770     transitionEnd: normalizeEvent('TransitionEnd'),
1771     animationEnd: normalizeEvent('AnimationEnd')
1772   }
1773 
1774   $.fn.animate = function(properties, duration, ease, callback){
1775     if ($.isPlainObject(duration))
1776       ease = duration.easing, callback = duration.complete, duration = duration.duration
1777     //如果duration是数字时,表示动画持续时间,如果是字符串,则从$.fx.speeds中取出相对应的值,如果没有找到相应的值,对取默认值
1778     if (duration) duration = (typeof duration == 'number' ? duration :
1779                     ($.fx.speeds[duration] || $.fx.speeds._default)) / 1000
1780     return this.anim(properties, duration, ease, callback)
1781   }
1782 
1783   $.fn.anim = function(properties, duration, ease, callback){
1784     var key, cssValues = {}, cssProperties, transforms = '',
1785         that = this, wrappedCallback, endEvent = $.fx.transitionEnd
1786     //动画持续时间默认值
1787     if (duration === undefined) duration = 0.4
1788     //如果浏览器不支持CSS3的动画,则duration=0,意思就是直接跳转最终值
1789     if ($.fx.off) duration = 0
1790     
1791     //如果properties是一个动画名keyframe
1792     if (typeof properties == 'string') {
1793       // keyframe animation
1794       cssValues[animationName] = properties
1795       cssValues[animationDuration] = duration + 's'
1796       cssValues[animationTiming] = (ease || 'linear')
1797       endEvent = $.fx.animationEnd
1798     } else {
1799       cssProperties = []
1800       // CSS transitions
1801       for (key in properties)
1802         //如果设置 的CSS属性是变形之类的
1803         if (supportedTransforms.test(key)) transforms += key + '(' + properties[key] + ') '
1804         else cssValues[key] = properties[key], cssProperties.push(dasherize(key))
1805 
1806       if (transforms) cssValues[transform] = transforms, cssProperties.push(transform)
1807       if (duration > 0 && typeof properties === 'object') {
1808         cssValues[transitionProperty] = cssProperties.join(', ')
1809         cssValues[transitionDuration] = duration + 's'
1810         cssValues[transitionTiming] = (ease || 'linear')
1811       }
1812     }
1813 
1814     wrappedCallback = function(event){
1815       if (typeof event !== 'undefined') {
1816         if (event.target !== event.currentTarget) return // makes sure the event didn't bubble from "below"
1817         $(event.target).unbind(endEvent, wrappedCallback)
1818       }
1819       $(this).css(cssReset)
1820       callback && callback.call(this)
1821     }
1822     //当可以执行动画的时候,那么动画结束后会执行回调,
1823     //如果不支持持续动画,在直接设置最终值后,不会执行动画结束回调
1824     if (duration > 0) this.bind(endEvent, wrappedCallback)
1825 
1826     // trigger page reflow so new elements can animate
1827     this.size() && this.get(0).clientLeft
1828     
1829     //设置
1830     this.css(cssValues)
1831 
1832     //当持续时间小于等于0时,立刻还原
1833     if (duration <= 0) setTimeout(function() {
1834       that.each(function(){ wrappedCallback.call(this) })
1835     }, 0)
1836 
1837     return this
1838   }
1839 
1840   testEl = null
1841 })(Zepto)
复制代码
 
 
分类: javascript
标签: js zepto
原文地址:https://www.cnblogs.com/Leo_wl/p/2989867.html