Angular源代码学习笔记-原创

时间:2014年12月15日 14:15:10javascript

   1 /**
   2  * @license AngularJS v1.3.0-beta.15
   3  * (c) 2010-2014 Google, Inc. http://angularjs.org
   4  * License: MIT
   5  */
   6 (function(window, document, undefined) {'use strict';
   7 
   8     /**
   9      * @description
  10      *
  11      * This object provides a utility for producing rich Error messages within
  12      * Angular. It can be called as follows:
  13      *当Angular在发生错误的时候,这个对象提供一个处理错误的工具类,它的调用方式以下:
  14      * 例如:
  15      * var exampleMinErr = minErr('example');
  16      * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
  17      *
  18      * The above creates an instance of minErr in the example namespace. The
  19      * resulting error will have a namespaced error code of example.one.  The
  20      * resulting error will replace {0} with the value of foo, and {1} with the
  21      * value of bar. The object is not restricted in the number of arguments it can
  22      * take.
  23      *在上面的代码中,在example命名空间中建立了一个minErr的实例。错误的结果会在example,
  24      *里面生成一个错误码的example.one命名空间。这个错误的结果会用foo的值和bar的值,
  25      *来进行替换前面的点位符{0},{1}.这个对象不严格必须给定错误参数,所以错误参数无关紧要。
  26      *
  27      * If fewer arguments are specified than necessary for interpolation, the extra
  28      * interpolation markers will be preserved in the final string.
  29      *若是指定的错误对象中的错误参数,则它会保留在最终生成的字符串中。
  30      *
  31      * Since data will be parsed statically during a build step, some restrictions
  32      * are applied with respect to how minErr instances are created and called.
  33      * Instances should have names of the form namespaceMinErr for a minErr created
  34      * using minErr('namespace') . Error codes, namespaces and template strings
  35      * should all be static strings, not variables or general expressions.
  36      *在编译阶段,错误参数中的数据会本身生成与解析,错误对象中的一些限制将会监视错误对象的
  37      实例该如何被建立与如何被调用。
  38      *
  39      * @param {string} module The namespace to use for the new minErr instance.
  40      * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
  41      */
  42 
  43 //module:模板名称,将会用于新的错误对象的实例中
  44     function minErr(module) {
  45         return function () {//直接进行返回操做
  46             //获取错误参数中的错误标识符,如,上面示例中的one
  47             var code = arguments[0],
  48             //获取错误对象模板的前缀
  49                 prefix = '[' + (module ? module + ':' : '') + code + '] ',
  50                 template = arguments[1],
  51                 templateArgs = arguments,
  52                 //这个属于偏置函数
  53                 stringify = function (obj) {
  54                     if (typeof obj === 'function') {//若是参数是一个函数
  55                         //返回的结果,如:{'item in items '}-->''
  56                         return obj.toString().replace(/ \{[\s\S]*$/, '');
  57                     } else if (typeof obj === 'undefined') {
  58                         return 'undefined';
  59                     } else if (typeof obj !== 'string') {
  60                         return JSON.stringify(obj);//强制参数转换为Json的字符串形式
  61                     }
  62                     return obj;
  63                 },
  64                 message, i;
  65 
  66 
  67             message = prefix + template.replace(/\{\d+\}/g, function (match) {
  68                 var index = +match.slice(1, -1), arg;
  69 
  70                 if (index + 2 < templateArgs.length) {
  71                     arg = templateArgs[index + 2];
  72                     if (typeof arg === 'function') {
  73                         return arg.toString().replace(/ ?\{[\s\S]*$/, '');
  74                     } else if (typeof arg === 'undefined') {
  75                         return 'undefined';
  76                     } else if (typeof arg !== 'string') {
  77                         return toJson(arg);
  78                     }
  79                     return arg;
  80                 }
  81                 return match;
  82             });
  83 
  84             message = message + '\nhttp://errors.angularjs.org/1.3.0-beta.15/' +
  85             (module ? module + '/' : '') + code;
  86             for (i = 2; i < arguments.length; i++) {
  87                 message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
  88                 encodeURIComponent(stringify(arguments[i]));
  89             }
  90 
  91             return new Error(message);
  92         };
  93     }
  94 
  95     /* We need to tell jshint what variables are being exported */
  96     /* global angular: true,
  97      msie: true,
  98      jqLite: true,
  99      jQuery: true,
 100      slice: true,
 101      push: true,
 102      toString: true,
 103      ngMinErr: true,
 104      angularModule: true,
 105      nodeName_: true,
 106      uid: true,
 107      REGEX_STRING_REGEXP: true,
 108      VALIDITY_STATE_PROPERTY: true,
 109 
 110      lowercase: true,
 111      uppercase: true,
 112      manualLowercase: true,
 113      manualUppercase: true,
 114      nodeName_: true,
 115      isArrayLike: true,
 116      forEach: true,
 117      sortedKeys: true,
 118      forEachSorted: true,
 119      reverseParams: true,
 120      nextUid: true,
 121      setHashKey: true,
 122      extend: true,
 123      int: true,
 124      inherit: true,
 125      noop: true,
 126      identity: true,
 127      valueFn: true,
 128      isUndefined: true,
 129      isDefined: true,
 130      isObject: true,
 131      isString: true,
 132      isNumber: true,
 133      isDate: true,
 134      isArray: true,
 135      isFunction: true,
 136      isRegExp: true,
 137      isWindow: true,
 138      isScope: true,
 139      isFile: true,
 140      isBlob: true,
 141      isBoolean: true,
 142      trim: true,
 143      isElement: true,
 144      makeMap: true,
 145      map: true,
 146      size: true,
 147      includes: true,
 148      indexOf: true,
 149      arrayRemove: true,
 150      isLeafNode: true,
 151      copy: true,
 152      shallowCopy: true,
 153      equals: true,
 154      csp: true,
 155      concat: true,
 156      sliceArgs: true,
 157      bind: true,
 158      toJsonReplacer: true,
 159      toJson: true,
 160      fromJson: true,
 161      startingTag: true,
 162      tryDecodeURIComponent: true,
 163      parseKeyValue: true,
 164      toKeyValue: true,
 165      encodeUriSegment: true,
 166      encodeUriQuery: true,
 167      angularInit: true,
 168      bootstrap: true,
 169      snake_case: true,
 170      bindJQuery: true,
 171      assertArg: true,
 172      assertArgFn: true,
 173      assertNotHasOwnProperty: true,
 174      getter: true,
 175      getBlockElements: true,
 176      hasOwnProperty: true,
 177      */
 178 
 179 ////////////////////////////////////
 180 
 181     /**
 182      * @ngdoc module
 183      * @name ng
 184      * @module ng
 185      * @description
 186      *
 187      * # ng (core module)
 188      * The ng module is loaded by default when an AngularJS application is started. The module itself
 189      * contains the essential components for an AngularJS application to function. The table below
 190      * lists a high level breakdown of each of the services/factories, filters, directives and testing
 191      * components available within this core module.
 192      *
 193      * <div doc-module-components="ng"></div>
 194      *
 195      * ng模板默认在Angular应用程序启动的时候会被自动加载。这个模板包含了Angular应用程序运行所必须的组件。
 196      *  下表列出了在这个核心的模块中所包含的服务/工厂,过滤器,指令和一些测试的组件
 197      */
 198 
 199     var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
 200 
 201 // The name of a form control's ValidityState property.
 202 // This is used so that it's possible for internal tests to create mock ValidityStates.
 203     var VALIDITY_STATE_PROPERTY = 'validity';
 204 
 205     /**
 206      * @ngdoc function
 207      * @name angular.lowercase
 208      * @module ng
 209      * @kind function
 210      *
 211      * @description Converts the specified string to lowercase.
 212      * @param {string} string String to be converted to lowercase.
 213      * @returns {string} Lowercased string.
 214      */
 215         //转换指定的字符串为小写
 216     var lowercase = function(string){return isString(string) ? string.toLowerCase() : string;};
 217     var hasOwnProperty = Object.prototype.hasOwnProperty;
 218 
 219     /**
 220      * @ngdoc function
 221      * @name angular.uppercase
 222      * @module ng
 223      * @kind function
 224      *
 225      * @description Converts the specified string to uppercase.
 226      * @param {string} string String to be converted to uppercase.
 227      * @returns {string} Uppercased string.
 228      */
 229         //转换指定的字符串为大写
 230     var uppercase = function(string){return isString(string) ? string.toUpperCase() : string;};
 231 
 232 
 233     var manualLowercase = function(s) {
 234         /* jshint bitwise: false */
 235         return isString(s)
 236             ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
 237             : s;
 238     };
 239     var manualUppercase = function(s) {
 240         /* jshint bitwise: false */
 241         return isString(s)
 242             ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
 243             : s;
 244     };
 245 
 246 
 247 // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
 248 // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
 249 // with correct but slower alternatives.
 250     if ('i' !== 'I'.toLowerCase()) {
 251         lowercase = manualLowercase;
 252         uppercase = manualUppercase;
 253     }
 254 
 255 
 256     var /** holds major version number for IE or NaN for real browsers */
 257         msie,
 258         jqLite,           // delay binding since jQuery could be loaded after us.
 259         jQuery,           // delay binding
 260         slice             = [].slice,//取出指定位置的数组数据
 261         push              = [].push,//把指定的数据放到数据里面
 262         toString          = Object.prototype.toString,//重写Object原型中的toString方法
 263         ngMinErr          = minErr('ng'),//定义一个全局的错误对象,对应的错误参数为,ng
 264 
 265         /** @name angular */
 266             //判断系统是否注册了angular应用程序,若是没有注册则注册angular应用程序的为空对象
 267         angular           = window.angular || (window.angular = {}),
 268         angularModule,
 269         nodeName_,
 270         uid               = 0;
 271 
 272     /**
 273      * IE 11 changed the format of the UserAgent string.
 274      * See http://msdn.microsoft.com/en-us/library/ms537503.aspx
 275      */
 276     //判断浏览器的类型
 277     msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]);
 278     if (isNaN(msie)) {
 279         msie = int((/trident\/.*; rv:(\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]);
 280     }
 281 
 282 
 283     /**
 284      * @private
 285      * @param {*} obj
 286      * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
 287      *                   String ...)
 288      */
 289     //判断传入的对象obj是不是数组或有相似数组的行为,它能够是json字符串或数组
 290     function isArrayLike(obj) {
 291         if (obj == null || isWindow(obj)) {//为空或window对象,则返回false
 292             return false;
 293         }
 294 
 295         var length = obj.length;//获取数组的长度
 296 
 297         if (obj.nodeType === 1 && length) {//判断obj的节点类型,若是为元素,则返回true
 298             return true;
 299         }
 300 
 301         return isString(obj) || isArray(obj) || length === 0 ||
 302             typeof length === 'number' && length > 0 && (length - 1) in obj;
 303     }
 304 
 305     /**
 306      * @ngdoc function
 307      * @name angular.forEach
 308      * @module ng
 309      * @kind function
 310      *
 311      * @description
 312      * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
 313      * object or an array. The `iterator` function is invoked with `iterator(value, key)`, where `value`
 314      * is the value of an object property or an array element and `key` is the object property key or
 315      * array element index. Specifying a `context` for the function is optional.
 316      *
 317      * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
 318      * using the `hasOwnProperty` method.
 319      *
 320      ```js
 321      var values = {name: 'misko', gender: 'male'};
 322      var log = [];
 323      angular.forEach(values, function(value, key) {
 324        this.push(key + ': ' + value);
 325      }, log);
 326      expect(log).toEqual(['name: misko', 'gender: male']);
 327      ```
 328      *
 329      * @param {Object|Array} obj Object to iterate over.//能够是一个对象或一个数组
 330      * @param {Function} iterator Iterator function.//迭代器是一个函数
 331      * @param {Object=} context Object to become context (`this`) for the iterator function.//这个对象是一个上下文对象充当this的角色
 332      * @returns {Object|Array} Reference to `obj`.
 333      */
 334     //其中context可选参数,iterator是一个迭代器,它是一个函数
 335     //它不能获取的继承的属性,由于程序中用hasOwnProperty来过滤了
 336     function forEach(obj, iterator, context) {
 337         var key, length;
 338         if (obj) {
 339             if (isFunction(obj)) {
 340                 for (key in obj) {
 341                     // Need to check if hasOwnProperty exists,
 342                     // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
 343                     if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
 344                         iterator.call(context, obj[key], key);
 345                     }
 346                 }
 347             } else if (isArray(obj) || isArrayLike(obj)) {
 348                 for (key = 0, length = obj.length; key < length; key++) {
 349                     iterator.call(context, obj[key], key);
 350                 }
 351             } else if (obj.forEach && obj.forEach !== forEach) {
 352                 obj.forEach(iterator, context);
 353             } else {
 354                 for (key in obj) {
 355                     if (obj.hasOwnProperty(key)) {
 356                         iterator.call(context, obj[key], key);
 357                     }
 358                 }
 359             }
 360         }
 361         return obj;
 362     }
 363 
 364     function sortedKeys(obj) {//按照键来排序
 365         var keys = [];
 366         for (var key in obj) {
 367             if (obj.hasOwnProperty(key)) {
 368                 keys.push(key);
 369             }
 370         }
 371         return keys.sort();//采用数组默认的排序方式
 372     }
 373 
 374     function forEachSorted(obj, iterator, context) {//先按照键排序,而后再遍历|迭代
 375         var keys = sortedKeys(obj);
 376         for ( var i = 0; i < keys.length; i++) {
 377             iterator.call(context, obj[keys[i]], keys[i]);
 378         }
 379         return keys;
 380     }
 381 
 382 
 383     /**
 384      * when using forEach the params are value, key, but it is often useful to have key, value.
 385      * @param {function(string, *)} iteratorFn
 386      * @returns {function(*, string)}
 387      */
 388     function reverseParams(iteratorFn) {
 389         return function(value, key) { iteratorFn(key, value); };
 390     }
 391 
 392     /**
 393      * A consistent way of creating unique IDs in angular.
 394      *
 395      * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
 396      * we hit number precision issues in JavaScript.
 397      *
 398      * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
 399      *
 400      * @returns {number} an unique alpha-numeric string
 401      */
 402     function nextUid() {//生成一个常量,它在andular里面是惟一的
 403         return ++uid;
 404     }
 405 
 406 
 407     /**
 408      * Set or clear the hashkey for an object.
 409      * @param obj object//参数是一个对象
 410      * @param h the hashkey (!truthy to delete the hashkey)
 411      */
 412     function setHashKey(obj, h) {
 413         if (h) {//若是存在就设置
 414             obj.$$hashKey = h;
 415         }
 416         else {//若是不存在则删除
 417             delete obj.$$hashKey;
 418         }
 419     }
 420 
 421     /**
 422      * @ngdoc function
 423      * @name angular.extend
 424      * @module ng
 425      * @kind function
 426      *
 427      * @description
 428      * Extends the destination object `dst` by copying all of the properties from the `src` object(s)
 429      * to `dst`. You can specify multiple `src` objects.
 430      *
 431      * @param {Object} dst Destination object.//最终的对象
 432      * @param {...Object} src Source object(s).//源对象
 433      * @returns {Object} Reference to `dst`.//返回最终的对象
 434      */
 435     function extend(dst) {//在javascript中实现继承
 436         var h = dst.$$hashKey;
 437         forEach(arguments, function(obj) {//这里采用拷贝属性的方式实现继承
 438             if (obj !== dst) {
 439                 forEach(obj, function(value, key) {
 440                     dst[key] = value;
 441                 });
 442             }
 443         });
 444 
 445         setHashKey(dst,h);
 446         return dst;
 447     }
 448 
 449     function int(str) {//转换为整形
 450         return parseInt(str, 10);
 451     }
 452 
 453 
 454     function inherit(parent, extra) {//继承
 455         return extend(new (extend(function() {}, {prototype:parent}))(), extra);
 456     }
 457 
 458     /**
 459      * @ngdoc function
 460      * @name angular.noop
 461      * @module ng
 462      * @kind function
 463      *
 464      * @description
 465      * A function that performs no operations. This function can be useful when writing code in the
 466      * functional style.
 467      ```js
 468      function foo(callback) {
 469        var result = calculateResult();
 470        (callback || angular.noop)(result);
 471      }
 472      ```
 473      */
 474     function noop() {}//在写样式代码的时候会被用到
 475     noop.$inject = [];
 476 
 477 
 478     /**
 479      * @ngdoc function
 480      * @name angular.identity
 481      * @module ng
 482      * @kind function
 483      *
 484      * @description
 485      * A function that returns its first argument. This function is useful when writing code in the
 486      * functional style.
 487      *
 488      ```js
 489      function transformer(transformationFn, value) {
 490        return (transformationFn || angular.identity)(value);
 491      };
 492      ```
 493      */
 494     function identity($) {return $;}//在写样式代码的时候会被用到//定义angular的标识
 495     identity.$inject = [];
 496 
 497 
 498     function valueFn(value) {return function() {return value;};}
 499 
 500     /**
 501      * @ngdoc function
 502      * @name angular.isUndefined
 503      * @module ng
 504      * @kind function
 505      *
 506      * @description
 507      * Determines if a reference is undefined.
 508      *
 509      * @param {*} value Reference to check.
 510      * @returns {boolean} True if `value` is undefined.
 511      */
 512     function isUndefined(value){return typeof value === 'undefined';}
 513 
 514 
 515     /**
 516      * @ngdoc function
 517      * @name angular.isDefined
 518      * @module ng
 519      * @kind function
 520      *
 521      * @description
 522      * Determines if a reference is defined.
 523      *
 524      * @param {*} value Reference to check.
 525      * @returns {boolean} True if `value` is defined.
 526      */
 527     function isDefined(value){return typeof value !== 'undefined';}
 528 
 529 
 530     /**
 531      * @ngdoc function
 532      * @name angular.isObject
 533      * @module ng
 534      * @kind function
 535      *
 536      * @description
 537      * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
 538      * considered to be objects. Note that JavaScript arrays are objects.
 539      *
 540      * @param {*} value Reference to check.
 541      * @returns {boolean} True if `value` is an `Object` but not `null`.
 542      */
 543     function isObject(value){return value != null && typeof value === 'object';}
 544 
 545 
 546     /**
 547      * @ngdoc function
 548      * @name angular.isString
 549      * @module ng
 550      * @kind function
 551      *
 552      * @description
 553      * Determines if a reference is a `String`.
 554      *
 555      * @param {*} value Reference to check.
 556      * @returns {boolean} True if `value` is a `String`.
 557      */
 558     function isString(value){return typeof value === 'string';}
 559 
 560 
 561     /**
 562      * @ngdoc function
 563      * @name angular.isNumber
 564      * @module ng
 565      * @kind function
 566      *
 567      * @description
 568      * Determines if a reference is a `Number`.
 569      *
 570      * @param {*} value Reference to check.
 571      * @returns {boolean} True if `value` is a `Number`.
 572      */
 573     function isNumber(value){return typeof value === 'number';}
 574 
 575 
 576     /**
 577      * @ngdoc function
 578      * @name angular.isDate
 579      * @module ng
 580      * @kind function
 581      *
 582      * @description
 583      * Determines if a value is a date.
 584      *
 585      * @param {*} value Reference to check.
 586      * @returns {boolean} True if `value` is a `Date`.
 587      */
 588     function isDate(value) {
 589         return toString.call(value) === '[object Date]';
 590     }
 591 
 592 
 593     /**
 594      * @ngdoc function
 595      * @name angular.isArray
 596      * @module ng
 597      * @kind function
 598      *
 599      * @description
 600      * Determines if a reference is an `Array`.
 601      *
 602      * @param {*} value Reference to check.
 603      * @returns {boolean} True if `value` is an `Array`.
 604      */
 605     var isArray = (function() {
 606         if (!isFunction(Array.isArray)) {
 607             return function(value) {
 608                 return toString.call(value) === '[object Array]';
 609             };
 610         }
 611         return Array.isArray;
 612     })();
 613 
 614     /**
 615      * @ngdoc function
 616      * @name angular.isFunction
 617      * @module ng
 618      * @kind function
 619      *
 620      * @description
 621      * Determines if a reference is a `Function`.
 622      *
 623      * @param {*} value Reference to check.
 624      * @returns {boolean} True if `value` is a `Function`.
 625      */
 626     function isFunction(value){return typeof value === 'function';}
 627 
 628 
 629     /**
 630      * Determines if a value is a regular expression object.
 631      *
 632      * @private
 633      * @param {*} value Reference to check.
 634      * @returns {boolean} True if `value` is a `RegExp`.
 635      */
 636     function isRegExp(value) {
 637         return toString.call(value) === '[object RegExp]';
 638     }
 639 
 640 
 641     /**
 642      * Checks if `obj` is a window object.
 643      *
 644      * @private
 645      * @param {*} obj Object to check
 646      * @returns {boolean} True if `obj` is a window obj.
 647      */
 648     function isWindow(obj) {
 649         return obj && obj.window === obj;
 650     }
 651 
 652 
 653     function isScope(obj) {
 654         return obj && obj.$evalAsync && obj.$watch;
 655     }
 656 
 657 
 658     function isFile(obj) {
 659         return toString.call(obj) === '[object File]';
 660     }
 661 
 662 
 663     function isBlob(obj) {
 664         return toString.call(obj) === '[object Blob]';
 665     }
 666 
 667 
 668     function isBoolean(value) {
 669         return typeof value === 'boolean';
 670     }
 671 
 672 
 673     var trim = (function() {
 674         // native trim is way faster: http://jsperf.com/angular-trim-test
 675         // but IE doesn't have it... :-(
 676         // TODO: we should move this into IE/ES5 polyfill
 677         if (!String.prototype.trim) {
 678             return function(value) {
 679                 return isString(value) ? value.replace(/^\s\s*/, '').replace(/\s\s*$/, '') : value;
 680             };
 681         }
 682         return function(value) {
 683             return isString(value) ? value.trim() : value;
 684         };
 685     })();
 686 
 687 
 688     /**
 689      * @ngdoc function
 690      * @name angular.isElement
 691      * @module ng
 692      * @kind function
 693      *
 694      * @description
 695      * Determines if a reference is a DOM element (or wrapped jQuery element).
 696      *
 697      * @param {*} value Reference to check.
 698      * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
 699      */
 700     function isElement(node) {
 701         return !!(node &&
 702         (node.nodeName  // we are a direct element
 703         || (node.prop && node.attr && node.find)));  // we have an on and find method part of jQuery API
 704     }
 705 
 706     /**
 707      * @param str 'key1,key2,...'
 708      * @returns {object} in the form of {key1:true, key2:true, ...}
 709      */
 710     function makeMap(str) {
 711         var obj = {}, items = str.split(","), i;
 712         for ( i = 0; i < items.length; i++ )
 713             obj[ items[i] ] = true;
 714         return obj;
 715     }
 716 
 717 
 718     if (msie < 9) {
 719         nodeName_ = function(element) {
 720             element = element.nodeName ? element : element[0];
 721             return lowercase(
 722                 (element.scopeName && element.scopeName != 'HTML')
 723                     ? element.scopeName + ':' + element.nodeName : element.nodeName
 724             );
 725         };
 726     } else {
 727         nodeName_ = function(element) {
 728             return lowercase(element.nodeName ? element.nodeName : element[0].nodeName);
 729         };
 730     }
 731 
 732 
 733     function map(obj, iterator, context) {
 734         var results = [];
 735         forEach(obj, function(value, index, list) {
 736             results.push(iterator.call(context, value, index, list));
 737         });
 738         return results;
 739     }
 740 
 741 
 742     /**
 743      * @description
 744      * Determines the number of elements in an array, the number of properties an object has, or
 745      * the length of a string.
 746      *
 747      * Note: This function is used to augment the Object type in Angular expressions. See
 748      * {@link angular.Object} for more information about Angular arrays.
 749      *
 750      * @param {Object|Array|string} obj Object, array, or string to inspect.
 751      * @param {boolean} [ownPropsOnly=false] Count only "own" properties in an object
 752      * @returns {number} The size of `obj` or `0` if `obj` is neither an object nor an array.
 753      */
 754     function size(obj, ownPropsOnly) {
 755         var count = 0, key;
 756 
 757         if (isArray(obj) || isString(obj)) {
 758             return obj.length;
 759         } else if (isObject(obj)) {
 760             for (key in obj)
 761                 if (!ownPropsOnly || obj.hasOwnProperty(key))
 762                     count++;
 763         }
 764 
 765         return count;
 766     }
 767 
 768 
 769     function includes(array, obj) {
 770         return indexOf(array, obj) != -1;
 771     }
 772 
 773     function indexOf(array, obj) {
 774         if (array.indexOf) return array.indexOf(obj);
 775 
 776         for (var i = 0; i < array.length; i++) {
 777             if (obj === array[i]) return i;
 778         }
 779         return -1;
 780     }
 781 
 782     function arrayRemove(array, value) {
 783         var index = indexOf(array, value);
 784         if (index >=0)
 785             array.splice(index, 1);
 786         return value;
 787     }
 788 
 789     function isLeafNode (node) {
 790         if (node) {
 791             switch (nodeName_(node)) {
 792                 case "option":
 793                 case "pre":
 794                 case "title":
 795                     return true;
 796             }
 797         }
 798         return false;
 799     }
 800 
 801     /**
 802      * @ngdoc function
 803      * @name angular.copy
 804      * @module ng
 805      * @kind function
 806      *
 807      * @description
 808      * Creates a deep copy of `source`, which should be an object or an array.
 809      *建立一个深拷贝的源,它能够是一个对象或一个数组
 810      * * If no destination is supplied, a copy of the object or array is created
 811      * 若是未指定dest,对象或数组的一个副本会被建立
 812      * * If a destination is provided, all of its elements (for array) or properties (for objects)
 813      *   are deleted and then all elements/properties from the source are copied to it.
 814      *   若是指定了它的dest,则它的全部属性或元素将会被删除,而且它的全部元素或属性将会从源里面拷贝出来。
 815      * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
 816      * 若是源不是一个对象或数组,则操做会当即中止执行
 817      * * If `source` is identical to 'destination' an exception will be thrown.
 818      *若是源被标识为一个dest,则会抛出一个异常
 819      * @param {*} source The source that will be used to make a copy.//源,进行拷贝的参照对象,能够是任意类型
 820      *                   Can be any type, including primitives, `null`, and `undefined`.
 821      * @param {(Object|Array)=} destination Destination into which the source is copied. If
 822      *     provided, must be of the same type as `source`.//参数为一个对象或数组,若是被提供,则必须与源的类型相同
 823      * @returns {*} The copy or updated `destination`, if `destination` was specified.
 824      * 若是dest被指定的话,则更新dest;若是dest未被指定的话,是返回源的拷贝副本。
 825      *
 826      * @example
 827      <example module="copyExample">
 828      <file name="index.html">
 829      <div ng-controller="ExampleController">
 830      <form novalidate class="simple-form">
 831      Name: <input type="text" ng-model="user.name" /><br />
 832      E-mail: <input type="email" ng-model="user.email" /><br />
 833      Gender: <input type="radio" ng-model="user.gender" value="male" />male
 834      <input type="radio" ng-model="user.gender" value="female" />female<br />
 835      <button ng-click="reset()">RESET</button>
 836      <button ng-click="update(user)">SAVE</button>
 837      </form>
 838      <pre>form = {{user | json}}</pre>//user是一个json对象
 839      <pre>master = {{master | json}}</pre>master是一个json对象
 840      </div>
 841 
 842      <script>
 843      angular.module('copyExample')
 844      .controller('ExampleController', ['$scope', function($scope) {//$scope默认会被注入
 845       $scope.master= {};
 846 
 847       $scope.update = function(user) {
 848         // Example with 1 argument
 849         $scope.master= angular.copy(user);
 850       };
 851 
 852       $scope.reset = function() {
 853         // Example with 2 arguments
 854         angular.copy($scope.master, $scope.user);
 855       };
 856 
 857       $scope.reset();
 858     }]);
 859      </script>
 860      </file>
 861      </example>
 862      */
 863     function copy(source, destination, stackSource, stackDest) {
 864         if (isWindow(source) || isScope(source)) {
 865             throw ngMinErr('cpws',
 866                 "Can't copy! Making copies of Window or Scope instances is not supported.");
 867         }
 868 
 869         if (!destination) {//若是指定dest
 870             destination = source;
 871             if (source) {
 872                 if (isArray(source)) {
 873                     destination = copy(source, [], stackSource, stackDest);
 874                 } else if (isDate(source)) {
 875                     destination = new Date(source.getTime());
 876                 } else if (isRegExp(source)) {
 877                     destination = new RegExp(source.source);
 878                 } else if (isObject(source)) {
 879                     var emptyObject = Object.create(Object.getPrototypeOf(source));
 880                     destination = copy(source, emptyObject, stackSource, stackDest);
 881                 }
 882             }
 883         } else {//若是不指定dest
 884             if (source === destination) throw ngMinErr('cpi',
 885                 "Can't copy! Source and destination are identical.");
 886 
 887             stackSource = stackSource || [];
 888             stackDest = stackDest || [];
 889 
 890             if (isObject(source)) {
 891                 var index = indexOf(stackSource, source);
 892                 if (index !== -1) return stackDest[index];
 893 
 894                 stackSource.push(source);
 895                 stackDest.push(destination);
 896             }
 897 
 898             var result;
 899             if (isArray(source)) {
 900                 destination.length = 0;
 901                 for ( var i = 0; i < source.length; i++) {
 902                     result = copy(source[i], null, stackSource, stackDest);
 903                     if (isObject(source[i])) {
 904                         stackSource.push(source[i]);
 905                         stackDest.push(result);
 906                     }
 907                     destination.push(result);
 908                 }
 909             } else {
 910                 var h = destination.$$hashKey;
 911                 forEach(destination, function(value, key) {
 912                     delete destination[key];
 913                 });
 914                 for ( var key in source) {
 915                     if(source.hasOwnProperty(key)) {
 916                         result = copy(source[key], null, stackSource, stackDest);
 917                         if (isObject(source[key])) {
 918                             stackSource.push(source[key]);
 919                             stackDest.push(result);
 920                         }
 921                         destination[key] = result;
 922                     }
 923                 }
 924                 setHashKey(destination,h);
 925             }
 926 
 927         }
 928         return destination;
 929     }
 930 
 931     /**
 932      * Creates a shallow copy of an object, an array or a primitive
 933      * 建立一个浅副本,能够是一个数组或一个原始数据
 934      */
 935     function shallowCopy(src, dst) {
 936         var i = 0;
 937         if (isArray(src)) {
 938             dst = dst || [];
 939 
 940             for (; i < src.length; i++) {
 941                 dst[i] = src[i];
 942             }
 943         } else if (isObject(src)) {
 944             dst = dst || {};
 945 
 946             var keys = Object.keys(src);
 947 
 948             for (var l = keys.length; i < l; i++) {
 949                 var key = keys[i];
 950 
 951                 if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
 952                     dst[key] = src[key];
 953                 }
 954             }
 955         }
 956 
 957         return dst || src;
 958     }
 959 
 960 
 961     /**
 962      * @ngdoc function
 963      * @name angular.equals
 964      * @module ng
 965      * @kind function
 966      *
 967      * @description
 968      * Determines if two objects or two values are equivalent. Supports value types, regular
 969      * expressions, arrays and objects.
 970      *
 971      * Two objects or values are considered equivalent if at least one of the following is true:
 972      *
 973      * * Both objects or values pass `===` comparison.
 974      * * Both objects or values are of the same type and all of their properties are equal by
 975      *   comparing them with `angular.equals`.
 976      * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
 977      * * Both values represent the same regular expression (In JavaScript,
 978      *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
 979      *   representation matches).
 980      *
 981      * During a property comparison, properties of `function` type and properties with names
 982      * that begin with `$` are ignored.
 983      *
 984      * Scope and DOMWindow objects are being compared only by identify (`===`).
 985      *
 986      * @param {*} o1 Object or value to compare.
 987      * @param {*} o2 Object or value to compare.
 988      * @returns {boolean} True if arguments are equal.
 989      */
 990     function equals(o1, o2) {
 991         if (o1 === o2) return true;
 992         if (o1 === null || o2 === null) return false;
 993         if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
 994         var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
 995         if (t1 == t2) {
 996             if (t1 == 'object') {
 997                 if (isArray(o1)) {
 998                     if (!isArray(o2)) return false;
 999                     if ((length = o1.length) == o2.length) {
1000                         for(key=0; key<length; key++) {
1001                             if (!equals(o1[key], o2[key])) return false;
1002                         }
1003                         return true;
1004                     }
1005                 } else if (isDate(o1)) {
1006                     return isDate(o2) && o1.getTime() == o2.getTime();
1007                 } else if (isRegExp(o1) && isRegExp(o2)) {
1008                     return o1.toString() == o2.toString();
1009                 } else {
1010                     if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || isArray(o2)) return false;
1011                     keySet = {};
1012                     for(key in o1) {
1013                         if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1014                         if (!equals(o1[key], o2[key])) return false;
1015                         keySet[key] = true;
1016                     }
1017                     for(key in o2) {
1018                         if (!keySet.hasOwnProperty(key) &&
1019                             key.charAt(0) !== '$' &&
1020                             o2[key] !== undefined &&
1021                             !isFunction(o2[key])) return false;
1022                     }
1023                     return true;
1024                 }
1025             }
1026         }
1027         return false;
1028     }
1029 
1030 
1031     function csp() {
1032         return (document.securityPolicy && document.securityPolicy.isActive) ||
1033             (document.querySelector &&
1034             !!(document.querySelector('[ng-csp]') || document.querySelector('[data-ng-csp]')));
1035     }
1036 
1037 
1038     function concat(array1, array2, index) {
1039         return array1.concat(slice.call(array2, index));//链接两个数组,call(obj,具体的参数)
1040     }
1041 
1042     //分割指定位置的数组数据
1043     function sliceArgs(args, startIndex) {
1044         return slice.call(args, startIndex || 0);//[].splice
1045     }
1046 
1047 
1048     /* jshint -W101 */
1049     /**
1050      * @ngdoc function
1051      * @name angular.bind
1052      * @module ng
1053      * @kind function
1054      *
1055      * @description
1056      * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1057      * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1058      * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1059      * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1060      *当调用fn函数时,bn会被绑定到self,其中,self至关于this的做用。能够指定args参数绑定到预先造成的函数中。
1061      * 返回一个函数的调用
1062      * @param {Object} self Context which `fn` should be evaluated in.//一个上下文对象
1063      * @param {function()} fn Function to be bound.//一个函数,它将会被绑定到self对象中
1064      * @param {...*} args Optional arguments to be prebound to the `fn` function call.//可选参数将会被绑定到fn函数中
1065      * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1066      */
1067     /* jshint +W101 */
1068     function bind(self, fn) {
1069         //判断可选参数是否被提供,若是被提供则为sliceArg(argumentts,2),若是未被提供则为空数组,[]
1070         var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1071         if (isFunction(fn) && !(fn instanceof RegExp)) {//若是fn是一个函数
1072             return curryArgs.length//判断可选参数是否被提供
1073                 ? function() {
1074                 return arguments.length
1075                     ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0)))//若是可选参数被提供,则把可选参数绑定到self中
1076                     : fn.apply(self, curryArgs);//若是可选参数未被提供,则返回self自己
1077             }
1078                 : function() {
1079                 return arguments.length
1080                     ? fn.apply(self, arguments)
1081                     : fn.call(self);
1082             };
1083         } else {
1084             // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1085             return fn;//在IE里面没有这些函数,因此绑定不了,直接返回fn函数
1086         }
1087     }
1088 
1089 
1090     function toJsonReplacer(key, value) {
1091         var val = value;
1092 
1093         if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1094             val = undefined;
1095         } else if (isWindow(value)) {
1096             val = '$WINDOW';
1097         } else if (value &&  document === value) {
1098             val = '$DOCUMENT';
1099         } else if (isScope(value)) {
1100             val = '$SCOPE';
1101         }
1102         return val;
1103     }
1104 
1105 
1106     /**
1107      * @ngdoc function
1108      * @name angular.toJson
1109      * @module ng
1110      * @kind function
1111      *
1112      * @description
1113      * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1114      * stripped since angular uses this notation internally.
1115      *
1116      * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1117      * @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace.
1118      * @returns {string|undefined} JSON-ified string representing `obj`.
1119      */
1120     //把指定的对象转换成json字符串,并把json字符串里面的值 替换成与之相对应的对象,如:$$scope,$widnow,$document
1121     function toJson(obj, pretty) {
1122         if (typeof obj === 'undefined') return undefined;
1123         return JSON.stringify(obj, toJsonReplacer, pretty ? '  ' : null);
1124     }
1125 
1126 
1127     /**
1128      * @ngdoc function
1129      * @name angular.fromJson
1130      * @module ng
1131      * @kind function
1132      *
1133      * @description
1134      * Deserializes a JSON string.
1135      *
1136      * @param {string} json JSON string to deserialize.//一个json字符串
1137      * @returns {Object|Array|string|number} Deserialized thingy.,返回一个数组,对象
1138      */
1139     function fromJson(json) {//
1140         return isString(json)
1141             ? JSON.parse(json)
1142             : json;
1143     }
1144 
1145 
1146     /**
1147      * @returns {string} Returns the string representation of the element.
1148      */
1149     function startingTag(element) {//查找angular的ng指令,如ngapp,ngrepeat,ngswitch etc.
1150         element = jqLite(element).clone();//克隆element元素
1151         try {
1152             // turns out IE does not let you set .html() on elements which
1153             // are not allowed to have children. So we just ignore it.
1154             element.empty();//IE不容许用设置.html()设置元素,也就是说不容许有子元素,所以置为空
1155         } catch(e) {}
1156         // As Per DOM Standards
1157         var TEXT_NODE = 3;//标准的文节点
1158         var elemHtml = jqLite('<div>').append(element).html();//把div标签添加到新克隆出来的element上,并获取如今的值
1159         try {
1160             return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) :
1161                 elemHtml.
1162                     match(/^(<[^>]+>)/)[1].//正则表达式表示的示例内容如,<div ng-app='myModule'>
1163                     //正则:/^<([\w\-]+)/,表示的示例内容,如:<div ng-app='myModule'>,
1164                     // 正则:([\w\-]+),表示的示例内容如,ng-app,由此看来,angular在html里面写的内容必须是:标准的Html文内容
1165                     replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });//<ng-app
1166         } catch(e) {
1167             return lowercase(elemHtml);//若是不是上面的状况,慢把新取出的值直接返回
1168         }
1169 
1170     }
1171 
1172 
1173 /////////////////////////////////////////////////
1174 
1175     /**
1176      * Tries to decode the URI component without throwing an exception.
1177      *调试解码URI组件,不抛出异常
1178      * @private 为私有方法
1179      * @param str value potential URI component to check.//str是URI组件须要检查的内容
1180      * @returns {boolean} True if `value` can be decoded,若是str被正常的解析,则返回true
1181      * with the decodeURIComponent function.
1182      */
1183     function tryDecodeURIComponent(value) {
1184         try {
1185             return decodeURIComponent(value);
1186         } catch(e) {
1187             // Ignore any invalid uri component//忽略任何无效的URI组件内容
1188         }
1189     }
1190 
1191 
1192     /**
1193      * Parses an escaped url query string into key-value pairs.//
1194      * 解析一个转义的url查询字符串为键值对形式
1195      * @returns {Object.<string,boolean|Array>}//返回Object.字符串/boolean,数组
1196      */
1197     function parseKeyValue(/**string*/keyValue) {
1198         var obj = {}, key_value, key;
1199         //keyvalue的内容示例如,http://www.baidu.com?search=angular&date=20141215,结果为:search:angular,date:20141215
1200         forEach((keyValue || "").split('&'), function(keyValue) {
1201             if ( keyValue ) {
1202                 key_value = keyValue.split('=');
1203                 key = tryDecodeURIComponent(key_value[0]);
1204                 if ( isDefined(key) ) {
1205                     var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
1206                     if (!hasOwnProperty.call(obj, key)) {
1207                         obj[key] = val;
1208                     } else if(isArray(obj[key])) {
1209                         obj[key].push(val);
1210                     } else {
1211                         obj[key] = [obj[key],val];
1212                     }
1213                 }
1214             }
1215         });
1216         return obj;
1217     }
查看代码

 续:时间:2014年12月15日 15:36:44css

  1     function toKeyValue(obj) {
  2         var parts = [];
  3         forEach(obj, function(value, key) {
  4             if (isArray(value)) {
  5                 forEach(value, function(arrayValue) {
  6                     parts.push(encodeUriQuery(key, true) +
  7                     (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
  8                 });
  9             } else {
 10                 parts.push(encodeUriQuery(key, true) +
 11                 (value === true ? '' : '=' + encodeUriQuery(value, true)));
 12             }
 13         });
 14         return parts.length ? parts.join('&') : '';
 15     }
 16 
 17 
 18     /**
 19      * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
 20      * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
 21      * segments:
 22      *    segment       = *pchar
 23      *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
 24      *    pct-encoded   = "%" HEXDIG HEXDIG
 25      *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
 26      *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
 27      *                     / "*" / "+" / "," / ";" / "="
 28      */
 29     function encodeUriSegment(val) {
 30         return encodeUriQuery(val, true).
 31             replace(/%26/gi, '&').
 32             replace(/%3D/gi, '=').
 33             replace(/%2B/gi, '+');
 34     }
 35 
 36 
 37     /**
 38      * This method is intended for encoding *key* or *value* parts of query component. We need a custom
 39      * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
 40      * encoded per http://tools.ietf.org/html/rfc3986:
 41      *    query       = *( pchar / "/" / "?" )
 42      *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"//pchar为字符串的分割符,对应下面的三种状况
 43      *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
 44      *    pct-encoded   = "%" HEXDIG HEXDIG
 45      *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"//
 46      *                     / "*" / "+" / "," / ";" / "="
 47      */
 48     function encodeUriQuery(val, pctEncodeSpaces) {
 49         return encodeURIComponent(val).
 50             replace(/%40/gi, '@').
 51             replace(/%3A/gi, ':').
 52             replace(/%24/g, '$').
 53             replace(/%2C/gi, ',').
 54             replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
 55     }
 56 
 57     //定义angular属性的前缀,如ng-,data-ng,ng:-,x-ng-
 58     var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
 59 
 60     //参数示例:ng-repeat,repeat
 61     function getNgAttribute(element, ngAttr) {//获取angular的*ng-属性
 62         var attr, i, ii = ngAttrPrefixes.length, j, jj;
 63         element = jqLite(element);//获取传进来的element元素
 64         for (i=0; i<ii; ++i) {
 65             attr = ngAttrPrefixes[i] + ngAttr;//显示的示例代码,如:ng-app,ng-repeat
 66             if (isString(attr = element.attr(attr))) {//若是找到则当即返回,找到的内容
 67                 return attr;
 68             }
 69         }
 70         return null;//若是找不到则返回为空
 71     }
 72 
 73     /**
 74      * @ngdoc directive
 75      * @name ngApp
 76      * @module ng
 77      *
 78      * @element ANY //能够是任意内容
 79      * @param {angular.Module} ngApp an optional application//angular.module,是一个可选的
 80      *   {@link angular.module module} name to load.//按照模块的名称载入的,如angular.module('myModule',["book-store","book"]);,其中,myModule就是模块名
 81      * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
 82      *   created in "strict-di" mode. This means that the application will fail to invoke functions which
 83      *   do not use explicit function annotation (and are thus unsuitable for minification), as described
 84      *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
 85      *   tracking down the root of these bugs.
 86      *
 87      * @description
 88      *
 89      * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
 90      * 用这个指令能够自动的去启动angular的应用程序,
 91      * designates the **root element** of the application and is typically placed near the root element
 92      * ngApp做为angular应用程序的根元素,它是一个典型的点位符,如<html><body><head><div> etc.
 93      * of the page - e.g. on the `<body>` or `<html>` tags.
 94      *
 95      * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp
 96      * 在每个html文档中,仅只有一个angular应用程序能够被自动启动
 97      * found in the document will be used to define the root element to auto-bootstrap as an
 98      * 在文档中发现的第一个ngApp被视为能够自动启动的应用程序的根元素
 99      * application. To run multiple applications in an HTML document you must manually bootstrap them using
100      * 在一个html文档中运行多个应用程序,则须要手动的启动他们。
101      * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
102      *angular应用程序不可以互相嵌套
103      * You can specify an **AngularJS module** to be used as the root module for the application.  This
104      * 你能够指定一个angular的模板,做为应用程序的顶级模块
105      * module will be loaded into the {@link auto.$injector} when the application is bootstrapped and
106      * should contain the application code needed or have dependencies on other modules that will
107      * contain the code. See {@link angular.module} for more information.
108      *当应用程序启动而且包含有它所依赖的其余模块代码时,上面所指定的模块能够被注入到应用程序当中
109      * In the example below if the `ngApp` directive were not placed on the `html` element then the
110      * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
111      * would not be resolved to `3`.
112      *在下面的示例中,若是ngApp在Html页面中找不到或不存在,则这个Html文档不可以被正常的编译,AppController这个控制器也不可以被正常的实例化
113      * 解析出来的{{a+b}}的结果也不会为3
114      * `ngApp` is the easiest, and most common, way to bootstrap an application.
115      *ngApp是用来 启动一个应用程序最简单,最经常使用 的指令,
116      <example module="ngAppDemo">
117      <file name="index.html">
118      <div ng-controller="ngAppDemoController">
119      I can add: {{a}} + {{b}} =  {{ a+b }}
120      </div>
121      </file>
122      <file name="script.js">
123      angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
124      $scope.a = 1;
125      $scope.b = 2;
126    });
127      </file>
128      </example>
129      *
130      * Using `ngStrictDi`, you would see something like this://启动严格模式
131      *
132      <example ng-app-included="true">
133      <file name="index.html">
134      <div ng-app="ngAppStrictDemo" ng-strict-di>//启动严格模式
135      <div ng-controller="GoodController1">
136      I can add: {{a}} + {{b}} =  {{ a+b }}
137 
138      <p>This renders because the controller does not fail to
139      instantiate, by using explicit annotation style (see
140      script.js for details)
141      </p>
142      </div>
143 
144      <div ng-controller="GoodController2">
145      Name: <input ng-model="name"><br />
146      Hello, {{name}}!
147 
148      <p>This renders because the controller does not fail to
149      instantiate, by using explicit annotation style
150      (see script.js for details)
151      </p>
152      </div>
153 
154      <div ng-controller="BadController">
155      I can add: {{a}} + {{b}} =  {{ a+b }}
156 
157      <p>The controller could not be instantiated, due to relying
158      on automatic function annotations (which are disabled in
159      strict mode). As such, the content of this section is not
160      interpolated, and there should be an error in your web console.
161      </p>
162      </div>
163      </div>
164      </file>
165      <file name="script.js">
166      angular.module('ngAppStrictDemo', [])
167      // BadController will fail to instantiate, due to relying on automatic function annotation,
168      // rather than an explicit annotation
169      .controller('BadController', function($scope) {
170        $scope.a = 1;
171        $scope.b = 2;
172      })
173      // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
174      // due to using explicit annotations using the array style and $inject property, respectively.
175      .controller('GoodController1', ['$scope', function($scope) {
176        $scope.a = 1;
177        $scope.b = 2;
178      }])
179      .controller('GoodController2', GoodController2);
180      function GoodController2($scope) {
181        $scope.name = "World";
182      }
183      GoodController2.$inject = ['$scope'];
184      </file>
185      <file name="style.css">
186      div[ng-controller] {
187        margin-bottom: 1em;
188        -webkit-border-radius: 4px;
189        border-radius: 4px;
190        border: 1px solid;
191        padding: .5em;
192    }
193      div[ng-controller^=Good] {
194        border-color: #d6e9c6;
195        background-color: #dff0d8;
196        color: #3c763d;
197    }
198      div[ng-controller^=Bad] {
199        border-color: #ebccd1;
200        background-color: #f2dede;
201        color: #a94442;
202        margin-bottom: 0;
203    }
204      </file>
205      </example>
206      */
207     function angularInit(element, bootstrap) {//type:any,[]
208         var elements = [element],
209             appElement,
210             module,
211             config = {},
212             names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'],//angular所能识别的ngApp指令
213             options = {
214                 'boolean': ['strict-di']
215             },
216             NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;//*ng-指令的正则表达式,如:ng-app:red
217 
218         function append(element) {//内部函数
219             element && elements.push(element);
220         }
221 
222         forEach(names, function(name) {
223             names[name] = true;
224             append(document.getElementById(name));
225             name = name.replace(':', '\\:');//示例内容,如:ng:app转换为,ng\:app
226             /*
227             * //获取<div>中的全部图像(和getElementsByTaName("img")同样)
228              var images = document.getElementById("myDiv").querySelectorAll("img");
229 
230              //获取全部包含“selected”类的元素
231              var selected = document.querySelectorall(".selected");
232 
233              //获取全部<p>元素中的<strong>元素
234              var strongs = document.querySelectorAll("p strong");
235             * */
236             if (element.querySelectorAll) {//只要调用querySelectorAll()都会返回一个StaticNodeList对象无论匹配的元素有几个;若是没有匹配,那么StaticNodeList为空。
237                 forEach(element.querySelectorAll('.' + name), append);//.ng-App
238                 forEach(element.querySelectorAll('.' + name + '\\:'), append);//.ng\:App
239                 forEach(element.querySelectorAll('[' + name + ']'), append);//[ng-App]
240             }
241         });
242 
243         forEach(elements, function(element) {
244             if (!appElement) {
245                 var className = ' ' + element.className + ' ';
246                 var match = NG_APP_CLASS_REGEXP.exec(className);//判断是否匹配*ng-指令的正则表达式,如ng-app:red
247                 if (match) {
248                     appElement = element;
249                     module = (match[2] || '').replace(/\s+/g, ',');
250                 } else {
251                     forEach(element.attributes, function(attr) {
252                         if (!appElement && names[attr.name]) {
253                             appElement = element;
254                             module = attr.value;
255                         }
256                     });
257                 }
258             }
259         });
260         if (appElement) {
261             config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
262             bootstrap(appElement, module ? [module] : [], config);//示例代码,如:angular.module(document,[],cofnig);//是一个回调函数
263         }
264     }
265 
266     /**
267      * @ngdoc function
268      * @name angular.bootstrap
269      * @module ng
270      * @description
271      * Use this function to manually start up angular application.
272      *用这个函数手动启动angular应用程序
273      * See: {@link guide/bootstrap Bootstrap}
274      *
275      * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
276      * They must use {@link ng.directive:ngApp ngApp}.
277      *注意:protractor是基于end-toend来测试的,所以不可以使用这个函数来手动启动应用程序
278      * Angular will detect if it has been loaded into the browser more than once and only allow the
279      * first loaded script to be bootstrapped and will report a warning to the browser console for
280      * each of the subsequent scripts. This prevents strange results in applications, where otherwise
281      * multiple instances of Angular try to work on the DOM.
282      *
283      * ```html
284      * <!doctype html>
285      * <html>
286      * <body>
287      * <div ng-controller="WelcomeController">
288      *   {{greeting}}
289      * </div>
290      *
291      * <script src="angular.js"></script>
292      * <script>
293      *   var app = angular.module('demo', [])
294      *   .controller('WelcomeController', function($scope) {
295  *       $scope.greeting = 'Welcome!';
296  *   });
297      *   angular.bootstrap(document, ['demo']);//document,[],config
298      * </script>
299      * </body>
300      * </html>
301      * ```
302      *
303      * @param {DOMElement} element DOM element which is the root of angular application.//dom元素,angular应用程序的根元素
304      * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.//被加载到应用程序中的依赖模块
305      *     Each item in the array should be the name of a predefined module or a (DI annotated)//在数组中的每一项,应该被预先定义或注入
306      *     function that will be invoked by the injector as a run block.
307      *     See: {@link angular.module modules}
308      * @param {Object=} config an object for defining configuration options for the application. The
309      *     following keys are supported:
310      *
311      *     - `strictDi`: disable automatic function annotation for the application. This is meant to
312      *       assist in finding bugs which break minified code.
313      *
314      * @returns {auto.$injector} Returns the newly created injector for this app.
315      * 返回新这个应用程序新建立的注入器
316      */
317     function bootstrap(element, modules, config) {
318         if (!isObject(config)) config = {};
319         var defaultConfig = {
320             strictDi: false
321         };
322         /**
323          * function extend(dst) {//这里实现config继承
324               var h = dst.$$hashKey;
325               forEach(arguments, function(obj) {
326                 if (obj !== dst) {
327                   forEach(obj, function(value, key) {
328                     dst[key] = value;
329                   });
330                 }
331               });
332 
333               setHashKey(dst,h);
334               return dst;
335             }
336          */
337         config = extend(defaultConfig, config);//这里实现config继承
338         var doBootstrap = function() {//返回新建立的注入器
339             element = jqLite(element);
340 
341             if (element.injector()) {//App Already Bootstrapped with this Element
342                 var tag = (element[0] === document) ? 'document' : startingTag(element);
343                 throw ngMinErr('btstrpd', "App Already Bootstrapped with this Element '{0}'", tag);
344             }
345 
346             modules = modules || [];//element所依赖的模块数组
347             modules.unshift(['$provide', function($provide) {
348                 $provide.value('$rootElement', element);//注入根元素,如:document,html,ng-app
349             }]);
350             modules.unshift('ng');//添加anggular的前缀,ng
351             var injector = createInjector(modules, config.strictDi);//为模块数组建立一个注入器
352             injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',//用新建立的注入器注入默认的对象,如:$rootScope,$rootEolement,$compile,$injector
353                     function(scope, element, compile, injector) {//这个函数的参数与injector.invoke里面的第一个参数所对应
354                         scope.$apply(function() {
355                             element.data('$injector', injector);
356                             compile(element)(scope);
357                         });
358                     }]
359             );
360             return injector;//返回新建立的注入器
361         };
362 
363         var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;//angular延迟启动
364 
365         if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
366             return doBootstrap();
367         }
368 
369         window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
370         angular.resumeBootstrap = function(extraModules) {//从新激活angular应用程序
371             forEach(extraModules, function(module) {
372                 modules.push(module);
373             });
374             doBootstrap();
375         };
376     }
377 
378     var SNAKE_CASE_REGEXP = /[A-Z]/g;
379     function snake_case(name, separator) {
380         separator = separator || '_';
381         return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
382             return (pos ? separator : '') + letter.toLowerCase();
383         });
384     }
385 
386     function bindJQuery() {
387         var originalCleanData;
388         // bind to jQuery if present;
389         jQuery = window.jQuery;
390         // Use jQuery if it exists with proper functionality, otherwise default to us.
391         // Angular 1.2+ requires jQuery 1.7.1+ for on()/off() support.
392         if (jQuery && jQuery.fn.on) {//判断是否已经引入其余版本的JQuery,若是是则angular使用的是外部的jQuery
393             jqLite = jQuery;
394             extend(jQuery.fn, {
395                 scope: JQLitePrototype.scope,
396                 isolateScope: JQLitePrototype.isolateScope,
397                 controller: JQLitePrototype.controller,
398                 injector: JQLitePrototype.injector,
399                 inheritedData: JQLitePrototype.inheritedData
400             });
401 
402             originalCleanData = jQuery.cleanData;
403             // Prevent double-proxying.
404             originalCleanData = originalCleanData.$$original || originalCleanData;
405 
406             // All nodes removed from the DOM via various jQuery APIs like .remove()
407             // are passed through jQuery.cleanData. Monkey-patch this method to fire
408             // the $destroy event on all removed nodes.
409             jQuery.cleanData = function(elems) {
410                 for (var i = 0, elem; (elem = elems[i]) != null; i++) {
411                     jQuery(elem).triggerHandler('$destroy');
412                 }
413                 originalCleanData(elems);
414             };
415             jQuery.cleanData.$$original = originalCleanData;
416         } else {//若是没有引入外部的jQuery,则angular使用其内容的jQlite
417             jqLite = JQLite;
418         }
419 
420         angular.element = jqLite;//把jqLit绑定到angular中
421     }
422 
423     /**
424      * throw error if the argument is falsy.
425      */
426     function assertArg(arg, name, reason) {
427         if (!arg) {
428             throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
429         }
430         return arg;
431     }
432 
433     function assertArgFn(arg, name, acceptArrayAnnotation) {
434         if (acceptArrayAnnotation && isArray(arg)) {
435             arg = arg[arg.length - 1];
436         }
437 
438         assertArg(isFunction(arg), name, 'not a function, got ' +
439         (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
440         return arg;
441     }
442 
443     /**
444      * throw error if the name given is hasOwnProperty
445      * @param  {String} name    the name to test
446      * @param  {String} context the context in which the name is used, such as module or directive
447      */
448     function assertNotHasOwnProperty(name, context) {
449         if (name === 'hasOwnProperty') {
450             throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
451         }
452     }
453 
454     /**
455      * Return the value accessible from the object by path. Any undefined traversals are ignored
456      * @param {Object} obj starting object
457      * @param {String} path path to traverse
458      * @param {boolean} [bindFnToScope=true]
459      * @returns {Object} value as accessible by path
460      */
461 //TODO(misko): this function needs to be removed
462     function getter(obj, path, bindFnToScope) {
463         if (!path) return obj;
464         var keys = path.split('.');
465         var key;
466         var lastInstance = obj;
467         var len = keys.length;
468 
469         for (var i = 0; i < len; i++) {
470             key = keys[i];
471             if (obj) {
472                 obj = (lastInstance = obj)[key];
473             }
474         }
475         if (!bindFnToScope && isFunction(obj)) {
476             return bind(lastInstance, obj);
477         }
478         return obj;
479     }
480 
481     /**
482      * Return the DOM siblings between the first and last node in the given array.
483      * @param {Array} array like object
484      * @returns {DOMElement} object containing the elements
485      */
486     function getBlockElements(nodes) {
487         var startNode = nodes[0],
488             endNode = nodes[nodes.length - 1];
489         if (startNode === endNode) {
490             return jqLite(startNode);
491         }
492 
493         var element = startNode;
494         var elements = [element];
495 
496         do {
497             element = element.nextSibling;
498             if (!element) break;
499             elements.push(element);
500         } while (element !== endNode);
501 
502         return jqLite(elements);
503     }
504 
505     /**
506      * @ngdoc type
507      * @name angular.Module
508      * @module ng
509      * @description
510      *
511      * Interface for configuring angular {@link angular.module modules}.
512      */
513 
514     function setupModuleLoader(window) {
515 
516         var $injectorMinErr = minErr('$injector');
517         var ngMinErr = minErr('ng');
518 
519         function ensure(obj, name, factory) {
520             return obj[name] || (obj[name] = factory());
521         }
522 
523         var angular = ensure(window, 'angular', Object);
524 
525         // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
526         angular.$$minErr = angular.$$minErr || minErr;
527 
528         return ensure(angular, 'module', function() {
529             /** @type {Object.<string, angular.Module>} */
530             var modules = {};
531 
532             /**
533              * @ngdoc function
534              * @name angular.module
535              * @module ng
536              * @description
537              *
538              * The `angular.module` is a global place for creating, registering and retrieving Angular
539              * modules.
540              * All modules (angular core or 3rd party) that should be available to an application must be
541              * registered using this mechanism.
542              *
543              * When passed two or more arguments, a new module is created.  If passed only one argument, an
544              * existing module (the name passed as the first argument to `module`) is retrieved.
545              *
546              *
547              * # Module
548              *
549              * A module is a collection of services, directives, controllers, filters, and configuration information.
550              * `angular.module` is used to configure the {@link auto.$injector $injector}.
551              *
552              * ```js
553              * // Create a new module
554              * var myModule = angular.module('myModule', []);
555              *
556              * // register a new service
557              * myModule.value('appName', 'MyCoolApp');
558              *
559              * // configure existing services inside initialization blocks.
560              * myModule.config(['$locationProvider', function($locationProvider) {
561      *   // Configure existing providers
562      *   $locationProvider.hashPrefix('!');
563      * }]);
564              * ```
565              *
566              * Then you can create an injector and load your modules like this:
567              *
568              * ```js
569              * var injector = angular.injector(['ng', 'myModule'])
570              * ```
571              *
572              * However it's more likely that you'll just use
573              * {@link ng.directive:ngApp ngApp} or
574              * {@link angular.bootstrap} to simplify this process for you.
575              *
576              * @param {!string} name The name of the module to create or retrieve.
577              * @param {!Array.<string>=} requires If specified then new module is being created. If
578              *        unspecified then the module is being retrieved for further configuration.
579              * @param {Function=} configFn Optional configuration function for the module. Same as
580              *        {@link angular.Module#config Module#config()}.
581              * @returns {module} new module with the {@link angular.Module} api.
582              */
583             return function module(name, requires, configFn) {//var myModule = angular.module('myModule', []);
584                 var assertNotHasOwnProperty = function(name, context) {
585                     if (name === 'hasOwnProperty') {
586                         throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
587                     }
588                 };
589 
590                 assertNotHasOwnProperty(name, 'module');
591                 if (requires && modules.hasOwnProperty(name)) {
592                     modules[name] = null;
593                 }
594                 return ensure(modules, name, function() {
595                     if (!requires) {
596                         throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
597                         "the module name or forgot to load it. If registering a module ensure that you " +
598                         "specify the dependencies as the second argument.", name);
599                     }
600 
601                     /** @type {!Array.<Array.<*>>} */
602                     var invokeQueue = [];
603 
604                     /** @type {!Array.<Function>} */
605                     var configBlocks = [];
606 
607                     /** @type {!Array.<Function>} */
608                     var runBlocks = [];
609 
610                     var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
611 
612                     /** @type {angular.Module} */
613                     var moduleInstance = {
614                         // Private state
615                         _invokeQueue: invokeQueue,
616                         _configBlocks: configBlocks,
617                         _runBlocks: runBlocks,
618 
619                         /**
620                          * @ngdoc property
621                          * @name angular.Module#requires
622                          * @module ng
623                          * @returns {Array.<string>} List of module names which must be loaded before this module.
624                          * @description
625                          * Holds the list of modules which the injector will load before the current module is
626                          * loaded.
627                          */
628                         requires: requires,
629 
630                         /**
631                          * @ngdoc property
632                          * @name angular.Module#name
633                          * @module ng
634                          * @returns {string} Name of the module.
635                          * @description
636                          */
637                         name: name,
638 
639 
640                         /**
641                          * @ngdoc method
642                          * @name angular.Module#provider
643                          * @module ng
644                          * @param {string} name service name
645                          * @param {Function} providerType Construction function for creating new instance of the
646                          *                                service.
647                          * @description
648                          * See {@link auto.$provide#provider $provide.provider()}.
649                          */
650                         provider: invokeLater('$provide', 'provider'),
651 
652                         /**
653                          * @ngdoc method
654                          * @name angular.Module#factory
655                          * @module ng
656                          * @param {string} name service name
657                          * @param {Function} providerFunction Function for creating new instance of the service.
658                          * @description
659                          * See {@link auto.$provide#factory $provide.factory()}.
660                          */
661                         factory: invokeLater('$provide', 'factory'),
662 
663                         /**
664                          * @ngdoc method
665                          * @name angular.Module#service
666                          * @module ng
667                          * @param {string} name service name
668                          * @param {Function} constructor A constructor function that will be instantiated.
669                          * @description
670                          * See {@link auto.$provide#service $provide.service()}.
671                          */
672                         service: invokeLater('$provide', 'service'),
673 
674                         /**
675                          * @ngdoc method
676                          * @name angular.Module#value
677                          * @module ng
678                          * @param {string} name service name
679                          * @param {*} object Service instance object.
680                          * @description
681                          * See {@link auto.$provide#value $provide.value()}.
682                          */
683                         value: invokeLater('$provide', 'value'),
684 
685                         /**
686                          * @ngdoc method
687                          * @name angular.Module#constant
688                          * @module ng
689                          * @param {string} name constant name
690                          * @param {*} object Constant value.
691                          * @description
692                          * Because the constant are fixed, they get applied before other provide methods.
693                          * See {@link auto.$provide#constant $provide.constant()}.
694                          */
695                         constant: invokeLater('$provide', 'constant', 'unshift'),
696 
697                         /**
698                          * @ngdoc method
699                          * @name angular.Module#animation
700                          * @module ng
701                          * @param {string} name animation name
702                          * @param {Function} animationFactory Factory function for creating new instance of an
703                          *                                    animation.
704                          * @description
705                          *
706                          * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
707                          *
708                          *
709                          * Defines an animation hook that can be later used with
710                          * {@link ngAnimate.$animate $animate} service and directives that use this service.
711                          *
712                          * ```js
713                          * module.animation('.animation-name', function($inject1, $inject2) {
714            *   return {
715            *     eventName : function(element, done) {
716            *       //code to run the animation
717            *       //once complete, then run done()
718            *       return function cancellationFunction(element) {
719            *         //code to cancel the animation
720            *       }
721            *     }
722            *   }
723            * })
724                          * ```
725                          *
726                          * See {@link ngAnimate.$animateProvider#register $animateProvider.register()} and
727                          * {@link ngAnimate ngAnimate module} for more information.
728                          */
729                         animation: invokeLater('$animateProvider', 'register'),
730 
731                         /**
732                          * @ngdoc method
733                          * @name angular.Module#filter
734                          * @module ng
735                          * @param {string} name Filter name.
736                          * @param {Function} filterFactory Factory function for creating new instance of filter.
737                          * @description
738                          * See {@link ng.$filterProvider#register $filterProvider.register()}.
739                          */
740                         filter: invokeLater('$filterProvider', 'register'),
741 
742                         /**
743                          * @ngdoc method
744                          * @name angular.Module#controller
745                          * @module ng
746                          * @param {string|Object} name Controller name, or an object map of controllers where the
747                          *    keys are the names and the values are the constructors.
748                          * @param {Function} constructor Controller constructor function.
749                          * @description
750                          * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
751                          */
752                         controller: invokeLater('$controllerProvider', 'register'),
753 
754                         /**
755                          * @ngdoc method
756                          * @name angular.Module#directive
757                          * @module ng
758                          * @param {string|Object} name Directive name, or an object map of directives where the
759                          *    keys are the names and the values are the factories.
760                          * @param {Function} directiveFactory Factory function for creating new instance of
761                          * directives.
762                          * @description
763                          * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
764                          */
765                         directive: invokeLater('$compileProvider', 'directive'),
766 
767                         /**
768                          * @ngdoc method
769                          * @name angular.Module#config
770                          * @module ng
771                          * @param {Function} configFn Execute this function on module load. Useful for service
772                          *    configuration.
773                          * @description
774                          * Use this method to register work which needs to be performed on module loading.
775                          * For more about how to configure services, see
776                          * {@link providers#providers_provider-recipe Provider Recipe}.
777                          */
778                         config: config,
779 
780                         /**
781                          * @ngdoc method
782                          * @name angular.Module#run
783                          * @module ng
784                          * @param {Function} initializationFn Execute this function after injector creation.
785                          *    Useful for application initialization.
786                          * @description
787                          * Use this method to register work which should be performed when the injector is done
788                          * loading all modules.
789                          */
790                         run: function(block) {
791                             runBlocks.push(block);
792                             return this;
793                         }
794                     };
795 
796                     if (configFn) {
797                         config(configFn);
798                     }
799 
800                     return  moduleInstance;
801 
802                     /**
803                      * @param {string} provider
804                      * @param {string} method
805                      * @param {String=} insertMethod
806                      * @returns {angular.Module}
807                      */
808                     function invokeLater(provider, method, insertMethod, queue) {
809                         if (!queue) queue = invokeQueue;
810                         return function() {
811                             queue[insertMethod || 'push']([provider, method, arguments]);
812                             return moduleInstance;
813                         };
814                     }
815                 });
816             };
817         });
818 
819     }
820 
821     /* global angularModule: true,//angular全局模块
822      version: true,
823 
824      $LocaleProvider,//local
825      $CompileProvider,//compile
826 
827      htmlAnchorDirective,//<a href="http://www.baidu.com">baidu</a>
828      inputDirective,//<input type='text' name='name'>
829      inputDirective,//<input type='text' name='pwd'>
830      formDirective,//<form action ='localhost:8080/book/booklist/' method='post'>
831      scriptDirective,//<script src='angular.min.js'></script>
832      selectDirective,//<select name='city' ></select>
833      styleDirective,//<style></style>
834      optionDirective,<option></option>
835      ngBindDirective,<div ng-bind='{{bookPrice}}'></div>
836      ngBindHtmlDirective,
837      ngBindTemplateDirective,
838      ngClassDirective,
839      ngClassEvenDirective,
840      ngClassOddDirective,
841      ngCspDirective,
842      ngCloakDirective,
843      ngControllerDirective,
844      ngFormDirective,
845      ngHideDirective,
846      ngIfDirective,
847      ngIncludeDirective,
848      ngIncludeFillContentDirective,
849      ngInitDirective,
850      ngNonBindableDirective,
851      ngPluralizeDirective,
852      ngRepeatDirective,
853      ngShowDirective,
854      ngStyleDirective,
855      ngSwitchDirective,
856      ngSwitchWhenDirective,
857      ngSwitchDefaultDirective,
858      ngOptionsDirective,
859      ngTranscludeDirective,
860      ngModelDirective,
861      ngListDirective,
862      ngChangeDirective,
863      patternDirective,
864      patternDirective,
865      requiredDirective,
866      requiredDirective,
867      minlengthDirective,
868      minlengthDirective,
869      maxlengthDirective,
870      maxlengthDirective,
871      ngValueDirective,
872      ngModelOptionsDirective,
873      ngAttributeAliasDirectives,
874      ngEventDirectives,
875 
876      $AnchorScrollProvider,
877      $AnimateProvider,
878      $BrowserProvider,
879      $CacheFactoryProvider,
880      $ControllerProvider,
881      $DocumentProvider,
882      $ExceptionHandlerProvider,
883      $FilterProvider,
884      $InterpolateProvider,
885      $IntervalProvider,
886      $HttpProvider,
887      $HttpBackendProvider,
888      $LocationProvider,
889      $LogProvider,
890      $ParseProvider,
891      $RootScopeProvider,
892      $QProvider,
893      $$QProvider,
894      $$SanitizeUriProvider,
895      $SceProvider,
896      $SceDelegateProvider,
897      $SnifferProvider,
898      $TemplateCacheProvider,
899      $TimeoutProvider,
900      $$RAFProvider,
901      $$AsyncCallbackProvider,
902      $WindowProvider
903      */
904 
905 
906     /**
907      * @ngdoc object
908      * @name angular.version
909      * @module ng
910      * @description
911      * An object that contains information about the current AngularJS version. This object has the
912      * following properties:
913      *
914      * - `full` – `{string}` – Full version string, such as "0.9.18".
915      * - `major` – `{number}` – Major version number, such as "0".
916      * - `minor` – `{number}` – Minor version number, such as "9".
917      * - `dot` – `{number}` – Dot version number, such as "18".
918      * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
919      */
920     var version = {
921         full: '1.3.0-beta.15',    // all of these placeholder strings will be replaced by grunt's
922         major: 1,    // package task
923         minor: 3,
924         dot: 0,
925         codeName: 'unbelievable-advancement'
926     };
查看代码

 续:时间:2014年12月15日 17:30:46html

   1     function publishExternalAPI(angular){
   2         extend(angular, {
   3             'bootstrap': bootstrap,
   4             'copy': copy,
   5             'extend': extend,
   6             'equals': equals,
   7             'element': jqLite,
   8             'forEach': forEach,
   9             'injector': createInjector,
  10             'noop':noop,
  11             'bind':bind,
  12             'toJson': toJson,
  13             'fromJson': fromJson,
  14             'identity':identity,
  15             'isUndefined': isUndefined,
  16             'isDefined': isDefined,
  17             'isString': isString,
  18             'isFunction': isFunction,
  19             'isObject': isObject,
  20             'isNumber': isNumber,
  21             'isElement': isElement,
  22             'isArray': isArray,
  23             'version': version,
  24             'isDate': isDate,
  25             'lowercase': lowercase,
  26             'uppercase': uppercase,
  27             'callbacks': {counter: 0},
  28             '$$minErr': minErr,
  29             '$$csp': csp
  30         });
  31 
  32         angularModule = setupModuleLoader(window);
  33         try {
  34             angularModule('ngLocale');
  35         } catch (e) {
  36             angularModule('ngLocale', []).provider('$locale', $LocaleProvider);
  37         }
  38 
  39         angularModule('ng', ['ngLocale'], ['$provide',
  40             function ngModule($provide) {
  41                 // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
  42                 $provide.provider({
  43                     $$sanitizeUri: $$SanitizeUriProvider
  44                 });
  45                 $provide.provider('$compile', $CompileProvider).
  46                     directive({
  47                         a: htmlAnchorDirective,
  48                         input: inputDirective,
  49                         textarea: inputDirective,
  50                         form: formDirective,
  51                         script: scriptDirective,
  52                         select: selectDirective,
  53                         style: styleDirective,
  54                         option: optionDirective,
  55                         ngBind: ngBindDirective,
  56                         ngBindHtml: ngBindHtmlDirective,
  57                         ngBindTemplate: ngBindTemplateDirective,
  58                         ngClass: ngClassDirective,
  59                         ngClassEven: ngClassEvenDirective,
  60                         ngClassOdd: ngClassOddDirective,
  61                         ngCloak: ngCloakDirective,
  62                         ngController: ngControllerDirective,
  63                         ngForm: ngFormDirective,
  64                         ngHide: ngHideDirective,
  65                         ngIf: ngIfDirective,
  66                         ngInclude: ngIncludeDirective,
  67                         ngInit: ngInitDirective,
  68                         ngNonBindable: ngNonBindableDirective,
  69                         ngPluralize: ngPluralizeDirective,
  70                         ngRepeat: ngRepeatDirective,
  71                         ngShow: ngShowDirective,
  72                         ngStyle: ngStyleDirective,
  73                         ngSwitch: ngSwitchDirective,
  74                         ngSwitchWhen: ngSwitchWhenDirective,
  75                         ngSwitchDefault: ngSwitchDefaultDirective,
  76                         ngOptions: ngOptionsDirective,
  77                         ngTransclude: ngTranscludeDirective,
  78                         ngModel: ngModelDirective,
  79                         ngList: ngListDirective,
  80                         ngChange: ngChangeDirective,
  81                         pattern: patternDirective,
  82                         ngPattern: patternDirective,
  83                         required: requiredDirective,
  84                         ngRequired: requiredDirective,
  85                         minlength: minlengthDirective,
  86                         ngMinlength: minlengthDirective,
  87                         maxlength: maxlengthDirective,
  88                         ngMaxlength: maxlengthDirective,
  89                         ngValue: ngValueDirective,
  90                         ngModelOptions: ngModelOptionsDirective
  91                     }).
  92                     directive({
  93                         ngInclude: ngIncludeFillContentDirective
  94                     }).
  95                     directive(ngAttributeAliasDirectives).
  96                     directive(ngEventDirectives);
  97                 $provide.provider({
  98                     $anchorScroll: $AnchorScrollProvider,
  99                     $animate: $AnimateProvider,
 100                     $browser: $BrowserProvider,
 101                     $cacheFactory: $CacheFactoryProvider,
 102                     $controller: $ControllerProvider,
 103                     $document: $DocumentProvider,
 104                     $exceptionHandler: $ExceptionHandlerProvider,
 105                     $filter: $FilterProvider,
 106                     $interpolate: $InterpolateProvider,
 107                     $interval: $IntervalProvider,
 108                     $http: $HttpProvider,
 109                     $httpBackend: $HttpBackendProvider,
 110                     $location: $LocationProvider,
 111                     $log: $LogProvider,
 112                     $parse: $ParseProvider,
 113                     $rootScope: $RootScopeProvider,
 114                     $q: $QProvider,
 115                     $$q: $$QProvider,
 116                     $sce: $SceProvider,
 117                     $sceDelegate: $SceDelegateProvider,
 118                     $sniffer: $SnifferProvider,
 119                     $templateCache: $TemplateCacheProvider,
 120                     $timeout: $TimeoutProvider,
 121                     $window: $WindowProvider,
 122                     $$rAF: $$RAFProvider,
 123                     $$asyncCallback : $$AsyncCallbackProvider
 124                 });
 125             }
 126         ]);
 127     }
 128 
 129     /* global JQLitePrototype: true,
 130      addEventListenerFn: true,
 131      removeEventListenerFn: true,
 132      BOOLEAN_ATTR: true,
 133      ALIASED_ATTR: true,
 134      */
 135 
 136 //////////////////////////////////
 137 //JQLite
 138 //////////////////////////////////
 139 
 140     /**
 141      * @ngdoc function
 142      * @name angular.element
 143      * @module ng
 144      * @kind function
 145      *
 146      * @description
 147      * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
 148      *
 149      * If jQuery is available, `angular.element` is an alias for the
 150      * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
 151      * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
 152      *
 153      * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
 154      * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
 155      * commonly needed functionality with the goal of having a very small footprint.</div>
 156      *
 157      * To use jQuery, simply load it before `DOMContentLoaded` event fired.
 158      *
 159      * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
 160      * jqLite; they are never raw DOM references.</div>
 161      *
 162      * ## Angular's jqLite
 163      * jqLite provides only the following jQuery methods:
 164      *
 165      * - [`addClass()`](http://api.jquery.com/addClass/)
 166      * - [`after()`](http://api.jquery.com/after/)
 167      * - [`append()`](http://api.jquery.com/append/)
 168      * - [`attr()`](http://api.jquery.com/attr/)
 169      * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
 170      * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
 171      * - [`clone()`](http://api.jquery.com/clone/)
 172      * - [`contents()`](http://api.jquery.com/contents/)
 173      * - [`css()`](http://api.jquery.com/css/)
 174      * - [`data()`](http://api.jquery.com/data/)
 175      * - [`empty()`](http://api.jquery.com/empty/)
 176      * - [`eq()`](http://api.jquery.com/eq/)
 177      * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
 178      * - [`hasClass()`](http://api.jquery.com/hasClass/)
 179      * - [`html()`](http://api.jquery.com/html/)
 180      * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
 181      * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
 182      * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors
 183      * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
 184      * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
 185      * - [`prepend()`](http://api.jquery.com/prepend/)
 186      * - [`prop()`](http://api.jquery.com/prop/)
 187      * - [`ready()`](http://api.jquery.com/ready/)
 188      * - [`remove()`](http://api.jquery.com/remove/)
 189      * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
 190      * - [`removeClass()`](http://api.jquery.com/removeClass/)
 191      * - [`removeData()`](http://api.jquery.com/removeData/)
 192      * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
 193      * - [`text()`](http://api.jquery.com/text/)
 194      * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
 195      * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
 196      * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces
 197      * - [`val()`](http://api.jquery.com/val/)
 198      * - [`wrap()`](http://api.jquery.com/wrap/)
 199      *
 200      * ## jQuery/jqLite Extras
 201      * Angular also provides the following additional methods and events to both jQuery and jqLite:
 202      *
 203      * ### Events
 204      * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
 205      *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
 206      *    element before it is removed.
 207      *
 208      * ### Methods
 209      * - `controller(name)` - retrieves the controller of the current element or its parent. By default
 210      * 返回当前元素的控制器或它的父节点。
 211      *   retrieves controller associated with the `ngController` directive. If `name` is provided as、
 212      *  默认的返回与 之关联的控制器,若是ame是camelCase命名的指令
 213      *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
 214      *   这个控制器将会被直接返回
 215      *   `'ngModel'`).例如,ngModel
 216      * - `injector()` - retrieves the injector of the current element or its parent.
 217      * 返回当前元素的注入器或它的父节点
 218      * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
 219      *   element or its parent.
 220      *   返回当前元素的顶级做用域或它的父元素
 221      * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
 222      * 若是一旦被附加到当前的元素
 223      *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
 224      *   当启用一个新的隔离做用域时,它只能做用于某一个元素,包括指令
 225      *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
 226      *   当在这个当前元素调用scope()方法时,它将老是返回其原来的非隔离做用域
 227      * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
 228      *   parent element is reached.
 229      *
 230      * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
 231      * @returns {Object} jQuery object.
 232      */
 233 
 234     JQLite.expando = 'ng339';
 235 
 236     var jqCache = JQLite.cache = {},
 237         jqId = 1,
 238         addEventListenerFn = (window.document.addEventListener
 239             ? function(element, type, fn) {element.addEventListener(type, fn, false);}
 240             : function(element, type, fn) {element.attachEvent('on' + type, fn);}),
 241         removeEventListenerFn = (window.document.removeEventListener
 242             ? function(element, type, fn) {element.removeEventListener(type, fn, false); }
 243             : function(element, type, fn) {element.detachEvent('on' + type, fn); });
 244 
 245     /*
 246      * !!! This is an undocumented "private" function !!!
 247      */
 248     var jqData = JQLite._data = function(node) {
 249         //jQuery always returns an object on cache miss
 250         //jQuery老是从cache返回一个对象
 251         return this.cache[node[this.expando]] || {};
 252     };
 253 
 254     function jqNextId() { return ++jqId; }
 255 
 256 
 257     var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
 258     var MOZ_HACK_REGEXP = /^moz([A-Z])/;
 259     var jqLiteMinErr = minErr('jqLite');
 260 
 261     /**
 262      * Converts snake_case to camelCase.
 263      * Also there is special case for Moz prefix starting with upper case letter.
 264      * @param name Name to normalize
 265      */
 266     function camelCase(name) {
 267         return name.
 268             replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
 269                 return offset ? letter.toUpperCase() : letter;
 270             }).
 271             replace(MOZ_HACK_REGEXP, 'Moz$1');
 272     }
 273 
 274     var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/;
 275     var HTML_REGEXP = /<|&#?\w+;/;
 276     var TAG_NAME_REGEXP = /<([\w:]+)/;
 277     var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi;
 278 
 279     var wrapMap = {
 280         'option': [1, '<select multiple="multiple">', '</select>'],
 281 
 282         'thead': [1, '<table>', '</table>'],
 283         'col': [2, '<table><colgroup>', '</colgroup></table>'],
 284         'tr': [2, '<table><tbody>', '</tbody></table>'],
 285         'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
 286         '_default': [0, "", ""]
 287     };
 288 
 289     wrapMap.optgroup = wrapMap.option;
 290     wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
 291     wrapMap.th = wrapMap.td;
 292 
 293     function jqLiteIsTextNode(html) {
 294         return !HTML_REGEXP.test(html);
 295     }
 296 
 297     function jqLiteAcceptsData(node) {
 298         // The window object can accept data but has no nodeType
 299         // Otherwise we are only interested in elements (1) and documents (9)
 300         return !node.nodeType || node.nodeType === 1 || node.nodeType === 9;
 301     }
 302 
 303     function jqLiteBuildFragment(html, context) {
 304         var elem, tmp, tag, wrap,
 305             fragment = context.createDocumentFragment(),
 306             nodes = [], i;
 307 
 308         if (jqLiteIsTextNode(html)) {
 309             // Convert non-html into a text node
 310             nodes.push(context.createTextNode(html));
 311         } else {
 312             // Convert html into DOM nodes
 313             tmp = tmp || fragment.appendChild(context.createElement("div"));
 314             tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
 315             wrap = wrapMap[tag] || wrapMap._default;
 316             tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
 317 
 318             // Descend through wrappers to the right content
 319             i = wrap[0];
 320             while (i--) {
 321                 tmp = tmp.lastChild;
 322             }
 323 
 324             nodes = concat(nodes, tmp.childNodes);
 325 
 326             tmp = fragment.firstChild;
 327             tmp.textContent = "";
 328         }
 329 
 330         // Remove wrapper from fragment
 331         fragment.textContent = "";
 332         fragment.innerHTML = ""; // Clear inner HTML
 333         forEach(nodes, function(node) {
 334             fragment.appendChild(node);
 335         });
 336 
 337         return fragment;
 338     }
 339 
 340     function jqLiteParseHTML(html, context) {
 341         context = context || document;
 342         var parsed;
 343 
 344         if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
 345             return [context.createElement(parsed[1])];
 346         }
 347 
 348         if ((parsed = jqLiteBuildFragment(html, context))) {
 349             return parsed.childNodes;
 350         }
 351 
 352         return [];
 353     }
 354 
 355 /////////////////////////////////////////////
 356     function JQLite(element) {
 357         if (element instanceof JQLite) {
 358             return element;
 359         }
 360         if (isString(element)) {
 361             element = trim(element);
 362         }
 363         if (!(this instanceof JQLite)) {
 364             if (isString(element) && element.charAt(0) != '<') {
 365                 throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
 366             }
 367             return new JQLite(element);
 368         }
 369 
 370         if (isString(element)) {
 371             jqLiteAddNodes(this, jqLiteParseHTML(element));
 372         } else {
 373             jqLiteAddNodes(this, element);
 374         }
 375     }
 376 
 377     function jqLiteClone(element) {
 378         return element.cloneNode(true);
 379     }
 380 
 381     function jqLiteDealoc(element, onlyDescendants){
 382         if (!onlyDescendants) jqLiteRemoveData(element);
 383 
 384         if (element.childNodes && element.childNodes.length) {
 385             // we use querySelectorAll because documentFragments don't have getElementsByTagName
 386             var descendants = element.getElementsByTagName ? element.getElementsByTagName('*') :
 387                 element.querySelectorAll ? element.querySelectorAll('*') : [];
 388             for (var i = 0, l = descendants.length; i < l; i++) {
 389                 jqLiteRemoveData(descendants[i]);
 390             }
 391         }
 392     }
 393 
 394     function jqLiteOff(element, type, fn, unsupported) {
 395         if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
 396 
 397         var events = jqLiteExpandoStore(element, 'events'),
 398             handle = jqLiteExpandoStore(element, 'handle');
 399 
 400         if (!handle) return; //no listeners registered
 401 
 402         if (isUndefined(type)) {
 403             forEach(events, function(eventHandler, type) {
 404                 removeEventListenerFn(element, type, eventHandler);
 405                 delete events[type];
 406             });
 407         } else {
 408             forEach(type.split(' '), function(type) {
 409                 if (isUndefined(fn)) {
 410                     removeEventListenerFn(element, type, events[type]);
 411                     delete events[type];
 412                 } else {
 413                     arrayRemove(events[type] || [], fn);
 414                 }
 415             });
 416         }
 417     }
 418 
 419     function jqLiteRemoveData(element, name) {
 420         var expandoId = element.ng339,
 421             expandoStore = jqCache[expandoId];
 422 
 423         if (expandoStore) {
 424             if (name) {
 425                 delete jqCache[expandoId].data[name];
 426                 return;
 427             }
 428 
 429             if (expandoStore.handle) {
 430                 expandoStore.events.$destroy && expandoStore.handle({}, '$destroy');
 431                 jqLiteOff(element);
 432             }
 433             delete jqCache[expandoId];
 434             element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
 435         }
 436     }
 437 
 438     function jqLiteExpandoStore(element, key, value) {
 439         var expandoId = element.ng339,
 440             expandoStore = jqCache[expandoId || -1];
 441 
 442         if (isDefined(value)) {
 443             if (!expandoStore) {
 444                 element.ng339 = expandoId = jqNextId();
 445                 expandoStore = jqCache[expandoId] = {};
 446             }
 447             expandoStore[key] = value;
 448         } else {
 449             return expandoStore && expandoStore[key];
 450         }
 451     }
 452 
 453     function jqLiteData(element, key, value) {
 454         if (jqLiteAcceptsData(element)) {
 455             var data = jqLiteExpandoStore(element, 'data'),
 456                 isSetter = isDefined(value),
 457                 keyDefined = !isSetter && isDefined(key),
 458                 isSimpleGetter = keyDefined && !isObject(key);
 459 
 460             if (!data && !isSimpleGetter) {
 461                 jqLiteExpandoStore(element, 'data', data = {});
 462             }
 463 
 464             if (isSetter) {
 465                 data[key] = value;
 466             } else {
 467                 if (keyDefined) {
 468                     if (isSimpleGetter) {
 469                         // don't create data in this case.
 470                         return data && data[key];
 471                     } else {
 472                         extend(data, key);
 473                     }
 474                 } else {
 475                     return data;
 476                 }
 477             }
 478         }
 479     }
 480 
 481     function jqLiteHasClass(element, selector) {
 482         if (!element.getAttribute) return false;
 483         return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
 484             indexOf( " " + selector + " " ) > -1);
 485     }
 486 
 487     function jqLiteRemoveClass(element, cssClasses) {
 488         if (cssClasses && element.setAttribute) {
 489             forEach(cssClasses.split(' '), function(cssClass) {
 490                 element.setAttribute('class', trim(
 491                         (" " + (element.getAttribute('class') || '') + " ")
 492                             .replace(/[\n\t]/g, " ")
 493                             .replace(" " + trim(cssClass) + " ", " "))
 494                 );
 495             });
 496         }
 497     }
 498 
 499     function jqLiteAddClass(element, cssClasses) {
 500         if (cssClasses && element.setAttribute) {
 501             var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
 502                 .replace(/[\n\t]/g, " ");
 503 
 504             forEach(cssClasses.split(' '), function(cssClass) {
 505                 cssClass = trim(cssClass);
 506                 if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
 507                     existingClasses += cssClass + ' ';
 508                 }
 509             });
 510 
 511             element.setAttribute('class', trim(existingClasses));
 512         }
 513     }
 514 
 515 
 516     function jqLiteAddNodes(root, elements) {
 517         // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
 518 
 519         if (elements) {
 520 
 521             // if a Node (the most common case)
 522             if (elements.nodeType) {
 523                 root[root.length++] = elements;
 524             } else {
 525                 var length = elements.length;
 526 
 527                 // if an Array or NodeList and not a Window
 528                 if (typeof length === 'number' && elements.window !== elements) {
 529                     if (length) {
 530                         if (elements.item) {
 531                             // convert NodeList to an Array to make PhantomJS 1.x happy
 532                             elements = slice.call(elements);
 533                         }
 534                         push.apply(root, elements);
 535                     }
 536                 } else {
 537                     root[root.length++] = elements;
 538                 }
 539             }
 540         }
 541     }
 542 
 543 
 544     function jqLiteController(element, name) {
 545         return jqLiteInheritedData(element, '$' + (name || 'ngController' ) + 'Controller');
 546     }
 547 
 548     function jqLiteInheritedData(element, name, value) {
 549         element = jqLite(element);
 550 
 551         // if element is the document object work with the html element instead
 552         // this makes $(document).scope() possible
 553         if(element[0].nodeType == 9) {
 554             element = element.find('html');
 555         }
 556         var names = isArray(name) ? name : [name];
 557 
 558         while (element.length) {
 559             var node = element[0];
 560             for (var i = 0, ii = names.length; i < ii; i++) {
 561                 if ((value = element.data(names[i])) !== undefined) return value;
 562             }
 563 
 564             // If dealing with a document fragment node with a host element, and no parent, use the host
 565             // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
 566             // to lookup parent controllers.
 567             element = jqLite(node.parentNode || (node.nodeType === 11 && node.host));
 568         }
 569     }
 570 
 571     function jqLiteEmpty(element) {
 572         jqLiteDealoc(element, true);
 573         while (element.firstChild) {
 574             element.removeChild(element.firstChild);
 575         }
 576     }
 577 
 578 //////////////////////////////////////////
 579 // Functions which are declared directly.
 580 //////////////////////////////////////////
 581     var JQLitePrototype = JQLite.prototype = {
 582         ready: function(fn) {
 583             var fired = false;
 584 
 585             function trigger() {
 586                 if (fired) return;
 587                 fired = true;
 588                 fn();
 589             }
 590 
 591             // check if document already is loaded
 592             if (document.readyState === 'complete'){//判断文档是否已经加载完成
 593                 setTimeout(trigger);
 594             } else {
 595                 this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
 596                 // we can not use jqLite since we are not done loading and jQuery could be loaded later.
 597                 // jshint -W064
 598                 JQLite(window).on('load', trigger); // fallback to window.onload for others
 599                 // jshint +W064
 600             }
 601         },
 602         toString: function() {
 603             var value = [];
 604             forEach(this, function(e){ value.push('' + e);});
 605             return '[' + value.join(', ') + ']';
 606         },
 607 
 608         eq: function(index) {
 609             return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
 610         },
 611 
 612         length: 0,
 613         push: push,
 614         sort: [].sort,
 615         splice: [].splice
 616     };
 617 
 618 //////////////////////////////////////////
 619 // Functions iterating getter/setters.
 620 // these functions return self on setter and
 621 // value on get.
 622 //////////////////////////////////////////
 623     var BOOLEAN_ATTR = {};
 624     forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
 625         BOOLEAN_ATTR[lowercase(value)] = value;
 626     });
 627     var BOOLEAN_ELEMENTS = {};
 628     forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
 629         BOOLEAN_ELEMENTS[value] = true;
 630     });
 631     var ALIASED_ATTR = {
 632         'ngMinlength' : 'minlength',
 633         'ngMaxlength' : 'maxlength',
 634         'ngPattern' : 'pattern'
 635     };
 636 
 637     function getBooleanAttrName(element, name) {
 638         // check dom last since we will most likely fail on name
 639         var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
 640 
 641         // booleanAttr is here twice to minimize DOM access
 642         return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
 643     }
 644 
 645     function getAliasedAttrName(element, name) {
 646         var nodeName = element.nodeName;
 647         return (nodeName === 'INPUT' || nodeName === 'TEXTAREA') && ALIASED_ATTR[name];
 648     }
 649 
 650     forEach({
 651         data: jqLiteData,
 652         inheritedData: jqLiteInheritedData,
 653 
 654         scope: function(element) {
 655             // Can't use jqLiteData here directly so we stay compatible with jQuery!
 656             return jqLite(element).data('$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
 657         },
 658 
 659         isolateScope: function(element) {
 660             // Can't use jqLiteData here directly so we stay compatible with jQuery!
 661             return jqLite(element).data('$isolateScope') || jqLite(element).data('$isolateScopeNoTemplate');
 662         },
 663 
 664         controller: jqLiteController,
 665 
 666         injector: function(element) {
 667             return jqLiteInheritedData(element, '$injector');
 668         },
 669 
 670         removeAttr: function(element,name) {
 671             element.removeAttribute(name);
 672         },
 673 
 674         hasClass: jqLiteHasClass,
 675 
 676         css: function(element, name, value) {
 677             name = camelCase(name);
 678 
 679             if (isDefined(value)) {
 680                 element.style[name] = value;
 681             } else {
 682                 var val;
 683 
 684                 if (msie <= 8) {
 685                     // this is some IE specific weirdness that jQuery 1.6.4 does not sure why
 686                     val = element.currentStyle && element.currentStyle[name];
 687                     if (val === '') val = 'auto';
 688                 }
 689 
 690                 val = val || element.style[name];
 691 
 692                 if (msie <= 8) {
 693                     // jquery weirdness :-/
 694                     val = (val === '') ? undefined : val;
 695                 }
 696 
 697                 return  val;
 698             }
 699         },
 700 
 701         attr: function(element, name, value){
 702             var lowercasedName = lowercase(name);
 703             if (BOOLEAN_ATTR[lowercasedName]) {
 704                 if (isDefined(value)) {
 705                     if (!!value) {
 706                         element[name] = true;
 707                         element.setAttribute(name, lowercasedName);
 708                     } else {
 709                         element[name] = false;
 710                         element.removeAttribute(lowercasedName);
 711                     }
 712                 } else {
 713                     return (element[name] ||
 714                     (element.attributes.getNamedItem(name)|| noop).specified)
 715                         ? lowercasedName
 716                         : undefined;
 717                 }
 718             } else if (isDefined(value)) {
 719                 element.setAttribute(name, value);
 720             } else if (element.getAttribute) {
 721                 // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
 722                 // some elements (e.g. Document) don't have get attribute, so return undefined
 723                 var ret = element.getAttribute(name, 2);
 724                 // normalize non-existing attributes to undefined (as jQuery)
 725                 return ret === null ? undefined : ret;
 726             }
 727         },
 728 
 729         prop: function(element, name, value) {
 730             if (isDefined(value)) {
 731                 element[name] = value;
 732             } else {
 733                 return element[name];
 734             }
 735         },
 736 
 737         text: (function() {
 738             getText.$dv = '';
 739             return getText;
 740 
 741             function getText(element, value) {
 742                 if (isUndefined(value)) {
 743                     var nodeType = element.nodeType;
 744                     return (nodeType === 1 || nodeType === 3) ? element.textContent : '';
 745                 }
 746                 element.textContent = value;
 747             }
 748         })(),
 749 
 750         val: function(element, value) {
 751             if (isUndefined(value)) {
 752                 if (element.multiple && nodeName_(element) === 'select') {
 753                     var result = [];
 754                     forEach(element.options, function (option) {
 755                         if (option.selected) {
 756                             result.push(option.value || option.text);
 757                         }
 758                     });
 759                     return result.length === 0 ? null : result;
 760                 }
 761                 return element.value;
 762             }
 763             element.value = value;
 764         },
 765 
 766         html: function(element, value) {
 767             if (isUndefined(value)) {
 768                 return element.innerHTML;
 769             }
 770             jqLiteDealoc(element, true);
 771             element.innerHTML = value;
 772         },
 773 
 774         empty: jqLiteEmpty
 775     }, function(fn, name){
 776         /**
 777          * Properties: writes return selection, reads return first value
 778          */
 779         JQLite.prototype[name] = function(arg1, arg2) {
 780             var i, key;
 781             var nodeCount = this.length;
 782 
 783             // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
 784             // in a way that survives minification.
 785             // jqLiteEmpty takes no arguments but is a setter.
 786             if (fn !== jqLiteEmpty &&
 787                 (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) {
 788                 if (isObject(arg1)) {
 789 
 790                     // we are a write, but the object properties are the key/values
 791                     for (i = 0; i < nodeCount; i++) {
 792                         if (fn === jqLiteData) {
 793                             // data() takes the whole object in jQuery
 794                             fn(this[i], arg1);
 795                         } else {
 796                             for (key in arg1) {
 797                                 fn(this[i], key, arg1[key]);
 798                             }
 799                         }
 800                     }
 801                     // return self for chaining
 802                     return this;
 803                 } else {
 804                     // we are a read, so read the first child.
 805                     // TODO: do we still need this?
 806                     var value = fn.$dv;
 807                     // Only if we have $dv do we iterate over all, otherwise it is just the first element.
 808                     var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount;
 809                     for (var j = 0; j < jj; j++) {
 810                         var nodeValue = fn(this[j], arg1, arg2);
 811                         value = value ? value + nodeValue : nodeValue;
 812                     }
 813                     return value;
 814                 }
 815             } else {
 816                 // we are a write, so apply to all children
 817                 for (i = 0; i < nodeCount; i++) {
 818                     fn(this[i], arg1, arg2);
 819                 }
 820                 // return self for chaining
 821                 return this;
 822             }
 823         };
 824     });
 825 
 826     function createEventHandler(element, events) {
 827         var eventHandler = function (event, type) {
 828             if (!event.preventDefault) {
 829                 event.preventDefault = function() {
 830                     event.returnValue = false; //ie
 831                 };
 832             }
 833 
 834             if (!event.stopPropagation) {
 835                 event.stopPropagation = function() {
 836                     event.cancelBubble = true; //ie
 837                 };
 838             }
 839 
 840             if (!event.target) {
 841                 event.target = event.srcElement || document;
 842             }
 843 
 844             if (isUndefined(event.defaultPrevented)) {
 845                 var prevent = event.preventDefault;
 846                 event.preventDefault = function() {
 847                     event.defaultPrevented = true;
 848                     prevent.call(event);
 849                 };
 850                 event.defaultPrevented = false;
 851             }
 852 
 853             event.isDefaultPrevented = function() {
 854                 return event.defaultPrevented || event.returnValue === false;
 855             };
 856 
 857             // Copy event handlers in case event handlers array is modified during execution.
 858             var eventHandlersCopy = shallowCopy(events[type || event.type] || []);
 859 
 860             forEach(eventHandlersCopy, function(fn) {
 861                 fn.call(element, event);
 862             });
 863 
 864             // Remove monkey-patched methods (IE),
 865             // as they would cause memory leaks in IE8.
 866             if (msie <= 8) {
 867                 // IE7/8 does not allow to delete property on native object
 868                 event.preventDefault = null;
 869                 event.stopPropagation = null;
 870                 event.isDefaultPrevented = null;
 871             } else {
 872                 // It shouldn't affect normal browsers (native methods are defined on prototype).
 873                 delete event.preventDefault;
 874                 delete event.stopPropagation;
 875                 delete event.isDefaultPrevented;
 876             }
 877         };
 878         eventHandler.elem = element;
 879         return eventHandler;
 880     }
 881 
 882 //////////////////////////////////////////
 883 // Functions iterating traversal.
 884 // These functions chain results into a single
 885 // selector.
 886 //////////////////////////////////////////
 887     forEach({
 888         removeData: jqLiteRemoveData,
 889 
 890         on: function onFn(element, type, fn, unsupported){
 891             if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
 892 
 893             // Do not add event handlers to non-elements because they will not be cleaned up.
 894             if (!jqLiteAcceptsData(element)) {
 895                 return;
 896             }
 897 
 898             var events = jqLiteExpandoStore(element, 'events'),
 899                 handle = jqLiteExpandoStore(element, 'handle');
 900 
 901             if (!events) jqLiteExpandoStore(element, 'events', events = {});
 902             if (!handle) jqLiteExpandoStore(element, 'handle', handle = createEventHandler(element, events));
 903 
 904             forEach(type.split(' '), function(type){
 905                 var eventFns = events[type];
 906 
 907                 if (!eventFns) {
 908                     if (type == 'mouseenter' || type == 'mouseleave') {
 909                         var contains = document.body.contains || document.body.compareDocumentPosition ?
 910                             function( a, b ) {
 911                                 // jshint bitwise: false
 912                                 var adown = a.nodeType === 9 ? a.documentElement : a,
 913                                     bup = b && b.parentNode;
 914                                 return a === bup || !!( bup && bup.nodeType === 1 && (
 915                                         adown.contains ?
 916                                             adown.contains( bup ) :
 917                                         a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
 918                                     ));
 919                             } :
 920                             function( a, b ) {
 921                                 if ( b ) {
 922                                     while ( (b = b.parentNode) ) {
 923                                         if ( b === a ) {
 924                                             return true;
 925                                         }
 926                                     }
 927                                 }
 928                                 return false;
 929                             };
 930 
 931                         events[type] = [];
 932 
 933                         // Refer to jQuery's implementation of mouseenter & mouseleave
 934                         // Read about mouseenter and mouseleave:
 935                         // http://www.quirksmode.org/js/events_mouse.html#link8
 936                         var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"};
 937 
 938                         onFn(element, eventmap[type], function(event) {
 939                             var target = this, related = event.relatedTarget;
 940                             // For mousenter/leave call the handler if related is outside the target.
 941                             // NB: No relatedTarget if the mouse left/entered the browser window
 942                             if ( !related || (related !== target && !contains(target, related)) ){
 943                                 handle(event, type);
 944                             }
 945                         });
 946 
 947                     } else {
 948                         addEventListenerFn(element, type, handle);
 949                         events[type] = [];
 950                     }
 951                     eventFns = events[type];
 952                 }
 953                 eventFns.push(fn);
 954             });
 955         },
 956 
 957         off: jqLiteOff,
 958 
 959         one: function(element, type, fn) {
 960             element = jqLite(element);
 961 
 962             //add the listener twice so that when it is called
 963             //you can remove the original function and still be
 964             //able to call element.off(ev, fn) normally
 965             element.on(type, function onFn() {
 966                 element.off(type, fn);
 967                 element.off(type, onFn);
 968             });
 969             element.on(type, fn);
 970         },
 971 
 972         replaceWith: function(element, replaceNode) {
 973             var index, parent = element.parentNode;
 974             jqLiteDealoc(element);
 975             forEach(new JQLite(replaceNode), function(node){
 976                 if (index) {
 977                     parent.insertBefore(node, index.nextSibling);
 978                 } else {
 979                     parent.replaceChild(node, element);
 980                 }
 981                 index = node;
 982             });
 983         },
 984 
 985         children: function(element) {
 986             var children = [];
 987             forEach(element.childNodes, function(element){
 988                 if (element.nodeType === 1)
 989                     children.push(element);
 990             });
 991             return children;
 992         },
 993 
 994         contents: function(element) {
 995             return element.contentDocument || element.childNodes || [];
 996         },
 997 
 998         append: function(element, node) {
 999             forEach(new JQLite(node), function(child){
1000                 if (element.nodeType === 1 || element.nodeType === 11) {
1001                     element.appendChild(child);
1002                 }
1003             });
1004         },
1005 
1006         prepend: function(element, node) {
1007             if (element.nodeType === 1) {
1008                 var index = element.firstChild;
1009                 forEach(new JQLite(node), function(child){
1010                     element.insertBefore(child, index);
1011                 });
1012             }
1013         },
1014 
1015         wrap: function(element, wrapNode) {
1016             wrapNode = jqLite(wrapNode)[0];
1017             var parent = element.parentNode;
1018             if (parent) {
1019                 parent.replaceChild(wrapNode, element);
1020             }
1021             wrapNode.appendChild(element);
1022         },
1023 
1024         remove: function(element) {
1025             jqLiteDealoc(element);
1026             var parent = element.parentNode;
1027             if (parent) parent.removeChild(element);
1028         },
1029 
1030         after: function(element, newElement) {
1031             var index = element, parent = element.parentNode;
1032             forEach(new JQLite(newElement), function(node){
1033                 parent.insertBefore(node, index.nextSibling);
1034                 index = node;
1035             });
1036         },
1037 
1038         addClass: jqLiteAddClass,
1039         removeClass: jqLiteRemoveClass,
1040 
1041         toggleClass: function(element, selector, condition) {
1042             if (selector) {
1043                 forEach(selector.split(' '), function(className){
1044                     var classCondition = condition;
1045                     if (isUndefined(classCondition)) {
1046                         classCondition = !jqLiteHasClass(element, className);
1047                     }
1048                     (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
1049                 });
1050             }
1051         },
1052 
1053         parent: function(element) {
1054             var parent = element.parentNode;
1055             return parent && parent.nodeType !== 11 ? parent : null;
1056         },
1057 
1058         next: function(element) {
1059             if (element.nextElementSibling) {
1060                 return element.nextElementSibling;
1061             }
1062 
1063             // IE8 doesn't have nextElementSibling
1064             var elm = element.nextSibling;
1065             while (elm != null && elm.nodeType !== 1) {
1066                 elm = elm.nextSibling;
1067             }
1068             return elm;
1069         },
1070 
1071         find: function(element, selector) {
1072             if (element.getElementsByTagName) {
1073                 return element.getElementsByTagName(selector);
1074             } else {
1075                 return [];
1076             }
1077         },
1078 
1079         clone: jqLiteClone,
1080 
1081         triggerHandler: function(element, eventName, eventData) {
1082             var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName];
1083 
1084             eventData = eventData || [];
1085 
1086             var event = [{
1087                 preventDefault: function() {
1088                     this.defaultPrevented = true;
1089                 },
1090                 isDefaultPrevented: function() {
1091                     return this.defaultPrevented === true;
1092                 },
1093                 stopPropagation: noop
1094             }];
1095 
1096             forEach(eventFns, function(fn) {
1097                 fn.apply(element, event.concat(eventData));
1098             });
1099         }
1100     }, function(fn, name){
1101         /**
1102          * chaining functions
1103          */
1104         JQLite.prototype[name] = function(arg1, arg2, arg3) {
1105             var value;
1106             for(var i=0; i < this.length; i++) {
1107                 if (isUndefined(value)) {
1108                     value = fn(this[i], arg1, arg2, arg3);
1109                     if (isDefined(value)) {
1110                         // any function which returns a value needs to be wrapped
1111                         value = jqLite(value);
1112                     }
1113                 } else {
1114                     jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
1115                 }
1116             }
1117             return isDefined(value) ? value : this;
1118         };
1119 
1120         // bind legacy bind/unbind to on/off
1121         JQLite.prototype.bind = JQLite.prototype.on;
1122         JQLite.prototype.unbind = JQLite.prototype.off;
1123     });
1124 
1125     /**
1126      * Computes a hash of an 'obj'.
1127      * Hash of a:
1128      *  string is string
1129      *  number is number as string
1130      *  object is either result of calling $$hashKey function on the object or uniquely generated id,
1131      *         that is also assigned to the $$hashKey property of the object.
1132      *
1133      * @param obj
1134      * @returns {string} hash string such that the same input will have the same hash string.
1135      *         The resulting string key is in 'type:hashKey' format.
1136      */
1137     function hashKey(obj, nextUidFn) {
1138         var objType = typeof obj,
1139             key;
1140 
1141         if (objType == 'function' || (objType == 'object' && obj !== null)) {
1142             if (typeof (key = obj.$$hashKey) == 'function') {
1143                 // must invoke on object to keep the right this
1144                 key = obj.$$hashKey();
1145             } else if (key === undefined) {
1146                 key = obj.$$hashKey = (nextUidFn || nextUid)();
1147             }
1148         } else {
1149             key = obj;
1150         }
1151 
1152         return objType + ':' + key;
1153     }
1154 
1155     /**
1156      * HashMap which can use objects as keys
1157      */
1158     function HashMap(array, isolatedUid) {
1159         if (isolatedUid) {
1160             var uid = 0;
1161             this.nextUid = function() {
1162                 return ++uid;
1163             };
1164         }
1165         forEach(array, this.put, this);
1166     }
1167     HashMap.prototype = {
1168         /**
1169          * Store key value pair
1170          * @param key key to store can be any type
1171          * @param value value to store can be any type
1172          */
1173         put: function(key, value) {
1174             this[hashKey(key, this.nextUid)] = value;
1175         },
1176 
1177         /**
1178          * @param key
1179          * @returns {Object} the value for the key
1180          */
1181         get: function(key) {
1182             return this[hashKey(key, this.nextUid)];
1183         },
1184 
1185         /**
1186          * Remove the key/value pair
1187          * @param key
1188          */
1189         remove: function(key) {
1190             var value = this[key = hashKey(key, this.nextUid)];
1191             delete this[key];
1192             return value;
1193         }
1194     };
1195 
1196     /**
1197      * @ngdoc function
1198      * @module ng
1199      * @name angular.injector
1200      * @kind function
1201      *
1202      * @description
1203      * Creates an injector function that can be used for retrieving services as well as for
1204      * dependency injection (see {@link guide/di dependency injection}).
1205      *
1206 
1207      * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
1208      *        {@link angular.module}. The `ng` module must be explicitly added.
1209      * @returns {function()} Injector function. See {@link auto.$injector $injector}.
1210      *
1211      * @example
1212      * Typical usage
1213      * ```js
1214      *   // create an injector
1215      *   var $injector = angular.injector(['ng']);
1216      *
1217      *   // use the injector to kick off your application
1218      *   // use the type inference to auto inject arguments, or use implicit injection
1219      *   $injector.invoke(function($rootScope, $compile, $document){
1220  *     $compile($document)($rootScope);
1221  *     $rootScope.$digest();
1222  *   });
1223      * ```
1224      *
1225      * Sometimes you want to get access to the injector of a currently running Angular app
1226      * from outside Angular. Perhaps, you want to inject and compile some markup after the
1227      * application has been bootstrapped. You can do this using the extra `injector()` added
1228      * to JQuery/jqLite elements. See {@link angular.element}.
1229      *
1230      * *This is fairly rare but could be the case if a third party library is injecting the
1231      * markup.*
1232      *
1233      * In the following example a new block of HTML containing a `ng-controller`
1234      * directive is added to the end of the document body by JQuery. We then compile and link
1235      * it into the current AngularJS scope.
1236      *
1237      * ```js
1238      * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
1239      * $(document.body).append($div);
1240      *
1241      * angular.element(document).injector().invoke(function($compile) {
1242  *   var scope = angular.element($div).scope();
1243  *   $compile($div)(scope);
1244  * });
1245      * ```
1246      */
1247 
1248 
1249     /**
1250      * @ngdoc module
1251      * @name auto
1252      * @description
1253      *
1254      * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
1255      */
1256 
1257     var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
1258     var FN_ARG_SPLIT = /,/;
1259     var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
1260     var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
1261     var $injectorMinErr = minErr('$injector');
1262 
1263     function anonFn(fn) {
1264         // For anonymous functions, showing at the very least the function signature can help in
1265         // debugging.
1266         var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
1267             args = fnText.match(FN_ARGS);
1268         if (args) {
1269             return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
1270         }
1271         return 'fn';
1272     }
1273 
1274     function annotate(fn, strictDi, name) {
1275         var $inject,
1276             fnText,
1277             argDecl,
1278             last;
1279 
1280         if (typeof fn === 'function') {
1281             if (!($inject = fn.$inject)) {
1282                 $inject = [];
1283                 if (fn.length) {
1284                     if (strictDi) {
1285                         if (!isString(name) || !name) {
1286                             name = fn.name || anonFn(fn);
1287                         }
1288                         throw $injectorMinErr('strictdi',
1289                             '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
1290                     }
1291                     fnText = fn.toString().replace(STRIP_COMMENTS, '');
1292                     argDecl = fnText.match(FN_ARGS);
1293                     forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
1294                         arg.replace(FN_ARG, function(all, underscore, name){
1295                             $inject.push(name);
1296                         });
1297                     });
1298                 }
1299                 fn.$inject = $inject;
1300             }
1301         } else if (isArray(fn)) {
1302             last = fn.length - 1;
1303             assertArgFn(fn[last], 'fn');
1304             $inject = fn.slice(0, last);
1305         } else {
1306             assertArgFn(fn, 'fn', true);
1307         }
1308         return $inject;
1309     }
1310 
1311 ///////////////////////////////////////
1312 
1313     /**
1314      * @ngdoc service
1315      * @name $injector
1316      * @kind function
1317      *
1318      * @description
1319      *
1320      * `$injector` is used to retrieve object instances as defined by
1321      * {@link auto.$provide provider}, instantiate types, invoke methods,
1322      * and load modules.
1323      *
1324      * The following always holds true:
1325      *
1326      * ```js
1327      *   var $injector = angular.injector();
1328      *   expect($injector.get('$injector')).toBe($injector);
1329      *   expect($injector.invoke(function($injector){
1330  *     return $injector;
1331  *   }).toBe($injector);
1332      * ```
1333      *
1334      * # Injection Function Annotation
1335      *
1336      * JavaScript does not have annotations, and annotations are needed for dependency injection. The
1337      * following are all valid ways of annotating function with injection arguments and are equivalent.
1338      *
1339      * ```js
1340      *   // inferred (only works if code not minified/obfuscated)
1341      *   $injector.invoke(function(serviceA){});
1342      *
1343      *   // annotated
1344      *   function explicit(serviceA) {};
1345      *   explicit.$inject = ['serviceA'];
1346      *   $injector.invoke(explicit);
1347      *
1348      *   // inline
1349      *   $injector.invoke(['serviceA', function(serviceA){}]);
1350      * ```
1351      *
1352      * ## Inference
1353      *
1354      * In JavaScript calling `toString()` on a function returns the function definition. The definition
1355      * can then be parsed and the function arguments can be extracted. *NOTE:* This does not work with
1356      * minification, and obfuscation tools since these tools change the argument names.
1357      *
1358      * ## `$inject` Annotation
1359      * By adding an `$inject` property onto a function the injection parameters can be specified.
1360      *
1361      * ## Inline
1362      * As an array of injection names, where the last item in the array is the function to call.
1363      */
1364 
1365     /**
1366      * @ngdoc method
1367      * @name $injector#get
1368      *
1369      * @description
1370      * Return an instance of the service.
1371      *
1372      * @param {string} name The name of the instance to retrieve.
1373      * @return {*} The instance.
1374      */
1375 
1376     /**
1377      * @ngdoc method
1378      * @name $injector#invoke
1379      *
1380      * @description
1381      * Invoke the method and supply the method arguments from the `$injector`.
1382      *
1383      * @param {!Function} fn The function to invoke. Function parameters are injected according to the
1384      *   {@link guide/di $inject Annotation} rules.
1385      * @param {Object=} self The `this` for the invoked method.
1386      * @param {Object=} locals Optional object. If preset then any argument names are read from this
1387      *                         object first, before the `$injector` is consulted.
1388      * @returns {*} the value returned by the invoked `fn` function.
1389      */
1390 
1391     /**
1392      * @ngdoc method
1393      * @name $injector#has
1394      *
1395      * @description
1396      * Allows the user to query if the particular service exists.
1397      *
1398      * @param {string} Name of the service to query.
1399      * @returns {boolean} returns true if injector has given service.
1400      */
1401 
1402     /**
1403      * @ngdoc method
1404      * @name $injector#instantiate
1405      * @description
1406      * Create a new instance of JS type. The method takes a constructor function, invokes the new
1407      * operator, and supplies all of the arguments to the constructor function as specified by the
1408      * constructor annotation.
1409      *
1410      * @param {Function} Type Annotated constructor function.
1411      * @param {Object=} locals Optional object. If preset then any argument names are read from this
1412      * object first, before the `$injector` is consulted.
1413      * @returns {Object} new instance of `Type`.
1414      */
1415 
1416     /**
1417      * @ngdoc method
1418      * @name $injector#annotate
1419      *
1420      * @description
1421      * Returns an array of service names which the function is requesting for injection. This API is
1422      * used by the injector to determine which services need to be injected into the function when the
1423      * function is invoked. There are three ways in which the function can be annotated with the needed
1424      * dependencies.
1425      *
1426      * # Argument names
1427      *
1428      * The simplest form is to extract the dependencies from the arguments of the function. This is done
1429      * by converting the function into a string using `toString()` method and extracting the argument
1430      * names.
1431      * ```js
1432      *   // Given
1433      *   function MyController($scope, $route) {
1434  *     // ...
1435  *   }
1436      *
1437      *   // Then
1438      *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
1439      * ```
1440      *
1441      * This method does not work with code minification / obfuscation. For this reason the following
1442      * annotation strategies are supported.
1443      *
1444      * # The `$inject` property
1445      *
1446      * If a function has an `$inject` property and its value is an array of strings, then the strings
1447      * represent names of services to be injected into the function.
1448      * ```js
1449      *   // Given
1450      *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
1451  *     // ...
1452  *   }
1453      *   // Define function dependencies
1454      *   MyController['$inject'] = ['$scope', '$route'];
1455      *
1456      *   // Then
1457      *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
1458      * ```
1459      *
1460      * # The array notation
1461      *
1462      * It is often desirable to inline Injected functions and that's when setting the `$inject` property
1463      * is very inconvenient. In these situations using the array notation to specify the dependencies in
1464      * a way that survives minification is a better choice:
1465      *
1466      * ```js
1467      *   // We wish to write this (not minification / obfuscation safe)
1468      *   injector.invoke(function($compile, $rootScope) {
1469  *     // ...
1470  *   });
1471      *
1472      *   // We are forced to write break inlining
1473      *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
1474  *     // ...
1475  *   };
1476      *   tmpFn.$inject = ['$compile', '$rootScope'];
1477      *   injector.invoke(tmpFn);
1478      *
1479      *   // To better support inline function the inline annotation is supported
1480      *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
1481  *     // ...
1482  *   }]);
1483      *
1484      *   // Therefore
1485      *   expect(injector.annotate(
1486      *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
1487      *    ).toEqual(['$compile', '$rootScope']);
1488      * ```
1489      *
1490      * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
1491      * be retrieved as described above.
1492      *
1493      * @returns {Array.<string>} The names of the services which the function requires.
1494      */
1495 
1496 
1497 
1498 
1499     /**
1500      * @ngdoc service
1501      * @name $provide
1502      *
1503      * @description
1504      *
1505      * The {@link auto.$provide $provide} service has a number of methods for registering components
1506      * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
1507      * {@link angular.Module}.
1508      *
1509      * An Angular **service** is a singleton object created by a **service factory**.  These **service
1510      * Angular服务是一个单例的对象经过factory建立
1511      * factories** are functions which, in turn, are created by a **service provider**.
1512      * 这个factory是一个函数,反过来讲则经过服务的提供者建立
1513      * The **service providers** are constructor functions. When instantiated they must contain a
1514      * 服务提供者是一个构造函数,当须要被实例化的时候,它们则必须包含一个$get的属性
1515      * property called `$get`, which holds the **service factory** function.
1516      *包含了服务工厂函数
1517      * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
1518      * correct **service provider**, instantiating it and then calling its `$get` **service factory**
1519      * function to get the instance of the **service**.
1520      *当你请求一个服务时,注入器会去查找服务提供者,实例化而且调用它的$get服务工厂获取上面请求的实例。
1521      * Often services have no configuration options and there is no need to add methods to the service
1522      * 其余的服务没有配置项而且不须要给服务提供者添加多余的方法
1523      * provider.  The provider will be no more than a constructor function with a `$get` property. For
1524      * 提供者不只仅是一个包含$get属性的构造函数,它还包括其余的内容
1525      * these cases the {@link auto.$provide $provide} service has additional helper methods to register
1526      * services without specifying a provider.
1527      *
1528      * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
1529      *     {@link auto.$injector $injector}注入器
1530      * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
1531      *     providers and services.常量,注入一个键值对,val/objec
1532      * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
1533      *     services, not providers.
1534      * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
1535      *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
1536      *     given factory function.
1537      * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
1538      *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
1539      *      a new object using the given constructor function.
1540      *
1541      * See the individual methods for more information and examples.
1542      */
1543 
1544     /**
1545      * @ngdoc method
1546      * @name $provide#provider
1547      * @description
1548      *
1549      * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
1550      * are constructor functions, whose instances are responsible for "providing" a factory for a
1551      * service.
1552      *
1553      * Service provider names start with the name of the service they provide followed by `Provider`.
1554      * For example, the {@link ng.$log $log} service has a provider called
1555      * {@link ng.$logProvider $logProvider}.
1556      *
1557      * Service provider objects can have additional methods which allow configuration of the provider
1558      * and its service. Importantly, you can configure what kind of service is created by the `$get`
1559      * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
1560      * method {@link ng.$logProvider#debugEnabled debugEnabled}
1561      * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
1562      * console or not.
1563      *
1564      * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
1565      'Provider'` key.
1566      * @param {(Object|function())} provider If the provider is:
1567      *
1568      *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
1569      *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
1570      *   - `Constructor`: a new instance of the provider will be created using
1571      *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
1572      *
1573      * @returns {Object} registered provider instance
1574 
1575      * @example
1576      *
1577      * The following example shows how to create a simple event tracking service and register it using
1578      * {@link auto.$provide#provider $provide.provider()}.
1579      *
1580      * ```js
1581      *  // Define the eventTracker provider
1582      *  function EventTrackerProvider() {
1583  *    var trackingUrl = '/track';
1584  *
1585  *    // A provider method for configuring where the tracked events should been saved
1586  *    this.setTrackingUrl = function(url) {
1587  *      trackingUrl = url;
1588  *    };
1589  *
1590  *    // The service factory function
1591  *    this.$get = ['$http', function($http) {
1592  *      var trackedEvents = {};
1593  *      return {
1594  *        // Call this to track an event
1595  *        event: function(event) {
1596  *          var count = trackedEvents[event] || 0;
1597  *          count += 1;
1598  *          trackedEvents[event] = count;
1599  *          return count;
1600  *        },
1601  *        // Call this to save the tracked events to the trackingUrl
1602  *        save: function() {
1603  *          $http.post(trackingUrl, trackedEvents);
1604  *        }
1605  *      };
1606  *    }];
1607  *  }
1608      *
1609      *  describe('eventTracker', function() {
1610  *    var postSpy;
1611  *
1612  *    beforeEach(module(function($provide) {
1613  *      // Register the eventTracker provider
1614  *      $provide.provider('eventTracker', EventTrackerProvider);
1615  *    }));
1616  *
1617  *    beforeEach(module(function(eventTrackerProvider) {
1618  *      // Configure eventTracker provider
1619  *      eventTrackerProvider.setTrackingUrl('/custom-track');
1620  *    }));
1621  *
1622  *    it('tracks events', inject(function(eventTracker) {
1623  *      expect(eventTracker.event('login')).toEqual(1);
1624  *      expect(eventTracker.event('login')).toEqual(2);
1625  *    }));
1626  *
1627  *    it('saves to the tracking url', inject(function(eventTracker, $http) {
1628  *      postSpy = spyOn($http, 'post');
1629  *      eventTracker.event('login');
1630  *      eventTracker.save();
1631  *      expect(postSpy).toHaveBeenCalled();
1632  *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
1633  *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
1634  *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
1635  *    }));
1636  *  });
1637      * ```
1638      */
1639 
1640     /**
1641      * @ngdoc method
1642      * @name $provide#factory
1643      * @description
1644      *
1645      * Register a **service factory**, which will be called to return the service instance.
1646      * This is short for registering a service where its provider consists of only a `$get` property,
1647      * which is the given service factory function.
1648      * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
1649      * configure your service in a provider.
1650      *
1651      * @param {string} name The name of the instance.
1652      * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand
1653      *                            for `$provide.provider(name, {$get: $getFn})`.
1654      * @returns {Object} registered provider instance
1655      *
1656      * @example
1657      * Here is an example of registering a service
1658      * ```js
1659      *   $provide.factory('ping', ['$http', function($http) {
1660  *     return function ping() {
1661  *       return $http.send('/ping');
1662  *     };
1663  *   }]);
1664      * ```
1665      * You would then inject and use this service like this:
1666      * ```js
1667      *   someModule.controller('Ctrl', ['ping', function(ping) {
1668  *     ping();
1669  *   }]);
1670      * ```
1671      */
1672 
1673 
1674     /**
1675      * @ngdoc method
1676      * @name $provide#service
1677      * @description
1678      *
1679      * Register a **service constructor**, which will be invoked with `new` to create the service
1680      * instance.
1681      * This is short for registering a service where its provider's `$get` property is the service
1682      * constructor function that will be used to instantiate the service instance.
1683      *
1684      * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
1685      * as a type/class.
1686      *
1687      * @param {string} name The name of the instance.
1688      * @param {Function} constructor A class (constructor function) that will be instantiated.
1689      * @returns {Object} registered provider instance
1690      *
1691      * @example
1692      * Here is an example of registering a service using
1693      * {@link auto.$provide#service $provide.service(class)}.
1694      * ```js
1695      *   var Ping = function($http) {
1696  *     this.$http = $http;
1697  *   };
1698      *
1699      *   Ping.$inject = ['$http'];
1700      *
1701      *   Ping.prototype.send = function() {
1702  *     return this.$http.get('/ping');
1703  *   };
1704      *   $provide.service('ping', Ping);
1705      * ```
1706      * You would then inject and use this service like this:
1707      * ```js
1708      *   someModule.controller('Ctrl', ['ping', function(ping) {
1709  *     ping.send();
1710  *   }]);
1711      * ```
1712      */
1713 
1714 
1715     /**
1716      * @ngdoc method
1717      * @name $provide#value
1718      * @description
1719      *
1720      * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
1721      * number, an array, an object or a function.  This is short for registering a service where its
1722      * provider's `$get` property is a factory function that takes no arguments and returns the **value
1723      * service**.
1724      *
1725      * Value services are similar to constant services, except that they cannot be injected into a
1726      * module configuration function (see {@link angular.Module#config}) but they can be overridden by
1727      * an Angular
1728      * {@link auto.$provide#decorator decorator}.
1729      *
1730      * @param {string} name The name of the instance.
1731      * @param {*} value The value.
1732      * @returns {Object} registered provider instance
1733      *
1734      * @example
1735      * Here are some examples of creating value services.
1736      * ```js
1737      *   $provide.value('ADMIN_USER', 'admin');
1738      *
1739      *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
1740      *
1741      *   $provide.value('halfOf', function(value) {
1742  *     return value / 2;
1743  *   });
1744      * ```
1745      */
1746 
1747 
1748     /**
1749      * @ngdoc method
1750      * @name $provide#constant
1751      * @description
1752      *
1753      * Register a **constant service**, such as a string, a number, an array, an object or a function,
1754      * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
1755      * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
1756      * be overridden by an Angular {@link auto.$provide#decorator decorator}.
1757      *
1758      * @param {string} name The name of the constant.
1759      * @param {*} value The constant value.
1760      * @returns {Object} registered instance
1761      *
1762      * @example
1763      * Here a some examples of creating constants:
1764      * ```js
1765      *   $provide.constant('SHARD_HEIGHT', 306);
1766      *
1767      *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
1768      *
1769      *   $provide.constant('double', function(value) {
1770  *     return value * 2;
1771  *   });
1772      * ```
1773      */
1774 
1775 
1776     /**
1777      * @ngdoc method
1778      * @name $provide#decorator
1779      * @description
1780      *
1781      * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
1782      * intercepts the creation of a service, allowing it to override or modify the behaviour of the
1783      * service. The object returned by the decorator may be the original service, or a new service
1784      * object which replaces or wraps and delegates to the original service.
1785      *
1786      * @param {string} name The name of the service to decorate.
1787      * @param {function()} decorator This function will be invoked when the service needs to be
1788      *    instantiated and should return the decorated service instance. The function is called using
1789      *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
1790      *    Local injection arguments:
1791      *
1792      *    * `$delegate` - The original service instance, which can be monkey patched, configured,
1793      *      decorated or delegated to.
1794      *
1795      * @example
1796      * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
1797      * calls to {@link ng.$log#error $log.warn()}.
1798      * ```js
1799      *   $provide.decorator('$log', ['$delegate', function($delegate) {
1800  *     $delegate.warn = $delegate.error;
1801  *     return $delegate;
1802  *   }]);
1803      * ```
1804      */
1805 
1806 
1807     function createInjector(modulesToLoad, strictDi) {
1808         strictDi = (strictDi === true);
1809         var INSTANTIATING = {},
1810             providerSuffix = 'Provider',
1811             path = [],
1812             loadedModules = new HashMap([], true),
1813             providerCache = {
1814                 $provide: {
1815                     provider: supportObject(provider),
1816                     factory: supportObject(factory),
1817                     service: supportObject(service),
1818                     value: supportObject(value),
1819                     constant: supportObject(constant),
1820                     decorator: decorator
1821                 }
1822             },
1823             providerInjector = (providerCache.$injector =
1824                 createInternalInjector(providerCache, function() {
1825                     throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
1826                 }, strictDi)),
1827             instanceCache = {},
1828             instanceInjector = (instanceCache.$injector =
1829                 createInternalInjector(instanceCache, function(servicename) {
1830                     var provider = providerInjector.get(servicename + providerSuffix);
1831                     return instanceInjector.invoke(provider.$get, provider, undefined, servicename);
1832                 }, strictDi));
1833 
1834 
1835         forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });
1836 
1837         return instanceInjector;
1838 
1839         ////////////////////////////////////
1840         // $provider
1841         ////////////////////////////////////
1842 
1843         function supportObject(delegate) {
1844             return function(key, value) {
1845                 if (isObject(key)) {
1846                     forEach(key, reverseParams(delegate));
1847                 } else {
1848                     return delegate(key, value);
1849                 }
1850             };
1851         }
1852 
1853         function provider(name, provider_) {
1854             assertNotHasOwnProperty(name, 'service');
1855             if (isFunction(provider_) || isArray(provider_)) {
1856                 provider_ = providerInjector.instantiate(provider_);
1857             }
1858             if (!provider_.$get) {
1859                 throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
1860             }
1861             return providerCache[name + providerSuffix] = provider_;
1862         }
1863 
1864         function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); }
1865 
1866         function service(name, constructor) {
1867             return factory(name, ['$injector', function($injector) {
1868                 return $injector.instantiate(constructor);
1869             }]);
1870         }
1871 
1872         function value(name, val) { return factory(name, valueFn(val)); }
1873 
1874         function constant(name, value) {
1875             assertNotHasOwnProperty(name, 'constant');
1876             providerCache[name] = value;
1877             instanceCache[name] = value;
1878         }
1879 
1880         function decorator(serviceName, decorFn) {
1881             var origProvider = providerInjector.get(serviceName + providerSuffix),
1882                 orig$get = origProvider.$get;
1883 
1884             origProvider.$get = function() {
1885                 var origInstance = instanceInjector.invoke(orig$get, origProvider);
1886                 return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
1887             };
1888         }
1889 
1890         ////////////////////////////////////
1891         // Module Loading
1892         ////////////////////////////////////
1893         function loadModules(modulesToLoad){
1894             var runBlocks = [], moduleFn, invokeQueue;
1895             forEach(modulesToLoad, function(module) {
1896                 if (loadedModules.get(module)) return;
1897                 loadedModules.put(module, true);
1898 
1899                 function runInvokeQueue(queue) {
1900                     var i, ii;
1901                     for(i = 0, ii = queue.length; i < ii; i++) {
1902                         var invokeArgs = queue[i],
1903                             provider = providerInjector.get(invokeArgs[0]);
1904 
1905                         provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
1906                     }
1907                 }
1908 
1909                 try {
1910                     if (isString(module)) {
1911                         moduleFn = angularModule(module);
1912                         runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
1913                         runInvokeQueue(moduleFn._invokeQueue);
1914                         runInvokeQueue(moduleFn._configBlocks);
1915                     } else if (isFunction(module)) {
1916                         runBlocks.push(providerInjector.invoke(module));
1917                     } else if (isArray(module)) {
1918                         runBlocks.push(providerInjector.invoke(module));
1919                     } else {
1920                         assertArgFn(module, 'module');
1921                     }
1922                 } catch (e) {
1923                     if (isArray(module)) {
1924                         module = module[module.length - 1];
1925                     }
1926                     if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
1927                         // Safari & FF's stack traces don't contain error.message content
1928                         // unlike those of Chrome and IE
1929                         // So if stack doesn't contain message, we create a new string that contains both.
1930                         // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
1931                         /* jshint -W022 */
1932                         e = e.message + '\n' + e.stack;
1933                     }
1934                     throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
1935                         module, e.stack || e.message || e);
1936                 }
1937             });
1938             return runBlocks;
1939         }
1940 
1941         ////////////////////////////////////
1942         // internal Injector
1943         ////////////////////////////////////
1944 
1945         function createInternalInjector(cache, factory) {
1946 
1947             function getService(serviceName) {
1948                 if (cache.hasOwnProperty(serviceName)) {
1949                     if (cache[serviceName] === INSTANTIATING) {
1950                         throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
1951                             serviceName + ' <- ' + path.join(' <- '));
1952                     }
1953                     return cache[serviceName];
1954                 } else {
1955                     try {
1956                         path.unshift(serviceName);
1957                         cache[serviceName] = INSTANTIATING;
1958                         return cache[serviceName] = factory(serviceName);
1959                     } catch (err) {
1960                         if (cache[serviceName] === INSTANTIATING) {
1961                             delete cache[serviceName];
1962                         }
1963                         throw err;
1964                     } finally {
1965                         path.shift();
1966                     }
1967                 }
1968             }
1969 
1970             function invoke(fn, self, locals, serviceName){
1971                 if (typeof locals === 'string') {
1972                     serviceName = locals;
1973                     locals = null;
1974                 }
1975 
1976                 var args = [],
1977                     $inject = annotate(fn, strictDi, serviceName),
1978                     length, i,
1979                     key;
1980 
1981                 for(i = 0, length = $inject.length; i < length; i++) {
1982                     key = $inject[i];
1983                     if (typeof key !== 'string') {
1984                         throw $injectorMinErr('itkn',
1985                             'Incorrect injection token! Expected service name as string, got {0}', key);
1986                     }
1987                     args.push(
1988                         locals && locals.hasOwnProperty(key)
1989                             ? locals[key]
1990                             : getService(key)
1991                     );
1992                 }
1993                 if (isArray(fn)) {
1994                     fn = fn[length];
1995                 }
1996 
1997                 // http://jsperf.com/angularjs-invoke-apply-vs-switch
1998                 // #5388
1999                 return fn.apply(self, args);
2000             }
2001 
2002             function instantiate(Type, locals, serviceName) {
2003                 var Constructor = function() {},
2004                     instance, returnedValue;
2005 
2006                 // Check if Type is annotated and use just the given function at n-1 as parameter
2007                 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
2008                 Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
2009                 instance = new Constructor();
2010                 returnedValue = invoke(Type, instance, locals, serviceName);
2011 
2012                 return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
2013             }
2014 
2015             return {
2016                 invoke: invoke,
2017                 instantiate: instantiate,
2018                 get: getService,
2019                 annotate: annotate,
2020                 has: function(name) {
2021                     return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
2022                 }
2023             };
2024         }
2025     }
2026 
2027     createInjector.$$annotate = annotate;
2028 
2029     /**
2030      * @ngdoc service
2031      * @name $anchorScroll
2032      * @kind function
2033      * @requires $window
2034      * @requires $location
2035      * @requires $rootScope
2036      *
2037      * @description
2038      * When called, it checks current value of `$location.hash()` and scrolls to the related element,
2039      * according to rules specified in
2040      * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
2041      *
2042      * It also watches the `$location.hash()` and scrolls whenever it changes to match any anchor.
2043      * This can be disabled by calling `$anchorScrollProvider.disableAutoScrolling()`.
2044      *
2045      * @example
2046      <example module="anchorScrollExample">
2047      <file name="index.html">
2048      <div id="scrollArea" ng-controller="ScrollController">
2049      <a ng-click="gotoBottom()">Go to bottom</a>
2050      <a id="bottom"></a> You're at the bottom!
2051      </div>
2052      </file>
2053      <file name="script.js">
2054      angular.module('anchorScrollExample', [])
2055      .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
2056      function ($scope, $location, $anchorScroll) {
2057              $scope.gotoBottom = function() {
2058                // set the location.hash to the id of
2059                // the element you wish to scroll to.
2060                $location.hash('bottom');
2061 
2062                // call $anchorScroll()
2063                $anchorScroll();
2064              };
2065            }]);
2066      </file>
2067      <file name="style.css">
2068      #scrollArea {
2069          height: 350px;
2070          overflow: auto;
2071        }
2072 
2073      #bottom {
2074          display: block;
2075          margin-top: 2000px;
2076        }
2077      </file>
2078      </example>
2079      */
2080     function $AnchorScrollProvider() {
2081 
2082         var autoScrollingEnabled = true;
2083 
2084         this.disableAutoScrolling = function() {
2085             autoScrollingEnabled = false;
2086         };
2087 
2088         this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
2089             var document = $window.document;
2090 
2091             // helper function to get first anchor from a NodeList
2092             // can't use filter.filter, as it accepts only instances of Array
2093             // and IE can't convert NodeList to an array using [].slice
2094             // TODO(vojta): use filter if we change it to accept lists as well
2095             function getFirstAnchor(list) {
2096                 var result = null;
2097                 forEach(list, function(element) {
2098                     if (!result && nodeName_(element) === 'a') result = element;
2099                 });
2100                 return result;
2101             }
2102 
2103             function scroll() {
2104                 var hash = $location.hash(), elm;
2105 
2106                 // empty hash, scroll to the top of the page
2107                 if (!hash) $window.scrollTo(0, 0);
2108 
2109                 // element with given id
2110                 else if ((elm = document.getElementById(hash))) elm.scrollIntoView();
2111 
2112                 // first anchor with given name :-D
2113                 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) elm.scrollIntoView();
2114 
2115                 // no element and hash == 'top', scroll to the top of the page
2116                 else if (hash === 'top') $window.scrollTo(0, 0);
2117             }
2118 
2119             // does not scroll when user clicks on anchor link that is currently on
2120             // (no url change, no $location.hash() change), browser native does scroll
2121             if (autoScrollingEnabled) {
2122                 $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
2123                     function autoScrollWatchAction() {
2124                         $rootScope.$evalAsync(scroll);
2125                     });
2126             }
2127 
2128             return scroll;
2129         }];
2130     }
2131 
2132     var $animateMinErr = minErr('$animate');
2133 
2134     /**
2135      * @ngdoc provider
2136      * @name $animateProvider
2137      *
2138      * @description
2139      * Default implementation of $animate that doesn't perform any animations, instead just
2140      * synchronously performs DOM
2141      * updates and calls done() callbacks.
2142      *
2143      * In order to enable animations the ngAnimate module has to be loaded.
2144      *
2145      * To see the functional implementation check out src/ngAnimate/animate.js
2146      */
2147     var $AnimateProvider = ['$provide', function($provide) {
2148 
2149 
2150         this.$$selectors = {};
2151 
2152 
2153         /**
2154          * @ngdoc method
2155          * @name $animateProvider#register
2156          *
2157          * @description
2158          * Registers a new injectable animation factory function. The factory function produces the
2159          * animation object which contains callback functions for each event that is expected to be
2160          * animated.
2161          *
2162          *   * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction`
2163          *   must be called once the element animation is complete. If a function is returned then the
2164          *   animation service will use this function to cancel the animation whenever a cancel event is
2165          *   triggered.
2166          *
2167          *
2168          * ```js
2169          *   return {
2170      *     eventFn : function(element, done) {
2171      *       //code to run the animation
2172      *       //once complete, then run done()
2173      *       return function cancellationFunction() {
2174      *         //code to cancel the animation
2175      *       }
2176      *     }
2177      *   }
2178          * ```
2179          *
2180          * @param {string} name The name of the animation.
2181          * @param {Function} factory The factory function that will be executed to return the animation
2182          *                           object.
2183          */
2184         this.register = function(name, factory) {
2185             var key = name + '-animation';
2186             if (name && name.charAt(0) != '.') throw $animateMinErr('notcsel',
2187                 "Expecting class selector starting with '.' got '{0}'.", name);
2188             this.$$selectors[name.substr(1)] = key;
2189             $provide.factory(key, factory);
2190         };
2191 
2192         /**
2193          * @ngdoc method
2194          * @name $animateProvider#classNameFilter
2195          *
2196          * @description
2197          * Sets and/or returns the CSS class regular expression that is checked when performing
2198          * an animation. Upon bootstrap the classNameFilter value is not set at all and will
2199          * therefore enable $animate to attempt to perform an animation on any element.
2200          * When setting the classNameFilter value, animations will only be performed on elements
2201          * that successfully match the filter expression. This in turn can boost performance
2202          * for low-powered devices as well as applications containing a lot of structural operations.
2203          * @param {RegExp=} expression The className expression which will be checked against all animations
2204          * @return {RegExp} The current CSS className expression value. If null then there is no expression value
2205          */
2206         this.classNameFilter = function(expression) {
2207             if(arguments.length === 1) {
2208                 this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
2209             }
2210             return this.$$classNameFilter;
2211         };
2212 
2213         this.$get = ['$timeout', '$$asyncCallback', function($timeout, $$asyncCallback) {
2214 
2215             function async(fn) {
2216                 fn && $$asyncCallback(fn);
2217             }
2218 
2219             /**
2220              *
2221              * @ngdoc service
2222              * @name $animate
2223              * @description The $animate service provides rudimentary DOM manipulation functions to
2224              * insert, remove and move elements within the DOM, as well as adding and removing classes.
2225              * This service is the core service used by the ngAnimate $animator service which provides
2226              * high-level animation hooks for CSS and JavaScript.
2227              *
2228              * $animate is available in the AngularJS core, however, the ngAnimate module must be included
2229              * to enable full out animation support. Otherwise, $animate will only perform simple DOM
2230              * manipulation operations.
2231              *
2232              * To learn more about enabling animation support, click here to visit the {@link ngAnimate
2233      * ngAnimate module page} as well as the {@link ngAnimate.$animate ngAnimate $animate service
2234      * page}.
2235              */
2236             return {
2237 
2238                 /**
2239                  *
2240                  * @ngdoc method
2241                  * @name $animate#enter
2242                  * @kind function
2243                  * @description Inserts the element into the DOM either after the `after` element or
2244                  * as the first child within the `parent` element. Once complete, the done() callback
2245                  * will be fired (if provided).
2246                  * @param {DOMElement} element the element which will be inserted into the DOM
2247                  * @param {DOMElement} parent the parent element which will append the element as
2248                  *   a child (if the after element is not present)
2249                  * @param {DOMElement} after the sibling element which will append the element
2250                  *   after itself
2251                  * @param {Function=} done callback function that will be called after the element has been
2252                  *   inserted into the DOM
2253                  */
2254                 enter : function(element, parent, after, done) {
2255                     after
2256                         ? after.after(element)
2257                         : parent.prepend(element);
2258                     async(done);
2259                     return noop;
2260                 },
2261 
2262                 /**
2263                  *
2264                  * @ngdoc method
2265                  * @name $animate#leave
2266                  * @kind function
2267                  * @description Removes the element from the DOM. Once complete, the done() callback will be
2268                  *   fired (if provided).
2269                  * @param {DOMElement} element the element which will be removed from the DOM
2270                  * @param {Function=} done callback function that will be called after the element has been
2271                  *   removed from the DOM
2272                  */
2273                 leave : function(element, done) {
2274                     element.remove();
2275                     async(done);
2276                     return noop;
2277                 },
2278 
2279                 /**
2280                  *
2281                  * @ngdoc method
2282                  * @name $animate#move
2283                  * @kind function
2284                  * @description Moves the position of the provided element within the DOM to be placed
2285                  * either after the `after` element or inside of the `parent` element. Once complete, the
2286                  * done() callback will be fired (if provided).
2287                  *
2288                  * @param {DOMElement} element the element which will be moved around within the
2289                  *   DOM
2290                  * @param {DOMElement} parent the parent element where the element will be
2291                  *   inserted into (if the after element is not present)
2292                  * @param {DOMElement} after the sibling element where the element will be
2293                  *   positioned next to
2294                  * @param {Function=} done the callback function (if provided) that will be fired after the
2295                  *   element has been moved to its new position
2296                  */
2297                 move : function(element, parent, after, done) {
2298                     // Do not remove element before insert. Removing will cause data associated with the
2299                     // element to be dropped. Insert will implicitly do the remove.
2300                     return this.enter(element, parent, after, done);
2301                 },
2302 
2303                 /**
2304                  *
2305                  * @ngdoc method
2306                  * @name $animate#addClass
2307                  * @kind function
2308                  * @description Adds the provided className CSS class value to the provided element. Once
2309                  * complete, the done() callback will be fired (if provided).
2310                  * @param {DOMElement} element the element which will have the className value
2311                  *   added to it
2312                  * @param {string} className the CSS class which will be added to the element
2313                  * @param {Function=} done the callback function (if provided) that will be fired after the
2314                  *   className value has been added to the element
2315                  */
2316                 addClass : function(element, className, done) {
2317                     className = !isString(className)
2318                         ? (isArray(className) ? className.join(' ') : '')
2319                         : className;
2320                     forEach(element, function (element) {
2321                         jqLiteAddClass(element, className);
2322                     });
2323                     async(done);
2324                     return noop;
2325                 },
2326 
2327                 /**
2328                  *
2329                  * @ngdoc method
2330                  * @name $animate#removeClass
2331                  * @kind function
2332                  * @description Removes the provided className CSS class value from the provided element.
2333                  * Once complete, the done() callback will be fired (if provided).
2334                  * @param {DOMElement} element the element which will have the className value
2335                  *   removed from it
2336                  * @param {string} className the CSS class which will be removed from the element
2337                  * @param {Function=} done the callback function (if provided) that will be fired after the
2338                  *   className value has been removed from the element
2339                  */
2340                 removeClass : function(element, className, done) {
2341                     className = isString(className) ?
2342                         className :
2343                         isArray(className) ? className.join(' ') : '';
2344                     forEach(element, function (element) {
2345                         jqLiteRemoveClass(element, className);
2346                     });
2347                     async(done);
2348                     return noop;
2349                 },
2350 
2351                 /**
2352                  *
2353                  * @ngdoc method
2354                  * @name $animate#setClass
2355                  * @kind function
2356                  * @description Adds and/or removes the given CSS classes to and from the element.
2357                  * Once complete, the done() callback will be fired (if provided).
2358                  * @param {DOMElement} element the element which will have its CSS classes changed
2359                  *   removed from it
2360                  * @param {string} add the CSS classes which will be added to the element
2361                  * @param {string} remove the CSS class which will be removed from the element
2362                  * @param {Function=} done the callback function (if provided) that will be fired after the
2363                  *   CSS classes have been set on the element
2364                  */
2365                 setClass : function(element, add, remove, done) {
2366                     forEach(element, function (element) {
2367                         jqLiteAddClass(element, add);
2368                         jqLiteRemoveClass(element, remove);
2369                     });
2370                     async(done);
2371                     return noop;
2372                 },
2373 
2374                 enabled : noop
2375             };
2376         }];
2377     }];
2378 
2379     function $$AsyncCallbackProvider(){
2380         this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) {
2381             return $$rAF.supported
2382                 ? function(fn) { return $$rAF(fn); }
2383                 : function(fn) {
2384                 return $timeout(fn, 0, false);
2385             };
2386         }];
2387     }
2388 
2389     /**
2390      * ! This is a private undocumented service !
2391      *
2392      * @name $browser
2393      * @requires $log
2394      * @description
2395      * This object has two goals:
2396      *
2397      * - hide all the global state in the browser caused by the window object
2398      * - abstract away all the browser specific features and inconsistencies
2399      *
2400      * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
2401      * service, which can be used for convenient testing of the application without the interaction with
2402      * the real browser apis.
2403      */
2404     /**
2405      * @param {object} window The global window object.
2406      * @param {object} document jQuery wrapped document.
2407      * @param {function()} XHR XMLHttpRequest constructor.
2408      * @param {object} $log console.log or an object with the same interface.
2409      * @param {object} $sniffer $sniffer service
2410      */
2411     function Browser(window, document, $log, $sniffer) {
2412         var self = this,
2413             rawDocument = document[0],
2414             location = window.location,
2415             history = window.history,
2416             setTimeout = window.setTimeout,
2417             clearTimeout = window.clearTimeout,
2418             pendingDeferIds = {};
2419 
2420         self.isMock = false;
2421 
2422         var outstandingRequestCount = 0;
2423         var outstandingRequestCallbacks = [];
2424 
2425         // TODO(vojta): remove this temporary api
2426         self.$$completeOutstandingRequest = completeOutstandingRequest;
2427         self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
2428 
2429         /**
2430          * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
2431          * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
2432          */
2433         function completeOutstandingRequest(fn) {
2434             try {
2435                 fn.apply(null, sliceArgs(arguments, 1));
2436             } finally {
2437                 outstandingRequestCount--;
2438                 if (outstandingRequestCount === 0) {
2439                     while(outstandingRequestCallbacks.length) {
2440                         try {
2441                             outstandingRequestCallbacks.pop()();
2442                         } catch (e) {
2443                             $log.error(e);
2444                         }
2445                     }
2446                 }
2447             }
2448         }
2449 
2450         /**
2451          * @private
2452          * Note: this method is used only by scenario runner
2453          * TODO(vojta): prefix this method with $$ ?
2454          * @param {function()} callback Function that will be called when no outstanding request
2455          */
2456         self.notifyWhenNoOutstandingRequests = function(callback) {
2457             // force browser to execute all pollFns - this is needed so that cookies and other pollers fire
2458             // at some deterministic time in respect to the test runner's actions. Leaving things up to the
2459             // regular poller would result in flaky tests.
2460             forEach(pollFns, function(pollFn){ pollFn(); });
2461 
2462             if (outstandingRequestCount === 0) {
2463                 callback();
2464             } else {
2465                 outstandingRequestCallbacks.push(callback);
2466             }
2467         };
查看代码