好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

zepto源码注释

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测试数据/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测试数据/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测试数据plete.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测试数据/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://HdhCmsTestxxxx测试数据 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测试数据plete, 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

作者: Leo_wl

    

出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于zepto源码注释的详细内容...

  阅读:39次