src/p4l/static/p4l/lib/angular/angular.js
changeset 166 0f99a7fff851
equal deleted inserted replaced
165:64aae3fee9d5 166:0f99a7fff851
       
     1 /**
       
     2  * @license AngularJS v1.2.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  *
       
    14  * var exampleMinErr = minErr('example');
       
    15  * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
       
    16  *
       
    17  * The above creates an instance of minErr in the example namespace. The
       
    18  * resulting error will have a namespaced error code of example.one.  The
       
    19  * resulting error will replace {0} with the value of foo, and {1} with the
       
    20  * value of bar. The object is not restricted in the number of arguments it can
       
    21  * take.
       
    22  *
       
    23  * If fewer arguments are specified than necessary for interpolation, the extra
       
    24  * interpolation markers will be preserved in the final string.
       
    25  *
       
    26  * Since data will be parsed statically during a build step, some restrictions
       
    27  * are applied with respect to how minErr instances are created and called.
       
    28  * Instances should have names of the form namespaceMinErr for a minErr created
       
    29  * using minErr('namespace') . Error codes, namespaces and template strings
       
    30  * should all be static strings, not variables or general expressions.
       
    31  *
       
    32  * @param {string} module The namespace to use for the new minErr instance.
       
    33  * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
       
    34  */
       
    35 
       
    36 function minErr(module) {
       
    37   return function () {
       
    38     var code = arguments[0],
       
    39       prefix = '[' + (module ? module + ':' : '') + code + '] ',
       
    40       template = arguments[1],
       
    41       templateArgs = arguments,
       
    42       stringify = function (obj) {
       
    43         if (typeof obj === 'function') {
       
    44           return obj.toString().replace(/ \{[\s\S]*$/, '');
       
    45         } else if (typeof obj === 'undefined') {
       
    46           return 'undefined';
       
    47         } else if (typeof obj !== 'string') {
       
    48           return JSON.stringify(obj);
       
    49         }
       
    50         return obj;
       
    51       },
       
    52       message, i;
       
    53 
       
    54     message = prefix + template.replace(/\{\d+\}/g, function (match) {
       
    55       var index = +match.slice(1, -1), arg;
       
    56 
       
    57       if (index + 2 < templateArgs.length) {
       
    58         arg = templateArgs[index + 2];
       
    59         if (typeof arg === 'function') {
       
    60           return arg.toString().replace(/ ?\{[\s\S]*$/, '');
       
    61         } else if (typeof arg === 'undefined') {
       
    62           return 'undefined';
       
    63         } else if (typeof arg !== 'string') {
       
    64           return toJson(arg);
       
    65         }
       
    66         return arg;
       
    67       }
       
    68       return match;
       
    69     });
       
    70 
       
    71     message = message + '\nhttp://errors.angularjs.org/1.2.15/' +
       
    72       (module ? module + '/' : '') + code;
       
    73     for (i = 2; i < arguments.length; i++) {
       
    74       message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
       
    75         encodeURIComponent(stringify(arguments[i]));
       
    76     }
       
    77 
       
    78     return new Error(message);
       
    79   };
       
    80 }
       
    81 
       
    82 /* We need to tell jshint what variables are being exported */
       
    83 /* global
       
    84     -angular,
       
    85     -msie,
       
    86     -jqLite,
       
    87     -jQuery,
       
    88     -slice,
       
    89     -push,
       
    90     -toString,
       
    91     -ngMinErr,
       
    92     -_angular,
       
    93     -angularModule,
       
    94     -nodeName_,
       
    95     -uid,
       
    96 
       
    97     -lowercase,
       
    98     -uppercase,
       
    99     -manualLowercase,
       
   100     -manualUppercase,
       
   101     -nodeName_,
       
   102     -isArrayLike,
       
   103     -forEach,
       
   104     -sortedKeys,
       
   105     -forEachSorted,
       
   106     -reverseParams,
       
   107     -nextUid,
       
   108     -setHashKey,
       
   109     -extend,
       
   110     -int,
       
   111     -inherit,
       
   112     -noop,
       
   113     -identity,
       
   114     -valueFn,
       
   115     -isUndefined,
       
   116     -isDefined,
       
   117     -isObject,
       
   118     -isString,
       
   119     -isNumber,
       
   120     -isDate,
       
   121     -isArray,
       
   122     -isFunction,
       
   123     -isRegExp,
       
   124     -isWindow,
       
   125     -isScope,
       
   126     -isFile,
       
   127     -isBlob,
       
   128     -isBoolean,
       
   129     -trim,
       
   130     -isElement,
       
   131     -makeMap,
       
   132     -map,
       
   133     -size,
       
   134     -includes,
       
   135     -indexOf,
       
   136     -arrayRemove,
       
   137     -isLeafNode,
       
   138     -copy,
       
   139     -shallowCopy,
       
   140     -equals,
       
   141     -csp,
       
   142     -concat,
       
   143     -sliceArgs,
       
   144     -bind,
       
   145     -toJsonReplacer,
       
   146     -toJson,
       
   147     -fromJson,
       
   148     -toBoolean,
       
   149     -startingTag,
       
   150     -tryDecodeURIComponent,
       
   151     -parseKeyValue,
       
   152     -toKeyValue,
       
   153     -encodeUriSegment,
       
   154     -encodeUriQuery,
       
   155     -angularInit,
       
   156     -bootstrap,
       
   157     -snake_case,
       
   158     -bindJQuery,
       
   159     -assertArg,
       
   160     -assertArgFn,
       
   161     -assertNotHasOwnProperty,
       
   162     -getter,
       
   163     -getBlockElements,
       
   164     -hasOwnProperty,
       
   165 
       
   166 */
       
   167 
       
   168 ////////////////////////////////////
       
   169 
       
   170 /**
       
   171  * @ngdoc module
       
   172  * @name ng
       
   173  * @module ng
       
   174  * @description
       
   175  *
       
   176  * # ng (core module)
       
   177  * The ng module is loaded by default when an AngularJS application is started. The module itself
       
   178  * contains the essential components for an AngularJS application to function. The table below
       
   179  * lists a high level breakdown of each of the services/factories, filters, directives and testing
       
   180  * components available within this core module.
       
   181  *
       
   182  * <div doc-module-components="ng"></div>
       
   183  */
       
   184 
       
   185 /**
       
   186  * @ngdoc function
       
   187  * @name angular.lowercase
       
   188  * @module ng
       
   189  * @function
       
   190  *
       
   191  * @description Converts the specified string to lowercase.
       
   192  * @param {string} string String to be converted to lowercase.
       
   193  * @returns {string} Lowercased string.
       
   194  */
       
   195 var lowercase = function(string){return isString(string) ? string.toLowerCase() : string;};
       
   196 var hasOwnProperty = Object.prototype.hasOwnProperty;
       
   197 
       
   198 /**
       
   199  * @ngdoc function
       
   200  * @name angular.uppercase
       
   201  * @module ng
       
   202  * @function
       
   203  *
       
   204  * @description Converts the specified string to uppercase.
       
   205  * @param {string} string String to be converted to uppercase.
       
   206  * @returns {string} Uppercased string.
       
   207  */
       
   208 var uppercase = function(string){return isString(string) ? string.toUpperCase() : string;};
       
   209 
       
   210 
       
   211 var manualLowercase = function(s) {
       
   212   /* jshint bitwise: false */
       
   213   return isString(s)
       
   214       ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
       
   215       : s;
       
   216 };
       
   217 var manualUppercase = function(s) {
       
   218   /* jshint bitwise: false */
       
   219   return isString(s)
       
   220       ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
       
   221       : s;
       
   222 };
       
   223 
       
   224 
       
   225 // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
       
   226 // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
       
   227 // with correct but slower alternatives.
       
   228 if ('i' !== 'I'.toLowerCase()) {
       
   229   lowercase = manualLowercase;
       
   230   uppercase = manualUppercase;
       
   231 }
       
   232 
       
   233 
       
   234 var /** holds major version number for IE or NaN for real browsers */
       
   235     msie,
       
   236     jqLite,           // delay binding since jQuery could be loaded after us.
       
   237     jQuery,           // delay binding
       
   238     slice             = [].slice,
       
   239     push              = [].push,
       
   240     toString          = Object.prototype.toString,
       
   241     ngMinErr          = minErr('ng'),
       
   242 
       
   243 
       
   244     _angular          = window.angular,
       
   245     /** @name angular */
       
   246     angular           = window.angular || (window.angular = {}),
       
   247     angularModule,
       
   248     nodeName_,
       
   249     uid               = ['0', '0', '0'];
       
   250 
       
   251 /**
       
   252  * IE 11 changed the format of the UserAgent string.
       
   253  * See http://msdn.microsoft.com/en-us/library/ms537503.aspx
       
   254  */
       
   255 msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]);
       
   256 if (isNaN(msie)) {
       
   257   msie = int((/trident\/.*; rv:(\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]);
       
   258 }
       
   259 
       
   260 
       
   261 /**
       
   262  * @private
       
   263  * @param {*} obj
       
   264  * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
       
   265  *                   String ...)
       
   266  */
       
   267 function isArrayLike(obj) {
       
   268   if (obj == null || isWindow(obj)) {
       
   269     return false;
       
   270   }
       
   271 
       
   272   var length = obj.length;
       
   273 
       
   274   if (obj.nodeType === 1 && length) {
       
   275     return true;
       
   276   }
       
   277 
       
   278   return isString(obj) || isArray(obj) || length === 0 ||
       
   279          typeof length === 'number' && length > 0 && (length - 1) in obj;
       
   280 }
       
   281 
       
   282 /**
       
   283  * @ngdoc function
       
   284  * @name angular.forEach
       
   285  * @module ng
       
   286  * @function
       
   287  *
       
   288  * @description
       
   289  * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
       
   290  * object or an array. The `iterator` function is invoked with `iterator(value, key)`, where `value`
       
   291  * is the value of an object property or an array element and `key` is the object property key or
       
   292  * array element index. Specifying a `context` for the function is optional.
       
   293  *
       
   294  * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
       
   295  * using the `hasOwnProperty` method.
       
   296  *
       
   297    ```js
       
   298      var values = {name: 'misko', gender: 'male'};
       
   299      var log = [];
       
   300      angular.forEach(values, function(value, key){
       
   301        this.push(key + ': ' + value);
       
   302      }, log);
       
   303      expect(log).toEqual(['name: misko', 'gender: male']);
       
   304    ```
       
   305  *
       
   306  * @param {Object|Array} obj Object to iterate over.
       
   307  * @param {Function} iterator Iterator function.
       
   308  * @param {Object=} context Object to become context (`this`) for the iterator function.
       
   309  * @returns {Object|Array} Reference to `obj`.
       
   310  */
       
   311 function forEach(obj, iterator, context) {
       
   312   var key;
       
   313   if (obj) {
       
   314     if (isFunction(obj)){
       
   315       for (key in obj) {
       
   316         // Need to check if hasOwnProperty exists,
       
   317         // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
       
   318         if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
       
   319           iterator.call(context, obj[key], key);
       
   320         }
       
   321       }
       
   322     } else if (obj.forEach && obj.forEach !== forEach) {
       
   323       obj.forEach(iterator, context);
       
   324     } else if (isArrayLike(obj)) {
       
   325       for (key = 0; key < obj.length; key++)
       
   326         iterator.call(context, obj[key], key);
       
   327     } else {
       
   328       for (key in obj) {
       
   329         if (obj.hasOwnProperty(key)) {
       
   330           iterator.call(context, obj[key], key);
       
   331         }
       
   332       }
       
   333     }
       
   334   }
       
   335   return obj;
       
   336 }
       
   337 
       
   338 function sortedKeys(obj) {
       
   339   var keys = [];
       
   340   for (var key in obj) {
       
   341     if (obj.hasOwnProperty(key)) {
       
   342       keys.push(key);
       
   343     }
       
   344   }
       
   345   return keys.sort();
       
   346 }
       
   347 
       
   348 function forEachSorted(obj, iterator, context) {
       
   349   var keys = sortedKeys(obj);
       
   350   for ( var i = 0; i < keys.length; i++) {
       
   351     iterator.call(context, obj[keys[i]], keys[i]);
       
   352   }
       
   353   return keys;
       
   354 }
       
   355 
       
   356 
       
   357 /**
       
   358  * when using forEach the params are value, key, but it is often useful to have key, value.
       
   359  * @param {function(string, *)} iteratorFn
       
   360  * @returns {function(*, string)}
       
   361  */
       
   362 function reverseParams(iteratorFn) {
       
   363   return function(value, key) { iteratorFn(key, value); };
       
   364 }
       
   365 
       
   366 /**
       
   367  * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
       
   368  * characters such as '012ABC'. The reason why we are not using simply a number counter is that
       
   369  * the number string gets longer over time, and it can also overflow, where as the nextId
       
   370  * will grow much slower, it is a string, and it will never overflow.
       
   371  *
       
   372  * @returns {string} an unique alpha-numeric string
       
   373  */
       
   374 function nextUid() {
       
   375   var index = uid.length;
       
   376   var digit;
       
   377 
       
   378   while(index) {
       
   379     index--;
       
   380     digit = uid[index].charCodeAt(0);
       
   381     if (digit == 57 /*'9'*/) {
       
   382       uid[index] = 'A';
       
   383       return uid.join('');
       
   384     }
       
   385     if (digit == 90  /*'Z'*/) {
       
   386       uid[index] = '0';
       
   387     } else {
       
   388       uid[index] = String.fromCharCode(digit + 1);
       
   389       return uid.join('');
       
   390     }
       
   391   }
       
   392   uid.unshift('0');
       
   393   return uid.join('');
       
   394 }
       
   395 
       
   396 
       
   397 /**
       
   398  * Set or clear the hashkey for an object.
       
   399  * @param obj object
       
   400  * @param h the hashkey (!truthy to delete the hashkey)
       
   401  */
       
   402 function setHashKey(obj, h) {
       
   403   if (h) {
       
   404     obj.$$hashKey = h;
       
   405   }
       
   406   else {
       
   407     delete obj.$$hashKey;
       
   408   }
       
   409 }
       
   410 
       
   411 /**
       
   412  * @ngdoc function
       
   413  * @name angular.extend
       
   414  * @module ng
       
   415  * @function
       
   416  *
       
   417  * @description
       
   418  * Extends the destination object `dst` by copying all of the properties from the `src` object(s)
       
   419  * to `dst`. You can specify multiple `src` objects.
       
   420  *
       
   421  * @param {Object} dst Destination object.
       
   422  * @param {...Object} src Source object(s).
       
   423  * @returns {Object} Reference to `dst`.
       
   424  */
       
   425 function extend(dst) {
       
   426   var h = dst.$$hashKey;
       
   427   forEach(arguments, function(obj){
       
   428     if (obj !== dst) {
       
   429       forEach(obj, function(value, key){
       
   430         dst[key] = value;
       
   431       });
       
   432     }
       
   433   });
       
   434 
       
   435   setHashKey(dst,h);
       
   436   return dst;
       
   437 }
       
   438 
       
   439 function int(str) {
       
   440   return parseInt(str, 10);
       
   441 }
       
   442 
       
   443 
       
   444 function inherit(parent, extra) {
       
   445   return extend(new (extend(function() {}, {prototype:parent}))(), extra);
       
   446 }
       
   447 
       
   448 /**
       
   449  * @ngdoc function
       
   450  * @name angular.noop
       
   451  * @module ng
       
   452  * @function
       
   453  *
       
   454  * @description
       
   455  * A function that performs no operations. This function can be useful when writing code in the
       
   456  * functional style.
       
   457    ```js
       
   458      function foo(callback) {
       
   459        var result = calculateResult();
       
   460        (callback || angular.noop)(result);
       
   461      }
       
   462    ```
       
   463  */
       
   464 function noop() {}
       
   465 noop.$inject = [];
       
   466 
       
   467 
       
   468 /**
       
   469  * @ngdoc function
       
   470  * @name angular.identity
       
   471  * @module ng
       
   472  * @function
       
   473  *
       
   474  * @description
       
   475  * A function that returns its first argument. This function is useful when writing code in the
       
   476  * functional style.
       
   477  *
       
   478    ```js
       
   479      function transformer(transformationFn, value) {
       
   480        return (transformationFn || angular.identity)(value);
       
   481      };
       
   482    ```
       
   483  */
       
   484 function identity($) {return $;}
       
   485 identity.$inject = [];
       
   486 
       
   487 
       
   488 function valueFn(value) {return function() {return value;};}
       
   489 
       
   490 /**
       
   491  * @ngdoc function
       
   492  * @name angular.isUndefined
       
   493  * @module ng
       
   494  * @function
       
   495  *
       
   496  * @description
       
   497  * Determines if a reference is undefined.
       
   498  *
       
   499  * @param {*} value Reference to check.
       
   500  * @returns {boolean} True if `value` is undefined.
       
   501  */
       
   502 function isUndefined(value){return typeof value === 'undefined';}
       
   503 
       
   504 
       
   505 /**
       
   506  * @ngdoc function
       
   507  * @name angular.isDefined
       
   508  * @module ng
       
   509  * @function
       
   510  *
       
   511  * @description
       
   512  * Determines if a reference is defined.
       
   513  *
       
   514  * @param {*} value Reference to check.
       
   515  * @returns {boolean} True if `value` is defined.
       
   516  */
       
   517 function isDefined(value){return typeof value !== 'undefined';}
       
   518 
       
   519 
       
   520 /**
       
   521  * @ngdoc function
       
   522  * @name angular.isObject
       
   523  * @module ng
       
   524  * @function
       
   525  *
       
   526  * @description
       
   527  * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
       
   528  * considered to be objects. Note that JavaScript arrays are objects.
       
   529  *
       
   530  * @param {*} value Reference to check.
       
   531  * @returns {boolean} True if `value` is an `Object` but not `null`.
       
   532  */
       
   533 function isObject(value){return value != null && typeof value === 'object';}
       
   534 
       
   535 
       
   536 /**
       
   537  * @ngdoc function
       
   538  * @name angular.isString
       
   539  * @module ng
       
   540  * @function
       
   541  *
       
   542  * @description
       
   543  * Determines if a reference is a `String`.
       
   544  *
       
   545  * @param {*} value Reference to check.
       
   546  * @returns {boolean} True if `value` is a `String`.
       
   547  */
       
   548 function isString(value){return typeof value === 'string';}
       
   549 
       
   550 
       
   551 /**
       
   552  * @ngdoc function
       
   553  * @name angular.isNumber
       
   554  * @module ng
       
   555  * @function
       
   556  *
       
   557  * @description
       
   558  * Determines if a reference is a `Number`.
       
   559  *
       
   560  * @param {*} value Reference to check.
       
   561  * @returns {boolean} True if `value` is a `Number`.
       
   562  */
       
   563 function isNumber(value){return typeof value === 'number';}
       
   564 
       
   565 
       
   566 /**
       
   567  * @ngdoc function
       
   568  * @name angular.isDate
       
   569  * @module ng
       
   570  * @function
       
   571  *
       
   572  * @description
       
   573  * Determines if a value is a date.
       
   574  *
       
   575  * @param {*} value Reference to check.
       
   576  * @returns {boolean} True if `value` is a `Date`.
       
   577  */
       
   578 function isDate(value){
       
   579   return toString.call(value) === '[object Date]';
       
   580 }
       
   581 
       
   582 
       
   583 /**
       
   584  * @ngdoc function
       
   585  * @name angular.isArray
       
   586  * @module ng
       
   587  * @function
       
   588  *
       
   589  * @description
       
   590  * Determines if a reference is an `Array`.
       
   591  *
       
   592  * @param {*} value Reference to check.
       
   593  * @returns {boolean} True if `value` is an `Array`.
       
   594  */
       
   595 function isArray(value) {
       
   596   return toString.call(value) === '[object Array]';
       
   597 }
       
   598 
       
   599 
       
   600 /**
       
   601  * @ngdoc function
       
   602  * @name angular.isFunction
       
   603  * @module ng
       
   604  * @function
       
   605  *
       
   606  * @description
       
   607  * Determines if a reference is a `Function`.
       
   608  *
       
   609  * @param {*} value Reference to check.
       
   610  * @returns {boolean} True if `value` is a `Function`.
       
   611  */
       
   612 function isFunction(value){return typeof value === 'function';}
       
   613 
       
   614 
       
   615 /**
       
   616  * Determines if a value is a regular expression object.
       
   617  *
       
   618  * @private
       
   619  * @param {*} value Reference to check.
       
   620  * @returns {boolean} True if `value` is a `RegExp`.
       
   621  */
       
   622 function isRegExp(value) {
       
   623   return toString.call(value) === '[object RegExp]';
       
   624 }
       
   625 
       
   626 
       
   627 /**
       
   628  * Checks if `obj` is a window object.
       
   629  *
       
   630  * @private
       
   631  * @param {*} obj Object to check
       
   632  * @returns {boolean} True if `obj` is a window obj.
       
   633  */
       
   634 function isWindow(obj) {
       
   635   return obj && obj.document && obj.location && obj.alert && obj.setInterval;
       
   636 }
       
   637 
       
   638 
       
   639 function isScope(obj) {
       
   640   return obj && obj.$evalAsync && obj.$watch;
       
   641 }
       
   642 
       
   643 
       
   644 function isFile(obj) {
       
   645   return toString.call(obj) === '[object File]';
       
   646 }
       
   647 
       
   648 
       
   649 function isBlob(obj) {
       
   650   return toString.call(obj) === '[object Blob]';
       
   651 }
       
   652 
       
   653 
       
   654 function isBoolean(value) {
       
   655   return typeof value === 'boolean';
       
   656 }
       
   657 
       
   658 
       
   659 var trim = (function() {
       
   660   // native trim is way faster: http://jsperf.com/angular-trim-test
       
   661   // but IE doesn't have it... :-(
       
   662   // TODO: we should move this into IE/ES5 polyfill
       
   663   if (!String.prototype.trim) {
       
   664     return function(value) {
       
   665       return isString(value) ? value.replace(/^\s\s*/, '').replace(/\s\s*$/, '') : value;
       
   666     };
       
   667   }
       
   668   return function(value) {
       
   669     return isString(value) ? value.trim() : value;
       
   670   };
       
   671 })();
       
   672 
       
   673 
       
   674 /**
       
   675  * @ngdoc function
       
   676  * @name angular.isElement
       
   677  * @module ng
       
   678  * @function
       
   679  *
       
   680  * @description
       
   681  * Determines if a reference is a DOM element (or wrapped jQuery element).
       
   682  *
       
   683  * @param {*} value Reference to check.
       
   684  * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
       
   685  */
       
   686 function isElement(node) {
       
   687   return !!(node &&
       
   688     (node.nodeName  // we are a direct element
       
   689     || (node.prop && node.attr && node.find)));  // we have an on and find method part of jQuery API
       
   690 }
       
   691 
       
   692 /**
       
   693  * @param str 'key1,key2,...'
       
   694  * @returns {object} in the form of {key1:true, key2:true, ...}
       
   695  */
       
   696 function makeMap(str){
       
   697   var obj = {}, items = str.split(","), i;
       
   698   for ( i = 0; i < items.length; i++ )
       
   699     obj[ items[i] ] = true;
       
   700   return obj;
       
   701 }
       
   702 
       
   703 
       
   704 if (msie < 9) {
       
   705   nodeName_ = function(element) {
       
   706     element = element.nodeName ? element : element[0];
       
   707     return (element.scopeName && element.scopeName != 'HTML')
       
   708       ? uppercase(element.scopeName + ':' + element.nodeName) : element.nodeName;
       
   709   };
       
   710 } else {
       
   711   nodeName_ = function(element) {
       
   712     return element.nodeName ? element.nodeName : element[0].nodeName;
       
   713   };
       
   714 }
       
   715 
       
   716 
       
   717 function map(obj, iterator, context) {
       
   718   var results = [];
       
   719   forEach(obj, function(value, index, list) {
       
   720     results.push(iterator.call(context, value, index, list));
       
   721   });
       
   722   return results;
       
   723 }
       
   724 
       
   725 
       
   726 /**
       
   727  * @description
       
   728  * Determines the number of elements in an array, the number of properties an object has, or
       
   729  * the length of a string.
       
   730  *
       
   731  * Note: This function is used to augment the Object type in Angular expressions. See
       
   732  * {@link angular.Object} for more information about Angular arrays.
       
   733  *
       
   734  * @param {Object|Array|string} obj Object, array, or string to inspect.
       
   735  * @param {boolean} [ownPropsOnly=false] Count only "own" properties in an object
       
   736  * @returns {number} The size of `obj` or `0` if `obj` is neither an object nor an array.
       
   737  */
       
   738 function size(obj, ownPropsOnly) {
       
   739   var count = 0, key;
       
   740 
       
   741   if (isArray(obj) || isString(obj)) {
       
   742     return obj.length;
       
   743   } else if (isObject(obj)){
       
   744     for (key in obj)
       
   745       if (!ownPropsOnly || obj.hasOwnProperty(key))
       
   746         count++;
       
   747   }
       
   748 
       
   749   return count;
       
   750 }
       
   751 
       
   752 
       
   753 function includes(array, obj) {
       
   754   return indexOf(array, obj) != -1;
       
   755 }
       
   756 
       
   757 function indexOf(array, obj) {
       
   758   if (array.indexOf) return array.indexOf(obj);
       
   759 
       
   760   for (var i = 0; i < array.length; i++) {
       
   761     if (obj === array[i]) return i;
       
   762   }
       
   763   return -1;
       
   764 }
       
   765 
       
   766 function arrayRemove(array, value) {
       
   767   var index = indexOf(array, value);
       
   768   if (index >=0)
       
   769     array.splice(index, 1);
       
   770   return value;
       
   771 }
       
   772 
       
   773 function isLeafNode (node) {
       
   774   if (node) {
       
   775     switch (node.nodeName) {
       
   776     case "OPTION":
       
   777     case "PRE":
       
   778     case "TITLE":
       
   779       return true;
       
   780     }
       
   781   }
       
   782   return false;
       
   783 }
       
   784 
       
   785 /**
       
   786  * @ngdoc function
       
   787  * @name angular.copy
       
   788  * @module ng
       
   789  * @function
       
   790  *
       
   791  * @description
       
   792  * Creates a deep copy of `source`, which should be an object or an array.
       
   793  *
       
   794  * * If no destination is supplied, a copy of the object or array is created.
       
   795  * * If a destination is provided, all of its elements (for array) or properties (for objects)
       
   796  *   are deleted and then all elements/properties from the source are copied to it.
       
   797  * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
       
   798  * * If `source` is identical to 'destination' an exception will be thrown.
       
   799  *
       
   800  * @param {*} source The source that will be used to make a copy.
       
   801  *                   Can be any type, including primitives, `null`, and `undefined`.
       
   802  * @param {(Object|Array)=} destination Destination into which the source is copied. If
       
   803  *     provided, must be of the same type as `source`.
       
   804  * @returns {*} The copy or updated `destination`, if `destination` was specified.
       
   805  *
       
   806  * @example
       
   807  <example>
       
   808  <file name="index.html">
       
   809  <div ng-controller="Controller">
       
   810  <form novalidate class="simple-form">
       
   811  Name: <input type="text" ng-model="user.name" /><br />
       
   812  E-mail: <input type="email" ng-model="user.email" /><br />
       
   813  Gender: <input type="radio" ng-model="user.gender" value="male" />male
       
   814  <input type="radio" ng-model="user.gender" value="female" />female<br />
       
   815  <button ng-click="reset()">RESET</button>
       
   816  <button ng-click="update(user)">SAVE</button>
       
   817  </form>
       
   818  <pre>form = {{user | json}}</pre>
       
   819  <pre>master = {{master | json}}</pre>
       
   820  </div>
       
   821 
       
   822  <script>
       
   823  function Controller($scope) {
       
   824     $scope.master= {};
       
   825 
       
   826     $scope.update = function(user) {
       
   827       // Example with 1 argument
       
   828       $scope.master= angular.copy(user);
       
   829     };
       
   830 
       
   831     $scope.reset = function() {
       
   832       // Example with 2 arguments
       
   833       angular.copy($scope.master, $scope.user);
       
   834     };
       
   835 
       
   836     $scope.reset();
       
   837   }
       
   838  </script>
       
   839  </file>
       
   840  </example>
       
   841  */
       
   842 function copy(source, destination){
       
   843   if (isWindow(source) || isScope(source)) {
       
   844     throw ngMinErr('cpws',
       
   845       "Can't copy! Making copies of Window or Scope instances is not supported.");
       
   846   }
       
   847 
       
   848   if (!destination) {
       
   849     destination = source;
       
   850     if (source) {
       
   851       if (isArray(source)) {
       
   852         destination = copy(source, []);
       
   853       } else if (isDate(source)) {
       
   854         destination = new Date(source.getTime());
       
   855       } else if (isRegExp(source)) {
       
   856         destination = new RegExp(source.source);
       
   857       } else if (isObject(source)) {
       
   858         destination = copy(source, {});
       
   859       }
       
   860     }
       
   861   } else {
       
   862     if (source === destination) throw ngMinErr('cpi',
       
   863       "Can't copy! Source and destination are identical.");
       
   864     if (isArray(source)) {
       
   865       destination.length = 0;
       
   866       for ( var i = 0; i < source.length; i++) {
       
   867         destination.push(copy(source[i]));
       
   868       }
       
   869     } else {
       
   870       var h = destination.$$hashKey;
       
   871       forEach(destination, function(value, key){
       
   872         delete destination[key];
       
   873       });
       
   874       for ( var key in source) {
       
   875         destination[key] = copy(source[key]);
       
   876       }
       
   877       setHashKey(destination,h);
       
   878     }
       
   879   }
       
   880   return destination;
       
   881 }
       
   882 
       
   883 /**
       
   884  * Create a shallow copy of an object
       
   885  */
       
   886 function shallowCopy(src, dst) {
       
   887   dst = dst || {};
       
   888 
       
   889   for(var key in src) {
       
   890     // shallowCopy is only ever called by $compile nodeLinkFn, which has control over src
       
   891     // so we don't need to worry about using our custom hasOwnProperty here
       
   892     if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
       
   893       dst[key] = src[key];
       
   894     }
       
   895   }
       
   896 
       
   897   return dst;
       
   898 }
       
   899 
       
   900 
       
   901 /**
       
   902  * @ngdoc function
       
   903  * @name angular.equals
       
   904  * @module ng
       
   905  * @function
       
   906  *
       
   907  * @description
       
   908  * Determines if two objects or two values are equivalent. Supports value types, regular
       
   909  * expressions, arrays and objects.
       
   910  *
       
   911  * Two objects or values are considered equivalent if at least one of the following is true:
       
   912  *
       
   913  * * Both objects or values pass `===` comparison.
       
   914  * * Both objects or values are of the same type and all of their properties are equal by
       
   915  *   comparing them with `angular.equals`.
       
   916  * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
       
   917  * * Both values represent the same regular expression (In JavasScript,
       
   918  *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
       
   919  *   representation matches).
       
   920  *
       
   921  * During a property comparison, properties of `function` type and properties with names
       
   922  * that begin with `$` are ignored.
       
   923  *
       
   924  * Scope and DOMWindow objects are being compared only by identify (`===`).
       
   925  *
       
   926  * @param {*} o1 Object or value to compare.
       
   927  * @param {*} o2 Object or value to compare.
       
   928  * @returns {boolean} True if arguments are equal.
       
   929  */
       
   930 function equals(o1, o2) {
       
   931   if (o1 === o2) return true;
       
   932   if (o1 === null || o2 === null) return false;
       
   933   if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
       
   934   var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
       
   935   if (t1 == t2) {
       
   936     if (t1 == 'object') {
       
   937       if (isArray(o1)) {
       
   938         if (!isArray(o2)) return false;
       
   939         if ((length = o1.length) == o2.length) {
       
   940           for(key=0; key<length; key++) {
       
   941             if (!equals(o1[key], o2[key])) return false;
       
   942           }
       
   943           return true;
       
   944         }
       
   945       } else if (isDate(o1)) {
       
   946         return isDate(o2) && o1.getTime() == o2.getTime();
       
   947       } else if (isRegExp(o1) && isRegExp(o2)) {
       
   948         return o1.toString() == o2.toString();
       
   949       } else {
       
   950         if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || isArray(o2)) return false;
       
   951         keySet = {};
       
   952         for(key in o1) {
       
   953           if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
       
   954           if (!equals(o1[key], o2[key])) return false;
       
   955           keySet[key] = true;
       
   956         }
       
   957         for(key in o2) {
       
   958           if (!keySet.hasOwnProperty(key) &&
       
   959               key.charAt(0) !== '$' &&
       
   960               o2[key] !== undefined &&
       
   961               !isFunction(o2[key])) return false;
       
   962         }
       
   963         return true;
       
   964       }
       
   965     }
       
   966   }
       
   967   return false;
       
   968 }
       
   969 
       
   970 
       
   971 function csp() {
       
   972   return (document.securityPolicy && document.securityPolicy.isActive) ||
       
   973       (document.querySelector &&
       
   974       !!(document.querySelector('[ng-csp]') || document.querySelector('[data-ng-csp]')));
       
   975 }
       
   976 
       
   977 
       
   978 function concat(array1, array2, index) {
       
   979   return array1.concat(slice.call(array2, index));
       
   980 }
       
   981 
       
   982 function sliceArgs(args, startIndex) {
       
   983   return slice.call(args, startIndex || 0);
       
   984 }
       
   985 
       
   986 
       
   987 /* jshint -W101 */
       
   988 /**
       
   989  * @ngdoc function
       
   990  * @name angular.bind
       
   991  * @module ng
       
   992  * @function
       
   993  *
       
   994  * @description
       
   995  * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
       
   996  * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
       
   997  * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
       
   998  * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
       
   999  *
       
  1000  * @param {Object} self Context which `fn` should be evaluated in.
       
  1001  * @param {function()} fn Function to be bound.
       
  1002  * @param {...*} args Optional arguments to be prebound to the `fn` function call.
       
  1003  * @returns {function()} Function that wraps the `fn` with all the specified bindings.
       
  1004  */
       
  1005 /* jshint +W101 */
       
  1006 function bind(self, fn) {
       
  1007   var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
       
  1008   if (isFunction(fn) && !(fn instanceof RegExp)) {
       
  1009     return curryArgs.length
       
  1010       ? function() {
       
  1011           return arguments.length
       
  1012             ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0)))
       
  1013             : fn.apply(self, curryArgs);
       
  1014         }
       
  1015       : function() {
       
  1016           return arguments.length
       
  1017             ? fn.apply(self, arguments)
       
  1018             : fn.call(self);
       
  1019         };
       
  1020   } else {
       
  1021     // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
       
  1022     return fn;
       
  1023   }
       
  1024 }
       
  1025 
       
  1026 
       
  1027 function toJsonReplacer(key, value) {
       
  1028   var val = value;
       
  1029 
       
  1030   if (typeof key === 'string' && key.charAt(0) === '$') {
       
  1031     val = undefined;
       
  1032   } else if (isWindow(value)) {
       
  1033     val = '$WINDOW';
       
  1034   } else if (value &&  document === value) {
       
  1035     val = '$DOCUMENT';
       
  1036   } else if (isScope(value)) {
       
  1037     val = '$SCOPE';
       
  1038   }
       
  1039 
       
  1040   return val;
       
  1041 }
       
  1042 
       
  1043 
       
  1044 /**
       
  1045  * @ngdoc function
       
  1046  * @name angular.toJson
       
  1047  * @module ng
       
  1048  * @function
       
  1049  *
       
  1050  * @description
       
  1051  * Serializes input into a JSON-formatted string. Properties with leading $ characters will be
       
  1052  * stripped since angular uses this notation internally.
       
  1053  *
       
  1054  * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
       
  1055  * @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace.
       
  1056  * @returns {string|undefined} JSON-ified string representing `obj`.
       
  1057  */
       
  1058 function toJson(obj, pretty) {
       
  1059   if (typeof obj === 'undefined') return undefined;
       
  1060   return JSON.stringify(obj, toJsonReplacer, pretty ? '  ' : null);
       
  1061 }
       
  1062 
       
  1063 
       
  1064 /**
       
  1065  * @ngdoc function
       
  1066  * @name angular.fromJson
       
  1067  * @module ng
       
  1068  * @function
       
  1069  *
       
  1070  * @description
       
  1071  * Deserializes a JSON string.
       
  1072  *
       
  1073  * @param {string} json JSON string to deserialize.
       
  1074  * @returns {Object|Array|string|number} Deserialized thingy.
       
  1075  */
       
  1076 function fromJson(json) {
       
  1077   return isString(json)
       
  1078       ? JSON.parse(json)
       
  1079       : json;
       
  1080 }
       
  1081 
       
  1082 
       
  1083 function toBoolean(value) {
       
  1084   if (typeof value === 'function') {
       
  1085     value = true;
       
  1086   } else if (value && value.length !== 0) {
       
  1087     var v = lowercase("" + value);
       
  1088     value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
       
  1089   } else {
       
  1090     value = false;
       
  1091   }
       
  1092   return value;
       
  1093 }
       
  1094 
       
  1095 /**
       
  1096  * @returns {string} Returns the string representation of the element.
       
  1097  */
       
  1098 function startingTag(element) {
       
  1099   element = jqLite(element).clone();
       
  1100   try {
       
  1101     // turns out IE does not let you set .html() on elements which
       
  1102     // are not allowed to have children. So we just ignore it.
       
  1103     element.empty();
       
  1104   } catch(e) {}
       
  1105   // As Per DOM Standards
       
  1106   var TEXT_NODE = 3;
       
  1107   var elemHtml = jqLite('<div>').append(element).html();
       
  1108   try {
       
  1109     return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) :
       
  1110         elemHtml.
       
  1111           match(/^(<[^>]+>)/)[1].
       
  1112           replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
       
  1113   } catch(e) {
       
  1114     return lowercase(elemHtml);
       
  1115   }
       
  1116 
       
  1117 }
       
  1118 
       
  1119 
       
  1120 /////////////////////////////////////////////////
       
  1121 
       
  1122 /**
       
  1123  * Tries to decode the URI component without throwing an exception.
       
  1124  *
       
  1125  * @private
       
  1126  * @param str value potential URI component to check.
       
  1127  * @returns {boolean} True if `value` can be decoded
       
  1128  * with the decodeURIComponent function.
       
  1129  */
       
  1130 function tryDecodeURIComponent(value) {
       
  1131   try {
       
  1132     return decodeURIComponent(value);
       
  1133   } catch(e) {
       
  1134     // Ignore any invalid uri component
       
  1135   }
       
  1136 }
       
  1137 
       
  1138 
       
  1139 /**
       
  1140  * Parses an escaped url query string into key-value pairs.
       
  1141  * @returns {Object.<string,boolean|Array>}
       
  1142  */
       
  1143 function parseKeyValue(/**string*/keyValue) {
       
  1144   var obj = {}, key_value, key;
       
  1145   forEach((keyValue || "").split('&'), function(keyValue){
       
  1146     if ( keyValue ) {
       
  1147       key_value = keyValue.split('=');
       
  1148       key = tryDecodeURIComponent(key_value[0]);
       
  1149       if ( isDefined(key) ) {
       
  1150         var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
       
  1151         if (!obj[key]) {
       
  1152           obj[key] = val;
       
  1153         } else if(isArray(obj[key])) {
       
  1154           obj[key].push(val);
       
  1155         } else {
       
  1156           obj[key] = [obj[key],val];
       
  1157         }
       
  1158       }
       
  1159     }
       
  1160   });
       
  1161   return obj;
       
  1162 }
       
  1163 
       
  1164 function toKeyValue(obj) {
       
  1165   var parts = [];
       
  1166   forEach(obj, function(value, key) {
       
  1167     if (isArray(value)) {
       
  1168       forEach(value, function(arrayValue) {
       
  1169         parts.push(encodeUriQuery(key, true) +
       
  1170                    (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
       
  1171       });
       
  1172     } else {
       
  1173     parts.push(encodeUriQuery(key, true) +
       
  1174                (value === true ? '' : '=' + encodeUriQuery(value, true)));
       
  1175     }
       
  1176   });
       
  1177   return parts.length ? parts.join('&') : '';
       
  1178 }
       
  1179 
       
  1180 
       
  1181 /**
       
  1182  * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
       
  1183  * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
       
  1184  * segments:
       
  1185  *    segment       = *pchar
       
  1186  *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
       
  1187  *    pct-encoded   = "%" HEXDIG HEXDIG
       
  1188  *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
       
  1189  *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
       
  1190  *                     / "*" / "+" / "," / ";" / "="
       
  1191  */
       
  1192 function encodeUriSegment(val) {
       
  1193   return encodeUriQuery(val, true).
       
  1194              replace(/%26/gi, '&').
       
  1195              replace(/%3D/gi, '=').
       
  1196              replace(/%2B/gi, '+');
       
  1197 }
       
  1198 
       
  1199 
       
  1200 /**
       
  1201  * This method is intended for encoding *key* or *value* parts of query component. We need a custom
       
  1202  * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
       
  1203  * encoded per http://tools.ietf.org/html/rfc3986:
       
  1204  *    query       = *( pchar / "/" / "?" )
       
  1205  *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
       
  1206  *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
       
  1207  *    pct-encoded   = "%" HEXDIG HEXDIG
       
  1208  *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
       
  1209  *                     / "*" / "+" / "," / ";" / "="
       
  1210  */
       
  1211 function encodeUriQuery(val, pctEncodeSpaces) {
       
  1212   return encodeURIComponent(val).
       
  1213              replace(/%40/gi, '@').
       
  1214              replace(/%3A/gi, ':').
       
  1215              replace(/%24/g, '$').
       
  1216              replace(/%2C/gi, ',').
       
  1217              replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
       
  1218 }
       
  1219 
       
  1220 
       
  1221 /**
       
  1222  * @ngdoc directive
       
  1223  * @name ngApp
       
  1224  * @module ng
       
  1225  *
       
  1226  * @element ANY
       
  1227  * @param {angular.Module} ngApp an optional application
       
  1228  *   {@link angular.module module} name to load.
       
  1229  *
       
  1230  * @description
       
  1231  *
       
  1232  * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
       
  1233  * designates the **root element** of the application and is typically placed near the root element
       
  1234  * of the page - e.g. on the `<body>` or `<html>` tags.
       
  1235  *
       
  1236  * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
       
  1237  * found in the document will be used to define the root element to auto-bootstrap as an
       
  1238  * application. To run multiple applications in an HTML document you must manually bootstrap them using
       
  1239  * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
       
  1240  *
       
  1241  * You can specify an **AngularJS module** to be used as the root module for the application.  This
       
  1242  * module will be loaded into the {@link auto.$injector} when the application is bootstrapped and
       
  1243  * should contain the application code needed or have dependencies on other modules that will
       
  1244  * contain the code. See {@link angular.module} for more information.
       
  1245  *
       
  1246  * In the example below if the `ngApp` directive were not placed on the `html` element then the
       
  1247  * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
       
  1248  * would not be resolved to `3`.
       
  1249  *
       
  1250  * `ngApp` is the easiest, and most common, way to bootstrap an application.
       
  1251  *
       
  1252  <example module="ngAppDemo">
       
  1253    <file name="index.html">
       
  1254    <div ng-controller="ngAppDemoController">
       
  1255      I can add: {{a}} + {{b}} =  {{ a+b }}
       
  1256    </div>
       
  1257    </file>
       
  1258    <file name="script.js">
       
  1259    angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
       
  1260      $scope.a = 1;
       
  1261      $scope.b = 2;
       
  1262    });
       
  1263    </file>
       
  1264  </example>
       
  1265  *
       
  1266  */
       
  1267 function angularInit(element, bootstrap) {
       
  1268   var elements = [element],
       
  1269       appElement,
       
  1270       module,
       
  1271       names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'],
       
  1272       NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;
       
  1273 
       
  1274   function append(element) {
       
  1275     element && elements.push(element);
       
  1276   }
       
  1277 
       
  1278   forEach(names, function(name) {
       
  1279     names[name] = true;
       
  1280     append(document.getElementById(name));
       
  1281     name = name.replace(':', '\\:');
       
  1282     if (element.querySelectorAll) {
       
  1283       forEach(element.querySelectorAll('.' + name), append);
       
  1284       forEach(element.querySelectorAll('.' + name + '\\:'), append);
       
  1285       forEach(element.querySelectorAll('[' + name + ']'), append);
       
  1286     }
       
  1287   });
       
  1288 
       
  1289   forEach(elements, function(element) {
       
  1290     if (!appElement) {
       
  1291       var className = ' ' + element.className + ' ';
       
  1292       var match = NG_APP_CLASS_REGEXP.exec(className);
       
  1293       if (match) {
       
  1294         appElement = element;
       
  1295         module = (match[2] || '').replace(/\s+/g, ',');
       
  1296       } else {
       
  1297         forEach(element.attributes, function(attr) {
       
  1298           if (!appElement && names[attr.name]) {
       
  1299             appElement = element;
       
  1300             module = attr.value;
       
  1301           }
       
  1302         });
       
  1303       }
       
  1304     }
       
  1305   });
       
  1306   if (appElement) {
       
  1307     bootstrap(appElement, module ? [module] : []);
       
  1308   }
       
  1309 }
       
  1310 
       
  1311 /**
       
  1312  * @ngdoc function
       
  1313  * @name angular.bootstrap
       
  1314  * @module ng
       
  1315  * @description
       
  1316  * Use this function to manually start up angular application.
       
  1317  *
       
  1318  * See: {@link guide/bootstrap Bootstrap}
       
  1319  *
       
  1320  * Note that ngScenario-based end-to-end tests cannot use this function to bootstrap manually.
       
  1321  * They must use {@link ng.directive:ngApp ngApp}.
       
  1322  *
       
  1323  * Angular will detect if it has been loaded into the browser more than once and only allow the
       
  1324  * first loaded script to be bootstrapped and will report a warning to the browser console for
       
  1325  * each of the subsequent scripts.   This prevents strange results in applications, where otherwise
       
  1326  * multiple instances of Angular try to work on the DOM.
       
  1327  *
       
  1328  * <example name="multi-bootstrap" module="multi-bootstrap">
       
  1329  * <file name="index.html">
       
  1330  * <script src="../../../angular.js"></script>
       
  1331  * <div ng-controller="BrokenTable">
       
  1332  *   <table>
       
  1333  *   <tr>
       
  1334  *     <th ng-repeat="heading in headings">{{heading}}</th>
       
  1335  *   </tr>
       
  1336  *   <tr ng-repeat="filling in fillings">
       
  1337  *     <td ng-repeat="fill in filling">{{fill}}</td>
       
  1338  *   </tr>
       
  1339  * </table>
       
  1340  * </div>
       
  1341  * </file>
       
  1342  * <file name="controller.js">
       
  1343  * var app = angular.module('multi-bootstrap', [])
       
  1344  *
       
  1345  * .controller('BrokenTable', function($scope) {
       
  1346  *     $scope.headings = ['One', 'Two', 'Three'];
       
  1347  *     $scope.fillings = [[1, 2, 3], ['A', 'B', 'C'], [7, 8, 9]];
       
  1348  * });
       
  1349  * </file>
       
  1350  * <file name="protractor.js" type="protractor">
       
  1351  * it('should only insert one table cell for each item in $scope.fillings', function() {
       
  1352  *  expect(element.all(by.css('td')).count())
       
  1353  *      .toBe(9);
       
  1354  * });
       
  1355  * </file>
       
  1356  * </example>
       
  1357  *
       
  1358  * @param {Element} element DOM element which is the root of angular application.
       
  1359  * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
       
  1360  *     Each item in the array should be the name of a predefined module or a (DI annotated)
       
  1361  *     function that will be invoked by the injector as a run block.
       
  1362  *     See: {@link angular.module modules}
       
  1363  * @returns {auto.$injector} Returns the newly created injector for this app.
       
  1364  */
       
  1365 function bootstrap(element, modules) {
       
  1366   var doBootstrap = function() {
       
  1367     element = jqLite(element);
       
  1368 
       
  1369     if (element.injector()) {
       
  1370       var tag = (element[0] === document) ? 'document' : startingTag(element);
       
  1371       throw ngMinErr('btstrpd', "App Already Bootstrapped with this Element '{0}'", tag);
       
  1372     }
       
  1373 
       
  1374     modules = modules || [];
       
  1375     modules.unshift(['$provide', function($provide) {
       
  1376       $provide.value('$rootElement', element);
       
  1377     }]);
       
  1378     modules.unshift('ng');
       
  1379     var injector = createInjector(modules);
       
  1380     injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animate',
       
  1381        function(scope, element, compile, injector, animate) {
       
  1382         scope.$apply(function() {
       
  1383           element.data('$injector', injector);
       
  1384           compile(element)(scope);
       
  1385         });
       
  1386       }]
       
  1387     );
       
  1388     return injector;
       
  1389   };
       
  1390 
       
  1391   var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
       
  1392 
       
  1393   if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
       
  1394     return doBootstrap();
       
  1395   }
       
  1396 
       
  1397   window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
       
  1398   angular.resumeBootstrap = function(extraModules) {
       
  1399     forEach(extraModules, function(module) {
       
  1400       modules.push(module);
       
  1401     });
       
  1402     doBootstrap();
       
  1403   };
       
  1404 }
       
  1405 
       
  1406 var SNAKE_CASE_REGEXP = /[A-Z]/g;
       
  1407 function snake_case(name, separator){
       
  1408   separator = separator || '_';
       
  1409   return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
       
  1410     return (pos ? separator : '') + letter.toLowerCase();
       
  1411   });
       
  1412 }
       
  1413 
       
  1414 function bindJQuery() {
       
  1415   // bind to jQuery if present;
       
  1416   jQuery = window.jQuery;
       
  1417   // reset to jQuery or default to us.
       
  1418   if (jQuery) {
       
  1419     jqLite = jQuery;
       
  1420     extend(jQuery.fn, {
       
  1421       scope: JQLitePrototype.scope,
       
  1422       isolateScope: JQLitePrototype.isolateScope,
       
  1423       controller: JQLitePrototype.controller,
       
  1424       injector: JQLitePrototype.injector,
       
  1425       inheritedData: JQLitePrototype.inheritedData
       
  1426     });
       
  1427     // Method signature:
       
  1428     //     jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments)
       
  1429     jqLitePatchJQueryRemove('remove', true, true, false);
       
  1430     jqLitePatchJQueryRemove('empty', false, false, false);
       
  1431     jqLitePatchJQueryRemove('html', false, false, true);
       
  1432   } else {
       
  1433     jqLite = JQLite;
       
  1434   }
       
  1435   angular.element = jqLite;
       
  1436 }
       
  1437 
       
  1438 /**
       
  1439  * throw error if the argument is falsy.
       
  1440  */
       
  1441 function assertArg(arg, name, reason) {
       
  1442   if (!arg) {
       
  1443     throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
       
  1444   }
       
  1445   return arg;
       
  1446 }
       
  1447 
       
  1448 function assertArgFn(arg, name, acceptArrayAnnotation) {
       
  1449   if (acceptArrayAnnotation && isArray(arg)) {
       
  1450       arg = arg[arg.length - 1];
       
  1451   }
       
  1452 
       
  1453   assertArg(isFunction(arg), name, 'not a function, got ' +
       
  1454       (arg && typeof arg == 'object' ? arg.constructor.name || 'Object' : typeof arg));
       
  1455   return arg;
       
  1456 }
       
  1457 
       
  1458 /**
       
  1459  * throw error if the name given is hasOwnProperty
       
  1460  * @param  {String} name    the name to test
       
  1461  * @param  {String} context the context in which the name is used, such as module or directive
       
  1462  */
       
  1463 function assertNotHasOwnProperty(name, context) {
       
  1464   if (name === 'hasOwnProperty') {
       
  1465     throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
       
  1466   }
       
  1467 }
       
  1468 
       
  1469 /**
       
  1470  * Return the value accessible from the object by path. Any undefined traversals are ignored
       
  1471  * @param {Object} obj starting object
       
  1472  * @param {String} path path to traverse
       
  1473  * @param {boolean} [bindFnToScope=true]
       
  1474  * @returns {Object} value as accessible by path
       
  1475  */
       
  1476 //TODO(misko): this function needs to be removed
       
  1477 function getter(obj, path, bindFnToScope) {
       
  1478   if (!path) return obj;
       
  1479   var keys = path.split('.');
       
  1480   var key;
       
  1481   var lastInstance = obj;
       
  1482   var len = keys.length;
       
  1483 
       
  1484   for (var i = 0; i < len; i++) {
       
  1485     key = keys[i];
       
  1486     if (obj) {
       
  1487       obj = (lastInstance = obj)[key];
       
  1488     }
       
  1489   }
       
  1490   if (!bindFnToScope && isFunction(obj)) {
       
  1491     return bind(lastInstance, obj);
       
  1492   }
       
  1493   return obj;
       
  1494 }
       
  1495 
       
  1496 /**
       
  1497  * Return the DOM siblings between the first and last node in the given array.
       
  1498  * @param {Array} array like object
       
  1499  * @returns {DOMElement} object containing the elements
       
  1500  */
       
  1501 function getBlockElements(nodes) {
       
  1502   var startNode = nodes[0],
       
  1503       endNode = nodes[nodes.length - 1];
       
  1504   if (startNode === endNode) {
       
  1505     return jqLite(startNode);
       
  1506   }
       
  1507 
       
  1508   var element = startNode;
       
  1509   var elements = [element];
       
  1510 
       
  1511   do {
       
  1512     element = element.nextSibling;
       
  1513     if (!element) break;
       
  1514     elements.push(element);
       
  1515   } while (element !== endNode);
       
  1516 
       
  1517   return jqLite(elements);
       
  1518 }
       
  1519 
       
  1520 /**
       
  1521  * @ngdoc type
       
  1522  * @name angular.Module
       
  1523  * @module ng
       
  1524  * @description
       
  1525  *
       
  1526  * Interface for configuring angular {@link angular.module modules}.
       
  1527  */
       
  1528 
       
  1529 function setupModuleLoader(window) {
       
  1530 
       
  1531   var $injectorMinErr = minErr('$injector');
       
  1532   var ngMinErr = minErr('ng');
       
  1533 
       
  1534   function ensure(obj, name, factory) {
       
  1535     return obj[name] || (obj[name] = factory());
       
  1536   }
       
  1537 
       
  1538   var angular = ensure(window, 'angular', Object);
       
  1539 
       
  1540   // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
       
  1541   angular.$$minErr = angular.$$minErr || minErr;
       
  1542 
       
  1543   return ensure(angular, 'module', function() {
       
  1544     /** @type {Object.<string, angular.Module>} */
       
  1545     var modules = {};
       
  1546 
       
  1547     /**
       
  1548      * @ngdoc function
       
  1549      * @name angular.module
       
  1550      * @module ng
       
  1551      * @description
       
  1552      *
       
  1553      * The `angular.module` is a global place for creating, registering and retrieving Angular
       
  1554      * modules.
       
  1555      * All modules (angular core or 3rd party) that should be available to an application must be
       
  1556      * registered using this mechanism.
       
  1557      *
       
  1558      * When passed two or more arguments, a new module is created.  If passed only one argument, an
       
  1559      * existing module (the name passed as the first argument to `module`) is retrieved.
       
  1560      *
       
  1561      *
       
  1562      * # Module
       
  1563      *
       
  1564      * A module is a collection of services, directives, filters, and configuration information.
       
  1565      * `angular.module` is used to configure the {@link auto.$injector $injector}.
       
  1566      *
       
  1567      * ```js
       
  1568      * // Create a new module
       
  1569      * var myModule = angular.module('myModule', []);
       
  1570      *
       
  1571      * // register a new service
       
  1572      * myModule.value('appName', 'MyCoolApp');
       
  1573      *
       
  1574      * // configure existing services inside initialization blocks.
       
  1575      * myModule.config(['$locationProvider', function($locationProvider) {
       
  1576      *   // Configure existing providers
       
  1577      *   $locationProvider.hashPrefix('!');
       
  1578      * }]);
       
  1579      * ```
       
  1580      *
       
  1581      * Then you can create an injector and load your modules like this:
       
  1582      *
       
  1583      * ```js
       
  1584      * var injector = angular.injector(['ng', 'myModule'])
       
  1585      * ```
       
  1586      *
       
  1587      * However it's more likely that you'll just use
       
  1588      * {@link ng.directive:ngApp ngApp} or
       
  1589      * {@link angular.bootstrap} to simplify this process for you.
       
  1590      *
       
  1591      * @param {!string} name The name of the module to create or retrieve.
       
  1592      * @param {Array.<string>=} requires If specified then new module is being created. If
       
  1593      *        unspecified then the module is being retrieved for further configuration.
       
  1594      * @param {Function} configFn Optional configuration function for the module. Same as
       
  1595      *        {@link angular.Module#config Module#config()}.
       
  1596      * @returns {module} new module with the {@link angular.Module} api.
       
  1597      */
       
  1598     return function module(name, requires, configFn) {
       
  1599       var assertNotHasOwnProperty = function(name, context) {
       
  1600         if (name === 'hasOwnProperty') {
       
  1601           throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
       
  1602         }
       
  1603       };
       
  1604 
       
  1605       assertNotHasOwnProperty(name, 'module');
       
  1606       if (requires && modules.hasOwnProperty(name)) {
       
  1607         modules[name] = null;
       
  1608       }
       
  1609       return ensure(modules, name, function() {
       
  1610         if (!requires) {
       
  1611           throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
       
  1612              "the module name or forgot to load it. If registering a module ensure that you " +
       
  1613              "specify the dependencies as the second argument.", name);
       
  1614         }
       
  1615 
       
  1616         /** @type {!Array.<Array.<*>>} */
       
  1617         var invokeQueue = [];
       
  1618 
       
  1619         /** @type {!Array.<Function>} */
       
  1620         var runBlocks = [];
       
  1621 
       
  1622         var config = invokeLater('$injector', 'invoke');
       
  1623 
       
  1624         /** @type {angular.Module} */
       
  1625         var moduleInstance = {
       
  1626           // Private state
       
  1627           _invokeQueue: invokeQueue,
       
  1628           _runBlocks: runBlocks,
       
  1629 
       
  1630           /**
       
  1631            * @ngdoc property
       
  1632            * @name angular.Module#requires
       
  1633            * @module ng
       
  1634            * @returns {Array.<string>} List of module names which must be loaded before this module.
       
  1635            * @description
       
  1636            * Holds the list of modules which the injector will load before the current module is
       
  1637            * loaded.
       
  1638            */
       
  1639           requires: requires,
       
  1640 
       
  1641           /**
       
  1642            * @ngdoc property
       
  1643            * @name angular.Module#name
       
  1644            * @module ng
       
  1645            * @returns {string} Name of the module.
       
  1646            * @description
       
  1647            */
       
  1648           name: name,
       
  1649 
       
  1650 
       
  1651           /**
       
  1652            * @ngdoc method
       
  1653            * @name angular.Module#provider
       
  1654            * @module ng
       
  1655            * @param {string} name service name
       
  1656            * @param {Function} providerType Construction function for creating new instance of the
       
  1657            *                                service.
       
  1658            * @description
       
  1659            * See {@link auto.$provide#provider $provide.provider()}.
       
  1660            */
       
  1661           provider: invokeLater('$provide', 'provider'),
       
  1662 
       
  1663           /**
       
  1664            * @ngdoc method
       
  1665            * @name angular.Module#factory
       
  1666            * @module ng
       
  1667            * @param {string} name service name
       
  1668            * @param {Function} providerFunction Function for creating new instance of the service.
       
  1669            * @description
       
  1670            * See {@link auto.$provide#factory $provide.factory()}.
       
  1671            */
       
  1672           factory: invokeLater('$provide', 'factory'),
       
  1673 
       
  1674           /**
       
  1675            * @ngdoc method
       
  1676            * @name angular.Module#service
       
  1677            * @module ng
       
  1678            * @param {string} name service name
       
  1679            * @param {Function} constructor A constructor function that will be instantiated.
       
  1680            * @description
       
  1681            * See {@link auto.$provide#service $provide.service()}.
       
  1682            */
       
  1683           service: invokeLater('$provide', 'service'),
       
  1684 
       
  1685           /**
       
  1686            * @ngdoc method
       
  1687            * @name angular.Module#value
       
  1688            * @module ng
       
  1689            * @param {string} name service name
       
  1690            * @param {*} object Service instance object.
       
  1691            * @description
       
  1692            * See {@link auto.$provide#value $provide.value()}.
       
  1693            */
       
  1694           value: invokeLater('$provide', 'value'),
       
  1695 
       
  1696           /**
       
  1697            * @ngdoc method
       
  1698            * @name angular.Module#constant
       
  1699            * @module ng
       
  1700            * @param {string} name constant name
       
  1701            * @param {*} object Constant value.
       
  1702            * @description
       
  1703            * Because the constant are fixed, they get applied before other provide methods.
       
  1704            * See {@link auto.$provide#constant $provide.constant()}.
       
  1705            */
       
  1706           constant: invokeLater('$provide', 'constant', 'unshift'),
       
  1707 
       
  1708           /**
       
  1709            * @ngdoc method
       
  1710            * @name angular.Module#animation
       
  1711            * @module ng
       
  1712            * @param {string} name animation name
       
  1713            * @param {Function} animationFactory Factory function for creating new instance of an
       
  1714            *                                    animation.
       
  1715            * @description
       
  1716            *
       
  1717            * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
       
  1718            *
       
  1719            *
       
  1720            * Defines an animation hook that can be later used with
       
  1721            * {@link ngAnimate.$animate $animate} service and directives that use this service.
       
  1722            *
       
  1723            * ```js
       
  1724            * module.animation('.animation-name', function($inject1, $inject2) {
       
  1725            *   return {
       
  1726            *     eventName : function(element, done) {
       
  1727            *       //code to run the animation
       
  1728            *       //once complete, then run done()
       
  1729            *       return function cancellationFunction(element) {
       
  1730            *         //code to cancel the animation
       
  1731            *       }
       
  1732            *     }
       
  1733            *   }
       
  1734            * })
       
  1735            * ```
       
  1736            *
       
  1737            * See {@link ngAnimate.$animateProvider#register $animateProvider.register()} and
       
  1738            * {@link ngAnimate ngAnimate module} for more information.
       
  1739            */
       
  1740           animation: invokeLater('$animateProvider', 'register'),
       
  1741 
       
  1742           /**
       
  1743            * @ngdoc method
       
  1744            * @name angular.Module#filter
       
  1745            * @module ng
       
  1746            * @param {string} name Filter name.
       
  1747            * @param {Function} filterFactory Factory function for creating new instance of filter.
       
  1748            * @description
       
  1749            * See {@link ng.$filterProvider#register $filterProvider.register()}.
       
  1750            */
       
  1751           filter: invokeLater('$filterProvider', 'register'),
       
  1752 
       
  1753           /**
       
  1754            * @ngdoc method
       
  1755            * @name angular.Module#controller
       
  1756            * @module ng
       
  1757            * @param {string|Object} name Controller name, or an object map of controllers where the
       
  1758            *    keys are the names and the values are the constructors.
       
  1759            * @param {Function} constructor Controller constructor function.
       
  1760            * @description
       
  1761            * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
       
  1762            */
       
  1763           controller: invokeLater('$controllerProvider', 'register'),
       
  1764 
       
  1765           /**
       
  1766            * @ngdoc method
       
  1767            * @name angular.Module#directive
       
  1768            * @module ng
       
  1769            * @param {string|Object} name Directive name, or an object map of directives where the
       
  1770            *    keys are the names and the values are the factories.
       
  1771            * @param {Function} directiveFactory Factory function for creating new instance of
       
  1772            * directives.
       
  1773            * @description
       
  1774            * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
       
  1775            */
       
  1776           directive: invokeLater('$compileProvider', 'directive'),
       
  1777 
       
  1778           /**
       
  1779            * @ngdoc method
       
  1780            * @name angular.Module#config
       
  1781            * @module ng
       
  1782            * @param {Function} configFn Execute this function on module load. Useful for service
       
  1783            *    configuration.
       
  1784            * @description
       
  1785            * Use this method to register work which needs to be performed on module loading.
       
  1786            */
       
  1787           config: config,
       
  1788 
       
  1789           /**
       
  1790            * @ngdoc method
       
  1791            * @name angular.Module#run
       
  1792            * @module ng
       
  1793            * @param {Function} initializationFn Execute this function after injector creation.
       
  1794            *    Useful for application initialization.
       
  1795            * @description
       
  1796            * Use this method to register work which should be performed when the injector is done
       
  1797            * loading all modules.
       
  1798            */
       
  1799           run: function(block) {
       
  1800             runBlocks.push(block);
       
  1801             return this;
       
  1802           }
       
  1803         };
       
  1804 
       
  1805         if (configFn) {
       
  1806           config(configFn);
       
  1807         }
       
  1808 
       
  1809         return  moduleInstance;
       
  1810 
       
  1811         /**
       
  1812          * @param {string} provider
       
  1813          * @param {string} method
       
  1814          * @param {String=} insertMethod
       
  1815          * @returns {angular.Module}
       
  1816          */
       
  1817         function invokeLater(provider, method, insertMethod) {
       
  1818           return function() {
       
  1819             invokeQueue[insertMethod || 'push']([provider, method, arguments]);
       
  1820             return moduleInstance;
       
  1821           };
       
  1822         }
       
  1823       });
       
  1824     };
       
  1825   });
       
  1826 
       
  1827 }
       
  1828 
       
  1829 /* global
       
  1830     angularModule: true,
       
  1831     version: true,
       
  1832 
       
  1833     $LocaleProvider,
       
  1834     $CompileProvider,
       
  1835 
       
  1836     htmlAnchorDirective,
       
  1837     inputDirective,
       
  1838     inputDirective,
       
  1839     formDirective,
       
  1840     scriptDirective,
       
  1841     selectDirective,
       
  1842     styleDirective,
       
  1843     optionDirective,
       
  1844     ngBindDirective,
       
  1845     ngBindHtmlDirective,
       
  1846     ngBindTemplateDirective,
       
  1847     ngClassDirective,
       
  1848     ngClassEvenDirective,
       
  1849     ngClassOddDirective,
       
  1850     ngCspDirective,
       
  1851     ngCloakDirective,
       
  1852     ngControllerDirective,
       
  1853     ngFormDirective,
       
  1854     ngHideDirective,
       
  1855     ngIfDirective,
       
  1856     ngIncludeDirective,
       
  1857     ngIncludeFillContentDirective,
       
  1858     ngInitDirective,
       
  1859     ngNonBindableDirective,
       
  1860     ngPluralizeDirective,
       
  1861     ngRepeatDirective,
       
  1862     ngShowDirective,
       
  1863     ngStyleDirective,
       
  1864     ngSwitchDirective,
       
  1865     ngSwitchWhenDirective,
       
  1866     ngSwitchDefaultDirective,
       
  1867     ngOptionsDirective,
       
  1868     ngTranscludeDirective,
       
  1869     ngModelDirective,
       
  1870     ngListDirective,
       
  1871     ngChangeDirective,
       
  1872     requiredDirective,
       
  1873     requiredDirective,
       
  1874     ngValueDirective,
       
  1875     ngAttributeAliasDirectives,
       
  1876     ngEventDirectives,
       
  1877 
       
  1878     $AnchorScrollProvider,
       
  1879     $AnimateProvider,
       
  1880     $BrowserProvider,
       
  1881     $CacheFactoryProvider,
       
  1882     $ControllerProvider,
       
  1883     $DocumentProvider,
       
  1884     $ExceptionHandlerProvider,
       
  1885     $FilterProvider,
       
  1886     $InterpolateProvider,
       
  1887     $IntervalProvider,
       
  1888     $HttpProvider,
       
  1889     $HttpBackendProvider,
       
  1890     $LocationProvider,
       
  1891     $LogProvider,
       
  1892     $ParseProvider,
       
  1893     $RootScopeProvider,
       
  1894     $QProvider,
       
  1895     $$SanitizeUriProvider,
       
  1896     $SceProvider,
       
  1897     $SceDelegateProvider,
       
  1898     $SnifferProvider,
       
  1899     $TemplateCacheProvider,
       
  1900     $TimeoutProvider,
       
  1901     $$RAFProvider,
       
  1902     $$AsyncCallbackProvider,
       
  1903     $WindowProvider
       
  1904 */
       
  1905 
       
  1906 
       
  1907 /**
       
  1908  * @ngdoc object
       
  1909  * @name angular.version
       
  1910  * @module ng
       
  1911  * @description
       
  1912  * An object that contains information about the current AngularJS version. This object has the
       
  1913  * following properties:
       
  1914  *
       
  1915  * - `full` – `{string}` – Full version string, such as "0.9.18".
       
  1916  * - `major` – `{number}` – Major version number, such as "0".
       
  1917  * - `minor` – `{number}` – Minor version number, such as "9".
       
  1918  * - `dot` – `{number}` – Dot version number, such as "18".
       
  1919  * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
       
  1920  */
       
  1921 var version = {
       
  1922   full: '1.2.15',    // all of these placeholder strings will be replaced by grunt's
       
  1923   major: 1,    // package task
       
  1924   minor: 2,
       
  1925   dot: 15,
       
  1926   codeName: 'beer-underestimating'
       
  1927 };
       
  1928 
       
  1929 
       
  1930 function publishExternalAPI(angular){
       
  1931   extend(angular, {
       
  1932     'bootstrap': bootstrap,
       
  1933     'copy': copy,
       
  1934     'extend': extend,
       
  1935     'equals': equals,
       
  1936     'element': jqLite,
       
  1937     'forEach': forEach,
       
  1938     'injector': createInjector,
       
  1939     'noop':noop,
       
  1940     'bind':bind,
       
  1941     'toJson': toJson,
       
  1942     'fromJson': fromJson,
       
  1943     'identity':identity,
       
  1944     'isUndefined': isUndefined,
       
  1945     'isDefined': isDefined,
       
  1946     'isString': isString,
       
  1947     'isFunction': isFunction,
       
  1948     'isObject': isObject,
       
  1949     'isNumber': isNumber,
       
  1950     'isElement': isElement,
       
  1951     'isArray': isArray,
       
  1952     'version': version,
       
  1953     'isDate': isDate,
       
  1954     'lowercase': lowercase,
       
  1955     'uppercase': uppercase,
       
  1956     'callbacks': {counter: 0},
       
  1957     '$$minErr': minErr,
       
  1958     '$$csp': csp
       
  1959   });
       
  1960 
       
  1961   angularModule = setupModuleLoader(window);
       
  1962   try {
       
  1963     angularModule('ngLocale');
       
  1964   } catch (e) {
       
  1965     angularModule('ngLocale', []).provider('$locale', $LocaleProvider);
       
  1966   }
       
  1967 
       
  1968   angularModule('ng', ['ngLocale'], ['$provide',
       
  1969     function ngModule($provide) {
       
  1970       // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
       
  1971       $provide.provider({
       
  1972         $$sanitizeUri: $$SanitizeUriProvider
       
  1973       });
       
  1974       $provide.provider('$compile', $CompileProvider).
       
  1975         directive({
       
  1976             a: htmlAnchorDirective,
       
  1977             input: inputDirective,
       
  1978             textarea: inputDirective,
       
  1979             form: formDirective,
       
  1980             script: scriptDirective,
       
  1981             select: selectDirective,
       
  1982             style: styleDirective,
       
  1983             option: optionDirective,
       
  1984             ngBind: ngBindDirective,
       
  1985             ngBindHtml: ngBindHtmlDirective,
       
  1986             ngBindTemplate: ngBindTemplateDirective,
       
  1987             ngClass: ngClassDirective,
       
  1988             ngClassEven: ngClassEvenDirective,
       
  1989             ngClassOdd: ngClassOddDirective,
       
  1990             ngCloak: ngCloakDirective,
       
  1991             ngController: ngControllerDirective,
       
  1992             ngForm: ngFormDirective,
       
  1993             ngHide: ngHideDirective,
       
  1994             ngIf: ngIfDirective,
       
  1995             ngInclude: ngIncludeDirective,
       
  1996             ngInit: ngInitDirective,
       
  1997             ngNonBindable: ngNonBindableDirective,
       
  1998             ngPluralize: ngPluralizeDirective,
       
  1999             ngRepeat: ngRepeatDirective,
       
  2000             ngShow: ngShowDirective,
       
  2001             ngStyle: ngStyleDirective,
       
  2002             ngSwitch: ngSwitchDirective,
       
  2003             ngSwitchWhen: ngSwitchWhenDirective,
       
  2004             ngSwitchDefault: ngSwitchDefaultDirective,
       
  2005             ngOptions: ngOptionsDirective,
       
  2006             ngTransclude: ngTranscludeDirective,
       
  2007             ngModel: ngModelDirective,
       
  2008             ngList: ngListDirective,
       
  2009             ngChange: ngChangeDirective,
       
  2010             required: requiredDirective,
       
  2011             ngRequired: requiredDirective,
       
  2012             ngValue: ngValueDirective
       
  2013         }).
       
  2014         directive({
       
  2015           ngInclude: ngIncludeFillContentDirective
       
  2016         }).
       
  2017         directive(ngAttributeAliasDirectives).
       
  2018         directive(ngEventDirectives);
       
  2019       $provide.provider({
       
  2020         $anchorScroll: $AnchorScrollProvider,
       
  2021         $animate: $AnimateProvider,
       
  2022         $browser: $BrowserProvider,
       
  2023         $cacheFactory: $CacheFactoryProvider,
       
  2024         $controller: $ControllerProvider,
       
  2025         $document: $DocumentProvider,
       
  2026         $exceptionHandler: $ExceptionHandlerProvider,
       
  2027         $filter: $FilterProvider,
       
  2028         $interpolate: $InterpolateProvider,
       
  2029         $interval: $IntervalProvider,
       
  2030         $http: $HttpProvider,
       
  2031         $httpBackend: $HttpBackendProvider,
       
  2032         $location: $LocationProvider,
       
  2033         $log: $LogProvider,
       
  2034         $parse: $ParseProvider,
       
  2035         $rootScope: $RootScopeProvider,
       
  2036         $q: $QProvider,
       
  2037         $sce: $SceProvider,
       
  2038         $sceDelegate: $SceDelegateProvider,
       
  2039         $sniffer: $SnifferProvider,
       
  2040         $templateCache: $TemplateCacheProvider,
       
  2041         $timeout: $TimeoutProvider,
       
  2042         $window: $WindowProvider,
       
  2043         $$rAF: $$RAFProvider,
       
  2044         $$asyncCallback : $$AsyncCallbackProvider
       
  2045       });
       
  2046     }
       
  2047   ]);
       
  2048 }
       
  2049 
       
  2050 /* global
       
  2051 
       
  2052   -JQLitePrototype,
       
  2053   -addEventListenerFn,
       
  2054   -removeEventListenerFn,
       
  2055   -BOOLEAN_ATTR
       
  2056 */
       
  2057 
       
  2058 //////////////////////////////////
       
  2059 //JQLite
       
  2060 //////////////////////////////////
       
  2061 
       
  2062 /**
       
  2063  * @ngdoc function
       
  2064  * @name angular.element
       
  2065  * @module ng
       
  2066  * @function
       
  2067  *
       
  2068  * @description
       
  2069  * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
       
  2070  *
       
  2071  * If jQuery is available, `angular.element` is an alias for the
       
  2072  * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
       
  2073  * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
       
  2074  *
       
  2075  * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
       
  2076  * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
       
  2077  * commonly needed functionality with the goal of having a very small footprint.</div>
       
  2078  *
       
  2079  * To use jQuery, simply load it before `DOMContentLoaded` event fired.
       
  2080  *
       
  2081  * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
       
  2082  * jqLite; they are never raw DOM references.</div>
       
  2083  *
       
  2084  * ## Angular's jqLite
       
  2085  * jqLite provides only the following jQuery methods:
       
  2086  *
       
  2087  * - [`addClass()`](http://api.jquery.com/addClass/)
       
  2088  * - [`after()`](http://api.jquery.com/after/)
       
  2089  * - [`append()`](http://api.jquery.com/append/)
       
  2090  * - [`attr()`](http://api.jquery.com/attr/)
       
  2091  * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
       
  2092  * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
       
  2093  * - [`clone()`](http://api.jquery.com/clone/)
       
  2094  * - [`contents()`](http://api.jquery.com/contents/)
       
  2095  * - [`css()`](http://api.jquery.com/css/)
       
  2096  * - [`data()`](http://api.jquery.com/data/)
       
  2097  * - [`empty()`](http://api.jquery.com/empty/)
       
  2098  * - [`eq()`](http://api.jquery.com/eq/)
       
  2099  * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
       
  2100  * - [`hasClass()`](http://api.jquery.com/hasClass/)
       
  2101  * - [`html()`](http://api.jquery.com/html/)
       
  2102  * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
       
  2103  * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
       
  2104  * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors
       
  2105  * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
       
  2106  * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
       
  2107  * - [`prepend()`](http://api.jquery.com/prepend/)
       
  2108  * - [`prop()`](http://api.jquery.com/prop/)
       
  2109  * - [`ready()`](http://api.jquery.com/ready/)
       
  2110  * - [`remove()`](http://api.jquery.com/remove/)
       
  2111  * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
       
  2112  * - [`removeClass()`](http://api.jquery.com/removeClass/)
       
  2113  * - [`removeData()`](http://api.jquery.com/removeData/)
       
  2114  * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
       
  2115  * - [`text()`](http://api.jquery.com/text/)
       
  2116  * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
       
  2117  * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
       
  2118  * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces
       
  2119  * - [`val()`](http://api.jquery.com/val/)
       
  2120  * - [`wrap()`](http://api.jquery.com/wrap/)
       
  2121  *
       
  2122  * ## jQuery/jqLite Extras
       
  2123  * Angular also provides the following additional methods and events to both jQuery and jqLite:
       
  2124  *
       
  2125  * ### Events
       
  2126  * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
       
  2127  *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
       
  2128  *    element before it is removed.
       
  2129  *
       
  2130  * ### Methods
       
  2131  * - `controller(name)` - retrieves the controller of the current element or its parent. By default
       
  2132  *   retrieves controller associated with the `ngController` directive. If `name` is provided as
       
  2133  *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
       
  2134  *   `'ngModel'`).
       
  2135  * - `injector()` - retrieves the injector of the current element or its parent.
       
  2136  * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
       
  2137  *   element or its parent.
       
  2138  * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
       
  2139  *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
       
  2140  *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
       
  2141  * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
       
  2142  *   parent element is reached.
       
  2143  *
       
  2144  * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
       
  2145  * @returns {Object} jQuery object.
       
  2146  */
       
  2147 
       
  2148 var jqCache = JQLite.cache = {},
       
  2149     jqName = JQLite.expando = 'ng-' + new Date().getTime(),
       
  2150     jqId = 1,
       
  2151     addEventListenerFn = (window.document.addEventListener
       
  2152       ? function(element, type, fn) {element.addEventListener(type, fn, false);}
       
  2153       : function(element, type, fn) {element.attachEvent('on' + type, fn);}),
       
  2154     removeEventListenerFn = (window.document.removeEventListener
       
  2155       ? function(element, type, fn) {element.removeEventListener(type, fn, false); }
       
  2156       : function(element, type, fn) {element.detachEvent('on' + type, fn); });
       
  2157 
       
  2158 /*
       
  2159  * !!! This is an undocumented "private" function !!!
       
  2160  */
       
  2161 var jqData = JQLite._data = function(node) {
       
  2162   //jQuery always returns an object on cache miss
       
  2163   return this.cache[node[this.expando]] || {};
       
  2164 };
       
  2165 
       
  2166 function jqNextId() { return ++jqId; }
       
  2167 
       
  2168 
       
  2169 var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
       
  2170 var MOZ_HACK_REGEXP = /^moz([A-Z])/;
       
  2171 var jqLiteMinErr = minErr('jqLite');
       
  2172 
       
  2173 /**
       
  2174  * Converts snake_case to camelCase.
       
  2175  * Also there is special case for Moz prefix starting with upper case letter.
       
  2176  * @param name Name to normalize
       
  2177  */
       
  2178 function camelCase(name) {
       
  2179   return name.
       
  2180     replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
       
  2181       return offset ? letter.toUpperCase() : letter;
       
  2182     }).
       
  2183     replace(MOZ_HACK_REGEXP, 'Moz$1');
       
  2184 }
       
  2185 
       
  2186 /////////////////////////////////////////////
       
  2187 // jQuery mutation patch
       
  2188 //
       
  2189 // In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a
       
  2190 // $destroy event on all DOM nodes being removed.
       
  2191 //
       
  2192 /////////////////////////////////////////////
       
  2193 
       
  2194 function jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) {
       
  2195   var originalJqFn = jQuery.fn[name];
       
  2196   originalJqFn = originalJqFn.$original || originalJqFn;
       
  2197   removePatch.$original = originalJqFn;
       
  2198   jQuery.fn[name] = removePatch;
       
  2199 
       
  2200   function removePatch(param) {
       
  2201     // jshint -W040
       
  2202     var list = filterElems && param ? [this.filter(param)] : [this],
       
  2203         fireEvent = dispatchThis,
       
  2204         set, setIndex, setLength,
       
  2205         element, childIndex, childLength, children;
       
  2206 
       
  2207     if (!getterIfNoArguments || param != null) {
       
  2208       while(list.length) {
       
  2209         set = list.shift();
       
  2210         for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) {
       
  2211           element = jqLite(set[setIndex]);
       
  2212           if (fireEvent) {
       
  2213             element.triggerHandler('$destroy');
       
  2214           } else {
       
  2215             fireEvent = !fireEvent;
       
  2216           }
       
  2217           for(childIndex = 0, childLength = (children = element.children()).length;
       
  2218               childIndex < childLength;
       
  2219               childIndex++) {
       
  2220             list.push(jQuery(children[childIndex]));
       
  2221           }
       
  2222         }
       
  2223       }
       
  2224     }
       
  2225     return originalJqFn.apply(this, arguments);
       
  2226   }
       
  2227 }
       
  2228 
       
  2229 /////////////////////////////////////////////
       
  2230 function JQLite(element) {
       
  2231   if (element instanceof JQLite) {
       
  2232     return element;
       
  2233   }
       
  2234   if (isString(element)) {
       
  2235     element = trim(element);
       
  2236   }
       
  2237   if (!(this instanceof JQLite)) {
       
  2238     if (isString(element) && element.charAt(0) != '<') {
       
  2239       throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
       
  2240     }
       
  2241     return new JQLite(element);
       
  2242   }
       
  2243 
       
  2244   if (isString(element)) {
       
  2245     var div = document.createElement('div');
       
  2246     // Read about the NoScope elements here:
       
  2247     // http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx
       
  2248     div.innerHTML = '<div>&#160;</div>' + element; // IE insanity to make NoScope elements work!
       
  2249     div.removeChild(div.firstChild); // remove the superfluous div
       
  2250     jqLiteAddNodes(this, div.childNodes);
       
  2251     var fragment = jqLite(document.createDocumentFragment());
       
  2252     fragment.append(this); // detach the elements from the temporary DOM div.
       
  2253   } else {
       
  2254     jqLiteAddNodes(this, element);
       
  2255   }
       
  2256 }
       
  2257 
       
  2258 function jqLiteClone(element) {
       
  2259   return element.cloneNode(true);
       
  2260 }
       
  2261 
       
  2262 function jqLiteDealoc(element){
       
  2263   jqLiteRemoveData(element);
       
  2264   for ( var i = 0, children = element.childNodes || []; i < children.length; i++) {
       
  2265     jqLiteDealoc(children[i]);
       
  2266   }
       
  2267 }
       
  2268 
       
  2269 function jqLiteOff(element, type, fn, unsupported) {
       
  2270   if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
       
  2271 
       
  2272   var events = jqLiteExpandoStore(element, 'events'),
       
  2273       handle = jqLiteExpandoStore(element, 'handle');
       
  2274 
       
  2275   if (!handle) return; //no listeners registered
       
  2276 
       
  2277   if (isUndefined(type)) {
       
  2278     forEach(events, function(eventHandler, type) {
       
  2279       removeEventListenerFn(element, type, eventHandler);
       
  2280       delete events[type];
       
  2281     });
       
  2282   } else {
       
  2283     forEach(type.split(' '), function(type) {
       
  2284       if (isUndefined(fn)) {
       
  2285         removeEventListenerFn(element, type, events[type]);
       
  2286         delete events[type];
       
  2287       } else {
       
  2288         arrayRemove(events[type] || [], fn);
       
  2289       }
       
  2290     });
       
  2291   }
       
  2292 }
       
  2293 
       
  2294 function jqLiteRemoveData(element, name) {
       
  2295   var expandoId = element[jqName],
       
  2296       expandoStore = jqCache[expandoId];
       
  2297 
       
  2298   if (expandoStore) {
       
  2299     if (name) {
       
  2300       delete jqCache[expandoId].data[name];
       
  2301       return;
       
  2302     }
       
  2303 
       
  2304     if (expandoStore.handle) {
       
  2305       expandoStore.events.$destroy && expandoStore.handle({}, '$destroy');
       
  2306       jqLiteOff(element);
       
  2307     }
       
  2308     delete jqCache[expandoId];
       
  2309     element[jqName] = undefined; // ie does not allow deletion of attributes on elements.
       
  2310   }
       
  2311 }
       
  2312 
       
  2313 function jqLiteExpandoStore(element, key, value) {
       
  2314   var expandoId = element[jqName],
       
  2315       expandoStore = jqCache[expandoId || -1];
       
  2316 
       
  2317   if (isDefined(value)) {
       
  2318     if (!expandoStore) {
       
  2319       element[jqName] = expandoId = jqNextId();
       
  2320       expandoStore = jqCache[expandoId] = {};
       
  2321     }
       
  2322     expandoStore[key] = value;
       
  2323   } else {
       
  2324     return expandoStore && expandoStore[key];
       
  2325   }
       
  2326 }
       
  2327 
       
  2328 function jqLiteData(element, key, value) {
       
  2329   var data = jqLiteExpandoStore(element, 'data'),
       
  2330       isSetter = isDefined(value),
       
  2331       keyDefined = !isSetter && isDefined(key),
       
  2332       isSimpleGetter = keyDefined && !isObject(key);
       
  2333 
       
  2334   if (!data && !isSimpleGetter) {
       
  2335     jqLiteExpandoStore(element, 'data', data = {});
       
  2336   }
       
  2337 
       
  2338   if (isSetter) {
       
  2339     data[key] = value;
       
  2340   } else {
       
  2341     if (keyDefined) {
       
  2342       if (isSimpleGetter) {
       
  2343         // don't create data in this case.
       
  2344         return data && data[key];
       
  2345       } else {
       
  2346         extend(data, key);
       
  2347       }
       
  2348     } else {
       
  2349       return data;
       
  2350     }
       
  2351   }
       
  2352 }
       
  2353 
       
  2354 function jqLiteHasClass(element, selector) {
       
  2355   if (!element.getAttribute) return false;
       
  2356   return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
       
  2357       indexOf( " " + selector + " " ) > -1);
       
  2358 }
       
  2359 
       
  2360 function jqLiteRemoveClass(element, cssClasses) {
       
  2361   if (cssClasses && element.setAttribute) {
       
  2362     forEach(cssClasses.split(' '), function(cssClass) {
       
  2363       element.setAttribute('class', trim(
       
  2364           (" " + (element.getAttribute('class') || '') + " ")
       
  2365           .replace(/[\n\t]/g, " ")
       
  2366           .replace(" " + trim(cssClass) + " ", " "))
       
  2367       );
       
  2368     });
       
  2369   }
       
  2370 }
       
  2371 
       
  2372 function jqLiteAddClass(element, cssClasses) {
       
  2373   if (cssClasses && element.setAttribute) {
       
  2374     var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
       
  2375                             .replace(/[\n\t]/g, " ");
       
  2376 
       
  2377     forEach(cssClasses.split(' '), function(cssClass) {
       
  2378       cssClass = trim(cssClass);
       
  2379       if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
       
  2380         existingClasses += cssClass + ' ';
       
  2381       }
       
  2382     });
       
  2383 
       
  2384     element.setAttribute('class', trim(existingClasses));
       
  2385   }
       
  2386 }
       
  2387 
       
  2388 function jqLiteAddNodes(root, elements) {
       
  2389   if (elements) {
       
  2390     elements = (!elements.nodeName && isDefined(elements.length) && !isWindow(elements))
       
  2391       ? elements
       
  2392       : [ elements ];
       
  2393     for(var i=0; i < elements.length; i++) {
       
  2394       root.push(elements[i]);
       
  2395     }
       
  2396   }
       
  2397 }
       
  2398 
       
  2399 function jqLiteController(element, name) {
       
  2400   return jqLiteInheritedData(element, '$' + (name || 'ngController' ) + 'Controller');
       
  2401 }
       
  2402 
       
  2403 function jqLiteInheritedData(element, name, value) {
       
  2404   element = jqLite(element);
       
  2405 
       
  2406   // if element is the document object work with the html element instead
       
  2407   // this makes $(document).scope() possible
       
  2408   if(element[0].nodeType == 9) {
       
  2409     element = element.find('html');
       
  2410   }
       
  2411   var names = isArray(name) ? name : [name];
       
  2412 
       
  2413   while (element.length) {
       
  2414     var node = element[0];
       
  2415     for (var i = 0, ii = names.length; i < ii; i++) {
       
  2416       if ((value = element.data(names[i])) !== undefined) return value;
       
  2417     }
       
  2418 
       
  2419     // If dealing with a document fragment node with a host element, and no parent, use the host
       
  2420     // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
       
  2421     // to lookup parent controllers.
       
  2422     element = jqLite(node.parentNode || (node.nodeType === 11 && node.host));
       
  2423   }
       
  2424 }
       
  2425 
       
  2426 function jqLiteEmpty(element) {
       
  2427   for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) {
       
  2428     jqLiteDealoc(childNodes[i]);
       
  2429   }
       
  2430   while (element.firstChild) {
       
  2431     element.removeChild(element.firstChild);
       
  2432   }
       
  2433 }
       
  2434 
       
  2435 //////////////////////////////////////////
       
  2436 // Functions which are declared directly.
       
  2437 //////////////////////////////////////////
       
  2438 var JQLitePrototype = JQLite.prototype = {
       
  2439   ready: function(fn) {
       
  2440     var fired = false;
       
  2441 
       
  2442     function trigger() {
       
  2443       if (fired) return;
       
  2444       fired = true;
       
  2445       fn();
       
  2446     }
       
  2447 
       
  2448     // check if document already is loaded
       
  2449     if (document.readyState === 'complete'){
       
  2450       setTimeout(trigger);
       
  2451     } else {
       
  2452       this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
       
  2453       // we can not use jqLite since we are not done loading and jQuery could be loaded later.
       
  2454       // jshint -W064
       
  2455       JQLite(window).on('load', trigger); // fallback to window.onload for others
       
  2456       // jshint +W064
       
  2457     }
       
  2458   },
       
  2459   toString: function() {
       
  2460     var value = [];
       
  2461     forEach(this, function(e){ value.push('' + e);});
       
  2462     return '[' + value.join(', ') + ']';
       
  2463   },
       
  2464 
       
  2465   eq: function(index) {
       
  2466       return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
       
  2467   },
       
  2468 
       
  2469   length: 0,
       
  2470   push: push,
       
  2471   sort: [].sort,
       
  2472   splice: [].splice
       
  2473 };
       
  2474 
       
  2475 //////////////////////////////////////////
       
  2476 // Functions iterating getter/setters.
       
  2477 // these functions return self on setter and
       
  2478 // value on get.
       
  2479 //////////////////////////////////////////
       
  2480 var BOOLEAN_ATTR = {};
       
  2481 forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
       
  2482   BOOLEAN_ATTR[lowercase(value)] = value;
       
  2483 });
       
  2484 var BOOLEAN_ELEMENTS = {};
       
  2485 forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
       
  2486   BOOLEAN_ELEMENTS[uppercase(value)] = true;
       
  2487 });
       
  2488 
       
  2489 function getBooleanAttrName(element, name) {
       
  2490   // check dom last since we will most likely fail on name
       
  2491   var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
       
  2492 
       
  2493   // booleanAttr is here twice to minimize DOM access
       
  2494   return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr;
       
  2495 }
       
  2496 
       
  2497 forEach({
       
  2498   data: jqLiteData,
       
  2499   inheritedData: jqLiteInheritedData,
       
  2500 
       
  2501   scope: function(element) {
       
  2502     // Can't use jqLiteData here directly so we stay compatible with jQuery!
       
  2503     return jqLite(element).data('$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
       
  2504   },
       
  2505 
       
  2506   isolateScope: function(element) {
       
  2507     // Can't use jqLiteData here directly so we stay compatible with jQuery!
       
  2508     return jqLite(element).data('$isolateScope') || jqLite(element).data('$isolateScopeNoTemplate');
       
  2509   },
       
  2510 
       
  2511   controller: jqLiteController,
       
  2512 
       
  2513   injector: function(element) {
       
  2514     return jqLiteInheritedData(element, '$injector');
       
  2515   },
       
  2516 
       
  2517   removeAttr: function(element,name) {
       
  2518     element.removeAttribute(name);
       
  2519   },
       
  2520 
       
  2521   hasClass: jqLiteHasClass,
       
  2522 
       
  2523   css: function(element, name, value) {
       
  2524     name = camelCase(name);
       
  2525 
       
  2526     if (isDefined(value)) {
       
  2527       element.style[name] = value;
       
  2528     } else {
       
  2529       var val;
       
  2530 
       
  2531       if (msie <= 8) {
       
  2532         // this is some IE specific weirdness that jQuery 1.6.4 does not sure why
       
  2533         val = element.currentStyle && element.currentStyle[name];
       
  2534         if (val === '') val = 'auto';
       
  2535       }
       
  2536 
       
  2537       val = val || element.style[name];
       
  2538 
       
  2539       if (msie <= 8) {
       
  2540         // jquery weirdness :-/
       
  2541         val = (val === '') ? undefined : val;
       
  2542       }
       
  2543 
       
  2544       return  val;
       
  2545     }
       
  2546   },
       
  2547 
       
  2548   attr: function(element, name, value){
       
  2549     var lowercasedName = lowercase(name);
       
  2550     if (BOOLEAN_ATTR[lowercasedName]) {
       
  2551       if (isDefined(value)) {
       
  2552         if (!!value) {
       
  2553           element[name] = true;
       
  2554           element.setAttribute(name, lowercasedName);
       
  2555         } else {
       
  2556           element[name] = false;
       
  2557           element.removeAttribute(lowercasedName);
       
  2558         }
       
  2559       } else {
       
  2560         return (element[name] ||
       
  2561                  (element.attributes.getNamedItem(name)|| noop).specified)
       
  2562                ? lowercasedName
       
  2563                : undefined;
       
  2564       }
       
  2565     } else if (isDefined(value)) {
       
  2566       element.setAttribute(name, value);
       
  2567     } else if (element.getAttribute) {
       
  2568       // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
       
  2569       // some elements (e.g. Document) don't have get attribute, so return undefined
       
  2570       var ret = element.getAttribute(name, 2);
       
  2571       // normalize non-existing attributes to undefined (as jQuery)
       
  2572       return ret === null ? undefined : ret;
       
  2573     }
       
  2574   },
       
  2575 
       
  2576   prop: function(element, name, value) {
       
  2577     if (isDefined(value)) {
       
  2578       element[name] = value;
       
  2579     } else {
       
  2580       return element[name];
       
  2581     }
       
  2582   },
       
  2583 
       
  2584   text: (function() {
       
  2585     var NODE_TYPE_TEXT_PROPERTY = [];
       
  2586     if (msie < 9) {
       
  2587       NODE_TYPE_TEXT_PROPERTY[1] = 'innerText';    /** Element **/
       
  2588       NODE_TYPE_TEXT_PROPERTY[3] = 'nodeValue';    /** Text **/
       
  2589     } else {
       
  2590       NODE_TYPE_TEXT_PROPERTY[1] =                 /** Element **/
       
  2591       NODE_TYPE_TEXT_PROPERTY[3] = 'textContent';  /** Text **/
       
  2592     }
       
  2593     getText.$dv = '';
       
  2594     return getText;
       
  2595 
       
  2596     function getText(element, value) {
       
  2597       var textProp = NODE_TYPE_TEXT_PROPERTY[element.nodeType];
       
  2598       if (isUndefined(value)) {
       
  2599         return textProp ? element[textProp] : '';
       
  2600       }
       
  2601       element[textProp] = value;
       
  2602     }
       
  2603   })(),
       
  2604 
       
  2605   val: function(element, value) {
       
  2606     if (isUndefined(value)) {
       
  2607       if (nodeName_(element) === 'SELECT' && element.multiple) {
       
  2608         var result = [];
       
  2609         forEach(element.options, function (option) {
       
  2610           if (option.selected) {
       
  2611             result.push(option.value || option.text);
       
  2612           }
       
  2613         });
       
  2614         return result.length === 0 ? null : result;
       
  2615       }
       
  2616       return element.value;
       
  2617     }
       
  2618     element.value = value;
       
  2619   },
       
  2620 
       
  2621   html: function(element, value) {
       
  2622     if (isUndefined(value)) {
       
  2623       return element.innerHTML;
       
  2624     }
       
  2625     for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) {
       
  2626       jqLiteDealoc(childNodes[i]);
       
  2627     }
       
  2628     element.innerHTML = value;
       
  2629   },
       
  2630 
       
  2631   empty: jqLiteEmpty
       
  2632 }, function(fn, name){
       
  2633   /**
       
  2634    * Properties: writes return selection, reads return first value
       
  2635    */
       
  2636   JQLite.prototype[name] = function(arg1, arg2) {
       
  2637     var i, key;
       
  2638 
       
  2639     // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
       
  2640     // in a way that survives minification.
       
  2641     // jqLiteEmpty takes no arguments but is a setter.
       
  2642     if (fn !== jqLiteEmpty &&
       
  2643         (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) {
       
  2644       if (isObject(arg1)) {
       
  2645 
       
  2646         // we are a write, but the object properties are the key/values
       
  2647         for (i = 0; i < this.length; i++) {
       
  2648           if (fn === jqLiteData) {
       
  2649             // data() takes the whole object in jQuery
       
  2650             fn(this[i], arg1);
       
  2651           } else {
       
  2652             for (key in arg1) {
       
  2653               fn(this[i], key, arg1[key]);
       
  2654             }
       
  2655           }
       
  2656         }
       
  2657         // return self for chaining
       
  2658         return this;
       
  2659       } else {
       
  2660         // we are a read, so read the first child.
       
  2661         var value = fn.$dv;
       
  2662         // Only if we have $dv do we iterate over all, otherwise it is just the first element.
       
  2663         var jj = (value === undefined) ? Math.min(this.length, 1) : this.length;
       
  2664         for (var j = 0; j < jj; j++) {
       
  2665           var nodeValue = fn(this[j], arg1, arg2);
       
  2666           value = value ? value + nodeValue : nodeValue;
       
  2667         }
       
  2668         return value;
       
  2669       }
       
  2670     } else {
       
  2671       // we are a write, so apply to all children
       
  2672       for (i = 0; i < this.length; i++) {
       
  2673         fn(this[i], arg1, arg2);
       
  2674       }
       
  2675       // return self for chaining
       
  2676       return this;
       
  2677     }
       
  2678   };
       
  2679 });
       
  2680 
       
  2681 function createEventHandler(element, events) {
       
  2682   var eventHandler = function (event, type) {
       
  2683     if (!event.preventDefault) {
       
  2684       event.preventDefault = function() {
       
  2685         event.returnValue = false; //ie
       
  2686       };
       
  2687     }
       
  2688 
       
  2689     if (!event.stopPropagation) {
       
  2690       event.stopPropagation = function() {
       
  2691         event.cancelBubble = true; //ie
       
  2692       };
       
  2693     }
       
  2694 
       
  2695     if (!event.target) {
       
  2696       event.target = event.srcElement || document;
       
  2697     }
       
  2698 
       
  2699     if (isUndefined(event.defaultPrevented)) {
       
  2700       var prevent = event.preventDefault;
       
  2701       event.preventDefault = function() {
       
  2702         event.defaultPrevented = true;
       
  2703         prevent.call(event);
       
  2704       };
       
  2705       event.defaultPrevented = false;
       
  2706     }
       
  2707 
       
  2708     event.isDefaultPrevented = function() {
       
  2709       return event.defaultPrevented || event.returnValue === false;
       
  2710     };
       
  2711 
       
  2712     // Copy event handlers in case event handlers array is modified during execution.
       
  2713     var eventHandlersCopy = shallowCopy(events[type || event.type] || []);
       
  2714 
       
  2715     forEach(eventHandlersCopy, function(fn) {
       
  2716       fn.call(element, event);
       
  2717     });
       
  2718 
       
  2719     // Remove monkey-patched methods (IE),
       
  2720     // as they would cause memory leaks in IE8.
       
  2721     if (msie <= 8) {
       
  2722       // IE7/8 does not allow to delete property on native object
       
  2723       event.preventDefault = null;
       
  2724       event.stopPropagation = null;
       
  2725       event.isDefaultPrevented = null;
       
  2726     } else {
       
  2727       // It shouldn't affect normal browsers (native methods are defined on prototype).
       
  2728       delete event.preventDefault;
       
  2729       delete event.stopPropagation;
       
  2730       delete event.isDefaultPrevented;
       
  2731     }
       
  2732   };
       
  2733   eventHandler.elem = element;
       
  2734   return eventHandler;
       
  2735 }
       
  2736 
       
  2737 //////////////////////////////////////////
       
  2738 // Functions iterating traversal.
       
  2739 // These functions chain results into a single
       
  2740 // selector.
       
  2741 //////////////////////////////////////////
       
  2742 forEach({
       
  2743   removeData: jqLiteRemoveData,
       
  2744 
       
  2745   dealoc: jqLiteDealoc,
       
  2746 
       
  2747   on: function onFn(element, type, fn, unsupported){
       
  2748     if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
       
  2749 
       
  2750     var events = jqLiteExpandoStore(element, 'events'),
       
  2751         handle = jqLiteExpandoStore(element, 'handle');
       
  2752 
       
  2753     if (!events) jqLiteExpandoStore(element, 'events', events = {});
       
  2754     if (!handle) jqLiteExpandoStore(element, 'handle', handle = createEventHandler(element, events));
       
  2755 
       
  2756     forEach(type.split(' '), function(type){
       
  2757       var eventFns = events[type];
       
  2758 
       
  2759       if (!eventFns) {
       
  2760         if (type == 'mouseenter' || type == 'mouseleave') {
       
  2761           var contains = document.body.contains || document.body.compareDocumentPosition ?
       
  2762           function( a, b ) {
       
  2763             // jshint bitwise: false
       
  2764             var adown = a.nodeType === 9 ? a.documentElement : a,
       
  2765             bup = b && b.parentNode;
       
  2766             return a === bup || !!( bup && bup.nodeType === 1 && (
       
  2767               adown.contains ?
       
  2768               adown.contains( bup ) :
       
  2769               a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
       
  2770               ));
       
  2771             } :
       
  2772             function( a, b ) {
       
  2773               if ( b ) {
       
  2774                 while ( (b = b.parentNode) ) {
       
  2775                   if ( b === a ) {
       
  2776                     return true;
       
  2777                   }
       
  2778                 }
       
  2779               }
       
  2780               return false;
       
  2781             };
       
  2782 
       
  2783           events[type] = [];
       
  2784 
       
  2785           // Refer to jQuery's implementation of mouseenter & mouseleave
       
  2786           // Read about mouseenter and mouseleave:
       
  2787           // http://www.quirksmode.org/js/events_mouse.html#link8
       
  2788           var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"};
       
  2789 
       
  2790           onFn(element, eventmap[type], function(event) {
       
  2791             var target = this, related = event.relatedTarget;
       
  2792             // For mousenter/leave call the handler if related is outside the target.
       
  2793             // NB: No relatedTarget if the mouse left/entered the browser window
       
  2794             if ( !related || (related !== target && !contains(target, related)) ){
       
  2795               handle(event, type);
       
  2796             }
       
  2797           });
       
  2798 
       
  2799         } else {
       
  2800           addEventListenerFn(element, type, handle);
       
  2801           events[type] = [];
       
  2802         }
       
  2803         eventFns = events[type];
       
  2804       }
       
  2805       eventFns.push(fn);
       
  2806     });
       
  2807   },
       
  2808 
       
  2809   off: jqLiteOff,
       
  2810 
       
  2811   one: function(element, type, fn) {
       
  2812     element = jqLite(element);
       
  2813 
       
  2814     //add the listener twice so that when it is called
       
  2815     //you can remove the original function and still be
       
  2816     //able to call element.off(ev, fn) normally
       
  2817     element.on(type, function onFn() {
       
  2818       element.off(type, fn);
       
  2819       element.off(type, onFn);
       
  2820     });
       
  2821     element.on(type, fn);
       
  2822   },
       
  2823 
       
  2824   replaceWith: function(element, replaceNode) {
       
  2825     var index, parent = element.parentNode;
       
  2826     jqLiteDealoc(element);
       
  2827     forEach(new JQLite(replaceNode), function(node){
       
  2828       if (index) {
       
  2829         parent.insertBefore(node, index.nextSibling);
       
  2830       } else {
       
  2831         parent.replaceChild(node, element);
       
  2832       }
       
  2833       index = node;
       
  2834     });
       
  2835   },
       
  2836 
       
  2837   children: function(element) {
       
  2838     var children = [];
       
  2839     forEach(element.childNodes, function(element){
       
  2840       if (element.nodeType === 1)
       
  2841         children.push(element);
       
  2842     });
       
  2843     return children;
       
  2844   },
       
  2845 
       
  2846   contents: function(element) {
       
  2847     return element.contentDocument || element.childNodes || [];
       
  2848   },
       
  2849 
       
  2850   append: function(element, node) {
       
  2851     forEach(new JQLite(node), function(child){
       
  2852       if (element.nodeType === 1 || element.nodeType === 11) {
       
  2853         element.appendChild(child);
       
  2854       }
       
  2855     });
       
  2856   },
       
  2857 
       
  2858   prepend: function(element, node) {
       
  2859     if (element.nodeType === 1) {
       
  2860       var index = element.firstChild;
       
  2861       forEach(new JQLite(node), function(child){
       
  2862         element.insertBefore(child, index);
       
  2863       });
       
  2864     }
       
  2865   },
       
  2866 
       
  2867   wrap: function(element, wrapNode) {
       
  2868     wrapNode = jqLite(wrapNode)[0];
       
  2869     var parent = element.parentNode;
       
  2870     if (parent) {
       
  2871       parent.replaceChild(wrapNode, element);
       
  2872     }
       
  2873     wrapNode.appendChild(element);
       
  2874   },
       
  2875 
       
  2876   remove: function(element) {
       
  2877     jqLiteDealoc(element);
       
  2878     var parent = element.parentNode;
       
  2879     if (parent) parent.removeChild(element);
       
  2880   },
       
  2881 
       
  2882   after: function(element, newElement) {
       
  2883     var index = element, parent = element.parentNode;
       
  2884     forEach(new JQLite(newElement), function(node){
       
  2885       parent.insertBefore(node, index.nextSibling);
       
  2886       index = node;
       
  2887     });
       
  2888   },
       
  2889 
       
  2890   addClass: jqLiteAddClass,
       
  2891   removeClass: jqLiteRemoveClass,
       
  2892 
       
  2893   toggleClass: function(element, selector, condition) {
       
  2894     if (selector) {
       
  2895       forEach(selector.split(' '), function(className){
       
  2896         var classCondition = condition;
       
  2897         if (isUndefined(classCondition)) {
       
  2898           classCondition = !jqLiteHasClass(element, className);
       
  2899         }
       
  2900         (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
       
  2901       });
       
  2902     }
       
  2903   },
       
  2904 
       
  2905   parent: function(element) {
       
  2906     var parent = element.parentNode;
       
  2907     return parent && parent.nodeType !== 11 ? parent : null;
       
  2908   },
       
  2909 
       
  2910   next: function(element) {
       
  2911     if (element.nextElementSibling) {
       
  2912       return element.nextElementSibling;
       
  2913     }
       
  2914 
       
  2915     // IE8 doesn't have nextElementSibling
       
  2916     var elm = element.nextSibling;
       
  2917     while (elm != null && elm.nodeType !== 1) {
       
  2918       elm = elm.nextSibling;
       
  2919     }
       
  2920     return elm;
       
  2921   },
       
  2922 
       
  2923   find: function(element, selector) {
       
  2924     if (element.getElementsByTagName) {
       
  2925       return element.getElementsByTagName(selector);
       
  2926     } else {
       
  2927       return [];
       
  2928     }
       
  2929   },
       
  2930 
       
  2931   clone: jqLiteClone,
       
  2932 
       
  2933   triggerHandler: function(element, eventName, eventData) {
       
  2934     var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName];
       
  2935 
       
  2936     eventData = eventData || [];
       
  2937 
       
  2938     var event = [{
       
  2939       preventDefault: noop,
       
  2940       stopPropagation: noop
       
  2941     }];
       
  2942 
       
  2943     forEach(eventFns, function(fn) {
       
  2944       fn.apply(element, event.concat(eventData));
       
  2945     });
       
  2946   }
       
  2947 }, function(fn, name){
       
  2948   /**
       
  2949    * chaining functions
       
  2950    */
       
  2951   JQLite.prototype[name] = function(arg1, arg2, arg3) {
       
  2952     var value;
       
  2953     for(var i=0; i < this.length; i++) {
       
  2954       if (isUndefined(value)) {
       
  2955         value = fn(this[i], arg1, arg2, arg3);
       
  2956         if (isDefined(value)) {
       
  2957           // any function which returns a value needs to be wrapped
       
  2958           value = jqLite(value);
       
  2959         }
       
  2960       } else {
       
  2961         jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
       
  2962       }
       
  2963     }
       
  2964     return isDefined(value) ? value : this;
       
  2965   };
       
  2966 
       
  2967   // bind legacy bind/unbind to on/off
       
  2968   JQLite.prototype.bind = JQLite.prototype.on;
       
  2969   JQLite.prototype.unbind = JQLite.prototype.off;
       
  2970 });
       
  2971 
       
  2972 /**
       
  2973  * Computes a hash of an 'obj'.
       
  2974  * Hash of a:
       
  2975  *  string is string
       
  2976  *  number is number as string
       
  2977  *  object is either result of calling $$hashKey function on the object or uniquely generated id,
       
  2978  *         that is also assigned to the $$hashKey property of the object.
       
  2979  *
       
  2980  * @param obj
       
  2981  * @returns {string} hash string such that the same input will have the same hash string.
       
  2982  *         The resulting string key is in 'type:hashKey' format.
       
  2983  */
       
  2984 function hashKey(obj) {
       
  2985   var objType = typeof obj,
       
  2986       key;
       
  2987 
       
  2988   if (objType == 'object' && obj !== null) {
       
  2989     if (typeof (key = obj.$$hashKey) == 'function') {
       
  2990       // must invoke on object to keep the right this
       
  2991       key = obj.$$hashKey();
       
  2992     } else if (key === undefined) {
       
  2993       key = obj.$$hashKey = nextUid();
       
  2994     }
       
  2995   } else {
       
  2996     key = obj;
       
  2997   }
       
  2998 
       
  2999   return objType + ':' + key;
       
  3000 }
       
  3001 
       
  3002 /**
       
  3003  * HashMap which can use objects as keys
       
  3004  */
       
  3005 function HashMap(array){
       
  3006   forEach(array, this.put, this);
       
  3007 }
       
  3008 HashMap.prototype = {
       
  3009   /**
       
  3010    * Store key value pair
       
  3011    * @param key key to store can be any type
       
  3012    * @param value value to store can be any type
       
  3013    */
       
  3014   put: function(key, value) {
       
  3015     this[hashKey(key)] = value;
       
  3016   },
       
  3017 
       
  3018   /**
       
  3019    * @param key
       
  3020    * @returns {Object} the value for the key
       
  3021    */
       
  3022   get: function(key) {
       
  3023     return this[hashKey(key)];
       
  3024   },
       
  3025 
       
  3026   /**
       
  3027    * Remove the key/value pair
       
  3028    * @param key
       
  3029    */
       
  3030   remove: function(key) {
       
  3031     var value = this[key = hashKey(key)];
       
  3032     delete this[key];
       
  3033     return value;
       
  3034   }
       
  3035 };
       
  3036 
       
  3037 /**
       
  3038  * @ngdoc function
       
  3039  * @module ng
       
  3040  * @name angular.injector
       
  3041  * @function
       
  3042  *
       
  3043  * @description
       
  3044  * Creates an injector function that can be used for retrieving services as well as for
       
  3045  * dependency injection (see {@link guide/di dependency injection}).
       
  3046  *
       
  3047 
       
  3048  * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
       
  3049  *        {@link angular.module}. The `ng` module must be explicitly added.
       
  3050  * @returns {function()} Injector function. See {@link auto.$injector $injector}.
       
  3051  *
       
  3052  * @example
       
  3053  * Typical usage
       
  3054  * ```js
       
  3055  *   // create an injector
       
  3056  *   var $injector = angular.injector(['ng']);
       
  3057  *
       
  3058  *   // use the injector to kick off your application
       
  3059  *   // use the type inference to auto inject arguments, or use implicit injection
       
  3060  *   $injector.invoke(function($rootScope, $compile, $document){
       
  3061  *     $compile($document)($rootScope);
       
  3062  *     $rootScope.$digest();
       
  3063  *   });
       
  3064  * ```
       
  3065  *
       
  3066  * Sometimes you want to get access to the injector of a currently running Angular app
       
  3067  * from outside Angular. Perhaps, you want to inject and compile some markup after the
       
  3068  * application has been bootstrapped. You can do this using extra `injector()` added
       
  3069  * to JQuery/jqLite elements. See {@link angular.element}.
       
  3070  *
       
  3071  * *This is fairly rare but could be the case if a third party library is injecting the
       
  3072  * markup.*
       
  3073  *
       
  3074  * In the following example a new block of HTML containing a `ng-controller`
       
  3075  * directive is added to the end of the document body by JQuery. We then compile and link
       
  3076  * it into the current AngularJS scope.
       
  3077  *
       
  3078  * ```js
       
  3079  * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
       
  3080  * $(document.body).append($div);
       
  3081  *
       
  3082  * angular.element(document).injector().invoke(function($compile) {
       
  3083  *   var scope = angular.element($div).scope();
       
  3084  *   $compile($div)(scope);
       
  3085  * });
       
  3086  * ```
       
  3087  */
       
  3088 
       
  3089 
       
  3090 /**
       
  3091  * @ngdoc module
       
  3092  * @name auto
       
  3093  * @description
       
  3094  *
       
  3095  * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
       
  3096  */
       
  3097 
       
  3098 var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
       
  3099 var FN_ARG_SPLIT = /,/;
       
  3100 var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
       
  3101 var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
       
  3102 var $injectorMinErr = minErr('$injector');
       
  3103 function annotate(fn) {
       
  3104   var $inject,
       
  3105       fnText,
       
  3106       argDecl,
       
  3107       last;
       
  3108 
       
  3109   if (typeof fn == 'function') {
       
  3110     if (!($inject = fn.$inject)) {
       
  3111       $inject = [];
       
  3112       if (fn.length) {
       
  3113         fnText = fn.toString().replace(STRIP_COMMENTS, '');
       
  3114         argDecl = fnText.match(FN_ARGS);
       
  3115         forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
       
  3116           arg.replace(FN_ARG, function(all, underscore, name){
       
  3117             $inject.push(name);
       
  3118           });
       
  3119         });
       
  3120       }
       
  3121       fn.$inject = $inject;
       
  3122     }
       
  3123   } else if (isArray(fn)) {
       
  3124     last = fn.length - 1;
       
  3125     assertArgFn(fn[last], 'fn');
       
  3126     $inject = fn.slice(0, last);
       
  3127   } else {
       
  3128     assertArgFn(fn, 'fn', true);
       
  3129   }
       
  3130   return $inject;
       
  3131 }
       
  3132 
       
  3133 ///////////////////////////////////////
       
  3134 
       
  3135 /**
       
  3136  * @ngdoc service
       
  3137  * @name $injector
       
  3138  * @function
       
  3139  *
       
  3140  * @description
       
  3141  *
       
  3142  * `$injector` is used to retrieve object instances as defined by
       
  3143  * {@link auto.$provide provider}, instantiate types, invoke methods,
       
  3144  * and load modules.
       
  3145  *
       
  3146  * The following always holds true:
       
  3147  *
       
  3148  * ```js
       
  3149  *   var $injector = angular.injector();
       
  3150  *   expect($injector.get('$injector')).toBe($injector);
       
  3151  *   expect($injector.invoke(function($injector){
       
  3152  *     return $injector;
       
  3153  *   }).toBe($injector);
       
  3154  * ```
       
  3155  *
       
  3156  * # Injection Function Annotation
       
  3157  *
       
  3158  * JavaScript does not have annotations, and annotations are needed for dependency injection. The
       
  3159  * following are all valid ways of annotating function with injection arguments and are equivalent.
       
  3160  *
       
  3161  * ```js
       
  3162  *   // inferred (only works if code not minified/obfuscated)
       
  3163  *   $injector.invoke(function(serviceA){});
       
  3164  *
       
  3165  *   // annotated
       
  3166  *   function explicit(serviceA) {};
       
  3167  *   explicit.$inject = ['serviceA'];
       
  3168  *   $injector.invoke(explicit);
       
  3169  *
       
  3170  *   // inline
       
  3171  *   $injector.invoke(['serviceA', function(serviceA){}]);
       
  3172  * ```
       
  3173  *
       
  3174  * ## Inference
       
  3175  *
       
  3176  * In JavaScript calling `toString()` on a function returns the function definition. The definition
       
  3177  * can then be parsed and the function arguments can be extracted. *NOTE:* This does not work with
       
  3178  * minification, and obfuscation tools since these tools change the argument names.
       
  3179  *
       
  3180  * ## `$inject` Annotation
       
  3181  * By adding a `$inject` property onto a function the injection parameters can be specified.
       
  3182  *
       
  3183  * ## Inline
       
  3184  * As an array of injection names, where the last item in the array is the function to call.
       
  3185  */
       
  3186 
       
  3187 /**
       
  3188  * @ngdoc method
       
  3189  * @name $injector#get
       
  3190  *
       
  3191  * @description
       
  3192  * Return an instance of the service.
       
  3193  *
       
  3194  * @param {string} name The name of the instance to retrieve.
       
  3195  * @return {*} The instance.
       
  3196  */
       
  3197 
       
  3198 /**
       
  3199  * @ngdoc method
       
  3200  * @name $injector#invoke
       
  3201  *
       
  3202  * @description
       
  3203  * Invoke the method and supply the method arguments from the `$injector`.
       
  3204  *
       
  3205  * @param {!Function} fn The function to invoke. Function parameters are injected according to the
       
  3206  *   {@link guide/di $inject Annotation} rules.
       
  3207  * @param {Object=} self The `this` for the invoked method.
       
  3208  * @param {Object=} locals Optional object. If preset then any argument names are read from this
       
  3209  *                         object first, before the `$injector` is consulted.
       
  3210  * @returns {*} the value returned by the invoked `fn` function.
       
  3211  */
       
  3212 
       
  3213 /**
       
  3214  * @ngdoc method
       
  3215  * @name $injector#has
       
  3216  *
       
  3217  * @description
       
  3218  * Allows the user to query if the particular service exist.
       
  3219  *
       
  3220  * @param {string} Name of the service to query.
       
  3221  * @returns {boolean} returns true if injector has given service.
       
  3222  */
       
  3223 
       
  3224 /**
       
  3225  * @ngdoc method
       
  3226  * @name $injector#instantiate
       
  3227  * @description
       
  3228  * Create a new instance of JS type. The method takes a constructor function invokes the new
       
  3229  * operator and supplies all of the arguments to the constructor function as specified by the
       
  3230  * constructor annotation.
       
  3231  *
       
  3232  * @param {Function} Type Annotated constructor function.
       
  3233  * @param {Object=} locals Optional object. If preset then any argument names are read from this
       
  3234  * object first, before the `$injector` is consulted.
       
  3235  * @returns {Object} new instance of `Type`.
       
  3236  */
       
  3237 
       
  3238 /**
       
  3239  * @ngdoc method
       
  3240  * @name $injector#annotate
       
  3241  *
       
  3242  * @description
       
  3243  * Returns an array of service names which the function is requesting for injection. This API is
       
  3244  * used by the injector to determine which services need to be injected into the function when the
       
  3245  * function is invoked. There are three ways in which the function can be annotated with the needed
       
  3246  * dependencies.
       
  3247  *
       
  3248  * # Argument names
       
  3249  *
       
  3250  * The simplest form is to extract the dependencies from the arguments of the function. This is done
       
  3251  * by converting the function into a string using `toString()` method and extracting the argument
       
  3252  * names.
       
  3253  * ```js
       
  3254  *   // Given
       
  3255  *   function MyController($scope, $route) {
       
  3256  *     // ...
       
  3257  *   }
       
  3258  *
       
  3259  *   // Then
       
  3260  *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
       
  3261  * ```
       
  3262  *
       
  3263  * This method does not work with code minification / obfuscation. For this reason the following
       
  3264  * annotation strategies are supported.
       
  3265  *
       
  3266  * # The `$inject` property
       
  3267  *
       
  3268  * If a function has an `$inject` property and its value is an array of strings, then the strings
       
  3269  * represent names of services to be injected into the function.
       
  3270  * ```js
       
  3271  *   // Given
       
  3272  *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
       
  3273  *     // ...
       
  3274  *   }
       
  3275  *   // Define function dependencies
       
  3276  *   MyController['$inject'] = ['$scope', '$route'];
       
  3277  *
       
  3278  *   // Then
       
  3279  *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
       
  3280  * ```
       
  3281  *
       
  3282  * # The array notation
       
  3283  *
       
  3284  * It is often desirable to inline Injected functions and that's when setting the `$inject` property
       
  3285  * is very inconvenient. In these situations using the array notation to specify the dependencies in
       
  3286  * a way that survives minification is a better choice:
       
  3287  *
       
  3288  * ```js
       
  3289  *   // We wish to write this (not minification / obfuscation safe)
       
  3290  *   injector.invoke(function($compile, $rootScope) {
       
  3291  *     // ...
       
  3292  *   });
       
  3293  *
       
  3294  *   // We are forced to write break inlining
       
  3295  *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
       
  3296  *     // ...
       
  3297  *   };
       
  3298  *   tmpFn.$inject = ['$compile', '$rootScope'];
       
  3299  *   injector.invoke(tmpFn);
       
  3300  *
       
  3301  *   // To better support inline function the inline annotation is supported
       
  3302  *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
       
  3303  *     // ...
       
  3304  *   }]);
       
  3305  *
       
  3306  *   // Therefore
       
  3307  *   expect(injector.annotate(
       
  3308  *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
       
  3309  *    ).toEqual(['$compile', '$rootScope']);
       
  3310  * ```
       
  3311  *
       
  3312  * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
       
  3313  * be retrieved as described above.
       
  3314  *
       
  3315  * @returns {Array.<string>} The names of the services which the function requires.
       
  3316  */
       
  3317 
       
  3318 
       
  3319 
       
  3320 
       
  3321 /**
       
  3322  * @ngdoc object
       
  3323  * @name $provide
       
  3324  *
       
  3325  * @description
       
  3326  *
       
  3327  * The {@link auto.$provide $provide} service has a number of methods for registering components
       
  3328  * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
       
  3329  * {@link angular.Module}.
       
  3330  *
       
  3331  * An Angular **service** is a singleton object created by a **service factory**.  These **service
       
  3332  * factories** are functions which, in turn, are created by a **service provider**.
       
  3333  * The **service providers** are constructor functions. When instantiated they must contain a
       
  3334  * property called `$get`, which holds the **service factory** function.
       
  3335  *
       
  3336  * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
       
  3337  * correct **service provider**, instantiating it and then calling its `$get` **service factory**
       
  3338  * function to get the instance of the **service**.
       
  3339  *
       
  3340  * Often services have no configuration options and there is no need to add methods to the service
       
  3341  * provider.  The provider will be no more than a constructor function with a `$get` property. For
       
  3342  * these cases the {@link auto.$provide $provide} service has additional helper methods to register
       
  3343  * services without specifying a provider.
       
  3344  *
       
  3345  * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
       
  3346  *     {@link auto.$injector $injector}
       
  3347  * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
       
  3348  *     providers and services.
       
  3349  * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
       
  3350  *     services, not providers.
       
  3351  * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
       
  3352  *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
       
  3353  *     given factory function.
       
  3354  * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
       
  3355  *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
       
  3356  *      a new object using the given constructor function.
       
  3357  *
       
  3358  * See the individual methods for more information and examples.
       
  3359  */
       
  3360 
       
  3361 /**
       
  3362  * @ngdoc method
       
  3363  * @name $provide#provider
       
  3364  * @description
       
  3365  *
       
  3366  * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
       
  3367  * are constructor functions, whose instances are responsible for "providing" a factory for a
       
  3368  * service.
       
  3369  *
       
  3370  * Service provider names start with the name of the service they provide followed by `Provider`.
       
  3371  * For example, the {@link ng.$log $log} service has a provider called
       
  3372  * {@link ng.$logProvider $logProvider}.
       
  3373  *
       
  3374  * Service provider objects can have additional methods which allow configuration of the provider
       
  3375  * and its service. Importantly, you can configure what kind of service is created by the `$get`
       
  3376  * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
       
  3377  * method {@link ng.$logProvider#debugEnabled debugEnabled}
       
  3378  * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
       
  3379  * console or not.
       
  3380  *
       
  3381  * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
       
  3382                         'Provider'` key.
       
  3383  * @param {(Object|function())} provider If the provider is:
       
  3384  *
       
  3385  *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
       
  3386  *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
       
  3387  *   - `Constructor`: a new instance of the provider will be created using
       
  3388  *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
       
  3389  *
       
  3390  * @returns {Object} registered provider instance
       
  3391 
       
  3392  * @example
       
  3393  *
       
  3394  * The following example shows how to create a simple event tracking service and register it using
       
  3395  * {@link auto.$provide#provider $provide.provider()}.
       
  3396  *
       
  3397  * ```js
       
  3398  *  // Define the eventTracker provider
       
  3399  *  function EventTrackerProvider() {
       
  3400  *    var trackingUrl = '/track';
       
  3401  *
       
  3402  *    // A provider method for configuring where the tracked events should been saved
       
  3403  *    this.setTrackingUrl = function(url) {
       
  3404  *      trackingUrl = url;
       
  3405  *    };
       
  3406  *
       
  3407  *    // The service factory function
       
  3408  *    this.$get = ['$http', function($http) {
       
  3409  *      var trackedEvents = {};
       
  3410  *      return {
       
  3411  *        // Call this to track an event
       
  3412  *        event: function(event) {
       
  3413  *          var count = trackedEvents[event] || 0;
       
  3414  *          count += 1;
       
  3415  *          trackedEvents[event] = count;
       
  3416  *          return count;
       
  3417  *        },
       
  3418  *        // Call this to save the tracked events to the trackingUrl
       
  3419  *        save: function() {
       
  3420  *          $http.post(trackingUrl, trackedEvents);
       
  3421  *        }
       
  3422  *      };
       
  3423  *    }];
       
  3424  *  }
       
  3425  *
       
  3426  *  describe('eventTracker', function() {
       
  3427  *    var postSpy;
       
  3428  *
       
  3429  *    beforeEach(module(function($provide) {
       
  3430  *      // Register the eventTracker provider
       
  3431  *      $provide.provider('eventTracker', EventTrackerProvider);
       
  3432  *    }));
       
  3433  *
       
  3434  *    beforeEach(module(function(eventTrackerProvider) {
       
  3435  *      // Configure eventTracker provider
       
  3436  *      eventTrackerProvider.setTrackingUrl('/custom-track');
       
  3437  *    }));
       
  3438  *
       
  3439  *    it('tracks events', inject(function(eventTracker) {
       
  3440  *      expect(eventTracker.event('login')).toEqual(1);
       
  3441  *      expect(eventTracker.event('login')).toEqual(2);
       
  3442  *    }));
       
  3443  *
       
  3444  *    it('saves to the tracking url', inject(function(eventTracker, $http) {
       
  3445  *      postSpy = spyOn($http, 'post');
       
  3446  *      eventTracker.event('login');
       
  3447  *      eventTracker.save();
       
  3448  *      expect(postSpy).toHaveBeenCalled();
       
  3449  *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
       
  3450  *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
       
  3451  *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
       
  3452  *    }));
       
  3453  *  });
       
  3454  * ```
       
  3455  */
       
  3456 
       
  3457 /**
       
  3458  * @ngdoc method
       
  3459  * @name $provide#factory
       
  3460  * @description
       
  3461  *
       
  3462  * Register a **service factory**, which will be called to return the service instance.
       
  3463  * This is short for registering a service where its provider consists of only a `$get` property,
       
  3464  * which is the given service factory function.
       
  3465  * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
       
  3466  * configure your service in a provider.
       
  3467  *
       
  3468  * @param {string} name The name of the instance.
       
  3469  * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand
       
  3470  *                            for `$provide.provider(name, {$get: $getFn})`.
       
  3471  * @returns {Object} registered provider instance
       
  3472  *
       
  3473  * @example
       
  3474  * Here is an example of registering a service
       
  3475  * ```js
       
  3476  *   $provide.factory('ping', ['$http', function($http) {
       
  3477  *     return function ping() {
       
  3478  *       return $http.send('/ping');
       
  3479  *     };
       
  3480  *   }]);
       
  3481  * ```
       
  3482  * You would then inject and use this service like this:
       
  3483  * ```js
       
  3484  *   someModule.controller('Ctrl', ['ping', function(ping) {
       
  3485  *     ping();
       
  3486  *   }]);
       
  3487  * ```
       
  3488  */
       
  3489 
       
  3490 
       
  3491 /**
       
  3492  * @ngdoc method
       
  3493  * @name $provide#service
       
  3494  * @description
       
  3495  *
       
  3496  * Register a **service constructor**, which will be invoked with `new` to create the service
       
  3497  * instance.
       
  3498  * This is short for registering a service where its provider's `$get` property is the service
       
  3499  * constructor function that will be used to instantiate the service instance.
       
  3500  *
       
  3501  * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
       
  3502  * as a type/class.
       
  3503  *
       
  3504  * @param {string} name The name of the instance.
       
  3505  * @param {Function} constructor A class (constructor function) that will be instantiated.
       
  3506  * @returns {Object} registered provider instance
       
  3507  *
       
  3508  * @example
       
  3509  * Here is an example of registering a service using
       
  3510  * {@link auto.$provide#service $provide.service(class)}.
       
  3511  * ```js
       
  3512  *   var Ping = function($http) {
       
  3513  *     this.$http = $http;
       
  3514  *   };
       
  3515  *
       
  3516  *   Ping.$inject = ['$http'];
       
  3517  *
       
  3518  *   Ping.prototype.send = function() {
       
  3519  *     return this.$http.get('/ping');
       
  3520  *   };
       
  3521  *   $provide.service('ping', Ping);
       
  3522  * ```
       
  3523  * You would then inject and use this service like this:
       
  3524  * ```js
       
  3525  *   someModule.controller('Ctrl', ['ping', function(ping) {
       
  3526  *     ping.send();
       
  3527  *   }]);
       
  3528  * ```
       
  3529  */
       
  3530 
       
  3531 
       
  3532 /**
       
  3533  * @ngdoc method
       
  3534  * @name $provide#value
       
  3535  * @description
       
  3536  *
       
  3537  * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
       
  3538  * number, an array, an object or a function.  This is short for registering a service where its
       
  3539  * provider's `$get` property is a factory function that takes no arguments and returns the **value
       
  3540  * service**.
       
  3541  *
       
  3542  * Value services are similar to constant services, except that they cannot be injected into a
       
  3543  * module configuration function (see {@link angular.Module#config}) but they can be overridden by
       
  3544  * an Angular
       
  3545  * {@link auto.$provide#decorator decorator}.
       
  3546  *
       
  3547  * @param {string} name The name of the instance.
       
  3548  * @param {*} value The value.
       
  3549  * @returns {Object} registered provider instance
       
  3550  *
       
  3551  * @example
       
  3552  * Here are some examples of creating value services.
       
  3553  * ```js
       
  3554  *   $provide.value('ADMIN_USER', 'admin');
       
  3555  *
       
  3556  *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
       
  3557  *
       
  3558  *   $provide.value('halfOf', function(value) {
       
  3559  *     return value / 2;
       
  3560  *   });
       
  3561  * ```
       
  3562  */
       
  3563 
       
  3564 
       
  3565 /**
       
  3566  * @ngdoc method
       
  3567  * @name $provide#constant
       
  3568  * @description
       
  3569  *
       
  3570  * Register a **constant service**, such as a string, a number, an array, an object or a function,
       
  3571  * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
       
  3572  * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
       
  3573  * be overridden by an Angular {@link auto.$provide#decorator decorator}.
       
  3574  *
       
  3575  * @param {string} name The name of the constant.
       
  3576  * @param {*} value The constant value.
       
  3577  * @returns {Object} registered instance
       
  3578  *
       
  3579  * @example
       
  3580  * Here a some examples of creating constants:
       
  3581  * ```js
       
  3582  *   $provide.constant('SHARD_HEIGHT', 306);
       
  3583  *
       
  3584  *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
       
  3585  *
       
  3586  *   $provide.constant('double', function(value) {
       
  3587  *     return value * 2;
       
  3588  *   });
       
  3589  * ```
       
  3590  */
       
  3591 
       
  3592 
       
  3593 /**
       
  3594  * @ngdoc method
       
  3595  * @name $provide#decorator
       
  3596  * @description
       
  3597  *
       
  3598  * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
       
  3599  * intercepts the creation of a service, allowing it to override or modify the behaviour of the
       
  3600  * service. The object returned by the decorator may be the original service, or a new service
       
  3601  * object which replaces or wraps and delegates to the original service.
       
  3602  *
       
  3603  * @param {string} name The name of the service to decorate.
       
  3604  * @param {function()} decorator This function will be invoked when the service needs to be
       
  3605  *    instantiated and should return the decorated service instance. The function is called using
       
  3606  *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
       
  3607  *    Local injection arguments:
       
  3608  *
       
  3609  *    * `$delegate` - The original service instance, which can be monkey patched, configured,
       
  3610  *      decorated or delegated to.
       
  3611  *
       
  3612  * @example
       
  3613  * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
       
  3614  * calls to {@link ng.$log#error $log.warn()}.
       
  3615  * ```js
       
  3616  *   $provide.decorator('$log', ['$delegate', function($delegate) {
       
  3617  *     $delegate.warn = $delegate.error;
       
  3618  *     return $delegate;
       
  3619  *   }]);
       
  3620  * ```
       
  3621  */
       
  3622 
       
  3623 
       
  3624 function createInjector(modulesToLoad) {
       
  3625   var INSTANTIATING = {},
       
  3626       providerSuffix = 'Provider',
       
  3627       path = [],
       
  3628       loadedModules = new HashMap(),
       
  3629       providerCache = {
       
  3630         $provide: {
       
  3631             provider: supportObject(provider),
       
  3632             factory: supportObject(factory),
       
  3633             service: supportObject(service),
       
  3634             value: supportObject(value),
       
  3635             constant: supportObject(constant),
       
  3636             decorator: decorator
       
  3637           }
       
  3638       },
       
  3639       providerInjector = (providerCache.$injector =
       
  3640           createInternalInjector(providerCache, function() {
       
  3641             throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
       
  3642           })),
       
  3643       instanceCache = {},
       
  3644       instanceInjector = (instanceCache.$injector =
       
  3645           createInternalInjector(instanceCache, function(servicename) {
       
  3646             var provider = providerInjector.get(servicename + providerSuffix);
       
  3647             return instanceInjector.invoke(provider.$get, provider);
       
  3648           }));
       
  3649 
       
  3650 
       
  3651   forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });
       
  3652 
       
  3653   return instanceInjector;
       
  3654 
       
  3655   ////////////////////////////////////
       
  3656   // $provider
       
  3657   ////////////////////////////////////
       
  3658 
       
  3659   function supportObject(delegate) {
       
  3660     return function(key, value) {
       
  3661       if (isObject(key)) {
       
  3662         forEach(key, reverseParams(delegate));
       
  3663       } else {
       
  3664         return delegate(key, value);
       
  3665       }
       
  3666     };
       
  3667   }
       
  3668 
       
  3669   function provider(name, provider_) {
       
  3670     assertNotHasOwnProperty(name, 'service');
       
  3671     if (isFunction(provider_) || isArray(provider_)) {
       
  3672       provider_ = providerInjector.instantiate(provider_);
       
  3673     }
       
  3674     if (!provider_.$get) {
       
  3675       throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
       
  3676     }
       
  3677     return providerCache[name + providerSuffix] = provider_;
       
  3678   }
       
  3679 
       
  3680   function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); }
       
  3681 
       
  3682   function service(name, constructor) {
       
  3683     return factory(name, ['$injector', function($injector) {
       
  3684       return $injector.instantiate(constructor);
       
  3685     }]);
       
  3686   }
       
  3687 
       
  3688   function value(name, val) { return factory(name, valueFn(val)); }
       
  3689 
       
  3690   function constant(name, value) {
       
  3691     assertNotHasOwnProperty(name, 'constant');
       
  3692     providerCache[name] = value;
       
  3693     instanceCache[name] = value;
       
  3694   }
       
  3695 
       
  3696   function decorator(serviceName, decorFn) {
       
  3697     var origProvider = providerInjector.get(serviceName + providerSuffix),
       
  3698         orig$get = origProvider.$get;
       
  3699 
       
  3700     origProvider.$get = function() {
       
  3701       var origInstance = instanceInjector.invoke(orig$get, origProvider);
       
  3702       return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
       
  3703     };
       
  3704   }
       
  3705 
       
  3706   ////////////////////////////////////
       
  3707   // Module Loading
       
  3708   ////////////////////////////////////
       
  3709   function loadModules(modulesToLoad){
       
  3710     var runBlocks = [], moduleFn, invokeQueue, i, ii;
       
  3711     forEach(modulesToLoad, function(module) {
       
  3712       if (loadedModules.get(module)) return;
       
  3713       loadedModules.put(module, true);
       
  3714 
       
  3715       try {
       
  3716         if (isString(module)) {
       
  3717           moduleFn = angularModule(module);
       
  3718           runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
       
  3719 
       
  3720           for(invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) {
       
  3721             var invokeArgs = invokeQueue[i],
       
  3722                 provider = providerInjector.get(invokeArgs[0]);
       
  3723 
       
  3724             provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
       
  3725           }
       
  3726         } else if (isFunction(module)) {
       
  3727             runBlocks.push(providerInjector.invoke(module));
       
  3728         } else if (isArray(module)) {
       
  3729             runBlocks.push(providerInjector.invoke(module));
       
  3730         } else {
       
  3731           assertArgFn(module, 'module');
       
  3732         }
       
  3733       } catch (e) {
       
  3734         if (isArray(module)) {
       
  3735           module = module[module.length - 1];
       
  3736         }
       
  3737         if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
       
  3738           // Safari & FF's stack traces don't contain error.message content
       
  3739           // unlike those of Chrome and IE
       
  3740           // So if stack doesn't contain message, we create a new string that contains both.
       
  3741           // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
       
  3742           /* jshint -W022 */
       
  3743           e = e.message + '\n' + e.stack;
       
  3744         }
       
  3745         throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
       
  3746                   module, e.stack || e.message || e);
       
  3747       }
       
  3748     });
       
  3749     return runBlocks;
       
  3750   }
       
  3751 
       
  3752   ////////////////////////////////////
       
  3753   // internal Injector
       
  3754   ////////////////////////////////////
       
  3755 
       
  3756   function createInternalInjector(cache, factory) {
       
  3757 
       
  3758     function getService(serviceName) {
       
  3759       if (cache.hasOwnProperty(serviceName)) {
       
  3760         if (cache[serviceName] === INSTANTIATING) {
       
  3761           throw $injectorMinErr('cdep', 'Circular dependency found: {0}', path.join(' <- '));
       
  3762         }
       
  3763         return cache[serviceName];
       
  3764       } else {
       
  3765         try {
       
  3766           path.unshift(serviceName);
       
  3767           cache[serviceName] = INSTANTIATING;
       
  3768           return cache[serviceName] = factory(serviceName);
       
  3769         } catch (err) {
       
  3770           if (cache[serviceName] === INSTANTIATING) {
       
  3771             delete cache[serviceName];
       
  3772           }
       
  3773           throw err;
       
  3774         } finally {
       
  3775           path.shift();
       
  3776         }
       
  3777       }
       
  3778     }
       
  3779 
       
  3780     function invoke(fn, self, locals){
       
  3781       var args = [],
       
  3782           $inject = annotate(fn),
       
  3783           length, i,
       
  3784           key;
       
  3785 
       
  3786       for(i = 0, length = $inject.length; i < length; i++) {
       
  3787         key = $inject[i];
       
  3788         if (typeof key !== 'string') {
       
  3789           throw $injectorMinErr('itkn',
       
  3790                   'Incorrect injection token! Expected service name as string, got {0}', key);
       
  3791         }
       
  3792         args.push(
       
  3793           locals && locals.hasOwnProperty(key)
       
  3794           ? locals[key]
       
  3795           : getService(key)
       
  3796         );
       
  3797       }
       
  3798       if (!fn.$inject) {
       
  3799         // this means that we must be an array.
       
  3800         fn = fn[length];
       
  3801       }
       
  3802 
       
  3803       // http://jsperf.com/angularjs-invoke-apply-vs-switch
       
  3804       // #5388
       
  3805       return fn.apply(self, args);
       
  3806     }
       
  3807 
       
  3808     function instantiate(Type, locals) {
       
  3809       var Constructor = function() {},
       
  3810           instance, returnedValue;
       
  3811 
       
  3812       // Check if Type is annotated and use just the given function at n-1 as parameter
       
  3813       // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
       
  3814       Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
       
  3815       instance = new Constructor();
       
  3816       returnedValue = invoke(Type, instance, locals);
       
  3817 
       
  3818       return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
       
  3819     }
       
  3820 
       
  3821     return {
       
  3822       invoke: invoke,
       
  3823       instantiate: instantiate,
       
  3824       get: getService,
       
  3825       annotate: annotate,
       
  3826       has: function(name) {
       
  3827         return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
       
  3828       }
       
  3829     };
       
  3830   }
       
  3831 }
       
  3832 
       
  3833 /**
       
  3834  * @ngdoc service
       
  3835  * @name $anchorScroll
       
  3836  * @kind function
       
  3837  * @requires $window
       
  3838  * @requires $location
       
  3839  * @requires $rootScope
       
  3840  *
       
  3841  * @description
       
  3842  * When called, it checks current value of `$location.hash()` and scroll to related element,
       
  3843  * according to rules specified in
       
  3844  * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
       
  3845  *
       
  3846  * It also watches the `$location.hash()` and scrolls whenever it changes to match any anchor.
       
  3847  * This can be disabled by calling `$anchorScrollProvider.disableAutoScrolling()`.
       
  3848  *
       
  3849  * @example
       
  3850    <example>
       
  3851      <file name="index.html">
       
  3852        <div id="scrollArea" ng-controller="ScrollCtrl">
       
  3853          <a ng-click="gotoBottom()">Go to bottom</a>
       
  3854          <a id="bottom"></a> You're at the bottom!
       
  3855        </div>
       
  3856      </file>
       
  3857      <file name="script.js">
       
  3858        function ScrollCtrl($scope, $location, $anchorScroll) {
       
  3859          $scope.gotoBottom = function (){
       
  3860            // set the location.hash to the id of
       
  3861            // the element you wish to scroll to.
       
  3862            $location.hash('bottom');
       
  3863 
       
  3864            // call $anchorScroll()
       
  3865            $anchorScroll();
       
  3866          };
       
  3867        }
       
  3868      </file>
       
  3869      <file name="style.css">
       
  3870        #scrollArea {
       
  3871          height: 350px;
       
  3872          overflow: auto;
       
  3873        }
       
  3874 
       
  3875        #bottom {
       
  3876          display: block;
       
  3877          margin-top: 2000px;
       
  3878        }
       
  3879      </file>
       
  3880    </example>
       
  3881  */
       
  3882 function $AnchorScrollProvider() {
       
  3883 
       
  3884   var autoScrollingEnabled = true;
       
  3885 
       
  3886   this.disableAutoScrolling = function() {
       
  3887     autoScrollingEnabled = false;
       
  3888   };
       
  3889 
       
  3890   this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
       
  3891     var document = $window.document;
       
  3892 
       
  3893     // helper function to get first anchor from a NodeList
       
  3894     // can't use filter.filter, as it accepts only instances of Array
       
  3895     // and IE can't convert NodeList to an array using [].slice
       
  3896     // TODO(vojta): use filter if we change it to accept lists as well
       
  3897     function getFirstAnchor(list) {
       
  3898       var result = null;
       
  3899       forEach(list, function(element) {
       
  3900         if (!result && lowercase(element.nodeName) === 'a') result = element;
       
  3901       });
       
  3902       return result;
       
  3903     }
       
  3904 
       
  3905     function scroll() {
       
  3906       var hash = $location.hash(), elm;
       
  3907 
       
  3908       // empty hash, scroll to the top of the page
       
  3909       if (!hash) $window.scrollTo(0, 0);
       
  3910 
       
  3911       // element with given id
       
  3912       else if ((elm = document.getElementById(hash))) elm.scrollIntoView();
       
  3913 
       
  3914       // first anchor with given name :-D
       
  3915       else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) elm.scrollIntoView();
       
  3916 
       
  3917       // no element and hash == 'top', scroll to the top of the page
       
  3918       else if (hash === 'top') $window.scrollTo(0, 0);
       
  3919     }
       
  3920 
       
  3921     // does not scroll when user clicks on anchor link that is currently on
       
  3922     // (no url change, no $location.hash() change), browser native does scroll
       
  3923     if (autoScrollingEnabled) {
       
  3924       $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
       
  3925         function autoScrollWatchAction() {
       
  3926           $rootScope.$evalAsync(scroll);
       
  3927         });
       
  3928     }
       
  3929 
       
  3930     return scroll;
       
  3931   }];
       
  3932 }
       
  3933 
       
  3934 var $animateMinErr = minErr('$animate');
       
  3935 
       
  3936 /**
       
  3937  * @ngdoc provider
       
  3938  * @name $animateProvider
       
  3939  *
       
  3940  * @description
       
  3941  * Default implementation of $animate that doesn't perform any animations, instead just
       
  3942  * synchronously performs DOM
       
  3943  * updates and calls done() callbacks.
       
  3944  *
       
  3945  * In order to enable animations the ngAnimate module has to be loaded.
       
  3946  *
       
  3947  * To see the functional implementation check out src/ngAnimate/animate.js
       
  3948  */
       
  3949 var $AnimateProvider = ['$provide', function($provide) {
       
  3950 
       
  3951 
       
  3952   this.$$selectors = {};
       
  3953 
       
  3954 
       
  3955   /**
       
  3956    * @ngdoc method
       
  3957    * @name $animateProvider#register
       
  3958    *
       
  3959    * @description
       
  3960    * Registers a new injectable animation factory function. The factory function produces the
       
  3961    * animation object which contains callback functions for each event that is expected to be
       
  3962    * animated.
       
  3963    *
       
  3964    *   * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction`
       
  3965    *   must be called once the element animation is complete. If a function is returned then the
       
  3966    *   animation service will use this function to cancel the animation whenever a cancel event is
       
  3967    *   triggered.
       
  3968    *
       
  3969    *
       
  3970    * ```js
       
  3971    *   return {
       
  3972      *     eventFn : function(element, done) {
       
  3973      *       //code to run the animation
       
  3974      *       //once complete, then run done()
       
  3975      *       return function cancellationFunction() {
       
  3976      *         //code to cancel the animation
       
  3977      *       }
       
  3978      *     }
       
  3979      *   }
       
  3980    * ```
       
  3981    *
       
  3982    * @param {string} name The name of the animation.
       
  3983    * @param {Function} factory The factory function that will be executed to return the animation
       
  3984    *                           object.
       
  3985    */
       
  3986   this.register = function(name, factory) {
       
  3987     var key = name + '-animation';
       
  3988     if (name && name.charAt(0) != '.') throw $animateMinErr('notcsel',
       
  3989         "Expecting class selector starting with '.' got '{0}'.", name);
       
  3990     this.$$selectors[name.substr(1)] = key;
       
  3991     $provide.factory(key, factory);
       
  3992   };
       
  3993 
       
  3994   /**
       
  3995    * @ngdoc method
       
  3996    * @name $animateProvider#classNameFilter
       
  3997    *
       
  3998    * @description
       
  3999    * Sets and/or returns the CSS class regular expression that is checked when performing
       
  4000    * an animation. Upon bootstrap the classNameFilter value is not set at all and will
       
  4001    * therefore enable $animate to attempt to perform an animation on any element.
       
  4002    * When setting the classNameFilter value, animations will only be performed on elements
       
  4003    * that successfully match the filter expression. This in turn can boost performance
       
  4004    * for low-powered devices as well as applications containing a lot of structural operations.
       
  4005    * @param {RegExp=} expression The className expression which will be checked against all animations
       
  4006    * @return {RegExp} The current CSS className expression value. If null then there is no expression value
       
  4007    */
       
  4008   this.classNameFilter = function(expression) {
       
  4009     if(arguments.length === 1) {
       
  4010       this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
       
  4011     }
       
  4012     return this.$$classNameFilter;
       
  4013   };
       
  4014 
       
  4015   this.$get = ['$timeout', '$$asyncCallback', function($timeout, $$asyncCallback) {
       
  4016 
       
  4017     function async(fn) {
       
  4018       fn && $$asyncCallback(fn);
       
  4019     }
       
  4020 
       
  4021     /**
       
  4022      *
       
  4023      * @ngdoc service
       
  4024      * @name $animate
       
  4025      * @description The $animate service provides rudimentary DOM manipulation functions to
       
  4026      * insert, remove and move elements within the DOM, as well as adding and removing classes.
       
  4027      * This service is the core service used by the ngAnimate $animator service which provides
       
  4028      * high-level animation hooks for CSS and JavaScript.
       
  4029      *
       
  4030      * $animate is available in the AngularJS core, however, the ngAnimate module must be included
       
  4031      * to enable full out animation support. Otherwise, $animate will only perform simple DOM
       
  4032      * manipulation operations.
       
  4033      *
       
  4034      * To learn more about enabling animation support, click here to visit the {@link ngAnimate
       
  4035      * ngAnimate module page} as well as the {@link ngAnimate.$animate ngAnimate $animate service
       
  4036      * page}.
       
  4037      */
       
  4038     return {
       
  4039 
       
  4040       /**
       
  4041        *
       
  4042        * @ngdoc method
       
  4043        * @name $animate#enter
       
  4044        * @function
       
  4045        * @description Inserts the element into the DOM either after the `after` element or within
       
  4046        *   the `parent` element. Once complete, the done() callback will be fired (if provided).
       
  4047        * @param {DOMElement} element the element which will be inserted into the DOM
       
  4048        * @param {DOMElement} parent the parent element which will append the element as
       
  4049        *   a child (if the after element is not present)
       
  4050        * @param {DOMElement} after the sibling element which will append the element
       
  4051        *   after itself
       
  4052        * @param {Function=} done callback function that will be called after the element has been
       
  4053        *   inserted into the DOM
       
  4054        */
       
  4055       enter : function(element, parent, after, done) {
       
  4056         if (after) {
       
  4057           after.after(element);
       
  4058         } else {
       
  4059           if (!parent || !parent[0]) {
       
  4060             parent = after.parent();
       
  4061           }
       
  4062           parent.append(element);
       
  4063         }
       
  4064         async(done);
       
  4065       },
       
  4066 
       
  4067       /**
       
  4068        *
       
  4069        * @ngdoc method
       
  4070        * @name $animate#leave
       
  4071        * @function
       
  4072        * @description Removes the element from the DOM. Once complete, the done() callback will be
       
  4073        *   fired (if provided).
       
  4074        * @param {DOMElement} element the element which will be removed from the DOM
       
  4075        * @param {Function=} done callback function that will be called after the element has been
       
  4076        *   removed from the DOM
       
  4077        */
       
  4078       leave : function(element, done) {
       
  4079         element.remove();
       
  4080         async(done);
       
  4081       },
       
  4082 
       
  4083       /**
       
  4084        *
       
  4085        * @ngdoc method
       
  4086        * @name $animate#move
       
  4087        * @function
       
  4088        * @description Moves the position of the provided element within the DOM to be placed
       
  4089        * either after the `after` element or inside of the `parent` element. Once complete, the
       
  4090        * done() callback will be fired (if provided).
       
  4091        *
       
  4092        * @param {DOMElement} element the element which will be moved around within the
       
  4093        *   DOM
       
  4094        * @param {DOMElement} parent the parent element where the element will be
       
  4095        *   inserted into (if the after element is not present)
       
  4096        * @param {DOMElement} after the sibling element where the element will be
       
  4097        *   positioned next to
       
  4098        * @param {Function=} done the callback function (if provided) that will be fired after the
       
  4099        *   element has been moved to its new position
       
  4100        */
       
  4101       move : function(element, parent, after, done) {
       
  4102         // Do not remove element before insert. Removing will cause data associated with the
       
  4103         // element to be dropped. Insert will implicitly do the remove.
       
  4104         this.enter(element, parent, after, done);
       
  4105       },
       
  4106 
       
  4107       /**
       
  4108        *
       
  4109        * @ngdoc method
       
  4110        * @name $animate#addClass
       
  4111        * @function
       
  4112        * @description Adds the provided className CSS class value to the provided element. Once
       
  4113        * complete, the done() callback will be fired (if provided).
       
  4114        * @param {DOMElement} element the element which will have the className value
       
  4115        *   added to it
       
  4116        * @param {string} className the CSS class which will be added to the element
       
  4117        * @param {Function=} done the callback function (if provided) that will be fired after the
       
  4118        *   className value has been added to the element
       
  4119        */
       
  4120       addClass : function(element, className, done) {
       
  4121         className = isString(className) ?
       
  4122                       className :
       
  4123                       isArray(className) ? className.join(' ') : '';
       
  4124         forEach(element, function (element) {
       
  4125           jqLiteAddClass(element, className);
       
  4126         });
       
  4127         async(done);
       
  4128       },
       
  4129 
       
  4130       /**
       
  4131        *
       
  4132        * @ngdoc method
       
  4133        * @name $animate#removeClass
       
  4134        * @function
       
  4135        * @description Removes the provided className CSS class value from the provided element.
       
  4136        * Once complete, the done() callback will be fired (if provided).
       
  4137        * @param {DOMElement} element the element which will have the className value
       
  4138        *   removed from it
       
  4139        * @param {string} className the CSS class which will be removed from the element
       
  4140        * @param {Function=} done the callback function (if provided) that will be fired after the
       
  4141        *   className value has been removed from the element
       
  4142        */
       
  4143       removeClass : function(element, className, done) {
       
  4144         className = isString(className) ?
       
  4145                       className :
       
  4146                       isArray(className) ? className.join(' ') : '';
       
  4147         forEach(element, function (element) {
       
  4148           jqLiteRemoveClass(element, className);
       
  4149         });
       
  4150         async(done);
       
  4151       },
       
  4152 
       
  4153       /**
       
  4154        *
       
  4155        * @ngdoc method
       
  4156        * @name $animate#setClass
       
  4157        * @function
       
  4158        * @description Adds and/or removes the given CSS classes to and from the element.
       
  4159        * Once complete, the done() callback will be fired (if provided).
       
  4160        * @param {DOMElement} element the element which will it's CSS classes changed
       
  4161        *   removed from it
       
  4162        * @param {string} add the CSS classes which will be added to the element
       
  4163        * @param {string} remove the CSS class which will be removed from the element
       
  4164        * @param {Function=} done the callback function (if provided) that will be fired after the
       
  4165        *   CSS classes have been set on the element
       
  4166        */
       
  4167       setClass : function(element, add, remove, done) {
       
  4168         forEach(element, function (element) {
       
  4169           jqLiteAddClass(element, add);
       
  4170           jqLiteRemoveClass(element, remove);
       
  4171         });
       
  4172         async(done);
       
  4173       },
       
  4174 
       
  4175       enabled : noop
       
  4176     };
       
  4177   }];
       
  4178 }];
       
  4179 
       
  4180 function $$AsyncCallbackProvider(){
       
  4181   this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) {
       
  4182     return $$rAF.supported
       
  4183       ? function(fn) { return $$rAF(fn); }
       
  4184       : function(fn) {
       
  4185         return $timeout(fn, 0, false);
       
  4186       };
       
  4187   }];
       
  4188 }
       
  4189 
       
  4190 /**
       
  4191  * ! This is a private undocumented service !
       
  4192  *
       
  4193  * @name $browser
       
  4194  * @requires $log
       
  4195  * @description
       
  4196  * This object has two goals:
       
  4197  *
       
  4198  * - hide all the global state in the browser caused by the window object
       
  4199  * - abstract away all the browser specific features and inconsistencies
       
  4200  *
       
  4201  * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
       
  4202  * service, which can be used for convenient testing of the application without the interaction with
       
  4203  * the real browser apis.
       
  4204  */
       
  4205 /**
       
  4206  * @param {object} window The global window object.
       
  4207  * @param {object} document jQuery wrapped document.
       
  4208  * @param {function()} XHR XMLHttpRequest constructor.
       
  4209  * @param {object} $log console.log or an object with the same interface.
       
  4210  * @param {object} $sniffer $sniffer service
       
  4211  */
       
  4212 function Browser(window, document, $log, $sniffer) {
       
  4213   var self = this,
       
  4214       rawDocument = document[0],
       
  4215       location = window.location,
       
  4216       history = window.history,
       
  4217       setTimeout = window.setTimeout,
       
  4218       clearTimeout = window.clearTimeout,
       
  4219       pendingDeferIds = {};
       
  4220 
       
  4221   self.isMock = false;
       
  4222 
       
  4223   var outstandingRequestCount = 0;
       
  4224   var outstandingRequestCallbacks = [];
       
  4225 
       
  4226   // TODO(vojta): remove this temporary api
       
  4227   self.$$completeOutstandingRequest = completeOutstandingRequest;
       
  4228   self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
       
  4229 
       
  4230   /**
       
  4231    * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
       
  4232    * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
       
  4233    */
       
  4234   function completeOutstandingRequest(fn) {
       
  4235     try {
       
  4236       fn.apply(null, sliceArgs(arguments, 1));
       
  4237     } finally {
       
  4238       outstandingRequestCount--;
       
  4239       if (outstandingRequestCount === 0) {
       
  4240         while(outstandingRequestCallbacks.length) {
       
  4241           try {
       
  4242             outstandingRequestCallbacks.pop()();
       
  4243           } catch (e) {
       
  4244             $log.error(e);
       
  4245           }
       
  4246         }
       
  4247       }
       
  4248     }
       
  4249   }
       
  4250 
       
  4251   /**
       
  4252    * @private
       
  4253    * Note: this method is used only by scenario runner
       
  4254    * TODO(vojta): prefix this method with $$ ?
       
  4255    * @param {function()} callback Function that will be called when no outstanding request
       
  4256    */
       
  4257   self.notifyWhenNoOutstandingRequests = function(callback) {
       
  4258     // force browser to execute all pollFns - this is needed so that cookies and other pollers fire
       
  4259     // at some deterministic time in respect to the test runner's actions. Leaving things up to the
       
  4260     // regular poller would result in flaky tests.
       
  4261     forEach(pollFns, function(pollFn){ pollFn(); });
       
  4262 
       
  4263     if (outstandingRequestCount === 0) {
       
  4264       callback();
       
  4265     } else {
       
  4266       outstandingRequestCallbacks.push(callback);
       
  4267     }
       
  4268   };
       
  4269 
       
  4270   //////////////////////////////////////////////////////////////
       
  4271   // Poll Watcher API
       
  4272   //////////////////////////////////////////////////////////////
       
  4273   var pollFns = [],
       
  4274       pollTimeout;
       
  4275 
       
  4276   /**
       
  4277    * @name $browser#addPollFn
       
  4278    *
       
  4279    * @param {function()} fn Poll function to add
       
  4280    *
       
  4281    * @description
       
  4282    * Adds a function to the list of functions that poller periodically executes,
       
  4283    * and starts polling if not started yet.
       
  4284    *
       
  4285    * @returns {function()} the added function
       
  4286    */
       
  4287   self.addPollFn = function(fn) {
       
  4288     if (isUndefined(pollTimeout)) startPoller(100, setTimeout);
       
  4289     pollFns.push(fn);
       
  4290     return fn;
       
  4291   };
       
  4292 
       
  4293   /**
       
  4294    * @param {number} interval How often should browser call poll functions (ms)
       
  4295    * @param {function()} setTimeout Reference to a real or fake `setTimeout` function.
       
  4296    *
       
  4297    * @description
       
  4298    * Configures the poller to run in the specified intervals, using the specified
       
  4299    * setTimeout fn and kicks it off.
       
  4300    */
       
  4301   function startPoller(interval, setTimeout) {
       
  4302     (function check() {
       
  4303       forEach(pollFns, function(pollFn){ pollFn(); });
       
  4304       pollTimeout = setTimeout(check, interval);
       
  4305     })();
       
  4306   }
       
  4307 
       
  4308   //////////////////////////////////////////////////////////////
       
  4309   // URL API
       
  4310   //////////////////////////////////////////////////////////////
       
  4311 
       
  4312   var lastBrowserUrl = location.href,
       
  4313       baseElement = document.find('base'),
       
  4314       newLocation = null;
       
  4315 
       
  4316   /**
       
  4317    * @name $browser#url
       
  4318    *
       
  4319    * @description
       
  4320    * GETTER:
       
  4321    * Without any argument, this method just returns current value of location.href.
       
  4322    *
       
  4323    * SETTER:
       
  4324    * With at least one argument, this method sets url to new value.
       
  4325    * If html5 history api supported, pushState/replaceState is used, otherwise
       
  4326    * location.href/location.replace is used.
       
  4327    * Returns its own instance to allow chaining
       
  4328    *
       
  4329    * NOTE: this api is intended for use only by the $location service. Please use the
       
  4330    * {@link ng.$location $location service} to change url.
       
  4331    *
       
  4332    * @param {string} url New url (when used as setter)
       
  4333    * @param {boolean=} replace Should new url replace current history record ?
       
  4334    */
       
  4335   self.url = function(url, replace) {
       
  4336     // Android Browser BFCache causes location, history reference to become stale.
       
  4337     if (location !== window.location) location = window.location;
       
  4338     if (history !== window.history) history = window.history;
       
  4339 
       
  4340     // setter
       
  4341     if (url) {
       
  4342       if (lastBrowserUrl == url) return;
       
  4343       lastBrowserUrl = url;
       
  4344       if ($sniffer.history) {
       
  4345         if (replace) history.replaceState(null, '', url);
       
  4346         else {
       
  4347           history.pushState(null, '', url);
       
  4348           // Crazy Opera Bug: http://my.opera.com/community/forums/topic.dml?id=1185462
       
  4349           baseElement.attr('href', baseElement.attr('href'));
       
  4350         }
       
  4351       } else {
       
  4352         newLocation = url;
       
  4353         if (replace) {
       
  4354           location.replace(url);
       
  4355         } else {
       
  4356           location.href = url;
       
  4357         }
       
  4358       }
       
  4359       return self;
       
  4360     // getter
       
  4361     } else {
       
  4362       // - newLocation is a workaround for an IE7-9 issue with location.replace and location.href
       
  4363       //   methods not updating location.href synchronously.
       
  4364       // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
       
  4365       return newLocation || location.href.replace(/%27/g,"'");
       
  4366     }
       
  4367   };
       
  4368 
       
  4369   var urlChangeListeners = [],
       
  4370       urlChangeInit = false;
       
  4371 
       
  4372   function fireUrlChange() {
       
  4373     newLocation = null;
       
  4374     if (lastBrowserUrl == self.url()) return;
       
  4375 
       
  4376     lastBrowserUrl = self.url();
       
  4377     forEach(urlChangeListeners, function(listener) {
       
  4378       listener(self.url());
       
  4379     });
       
  4380   }
       
  4381 
       
  4382   /**
       
  4383    * @name $browser#onUrlChange
       
  4384    *
       
  4385    * @description
       
  4386    * Register callback function that will be called, when url changes.
       
  4387    *
       
  4388    * It's only called when the url is changed from outside of angular:
       
  4389    * - user types different url into address bar
       
  4390    * - user clicks on history (forward/back) button
       
  4391    * - user clicks on a link
       
  4392    *
       
  4393    * It's not called when url is changed by $browser.url() method
       
  4394    *
       
  4395    * The listener gets called with new url as parameter.
       
  4396    *
       
  4397    * NOTE: this api is intended for use only by the $location service. Please use the
       
  4398    * {@link ng.$location $location service} to monitor url changes in angular apps.
       
  4399    *
       
  4400    * @param {function(string)} listener Listener function to be called when url changes.
       
  4401    * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
       
  4402    */
       
  4403   self.onUrlChange = function(callback) {
       
  4404     // TODO(vojta): refactor to use node's syntax for events
       
  4405     if (!urlChangeInit) {
       
  4406       // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
       
  4407       // don't fire popstate when user change the address bar and don't fire hashchange when url
       
  4408       // changed by push/replaceState
       
  4409 
       
  4410       // html5 history api - popstate event
       
  4411       if ($sniffer.history) jqLite(window).on('popstate', fireUrlChange);
       
  4412       // hashchange event
       
  4413       if ($sniffer.hashchange) jqLite(window).on('hashchange', fireUrlChange);
       
  4414       // polling
       
  4415       else self.addPollFn(fireUrlChange);
       
  4416 
       
  4417       urlChangeInit = true;
       
  4418     }
       
  4419 
       
  4420     urlChangeListeners.push(callback);
       
  4421     return callback;
       
  4422   };
       
  4423 
       
  4424   //////////////////////////////////////////////////////////////
       
  4425   // Misc API
       
  4426   //////////////////////////////////////////////////////////////
       
  4427 
       
  4428   /**
       
  4429    * @name $browser#baseHref
       
  4430    *
       
  4431    * @description
       
  4432    * Returns current <base href>
       
  4433    * (always relative - without domain)
       
  4434    *
       
  4435    * @returns {string} The current base href
       
  4436    */
       
  4437   self.baseHref = function() {
       
  4438     var href = baseElement.attr('href');
       
  4439     return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
       
  4440   };
       
  4441 
       
  4442   //////////////////////////////////////////////////////////////
       
  4443   // Cookies API
       
  4444   //////////////////////////////////////////////////////////////
       
  4445   var lastCookies = {};
       
  4446   var lastCookieString = '';
       
  4447   var cookiePath = self.baseHref();
       
  4448 
       
  4449   /**
       
  4450    * @name $browser#cookies
       
  4451    *
       
  4452    * @param {string=} name Cookie name
       
  4453    * @param {string=} value Cookie value
       
  4454    *
       
  4455    * @description
       
  4456    * The cookies method provides a 'private' low level access to browser cookies.
       
  4457    * It is not meant to be used directly, use the $cookie service instead.
       
  4458    *
       
  4459    * The return values vary depending on the arguments that the method was called with as follows:
       
  4460    *
       
  4461    * - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify
       
  4462    *   it
       
  4463    * - cookies(name, value) -> set name to value, if value is undefined delete the cookie
       
  4464    * - cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that
       
  4465    *   way)
       
  4466    *
       
  4467    * @returns {Object} Hash of all cookies (if called without any parameter)
       
  4468    */
       
  4469   self.cookies = function(name, value) {
       
  4470     /* global escape: false, unescape: false */
       
  4471     var cookieLength, cookieArray, cookie, i, index;
       
  4472 
       
  4473     if (name) {
       
  4474       if (value === undefined) {
       
  4475         rawDocument.cookie = escape(name) + "=;path=" + cookiePath +
       
  4476                                 ";expires=Thu, 01 Jan 1970 00:00:00 GMT";
       
  4477       } else {
       
  4478         if (isString(value)) {
       
  4479           cookieLength = (rawDocument.cookie = escape(name) + '=' + escape(value) +
       
  4480                                 ';path=' + cookiePath).length + 1;
       
  4481 
       
  4482           // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
       
  4483           // - 300 cookies
       
  4484           // - 20 cookies per unique domain
       
  4485           // - 4096 bytes per cookie
       
  4486           if (cookieLength > 4096) {
       
  4487             $log.warn("Cookie '"+ name +
       
  4488               "' possibly not set or overflowed because it was too large ("+
       
  4489               cookieLength + " > 4096 bytes)!");
       
  4490           }
       
  4491         }
       
  4492       }
       
  4493     } else {
       
  4494       if (rawDocument.cookie !== lastCookieString) {
       
  4495         lastCookieString = rawDocument.cookie;
       
  4496         cookieArray = lastCookieString.split("; ");
       
  4497         lastCookies = {};
       
  4498 
       
  4499         for (i = 0; i < cookieArray.length; i++) {
       
  4500           cookie = cookieArray[i];
       
  4501           index = cookie.indexOf('=');
       
  4502           if (index > 0) { //ignore nameless cookies
       
  4503             name = unescape(cookie.substring(0, index));
       
  4504             // the first value that is seen for a cookie is the most
       
  4505             // specific one.  values for the same cookie name that
       
  4506             // follow are for less specific paths.
       
  4507             if (lastCookies[name] === undefined) {
       
  4508               lastCookies[name] = unescape(cookie.substring(index + 1));
       
  4509             }
       
  4510           }
       
  4511         }
       
  4512       }
       
  4513       return lastCookies;
       
  4514     }
       
  4515   };
       
  4516 
       
  4517 
       
  4518   /**
       
  4519    * @name $browser#defer
       
  4520    * @param {function()} fn A function, who's execution should be deferred.
       
  4521    * @param {number=} [delay=0] of milliseconds to defer the function execution.
       
  4522    * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
       
  4523    *
       
  4524    * @description
       
  4525    * Executes a fn asynchronously via `setTimeout(fn, delay)`.
       
  4526    *
       
  4527    * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
       
  4528    * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
       
  4529    * via `$browser.defer.flush()`.
       
  4530    *
       
  4531    */
       
  4532   self.defer = function(fn, delay) {
       
  4533     var timeoutId;
       
  4534     outstandingRequestCount++;
       
  4535     timeoutId = setTimeout(function() {
       
  4536       delete pendingDeferIds[timeoutId];
       
  4537       completeOutstandingRequest(fn);
       
  4538     }, delay || 0);
       
  4539     pendingDeferIds[timeoutId] = true;
       
  4540     return timeoutId;
       
  4541   };
       
  4542 
       
  4543 
       
  4544   /**
       
  4545    * @name $browser#defer.cancel
       
  4546    *
       
  4547    * @description
       
  4548    * Cancels a deferred task identified with `deferId`.
       
  4549    *
       
  4550    * @param {*} deferId Token returned by the `$browser.defer` function.
       
  4551    * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
       
  4552    *                    canceled.
       
  4553    */
       
  4554   self.defer.cancel = function(deferId) {
       
  4555     if (pendingDeferIds[deferId]) {
       
  4556       delete pendingDeferIds[deferId];
       
  4557       clearTimeout(deferId);
       
  4558       completeOutstandingRequest(noop);
       
  4559       return true;
       
  4560     }
       
  4561     return false;
       
  4562   };
       
  4563 
       
  4564 }
       
  4565 
       
  4566 function $BrowserProvider(){
       
  4567   this.$get = ['$window', '$log', '$sniffer', '$document',
       
  4568       function( $window,   $log,   $sniffer,   $document){
       
  4569         return new Browser($window, $document, $log, $sniffer);
       
  4570       }];
       
  4571 }
       
  4572 
       
  4573 /**
       
  4574  * @ngdoc service
       
  4575  * @name $cacheFactory
       
  4576  *
       
  4577  * @description
       
  4578  * Factory that constructs cache objects and gives access to them.
       
  4579  *
       
  4580  * ```js
       
  4581  *
       
  4582  *  var cache = $cacheFactory('cacheId');
       
  4583  *  expect($cacheFactory.get('cacheId')).toBe(cache);
       
  4584  *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
       
  4585  *
       
  4586  *  cache.put("key", "value");
       
  4587  *  cache.put("another key", "another value");
       
  4588  *
       
  4589  *  // We've specified no options on creation
       
  4590  *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
       
  4591  *
       
  4592  * ```
       
  4593  *
       
  4594  *
       
  4595  * @param {string} cacheId Name or id of the newly created cache.
       
  4596  * @param {object=} options Options object that specifies the cache behavior. Properties:
       
  4597  *
       
  4598  *   - `{number=}` `capacity` — turns the cache into LRU cache.
       
  4599  *
       
  4600  * @returns {object} Newly created cache object with the following set of methods:
       
  4601  *
       
  4602  * - `{object}` `info()` — Returns id, size, and options of cache.
       
  4603  * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
       
  4604  *   it.
       
  4605  * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
       
  4606  * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
       
  4607  * - `{void}` `removeAll()` — Removes all cached values.
       
  4608  * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
       
  4609  *
       
  4610  */
       
  4611 function $CacheFactoryProvider() {
       
  4612 
       
  4613   this.$get = function() {
       
  4614     var caches = {};
       
  4615 
       
  4616     function cacheFactory(cacheId, options) {
       
  4617       if (cacheId in caches) {
       
  4618         throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
       
  4619       }
       
  4620 
       
  4621       var size = 0,
       
  4622           stats = extend({}, options, {id: cacheId}),
       
  4623           data = {},
       
  4624           capacity = (options && options.capacity) || Number.MAX_VALUE,
       
  4625           lruHash = {},
       
  4626           freshEnd = null,
       
  4627           staleEnd = null;
       
  4628 
       
  4629       return caches[cacheId] = {
       
  4630 
       
  4631         put: function(key, value) {
       
  4632           if (capacity < Number.MAX_VALUE) {
       
  4633             var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
       
  4634 
       
  4635             refresh(lruEntry);
       
  4636           }
       
  4637 
       
  4638           if (isUndefined(value)) return;
       
  4639           if (!(key in data)) size++;
       
  4640           data[key] = value;
       
  4641 
       
  4642           if (size > capacity) {
       
  4643             this.remove(staleEnd.key);
       
  4644           }
       
  4645 
       
  4646           return value;
       
  4647         },
       
  4648 
       
  4649 
       
  4650         get: function(key) {
       
  4651           if (capacity < Number.MAX_VALUE) {
       
  4652             var lruEntry = lruHash[key];
       
  4653 
       
  4654             if (!lruEntry) return;
       
  4655 
       
  4656             refresh(lruEntry);
       
  4657           }
       
  4658 
       
  4659           return data[key];
       
  4660         },
       
  4661 
       
  4662 
       
  4663         remove: function(key) {
       
  4664           if (capacity < Number.MAX_VALUE) {
       
  4665             var lruEntry = lruHash[key];
       
  4666 
       
  4667             if (!lruEntry) return;
       
  4668 
       
  4669             if (lruEntry == freshEnd) freshEnd = lruEntry.p;
       
  4670             if (lruEntry == staleEnd) staleEnd = lruEntry.n;
       
  4671             link(lruEntry.n,lruEntry.p);
       
  4672 
       
  4673             delete lruHash[key];
       
  4674           }
       
  4675 
       
  4676           delete data[key];
       
  4677           size--;
       
  4678         },
       
  4679 
       
  4680 
       
  4681         removeAll: function() {
       
  4682           data = {};
       
  4683           size = 0;
       
  4684           lruHash = {};
       
  4685           freshEnd = staleEnd = null;
       
  4686         },
       
  4687 
       
  4688 
       
  4689         destroy: function() {
       
  4690           data = null;
       
  4691           stats = null;
       
  4692           lruHash = null;
       
  4693           delete caches[cacheId];
       
  4694         },
       
  4695 
       
  4696 
       
  4697         info: function() {
       
  4698           return extend({}, stats, {size: size});
       
  4699         }
       
  4700       };
       
  4701 
       
  4702 
       
  4703       /**
       
  4704        * makes the `entry` the freshEnd of the LRU linked list
       
  4705        */
       
  4706       function refresh(entry) {
       
  4707         if (entry != freshEnd) {
       
  4708           if (!staleEnd) {
       
  4709             staleEnd = entry;
       
  4710           } else if (staleEnd == entry) {
       
  4711             staleEnd = entry.n;
       
  4712           }
       
  4713 
       
  4714           link(entry.n, entry.p);
       
  4715           link(entry, freshEnd);
       
  4716           freshEnd = entry;
       
  4717           freshEnd.n = null;
       
  4718         }
       
  4719       }
       
  4720 
       
  4721 
       
  4722       /**
       
  4723        * bidirectionally links two entries of the LRU linked list
       
  4724        */
       
  4725       function link(nextEntry, prevEntry) {
       
  4726         if (nextEntry != prevEntry) {
       
  4727           if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
       
  4728           if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
       
  4729         }
       
  4730       }
       
  4731     }
       
  4732 
       
  4733 
       
  4734   /**
       
  4735    * @ngdoc method
       
  4736    * @name $cacheFactory#info
       
  4737    *
       
  4738    * @description
       
  4739    * Get information about all the of the caches that have been created
       
  4740    *
       
  4741    * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
       
  4742    */
       
  4743     cacheFactory.info = function() {
       
  4744       var info = {};
       
  4745       forEach(caches, function(cache, cacheId) {
       
  4746         info[cacheId] = cache.info();
       
  4747       });
       
  4748       return info;
       
  4749     };
       
  4750 
       
  4751 
       
  4752   /**
       
  4753    * @ngdoc method
       
  4754    * @name $cacheFactory#get
       
  4755    *
       
  4756    * @description
       
  4757    * Get access to a cache object by the `cacheId` used when it was created.
       
  4758    *
       
  4759    * @param {string} cacheId Name or id of a cache to access.
       
  4760    * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
       
  4761    */
       
  4762     cacheFactory.get = function(cacheId) {
       
  4763       return caches[cacheId];
       
  4764     };
       
  4765 
       
  4766 
       
  4767     return cacheFactory;
       
  4768   };
       
  4769 }
       
  4770 
       
  4771 /**
       
  4772  * @ngdoc service
       
  4773  * @name $templateCache
       
  4774  *
       
  4775  * @description
       
  4776  * The first time a template is used, it is loaded in the template cache for quick retrieval. You
       
  4777  * can load templates directly into the cache in a `script` tag, or by consuming the
       
  4778  * `$templateCache` service directly.
       
  4779  *
       
  4780  * Adding via the `script` tag:
       
  4781  *
       
  4782  * ```html
       
  4783  *   <script type="text/ng-template" id="templateId.html">
       
  4784  *     <p>This is the content of the template</p>
       
  4785  *   </script>
       
  4786  * ```
       
  4787  *
       
  4788  * **Note:** the `script` tag containing the template does not need to be included in the `head` of
       
  4789  * the document, but it must be below the `ng-app` definition.
       
  4790  *
       
  4791  * Adding via the $templateCache service:
       
  4792  *
       
  4793  * ```js
       
  4794  * var myApp = angular.module('myApp', []);
       
  4795  * myApp.run(function($templateCache) {
       
  4796  *   $templateCache.put('templateId.html', 'This is the content of the template');
       
  4797  * });
       
  4798  * ```
       
  4799  *
       
  4800  * To retrieve the template later, simply use it in your HTML:
       
  4801  * ```html
       
  4802  * <div ng-include=" 'templateId.html' "></div>
       
  4803  * ```
       
  4804  *
       
  4805  * or get it via Javascript:
       
  4806  * ```js
       
  4807  * $templateCache.get('templateId.html')
       
  4808  * ```
       
  4809  *
       
  4810  * See {@link ng.$cacheFactory $cacheFactory}.
       
  4811  *
       
  4812  */
       
  4813 function $TemplateCacheProvider() {
       
  4814   this.$get = ['$cacheFactory', function($cacheFactory) {
       
  4815     return $cacheFactory('templates');
       
  4816   }];
       
  4817 }
       
  4818 
       
  4819 /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
       
  4820  *
       
  4821  * DOM-related variables:
       
  4822  *
       
  4823  * - "node" - DOM Node
       
  4824  * - "element" - DOM Element or Node
       
  4825  * - "$node" or "$element" - jqLite-wrapped node or element
       
  4826  *
       
  4827  *
       
  4828  * Compiler related stuff:
       
  4829  *
       
  4830  * - "linkFn" - linking fn of a single directive
       
  4831  * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
       
  4832  * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
       
  4833  * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
       
  4834  */
       
  4835 
       
  4836 
       
  4837 /**
       
  4838  * @ngdoc service
       
  4839  * @name $compile
       
  4840  * @function
       
  4841  *
       
  4842  * @description
       
  4843  * Compiles an HTML string or DOM into a template and produces a template function, which
       
  4844  * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
       
  4845  *
       
  4846  * The compilation is a process of walking the DOM tree and matching DOM elements to
       
  4847  * {@link ng.$compileProvider#directive directives}.
       
  4848  *
       
  4849  * <div class="alert alert-warning">
       
  4850  * **Note:** This document is an in-depth reference of all directive options.
       
  4851  * For a gentle introduction to directives with examples of common use cases,
       
  4852  * see the {@link guide/directive directive guide}.
       
  4853  * </div>
       
  4854  *
       
  4855  * ## Comprehensive Directive API
       
  4856  *
       
  4857  * There are many different options for a directive.
       
  4858  *
       
  4859  * The difference resides in the return value of the factory function.
       
  4860  * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
       
  4861  * or just the `postLink` function (all other properties will have the default values).
       
  4862  *
       
  4863  * <div class="alert alert-success">
       
  4864  * **Best Practice:** It's recommended to use the "directive definition object" form.
       
  4865  * </div>
       
  4866  *
       
  4867  * Here's an example directive declared with a Directive Definition Object:
       
  4868  *
       
  4869  * ```js
       
  4870  *   var myModule = angular.module(...);
       
  4871  *
       
  4872  *   myModule.directive('directiveName', function factory(injectables) {
       
  4873  *     var directiveDefinitionObject = {
       
  4874  *       priority: 0,
       
  4875  *       template: '<div></div>', // or // function(tElement, tAttrs) { ... },
       
  4876  *       // or
       
  4877  *       // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
       
  4878  *       replace: false,
       
  4879  *       transclude: false,
       
  4880  *       restrict: 'A',
       
  4881  *       scope: false,
       
  4882  *       controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
       
  4883  *       require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
       
  4884  *       compile: function compile(tElement, tAttrs, transclude) {
       
  4885  *         return {
       
  4886  *           pre: function preLink(scope, iElement, iAttrs, controller) { ... },
       
  4887  *           post: function postLink(scope, iElement, iAttrs, controller) { ... }
       
  4888  *         }
       
  4889  *         // or
       
  4890  *         // return function postLink( ... ) { ... }
       
  4891  *       },
       
  4892  *       // or
       
  4893  *       // link: {
       
  4894  *       //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
       
  4895  *       //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
       
  4896  *       // }
       
  4897  *       // or
       
  4898  *       // link: function postLink( ... ) { ... }
       
  4899  *     };
       
  4900  *     return directiveDefinitionObject;
       
  4901  *   });
       
  4902  * ```
       
  4903  *
       
  4904  * <div class="alert alert-warning">
       
  4905  * **Note:** Any unspecified options will use the default value. You can see the default values below.
       
  4906  * </div>
       
  4907  *
       
  4908  * Therefore the above can be simplified as:
       
  4909  *
       
  4910  * ```js
       
  4911  *   var myModule = angular.module(...);
       
  4912  *
       
  4913  *   myModule.directive('directiveName', function factory(injectables) {
       
  4914  *     var directiveDefinitionObject = {
       
  4915  *       link: function postLink(scope, iElement, iAttrs) { ... }
       
  4916  *     };
       
  4917  *     return directiveDefinitionObject;
       
  4918  *     // or
       
  4919  *     // return function postLink(scope, iElement, iAttrs) { ... }
       
  4920  *   });
       
  4921  * ```
       
  4922  *
       
  4923  *
       
  4924  *
       
  4925  * ### Directive Definition Object
       
  4926  *
       
  4927  * The directive definition object provides instructions to the {@link ng.$compile
       
  4928  * compiler}. The attributes are:
       
  4929  *
       
  4930  * #### `priority`
       
  4931  * When there are multiple directives defined on a single DOM element, sometimes it
       
  4932  * is necessary to specify the order in which the directives are applied. The `priority` is used
       
  4933  * to sort the directives before their `compile` functions get called. Priority is defined as a
       
  4934  * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
       
  4935  * are also run in priority order, but post-link functions are run in reverse order. The order
       
  4936  * of directives with the same priority is undefined. The default priority is `0`.
       
  4937  *
       
  4938  * #### `terminal`
       
  4939  * If set to true then the current `priority` will be the last set of directives
       
  4940  * which will execute (any directives at the current priority will still execute
       
  4941  * as the order of execution on same `priority` is undefined).
       
  4942  *
       
  4943  * #### `scope`
       
  4944  * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the
       
  4945  * same element request a new scope, only one new scope is created. The new scope rule does not
       
  4946  * apply for the root of the template since the root of the template always gets a new scope.
       
  4947  *
       
  4948  * **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from
       
  4949  * normal scope in that it does not prototypically inherit from the parent scope. This is useful
       
  4950  * when creating reusable components, which should not accidentally read or modify data in the
       
  4951  * parent scope.
       
  4952  *
       
  4953  * The 'isolate' scope takes an object hash which defines a set of local scope properties
       
  4954  * derived from the parent scope. These local properties are useful for aliasing values for
       
  4955  * templates. Locals definition is a hash of local scope property to its source:
       
  4956  *
       
  4957  * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
       
  4958  *   always a string since DOM attributes are strings. If no `attr` name is specified  then the
       
  4959  *   attribute name is assumed to be the same as the local name.
       
  4960  *   Given `<widget my-attr="hello {{name}}">` and widget definition
       
  4961  *   of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
       
  4962  *   the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
       
  4963  *   `localName` property on the widget scope. The `name` is read from the parent scope (not
       
  4964  *   component scope).
       
  4965  *
       
  4966  * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
       
  4967  *   parent scope property of name defined via the value of the `attr` attribute. If no `attr`
       
  4968  *   name is specified then the attribute name is assumed to be the same as the local name.
       
  4969  *   Given `<widget my-attr="parentModel">` and widget definition of
       
  4970  *   `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
       
  4971  *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
       
  4972  *   in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
       
  4973  *   scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
       
  4974  *   can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional.
       
  4975  *
       
  4976  * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
       
  4977  *   If no `attr` name is specified then the attribute name is assumed to be the same as the
       
  4978  *   local name. Given `<widget my-attr="count = count + value">` and widget definition of
       
  4979  *   `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
       
  4980  *   a function wrapper for the `count = count + value` expression. Often it's desirable to
       
  4981  *   pass data from the isolated scope via an expression and to the parent scope, this can be
       
  4982  *   done by passing a map of local variable names and values into the expression wrapper fn.
       
  4983  *   For example, if the expression is `increment(amount)` then we can specify the amount value
       
  4984  *   by calling the `localFn` as `localFn({amount: 22})`.
       
  4985  *
       
  4986  *
       
  4987  *
       
  4988  * #### `controller`
       
  4989  * Controller constructor function. The controller is instantiated before the
       
  4990  * pre-linking phase and it is shared with other directives (see
       
  4991  * `require` attribute). This allows the directives to communicate with each other and augment
       
  4992  * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
       
  4993  *
       
  4994  * * `$scope` - Current scope associated with the element
       
  4995  * * `$element` - Current element
       
  4996  * * `$attrs` - Current attributes object for the element
       
  4997  * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope.
       
  4998  *    The scope can be overridden by an optional first argument.
       
  4999  *   `function([scope], cloneLinkingFn)`.
       
  5000  *
       
  5001  *
       
  5002  * #### `require`
       
  5003  * Require another directive and inject its controller as the fourth argument to the linking function. The
       
  5004  * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
       
  5005  * injected argument will be an array in corresponding order. If no such directive can be
       
  5006  * found, or if the directive does not have a controller, then an error is raised. The name can be prefixed with:
       
  5007  *
       
  5008  * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
       
  5009  * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
       
  5010  * * `^` - Locate the required controller by searching the element's parents. Throw an error if not found.
       
  5011  * * `?^` - Attempt to locate the required controller by searching the element's parents or pass `null` to the
       
  5012  *   `link` fn if not found.
       
  5013  *
       
  5014  *
       
  5015  * #### `controllerAs`
       
  5016  * Controller alias at the directive scope. An alias for the controller so it
       
  5017  * can be referenced at the directive template. The directive needs to define a scope for this
       
  5018  * configuration to be used. Useful in the case when directive is used as component.
       
  5019  *
       
  5020  *
       
  5021  * #### `restrict`
       
  5022  * String of subset of `EACM` which restricts the directive to a specific directive
       
  5023  * declaration style. If omitted, the default (attributes only) is used.
       
  5024  *
       
  5025  * * `E` - Element name: `<my-directive></my-directive>`
       
  5026  * * `A` - Attribute (default): `<div my-directive="exp"></div>`
       
  5027  * * `C` - Class: `<div class="my-directive: exp;"></div>`
       
  5028  * * `M` - Comment: `<!-- directive: my-directive exp -->`
       
  5029  *
       
  5030  *
       
  5031  * #### `template`
       
  5032  * replace the current element with the contents of the HTML. The replacement process
       
  5033  * migrates all of the attributes / classes from the old element to the new one. See the
       
  5034  * {@link guide/directive#creating-custom-directives_creating-directives_template-expanding-directive
       
  5035  * Directives Guide} for an example.
       
  5036  *
       
  5037  * You can specify `template` as a string representing the template or as a function which takes
       
  5038  * two arguments `tElement` and `tAttrs` (described in the `compile` function api below) and
       
  5039  * returns a string value representing the template.
       
  5040  *
       
  5041  *
       
  5042  * #### `templateUrl`
       
  5043  * Same as `template` but the template is loaded from the specified URL. Because
       
  5044  * the template loading is asynchronous the compilation/linking is suspended until the template
       
  5045  * is loaded.
       
  5046  *
       
  5047  * You can specify `templateUrl` as a string representing the URL or as a function which takes two
       
  5048  * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
       
  5049  * a string value representing the url.  In either case, the template URL is passed through {@link
       
  5050  * api/ng.$sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
       
  5051  *
       
  5052  *
       
  5053  * #### `replace`
       
  5054  * specify where the template should be inserted. Defaults to `false`.
       
  5055  *
       
  5056  * * `true` - the template will replace the current element.
       
  5057  * * `false` - the template will replace the contents of the current element.
       
  5058  *
       
  5059  *
       
  5060  * #### `transclude`
       
  5061  * compile the content of the element and make it available to the directive.
       
  5062  * Typically used with {@link ng.directive:ngTransclude
       
  5063  * ngTransclude}. The advantage of transclusion is that the linking function receives a
       
  5064  * transclusion function which is pre-bound to the correct scope. In a typical setup the widget
       
  5065  * creates an `isolate` scope, but the transclusion is not a child, but a sibling of the `isolate`
       
  5066  * scope. This makes it possible for the widget to have private state, and the transclusion to
       
  5067  * be bound to the parent (pre-`isolate`) scope.
       
  5068  *
       
  5069  * * `true` - transclude the content of the directive.
       
  5070  * * `'element'` - transclude the whole element including any directives defined at lower priority.
       
  5071  *
       
  5072  *
       
  5073  * #### `compile`
       
  5074  *
       
  5075  * ```js
       
  5076  *   function compile(tElement, tAttrs, transclude) { ... }
       
  5077  * ```
       
  5078  *
       
  5079  * The compile function deals with transforming the template DOM. Since most directives do not do
       
  5080  * template transformation, it is not used often. Examples that require compile functions are
       
  5081  * directives that transform template DOM, such as {@link
       
  5082  * api/ng.directive:ngRepeat ngRepeat}, or load the contents
       
  5083  * asynchronously, such as {@link ngRoute.directive:ngView ngView}. The
       
  5084  * compile function takes the following arguments.
       
  5085  *
       
  5086  *   * `tElement` - template element - The element where the directive has been declared. It is
       
  5087  *     safe to do template transformation on the element and child elements only.
       
  5088  *
       
  5089  *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
       
  5090  *     between all directive compile functions.
       
  5091  *
       
  5092  *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
       
  5093  *
       
  5094  * <div class="alert alert-warning">
       
  5095  * **Note:** The template instance and the link instance may be different objects if the template has
       
  5096  * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
       
  5097  * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
       
  5098  * should be done in a linking function rather than in a compile function.
       
  5099  * </div>
       
  5100  *
       
  5101  * <div class="alert alert-error">
       
  5102  * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
       
  5103  *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
       
  5104  *   to the link function instead.
       
  5105  * </div>
       
  5106 
       
  5107  * A compile function can have a return value which can be either a function or an object.
       
  5108  *
       
  5109  * * returning a (post-link) function - is equivalent to registering the linking function via the
       
  5110  *   `link` property of the config object when the compile function is empty.
       
  5111  *
       
  5112  * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
       
  5113  *   control when a linking function should be called during the linking phase. See info about
       
  5114  *   pre-linking and post-linking functions below.
       
  5115  *
       
  5116  *
       
  5117  * #### `link`
       
  5118  * This property is used only if the `compile` property is not defined.
       
  5119  *
       
  5120  * ```js
       
  5121  *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
       
  5122  * ```
       
  5123  *
       
  5124  * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
       
  5125  * executed after the template has been cloned. This is where most of the directive logic will be
       
  5126  * put.
       
  5127  *
       
  5128  *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
       
  5129  *     directive for registering {@link ng.$rootScope.Scope#$watch watches}.
       
  5130  *
       
  5131  *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
       
  5132  *     manipulate the children of the element only in `postLink` function since the children have
       
  5133  *     already been linked.
       
  5134  *
       
  5135  *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
       
  5136  *     between all directive linking functions.
       
  5137  *
       
  5138  *   * `controller` - a controller instance - A controller instance if at least one directive on the
       
  5139  *     element defines a controller. The controller is shared among all the directives, which allows
       
  5140  *     the directives to use the controllers as a communication channel.
       
  5141  *
       
  5142  *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
       
  5143  *     The scope can be overridden by an optional first argument. This is the same as the `$transclude`
       
  5144  *     parameter of directive controllers.
       
  5145  *     `function([scope], cloneLinkingFn)`.
       
  5146  *
       
  5147  *
       
  5148  * #### Pre-linking function
       
  5149  *
       
  5150  * Executed before the child elements are linked. Not safe to do DOM transformation since the
       
  5151  * compiler linking function will fail to locate the correct elements for linking.
       
  5152  *
       
  5153  * #### Post-linking function
       
  5154  *
       
  5155  * Executed after the child elements are linked. It is safe to do DOM transformation in the post-linking function.
       
  5156  *
       
  5157  * <a name="Attributes"></a>
       
  5158  * ### Attributes
       
  5159  *
       
  5160  * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
       
  5161  * `link()` or `compile()` functions. It has a variety of uses.
       
  5162  *
       
  5163  * accessing *Normalized attribute names:*
       
  5164  * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
       
  5165  * the attributes object allows for normalized access to
       
  5166  *   the attributes.
       
  5167  *
       
  5168  * * *Directive inter-communication:* All directives share the same instance of the attributes
       
  5169  *   object which allows the directives to use the attributes object as inter directive
       
  5170  *   communication.
       
  5171  *
       
  5172  * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
       
  5173  *   allowing other directives to read the interpolated value.
       
  5174  *
       
  5175  * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
       
  5176  *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
       
  5177  *   the only way to easily get the actual value because during the linking phase the interpolation
       
  5178  *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
       
  5179  *
       
  5180  * ```js
       
  5181  * function linkingFn(scope, elm, attrs, ctrl) {
       
  5182  *   // get the attribute value
       
  5183  *   console.log(attrs.ngModel);
       
  5184  *
       
  5185  *   // change the attribute
       
  5186  *   attrs.$set('ngModel', 'new value');
       
  5187  *
       
  5188  *   // observe changes to interpolated attribute
       
  5189  *   attrs.$observe('ngModel', function(value) {
       
  5190  *     console.log('ngModel has changed value to ' + value);
       
  5191  *   });
       
  5192  * }
       
  5193  * ```
       
  5194  *
       
  5195  * Below is an example using `$compileProvider`.
       
  5196  *
       
  5197  * <div class="alert alert-warning">
       
  5198  * **Note**: Typically directives are registered with `module.directive`. The example below is
       
  5199  * to illustrate how `$compile` works.
       
  5200  * </div>
       
  5201  *
       
  5202  <example module="compile">
       
  5203    <file name="index.html">
       
  5204     <script>
       
  5205       angular.module('compile', [], function($compileProvider) {
       
  5206         // configure new 'compile' directive by passing a directive
       
  5207         // factory function. The factory function injects the '$compile'
       
  5208         $compileProvider.directive('compile', function($compile) {
       
  5209           // directive factory creates a link function
       
  5210           return function(scope, element, attrs) {
       
  5211             scope.$watch(
       
  5212               function(scope) {
       
  5213                  // watch the 'compile' expression for changes
       
  5214                 return scope.$eval(attrs.compile);
       
  5215               },
       
  5216               function(value) {
       
  5217                 // when the 'compile' expression changes
       
  5218                 // assign it into the current DOM
       
  5219                 element.html(value);
       
  5220 
       
  5221                 // compile the new DOM and link it to the current
       
  5222                 // scope.
       
  5223                 // NOTE: we only compile .childNodes so that
       
  5224                 // we don't get into infinite loop compiling ourselves
       
  5225                 $compile(element.contents())(scope);
       
  5226               }
       
  5227             );
       
  5228           };
       
  5229         })
       
  5230       });
       
  5231 
       
  5232       function Ctrl($scope) {
       
  5233         $scope.name = 'Angular';
       
  5234         $scope.html = 'Hello {{name}}';
       
  5235       }
       
  5236     </script>
       
  5237     <div ng-controller="Ctrl">
       
  5238       <input ng-model="name"> <br>
       
  5239       <textarea ng-model="html"></textarea> <br>
       
  5240       <div compile="html"></div>
       
  5241     </div>
       
  5242    </file>
       
  5243    <file name="protractor.js" type="protractor">
       
  5244      it('should auto compile', function() {
       
  5245        var textarea = $('textarea');
       
  5246        var output = $('div[compile]');
       
  5247        // The initial state reads 'Hello Angular'.
       
  5248        expect(output.getText()).toBe('Hello Angular');
       
  5249        textarea.clear();
       
  5250        textarea.sendKeys('{{name}}!');
       
  5251        expect(output.getText()).toBe('Angular!');
       
  5252      });
       
  5253    </file>
       
  5254  </example>
       
  5255 
       
  5256  *
       
  5257  *
       
  5258  * @param {string|DOMElement} element Element or HTML string to compile into a template function.
       
  5259  * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives.
       
  5260  * @param {number} maxPriority only apply directives lower than given priority (Only effects the
       
  5261  *                 root element(s), not their children)
       
  5262  * @returns {function(scope, cloneAttachFn=)} a link function which is used to bind template
       
  5263  * (a DOM element/tree) to a scope. Where:
       
  5264  *
       
  5265  *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
       
  5266  *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
       
  5267  *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
       
  5268  *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
       
  5269  *  called as: <br> `cloneAttachFn(clonedElement, scope)` where:
       
  5270  *
       
  5271  *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
       
  5272  *      * `scope` - is the current scope with which the linking function is working with.
       
  5273  *
       
  5274  * Calling the linking function returns the element of the template. It is either the original
       
  5275  * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
       
  5276  *
       
  5277  * After linking the view is not updated until after a call to $digest which typically is done by
       
  5278  * Angular automatically.
       
  5279  *
       
  5280  * If you need access to the bound view, there are two ways to do it:
       
  5281  *
       
  5282  * - If you are not asking the linking function to clone the template, create the DOM element(s)
       
  5283  *   before you send them to the compiler and keep this reference around.
       
  5284  *   ```js
       
  5285  *     var element = $compile('<p>{{total}}</p>')(scope);
       
  5286  *   ```
       
  5287  *
       
  5288  * - if on the other hand, you need the element to be cloned, the view reference from the original
       
  5289  *   example would not point to the clone, but rather to the original template that was cloned. In
       
  5290  *   this case, you can access the clone via the cloneAttachFn:
       
  5291  *   ```js
       
  5292  *     var templateElement = angular.element('<p>{{total}}</p>'),
       
  5293  *         scope = ....;
       
  5294  *
       
  5295  *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
       
  5296  *       //attach the clone to DOM document at the right place
       
  5297  *     });
       
  5298  *
       
  5299  *     //now we have reference to the cloned DOM via `clonedElement`
       
  5300  *   ```
       
  5301  *
       
  5302  *
       
  5303  * For information on how the compiler works, see the
       
  5304  * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
       
  5305  */
       
  5306 
       
  5307 var $compileMinErr = minErr('$compile');
       
  5308 
       
  5309 /**
       
  5310  * @ngdoc provider
       
  5311  * @name $compileProvider
       
  5312  * @function
       
  5313  *
       
  5314  * @description
       
  5315  */
       
  5316 $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
       
  5317 function $CompileProvider($provide, $$sanitizeUriProvider) {
       
  5318   var hasDirectives = {},
       
  5319       Suffix = 'Directive',
       
  5320       COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
       
  5321       CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
       
  5322       TABLE_CONTENT_REGEXP = /^<\s*(tr|th|td|thead|tbody|tfoot)(\s+[^>]*)?>/i;
       
  5323 
       
  5324   // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
       
  5325   // The assumption is that future DOM event attribute names will begin with
       
  5326   // 'on' and be composed of only English letters.
       
  5327   var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
       
  5328 
       
  5329   /**
       
  5330    * @ngdoc method
       
  5331    * @name $compileProvider#directive
       
  5332    * @function
       
  5333    *
       
  5334    * @description
       
  5335    * Register a new directive with the compiler.
       
  5336    *
       
  5337    * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
       
  5338    *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
       
  5339    *    names and the values are the factories.
       
  5340    * @param {Function|Array} directiveFactory An injectable directive factory function. See
       
  5341    *    {@link guide/directive} for more info.
       
  5342    * @returns {ng.$compileProvider} Self for chaining.
       
  5343    */
       
  5344    this.directive = function registerDirective(name, directiveFactory) {
       
  5345     assertNotHasOwnProperty(name, 'directive');
       
  5346     if (isString(name)) {
       
  5347       assertArg(directiveFactory, 'directiveFactory');
       
  5348       if (!hasDirectives.hasOwnProperty(name)) {
       
  5349         hasDirectives[name] = [];
       
  5350         $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
       
  5351           function($injector, $exceptionHandler) {
       
  5352             var directives = [];
       
  5353             forEach(hasDirectives[name], function(directiveFactory, index) {
       
  5354               try {
       
  5355                 var directive = $injector.invoke(directiveFactory);
       
  5356                 if (isFunction(directive)) {
       
  5357                   directive = { compile: valueFn(directive) };
       
  5358                 } else if (!directive.compile && directive.link) {
       
  5359                   directive.compile = valueFn(directive.link);
       
  5360                 }
       
  5361                 directive.priority = directive.priority || 0;
       
  5362                 directive.index = index;
       
  5363                 directive.name = directive.name || name;
       
  5364                 directive.require = directive.require || (directive.controller && directive.name);
       
  5365                 directive.restrict = directive.restrict || 'A';
       
  5366                 directives.push(directive);
       
  5367               } catch (e) {
       
  5368                 $exceptionHandler(e);
       
  5369               }
       
  5370             });
       
  5371             return directives;
       
  5372           }]);
       
  5373       }
       
  5374       hasDirectives[name].push(directiveFactory);
       
  5375     } else {
       
  5376       forEach(name, reverseParams(registerDirective));
       
  5377     }
       
  5378     return this;
       
  5379   };
       
  5380 
       
  5381 
       
  5382   /**
       
  5383    * @ngdoc method
       
  5384    * @name $compileProvider#aHrefSanitizationWhitelist
       
  5385    * @function
       
  5386    *
       
  5387    * @description
       
  5388    * Retrieves or overrides the default regular expression that is used for whitelisting of safe
       
  5389    * urls during a[href] sanitization.
       
  5390    *
       
  5391    * The sanitization is a security measure aimed at prevent XSS attacks via html links.
       
  5392    *
       
  5393    * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
       
  5394    * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
       
  5395    * regular expression. If a match is found, the original url is written into the dom. Otherwise,
       
  5396    * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
       
  5397    *
       
  5398    * @param {RegExp=} regexp New regexp to whitelist urls with.
       
  5399    * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
       
  5400    *    chaining otherwise.
       
  5401    */
       
  5402   this.aHrefSanitizationWhitelist = function(regexp) {
       
  5403     if (isDefined(regexp)) {
       
  5404       $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
       
  5405       return this;
       
  5406     } else {
       
  5407       return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
       
  5408     }
       
  5409   };
       
  5410 
       
  5411 
       
  5412   /**
       
  5413    * @ngdoc method
       
  5414    * @name $compileProvider#imgSrcSanitizationWhitelist
       
  5415    * @function
       
  5416    *
       
  5417    * @description
       
  5418    * Retrieves or overrides the default regular expression that is used for whitelisting of safe
       
  5419    * urls during img[src] sanitization.
       
  5420    *
       
  5421    * The sanitization is a security measure aimed at prevent XSS attacks via html links.
       
  5422    *
       
  5423    * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
       
  5424    * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
       
  5425    * regular expression. If a match is found, the original url is written into the dom. Otherwise,
       
  5426    * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
       
  5427    *
       
  5428    * @param {RegExp=} regexp New regexp to whitelist urls with.
       
  5429    * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
       
  5430    *    chaining otherwise.
       
  5431    */
       
  5432   this.imgSrcSanitizationWhitelist = function(regexp) {
       
  5433     if (isDefined(regexp)) {
       
  5434       $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
       
  5435       return this;
       
  5436     } else {
       
  5437       return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
       
  5438     }
       
  5439   };
       
  5440 
       
  5441   this.$get = [
       
  5442             '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
       
  5443             '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
       
  5444     function($injector,   $interpolate,   $exceptionHandler,   $http,   $templateCache,   $parse,
       
  5445              $controller,   $rootScope,   $document,   $sce,   $animate,   $$sanitizeUri) {
       
  5446 
       
  5447     var Attributes = function(element, attr) {
       
  5448       this.$$element = element;
       
  5449       this.$attr = attr || {};
       
  5450     };
       
  5451 
       
  5452     Attributes.prototype = {
       
  5453       $normalize: directiveNormalize,
       
  5454 
       
  5455 
       
  5456       /**
       
  5457        * @ngdoc method
       
  5458        * @name $compile.directive.Attributes#$addClass
       
  5459        * @function
       
  5460        *
       
  5461        * @description
       
  5462        * Adds the CSS class value specified by the classVal parameter to the element. If animations
       
  5463        * are enabled then an animation will be triggered for the class addition.
       
  5464        *
       
  5465        * @param {string} classVal The className value that will be added to the element
       
  5466        */
       
  5467       $addClass : function(classVal) {
       
  5468         if(classVal && classVal.length > 0) {
       
  5469           $animate.addClass(this.$$element, classVal);
       
  5470         }
       
  5471       },
       
  5472 
       
  5473       /**
       
  5474        * @ngdoc method
       
  5475        * @name $compile.directive.Attributes#$removeClass
       
  5476        * @function
       
  5477        *
       
  5478        * @description
       
  5479        * Removes the CSS class value specified by the classVal parameter from the element. If
       
  5480        * animations are enabled then an animation will be triggered for the class removal.
       
  5481        *
       
  5482        * @param {string} classVal The className value that will be removed from the element
       
  5483        */
       
  5484       $removeClass : function(classVal) {
       
  5485         if(classVal && classVal.length > 0) {
       
  5486           $animate.removeClass(this.$$element, classVal);
       
  5487         }
       
  5488       },
       
  5489 
       
  5490       /**
       
  5491        * @ngdoc method
       
  5492        * @name $compile.directive.Attributes#$updateClass
       
  5493        * @function
       
  5494        *
       
  5495        * @description
       
  5496        * Adds and removes the appropriate CSS class values to the element based on the difference
       
  5497        * between the new and old CSS class values (specified as newClasses and oldClasses).
       
  5498        *
       
  5499        * @param {string} newClasses The current CSS className value
       
  5500        * @param {string} oldClasses The former CSS className value
       
  5501        */
       
  5502       $updateClass : function(newClasses, oldClasses) {
       
  5503         var toAdd = tokenDifference(newClasses, oldClasses);
       
  5504         var toRemove = tokenDifference(oldClasses, newClasses);
       
  5505 
       
  5506         if(toAdd.length === 0) {
       
  5507           $animate.removeClass(this.$$element, toRemove);
       
  5508         } else if(toRemove.length === 0) {
       
  5509           $animate.addClass(this.$$element, toAdd);
       
  5510         } else {
       
  5511           $animate.setClass(this.$$element, toAdd, toRemove);
       
  5512         }
       
  5513       },
       
  5514 
       
  5515       /**
       
  5516        * Set a normalized attribute on the element in a way such that all directives
       
  5517        * can share the attribute. This function properly handles boolean attributes.
       
  5518        * @param {string} key Normalized key. (ie ngAttribute)
       
  5519        * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
       
  5520        * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
       
  5521        *     Defaults to true.
       
  5522        * @param {string=} attrName Optional none normalized name. Defaults to key.
       
  5523        */
       
  5524       $set: function(key, value, writeAttr, attrName) {
       
  5525         // TODO: decide whether or not to throw an error if "class"
       
  5526         //is set through this function since it may cause $updateClass to
       
  5527         //become unstable.
       
  5528 
       
  5529         var booleanKey = getBooleanAttrName(this.$$element[0], key),
       
  5530             normalizedVal,
       
  5531             nodeName;
       
  5532 
       
  5533         if (booleanKey) {
       
  5534           this.$$element.prop(key, value);
       
  5535           attrName = booleanKey;
       
  5536         }
       
  5537 
       
  5538         this[key] = value;
       
  5539 
       
  5540         // translate normalized key to actual key
       
  5541         if (attrName) {
       
  5542           this.$attr[key] = attrName;
       
  5543         } else {
       
  5544           attrName = this.$attr[key];
       
  5545           if (!attrName) {
       
  5546             this.$attr[key] = attrName = snake_case(key, '-');
       
  5547           }
       
  5548         }
       
  5549 
       
  5550         nodeName = nodeName_(this.$$element);
       
  5551 
       
  5552         // sanitize a[href] and img[src] values
       
  5553         if ((nodeName === 'A' && key === 'href') ||
       
  5554             (nodeName === 'IMG' && key === 'src')) {
       
  5555           this[key] = value = $$sanitizeUri(value, key === 'src');
       
  5556         }
       
  5557 
       
  5558         if (writeAttr !== false) {
       
  5559           if (value === null || value === undefined) {
       
  5560             this.$$element.removeAttr(attrName);
       
  5561           } else {
       
  5562             this.$$element.attr(attrName, value);
       
  5563           }
       
  5564         }
       
  5565 
       
  5566         // fire observers
       
  5567         var $$observers = this.$$observers;
       
  5568         $$observers && forEach($$observers[key], function(fn) {
       
  5569           try {
       
  5570             fn(value);
       
  5571           } catch (e) {
       
  5572             $exceptionHandler(e);
       
  5573           }
       
  5574         });
       
  5575       },
       
  5576 
       
  5577 
       
  5578       /**
       
  5579        * @ngdoc method
       
  5580        * @name $compile.directive.Attributes#$observe
       
  5581        * @function
       
  5582        *
       
  5583        * @description
       
  5584        * Observes an interpolated attribute.
       
  5585        *
       
  5586        * The observer function will be invoked once during the next `$digest` following
       
  5587        * compilation. The observer is then invoked whenever the interpolated value
       
  5588        * changes.
       
  5589        *
       
  5590        * @param {string} key Normalized key. (ie ngAttribute) .
       
  5591        * @param {function(interpolatedValue)} fn Function that will be called whenever
       
  5592                 the interpolated value of the attribute changes.
       
  5593        *        See the {@link guide/directive#Attributes Directives} guide for more info.
       
  5594        * @returns {function()} the `fn` parameter.
       
  5595        */
       
  5596       $observe: function(key, fn) {
       
  5597         var attrs = this,
       
  5598             $$observers = (attrs.$$observers || (attrs.$$observers = {})),
       
  5599             listeners = ($$observers[key] || ($$observers[key] = []));
       
  5600 
       
  5601         listeners.push(fn);
       
  5602         $rootScope.$evalAsync(function() {
       
  5603           if (!listeners.$$inter) {
       
  5604             // no one registered attribute interpolation function, so lets call it manually
       
  5605             fn(attrs[key]);
       
  5606           }
       
  5607         });
       
  5608         return fn;
       
  5609       }
       
  5610     };
       
  5611 
       
  5612     var startSymbol = $interpolate.startSymbol(),
       
  5613         endSymbol = $interpolate.endSymbol(),
       
  5614         denormalizeTemplate = (startSymbol == '{{' || endSymbol  == '}}')
       
  5615             ? identity
       
  5616             : function denormalizeTemplate(template) {
       
  5617               return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
       
  5618         },
       
  5619         NG_ATTR_BINDING = /^ngAttr[A-Z]/;
       
  5620 
       
  5621 
       
  5622     return compile;
       
  5623 
       
  5624     //================================
       
  5625 
       
  5626     function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
       
  5627                         previousCompileContext) {
       
  5628       if (!($compileNodes instanceof jqLite)) {
       
  5629         // jquery always rewraps, whereas we need to preserve the original selector so that we can
       
  5630         // modify it.
       
  5631         $compileNodes = jqLite($compileNodes);
       
  5632       }
       
  5633       // We can not compile top level text elements since text nodes can be merged and we will
       
  5634       // not be able to attach scope data to them, so we will wrap them in <span>
       
  5635       forEach($compileNodes, function(node, index){
       
  5636         if (node.nodeType == 3 /* text node */ && node.nodeValue.match(/\S+/) /* non-empty */ ) {
       
  5637           $compileNodes[index] = node = jqLite(node).wrap('<span></span>').parent()[0];
       
  5638         }
       
  5639       });
       
  5640       var compositeLinkFn =
       
  5641               compileNodes($compileNodes, transcludeFn, $compileNodes,
       
  5642                            maxPriority, ignoreDirective, previousCompileContext);
       
  5643       safeAddClass($compileNodes, 'ng-scope');
       
  5644       return function publicLinkFn(scope, cloneConnectFn, transcludeControllers){
       
  5645         assertArg(scope, 'scope');
       
  5646         // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
       
  5647         // and sometimes changes the structure of the DOM.
       
  5648         var $linkNode = cloneConnectFn
       
  5649           ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!!
       
  5650           : $compileNodes;
       
  5651 
       
  5652         forEach(transcludeControllers, function(instance, name) {
       
  5653           $linkNode.data('$' + name + 'Controller', instance);
       
  5654         });
       
  5655 
       
  5656         // Attach scope only to non-text nodes.
       
  5657         for(var i = 0, ii = $linkNode.length; i<ii; i++) {
       
  5658           var node = $linkNode[i],
       
  5659               nodeType = node.nodeType;
       
  5660           if (nodeType === 1 /* element */ || nodeType === 9 /* document */) {
       
  5661             $linkNode.eq(i).data('$scope', scope);
       
  5662           }
       
  5663         }
       
  5664 
       
  5665         if (cloneConnectFn) cloneConnectFn($linkNode, scope);
       
  5666         if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
       
  5667         return $linkNode;
       
  5668       };
       
  5669     }
       
  5670 
       
  5671     function safeAddClass($element, className) {
       
  5672       try {
       
  5673         $element.addClass(className);
       
  5674       } catch(e) {
       
  5675         // ignore, since it means that we are trying to set class on
       
  5676         // SVG element, where class name is read-only.
       
  5677       }
       
  5678     }
       
  5679 
       
  5680     /**
       
  5681      * Compile function matches each node in nodeList against the directives. Once all directives
       
  5682      * for a particular node are collected their compile functions are executed. The compile
       
  5683      * functions return values - the linking functions - are combined into a composite linking
       
  5684      * function, which is the a linking function for the node.
       
  5685      *
       
  5686      * @param {NodeList} nodeList an array of nodes or NodeList to compile
       
  5687      * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
       
  5688      *        scope argument is auto-generated to the new child of the transcluded parent scope.
       
  5689      * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
       
  5690      *        the rootElement must be set the jqLite collection of the compile root. This is
       
  5691      *        needed so that the jqLite collection items can be replaced with widgets.
       
  5692      * @param {number=} maxPriority Max directive priority.
       
  5693      * @returns {Function} A composite linking function of all of the matched directives or null.
       
  5694      */
       
  5695     function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
       
  5696                             previousCompileContext) {
       
  5697       var linkFns = [],
       
  5698           attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound;
       
  5699 
       
  5700       for (var i = 0; i < nodeList.length; i++) {
       
  5701         attrs = new Attributes();
       
  5702 
       
  5703         // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
       
  5704         directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
       
  5705                                         ignoreDirective);
       
  5706 
       
  5707         nodeLinkFn = (directives.length)
       
  5708             ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
       
  5709                                       null, [], [], previousCompileContext)
       
  5710             : null;
       
  5711 
       
  5712         if (nodeLinkFn && nodeLinkFn.scope) {
       
  5713           safeAddClass(jqLite(nodeList[i]), 'ng-scope');
       
  5714         }
       
  5715 
       
  5716         childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
       
  5717                       !(childNodes = nodeList[i].childNodes) ||
       
  5718                       !childNodes.length)
       
  5719             ? null
       
  5720             : compileNodes(childNodes,
       
  5721                  nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
       
  5722 
       
  5723         linkFns.push(nodeLinkFn, childLinkFn);
       
  5724         linkFnFound = linkFnFound || nodeLinkFn || childLinkFn;
       
  5725         //use the previous context only for the first element in the virtual group
       
  5726         previousCompileContext = null;
       
  5727       }
       
  5728 
       
  5729       // return a linking function if we have found anything, null otherwise
       
  5730       return linkFnFound ? compositeLinkFn : null;
       
  5731 
       
  5732       function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) {
       
  5733         var nodeLinkFn, childLinkFn, node, $node, childScope, childTranscludeFn, i, ii, n;
       
  5734 
       
  5735         // copy nodeList so that linking doesn't break due to live list updates.
       
  5736         var nodeListLength = nodeList.length,
       
  5737             stableNodeList = new Array(nodeListLength);
       
  5738         for (i = 0; i < nodeListLength; i++) {
       
  5739           stableNodeList[i] = nodeList[i];
       
  5740         }
       
  5741 
       
  5742         for(i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
       
  5743           node = stableNodeList[n];
       
  5744           nodeLinkFn = linkFns[i++];
       
  5745           childLinkFn = linkFns[i++];
       
  5746           $node = jqLite(node);
       
  5747 
       
  5748           if (nodeLinkFn) {
       
  5749             if (nodeLinkFn.scope) {
       
  5750               childScope = scope.$new();
       
  5751               $node.data('$scope', childScope);
       
  5752             } else {
       
  5753               childScope = scope;
       
  5754             }
       
  5755             childTranscludeFn = nodeLinkFn.transclude;
       
  5756             if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
       
  5757               nodeLinkFn(childLinkFn, childScope, node, $rootElement,
       
  5758                 createBoundTranscludeFn(scope, childTranscludeFn || transcludeFn)
       
  5759               );
       
  5760             } else {
       
  5761               nodeLinkFn(childLinkFn, childScope, node, $rootElement, boundTranscludeFn);
       
  5762             }
       
  5763           } else if (childLinkFn) {
       
  5764             childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
       
  5765           }
       
  5766         }
       
  5767       }
       
  5768     }
       
  5769 
       
  5770     function createBoundTranscludeFn(scope, transcludeFn) {
       
  5771       return function boundTranscludeFn(transcludedScope, cloneFn, controllers) {
       
  5772         var scopeCreated = false;
       
  5773 
       
  5774         if (!transcludedScope) {
       
  5775           transcludedScope = scope.$new();
       
  5776           transcludedScope.$$transcluded = true;
       
  5777           scopeCreated = true;
       
  5778         }
       
  5779 
       
  5780         var clone = transcludeFn(transcludedScope, cloneFn, controllers);
       
  5781         if (scopeCreated) {
       
  5782           clone.on('$destroy', bind(transcludedScope, transcludedScope.$destroy));
       
  5783         }
       
  5784         return clone;
       
  5785       };
       
  5786     }
       
  5787 
       
  5788     /**
       
  5789      * Looks for directives on the given node and adds them to the directive collection which is
       
  5790      * sorted.
       
  5791      *
       
  5792      * @param node Node to search.
       
  5793      * @param directives An array to which the directives are added to. This array is sorted before
       
  5794      *        the function returns.
       
  5795      * @param attrs The shared attrs object which is used to populate the normalized attributes.
       
  5796      * @param {number=} maxPriority Max directive priority.
       
  5797      */
       
  5798     function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
       
  5799       var nodeType = node.nodeType,
       
  5800           attrsMap = attrs.$attr,
       
  5801           match,
       
  5802           className;
       
  5803 
       
  5804       switch(nodeType) {
       
  5805         case 1: /* Element */
       
  5806           // use the node name: <directive>
       
  5807           addDirective(directives,
       
  5808               directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority, ignoreDirective);
       
  5809 
       
  5810           // iterate over the attributes
       
  5811           for (var attr, name, nName, ngAttrName, value, nAttrs = node.attributes,
       
  5812                    j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
       
  5813             var attrStartName = false;
       
  5814             var attrEndName = false;
       
  5815 
       
  5816             attr = nAttrs[j];
       
  5817             if (!msie || msie >= 8 || attr.specified) {
       
  5818               name = attr.name;
       
  5819               // support ngAttr attribute binding
       
  5820               ngAttrName = directiveNormalize(name);
       
  5821               if (NG_ATTR_BINDING.test(ngAttrName)) {
       
  5822                 name = snake_case(ngAttrName.substr(6), '-');
       
  5823               }
       
  5824 
       
  5825               var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
       
  5826               if (ngAttrName === directiveNName + 'Start') {
       
  5827                 attrStartName = name;
       
  5828                 attrEndName = name.substr(0, name.length - 5) + 'end';
       
  5829                 name = name.substr(0, name.length - 6);
       
  5830               }
       
  5831 
       
  5832               nName = directiveNormalize(name.toLowerCase());
       
  5833               attrsMap[nName] = name;
       
  5834               attrs[nName] = value = trim(attr.value);
       
  5835               if (getBooleanAttrName(node, nName)) {
       
  5836                 attrs[nName] = true; // presence means true
       
  5837               }
       
  5838               addAttrInterpolateDirective(node, directives, value, nName);
       
  5839               addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
       
  5840                             attrEndName);
       
  5841             }
       
  5842           }
       
  5843 
       
  5844           // use class as directive
       
  5845           className = node.className;
       
  5846           if (isString(className) && className !== '') {
       
  5847             while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
       
  5848               nName = directiveNormalize(match[2]);
       
  5849               if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
       
  5850                 attrs[nName] = trim(match[3]);
       
  5851               }
       
  5852               className = className.substr(match.index + match[0].length);
       
  5853             }
       
  5854           }
       
  5855           break;
       
  5856         case 3: /* Text Node */
       
  5857           addTextInterpolateDirective(directives, node.nodeValue);
       
  5858           break;
       
  5859         case 8: /* Comment */
       
  5860           try {
       
  5861             match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
       
  5862             if (match) {
       
  5863               nName = directiveNormalize(match[1]);
       
  5864               if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
       
  5865                 attrs[nName] = trim(match[2]);
       
  5866               }
       
  5867             }
       
  5868           } catch (e) {
       
  5869             // turns out that under some circumstances IE9 throws errors when one attempts to read
       
  5870             // comment's node value.
       
  5871             // Just ignore it and continue. (Can't seem to reproduce in test case.)
       
  5872           }
       
  5873           break;
       
  5874       }
       
  5875 
       
  5876       directives.sort(byPriority);
       
  5877       return directives;
       
  5878     }
       
  5879 
       
  5880     /**
       
  5881      * Given a node with an directive-start it collects all of the siblings until it finds
       
  5882      * directive-end.
       
  5883      * @param node
       
  5884      * @param attrStart
       
  5885      * @param attrEnd
       
  5886      * @returns {*}
       
  5887      */
       
  5888     function groupScan(node, attrStart, attrEnd) {
       
  5889       var nodes = [];
       
  5890       var depth = 0;
       
  5891       if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
       
  5892         var startNode = node;
       
  5893         do {
       
  5894           if (!node) {
       
  5895             throw $compileMinErr('uterdir',
       
  5896                       "Unterminated attribute, found '{0}' but no matching '{1}' found.",
       
  5897                       attrStart, attrEnd);
       
  5898           }
       
  5899           if (node.nodeType == 1 /** Element **/) {
       
  5900             if (node.hasAttribute(attrStart)) depth++;
       
  5901             if (node.hasAttribute(attrEnd)) depth--;
       
  5902           }
       
  5903           nodes.push(node);
       
  5904           node = node.nextSibling;
       
  5905         } while (depth > 0);
       
  5906       } else {
       
  5907         nodes.push(node);
       
  5908       }
       
  5909 
       
  5910       return jqLite(nodes);
       
  5911     }
       
  5912 
       
  5913     /**
       
  5914      * Wrapper for linking function which converts normal linking function into a grouped
       
  5915      * linking function.
       
  5916      * @param linkFn
       
  5917      * @param attrStart
       
  5918      * @param attrEnd
       
  5919      * @returns {Function}
       
  5920      */
       
  5921     function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
       
  5922       return function(scope, element, attrs, controllers, transcludeFn) {
       
  5923         element = groupScan(element[0], attrStart, attrEnd);
       
  5924         return linkFn(scope, element, attrs, controllers, transcludeFn);
       
  5925       };
       
  5926     }
       
  5927 
       
  5928     /**
       
  5929      * Once the directives have been collected, their compile functions are executed. This method
       
  5930      * is responsible for inlining directive templates as well as terminating the application
       
  5931      * of the directives if the terminal directive has been reached.
       
  5932      *
       
  5933      * @param {Array} directives Array of collected directives to execute their compile function.
       
  5934      *        this needs to be pre-sorted by priority order.
       
  5935      * @param {Node} compileNode The raw DOM node to apply the compile functions to
       
  5936      * @param {Object} templateAttrs The shared attribute function
       
  5937      * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
       
  5938      *                                                  scope argument is auto-generated to the new
       
  5939      *                                                  child of the transcluded parent scope.
       
  5940      * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
       
  5941      *                              argument has the root jqLite array so that we can replace nodes
       
  5942      *                              on it.
       
  5943      * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
       
  5944      *                                           compiling the transclusion.
       
  5945      * @param {Array.<Function>} preLinkFns
       
  5946      * @param {Array.<Function>} postLinkFns
       
  5947      * @param {Object} previousCompileContext Context used for previous compilation of the current
       
  5948      *                                        node
       
  5949      * @returns {Function} linkFn
       
  5950      */
       
  5951     function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
       
  5952                                    jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
       
  5953                                    previousCompileContext) {
       
  5954       previousCompileContext = previousCompileContext || {};
       
  5955 
       
  5956       var terminalPriority = -Number.MAX_VALUE,
       
  5957           newScopeDirective,
       
  5958           controllerDirectives = previousCompileContext.controllerDirectives,
       
  5959           newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
       
  5960           templateDirective = previousCompileContext.templateDirective,
       
  5961           nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
       
  5962           hasTranscludeDirective = false,
       
  5963           hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
       
  5964           $compileNode = templateAttrs.$$element = jqLite(compileNode),
       
  5965           directive,
       
  5966           directiveName,
       
  5967           $template,
       
  5968           replaceDirective = originalReplaceDirective,
       
  5969           childTranscludeFn = transcludeFn,
       
  5970           linkFn,
       
  5971           directiveValue;
       
  5972 
       
  5973       // executes all directives on the current element
       
  5974       for(var i = 0, ii = directives.length; i < ii; i++) {
       
  5975         directive = directives[i];
       
  5976         var attrStart = directive.$$start;
       
  5977         var attrEnd = directive.$$end;
       
  5978 
       
  5979         // collect multiblock sections
       
  5980         if (attrStart) {
       
  5981           $compileNode = groupScan(compileNode, attrStart, attrEnd);
       
  5982         }
       
  5983         $template = undefined;
       
  5984 
       
  5985         if (terminalPriority > directive.priority) {
       
  5986           break; // prevent further processing of directives
       
  5987         }
       
  5988 
       
  5989         if (directiveValue = directive.scope) {
       
  5990           newScopeDirective = newScopeDirective || directive;
       
  5991 
       
  5992           // skip the check for directives with async templates, we'll check the derived sync
       
  5993           // directive when the template arrives
       
  5994           if (!directive.templateUrl) {
       
  5995             assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
       
  5996                               $compileNode);
       
  5997             if (isObject(directiveValue)) {
       
  5998               newIsolateScopeDirective = directive;
       
  5999             }
       
  6000           }
       
  6001         }
       
  6002 
       
  6003         directiveName = directive.name;
       
  6004 
       
  6005         if (!directive.templateUrl && directive.controller) {
       
  6006           directiveValue = directive.controller;
       
  6007           controllerDirectives = controllerDirectives || {};
       
  6008           assertNoDuplicate("'" + directiveName + "' controller",
       
  6009               controllerDirectives[directiveName], directive, $compileNode);
       
  6010           controllerDirectives[directiveName] = directive;
       
  6011         }
       
  6012 
       
  6013         if (directiveValue = directive.transclude) {
       
  6014           hasTranscludeDirective = true;
       
  6015 
       
  6016           // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
       
  6017           // This option should only be used by directives that know how to safely handle element transclusion,
       
  6018           // where the transcluded nodes are added or replaced after linking.
       
  6019           if (!directive.$$tlb) {
       
  6020             assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
       
  6021             nonTlbTranscludeDirective = directive;
       
  6022           }
       
  6023 
       
  6024           if (directiveValue == 'element') {
       
  6025             hasElementTranscludeDirective = true;
       
  6026             terminalPriority = directive.priority;
       
  6027             $template = groupScan(compileNode, attrStart, attrEnd);
       
  6028             $compileNode = templateAttrs.$$element =
       
  6029                 jqLite(document.createComment(' ' + directiveName + ': ' +
       
  6030                                               templateAttrs[directiveName] + ' '));
       
  6031             compileNode = $compileNode[0];
       
  6032             replaceWith(jqCollection, jqLite(sliceArgs($template)), compileNode);
       
  6033 
       
  6034             childTranscludeFn = compile($template, transcludeFn, terminalPriority,
       
  6035                                         replaceDirective && replaceDirective.name, {
       
  6036                                           // Don't pass in:
       
  6037                                           // - controllerDirectives - otherwise we'll create duplicates controllers
       
  6038                                           // - newIsolateScopeDirective or templateDirective - combining templates with
       
  6039                                           //   element transclusion doesn't make sense.
       
  6040                                           //
       
  6041                                           // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
       
  6042                                           // on the same element more than once.
       
  6043                                           nonTlbTranscludeDirective: nonTlbTranscludeDirective
       
  6044                                         });
       
  6045           } else {
       
  6046             $template = jqLite(jqLiteClone(compileNode)).contents();
       
  6047             $compileNode.empty(); // clear contents
       
  6048             childTranscludeFn = compile($template, transcludeFn);
       
  6049           }
       
  6050         }
       
  6051 
       
  6052         if (directive.template) {
       
  6053           assertNoDuplicate('template', templateDirective, directive, $compileNode);
       
  6054           templateDirective = directive;
       
  6055 
       
  6056           directiveValue = (isFunction(directive.template))
       
  6057               ? directive.template($compileNode, templateAttrs)
       
  6058               : directive.template;
       
  6059 
       
  6060           directiveValue = denormalizeTemplate(directiveValue);
       
  6061 
       
  6062           if (directive.replace) {
       
  6063             replaceDirective = directive;
       
  6064             $template = directiveTemplateContents(directiveValue);
       
  6065             compileNode = $template[0];
       
  6066 
       
  6067             if ($template.length != 1 || compileNode.nodeType !== 1) {
       
  6068               throw $compileMinErr('tplrt',
       
  6069                   "Template for directive '{0}' must have exactly one root element. {1}",
       
  6070                   directiveName, '');
       
  6071             }
       
  6072 
       
  6073             replaceWith(jqCollection, $compileNode, compileNode);
       
  6074 
       
  6075             var newTemplateAttrs = {$attr: {}};
       
  6076 
       
  6077             // combine directives from the original node and from the template:
       
  6078             // - take the array of directives for this element
       
  6079             // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
       
  6080             // - collect directives from the template and sort them by priority
       
  6081             // - combine directives as: processed + template + unprocessed
       
  6082             var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
       
  6083             var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
       
  6084 
       
  6085             if (newIsolateScopeDirective) {
       
  6086               markDirectivesAsIsolate(templateDirectives);
       
  6087             }
       
  6088             directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
       
  6089             mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
       
  6090 
       
  6091             ii = directives.length;
       
  6092           } else {
       
  6093             $compileNode.html(directiveValue);
       
  6094           }
       
  6095         }
       
  6096 
       
  6097         if (directive.templateUrl) {
       
  6098           assertNoDuplicate('template', templateDirective, directive, $compileNode);
       
  6099           templateDirective = directive;
       
  6100 
       
  6101           if (directive.replace) {
       
  6102             replaceDirective = directive;
       
  6103           }
       
  6104 
       
  6105           nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
       
  6106               templateAttrs, jqCollection, childTranscludeFn, preLinkFns, postLinkFns, {
       
  6107                 controllerDirectives: controllerDirectives,
       
  6108                 newIsolateScopeDirective: newIsolateScopeDirective,
       
  6109                 templateDirective: templateDirective,
       
  6110                 nonTlbTranscludeDirective: nonTlbTranscludeDirective
       
  6111               });
       
  6112           ii = directives.length;
       
  6113         } else if (directive.compile) {
       
  6114           try {
       
  6115             linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
       
  6116             if (isFunction(linkFn)) {
       
  6117               addLinkFns(null, linkFn, attrStart, attrEnd);
       
  6118             } else if (linkFn) {
       
  6119               addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
       
  6120             }
       
  6121           } catch (e) {
       
  6122             $exceptionHandler(e, startingTag($compileNode));
       
  6123           }
       
  6124         }
       
  6125 
       
  6126         if (directive.terminal) {
       
  6127           nodeLinkFn.terminal = true;
       
  6128           terminalPriority = Math.max(terminalPriority, directive.priority);
       
  6129         }
       
  6130 
       
  6131       }
       
  6132 
       
  6133       nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
       
  6134       nodeLinkFn.transclude = hasTranscludeDirective && childTranscludeFn;
       
  6135       previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
       
  6136 
       
  6137       // might be normal or delayed nodeLinkFn depending on if templateUrl is present
       
  6138       return nodeLinkFn;
       
  6139 
       
  6140       ////////////////////
       
  6141 
       
  6142       function addLinkFns(pre, post, attrStart, attrEnd) {
       
  6143         if (pre) {
       
  6144           if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
       
  6145           pre.require = directive.require;
       
  6146           if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
       
  6147             pre = cloneAndAnnotateFn(pre, {isolateScope: true});
       
  6148           }
       
  6149           preLinkFns.push(pre);
       
  6150         }
       
  6151         if (post) {
       
  6152           if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
       
  6153           post.require = directive.require;
       
  6154           if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
       
  6155             post = cloneAndAnnotateFn(post, {isolateScope: true});
       
  6156           }
       
  6157           postLinkFns.push(post);
       
  6158         }
       
  6159       }
       
  6160 
       
  6161 
       
  6162       function getControllers(require, $element, elementControllers) {
       
  6163         var value, retrievalMethod = 'data', optional = false;
       
  6164         if (isString(require)) {
       
  6165           while((value = require.charAt(0)) == '^' || value == '?') {
       
  6166             require = require.substr(1);
       
  6167             if (value == '^') {
       
  6168               retrievalMethod = 'inheritedData';
       
  6169             }
       
  6170             optional = optional || value == '?';
       
  6171           }
       
  6172           value = null;
       
  6173 
       
  6174           if (elementControllers && retrievalMethod === 'data') {
       
  6175             value = elementControllers[require];
       
  6176           }
       
  6177           value = value || $element[retrievalMethod]('$' + require + 'Controller');
       
  6178 
       
  6179           if (!value && !optional) {
       
  6180             throw $compileMinErr('ctreq',
       
  6181                 "Controller '{0}', required by directive '{1}', can't be found!",
       
  6182                 require, directiveName);
       
  6183           }
       
  6184           return value;
       
  6185         } else if (isArray(require)) {
       
  6186           value = [];
       
  6187           forEach(require, function(require) {
       
  6188             value.push(getControllers(require, $element, elementControllers));
       
  6189           });
       
  6190         }
       
  6191         return value;
       
  6192       }
       
  6193 
       
  6194 
       
  6195       function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
       
  6196         var attrs, $element, i, ii, linkFn, controller, isolateScope, elementControllers = {}, transcludeFn;
       
  6197 
       
  6198         if (compileNode === linkNode) {
       
  6199           attrs = templateAttrs;
       
  6200         } else {
       
  6201           attrs = shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr));
       
  6202         }
       
  6203         $element = attrs.$$element;
       
  6204 
       
  6205         if (newIsolateScopeDirective) {
       
  6206           var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/;
       
  6207           var $linkNode = jqLite(linkNode);
       
  6208 
       
  6209           isolateScope = scope.$new(true);
       
  6210 
       
  6211           if (templateDirective && (templateDirective === newIsolateScopeDirective.$$originalDirective)) {
       
  6212             $linkNode.data('$isolateScope', isolateScope) ;
       
  6213           } else {
       
  6214             $linkNode.data('$isolateScopeNoTemplate', isolateScope);
       
  6215           }
       
  6216 
       
  6217 
       
  6218 
       
  6219           safeAddClass($linkNode, 'ng-isolate-scope');
       
  6220 
       
  6221           forEach(newIsolateScopeDirective.scope, function(definition, scopeName) {
       
  6222             var match = definition.match(LOCAL_REGEXP) || [],
       
  6223                 attrName = match[3] || scopeName,
       
  6224                 optional = (match[2] == '?'),
       
  6225                 mode = match[1], // @, =, or &
       
  6226                 lastValue,
       
  6227                 parentGet, parentSet, compare;
       
  6228 
       
  6229             isolateScope.$$isolateBindings[scopeName] = mode + attrName;
       
  6230 
       
  6231             switch (mode) {
       
  6232 
       
  6233               case '@':
       
  6234                 attrs.$observe(attrName, function(value) {
       
  6235                   isolateScope[scopeName] = value;
       
  6236                 });
       
  6237                 attrs.$$observers[attrName].$$scope = scope;
       
  6238                 if( attrs[attrName] ) {
       
  6239                   // If the attribute has been provided then we trigger an interpolation to ensure
       
  6240                   // the value is there for use in the link fn
       
  6241                   isolateScope[scopeName] = $interpolate(attrs[attrName])(scope);
       
  6242                 }
       
  6243                 break;
       
  6244 
       
  6245               case '=':
       
  6246                 if (optional && !attrs[attrName]) {
       
  6247                   return;
       
  6248                 }
       
  6249                 parentGet = $parse(attrs[attrName]);
       
  6250                 if (parentGet.literal) {
       
  6251                   compare = equals;
       
  6252                 } else {
       
  6253                   compare = function(a,b) { return a === b; };
       
  6254                 }
       
  6255                 parentSet = parentGet.assign || function() {
       
  6256                   // reset the change, or we will throw this exception on every $digest
       
  6257                   lastValue = isolateScope[scopeName] = parentGet(scope);
       
  6258                   throw $compileMinErr('nonassign',
       
  6259                       "Expression '{0}' used with directive '{1}' is non-assignable!",
       
  6260                       attrs[attrName], newIsolateScopeDirective.name);
       
  6261                 };
       
  6262                 lastValue = isolateScope[scopeName] = parentGet(scope);
       
  6263                 isolateScope.$watch(function parentValueWatch() {
       
  6264                   var parentValue = parentGet(scope);
       
  6265                   if (!compare(parentValue, isolateScope[scopeName])) {
       
  6266                     // we are out of sync and need to copy
       
  6267                     if (!compare(parentValue, lastValue)) {
       
  6268                       // parent changed and it has precedence
       
  6269                       isolateScope[scopeName] = parentValue;
       
  6270                     } else {
       
  6271                       // if the parent can be assigned then do so
       
  6272                       parentSet(scope, parentValue = isolateScope[scopeName]);
       
  6273                     }
       
  6274                   }
       
  6275                   return lastValue = parentValue;
       
  6276                 }, null, parentGet.literal);
       
  6277                 break;
       
  6278 
       
  6279               case '&':
       
  6280                 parentGet = $parse(attrs[attrName]);
       
  6281                 isolateScope[scopeName] = function(locals) {
       
  6282                   return parentGet(scope, locals);
       
  6283                 };
       
  6284                 break;
       
  6285 
       
  6286               default:
       
  6287                 throw $compileMinErr('iscp',
       
  6288                     "Invalid isolate scope definition for directive '{0}'." +
       
  6289                     " Definition: {... {1}: '{2}' ...}",
       
  6290                     newIsolateScopeDirective.name, scopeName, definition);
       
  6291             }
       
  6292           });
       
  6293         }
       
  6294         transcludeFn = boundTranscludeFn && controllersBoundTransclude;
       
  6295         if (controllerDirectives) {
       
  6296           forEach(controllerDirectives, function(directive) {
       
  6297             var locals = {
       
  6298               $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
       
  6299               $element: $element,
       
  6300               $attrs: attrs,
       
  6301               $transclude: transcludeFn
       
  6302             }, controllerInstance;
       
  6303 
       
  6304             controller = directive.controller;
       
  6305             if (controller == '@') {
       
  6306               controller = attrs[directive.name];
       
  6307             }
       
  6308 
       
  6309             controllerInstance = $controller(controller, locals);
       
  6310             // For directives with element transclusion the element is a comment,
       
  6311             // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
       
  6312             // clean up (http://bugs.jquery.com/ticket/8335).
       
  6313             // Instead, we save the controllers for the element in a local hash and attach to .data
       
  6314             // later, once we have the actual element.
       
  6315             elementControllers[directive.name] = controllerInstance;
       
  6316             if (!hasElementTranscludeDirective) {
       
  6317               $element.data('$' + directive.name + 'Controller', controllerInstance);
       
  6318             }
       
  6319 
       
  6320             if (directive.controllerAs) {
       
  6321               locals.$scope[directive.controllerAs] = controllerInstance;
       
  6322             }
       
  6323           });
       
  6324         }
       
  6325 
       
  6326         // PRELINKING
       
  6327         for(i = 0, ii = preLinkFns.length; i < ii; i++) {
       
  6328           try {
       
  6329             linkFn = preLinkFns[i];
       
  6330             linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs,
       
  6331                 linkFn.require && getControllers(linkFn.require, $element, elementControllers), transcludeFn);
       
  6332           } catch (e) {
       
  6333             $exceptionHandler(e, startingTag($element));
       
  6334           }
       
  6335         }
       
  6336 
       
  6337         // RECURSION
       
  6338         // We only pass the isolate scope, if the isolate directive has a template,
       
  6339         // otherwise the child elements do not belong to the isolate directive.
       
  6340         var scopeToChild = scope;
       
  6341         if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
       
  6342           scopeToChild = isolateScope;
       
  6343         }
       
  6344         childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
       
  6345 
       
  6346         // POSTLINKING
       
  6347         for(i = postLinkFns.length - 1; i >= 0; i--) {
       
  6348           try {
       
  6349             linkFn = postLinkFns[i];
       
  6350             linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs,
       
  6351                 linkFn.require && getControllers(linkFn.require, $element, elementControllers), transcludeFn);
       
  6352           } catch (e) {
       
  6353             $exceptionHandler(e, startingTag($element));
       
  6354           }
       
  6355         }
       
  6356 
       
  6357         // This is the function that is injected as `$transclude`.
       
  6358         function controllersBoundTransclude(scope, cloneAttachFn) {
       
  6359           var transcludeControllers;
       
  6360 
       
  6361           // no scope passed
       
  6362           if (arguments.length < 2) {
       
  6363             cloneAttachFn = scope;
       
  6364             scope = undefined;
       
  6365           }
       
  6366 
       
  6367           if (hasElementTranscludeDirective) {
       
  6368             transcludeControllers = elementControllers;
       
  6369           }
       
  6370 
       
  6371           return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers);
       
  6372         }
       
  6373       }
       
  6374     }
       
  6375 
       
  6376     function markDirectivesAsIsolate(directives) {
       
  6377       // mark all directives as needing isolate scope.
       
  6378       for (var j = 0, jj = directives.length; j < jj; j++) {
       
  6379         directives[j] = inherit(directives[j], {$$isolateScope: true});
       
  6380       }
       
  6381     }
       
  6382 
       
  6383     /**
       
  6384      * looks up the directive and decorates it with exception handling and proper parameters. We
       
  6385      * call this the boundDirective.
       
  6386      *
       
  6387      * @param {string} name name of the directive to look up.
       
  6388      * @param {string} location The directive must be found in specific format.
       
  6389      *   String containing any of theses characters:
       
  6390      *
       
  6391      *   * `E`: element name
       
  6392      *   * `A': attribute
       
  6393      *   * `C`: class
       
  6394      *   * `M`: comment
       
  6395      * @returns {boolean} true if directive was added.
       
  6396      */
       
  6397     function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
       
  6398                           endAttrName) {
       
  6399       if (name === ignoreDirective) return null;
       
  6400       var match = null;
       
  6401       if (hasDirectives.hasOwnProperty(name)) {
       
  6402         for(var directive, directives = $injector.get(name + Suffix),
       
  6403             i = 0, ii = directives.length; i<ii; i++) {
       
  6404           try {
       
  6405             directive = directives[i];
       
  6406             if ( (maxPriority === undefined || maxPriority > directive.priority) &&
       
  6407                  directive.restrict.indexOf(location) != -1) {
       
  6408               if (startAttrName) {
       
  6409                 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
       
  6410               }
       
  6411               tDirectives.push(directive);
       
  6412               match = directive;
       
  6413             }
       
  6414           } catch(e) { $exceptionHandler(e); }
       
  6415         }
       
  6416       }
       
  6417       return match;
       
  6418     }
       
  6419 
       
  6420 
       
  6421     /**
       
  6422      * When the element is replaced with HTML template then the new attributes
       
  6423      * on the template need to be merged with the existing attributes in the DOM.
       
  6424      * The desired effect is to have both of the attributes present.
       
  6425      *
       
  6426      * @param {object} dst destination attributes (original DOM)
       
  6427      * @param {object} src source attributes (from the directive template)
       
  6428      */
       
  6429     function mergeTemplateAttributes(dst, src) {
       
  6430       var srcAttr = src.$attr,
       
  6431           dstAttr = dst.$attr,
       
  6432           $element = dst.$$element;
       
  6433 
       
  6434       // reapply the old attributes to the new element
       
  6435       forEach(dst, function(value, key) {
       
  6436         if (key.charAt(0) != '$') {
       
  6437           if (src[key]) {
       
  6438             value += (key === 'style' ? ';' : ' ') + src[key];
       
  6439           }
       
  6440           dst.$set(key, value, true, srcAttr[key]);
       
  6441         }
       
  6442       });
       
  6443 
       
  6444       // copy the new attributes on the old attrs object
       
  6445       forEach(src, function(value, key) {
       
  6446         if (key == 'class') {
       
  6447           safeAddClass($element, value);
       
  6448           dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
       
  6449         } else if (key == 'style') {
       
  6450           $element.attr('style', $element.attr('style') + ';' + value);
       
  6451           dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
       
  6452           // `dst` will never contain hasOwnProperty as DOM parser won't let it.
       
  6453           // You will get an "InvalidCharacterError: DOM Exception 5" error if you
       
  6454           // have an attribute like "has-own-property" or "data-has-own-property", etc.
       
  6455         } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
       
  6456           dst[key] = value;
       
  6457           dstAttr[key] = srcAttr[key];
       
  6458         }
       
  6459       });
       
  6460     }
       
  6461 
       
  6462 
       
  6463     function directiveTemplateContents(template) {
       
  6464       var type;
       
  6465       template = trim(template);
       
  6466       if ((type = TABLE_CONTENT_REGEXP.exec(template))) {
       
  6467         type = type[1].toLowerCase();
       
  6468         var table = jqLite('<table>' + template + '</table>');
       
  6469         if (/(thead|tbody|tfoot)/.test(type)) {
       
  6470           return table.children(type);
       
  6471         }
       
  6472         table = table.children('tbody');
       
  6473         if (type === 'tr') {
       
  6474           return table.children('tr');
       
  6475         }
       
  6476         return table.children('tr').contents();
       
  6477       }
       
  6478       return jqLite('<div>' +
       
  6479                       template +
       
  6480                     '</div>').contents();
       
  6481     }
       
  6482 
       
  6483 
       
  6484     function compileTemplateUrl(directives, $compileNode, tAttrs,
       
  6485         $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
       
  6486       var linkQueue = [],
       
  6487           afterTemplateNodeLinkFn,
       
  6488           afterTemplateChildLinkFn,
       
  6489           beforeTemplateCompileNode = $compileNode[0],
       
  6490           origAsyncDirective = directives.shift(),
       
  6491           // The fact that we have to copy and patch the directive seems wrong!
       
  6492           derivedSyncDirective = extend({}, origAsyncDirective, {
       
  6493             templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
       
  6494           }),
       
  6495           templateUrl = (isFunction(origAsyncDirective.templateUrl))
       
  6496               ? origAsyncDirective.templateUrl($compileNode, tAttrs)
       
  6497               : origAsyncDirective.templateUrl;
       
  6498 
       
  6499       $compileNode.empty();
       
  6500 
       
  6501       $http.get($sce.getTrustedResourceUrl(templateUrl), {cache: $templateCache}).
       
  6502         success(function(content) {
       
  6503           var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
       
  6504 
       
  6505           content = denormalizeTemplate(content);
       
  6506 
       
  6507           if (origAsyncDirective.replace) {
       
  6508             $template = directiveTemplateContents(content);
       
  6509             compileNode = $template[0];
       
  6510 
       
  6511             if ($template.length != 1 || compileNode.nodeType !== 1) {
       
  6512               throw $compileMinErr('tplrt',
       
  6513                   "Template for directive '{0}' must have exactly one root element. {1}",
       
  6514                   origAsyncDirective.name, templateUrl);
       
  6515             }
       
  6516 
       
  6517             tempTemplateAttrs = {$attr: {}};
       
  6518             replaceWith($rootElement, $compileNode, compileNode);
       
  6519             var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
       
  6520 
       
  6521             if (isObject(origAsyncDirective.scope)) {
       
  6522               markDirectivesAsIsolate(templateDirectives);
       
  6523             }
       
  6524             directives = templateDirectives.concat(directives);
       
  6525             mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
       
  6526           } else {
       
  6527             compileNode = beforeTemplateCompileNode;
       
  6528             $compileNode.html(content);
       
  6529           }
       
  6530 
       
  6531           directives.unshift(derivedSyncDirective);
       
  6532 
       
  6533           afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
       
  6534               childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
       
  6535               previousCompileContext);
       
  6536           forEach($rootElement, function(node, i) {
       
  6537             if (node == compileNode) {
       
  6538               $rootElement[i] = $compileNode[0];
       
  6539             }
       
  6540           });
       
  6541           afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
       
  6542 
       
  6543 
       
  6544           while(linkQueue.length) {
       
  6545             var scope = linkQueue.shift(),
       
  6546                 beforeTemplateLinkNode = linkQueue.shift(),
       
  6547                 linkRootElement = linkQueue.shift(),
       
  6548                 boundTranscludeFn = linkQueue.shift(),
       
  6549                 linkNode = $compileNode[0];
       
  6550 
       
  6551             if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
       
  6552               var oldClasses = beforeTemplateLinkNode.className;
       
  6553 
       
  6554               if (!(previousCompileContext.hasElementTranscludeDirective &&
       
  6555                   origAsyncDirective.replace)) {
       
  6556                 // it was cloned therefore we have to clone as well.
       
  6557                 linkNode = jqLiteClone(compileNode);
       
  6558               }
       
  6559 
       
  6560               replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
       
  6561 
       
  6562               // Copy in CSS classes from original node
       
  6563               safeAddClass(jqLite(linkNode), oldClasses);
       
  6564             }
       
  6565             if (afterTemplateNodeLinkFn.transclude) {
       
  6566               childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude);
       
  6567             } else {
       
  6568               childBoundTranscludeFn = boundTranscludeFn;
       
  6569             }
       
  6570             afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
       
  6571               childBoundTranscludeFn);
       
  6572           }
       
  6573           linkQueue = null;
       
  6574         }).
       
  6575         error(function(response, code, headers, config) {
       
  6576           throw $compileMinErr('tpload', 'Failed to load template: {0}', config.url);
       
  6577         });
       
  6578 
       
  6579       return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
       
  6580         if (linkQueue) {
       
  6581           linkQueue.push(scope);
       
  6582           linkQueue.push(node);
       
  6583           linkQueue.push(rootElement);
       
  6584           linkQueue.push(boundTranscludeFn);
       
  6585         } else {
       
  6586           afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, boundTranscludeFn);
       
  6587         }
       
  6588       };
       
  6589     }
       
  6590 
       
  6591 
       
  6592     /**
       
  6593      * Sorting function for bound directives.
       
  6594      */
       
  6595     function byPriority(a, b) {
       
  6596       var diff = b.priority - a.priority;
       
  6597       if (diff !== 0) return diff;
       
  6598       if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
       
  6599       return a.index - b.index;
       
  6600     }
       
  6601 
       
  6602 
       
  6603     function assertNoDuplicate(what, previousDirective, directive, element) {
       
  6604       if (previousDirective) {
       
  6605         throw $compileMinErr('multidir', 'Multiple directives [{0}, {1}] asking for {2} on: {3}',
       
  6606             previousDirective.name, directive.name, what, startingTag(element));
       
  6607       }
       
  6608     }
       
  6609 
       
  6610 
       
  6611     function addTextInterpolateDirective(directives, text) {
       
  6612       var interpolateFn = $interpolate(text, true);
       
  6613       if (interpolateFn) {
       
  6614         directives.push({
       
  6615           priority: 0,
       
  6616           compile: valueFn(function textInterpolateLinkFn(scope, node) {
       
  6617             var parent = node.parent(),
       
  6618                 bindings = parent.data('$binding') || [];
       
  6619             bindings.push(interpolateFn);
       
  6620             safeAddClass(parent.data('$binding', bindings), 'ng-binding');
       
  6621             scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
       
  6622               node[0].nodeValue = value;
       
  6623             });
       
  6624           })
       
  6625         });
       
  6626       }
       
  6627     }
       
  6628 
       
  6629 
       
  6630     function getTrustedContext(node, attrNormalizedName) {
       
  6631       if (attrNormalizedName == "srcdoc") {
       
  6632         return $sce.HTML;
       
  6633       }
       
  6634       var tag = nodeName_(node);
       
  6635       // maction[xlink:href] can source SVG.  It's not limited to <maction>.
       
  6636       if (attrNormalizedName == "xlinkHref" ||
       
  6637           (tag == "FORM" && attrNormalizedName == "action") ||
       
  6638           (tag != "IMG" && (attrNormalizedName == "src" ||
       
  6639                             attrNormalizedName == "ngSrc"))) {
       
  6640         return $sce.RESOURCE_URL;
       
  6641       }
       
  6642     }
       
  6643 
       
  6644 
       
  6645     function addAttrInterpolateDirective(node, directives, value, name) {
       
  6646       var interpolateFn = $interpolate(value, true);
       
  6647 
       
  6648       // no interpolation found -> ignore
       
  6649       if (!interpolateFn) return;
       
  6650 
       
  6651 
       
  6652       if (name === "multiple" && nodeName_(node) === "SELECT") {
       
  6653         throw $compileMinErr("selmulti",
       
  6654             "Binding to the 'multiple' attribute is not supported. Element: {0}",
       
  6655             startingTag(node));
       
  6656       }
       
  6657 
       
  6658       directives.push({
       
  6659         priority: 100,
       
  6660         compile: function() {
       
  6661             return {
       
  6662               pre: function attrInterpolatePreLinkFn(scope, element, attr) {
       
  6663                 var $$observers = (attr.$$observers || (attr.$$observers = {}));
       
  6664 
       
  6665                 if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
       
  6666                   throw $compileMinErr('nodomevents',
       
  6667                       "Interpolations for HTML DOM event attributes are disallowed.  Please use the " +
       
  6668                           "ng- versions (such as ng-click instead of onclick) instead.");
       
  6669                 }
       
  6670 
       
  6671                 // we need to interpolate again, in case the attribute value has been updated
       
  6672                 // (e.g. by another directive's compile function)
       
  6673                 interpolateFn = $interpolate(attr[name], true, getTrustedContext(node, name));
       
  6674 
       
  6675                 // if attribute was updated so that there is no interpolation going on we don't want to
       
  6676                 // register any observers
       
  6677                 if (!interpolateFn) return;
       
  6678 
       
  6679                 // TODO(i): this should likely be attr.$set(name, iterpolateFn(scope) so that we reset the
       
  6680                 // actual attr value
       
  6681                 attr[name] = interpolateFn(scope);
       
  6682                 ($$observers[name] || ($$observers[name] = [])).$$inter = true;
       
  6683                 (attr.$$observers && attr.$$observers[name].$$scope || scope).
       
  6684                   $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
       
  6685                     //special case for class attribute addition + removal
       
  6686                     //so that class changes can tap into the animation
       
  6687                     //hooks provided by the $animate service. Be sure to
       
  6688                     //skip animations when the first digest occurs (when
       
  6689                     //both the new and the old values are the same) since
       
  6690                     //the CSS classes are the non-interpolated values
       
  6691                     if(name === 'class' && newValue != oldValue) {
       
  6692                       attr.$updateClass(newValue, oldValue);
       
  6693                     } else {
       
  6694                       attr.$set(name, newValue);
       
  6695                     }
       
  6696                   });
       
  6697               }
       
  6698             };
       
  6699           }
       
  6700       });
       
  6701     }
       
  6702 
       
  6703 
       
  6704     /**
       
  6705      * This is a special jqLite.replaceWith, which can replace items which
       
  6706      * have no parents, provided that the containing jqLite collection is provided.
       
  6707      *
       
  6708      * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
       
  6709      *                               in the root of the tree.
       
  6710      * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
       
  6711      *                                  the shell, but replace its DOM node reference.
       
  6712      * @param {Node} newNode The new DOM node.
       
  6713      */
       
  6714     function replaceWith($rootElement, elementsToRemove, newNode) {
       
  6715       var firstElementToRemove = elementsToRemove[0],
       
  6716           removeCount = elementsToRemove.length,
       
  6717           parent = firstElementToRemove.parentNode,
       
  6718           i, ii;
       
  6719 
       
  6720       if ($rootElement) {
       
  6721         for(i = 0, ii = $rootElement.length; i < ii; i++) {
       
  6722           if ($rootElement[i] == firstElementToRemove) {
       
  6723             $rootElement[i++] = newNode;
       
  6724             for (var j = i, j2 = j + removeCount - 1,
       
  6725                      jj = $rootElement.length;
       
  6726                  j < jj; j++, j2++) {
       
  6727               if (j2 < jj) {
       
  6728                 $rootElement[j] = $rootElement[j2];
       
  6729               } else {
       
  6730                 delete $rootElement[j];
       
  6731               }
       
  6732             }
       
  6733             $rootElement.length -= removeCount - 1;
       
  6734             break;
       
  6735           }
       
  6736         }
       
  6737       }
       
  6738 
       
  6739       if (parent) {
       
  6740         parent.replaceChild(newNode, firstElementToRemove);
       
  6741       }
       
  6742       var fragment = document.createDocumentFragment();
       
  6743       fragment.appendChild(firstElementToRemove);
       
  6744       newNode[jqLite.expando] = firstElementToRemove[jqLite.expando];
       
  6745       for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
       
  6746         var element = elementsToRemove[k];
       
  6747         jqLite(element).remove(); // must do this way to clean up expando
       
  6748         fragment.appendChild(element);
       
  6749         delete elementsToRemove[k];
       
  6750       }
       
  6751 
       
  6752       elementsToRemove[0] = newNode;
       
  6753       elementsToRemove.length = 1;
       
  6754     }
       
  6755 
       
  6756 
       
  6757     function cloneAndAnnotateFn(fn, annotation) {
       
  6758       return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
       
  6759     }
       
  6760   }];
       
  6761 }
       
  6762 
       
  6763 var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i;
       
  6764 /**
       
  6765  * Converts all accepted directives format into proper directive name.
       
  6766  * All of these will become 'myDirective':
       
  6767  *   my:Directive
       
  6768  *   my-directive
       
  6769  *   x-my-directive
       
  6770  *   data-my:directive
       
  6771  *
       
  6772  * Also there is special case for Moz prefix starting with upper case letter.
       
  6773  * @param name Name to normalize
       
  6774  */
       
  6775 function directiveNormalize(name) {
       
  6776   return camelCase(name.replace(PREFIX_REGEXP, ''));
       
  6777 }
       
  6778 
       
  6779 /**
       
  6780  * @ngdoc type
       
  6781  * @name $compile.directive.Attributes
       
  6782  *
       
  6783  * @description
       
  6784  * A shared object between directive compile / linking functions which contains normalized DOM
       
  6785  * element attributes. The values reflect current binding state `{{ }}`. The normalization is
       
  6786  * needed since all of these are treated as equivalent in Angular:
       
  6787  *
       
  6788  *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
       
  6789  */
       
  6790 
       
  6791 /**
       
  6792  * @ngdoc property
       
  6793  * @name $compile.directive.Attributes#$attr
       
  6794  * @returns {object} A map of DOM element attribute names to the normalized name. This is
       
  6795  *                   needed to do reverse lookup from normalized name back to actual name.
       
  6796  */
       
  6797 
       
  6798 
       
  6799 /**
       
  6800  * @ngdoc method
       
  6801  * @name $compile.directive.Attributes#$set
       
  6802  * @function
       
  6803  *
       
  6804  * @description
       
  6805  * Set DOM element attribute value.
       
  6806  *
       
  6807  *
       
  6808  * @param {string} name Normalized element attribute name of the property to modify. The name is
       
  6809  *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
       
  6810  *          property to the original name.
       
  6811  * @param {string} value Value to set the attribute to. The value can be an interpolated string.
       
  6812  */
       
  6813 
       
  6814 
       
  6815 
       
  6816 /**
       
  6817  * Closure compiler type information
       
  6818  */
       
  6819 
       
  6820 function nodesetLinkingFn(
       
  6821   /* angular.Scope */ scope,
       
  6822   /* NodeList */ nodeList,
       
  6823   /* Element */ rootElement,
       
  6824   /* function(Function) */ boundTranscludeFn
       
  6825 ){}
       
  6826 
       
  6827 function directiveLinkingFn(
       
  6828   /* nodesetLinkingFn */ nodesetLinkingFn,
       
  6829   /* angular.Scope */ scope,
       
  6830   /* Node */ node,
       
  6831   /* Element */ rootElement,
       
  6832   /* function(Function) */ boundTranscludeFn
       
  6833 ){}
       
  6834 
       
  6835 function tokenDifference(str1, str2) {
       
  6836   var values = '',
       
  6837       tokens1 = str1.split(/\s+/),
       
  6838       tokens2 = str2.split(/\s+/);
       
  6839 
       
  6840   outer:
       
  6841   for(var i = 0; i < tokens1.length; i++) {
       
  6842     var token = tokens1[i];
       
  6843     for(var j = 0; j < tokens2.length; j++) {
       
  6844       if(token == tokens2[j]) continue outer;
       
  6845     }
       
  6846     values += (values.length > 0 ? ' ' : '') + token;
       
  6847   }
       
  6848   return values;
       
  6849 }
       
  6850 
       
  6851 /**
       
  6852  * @ngdoc provider
       
  6853  * @name $controllerProvider
       
  6854  * @description
       
  6855  * The {@link ng.$controller $controller service} is used by Angular to create new
       
  6856  * controllers.
       
  6857  *
       
  6858  * This provider allows controller registration via the
       
  6859  * {@link ng.$controllerProvider#register register} method.
       
  6860  */
       
  6861 function $ControllerProvider() {
       
  6862   var controllers = {},
       
  6863       CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
       
  6864 
       
  6865 
       
  6866   /**
       
  6867    * @ngdoc method
       
  6868    * @name $controllerProvider#register
       
  6869    * @param {string|Object} name Controller name, or an object map of controllers where the keys are
       
  6870    *    the names and the values are the constructors.
       
  6871    * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
       
  6872    *    annotations in the array notation).
       
  6873    */
       
  6874   this.register = function(name, constructor) {
       
  6875     assertNotHasOwnProperty(name, 'controller');
       
  6876     if (isObject(name)) {
       
  6877       extend(controllers, name);
       
  6878     } else {
       
  6879       controllers[name] = constructor;
       
  6880     }
       
  6881   };
       
  6882 
       
  6883 
       
  6884   this.$get = ['$injector', '$window', function($injector, $window) {
       
  6885 
       
  6886     /**
       
  6887      * @ngdoc service
       
  6888      * @name $controller
       
  6889      * @requires $injector
       
  6890      *
       
  6891      * @param {Function|string} constructor If called with a function then it's considered to be the
       
  6892      *    controller constructor function. Otherwise it's considered to be a string which is used
       
  6893      *    to retrieve the controller constructor using the following steps:
       
  6894      *
       
  6895      *    * check if a controller with given name is registered via `$controllerProvider`
       
  6896      *    * check if evaluating the string on the current scope returns a constructor
       
  6897      *    * check `window[constructor]` on the global `window` object
       
  6898      *
       
  6899      * @param {Object} locals Injection locals for Controller.
       
  6900      * @return {Object} Instance of given controller.
       
  6901      *
       
  6902      * @description
       
  6903      * `$controller` service is responsible for instantiating controllers.
       
  6904      *
       
  6905      * It's just a simple call to {@link auto.$injector $injector}, but extracted into
       
  6906      * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
       
  6907      */
       
  6908     return function(expression, locals) {
       
  6909       var instance, match, constructor, identifier;
       
  6910 
       
  6911       if(isString(expression)) {
       
  6912         match = expression.match(CNTRL_REG),
       
  6913         constructor = match[1],
       
  6914         identifier = match[3];
       
  6915         expression = controllers.hasOwnProperty(constructor)
       
  6916             ? controllers[constructor]
       
  6917             : getter(locals.$scope, constructor, true) || getter($window, constructor, true);
       
  6918 
       
  6919         assertArgFn(expression, constructor, true);
       
  6920       }
       
  6921 
       
  6922       instance = $injector.instantiate(expression, locals);
       
  6923 
       
  6924       if (identifier) {
       
  6925         if (!(locals && typeof locals.$scope == 'object')) {
       
  6926           throw minErr('$controller')('noscp',
       
  6927               "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
       
  6928               constructor || expression.name, identifier);
       
  6929         }
       
  6930 
       
  6931         locals.$scope[identifier] = instance;
       
  6932       }
       
  6933 
       
  6934       return instance;
       
  6935     };
       
  6936   }];
       
  6937 }
       
  6938 
       
  6939 /**
       
  6940  * @ngdoc service
       
  6941  * @name $document
       
  6942  * @requires $window
       
  6943  *
       
  6944  * @description
       
  6945  * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
       
  6946  *
       
  6947  * @example
       
  6948    <example>
       
  6949      <file name="index.html">
       
  6950        <div ng-controller="MainCtrl">
       
  6951          <p>$document title: <b ng-bind="title"></b></p>
       
  6952          <p>window.document title: <b ng-bind="windowTitle"></b></p>
       
  6953        </div>
       
  6954      </file>
       
  6955      <file name="script.js">
       
  6956        function MainCtrl($scope, $document) {
       
  6957          $scope.title = $document[0].title;
       
  6958          $scope.windowTitle = angular.element(window.document)[0].title;
       
  6959        }
       
  6960      </file>
       
  6961    </example>
       
  6962  */
       
  6963 function $DocumentProvider(){
       
  6964   this.$get = ['$window', function(window){
       
  6965     return jqLite(window.document);
       
  6966   }];
       
  6967 }
       
  6968 
       
  6969 /**
       
  6970  * @ngdoc service
       
  6971  * @name $exceptionHandler
       
  6972  * @requires ng.$log
       
  6973  *
       
  6974  * @description
       
  6975  * Any uncaught exception in angular expressions is delegated to this service.
       
  6976  * The default implementation simply delegates to `$log.error` which logs it into
       
  6977  * the browser console.
       
  6978  *
       
  6979  * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
       
  6980  * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
       
  6981  *
       
  6982  * ## Example:
       
  6983  *
       
  6984  * ```js
       
  6985  *   angular.module('exceptionOverride', []).factory('$exceptionHandler', function () {
       
  6986  *     return function (exception, cause) {
       
  6987  *       exception.message += ' (caused by "' + cause + '")';
       
  6988  *       throw exception;
       
  6989  *     };
       
  6990  *   });
       
  6991  * ```
       
  6992  *
       
  6993  * This example will override the normal action of `$exceptionHandler`, to make angular
       
  6994  * exceptions fail hard when they happen, instead of just logging to the console.
       
  6995  *
       
  6996  * @param {Error} exception Exception associated with the error.
       
  6997  * @param {string=} cause optional information about the context in which
       
  6998  *       the error was thrown.
       
  6999  *
       
  7000  */
       
  7001 function $ExceptionHandlerProvider() {
       
  7002   this.$get = ['$log', function($log) {
       
  7003     return function(exception, cause) {
       
  7004       $log.error.apply($log, arguments);
       
  7005     };
       
  7006   }];
       
  7007 }
       
  7008 
       
  7009 /**
       
  7010  * Parse headers into key value object
       
  7011  *
       
  7012  * @param {string} headers Raw headers as a string
       
  7013  * @returns {Object} Parsed headers as key value object
       
  7014  */
       
  7015 function parseHeaders(headers) {
       
  7016   var parsed = {}, key, val, i;
       
  7017 
       
  7018   if (!headers) return parsed;
       
  7019 
       
  7020   forEach(headers.split('\n'), function(line) {
       
  7021     i = line.indexOf(':');
       
  7022     key = lowercase(trim(line.substr(0, i)));
       
  7023     val = trim(line.substr(i + 1));
       
  7024 
       
  7025     if (key) {
       
  7026       if (parsed[key]) {
       
  7027         parsed[key] += ', ' + val;
       
  7028       } else {
       
  7029         parsed[key] = val;
       
  7030       }
       
  7031     }
       
  7032   });
       
  7033 
       
  7034   return parsed;
       
  7035 }
       
  7036 
       
  7037 
       
  7038 /**
       
  7039  * Returns a function that provides access to parsed headers.
       
  7040  *
       
  7041  * Headers are lazy parsed when first requested.
       
  7042  * @see parseHeaders
       
  7043  *
       
  7044  * @param {(string|Object)} headers Headers to provide access to.
       
  7045  * @returns {function(string=)} Returns a getter function which if called with:
       
  7046  *
       
  7047  *   - if called with single an argument returns a single header value or null
       
  7048  *   - if called with no arguments returns an object containing all headers.
       
  7049  */
       
  7050 function headersGetter(headers) {
       
  7051   var headersObj = isObject(headers) ? headers : undefined;
       
  7052 
       
  7053   return function(name) {
       
  7054     if (!headersObj) headersObj =  parseHeaders(headers);
       
  7055 
       
  7056     if (name) {
       
  7057       return headersObj[lowercase(name)] || null;
       
  7058     }
       
  7059 
       
  7060     return headersObj;
       
  7061   };
       
  7062 }
       
  7063 
       
  7064 
       
  7065 /**
       
  7066  * Chain all given functions
       
  7067  *
       
  7068  * This function is used for both request and response transforming
       
  7069  *
       
  7070  * @param {*} data Data to transform.
       
  7071  * @param {function(string=)} headers Http headers getter fn.
       
  7072  * @param {(Function|Array.<Function>)} fns Function or an array of functions.
       
  7073  * @returns {*} Transformed data.
       
  7074  */
       
  7075 function transformData(data, headers, fns) {
       
  7076   if (isFunction(fns))
       
  7077     return fns(data, headers);
       
  7078 
       
  7079   forEach(fns, function(fn) {
       
  7080     data = fn(data, headers);
       
  7081   });
       
  7082 
       
  7083   return data;
       
  7084 }
       
  7085 
       
  7086 
       
  7087 function isSuccess(status) {
       
  7088   return 200 <= status && status < 300;
       
  7089 }
       
  7090 
       
  7091 
       
  7092 function $HttpProvider() {
       
  7093   var JSON_START = /^\s*(\[|\{[^\{])/,
       
  7094       JSON_END = /[\}\]]\s*$/,
       
  7095       PROTECTION_PREFIX = /^\)\]\}',?\n/,
       
  7096       CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': 'application/json;charset=utf-8'};
       
  7097 
       
  7098   var defaults = this.defaults = {
       
  7099     // transform incoming response data
       
  7100     transformResponse: [function(data) {
       
  7101       if (isString(data)) {
       
  7102         // strip json vulnerability protection prefix
       
  7103         data = data.replace(PROTECTION_PREFIX, '');
       
  7104         if (JSON_START.test(data) && JSON_END.test(data))
       
  7105           data = fromJson(data);
       
  7106       }
       
  7107       return data;
       
  7108     }],
       
  7109 
       
  7110     // transform outgoing request data
       
  7111     transformRequest: [function(d) {
       
  7112       return isObject(d) && !isFile(d) && !isBlob(d) ? toJson(d) : d;
       
  7113     }],
       
  7114 
       
  7115     // default headers
       
  7116     headers: {
       
  7117       common: {
       
  7118         'Accept': 'application/json, text/plain, */*'
       
  7119       },
       
  7120       post:   copy(CONTENT_TYPE_APPLICATION_JSON),
       
  7121       put:    copy(CONTENT_TYPE_APPLICATION_JSON),
       
  7122       patch:  copy(CONTENT_TYPE_APPLICATION_JSON)
       
  7123     },
       
  7124 
       
  7125     xsrfCookieName: 'XSRF-TOKEN',
       
  7126     xsrfHeaderName: 'X-XSRF-TOKEN'
       
  7127   };
       
  7128 
       
  7129   /**
       
  7130    * Are ordered by request, i.e. they are applied in the same order as the
       
  7131    * array, on request, but reverse order, on response.
       
  7132    */
       
  7133   var interceptorFactories = this.interceptors = [];
       
  7134 
       
  7135   /**
       
  7136    * For historical reasons, response interceptors are ordered by the order in which
       
  7137    * they are applied to the response. (This is the opposite of interceptorFactories)
       
  7138    */
       
  7139   var responseInterceptorFactories = this.responseInterceptors = [];
       
  7140 
       
  7141   this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector',
       
  7142       function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) {
       
  7143 
       
  7144     var defaultCache = $cacheFactory('$http');
       
  7145 
       
  7146     /**
       
  7147      * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
       
  7148      * The reversal is needed so that we can build up the interception chain around the
       
  7149      * server request.
       
  7150      */
       
  7151     var reversedInterceptors = [];
       
  7152 
       
  7153     forEach(interceptorFactories, function(interceptorFactory) {
       
  7154       reversedInterceptors.unshift(isString(interceptorFactory)
       
  7155           ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
       
  7156     });
       
  7157 
       
  7158     forEach(responseInterceptorFactories, function(interceptorFactory, index) {
       
  7159       var responseFn = isString(interceptorFactory)
       
  7160           ? $injector.get(interceptorFactory)
       
  7161           : $injector.invoke(interceptorFactory);
       
  7162 
       
  7163       /**
       
  7164        * Response interceptors go before "around" interceptors (no real reason, just
       
  7165        * had to pick one.) But they are already reversed, so we can't use unshift, hence
       
  7166        * the splice.
       
  7167        */
       
  7168       reversedInterceptors.splice(index, 0, {
       
  7169         response: function(response) {
       
  7170           return responseFn($q.when(response));
       
  7171         },
       
  7172         responseError: function(response) {
       
  7173           return responseFn($q.reject(response));
       
  7174         }
       
  7175       });
       
  7176     });
       
  7177 
       
  7178 
       
  7179     /**
       
  7180      * @ngdoc service
       
  7181      * @kind function
       
  7182      * @name $http
       
  7183      * @requires ng.$httpBackend
       
  7184      * @requires $cacheFactory
       
  7185      * @requires $rootScope
       
  7186      * @requires $q
       
  7187      * @requires $injector
       
  7188      *
       
  7189      * @description
       
  7190      * The `$http` service is a core Angular service that facilitates communication with the remote
       
  7191      * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
       
  7192      * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
       
  7193      *
       
  7194      * For unit testing applications that use `$http` service, see
       
  7195      * {@link ngMock.$httpBackend $httpBackend mock}.
       
  7196      *
       
  7197      * For a higher level of abstraction, please check out the {@link ngResource.$resource
       
  7198      * $resource} service.
       
  7199      *
       
  7200      * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
       
  7201      * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
       
  7202      * it is important to familiarize yourself with these APIs and the guarantees they provide.
       
  7203      *
       
  7204      *
       
  7205      * # General usage
       
  7206      * The `$http` service is a function which takes a single argument — a configuration object —
       
  7207      * that is used to generate an HTTP request and returns  a {@link ng.$q promise}
       
  7208      * with two $http specific methods: `success` and `error`.
       
  7209      *
       
  7210      * ```js
       
  7211      *   $http({method: 'GET', url: '/someUrl'}).
       
  7212      *     success(function(data, status, headers, config) {
       
  7213      *       // this callback will be called asynchronously
       
  7214      *       // when the response is available
       
  7215      *     }).
       
  7216      *     error(function(data, status, headers, config) {
       
  7217      *       // called asynchronously if an error occurs
       
  7218      *       // or server returns response with an error status.
       
  7219      *     });
       
  7220      * ```
       
  7221      *
       
  7222      * Since the returned value of calling the $http function is a `promise`, you can also use
       
  7223      * the `then` method to register callbacks, and these callbacks will receive a single argument –
       
  7224      * an object representing the response. See the API signature and type info below for more
       
  7225      * details.
       
  7226      *
       
  7227      * A response status code between 200 and 299 is considered a success status and
       
  7228      * will result in the success callback being called. Note that if the response is a redirect,
       
  7229      * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
       
  7230      * called for such responses.
       
  7231      *
       
  7232      * # Writing Unit Tests that use $http
       
  7233      * When unit testing (using {@link ngMock ngMock}), it is necessary to call
       
  7234      * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
       
  7235      * request using trained responses.
       
  7236      *
       
  7237      * ```
       
  7238      * $httpBackend.expectGET(...);
       
  7239      * $http.get(...);
       
  7240      * $httpBackend.flush();
       
  7241      * ```
       
  7242      *
       
  7243      * # Shortcut methods
       
  7244      *
       
  7245      * Shortcut methods are also available. All shortcut methods require passing in the URL, and
       
  7246      * request data must be passed in for POST/PUT requests.
       
  7247      *
       
  7248      * ```js
       
  7249      *   $http.get('/someUrl').success(successCallback);
       
  7250      *   $http.post('/someUrl', data).success(successCallback);
       
  7251      * ```
       
  7252      *
       
  7253      * Complete list of shortcut methods:
       
  7254      *
       
  7255      * - {@link ng.$http#get $http.get}
       
  7256      * - {@link ng.$http#head $http.head}
       
  7257      * - {@link ng.$http#post $http.post}
       
  7258      * - {@link ng.$http#put $http.put}
       
  7259      * - {@link ng.$http#delete $http.delete}
       
  7260      * - {@link ng.$http#jsonp $http.jsonp}
       
  7261      *
       
  7262      *
       
  7263      * # Setting HTTP Headers
       
  7264      *
       
  7265      * The $http service will automatically add certain HTTP headers to all requests. These defaults
       
  7266      * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
       
  7267      * object, which currently contains this default configuration:
       
  7268      *
       
  7269      * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
       
  7270      *   - `Accept: application/json, text/plain, * / *`
       
  7271      * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
       
  7272      *   - `Content-Type: application/json`
       
  7273      * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
       
  7274      *   - `Content-Type: application/json`
       
  7275      *
       
  7276      * To add or overwrite these defaults, simply add or remove a property from these configuration
       
  7277      * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
       
  7278      * with the lowercased HTTP method name as the key, e.g.
       
  7279      * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }.
       
  7280      *
       
  7281      * The defaults can also be set at runtime via the `$http.defaults` object in the same
       
  7282      * fashion. For example:
       
  7283      *
       
  7284      * ```
       
  7285      * module.run(function($http) {
       
  7286      *   $http.defaults.headers.common.Authentication = 'Basic YmVlcDpib29w'
       
  7287      * });
       
  7288      * ```
       
  7289      *
       
  7290      * In addition, you can supply a `headers` property in the config object passed when
       
  7291      * calling `$http(config)`, which overrides the defaults without changing them globally.
       
  7292      *
       
  7293      *
       
  7294      * # Transforming Requests and Responses
       
  7295      *
       
  7296      * Both requests and responses can be transformed using transform functions. By default, Angular
       
  7297      * applies these transformations:
       
  7298      *
       
  7299      * Request transformations:
       
  7300      *
       
  7301      * - If the `data` property of the request configuration object contains an object, serialize it
       
  7302      *   into JSON format.
       
  7303      *
       
  7304      * Response transformations:
       
  7305      *
       
  7306      *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
       
  7307      *  - If JSON response is detected, deserialize it using a JSON parser.
       
  7308      *
       
  7309      * To globally augment or override the default transforms, modify the
       
  7310      * `$httpProvider.defaults.transformRequest` and `$httpProvider.defaults.transformResponse`
       
  7311      * properties. These properties are by default an array of transform functions, which allows you
       
  7312      * to `push` or `unshift` a new transformation function into the transformation chain. You can
       
  7313      * also decide to completely override any default transformations by assigning your
       
  7314      * transformation functions to these properties directly without the array wrapper.  These defaults
       
  7315      * are again available on the $http factory at run-time, which may be useful if you have run-time
       
  7316      * services you wish to be involved in your transformations.
       
  7317      *
       
  7318      * Similarly, to locally override the request/response transforms, augment the
       
  7319      * `transformRequest` and/or `transformResponse` properties of the configuration object passed
       
  7320      * into `$http`.
       
  7321      *
       
  7322      *
       
  7323      * # Caching
       
  7324      *
       
  7325      * To enable caching, set the request configuration `cache` property to `true` (to use default
       
  7326      * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
       
  7327      * When the cache is enabled, `$http` stores the response from the server in the specified
       
  7328      * cache. The next time the same request is made, the response is served from the cache without
       
  7329      * sending a request to the server.
       
  7330      *
       
  7331      * Note that even if the response is served from cache, delivery of the data is asynchronous in
       
  7332      * the same way that real requests are.
       
  7333      *
       
  7334      * If there are multiple GET requests for the same URL that should be cached using the same
       
  7335      * cache, but the cache is not populated yet, only one request to the server will be made and
       
  7336      * the remaining requests will be fulfilled using the response from the first request.
       
  7337      *
       
  7338      * You can change the default cache to a new object (built with
       
  7339      * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
       
  7340      * {@link ng.$http#properties_defaults `$http.defaults.cache`} property. All requests who set
       
  7341      * their `cache` property to `true` will now use this cache object.
       
  7342      *
       
  7343      * If you set the default cache to `false` then only requests that specify their own custom
       
  7344      * cache object will be cached.
       
  7345      *
       
  7346      * # Interceptors
       
  7347      *
       
  7348      * Before you start creating interceptors, be sure to understand the
       
  7349      * {@link ng.$q $q and deferred/promise APIs}.
       
  7350      *
       
  7351      * For purposes of global error handling, authentication, or any kind of synchronous or
       
  7352      * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
       
  7353      * able to intercept requests before they are handed to the server and
       
  7354      * responses before they are handed over to the application code that
       
  7355      * initiated these requests. The interceptors leverage the {@link ng.$q
       
  7356      * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
       
  7357      *
       
  7358      * The interceptors are service factories that are registered with the `$httpProvider` by
       
  7359      * adding them to the `$httpProvider.interceptors` array. The factory is called and
       
  7360      * injected with dependencies (if specified) and returns the interceptor.
       
  7361      *
       
  7362      * There are two kinds of interceptors (and two kinds of rejection interceptors):
       
  7363      *
       
  7364      *   * `request`: interceptors get called with http `config` object. The function is free to
       
  7365      *     modify the `config` or create a new one. The function needs to return the `config`
       
  7366      *     directly or as a promise.
       
  7367      *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
       
  7368      *     resolved with a rejection.
       
  7369      *   * `response`: interceptors get called with http `response` object. The function is free to
       
  7370      *     modify the `response` or create a new one. The function needs to return the `response`
       
  7371      *     directly or as a promise.
       
  7372      *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
       
  7373      *     resolved with a rejection.
       
  7374      *
       
  7375      *
       
  7376      * ```js
       
  7377      *   // register the interceptor as a service
       
  7378      *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
       
  7379      *     return {
       
  7380      *       // optional method
       
  7381      *       'request': function(config) {
       
  7382      *         // do something on success
       
  7383      *         return config || $q.when(config);
       
  7384      *       },
       
  7385      *
       
  7386      *       // optional method
       
  7387      *      'requestError': function(rejection) {
       
  7388      *         // do something on error
       
  7389      *         if (canRecover(rejection)) {
       
  7390      *           return responseOrNewPromise
       
  7391      *         }
       
  7392      *         return $q.reject(rejection);
       
  7393      *       },
       
  7394      *
       
  7395      *
       
  7396      *
       
  7397      *       // optional method
       
  7398      *       'response': function(response) {
       
  7399      *         // do something on success
       
  7400      *         return response || $q.when(response);
       
  7401      *       },
       
  7402      *
       
  7403      *       // optional method
       
  7404      *      'responseError': function(rejection) {
       
  7405      *         // do something on error
       
  7406      *         if (canRecover(rejection)) {
       
  7407      *           return responseOrNewPromise
       
  7408      *         }
       
  7409      *         return $q.reject(rejection);
       
  7410      *       }
       
  7411      *     };
       
  7412      *   });
       
  7413      *
       
  7414      *   $httpProvider.interceptors.push('myHttpInterceptor');
       
  7415      *
       
  7416      *
       
  7417      *   // alternatively, register the interceptor via an anonymous factory
       
  7418      *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
       
  7419      *     return {
       
  7420      *      'request': function(config) {
       
  7421      *          // same as above
       
  7422      *       },
       
  7423      *
       
  7424      *       'response': function(response) {
       
  7425      *          // same as above
       
  7426      *       }
       
  7427      *     };
       
  7428      *   });
       
  7429      * ```
       
  7430      *
       
  7431      * # Response interceptors (DEPRECATED)
       
  7432      *
       
  7433      * Before you start creating interceptors, be sure to understand the
       
  7434      * {@link ng.$q $q and deferred/promise APIs}.
       
  7435      *
       
  7436      * For purposes of global error handling, authentication or any kind of synchronous or
       
  7437      * asynchronous preprocessing of received responses, it is desirable to be able to intercept
       
  7438      * responses for http requests before they are handed over to the application code that
       
  7439      * initiated these requests. The response interceptors leverage the {@link ng.$q
       
  7440      * promise apis} to fulfil this need for both synchronous and asynchronous preprocessing.
       
  7441      *
       
  7442      * The interceptors are service factories that are registered with the $httpProvider by
       
  7443      * adding them to the `$httpProvider.responseInterceptors` array. The factory is called and
       
  7444      * injected with dependencies (if specified) and returns the interceptor  — a function that
       
  7445      * takes a {@link ng.$q promise} and returns the original or a new promise.
       
  7446      *
       
  7447      * ```js
       
  7448      *   // register the interceptor as a service
       
  7449      *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
       
  7450      *     return function(promise) {
       
  7451      *       return promise.then(function(response) {
       
  7452      *         // do something on success
       
  7453      *         return response;
       
  7454      *       }, function(response) {
       
  7455      *         // do something on error
       
  7456      *         if (canRecover(response)) {
       
  7457      *           return responseOrNewPromise
       
  7458      *         }
       
  7459      *         return $q.reject(response);
       
  7460      *       });
       
  7461      *     }
       
  7462      *   });
       
  7463      *
       
  7464      *   $httpProvider.responseInterceptors.push('myHttpInterceptor');
       
  7465      *
       
  7466      *
       
  7467      *   // register the interceptor via an anonymous factory
       
  7468      *   $httpProvider.responseInterceptors.push(function($q, dependency1, dependency2) {
       
  7469      *     return function(promise) {
       
  7470      *       // same as above
       
  7471      *     }
       
  7472      *   });
       
  7473      * ```
       
  7474      *
       
  7475      *
       
  7476      * # Security Considerations
       
  7477      *
       
  7478      * When designing web applications, consider security threats from:
       
  7479      *
       
  7480      * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
       
  7481      * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
       
  7482      *
       
  7483      * Both server and the client must cooperate in order to eliminate these threats. Angular comes
       
  7484      * pre-configured with strategies that address these issues, but for this to work backend server
       
  7485      * cooperation is required.
       
  7486      *
       
  7487      * ## JSON Vulnerability Protection
       
  7488      *
       
  7489      * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
       
  7490      * allows third party website to turn your JSON resource URL into
       
  7491      * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
       
  7492      * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
       
  7493      * Angular will automatically strip the prefix before processing it as JSON.
       
  7494      *
       
  7495      * For example if your server needs to return:
       
  7496      * ```js
       
  7497      * ['one','two']
       
  7498      * ```
       
  7499      *
       
  7500      * which is vulnerable to attack, your server can return:
       
  7501      * ```js
       
  7502      * )]}',
       
  7503      * ['one','two']
       
  7504      * ```
       
  7505      *
       
  7506      * Angular will strip the prefix, before processing the JSON.
       
  7507      *
       
  7508      *
       
  7509      * ## Cross Site Request Forgery (XSRF) Protection
       
  7510      *
       
  7511      * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
       
  7512      * an unauthorized site can gain your user's private data. Angular provides a mechanism
       
  7513      * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
       
  7514      * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
       
  7515      * JavaScript that runs on your domain could read the cookie, your server can be assured that
       
  7516      * the XHR came from JavaScript running on your domain. The header will not be set for
       
  7517      * cross-domain requests.
       
  7518      *
       
  7519      * To take advantage of this, your server needs to set a token in a JavaScript readable session
       
  7520      * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
       
  7521      * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
       
  7522      * that only JavaScript running on your domain could have sent the request. The token must be
       
  7523      * unique for each user and must be verifiable by the server (to prevent the JavaScript from
       
  7524      * making up its own tokens). We recommend that the token is a digest of your site's
       
  7525      * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography))
       
  7526      * for added security.
       
  7527      *
       
  7528      * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
       
  7529      * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
       
  7530      * or the per-request config object.
       
  7531      *
       
  7532      *
       
  7533      * @param {object} config Object describing the request to be made and how it should be
       
  7534      *    processed. The object has following properties:
       
  7535      *
       
  7536      *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
       
  7537      *    - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
       
  7538      *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be turned
       
  7539      *      to `?key1=value1&key2=value2` after the url. If the value is not a string, it will be
       
  7540      *      JSONified.
       
  7541      *    - **data** – `{string|Object}` – Data to be sent as the request message data.
       
  7542      *    - **headers** – `{Object}` – Map of strings or functions which return strings representing
       
  7543      *      HTTP headers to send to the server. If the return value of a function is null, the
       
  7544      *      header will not be sent.
       
  7545      *    - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
       
  7546      *    - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
       
  7547      *    - **transformRequest** –
       
  7548      *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
       
  7549      *      transform function or an array of such functions. The transform function takes the http
       
  7550      *      request body and headers and returns its transformed (typically serialized) version.
       
  7551      *    - **transformResponse** –
       
  7552      *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
       
  7553      *      transform function or an array of such functions. The transform function takes the http
       
  7554      *      response body and headers and returns its transformed (typically deserialized) version.
       
  7555      *    - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
       
  7556      *      GET request, otherwise if a cache instance built with
       
  7557      *      {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
       
  7558      *      caching.
       
  7559      *    - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
       
  7560      *      that should abort the request when resolved.
       
  7561      *    - **withCredentials** - `{boolean}` - whether to to set the `withCredentials` flag on the
       
  7562      *      XHR object. See [requests with credentials]https://developer.mozilla.org/en/http_access_control#section_5
       
  7563      *      for more information.
       
  7564      *    - **responseType** - `{string}` - see
       
  7565      *      [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
       
  7566      *
       
  7567      * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the
       
  7568      *   standard `then` method and two http specific methods: `success` and `error`. The `then`
       
  7569      *   method takes two arguments a success and an error callback which will be called with a
       
  7570      *   response object. The `success` and `error` methods take a single argument - a function that
       
  7571      *   will be called when the request succeeds or fails respectively. The arguments passed into
       
  7572      *   these functions are destructured representation of the response object passed into the
       
  7573      *   `then` method. The response object has these properties:
       
  7574      *
       
  7575      *   - **data** – `{string|Object}` – The response body transformed with the transform
       
  7576      *     functions.
       
  7577      *   - **status** – `{number}` – HTTP status code of the response.
       
  7578      *   - **headers** – `{function([headerName])}` – Header getter function.
       
  7579      *   - **config** – `{Object}` – The configuration object that was used to generate the request.
       
  7580      *
       
  7581      * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
       
  7582      *   requests. This is primarily meant to be used for debugging purposes.
       
  7583      *
       
  7584      *
       
  7585      * @example
       
  7586 <example>
       
  7587 <file name="index.html">
       
  7588   <div ng-controller="FetchCtrl">
       
  7589     <select ng-model="method">
       
  7590       <option>GET</option>
       
  7591       <option>JSONP</option>
       
  7592     </select>
       
  7593     <input type="text" ng-model="url" size="80"/>
       
  7594     <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
       
  7595     <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
       
  7596     <button id="samplejsonpbtn"
       
  7597       ng-click="updateModel('JSONP',
       
  7598                     'http://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
       
  7599       Sample JSONP
       
  7600     </button>
       
  7601     <button id="invalidjsonpbtn"
       
  7602       ng-click="updateModel('JSONP', 'http://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
       
  7603         Invalid JSONP
       
  7604       </button>
       
  7605     <pre>http status code: {{status}}</pre>
       
  7606     <pre>http response data: {{data}}</pre>
       
  7607   </div>
       
  7608 </file>
       
  7609 <file name="script.js">
       
  7610   function FetchCtrl($scope, $http, $templateCache) {
       
  7611     $scope.method = 'GET';
       
  7612     $scope.url = 'http-hello.html';
       
  7613 
       
  7614     $scope.fetch = function() {
       
  7615       $scope.code = null;
       
  7616       $scope.response = null;
       
  7617 
       
  7618       $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
       
  7619         success(function(data, status) {
       
  7620           $scope.status = status;
       
  7621           $scope.data = data;
       
  7622         }).
       
  7623         error(function(data, status) {
       
  7624           $scope.data = data || "Request failed";
       
  7625           $scope.status = status;
       
  7626       });
       
  7627     };
       
  7628 
       
  7629     $scope.updateModel = function(method, url) {
       
  7630       $scope.method = method;
       
  7631       $scope.url = url;
       
  7632     };
       
  7633   }
       
  7634 </file>
       
  7635 <file name="http-hello.html">
       
  7636   Hello, $http!
       
  7637 </file>
       
  7638 <file name="protractor.js" type="protractor">
       
  7639   var status = element(by.binding('status'));
       
  7640   var data = element(by.binding('data'));
       
  7641   var fetchBtn = element(by.id('fetchbtn'));
       
  7642   var sampleGetBtn = element(by.id('samplegetbtn'));
       
  7643   var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
       
  7644   var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
       
  7645 
       
  7646   it('should make an xhr GET request', function() {
       
  7647     sampleGetBtn.click();
       
  7648     fetchBtn.click();
       
  7649     expect(status.getText()).toMatch('200');
       
  7650     expect(data.getText()).toMatch(/Hello, \$http!/);
       
  7651   });
       
  7652 
       
  7653   it('should make a JSONP request to angularjs.org', function() {
       
  7654     sampleJsonpBtn.click();
       
  7655     fetchBtn.click();
       
  7656     expect(status.getText()).toMatch('200');
       
  7657     expect(data.getText()).toMatch(/Super Hero!/);
       
  7658   });
       
  7659 
       
  7660   it('should make JSONP request to invalid URL and invoke the error handler',
       
  7661       function() {
       
  7662     invalidJsonpBtn.click();
       
  7663     fetchBtn.click();
       
  7664     expect(status.getText()).toMatch('0');
       
  7665     expect(data.getText()).toMatch('Request failed');
       
  7666   });
       
  7667 </file>
       
  7668 </example>
       
  7669      */
       
  7670     function $http(requestConfig) {
       
  7671       var config = {
       
  7672         method: 'get',
       
  7673         transformRequest: defaults.transformRequest,
       
  7674         transformResponse: defaults.transformResponse
       
  7675       };
       
  7676       var headers = mergeHeaders(requestConfig);
       
  7677 
       
  7678       extend(config, requestConfig);
       
  7679       config.headers = headers;
       
  7680       config.method = uppercase(config.method);
       
  7681 
       
  7682       var xsrfValue = urlIsSameOrigin(config.url)
       
  7683           ? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName]
       
  7684           : undefined;
       
  7685       if (xsrfValue) {
       
  7686         headers[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
       
  7687       }
       
  7688 
       
  7689 
       
  7690       var serverRequest = function(config) {
       
  7691         headers = config.headers;
       
  7692         var reqData = transformData(config.data, headersGetter(headers), config.transformRequest);
       
  7693 
       
  7694         // strip content-type if data is undefined
       
  7695         if (isUndefined(config.data)) {
       
  7696           forEach(headers, function(value, header) {
       
  7697             if (lowercase(header) === 'content-type') {
       
  7698                 delete headers[header];
       
  7699             }
       
  7700           });
       
  7701         }
       
  7702 
       
  7703         if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
       
  7704           config.withCredentials = defaults.withCredentials;
       
  7705         }
       
  7706 
       
  7707         // send request
       
  7708         return sendReq(config, reqData, headers).then(transformResponse, transformResponse);
       
  7709       };
       
  7710 
       
  7711       var chain = [serverRequest, undefined];
       
  7712       var promise = $q.when(config);
       
  7713 
       
  7714       // apply interceptors
       
  7715       forEach(reversedInterceptors, function(interceptor) {
       
  7716         if (interceptor.request || interceptor.requestError) {
       
  7717           chain.unshift(interceptor.request, interceptor.requestError);
       
  7718         }
       
  7719         if (interceptor.response || interceptor.responseError) {
       
  7720           chain.push(interceptor.response, interceptor.responseError);
       
  7721         }
       
  7722       });
       
  7723 
       
  7724       while(chain.length) {
       
  7725         var thenFn = chain.shift();
       
  7726         var rejectFn = chain.shift();
       
  7727 
       
  7728         promise = promise.then(thenFn, rejectFn);
       
  7729       }
       
  7730 
       
  7731       promise.success = function(fn) {
       
  7732         promise.then(function(response) {
       
  7733           fn(response.data, response.status, response.headers, config);
       
  7734         });
       
  7735         return promise;
       
  7736       };
       
  7737 
       
  7738       promise.error = function(fn) {
       
  7739         promise.then(null, function(response) {
       
  7740           fn(response.data, response.status, response.headers, config);
       
  7741         });
       
  7742         return promise;
       
  7743       };
       
  7744 
       
  7745       return promise;
       
  7746 
       
  7747       function transformResponse(response) {
       
  7748         // make a copy since the response must be cacheable
       
  7749         var resp = extend({}, response, {
       
  7750           data: transformData(response.data, response.headers, config.transformResponse)
       
  7751         });
       
  7752         return (isSuccess(response.status))
       
  7753           ? resp
       
  7754           : $q.reject(resp);
       
  7755       }
       
  7756 
       
  7757       function mergeHeaders(config) {
       
  7758         var defHeaders = defaults.headers,
       
  7759             reqHeaders = extend({}, config.headers),
       
  7760             defHeaderName, lowercaseDefHeaderName, reqHeaderName;
       
  7761 
       
  7762         defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
       
  7763 
       
  7764         // execute if header value is function
       
  7765         execHeaders(defHeaders);
       
  7766         execHeaders(reqHeaders);
       
  7767 
       
  7768         // using for-in instead of forEach to avoid unecessary iteration after header has been found
       
  7769         defaultHeadersIteration:
       
  7770         for (defHeaderName in defHeaders) {
       
  7771           lowercaseDefHeaderName = lowercase(defHeaderName);
       
  7772 
       
  7773           for (reqHeaderName in reqHeaders) {
       
  7774             if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
       
  7775               continue defaultHeadersIteration;
       
  7776             }
       
  7777           }
       
  7778 
       
  7779           reqHeaders[defHeaderName] = defHeaders[defHeaderName];
       
  7780         }
       
  7781 
       
  7782         return reqHeaders;
       
  7783 
       
  7784         function execHeaders(headers) {
       
  7785           var headerContent;
       
  7786 
       
  7787           forEach(headers, function(headerFn, header) {
       
  7788             if (isFunction(headerFn)) {
       
  7789               headerContent = headerFn();
       
  7790               if (headerContent != null) {
       
  7791                 headers[header] = headerContent;
       
  7792               } else {
       
  7793                 delete headers[header];
       
  7794               }
       
  7795             }
       
  7796           });
       
  7797         }
       
  7798       }
       
  7799     }
       
  7800 
       
  7801     $http.pendingRequests = [];
       
  7802 
       
  7803     /**
       
  7804      * @ngdoc method
       
  7805      * @name $http#get
       
  7806      *
       
  7807      * @description
       
  7808      * Shortcut method to perform `GET` request.
       
  7809      *
       
  7810      * @param {string} url Relative or absolute URL specifying the destination of the request
       
  7811      * @param {Object=} config Optional configuration object
       
  7812      * @returns {HttpPromise} Future object
       
  7813      */
       
  7814 
       
  7815     /**
       
  7816      * @ngdoc method
       
  7817      * @name $http#delete
       
  7818      *
       
  7819      * @description
       
  7820      * Shortcut method to perform `DELETE` request.
       
  7821      *
       
  7822      * @param {string} url Relative or absolute URL specifying the destination of the request
       
  7823      * @param {Object=} config Optional configuration object
       
  7824      * @returns {HttpPromise} Future object
       
  7825      */
       
  7826 
       
  7827     /**
       
  7828      * @ngdoc method
       
  7829      * @name $http#head
       
  7830      *
       
  7831      * @description
       
  7832      * Shortcut method to perform `HEAD` request.
       
  7833      *
       
  7834      * @param {string} url Relative or absolute URL specifying the destination of the request
       
  7835      * @param {Object=} config Optional configuration object
       
  7836      * @returns {HttpPromise} Future object
       
  7837      */
       
  7838 
       
  7839     /**
       
  7840      * @ngdoc method
       
  7841      * @name $http#jsonp
       
  7842      *
       
  7843      * @description
       
  7844      * Shortcut method to perform `JSONP` request.
       
  7845      *
       
  7846      * @param {string} url Relative or absolute URL specifying the destination of the request.
       
  7847      *                     Should contain `JSON_CALLBACK` string.
       
  7848      * @param {Object=} config Optional configuration object
       
  7849      * @returns {HttpPromise} Future object
       
  7850      */
       
  7851     createShortMethods('get', 'delete', 'head', 'jsonp');
       
  7852 
       
  7853     /**
       
  7854      * @ngdoc method
       
  7855      * @name $http#post
       
  7856      *
       
  7857      * @description
       
  7858      * Shortcut method to perform `POST` request.
       
  7859      *
       
  7860      * @param {string} url Relative or absolute URL specifying the destination of the request
       
  7861      * @param {*} data Request content
       
  7862      * @param {Object=} config Optional configuration object
       
  7863      * @returns {HttpPromise} Future object
       
  7864      */
       
  7865 
       
  7866     /**
       
  7867      * @ngdoc method
       
  7868      * @name $http#put
       
  7869      *
       
  7870      * @description
       
  7871      * Shortcut method to perform `PUT` request.
       
  7872      *
       
  7873      * @param {string} url Relative or absolute URL specifying the destination of the request
       
  7874      * @param {*} data Request content
       
  7875      * @param {Object=} config Optional configuration object
       
  7876      * @returns {HttpPromise} Future object
       
  7877      */
       
  7878     createShortMethodsWithData('post', 'put');
       
  7879 
       
  7880         /**
       
  7881          * @ngdoc property
       
  7882          * @name $http#defaults
       
  7883          *
       
  7884          * @description
       
  7885          * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
       
  7886          * default headers, withCredentials as well as request and response transformations.
       
  7887          *
       
  7888          * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
       
  7889          */
       
  7890     $http.defaults = defaults;
       
  7891 
       
  7892 
       
  7893     return $http;
       
  7894 
       
  7895 
       
  7896     function createShortMethods(names) {
       
  7897       forEach(arguments, function(name) {
       
  7898         $http[name] = function(url, config) {
       
  7899           return $http(extend(config || {}, {
       
  7900             method: name,
       
  7901             url: url
       
  7902           }));
       
  7903         };
       
  7904       });
       
  7905     }
       
  7906 
       
  7907 
       
  7908     function createShortMethodsWithData(name) {
       
  7909       forEach(arguments, function(name) {
       
  7910         $http[name] = function(url, data, config) {
       
  7911           return $http(extend(config || {}, {
       
  7912             method: name,
       
  7913             url: url,
       
  7914             data: data
       
  7915           }));
       
  7916         };
       
  7917       });
       
  7918     }
       
  7919 
       
  7920 
       
  7921     /**
       
  7922      * Makes the request.
       
  7923      *
       
  7924      * !!! ACCESSES CLOSURE VARS:
       
  7925      * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
       
  7926      */
       
  7927     function sendReq(config, reqData, reqHeaders) {
       
  7928       var deferred = $q.defer(),
       
  7929           promise = deferred.promise,
       
  7930           cache,
       
  7931           cachedResp,
       
  7932           url = buildUrl(config.url, config.params);
       
  7933 
       
  7934       $http.pendingRequests.push(config);
       
  7935       promise.then(removePendingReq, removePendingReq);
       
  7936 
       
  7937 
       
  7938       if ((config.cache || defaults.cache) && config.cache !== false && config.method == 'GET') {
       
  7939         cache = isObject(config.cache) ? config.cache
       
  7940               : isObject(defaults.cache) ? defaults.cache
       
  7941               : defaultCache;
       
  7942       }
       
  7943 
       
  7944       if (cache) {
       
  7945         cachedResp = cache.get(url);
       
  7946         if (isDefined(cachedResp)) {
       
  7947           if (cachedResp.then) {
       
  7948             // cached request has already been sent, but there is no response yet
       
  7949             cachedResp.then(removePendingReq, removePendingReq);
       
  7950             return cachedResp;
       
  7951           } else {
       
  7952             // serving from cache
       
  7953             if (isArray(cachedResp)) {
       
  7954               resolvePromise(cachedResp[1], cachedResp[0], copy(cachedResp[2]));
       
  7955             } else {
       
  7956               resolvePromise(cachedResp, 200, {});
       
  7957             }
       
  7958           }
       
  7959         } else {
       
  7960           // put the promise for the non-transformed response into cache as a placeholder
       
  7961           cache.put(url, promise);
       
  7962         }
       
  7963       }
       
  7964 
       
  7965       // if we won't have the response in cache, send the request to the backend
       
  7966       if (isUndefined(cachedResp)) {
       
  7967         $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
       
  7968             config.withCredentials, config.responseType);
       
  7969       }
       
  7970 
       
  7971       return promise;
       
  7972 
       
  7973 
       
  7974       /**
       
  7975        * Callback registered to $httpBackend():
       
  7976        *  - caches the response if desired
       
  7977        *  - resolves the raw $http promise
       
  7978        *  - calls $apply
       
  7979        */
       
  7980       function done(status, response, headersString) {
       
  7981         if (cache) {
       
  7982           if (isSuccess(status)) {
       
  7983             cache.put(url, [status, response, parseHeaders(headersString)]);
       
  7984           } else {
       
  7985             // remove promise from the cache
       
  7986             cache.remove(url);
       
  7987           }
       
  7988         }
       
  7989 
       
  7990         resolvePromise(response, status, headersString);
       
  7991         if (!$rootScope.$$phase) $rootScope.$apply();
       
  7992       }
       
  7993 
       
  7994 
       
  7995       /**
       
  7996        * Resolves the raw $http promise.
       
  7997        */
       
  7998       function resolvePromise(response, status, headers) {
       
  7999         // normalize internal statuses to 0
       
  8000         status = Math.max(status, 0);
       
  8001 
       
  8002         (isSuccess(status) ? deferred.resolve : deferred.reject)({
       
  8003           data: response,
       
  8004           status: status,
       
  8005           headers: headersGetter(headers),
       
  8006           config: config
       
  8007         });
       
  8008       }
       
  8009 
       
  8010 
       
  8011       function removePendingReq() {
       
  8012         var idx = indexOf($http.pendingRequests, config);
       
  8013         if (idx !== -1) $http.pendingRequests.splice(idx, 1);
       
  8014       }
       
  8015     }
       
  8016 
       
  8017 
       
  8018     function buildUrl(url, params) {
       
  8019           if (!params) return url;
       
  8020           var parts = [];
       
  8021           forEachSorted(params, function(value, key) {
       
  8022             if (value === null || isUndefined(value)) return;
       
  8023             if (!isArray(value)) value = [value];
       
  8024 
       
  8025             forEach(value, function(v) {
       
  8026               if (isObject(v)) {
       
  8027                 v = toJson(v);
       
  8028               }
       
  8029               parts.push(encodeUriQuery(key) + '=' +
       
  8030                          encodeUriQuery(v));
       
  8031             });
       
  8032           });
       
  8033           if(parts.length > 0) {
       
  8034             url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
       
  8035           }
       
  8036           return url;
       
  8037         }
       
  8038 
       
  8039 
       
  8040   }];
       
  8041 }
       
  8042 
       
  8043 function createXhr(method) {
       
  8044     //if IE and the method is not RFC2616 compliant, or if XMLHttpRequest
       
  8045     //is not available, try getting an ActiveXObject. Otherwise, use XMLHttpRequest
       
  8046     //if it is available
       
  8047     if (msie <= 8 && (!method.match(/^(get|post|head|put|delete|options)$/i) ||
       
  8048       !window.XMLHttpRequest)) {
       
  8049       return new window.ActiveXObject("Microsoft.XMLHTTP");
       
  8050     } else if (window.XMLHttpRequest) {
       
  8051       return new window.XMLHttpRequest();
       
  8052     }
       
  8053 
       
  8054     throw minErr('$httpBackend')('noxhr', "This browser does not support XMLHttpRequest.");
       
  8055 }
       
  8056 
       
  8057 /**
       
  8058  * @ngdoc service
       
  8059  * @name $httpBackend
       
  8060  * @requires $window
       
  8061  * @requires $document
       
  8062  *
       
  8063  * @description
       
  8064  * HTTP backend used by the {@link ng.$http service} that delegates to
       
  8065  * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
       
  8066  *
       
  8067  * You should never need to use this service directly, instead use the higher-level abstractions:
       
  8068  * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
       
  8069  *
       
  8070  * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
       
  8071  * $httpBackend} which can be trained with responses.
       
  8072  */
       
  8073 function $HttpBackendProvider() {
       
  8074   this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
       
  8075     return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]);
       
  8076   }];
       
  8077 }
       
  8078 
       
  8079 function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
       
  8080   var ABORTED = -1;
       
  8081 
       
  8082   // TODO(vojta): fix the signature
       
  8083   return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
       
  8084     var status;
       
  8085     $browser.$$incOutstandingRequestCount();
       
  8086     url = url || $browser.url();
       
  8087 
       
  8088     if (lowercase(method) == 'jsonp') {
       
  8089       var callbackId = '_' + (callbacks.counter++).toString(36);
       
  8090       callbacks[callbackId] = function(data) {
       
  8091         callbacks[callbackId].data = data;
       
  8092       };
       
  8093 
       
  8094       var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
       
  8095           function() {
       
  8096         if (callbacks[callbackId].data) {
       
  8097           completeRequest(callback, 200, callbacks[callbackId].data);
       
  8098         } else {
       
  8099           completeRequest(callback, status || -2);
       
  8100         }
       
  8101         callbacks[callbackId] = angular.noop;
       
  8102       });
       
  8103     } else {
       
  8104 
       
  8105       var xhr = createXhr(method);
       
  8106 
       
  8107       xhr.open(method, url, true);
       
  8108       forEach(headers, function(value, key) {
       
  8109         if (isDefined(value)) {
       
  8110             xhr.setRequestHeader(key, value);
       
  8111         }
       
  8112       });
       
  8113 
       
  8114       // In IE6 and 7, this might be called synchronously when xhr.send below is called and the
       
  8115       // response is in the cache. the promise api will ensure that to the app code the api is
       
  8116       // always async
       
  8117       xhr.onreadystatechange = function() {
       
  8118         // onreadystatechange might get called multiple times with readyState === 4 on mobile webkit caused by
       
  8119         // xhrs that are resolved while the app is in the background (see #5426).
       
  8120         // since calling completeRequest sets the `xhr` variable to null, we just check if it's not null before
       
  8121         // continuing
       
  8122         //
       
  8123         // we can't set xhr.onreadystatechange to undefined or delete it because that breaks IE8 (method=PATCH) and
       
  8124         // Safari respectively.
       
  8125         if (xhr && xhr.readyState == 4) {
       
  8126           var responseHeaders = null,
       
  8127               response = null;
       
  8128 
       
  8129           if(status !== ABORTED) {
       
  8130             responseHeaders = xhr.getAllResponseHeaders();
       
  8131 
       
  8132             // responseText is the old-school way of retrieving response (supported by IE8 & 9)
       
  8133             // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
       
  8134             response = ('response' in xhr) ? xhr.response : xhr.responseText;
       
  8135           }
       
  8136 
       
  8137           completeRequest(callback,
       
  8138               status || xhr.status,
       
  8139               response,
       
  8140               responseHeaders);
       
  8141         }
       
  8142       };
       
  8143 
       
  8144       if (withCredentials) {
       
  8145         xhr.withCredentials = true;
       
  8146       }
       
  8147 
       
  8148       if (responseType) {
       
  8149         try {
       
  8150           xhr.responseType = responseType;
       
  8151         } catch (e) {
       
  8152           // WebKit added support for the json responseType value on 09/03/2013
       
  8153           // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
       
  8154           // known to throw when setting the value "json" as the response type. Other older
       
  8155           // browsers implementing the responseType
       
  8156           //
       
  8157           // The json response type can be ignored if not supported, because JSON payloads are
       
  8158           // parsed on the client-side regardless.
       
  8159           if (responseType !== 'json') {
       
  8160             throw e;
       
  8161           }
       
  8162         }
       
  8163       }
       
  8164 
       
  8165       xhr.send(post || null);
       
  8166     }
       
  8167 
       
  8168     if (timeout > 0) {
       
  8169       var timeoutId = $browserDefer(timeoutRequest, timeout);
       
  8170     } else if (timeout && timeout.then) {
       
  8171       timeout.then(timeoutRequest);
       
  8172     }
       
  8173 
       
  8174 
       
  8175     function timeoutRequest() {
       
  8176       status = ABORTED;
       
  8177       jsonpDone && jsonpDone();
       
  8178       xhr && xhr.abort();
       
  8179     }
       
  8180 
       
  8181     function completeRequest(callback, status, response, headersString) {
       
  8182       // cancel timeout and subsequent timeout promise resolution
       
  8183       timeoutId && $browserDefer.cancel(timeoutId);
       
  8184       jsonpDone = xhr = null;
       
  8185 
       
  8186       // fix status code when it is 0 (0 status is undocumented).
       
  8187       // Occurs when accessing file resources or on Android 4.1 stock browser
       
  8188       // while retrieving files from application cache.
       
  8189       if (status === 0) {
       
  8190         status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
       
  8191       }
       
  8192 
       
  8193       // normalize IE bug (http://bugs.jquery.com/ticket/1450)
       
  8194       status = status == 1223 ? 204 : status;
       
  8195 
       
  8196       callback(status, response, headersString);
       
  8197       $browser.$$completeOutstandingRequest(noop);
       
  8198     }
       
  8199   };
       
  8200 
       
  8201   function jsonpReq(url, done) {
       
  8202     // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.:
       
  8203     // - fetches local scripts via XHR and evals them
       
  8204     // - adds and immediately removes script elements from the document
       
  8205     var script = rawDocument.createElement('script'),
       
  8206         doneWrapper = function() {
       
  8207           script.onreadystatechange = script.onload = script.onerror = null;
       
  8208           rawDocument.body.removeChild(script);
       
  8209           if (done) done();
       
  8210         };
       
  8211 
       
  8212     script.type = 'text/javascript';
       
  8213     script.src = url;
       
  8214 
       
  8215     if (msie && msie <= 8) {
       
  8216       script.onreadystatechange = function() {
       
  8217         if (/loaded|complete/.test(script.readyState)) {
       
  8218           doneWrapper();
       
  8219         }
       
  8220       };
       
  8221     } else {
       
  8222       script.onload = script.onerror = function() {
       
  8223         doneWrapper();
       
  8224       };
       
  8225     }
       
  8226 
       
  8227     rawDocument.body.appendChild(script);
       
  8228     return doneWrapper;
       
  8229   }
       
  8230 }
       
  8231 
       
  8232 var $interpolateMinErr = minErr('$interpolate');
       
  8233 
       
  8234 /**
       
  8235  * @ngdoc provider
       
  8236  * @name $interpolateProvider
       
  8237  * @function
       
  8238  *
       
  8239  * @description
       
  8240  *
       
  8241  * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
       
  8242  *
       
  8243  * @example
       
  8244 <example module="customInterpolationApp">
       
  8245 <file name="index.html">
       
  8246 <script>
       
  8247   var customInterpolationApp = angular.module('customInterpolationApp', []);
       
  8248 
       
  8249   customInterpolationApp.config(function($interpolateProvider) {
       
  8250     $interpolateProvider.startSymbol('//');
       
  8251     $interpolateProvider.endSymbol('//');
       
  8252   });
       
  8253 
       
  8254 
       
  8255   customInterpolationApp.controller('DemoController', function DemoController() {
       
  8256       this.label = "This binding is brought you by // interpolation symbols.";
       
  8257   });
       
  8258 </script>
       
  8259 <div ng-app="App" ng-controller="DemoController as demo">
       
  8260     //demo.label//
       
  8261 </div>
       
  8262 </file>
       
  8263 <file name="protractor.js" type="protractor">
       
  8264   it('should interpolate binding with custom symbols', function() {
       
  8265     expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
       
  8266   });
       
  8267 </file>
       
  8268 </example>
       
  8269  */
       
  8270 function $InterpolateProvider() {
       
  8271   var startSymbol = '{{';
       
  8272   var endSymbol = '}}';
       
  8273 
       
  8274   /**
       
  8275    * @ngdoc method
       
  8276    * @name $interpolateProvider#startSymbol
       
  8277    * @description
       
  8278    * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
       
  8279    *
       
  8280    * @param {string=} value new value to set the starting symbol to.
       
  8281    * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
       
  8282    */
       
  8283   this.startSymbol = function(value){
       
  8284     if (value) {
       
  8285       startSymbol = value;
       
  8286       return this;
       
  8287     } else {
       
  8288       return startSymbol;
       
  8289     }
       
  8290   };
       
  8291 
       
  8292   /**
       
  8293    * @ngdoc method
       
  8294    * @name $interpolateProvider#endSymbol
       
  8295    * @description
       
  8296    * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
       
  8297    *
       
  8298    * @param {string=} value new value to set the ending symbol to.
       
  8299    * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
       
  8300    */
       
  8301   this.endSymbol = function(value){
       
  8302     if (value) {
       
  8303       endSymbol = value;
       
  8304       return this;
       
  8305     } else {
       
  8306       return endSymbol;
       
  8307     }
       
  8308   };
       
  8309 
       
  8310 
       
  8311   this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
       
  8312     var startSymbolLength = startSymbol.length,
       
  8313         endSymbolLength = endSymbol.length;
       
  8314 
       
  8315     /**
       
  8316      * @ngdoc service
       
  8317      * @name $interpolate
       
  8318      * @function
       
  8319      *
       
  8320      * @requires $parse
       
  8321      * @requires $sce
       
  8322      *
       
  8323      * @description
       
  8324      *
       
  8325      * Compiles a string with markup into an interpolation function. This service is used by the
       
  8326      * HTML {@link ng.$compile $compile} service for data binding. See
       
  8327      * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
       
  8328      * interpolation markup.
       
  8329      *
       
  8330      *
       
  8331      * ```js
       
  8332      *   var $interpolate = ...; // injected
       
  8333      *   var exp = $interpolate('Hello {{name | uppercase}}!');
       
  8334      *   expect(exp({name:'Angular'}).toEqual('Hello ANGULAR!');
       
  8335      * ```
       
  8336      *
       
  8337      *
       
  8338      * @param {string} text The text with markup to interpolate.
       
  8339      * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
       
  8340      *    embedded expression in order to return an interpolation function. Strings with no
       
  8341      *    embedded expression will return null for the interpolation function.
       
  8342      * @param {string=} trustedContext when provided, the returned function passes the interpolated
       
  8343      *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
       
  8344      *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
       
  8345      *    provides Strict Contextual Escaping for details.
       
  8346      * @returns {function(context)} an interpolation function which is used to compute the
       
  8347      *    interpolated string. The function has these parameters:
       
  8348      *
       
  8349      *    * `context`: an object against which any expressions embedded in the strings are evaluated
       
  8350      *      against.
       
  8351      *
       
  8352      */
       
  8353     function $interpolate(text, mustHaveExpression, trustedContext) {
       
  8354       var startIndex,
       
  8355           endIndex,
       
  8356           index = 0,
       
  8357           parts = [],
       
  8358           length = text.length,
       
  8359           hasInterpolation = false,
       
  8360           fn,
       
  8361           exp,
       
  8362           concat = [];
       
  8363 
       
  8364       while(index < length) {
       
  8365         if ( ((startIndex = text.indexOf(startSymbol, index)) != -1) &&
       
  8366              ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1) ) {
       
  8367           (index != startIndex) && parts.push(text.substring(index, startIndex));
       
  8368           parts.push(fn = $parse(exp = text.substring(startIndex + startSymbolLength, endIndex)));
       
  8369           fn.exp = exp;
       
  8370           index = endIndex + endSymbolLength;
       
  8371           hasInterpolation = true;
       
  8372         } else {
       
  8373           // we did not find anything, so we have to add the remainder to the parts array
       
  8374           (index != length) && parts.push(text.substring(index));
       
  8375           index = length;
       
  8376         }
       
  8377       }
       
  8378 
       
  8379       if (!(length = parts.length)) {
       
  8380         // we added, nothing, must have been an empty string.
       
  8381         parts.push('');
       
  8382         length = 1;
       
  8383       }
       
  8384 
       
  8385       // Concatenating expressions makes it hard to reason about whether some combination of
       
  8386       // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
       
  8387       // single expression be used for iframe[src], object[src], etc., we ensure that the value
       
  8388       // that's used is assigned or constructed by some JS code somewhere that is more testable or
       
  8389       // make it obvious that you bound the value to some user controlled value.  This helps reduce
       
  8390       // the load when auditing for XSS issues.
       
  8391       if (trustedContext && parts.length > 1) {
       
  8392           throw $interpolateMinErr('noconcat',
       
  8393               "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
       
  8394               "interpolations that concatenate multiple expressions when a trusted value is " +
       
  8395               "required.  See http://docs.angularjs.org/api/ng.$sce", text);
       
  8396       }
       
  8397 
       
  8398       if (!mustHaveExpression  || hasInterpolation) {
       
  8399         concat.length = length;
       
  8400         fn = function(context) {
       
  8401           try {
       
  8402             for(var i = 0, ii = length, part; i<ii; i++) {
       
  8403               if (typeof (part = parts[i]) == 'function') {
       
  8404                 part = part(context);
       
  8405                 if (trustedContext) {
       
  8406                   part = $sce.getTrusted(trustedContext, part);
       
  8407                 } else {
       
  8408                   part = $sce.valueOf(part);
       
  8409                 }
       
  8410                 if (part === null || isUndefined(part)) {
       
  8411                   part = '';
       
  8412                 } else if (typeof part != 'string') {
       
  8413                   part = toJson(part);
       
  8414                 }
       
  8415               }
       
  8416               concat[i] = part;
       
  8417             }
       
  8418             return concat.join('');
       
  8419           }
       
  8420           catch(err) {
       
  8421             var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text,
       
  8422                 err.toString());
       
  8423             $exceptionHandler(newErr);
       
  8424           }
       
  8425         };
       
  8426         fn.exp = text;
       
  8427         fn.parts = parts;
       
  8428         return fn;
       
  8429       }
       
  8430     }
       
  8431 
       
  8432 
       
  8433     /**
       
  8434      * @ngdoc method
       
  8435      * @name $interpolate#startSymbol
       
  8436      * @description
       
  8437      * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
       
  8438      *
       
  8439      * Use {@link ng.$interpolateProvider#startSymbol $interpolateProvider#startSymbol} to change
       
  8440      * the symbol.
       
  8441      *
       
  8442      * @returns {string} start symbol.
       
  8443      */
       
  8444     $interpolate.startSymbol = function() {
       
  8445       return startSymbol;
       
  8446     };
       
  8447 
       
  8448 
       
  8449     /**
       
  8450      * @ngdoc method
       
  8451      * @name $interpolate#endSymbol
       
  8452      * @description
       
  8453      * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
       
  8454      *
       
  8455      * Use {@link ng.$interpolateProvider#endSymbol $interpolateProvider#endSymbol} to change
       
  8456      * the symbol.
       
  8457      *
       
  8458      * @returns {string} end symbol.
       
  8459      */
       
  8460     $interpolate.endSymbol = function() {
       
  8461       return endSymbol;
       
  8462     };
       
  8463 
       
  8464     return $interpolate;
       
  8465   }];
       
  8466 }
       
  8467 
       
  8468 function $IntervalProvider() {
       
  8469   this.$get = ['$rootScope', '$window', '$q',
       
  8470        function($rootScope,   $window,   $q) {
       
  8471     var intervals = {};
       
  8472 
       
  8473 
       
  8474      /**
       
  8475       * @ngdoc service
       
  8476       * @name $interval
       
  8477       *
       
  8478       * @description
       
  8479       * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
       
  8480       * milliseconds.
       
  8481       *
       
  8482       * The return value of registering an interval function is a promise. This promise will be
       
  8483       * notified upon each tick of the interval, and will be resolved after `count` iterations, or
       
  8484       * run indefinitely if `count` is not defined. The value of the notification will be the
       
  8485       * number of iterations that have run.
       
  8486       * To cancel an interval, call `$interval.cancel(promise)`.
       
  8487       *
       
  8488       * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
       
  8489       * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
       
  8490       * time.
       
  8491       *
       
  8492       * <div class="alert alert-warning">
       
  8493       * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
       
  8494       * with them.  In particular they are not automatically destroyed when a controller's scope or a
       
  8495       * directive's element are destroyed.
       
  8496       * You should take this into consideration and make sure to always cancel the interval at the
       
  8497       * appropriate moment.  See the example below for more details on how and when to do this.
       
  8498       * </div>
       
  8499       *
       
  8500       * @param {function()} fn A function that should be called repeatedly.
       
  8501       * @param {number} delay Number of milliseconds between each function call.
       
  8502       * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
       
  8503       *   indefinitely.
       
  8504       * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
       
  8505       *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
       
  8506       * @returns {promise} A promise which will be notified on each iteration.
       
  8507       *
       
  8508       * @example
       
  8509       * <example module="time">
       
  8510       *   <file name="index.html">
       
  8511       *     <script>
       
  8512       *       function Ctrl2($scope,$interval) {
       
  8513       *         $scope.format = 'M/d/yy h:mm:ss a';
       
  8514       *         $scope.blood_1 = 100;
       
  8515       *         $scope.blood_2 = 120;
       
  8516       *
       
  8517       *         var stop;
       
  8518       *         $scope.fight = function() {
       
  8519       *           // Don't start a new fight if we are already fighting
       
  8520       *           if ( angular.isDefined(stop) ) return;
       
  8521       *
       
  8522       *           stop = $interval(function() {
       
  8523       *             if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
       
  8524       *                 $scope.blood_1 = $scope.blood_1 - 3;
       
  8525       *                 $scope.blood_2 = $scope.blood_2 - 4;
       
  8526       *             } else {
       
  8527       *                 $scope.stopFight();
       
  8528       *             }
       
  8529       *           }, 100);
       
  8530       *         };
       
  8531       *
       
  8532       *         $scope.stopFight = function() {
       
  8533       *           if (angular.isDefined(stop)) {
       
  8534       *             $interval.cancel(stop);
       
  8535       *             stop = undefined;
       
  8536       *           }
       
  8537       *         };
       
  8538       *
       
  8539       *         $scope.resetFight = function() {
       
  8540       *           $scope.blood_1 = 100;
       
  8541       *           $scope.blood_2 = 120;
       
  8542       *         }
       
  8543       *
       
  8544       *         $scope.$on('$destroy', function() {
       
  8545       *           // Make sure that the interval is destroyed too
       
  8546       *           $scope.stopFight();
       
  8547       *         });
       
  8548       *       }
       
  8549       *
       
  8550       *       angular.module('time', [])
       
  8551       *         // Register the 'myCurrentTime' directive factory method.
       
  8552       *         // We inject $interval and dateFilter service since the factory method is DI.
       
  8553       *         .directive('myCurrentTime', function($interval, dateFilter) {
       
  8554       *           // return the directive link function. (compile function not needed)
       
  8555       *           return function(scope, element, attrs) {
       
  8556       *             var format,  // date format
       
  8557       *             stopTime; // so that we can cancel the time updates
       
  8558       *
       
  8559       *             // used to update the UI
       
  8560       *             function updateTime() {
       
  8561       *               element.text(dateFilter(new Date(), format));
       
  8562       *             }
       
  8563       *
       
  8564       *             // watch the expression, and update the UI on change.
       
  8565       *             scope.$watch(attrs.myCurrentTime, function(value) {
       
  8566       *               format = value;
       
  8567       *               updateTime();
       
  8568       *             });
       
  8569       *
       
  8570       *             stopTime = $interval(updateTime, 1000);
       
  8571       *
       
  8572       *             // listen on DOM destroy (removal) event, and cancel the next UI update
       
  8573       *             // to prevent updating time ofter the DOM element was removed.
       
  8574       *             element.bind('$destroy', function() {
       
  8575       *               $interval.cancel(stopTime);
       
  8576       *             });
       
  8577       *           }
       
  8578       *         });
       
  8579       *     </script>
       
  8580       *
       
  8581       *     <div>
       
  8582       *       <div ng-controller="Ctrl2">
       
  8583       *         Date format: <input ng-model="format"> <hr/>
       
  8584       *         Current time is: <span my-current-time="format"></span>
       
  8585       *         <hr/>
       
  8586       *         Blood 1 : <font color='red'>{{blood_1}}</font>
       
  8587       *         Blood 2 : <font color='red'>{{blood_2}}</font>
       
  8588       *         <button type="button" data-ng-click="fight()">Fight</button>
       
  8589       *         <button type="button" data-ng-click="stopFight()">StopFight</button>
       
  8590       *         <button type="button" data-ng-click="resetFight()">resetFight</button>
       
  8591       *       </div>
       
  8592       *     </div>
       
  8593       *
       
  8594       *   </file>
       
  8595       * </example>
       
  8596       */
       
  8597     function interval(fn, delay, count, invokeApply) {
       
  8598       var setInterval = $window.setInterval,
       
  8599           clearInterval = $window.clearInterval,
       
  8600           deferred = $q.defer(),
       
  8601           promise = deferred.promise,
       
  8602           iteration = 0,
       
  8603           skipApply = (isDefined(invokeApply) && !invokeApply);
       
  8604 
       
  8605       count = isDefined(count) ? count : 0;
       
  8606 
       
  8607       promise.then(null, null, fn);
       
  8608 
       
  8609       promise.$$intervalId = setInterval(function tick() {
       
  8610         deferred.notify(iteration++);
       
  8611 
       
  8612         if (count > 0 && iteration >= count) {
       
  8613           deferred.resolve(iteration);
       
  8614           clearInterval(promise.$$intervalId);
       
  8615           delete intervals[promise.$$intervalId];
       
  8616         }
       
  8617 
       
  8618         if (!skipApply) $rootScope.$apply();
       
  8619 
       
  8620       }, delay);
       
  8621 
       
  8622       intervals[promise.$$intervalId] = deferred;
       
  8623 
       
  8624       return promise;
       
  8625     }
       
  8626 
       
  8627 
       
  8628      /**
       
  8629       * @ngdoc method
       
  8630       * @name $interval#cancel
       
  8631       *
       
  8632       * @description
       
  8633       * Cancels a task associated with the `promise`.
       
  8634       *
       
  8635       * @param {promise} promise returned by the `$interval` function.
       
  8636       * @returns {boolean} Returns `true` if the task was successfully canceled.
       
  8637       */
       
  8638     interval.cancel = function(promise) {
       
  8639       if (promise && promise.$$intervalId in intervals) {
       
  8640         intervals[promise.$$intervalId].reject('canceled');
       
  8641         clearInterval(promise.$$intervalId);
       
  8642         delete intervals[promise.$$intervalId];
       
  8643         return true;
       
  8644       }
       
  8645       return false;
       
  8646     };
       
  8647 
       
  8648     return interval;
       
  8649   }];
       
  8650 }
       
  8651 
       
  8652 /**
       
  8653  * @ngdoc service
       
  8654  * @name $locale
       
  8655  *
       
  8656  * @description
       
  8657  * $locale service provides localization rules for various Angular components. As of right now the
       
  8658  * only public api is:
       
  8659  *
       
  8660  * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
       
  8661  */
       
  8662 function $LocaleProvider(){
       
  8663   this.$get = function() {
       
  8664     return {
       
  8665       id: 'en-us',
       
  8666 
       
  8667       NUMBER_FORMATS: {
       
  8668         DECIMAL_SEP: '.',
       
  8669         GROUP_SEP: ',',
       
  8670         PATTERNS: [
       
  8671           { // Decimal Pattern
       
  8672             minInt: 1,
       
  8673             minFrac: 0,
       
  8674             maxFrac: 3,
       
  8675             posPre: '',
       
  8676             posSuf: '',
       
  8677             negPre: '-',
       
  8678             negSuf: '',
       
  8679             gSize: 3,
       
  8680             lgSize: 3
       
  8681           },{ //Currency Pattern
       
  8682             minInt: 1,
       
  8683             minFrac: 2,
       
  8684             maxFrac: 2,
       
  8685             posPre: '\u00A4',
       
  8686             posSuf: '',
       
  8687             negPre: '(\u00A4',
       
  8688             negSuf: ')',
       
  8689             gSize: 3,
       
  8690             lgSize: 3
       
  8691           }
       
  8692         ],
       
  8693         CURRENCY_SYM: '$'
       
  8694       },
       
  8695 
       
  8696       DATETIME_FORMATS: {
       
  8697         MONTH:
       
  8698             'January,February,March,April,May,June,July,August,September,October,November,December'
       
  8699             .split(','),
       
  8700         SHORTMONTH:  'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','),
       
  8701         DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','),
       
  8702         SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','),
       
  8703         AMPMS: ['AM','PM'],
       
  8704         medium: 'MMM d, y h:mm:ss a',
       
  8705         short: 'M/d/yy h:mm a',
       
  8706         fullDate: 'EEEE, MMMM d, y',
       
  8707         longDate: 'MMMM d, y',
       
  8708         mediumDate: 'MMM d, y',
       
  8709         shortDate: 'M/d/yy',
       
  8710         mediumTime: 'h:mm:ss a',
       
  8711         shortTime: 'h:mm a'
       
  8712       },
       
  8713 
       
  8714       pluralCat: function(num) {
       
  8715         if (num === 1) {
       
  8716           return 'one';
       
  8717         }
       
  8718         return 'other';
       
  8719       }
       
  8720     };
       
  8721   };
       
  8722 }
       
  8723 
       
  8724 var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
       
  8725     DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
       
  8726 var $locationMinErr = minErr('$location');
       
  8727 
       
  8728 
       
  8729 /**
       
  8730  * Encode path using encodeUriSegment, ignoring forward slashes
       
  8731  *
       
  8732  * @param {string} path Path to encode
       
  8733  * @returns {string}
       
  8734  */
       
  8735 function encodePath(path) {
       
  8736   var segments = path.split('/'),
       
  8737       i = segments.length;
       
  8738 
       
  8739   while (i--) {
       
  8740     segments[i] = encodeUriSegment(segments[i]);
       
  8741   }
       
  8742 
       
  8743   return segments.join('/');
       
  8744 }
       
  8745 
       
  8746 function parseAbsoluteUrl(absoluteUrl, locationObj, appBase) {
       
  8747   var parsedUrl = urlResolve(absoluteUrl, appBase);
       
  8748 
       
  8749   locationObj.$$protocol = parsedUrl.protocol;
       
  8750   locationObj.$$host = parsedUrl.hostname;
       
  8751   locationObj.$$port = int(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
       
  8752 }
       
  8753 
       
  8754 
       
  8755 function parseAppUrl(relativeUrl, locationObj, appBase) {
       
  8756   var prefixed = (relativeUrl.charAt(0) !== '/');
       
  8757   if (prefixed) {
       
  8758     relativeUrl = '/' + relativeUrl;
       
  8759   }
       
  8760   var match = urlResolve(relativeUrl, appBase);
       
  8761   locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
       
  8762       match.pathname.substring(1) : match.pathname);
       
  8763   locationObj.$$search = parseKeyValue(match.search);
       
  8764   locationObj.$$hash = decodeURIComponent(match.hash);
       
  8765 
       
  8766   // make sure path starts with '/';
       
  8767   if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
       
  8768     locationObj.$$path = '/' + locationObj.$$path;
       
  8769   }
       
  8770 }
       
  8771 
       
  8772 
       
  8773 /**
       
  8774  *
       
  8775  * @param {string} begin
       
  8776  * @param {string} whole
       
  8777  * @returns {string} returns text from whole after begin or undefined if it does not begin with
       
  8778  *                   expected string.
       
  8779  */
       
  8780 function beginsWith(begin, whole) {
       
  8781   if (whole.indexOf(begin) === 0) {
       
  8782     return whole.substr(begin.length);
       
  8783   }
       
  8784 }
       
  8785 
       
  8786 
       
  8787 function stripHash(url) {
       
  8788   var index = url.indexOf('#');
       
  8789   return index == -1 ? url : url.substr(0, index);
       
  8790 }
       
  8791 
       
  8792 
       
  8793 function stripFile(url) {
       
  8794   return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
       
  8795 }
       
  8796 
       
  8797 /* return the server only (scheme://host:port) */
       
  8798 function serverBase(url) {
       
  8799   return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
       
  8800 }
       
  8801 
       
  8802 
       
  8803 /**
       
  8804  * LocationHtml5Url represents an url
       
  8805  * This object is exposed as $location service when HTML5 mode is enabled and supported
       
  8806  *
       
  8807  * @constructor
       
  8808  * @param {string} appBase application base URL
       
  8809  * @param {string} basePrefix url path prefix
       
  8810  */
       
  8811 function LocationHtml5Url(appBase, basePrefix) {
       
  8812   this.$$html5 = true;
       
  8813   basePrefix = basePrefix || '';
       
  8814   var appBaseNoFile = stripFile(appBase);
       
  8815   parseAbsoluteUrl(appBase, this, appBase);
       
  8816 
       
  8817 
       
  8818   /**
       
  8819    * Parse given html5 (regular) url string into properties
       
  8820    * @param {string} newAbsoluteUrl HTML5 url
       
  8821    * @private
       
  8822    */
       
  8823   this.$$parse = function(url) {
       
  8824     var pathUrl = beginsWith(appBaseNoFile, url);
       
  8825     if (!isString(pathUrl)) {
       
  8826       throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
       
  8827           appBaseNoFile);
       
  8828     }
       
  8829 
       
  8830     parseAppUrl(pathUrl, this, appBase);
       
  8831 
       
  8832     if (!this.$$path) {
       
  8833       this.$$path = '/';
       
  8834     }
       
  8835 
       
  8836     this.$$compose();
       
  8837   };
       
  8838 
       
  8839   /**
       
  8840    * Compose url and update `absUrl` property
       
  8841    * @private
       
  8842    */
       
  8843   this.$$compose = function() {
       
  8844     var search = toKeyValue(this.$$search),
       
  8845         hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
       
  8846 
       
  8847     this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
       
  8848     this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
       
  8849   };
       
  8850 
       
  8851   this.$$rewrite = function(url) {
       
  8852     var appUrl, prevAppUrl;
       
  8853 
       
  8854     if ( (appUrl = beginsWith(appBase, url)) !== undefined ) {
       
  8855       prevAppUrl = appUrl;
       
  8856       if ( (appUrl = beginsWith(basePrefix, appUrl)) !== undefined ) {
       
  8857         return appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
       
  8858       } else {
       
  8859         return appBase + prevAppUrl;
       
  8860       }
       
  8861     } else if ( (appUrl = beginsWith(appBaseNoFile, url)) !== undefined ) {
       
  8862       return appBaseNoFile + appUrl;
       
  8863     } else if (appBaseNoFile == url + '/') {
       
  8864       return appBaseNoFile;
       
  8865     }
       
  8866   };
       
  8867 }
       
  8868 
       
  8869 
       
  8870 /**
       
  8871  * LocationHashbangUrl represents url
       
  8872  * This object is exposed as $location service when developer doesn't opt into html5 mode.
       
  8873  * It also serves as the base class for html5 mode fallback on legacy browsers.
       
  8874  *
       
  8875  * @constructor
       
  8876  * @param {string} appBase application base URL
       
  8877  * @param {string} hashPrefix hashbang prefix
       
  8878  */
       
  8879 function LocationHashbangUrl(appBase, hashPrefix) {
       
  8880   var appBaseNoFile = stripFile(appBase);
       
  8881 
       
  8882   parseAbsoluteUrl(appBase, this, appBase);
       
  8883 
       
  8884 
       
  8885   /**
       
  8886    * Parse given hashbang url into properties
       
  8887    * @param {string} url Hashbang url
       
  8888    * @private
       
  8889    */
       
  8890   this.$$parse = function(url) {
       
  8891     var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
       
  8892     var withoutHashUrl = withoutBaseUrl.charAt(0) == '#'
       
  8893         ? beginsWith(hashPrefix, withoutBaseUrl)
       
  8894         : (this.$$html5)
       
  8895           ? withoutBaseUrl
       
  8896           : '';
       
  8897 
       
  8898     if (!isString(withoutHashUrl)) {
       
  8899       throw $locationMinErr('ihshprfx', 'Invalid url "{0}", missing hash prefix "{1}".', url,
       
  8900           hashPrefix);
       
  8901     }
       
  8902     parseAppUrl(withoutHashUrl, this, appBase);
       
  8903 
       
  8904     this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
       
  8905 
       
  8906     this.$$compose();
       
  8907 
       
  8908     /*
       
  8909      * In Windows, on an anchor node on documents loaded from
       
  8910      * the filesystem, the browser will return a pathname
       
  8911      * prefixed with the drive name ('/C:/path') when a
       
  8912      * pathname without a drive is set:
       
  8913      *  * a.setAttribute('href', '/foo')
       
  8914      *   * a.pathname === '/C:/foo' //true
       
  8915      *
       
  8916      * Inside of Angular, we're always using pathnames that
       
  8917      * do not include drive names for routing.
       
  8918      */
       
  8919     function removeWindowsDriveName (path, url, base) {
       
  8920       /*
       
  8921       Matches paths for file protocol on windows,
       
  8922       such as /C:/foo/bar, and captures only /foo/bar.
       
  8923       */
       
  8924       var windowsFilePathExp = /^\/?.*?:(\/.*)/;
       
  8925 
       
  8926       var firstPathSegmentMatch;
       
  8927 
       
  8928       //Get the relative path from the input URL.
       
  8929       if (url.indexOf(base) === 0) {
       
  8930         url = url.replace(base, '');
       
  8931       }
       
  8932 
       
  8933       /*
       
  8934        * The input URL intentionally contains a
       
  8935        * first path segment that ends with a colon.
       
  8936        */
       
  8937       if (windowsFilePathExp.exec(url)) {
       
  8938         return path;
       
  8939       }
       
  8940 
       
  8941       firstPathSegmentMatch = windowsFilePathExp.exec(path);
       
  8942       return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
       
  8943     }
       
  8944   };
       
  8945 
       
  8946   /**
       
  8947    * Compose hashbang url and update `absUrl` property
       
  8948    * @private
       
  8949    */
       
  8950   this.$$compose = function() {
       
  8951     var search = toKeyValue(this.$$search),
       
  8952         hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
       
  8953 
       
  8954     this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
       
  8955     this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
       
  8956   };
       
  8957 
       
  8958   this.$$rewrite = function(url) {
       
  8959     if(stripHash(appBase) == stripHash(url)) {
       
  8960       return url;
       
  8961     }
       
  8962   };
       
  8963 }
       
  8964 
       
  8965 
       
  8966 /**
       
  8967  * LocationHashbangUrl represents url
       
  8968  * This object is exposed as $location service when html5 history api is enabled but the browser
       
  8969  * does not support it.
       
  8970  *
       
  8971  * @constructor
       
  8972  * @param {string} appBase application base URL
       
  8973  * @param {string} hashPrefix hashbang prefix
       
  8974  */
       
  8975 function LocationHashbangInHtml5Url(appBase, hashPrefix) {
       
  8976   this.$$html5 = true;
       
  8977   LocationHashbangUrl.apply(this, arguments);
       
  8978 
       
  8979   var appBaseNoFile = stripFile(appBase);
       
  8980 
       
  8981   this.$$rewrite = function(url) {
       
  8982     var appUrl;
       
  8983 
       
  8984     if ( appBase == stripHash(url) ) {
       
  8985       return url;
       
  8986     } else if ( (appUrl = beginsWith(appBaseNoFile, url)) ) {
       
  8987       return appBase + hashPrefix + appUrl;
       
  8988     } else if ( appBaseNoFile === url + '/') {
       
  8989       return appBaseNoFile;
       
  8990     }
       
  8991   };
       
  8992 }
       
  8993 
       
  8994 
       
  8995 LocationHashbangInHtml5Url.prototype =
       
  8996   LocationHashbangUrl.prototype =
       
  8997   LocationHtml5Url.prototype = {
       
  8998 
       
  8999   /**
       
  9000    * Are we in html5 mode?
       
  9001    * @private
       
  9002    */
       
  9003   $$html5: false,
       
  9004 
       
  9005   /**
       
  9006    * Has any change been replacing ?
       
  9007    * @private
       
  9008    */
       
  9009   $$replace: false,
       
  9010 
       
  9011   /**
       
  9012    * @ngdoc method
       
  9013    * @name $location#absUrl
       
  9014    *
       
  9015    * @description
       
  9016    * This method is getter only.
       
  9017    *
       
  9018    * Return full url representation with all segments encoded according to rules specified in
       
  9019    * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
       
  9020    *
       
  9021    * @return {string} full url
       
  9022    */
       
  9023   absUrl: locationGetter('$$absUrl'),
       
  9024 
       
  9025   /**
       
  9026    * @ngdoc method
       
  9027    * @name $location#url
       
  9028    *
       
  9029    * @description
       
  9030    * This method is getter / setter.
       
  9031    *
       
  9032    * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
       
  9033    *
       
  9034    * Change path, search and hash, when called with parameter and return `$location`.
       
  9035    *
       
  9036    * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
       
  9037    * @param {string=} replace The path that will be changed
       
  9038    * @return {string} url
       
  9039    */
       
  9040   url: function(url, replace) {
       
  9041     if (isUndefined(url))
       
  9042       return this.$$url;
       
  9043 
       
  9044     var match = PATH_MATCH.exec(url);
       
  9045     if (match[1]) this.path(decodeURIComponent(match[1]));
       
  9046     if (match[2] || match[1]) this.search(match[3] || '');
       
  9047     this.hash(match[5] || '', replace);
       
  9048 
       
  9049     return this;
       
  9050   },
       
  9051 
       
  9052   /**
       
  9053    * @ngdoc method
       
  9054    * @name $location#protocol
       
  9055    *
       
  9056    * @description
       
  9057    * This method is getter only.
       
  9058    *
       
  9059    * Return protocol of current url.
       
  9060    *
       
  9061    * @return {string} protocol of current url
       
  9062    */
       
  9063   protocol: locationGetter('$$protocol'),
       
  9064 
       
  9065   /**
       
  9066    * @ngdoc method
       
  9067    * @name $location#host
       
  9068    *
       
  9069    * @description
       
  9070    * This method is getter only.
       
  9071    *
       
  9072    * Return host of current url.
       
  9073    *
       
  9074    * @return {string} host of current url.
       
  9075    */
       
  9076   host: locationGetter('$$host'),
       
  9077 
       
  9078   /**
       
  9079    * @ngdoc method
       
  9080    * @name $location#port
       
  9081    *
       
  9082    * @description
       
  9083    * This method is getter only.
       
  9084    *
       
  9085    * Return port of current url.
       
  9086    *
       
  9087    * @return {Number} port
       
  9088    */
       
  9089   port: locationGetter('$$port'),
       
  9090 
       
  9091   /**
       
  9092    * @ngdoc method
       
  9093    * @name $location#path
       
  9094    *
       
  9095    * @description
       
  9096    * This method is getter / setter.
       
  9097    *
       
  9098    * Return path of current url when called without any parameter.
       
  9099    *
       
  9100    * Change path when called with parameter and return `$location`.
       
  9101    *
       
  9102    * Note: Path should always begin with forward slash (/), this method will add the forward slash
       
  9103    * if it is missing.
       
  9104    *
       
  9105    * @param {string=} path New path
       
  9106    * @return {string} path
       
  9107    */
       
  9108   path: locationGetterSetter('$$path', function(path) {
       
  9109     return path.charAt(0) == '/' ? path : '/' + path;
       
  9110   }),
       
  9111 
       
  9112   /**
       
  9113    * @ngdoc method
       
  9114    * @name $location#search
       
  9115    *
       
  9116    * @description
       
  9117    * This method is getter / setter.
       
  9118    *
       
  9119    * Return search part (as object) of current url when called without any parameter.
       
  9120    *
       
  9121    * Change search part when called with parameter and return `$location`.
       
  9122    *
       
  9123    * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
       
  9124    * hash object. Hash object may contain an array of values, which will be decoded as duplicates in
       
  9125    * the url.
       
  9126    *
       
  9127    * @param {(string|Array<string>)=} paramValue If `search` is a string, then `paramValue` will override only a
       
  9128    * single search parameter. If `paramValue` is an array, it will set the parameter as a
       
  9129    * comma-separated value. If `paramValue` is `null`, the parameter will be deleted.
       
  9130    *
       
  9131    * @return {string} search
       
  9132    */
       
  9133   search: function(search, paramValue) {
       
  9134     switch (arguments.length) {
       
  9135       case 0:
       
  9136         return this.$$search;
       
  9137       case 1:
       
  9138         if (isString(search)) {
       
  9139           this.$$search = parseKeyValue(search);
       
  9140         } else if (isObject(search)) {
       
  9141           this.$$search = search;
       
  9142         } else {
       
  9143           throw $locationMinErr('isrcharg',
       
  9144               'The first argument of the `$location#search()` call must be a string or an object.');
       
  9145         }
       
  9146         break;
       
  9147       default:
       
  9148         if (isUndefined(paramValue) || paramValue === null) {
       
  9149           delete this.$$search[search];
       
  9150         } else {
       
  9151           this.$$search[search] = paramValue;
       
  9152         }
       
  9153     }
       
  9154 
       
  9155     this.$$compose();
       
  9156     return this;
       
  9157   },
       
  9158 
       
  9159   /**
       
  9160    * @ngdoc method
       
  9161    * @name $location#hash
       
  9162    *
       
  9163    * @description
       
  9164    * This method is getter / setter.
       
  9165    *
       
  9166    * Return hash fragment when called without any parameter.
       
  9167    *
       
  9168    * Change hash fragment when called with parameter and return `$location`.
       
  9169    *
       
  9170    * @param {string=} hash New hash fragment
       
  9171    * @return {string} hash
       
  9172    */
       
  9173   hash: locationGetterSetter('$$hash', identity),
       
  9174 
       
  9175   /**
       
  9176    * @ngdoc method
       
  9177    * @name $location#replace
       
  9178    *
       
  9179    * @description
       
  9180    * If called, all changes to $location during current `$digest` will be replacing current history
       
  9181    * record, instead of adding new one.
       
  9182    */
       
  9183   replace: function() {
       
  9184     this.$$replace = true;
       
  9185     return this;
       
  9186   }
       
  9187 };
       
  9188 
       
  9189 function locationGetter(property) {
       
  9190   return function() {
       
  9191     return this[property];
       
  9192   };
       
  9193 }
       
  9194 
       
  9195 
       
  9196 function locationGetterSetter(property, preprocess) {
       
  9197   return function(value) {
       
  9198     if (isUndefined(value))
       
  9199       return this[property];
       
  9200 
       
  9201     this[property] = preprocess(value);
       
  9202     this.$$compose();
       
  9203 
       
  9204     return this;
       
  9205   };
       
  9206 }
       
  9207 
       
  9208 
       
  9209 /**
       
  9210  * @ngdoc service
       
  9211  * @name $location
       
  9212  *
       
  9213  * @requires $rootElement
       
  9214  *
       
  9215  * @description
       
  9216  * The $location service parses the URL in the browser address bar (based on the
       
  9217  * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
       
  9218  * available to your application. Changes to the URL in the address bar are reflected into
       
  9219  * $location service and changes to $location are reflected into the browser address bar.
       
  9220  *
       
  9221  * **The $location service:**
       
  9222  *
       
  9223  * - Exposes the current URL in the browser address bar, so you can
       
  9224  *   - Watch and observe the URL.
       
  9225  *   - Change the URL.
       
  9226  * - Synchronizes the URL with the browser when the user
       
  9227  *   - Changes the address bar.
       
  9228  *   - Clicks the back or forward button (or clicks a History link).
       
  9229  *   - Clicks on a link.
       
  9230  * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
       
  9231  *
       
  9232  * For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular
       
  9233  * Services: Using $location}
       
  9234  */
       
  9235 
       
  9236 /**
       
  9237  * @ngdoc provider
       
  9238  * @name $locationProvider
       
  9239  * @description
       
  9240  * Use the `$locationProvider` to configure how the application deep linking paths are stored.
       
  9241  */
       
  9242 function $LocationProvider(){
       
  9243   var hashPrefix = '',
       
  9244       html5Mode = false;
       
  9245 
       
  9246   /**
       
  9247    * @ngdoc property
       
  9248    * @name $locationProvider#hashPrefix
       
  9249    * @description
       
  9250    * @param {string=} prefix Prefix for hash part (containing path and search)
       
  9251    * @returns {*} current value if used as getter or itself (chaining) if used as setter
       
  9252    */
       
  9253   this.hashPrefix = function(prefix) {
       
  9254     if (isDefined(prefix)) {
       
  9255       hashPrefix = prefix;
       
  9256       return this;
       
  9257     } else {
       
  9258       return hashPrefix;
       
  9259     }
       
  9260   };
       
  9261 
       
  9262   /**
       
  9263    * @ngdoc property
       
  9264    * @name $locationProvider#html5Mode
       
  9265    * @description
       
  9266    * @param {boolean=} mode Use HTML5 strategy if available.
       
  9267    * @returns {*} current value if used as getter or itself (chaining) if used as setter
       
  9268    */
       
  9269   this.html5Mode = function(mode) {
       
  9270     if (isDefined(mode)) {
       
  9271       html5Mode = mode;
       
  9272       return this;
       
  9273     } else {
       
  9274       return html5Mode;
       
  9275     }
       
  9276   };
       
  9277 
       
  9278   /**
       
  9279    * @ngdoc event
       
  9280    * @name $location#$locationChangeStart
       
  9281    * @eventType broadcast on root scope
       
  9282    * @description
       
  9283    * Broadcasted before a URL will change. This change can be prevented by calling
       
  9284    * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
       
  9285    * details about event object. Upon successful change
       
  9286    * {@link ng.$location#events_$locationChangeSuccess $locationChangeSuccess} is fired.
       
  9287    *
       
  9288    * @param {Object} angularEvent Synthetic event object.
       
  9289    * @param {string} newUrl New URL
       
  9290    * @param {string=} oldUrl URL that was before it was changed.
       
  9291    */
       
  9292 
       
  9293   /**
       
  9294    * @ngdoc event
       
  9295    * @name $location#$locationChangeSuccess
       
  9296    * @eventType broadcast on root scope
       
  9297    * @description
       
  9298    * Broadcasted after a URL was changed.
       
  9299    *
       
  9300    * @param {Object} angularEvent Synthetic event object.
       
  9301    * @param {string} newUrl New URL
       
  9302    * @param {string=} oldUrl URL that was before it was changed.
       
  9303    */
       
  9304 
       
  9305   this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement',
       
  9306       function( $rootScope,   $browser,   $sniffer,   $rootElement) {
       
  9307     var $location,
       
  9308         LocationMode,
       
  9309         baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
       
  9310         initialUrl = $browser.url(),
       
  9311         appBase;
       
  9312 
       
  9313     if (html5Mode) {
       
  9314       appBase = serverBase(initialUrl) + (baseHref || '/');
       
  9315       LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
       
  9316     } else {
       
  9317       appBase = stripHash(initialUrl);
       
  9318       LocationMode = LocationHashbangUrl;
       
  9319     }
       
  9320     $location = new LocationMode(appBase, '#' + hashPrefix);
       
  9321     $location.$$parse($location.$$rewrite(initialUrl));
       
  9322 
       
  9323     $rootElement.on('click', function(event) {
       
  9324       // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
       
  9325       // currently we open nice url link and redirect then
       
  9326 
       
  9327       if (event.ctrlKey || event.metaKey || event.which == 2) return;
       
  9328 
       
  9329       var elm = jqLite(event.target);
       
  9330 
       
  9331       // traverse the DOM up to find first A tag
       
  9332       while (lowercase(elm[0].nodeName) !== 'a') {
       
  9333         // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
       
  9334         if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
       
  9335       }
       
  9336 
       
  9337       var absHref = elm.prop('href');
       
  9338 
       
  9339       if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
       
  9340         // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
       
  9341         // an animation.
       
  9342         absHref = urlResolve(absHref.animVal).href;
       
  9343       }
       
  9344 
       
  9345       var rewrittenUrl = $location.$$rewrite(absHref);
       
  9346 
       
  9347       if (absHref && !elm.attr('target') && rewrittenUrl && !event.isDefaultPrevented()) {
       
  9348         event.preventDefault();
       
  9349         if (rewrittenUrl != $browser.url()) {
       
  9350           // update location manually
       
  9351           $location.$$parse(rewrittenUrl);
       
  9352           $rootScope.$apply();
       
  9353           // hack to work around FF6 bug 684208 when scenario runner clicks on links
       
  9354           window.angular['ff-684208-preventDefault'] = true;
       
  9355         }
       
  9356       }
       
  9357     });
       
  9358 
       
  9359 
       
  9360     // rewrite hashbang url <> html5 url
       
  9361     if ($location.absUrl() != initialUrl) {
       
  9362       $browser.url($location.absUrl(), true);
       
  9363     }
       
  9364 
       
  9365     // update $location when $browser url changes
       
  9366     $browser.onUrlChange(function(newUrl) {
       
  9367       if ($location.absUrl() != newUrl) {
       
  9368         $rootScope.$evalAsync(function() {
       
  9369           var oldUrl = $location.absUrl();
       
  9370 
       
  9371           $location.$$parse(newUrl);
       
  9372           if ($rootScope.$broadcast('$locationChangeStart', newUrl,
       
  9373                                     oldUrl).defaultPrevented) {
       
  9374             $location.$$parse(oldUrl);
       
  9375             $browser.url(oldUrl);
       
  9376           } else {
       
  9377             afterLocationChange(oldUrl);
       
  9378           }
       
  9379         });
       
  9380         if (!$rootScope.$$phase) $rootScope.$digest();
       
  9381       }
       
  9382     });
       
  9383 
       
  9384     // update browser
       
  9385     var changeCounter = 0;
       
  9386     $rootScope.$watch(function $locationWatch() {
       
  9387       var oldUrl = $browser.url();
       
  9388       var currentReplace = $location.$$replace;
       
  9389 
       
  9390       if (!changeCounter || oldUrl != $location.absUrl()) {
       
  9391         changeCounter++;
       
  9392         $rootScope.$evalAsync(function() {
       
  9393           if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl).
       
  9394               defaultPrevented) {
       
  9395             $location.$$parse(oldUrl);
       
  9396           } else {
       
  9397             $browser.url($location.absUrl(), currentReplace);
       
  9398             afterLocationChange(oldUrl);
       
  9399           }
       
  9400         });
       
  9401       }
       
  9402       $location.$$replace = false;
       
  9403 
       
  9404       return changeCounter;
       
  9405     });
       
  9406 
       
  9407     return $location;
       
  9408 
       
  9409     function afterLocationChange(oldUrl) {
       
  9410       $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl);
       
  9411     }
       
  9412 }];
       
  9413 }
       
  9414 
       
  9415 /**
       
  9416  * @ngdoc service
       
  9417  * @name $log
       
  9418  * @requires $window
       
  9419  *
       
  9420  * @description
       
  9421  * Simple service for logging. Default implementation safely writes the message
       
  9422  * into the browser's console (if present).
       
  9423  *
       
  9424  * The main purpose of this service is to simplify debugging and troubleshooting.
       
  9425  *
       
  9426  * The default is to log `debug` messages. You can use
       
  9427  * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
       
  9428  *
       
  9429  * @example
       
  9430    <example>
       
  9431      <file name="script.js">
       
  9432        function LogCtrl($scope, $log) {
       
  9433          $scope.$log = $log;
       
  9434          $scope.message = 'Hello World!';
       
  9435        }
       
  9436      </file>
       
  9437      <file name="index.html">
       
  9438        <div ng-controller="LogCtrl">
       
  9439          <p>Reload this page with open console, enter text and hit the log button...</p>
       
  9440          Message:
       
  9441          <input type="text" ng-model="message"/>
       
  9442          <button ng-click="$log.log(message)">log</button>
       
  9443          <button ng-click="$log.warn(message)">warn</button>
       
  9444          <button ng-click="$log.info(message)">info</button>
       
  9445          <button ng-click="$log.error(message)">error</button>
       
  9446        </div>
       
  9447      </file>
       
  9448    </example>
       
  9449  */
       
  9450 
       
  9451 /**
       
  9452  * @ngdoc provider
       
  9453  * @name $logProvider
       
  9454  * @description
       
  9455  * Use the `$logProvider` to configure how the application logs messages
       
  9456  */
       
  9457 function $LogProvider(){
       
  9458   var debug = true,
       
  9459       self = this;
       
  9460 
       
  9461   /**
       
  9462    * @ngdoc property
       
  9463    * @name $logProvider#debugEnabled
       
  9464    * @description
       
  9465    * @param {boolean=} flag enable or disable debug level messages
       
  9466    * @returns {*} current value if used as getter or itself (chaining) if used as setter
       
  9467    */
       
  9468   this.debugEnabled = function(flag) {
       
  9469     if (isDefined(flag)) {
       
  9470       debug = flag;
       
  9471     return this;
       
  9472     } else {
       
  9473       return debug;
       
  9474     }
       
  9475   };
       
  9476 
       
  9477   this.$get = ['$window', function($window){
       
  9478     return {
       
  9479       /**
       
  9480        * @ngdoc method
       
  9481        * @name $log#log
       
  9482        *
       
  9483        * @description
       
  9484        * Write a log message
       
  9485        */
       
  9486       log: consoleLog('log'),
       
  9487 
       
  9488       /**
       
  9489        * @ngdoc method
       
  9490        * @name $log#info
       
  9491        *
       
  9492        * @description
       
  9493        * Write an information message
       
  9494        */
       
  9495       info: consoleLog('info'),
       
  9496 
       
  9497       /**
       
  9498        * @ngdoc method
       
  9499        * @name $log#warn
       
  9500        *
       
  9501        * @description
       
  9502        * Write a warning message
       
  9503        */
       
  9504       warn: consoleLog('warn'),
       
  9505 
       
  9506       /**
       
  9507        * @ngdoc method
       
  9508        * @name $log#error
       
  9509        *
       
  9510        * @description
       
  9511        * Write an error message
       
  9512        */
       
  9513       error: consoleLog('error'),
       
  9514 
       
  9515       /**
       
  9516        * @ngdoc method
       
  9517        * @name $log#debug
       
  9518        *
       
  9519        * @description
       
  9520        * Write a debug message
       
  9521        */
       
  9522       debug: (function () {
       
  9523         var fn = consoleLog('debug');
       
  9524 
       
  9525         return function() {
       
  9526           if (debug) {
       
  9527             fn.apply(self, arguments);
       
  9528           }
       
  9529         };
       
  9530       }())
       
  9531     };
       
  9532 
       
  9533     function formatError(arg) {
       
  9534       if (arg instanceof Error) {
       
  9535         if (arg.stack) {
       
  9536           arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
       
  9537               ? 'Error: ' + arg.message + '\n' + arg.stack
       
  9538               : arg.stack;
       
  9539         } else if (arg.sourceURL) {
       
  9540           arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
       
  9541         }
       
  9542       }
       
  9543       return arg;
       
  9544     }
       
  9545 
       
  9546     function consoleLog(type) {
       
  9547       var console = $window.console || {},
       
  9548           logFn = console[type] || console.log || noop,
       
  9549           hasApply = false;
       
  9550 
       
  9551       // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
       
  9552       // The reason behind this is that console.log has type "object" in IE8...
       
  9553       try {
       
  9554         hasApply = !!logFn.apply;
       
  9555       } catch (e) {}
       
  9556 
       
  9557       if (hasApply) {
       
  9558         return function() {
       
  9559           var args = [];
       
  9560           forEach(arguments, function(arg) {
       
  9561             args.push(formatError(arg));
       
  9562           });
       
  9563           return logFn.apply(console, args);
       
  9564         };
       
  9565       }
       
  9566 
       
  9567       // we are IE which either doesn't have window.console => this is noop and we do nothing,
       
  9568       // or we are IE where console.log doesn't have apply so we log at least first 2 args
       
  9569       return function(arg1, arg2) {
       
  9570         logFn(arg1, arg2 == null ? '' : arg2);
       
  9571       };
       
  9572     }
       
  9573   }];
       
  9574 }
       
  9575 
       
  9576 var $parseMinErr = minErr('$parse');
       
  9577 var promiseWarningCache = {};
       
  9578 var promiseWarning;
       
  9579 
       
  9580 // Sandboxing Angular Expressions
       
  9581 // ------------------------------
       
  9582 // Angular expressions are generally considered safe because these expressions only have direct
       
  9583 // access to $scope and locals. However, one can obtain the ability to execute arbitrary JS code by
       
  9584 // obtaining a reference to native JS functions such as the Function constructor.
       
  9585 //
       
  9586 // As an example, consider the following Angular expression:
       
  9587 //
       
  9588 //   {}.toString.constructor(alert("evil JS code"))
       
  9589 //
       
  9590 // We want to prevent this type of access. For the sake of performance, during the lexing phase we
       
  9591 // disallow any "dotted" access to any member named "constructor".
       
  9592 //
       
  9593 // For reflective calls (a[b]) we check that the value of the lookup is not the Function constructor
       
  9594 // while evaluating the expression, which is a stronger but more expensive test. Since reflective
       
  9595 // calls are expensive anyway, this is not such a big deal compared to static dereferencing.
       
  9596 //
       
  9597 // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
       
  9598 // against the expression language, but not to prevent exploits that were enabled by exposing
       
  9599 // sensitive JavaScript or browser apis on Scope. Exposing such objects on a Scope is never a good
       
  9600 // practice and therefore we are not even trying to protect against interaction with an object
       
  9601 // explicitly exposed in this way.
       
  9602 //
       
  9603 // A developer could foil the name check by aliasing the Function constructor under a different
       
  9604 // name on the scope.
       
  9605 //
       
  9606 // In general, it is not possible to access a Window object from an angular expression unless a
       
  9607 // window or some DOM object that has a reference to window is published onto a Scope.
       
  9608 
       
  9609 function ensureSafeMemberName(name, fullExpression) {
       
  9610   if (name === "constructor") {
       
  9611     throw $parseMinErr('isecfld',
       
  9612         'Referencing "constructor" field in Angular expressions is disallowed! Expression: {0}',
       
  9613         fullExpression);
       
  9614   }
       
  9615   return name;
       
  9616 }
       
  9617 
       
  9618 function ensureSafeObject(obj, fullExpression) {
       
  9619   // nifty check if obj is Function that is fast and works across iframes and other contexts
       
  9620   if (obj) {
       
  9621     if (obj.constructor === obj) {
       
  9622       throw $parseMinErr('isecfn',
       
  9623           'Referencing Function in Angular expressions is disallowed! Expression: {0}',
       
  9624           fullExpression);
       
  9625     } else if (// isWindow(obj)
       
  9626         obj.document && obj.location && obj.alert && obj.setInterval) {
       
  9627       throw $parseMinErr('isecwindow',
       
  9628           'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
       
  9629           fullExpression);
       
  9630     } else if (// isElement(obj)
       
  9631         obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
       
  9632       throw $parseMinErr('isecdom',
       
  9633           'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
       
  9634           fullExpression);
       
  9635     }
       
  9636   }
       
  9637   return obj;
       
  9638 }
       
  9639 
       
  9640 var OPERATORS = {
       
  9641     /* jshint bitwise : false */
       
  9642     'null':function(){return null;},
       
  9643     'true':function(){return true;},
       
  9644     'false':function(){return false;},
       
  9645     undefined:noop,
       
  9646     '+':function(self, locals, a,b){
       
  9647       a=a(self, locals); b=b(self, locals);
       
  9648       if (isDefined(a)) {
       
  9649         if (isDefined(b)) {
       
  9650           return a + b;
       
  9651         }
       
  9652         return a;
       
  9653       }
       
  9654       return isDefined(b)?b:undefined;},
       
  9655     '-':function(self, locals, a,b){
       
  9656           a=a(self, locals); b=b(self, locals);
       
  9657           return (isDefined(a)?a:0)-(isDefined(b)?b:0);
       
  9658         },
       
  9659     '*':function(self, locals, a,b){return a(self, locals)*b(self, locals);},
       
  9660     '/':function(self, locals, a,b){return a(self, locals)/b(self, locals);},
       
  9661     '%':function(self, locals, a,b){return a(self, locals)%b(self, locals);},
       
  9662     '^':function(self, locals, a,b){return a(self, locals)^b(self, locals);},
       
  9663     '=':noop,
       
  9664     '===':function(self, locals, a, b){return a(self, locals)===b(self, locals);},
       
  9665     '!==':function(self, locals, a, b){return a(self, locals)!==b(self, locals);},
       
  9666     '==':function(self, locals, a,b){return a(self, locals)==b(self, locals);},
       
  9667     '!=':function(self, locals, a,b){return a(self, locals)!=b(self, locals);},
       
  9668     '<':function(self, locals, a,b){return a(self, locals)<b(self, locals);},
       
  9669     '>':function(self, locals, a,b){return a(self, locals)>b(self, locals);},
       
  9670     '<=':function(self, locals, a,b){return a(self, locals)<=b(self, locals);},
       
  9671     '>=':function(self, locals, a,b){return a(self, locals)>=b(self, locals);},
       
  9672     '&&':function(self, locals, a,b){return a(self, locals)&&b(self, locals);},
       
  9673     '||':function(self, locals, a,b){return a(self, locals)||b(self, locals);},
       
  9674     '&':function(self, locals, a,b){return a(self, locals)&b(self, locals);},
       
  9675 //    '|':function(self, locals, a,b){return a|b;},
       
  9676     '|':function(self, locals, a,b){return b(self, locals)(self, locals, a(self, locals));},
       
  9677     '!':function(self, locals, a){return !a(self, locals);}
       
  9678 };
       
  9679 /* jshint bitwise: true */
       
  9680 var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
       
  9681 
       
  9682 
       
  9683 /////////////////////////////////////////
       
  9684 
       
  9685 
       
  9686 /**
       
  9687  * @constructor
       
  9688  */
       
  9689 var Lexer = function (options) {
       
  9690   this.options = options;
       
  9691 };
       
  9692 
       
  9693 Lexer.prototype = {
       
  9694   constructor: Lexer,
       
  9695 
       
  9696   lex: function (text) {
       
  9697     this.text = text;
       
  9698 
       
  9699     this.index = 0;
       
  9700     this.ch = undefined;
       
  9701     this.lastCh = ':'; // can start regexp
       
  9702 
       
  9703     this.tokens = [];
       
  9704 
       
  9705     var token;
       
  9706     var json = [];
       
  9707 
       
  9708     while (this.index < this.text.length) {
       
  9709       this.ch = this.text.charAt(this.index);
       
  9710       if (this.is('"\'')) {
       
  9711         this.readString(this.ch);
       
  9712       } else if (this.isNumber(this.ch) || this.is('.') && this.isNumber(this.peek())) {
       
  9713         this.readNumber();
       
  9714       } else if (this.isIdent(this.ch)) {
       
  9715         this.readIdent();
       
  9716         // identifiers can only be if the preceding char was a { or ,
       
  9717         if (this.was('{,') && json[0] === '{' &&
       
  9718             (token = this.tokens[this.tokens.length - 1])) {
       
  9719           token.json = token.text.indexOf('.') === -1;
       
  9720         }
       
  9721       } else if (this.is('(){}[].,;:?')) {
       
  9722         this.tokens.push({
       
  9723           index: this.index,
       
  9724           text: this.ch,
       
  9725           json: (this.was(':[,') && this.is('{[')) || this.is('}]:,')
       
  9726         });
       
  9727         if (this.is('{[')) json.unshift(this.ch);
       
  9728         if (this.is('}]')) json.shift();
       
  9729         this.index++;
       
  9730       } else if (this.isWhitespace(this.ch)) {
       
  9731         this.index++;
       
  9732         continue;
       
  9733       } else {
       
  9734         var ch2 = this.ch + this.peek();
       
  9735         var ch3 = ch2 + this.peek(2);
       
  9736         var fn = OPERATORS[this.ch];
       
  9737         var fn2 = OPERATORS[ch2];
       
  9738         var fn3 = OPERATORS[ch3];
       
  9739         if (fn3) {
       
  9740           this.tokens.push({index: this.index, text: ch3, fn: fn3});
       
  9741           this.index += 3;
       
  9742         } else if (fn2) {
       
  9743           this.tokens.push({index: this.index, text: ch2, fn: fn2});
       
  9744           this.index += 2;
       
  9745         } else if (fn) {
       
  9746           this.tokens.push({
       
  9747             index: this.index,
       
  9748             text: this.ch,
       
  9749             fn: fn,
       
  9750             json: (this.was('[,:') && this.is('+-'))
       
  9751           });
       
  9752           this.index += 1;
       
  9753         } else {
       
  9754           this.throwError('Unexpected next character ', this.index, this.index + 1);
       
  9755         }
       
  9756       }
       
  9757       this.lastCh = this.ch;
       
  9758     }
       
  9759     return this.tokens;
       
  9760   },
       
  9761 
       
  9762   is: function(chars) {
       
  9763     return chars.indexOf(this.ch) !== -1;
       
  9764   },
       
  9765 
       
  9766   was: function(chars) {
       
  9767     return chars.indexOf(this.lastCh) !== -1;
       
  9768   },
       
  9769 
       
  9770   peek: function(i) {
       
  9771     var num = i || 1;
       
  9772     return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
       
  9773   },
       
  9774 
       
  9775   isNumber: function(ch) {
       
  9776     return ('0' <= ch && ch <= '9');
       
  9777   },
       
  9778 
       
  9779   isWhitespace: function(ch) {
       
  9780     // IE treats non-breaking space as \u00A0
       
  9781     return (ch === ' ' || ch === '\r' || ch === '\t' ||
       
  9782             ch === '\n' || ch === '\v' || ch === '\u00A0');
       
  9783   },
       
  9784 
       
  9785   isIdent: function(ch) {
       
  9786     return ('a' <= ch && ch <= 'z' ||
       
  9787             'A' <= ch && ch <= 'Z' ||
       
  9788             '_' === ch || ch === '$');
       
  9789   },
       
  9790 
       
  9791   isExpOperator: function(ch) {
       
  9792     return (ch === '-' || ch === '+' || this.isNumber(ch));
       
  9793   },
       
  9794 
       
  9795   throwError: function(error, start, end) {
       
  9796     end = end || this.index;
       
  9797     var colStr = (isDefined(start)
       
  9798             ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
       
  9799             : ' ' + end);
       
  9800     throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
       
  9801         error, colStr, this.text);
       
  9802   },
       
  9803 
       
  9804   readNumber: function() {
       
  9805     var number = '';
       
  9806     var start = this.index;
       
  9807     while (this.index < this.text.length) {
       
  9808       var ch = lowercase(this.text.charAt(this.index));
       
  9809       if (ch == '.' || this.isNumber(ch)) {
       
  9810         number += ch;
       
  9811       } else {
       
  9812         var peekCh = this.peek();
       
  9813         if (ch == 'e' && this.isExpOperator(peekCh)) {
       
  9814           number += ch;
       
  9815         } else if (this.isExpOperator(ch) &&
       
  9816             peekCh && this.isNumber(peekCh) &&
       
  9817             number.charAt(number.length - 1) == 'e') {
       
  9818           number += ch;
       
  9819         } else if (this.isExpOperator(ch) &&
       
  9820             (!peekCh || !this.isNumber(peekCh)) &&
       
  9821             number.charAt(number.length - 1) == 'e') {
       
  9822           this.throwError('Invalid exponent');
       
  9823         } else {
       
  9824           break;
       
  9825         }
       
  9826       }
       
  9827       this.index++;
       
  9828     }
       
  9829     number = 1 * number;
       
  9830     this.tokens.push({
       
  9831       index: start,
       
  9832       text: number,
       
  9833       json: true,
       
  9834       fn: function() { return number; }
       
  9835     });
       
  9836   },
       
  9837 
       
  9838   readIdent: function() {
       
  9839     var parser = this;
       
  9840 
       
  9841     var ident = '';
       
  9842     var start = this.index;
       
  9843 
       
  9844     var lastDot, peekIndex, methodName, ch;
       
  9845 
       
  9846     while (this.index < this.text.length) {
       
  9847       ch = this.text.charAt(this.index);
       
  9848       if (ch === '.' || this.isIdent(ch) || this.isNumber(ch)) {
       
  9849         if (ch === '.') lastDot = this.index;
       
  9850         ident += ch;
       
  9851       } else {
       
  9852         break;
       
  9853       }
       
  9854       this.index++;
       
  9855     }
       
  9856 
       
  9857     //check if this is not a method invocation and if it is back out to last dot
       
  9858     if (lastDot) {
       
  9859       peekIndex = this.index;
       
  9860       while (peekIndex < this.text.length) {
       
  9861         ch = this.text.charAt(peekIndex);
       
  9862         if (ch === '(') {
       
  9863           methodName = ident.substr(lastDot - start + 1);
       
  9864           ident = ident.substr(0, lastDot - start);
       
  9865           this.index = peekIndex;
       
  9866           break;
       
  9867         }
       
  9868         if (this.isWhitespace(ch)) {
       
  9869           peekIndex++;
       
  9870         } else {
       
  9871           break;
       
  9872         }
       
  9873       }
       
  9874     }
       
  9875 
       
  9876 
       
  9877     var token = {
       
  9878       index: start,
       
  9879       text: ident
       
  9880     };
       
  9881 
       
  9882     // OPERATORS is our own object so we don't need to use special hasOwnPropertyFn
       
  9883     if (OPERATORS.hasOwnProperty(ident)) {
       
  9884       token.fn = OPERATORS[ident];
       
  9885       token.json = OPERATORS[ident];
       
  9886     } else {
       
  9887       var getter = getterFn(ident, this.options, this.text);
       
  9888       token.fn = extend(function(self, locals) {
       
  9889         return (getter(self, locals));
       
  9890       }, {
       
  9891         assign: function(self, value) {
       
  9892           return setter(self, ident, value, parser.text, parser.options);
       
  9893         }
       
  9894       });
       
  9895     }
       
  9896 
       
  9897     this.tokens.push(token);
       
  9898 
       
  9899     if (methodName) {
       
  9900       this.tokens.push({
       
  9901         index:lastDot,
       
  9902         text: '.',
       
  9903         json: false
       
  9904       });
       
  9905       this.tokens.push({
       
  9906         index: lastDot + 1,
       
  9907         text: methodName,
       
  9908         json: false
       
  9909       });
       
  9910     }
       
  9911   },
       
  9912 
       
  9913   readString: function(quote) {
       
  9914     var start = this.index;
       
  9915     this.index++;
       
  9916     var string = '';
       
  9917     var rawString = quote;
       
  9918     var escape = false;
       
  9919     while (this.index < this.text.length) {
       
  9920       var ch = this.text.charAt(this.index);
       
  9921       rawString += ch;
       
  9922       if (escape) {
       
  9923         if (ch === 'u') {
       
  9924           var hex = this.text.substring(this.index + 1, this.index + 5);
       
  9925           if (!hex.match(/[\da-f]{4}/i))
       
  9926             this.throwError('Invalid unicode escape [\\u' + hex + ']');
       
  9927           this.index += 4;
       
  9928           string += String.fromCharCode(parseInt(hex, 16));
       
  9929         } else {
       
  9930           var rep = ESCAPE[ch];
       
  9931           if (rep) {
       
  9932             string += rep;
       
  9933           } else {
       
  9934             string += ch;
       
  9935           }
       
  9936         }
       
  9937         escape = false;
       
  9938       } else if (ch === '\\') {
       
  9939         escape = true;
       
  9940       } else if (ch === quote) {
       
  9941         this.index++;
       
  9942         this.tokens.push({
       
  9943           index: start,
       
  9944           text: rawString,
       
  9945           string: string,
       
  9946           json: true,
       
  9947           fn: function() { return string; }
       
  9948         });
       
  9949         return;
       
  9950       } else {
       
  9951         string += ch;
       
  9952       }
       
  9953       this.index++;
       
  9954     }
       
  9955     this.throwError('Unterminated quote', start);
       
  9956   }
       
  9957 };
       
  9958 
       
  9959 
       
  9960 /**
       
  9961  * @constructor
       
  9962  */
       
  9963 var Parser = function (lexer, $filter, options) {
       
  9964   this.lexer = lexer;
       
  9965   this.$filter = $filter;
       
  9966   this.options = options;
       
  9967 };
       
  9968 
       
  9969 Parser.ZERO = function () { return 0; };
       
  9970 
       
  9971 Parser.prototype = {
       
  9972   constructor: Parser,
       
  9973 
       
  9974   parse: function (text, json) {
       
  9975     this.text = text;
       
  9976 
       
  9977     //TODO(i): strip all the obsolte json stuff from this file
       
  9978     this.json = json;
       
  9979 
       
  9980     this.tokens = this.lexer.lex(text);
       
  9981 
       
  9982     if (json) {
       
  9983       // The extra level of aliasing is here, just in case the lexer misses something, so that
       
  9984       // we prevent any accidental execution in JSON.
       
  9985       this.assignment = this.logicalOR;
       
  9986 
       
  9987       this.functionCall =
       
  9988       this.fieldAccess =
       
  9989       this.objectIndex =
       
  9990       this.filterChain = function() {
       
  9991         this.throwError('is not valid json', {text: text, index: 0});
       
  9992       };
       
  9993     }
       
  9994 
       
  9995     var value = json ? this.primary() : this.statements();
       
  9996 
       
  9997     if (this.tokens.length !== 0) {
       
  9998       this.throwError('is an unexpected token', this.tokens[0]);
       
  9999     }
       
 10000 
       
 10001     value.literal = !!value.literal;
       
 10002     value.constant = !!value.constant;
       
 10003 
       
 10004     return value;
       
 10005   },
       
 10006 
       
 10007   primary: function () {
       
 10008     var primary;
       
 10009     if (this.expect('(')) {
       
 10010       primary = this.filterChain();
       
 10011       this.consume(')');
       
 10012     } else if (this.expect('[')) {
       
 10013       primary = this.arrayDeclaration();
       
 10014     } else if (this.expect('{')) {
       
 10015       primary = this.object();
       
 10016     } else {
       
 10017       var token = this.expect();
       
 10018       primary = token.fn;
       
 10019       if (!primary) {
       
 10020         this.throwError('not a primary expression', token);
       
 10021       }
       
 10022       if (token.json) {
       
 10023         primary.constant = true;
       
 10024         primary.literal = true;
       
 10025       }
       
 10026     }
       
 10027 
       
 10028     var next, context;
       
 10029     while ((next = this.expect('(', '[', '.'))) {
       
 10030       if (next.text === '(') {
       
 10031         primary = this.functionCall(primary, context);
       
 10032         context = null;
       
 10033       } else if (next.text === '[') {
       
 10034         context = primary;
       
 10035         primary = this.objectIndex(primary);
       
 10036       } else if (next.text === '.') {
       
 10037         context = primary;
       
 10038         primary = this.fieldAccess(primary);
       
 10039       } else {
       
 10040         this.throwError('IMPOSSIBLE');
       
 10041       }
       
 10042     }
       
 10043     return primary;
       
 10044   },
       
 10045 
       
 10046   throwError: function(msg, token) {
       
 10047     throw $parseMinErr('syntax',
       
 10048         'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
       
 10049           token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
       
 10050   },
       
 10051 
       
 10052   peekToken: function() {
       
 10053     if (this.tokens.length === 0)
       
 10054       throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
       
 10055     return this.tokens[0];
       
 10056   },
       
 10057 
       
 10058   peek: function(e1, e2, e3, e4) {
       
 10059     if (this.tokens.length > 0) {
       
 10060       var token = this.tokens[0];
       
 10061       var t = token.text;
       
 10062       if (t === e1 || t === e2 || t === e3 || t === e4 ||
       
 10063           (!e1 && !e2 && !e3 && !e4)) {
       
 10064         return token;
       
 10065       }
       
 10066     }
       
 10067     return false;
       
 10068   },
       
 10069 
       
 10070   expect: function(e1, e2, e3, e4){
       
 10071     var token = this.peek(e1, e2, e3, e4);
       
 10072     if (token) {
       
 10073       if (this.json && !token.json) {
       
 10074         this.throwError('is not valid json', token);
       
 10075       }
       
 10076       this.tokens.shift();
       
 10077       return token;
       
 10078     }
       
 10079     return false;
       
 10080   },
       
 10081 
       
 10082   consume: function(e1){
       
 10083     if (!this.expect(e1)) {
       
 10084       this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
       
 10085     }
       
 10086   },
       
 10087 
       
 10088   unaryFn: function(fn, right) {
       
 10089     return extend(function(self, locals) {
       
 10090       return fn(self, locals, right);
       
 10091     }, {
       
 10092       constant:right.constant
       
 10093     });
       
 10094   },
       
 10095 
       
 10096   ternaryFn: function(left, middle, right){
       
 10097     return extend(function(self, locals){
       
 10098       return left(self, locals) ? middle(self, locals) : right(self, locals);
       
 10099     }, {
       
 10100       constant: left.constant && middle.constant && right.constant
       
 10101     });
       
 10102   },
       
 10103 
       
 10104   binaryFn: function(left, fn, right) {
       
 10105     return extend(function(self, locals) {
       
 10106       return fn(self, locals, left, right);
       
 10107     }, {
       
 10108       constant:left.constant && right.constant
       
 10109     });
       
 10110   },
       
 10111 
       
 10112   statements: function() {
       
 10113     var statements = [];
       
 10114     while (true) {
       
 10115       if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
       
 10116         statements.push(this.filterChain());
       
 10117       if (!this.expect(';')) {
       
 10118         // optimize for the common case where there is only one statement.
       
 10119         // TODO(size): maybe we should not support multiple statements?
       
 10120         return (statements.length === 1)
       
 10121             ? statements[0]
       
 10122             : function(self, locals) {
       
 10123                 var value;
       
 10124                 for (var i = 0; i < statements.length; i++) {
       
 10125                   var statement = statements[i];
       
 10126                   if (statement) {
       
 10127                     value = statement(self, locals);
       
 10128                   }
       
 10129                 }
       
 10130                 return value;
       
 10131               };
       
 10132       }
       
 10133     }
       
 10134   },
       
 10135 
       
 10136   filterChain: function() {
       
 10137     var left = this.expression();
       
 10138     var token;
       
 10139     while (true) {
       
 10140       if ((token = this.expect('|'))) {
       
 10141         left = this.binaryFn(left, token.fn, this.filter());
       
 10142       } else {
       
 10143         return left;
       
 10144       }
       
 10145     }
       
 10146   },
       
 10147 
       
 10148   filter: function() {
       
 10149     var token = this.expect();
       
 10150     var fn = this.$filter(token.text);
       
 10151     var argsFn = [];
       
 10152     while (true) {
       
 10153       if ((token = this.expect(':'))) {
       
 10154         argsFn.push(this.expression());
       
 10155       } else {
       
 10156         var fnInvoke = function(self, locals, input) {
       
 10157           var args = [input];
       
 10158           for (var i = 0; i < argsFn.length; i++) {
       
 10159             args.push(argsFn[i](self, locals));
       
 10160           }
       
 10161           return fn.apply(self, args);
       
 10162         };
       
 10163         return function() {
       
 10164           return fnInvoke;
       
 10165         };
       
 10166       }
       
 10167     }
       
 10168   },
       
 10169 
       
 10170   expression: function() {
       
 10171     return this.assignment();
       
 10172   },
       
 10173 
       
 10174   assignment: function() {
       
 10175     var left = this.ternary();
       
 10176     var right;
       
 10177     var token;
       
 10178     if ((token = this.expect('='))) {
       
 10179       if (!left.assign) {
       
 10180         this.throwError('implies assignment but [' +
       
 10181             this.text.substring(0, token.index) + '] can not be assigned to', token);
       
 10182       }
       
 10183       right = this.ternary();
       
 10184       return function(scope, locals) {
       
 10185         return left.assign(scope, right(scope, locals), locals);
       
 10186       };
       
 10187     }
       
 10188     return left;
       
 10189   },
       
 10190 
       
 10191   ternary: function() {
       
 10192     var left = this.logicalOR();
       
 10193     var middle;
       
 10194     var token;
       
 10195     if ((token = this.expect('?'))) {
       
 10196       middle = this.ternary();
       
 10197       if ((token = this.expect(':'))) {
       
 10198         return this.ternaryFn(left, middle, this.ternary());
       
 10199       } else {
       
 10200         this.throwError('expected :', token);
       
 10201       }
       
 10202     } else {
       
 10203       return left;
       
 10204     }
       
 10205   },
       
 10206 
       
 10207   logicalOR: function() {
       
 10208     var left = this.logicalAND();
       
 10209     var token;
       
 10210     while (true) {
       
 10211       if ((token = this.expect('||'))) {
       
 10212         left = this.binaryFn(left, token.fn, this.logicalAND());
       
 10213       } else {
       
 10214         return left;
       
 10215       }
       
 10216     }
       
 10217   },
       
 10218 
       
 10219   logicalAND: function() {
       
 10220     var left = this.equality();
       
 10221     var token;
       
 10222     if ((token = this.expect('&&'))) {
       
 10223       left = this.binaryFn(left, token.fn, this.logicalAND());
       
 10224     }
       
 10225     return left;
       
 10226   },
       
 10227 
       
 10228   equality: function() {
       
 10229     var left = this.relational();
       
 10230     var token;
       
 10231     if ((token = this.expect('==','!=','===','!=='))) {
       
 10232       left = this.binaryFn(left, token.fn, this.equality());
       
 10233     }
       
 10234     return left;
       
 10235   },
       
 10236 
       
 10237   relational: function() {
       
 10238     var left = this.additive();
       
 10239     var token;
       
 10240     if ((token = this.expect('<', '>', '<=', '>='))) {
       
 10241       left = this.binaryFn(left, token.fn, this.relational());
       
 10242     }
       
 10243     return left;
       
 10244   },
       
 10245 
       
 10246   additive: function() {
       
 10247     var left = this.multiplicative();
       
 10248     var token;
       
 10249     while ((token = this.expect('+','-'))) {
       
 10250       left = this.binaryFn(left, token.fn, this.multiplicative());
       
 10251     }
       
 10252     return left;
       
 10253   },
       
 10254 
       
 10255   multiplicative: function() {
       
 10256     var left = this.unary();
       
 10257     var token;
       
 10258     while ((token = this.expect('*','/','%'))) {
       
 10259       left = this.binaryFn(left, token.fn, this.unary());
       
 10260     }
       
 10261     return left;
       
 10262   },
       
 10263 
       
 10264   unary: function() {
       
 10265     var token;
       
 10266     if (this.expect('+')) {
       
 10267       return this.primary();
       
 10268     } else if ((token = this.expect('-'))) {
       
 10269       return this.binaryFn(Parser.ZERO, token.fn, this.unary());
       
 10270     } else if ((token = this.expect('!'))) {
       
 10271       return this.unaryFn(token.fn, this.unary());
       
 10272     } else {
       
 10273       return this.primary();
       
 10274     }
       
 10275   },
       
 10276 
       
 10277   fieldAccess: function(object) {
       
 10278     var parser = this;
       
 10279     var field = this.expect().text;
       
 10280     var getter = getterFn(field, this.options, this.text);
       
 10281 
       
 10282     return extend(function(scope, locals, self) {
       
 10283       return getter(self || object(scope, locals));
       
 10284     }, {
       
 10285       assign: function(scope, value, locals) {
       
 10286         return setter(object(scope, locals), field, value, parser.text, parser.options);
       
 10287       }
       
 10288     });
       
 10289   },
       
 10290 
       
 10291   objectIndex: function(obj) {
       
 10292     var parser = this;
       
 10293 
       
 10294     var indexFn = this.expression();
       
 10295     this.consume(']');
       
 10296 
       
 10297     return extend(function(self, locals) {
       
 10298       var o = obj(self, locals),
       
 10299           i = indexFn(self, locals),
       
 10300           v, p;
       
 10301 
       
 10302       if (!o) return undefined;
       
 10303       v = ensureSafeObject(o[i], parser.text);
       
 10304       if (v && v.then && parser.options.unwrapPromises) {
       
 10305         p = v;
       
 10306         if (!('$$v' in v)) {
       
 10307           p.$$v = undefined;
       
 10308           p.then(function(val) { p.$$v = val; });
       
 10309         }
       
 10310         v = v.$$v;
       
 10311       }
       
 10312       return v;
       
 10313     }, {
       
 10314       assign: function(self, value, locals) {
       
 10315         var key = indexFn(self, locals);
       
 10316         // prevent overwriting of Function.constructor which would break ensureSafeObject check
       
 10317         var safe = ensureSafeObject(obj(self, locals), parser.text);
       
 10318         return safe[key] = value;
       
 10319       }
       
 10320     });
       
 10321   },
       
 10322 
       
 10323   functionCall: function(fn, contextGetter) {
       
 10324     var argsFn = [];
       
 10325     if (this.peekToken().text !== ')') {
       
 10326       do {
       
 10327         argsFn.push(this.expression());
       
 10328       } while (this.expect(','));
       
 10329     }
       
 10330     this.consume(')');
       
 10331 
       
 10332     var parser = this;
       
 10333 
       
 10334     return function(scope, locals) {
       
 10335       var args = [];
       
 10336       var context = contextGetter ? contextGetter(scope, locals) : scope;
       
 10337 
       
 10338       for (var i = 0; i < argsFn.length; i++) {
       
 10339         args.push(argsFn[i](scope, locals));
       
 10340       }
       
 10341       var fnPtr = fn(scope, locals, context) || noop;
       
 10342 
       
 10343       ensureSafeObject(context, parser.text);
       
 10344       ensureSafeObject(fnPtr, parser.text);
       
 10345 
       
 10346       // IE stupidity! (IE doesn't have apply for some native functions)
       
 10347       var v = fnPtr.apply
       
 10348             ? fnPtr.apply(context, args)
       
 10349             : fnPtr(args[0], args[1], args[2], args[3], args[4]);
       
 10350 
       
 10351       return ensureSafeObject(v, parser.text);
       
 10352     };
       
 10353   },
       
 10354 
       
 10355   // This is used with json array declaration
       
 10356   arrayDeclaration: function () {
       
 10357     var elementFns = [];
       
 10358     var allConstant = true;
       
 10359     if (this.peekToken().text !== ']') {
       
 10360       do {
       
 10361         if (this.peek(']')) {
       
 10362           // Support trailing commas per ES5.1.
       
 10363           break;
       
 10364         }
       
 10365         var elementFn = this.expression();
       
 10366         elementFns.push(elementFn);
       
 10367         if (!elementFn.constant) {
       
 10368           allConstant = false;
       
 10369         }
       
 10370       } while (this.expect(','));
       
 10371     }
       
 10372     this.consume(']');
       
 10373 
       
 10374     return extend(function(self, locals) {
       
 10375       var array = [];
       
 10376       for (var i = 0; i < elementFns.length; i++) {
       
 10377         array.push(elementFns[i](self, locals));
       
 10378       }
       
 10379       return array;
       
 10380     }, {
       
 10381       literal: true,
       
 10382       constant: allConstant
       
 10383     });
       
 10384   },
       
 10385 
       
 10386   object: function () {
       
 10387     var keyValues = [];
       
 10388     var allConstant = true;
       
 10389     if (this.peekToken().text !== '}') {
       
 10390       do {
       
 10391         if (this.peek('}')) {
       
 10392           // Support trailing commas per ES5.1.
       
 10393           break;
       
 10394         }
       
 10395         var token = this.expect(),
       
 10396         key = token.string || token.text;
       
 10397         this.consume(':');
       
 10398         var value = this.expression();
       
 10399         keyValues.push({key: key, value: value});
       
 10400         if (!value.constant) {
       
 10401           allConstant = false;
       
 10402         }
       
 10403       } while (this.expect(','));
       
 10404     }
       
 10405     this.consume('}');
       
 10406 
       
 10407     return extend(function(self, locals) {
       
 10408       var object = {};
       
 10409       for (var i = 0; i < keyValues.length; i++) {
       
 10410         var keyValue = keyValues[i];
       
 10411         object[keyValue.key] = keyValue.value(self, locals);
       
 10412       }
       
 10413       return object;
       
 10414     }, {
       
 10415       literal: true,
       
 10416       constant: allConstant
       
 10417     });
       
 10418   }
       
 10419 };
       
 10420 
       
 10421 
       
 10422 //////////////////////////////////////////////////
       
 10423 // Parser helper functions
       
 10424 //////////////////////////////////////////////////
       
 10425 
       
 10426 function setter(obj, path, setValue, fullExp, options) {
       
 10427   //needed?
       
 10428   options = options || {};
       
 10429 
       
 10430   var element = path.split('.'), key;
       
 10431   for (var i = 0; element.length > 1; i++) {
       
 10432     key = ensureSafeMemberName(element.shift(), fullExp);
       
 10433     var propertyObj = obj[key];
       
 10434     if (!propertyObj) {
       
 10435       propertyObj = {};
       
 10436       obj[key] = propertyObj;
       
 10437     }
       
 10438     obj = propertyObj;
       
 10439     if (obj.then && options.unwrapPromises) {
       
 10440       promiseWarning(fullExp);
       
 10441       if (!("$$v" in obj)) {
       
 10442         (function(promise) {
       
 10443           promise.then(function(val) { promise.$$v = val; }); }
       
 10444         )(obj);
       
 10445       }
       
 10446       if (obj.$$v === undefined) {
       
 10447         obj.$$v = {};
       
 10448       }
       
 10449       obj = obj.$$v;
       
 10450     }
       
 10451   }
       
 10452   key = ensureSafeMemberName(element.shift(), fullExp);
       
 10453   obj[key] = setValue;
       
 10454   return setValue;
       
 10455 }
       
 10456 
       
 10457 var getterFnCache = {};
       
 10458 
       
 10459 /**
       
 10460  * Implementation of the "Black Hole" variant from:
       
 10461  * - http://jsperf.com/angularjs-parse-getter/4
       
 10462  * - http://jsperf.com/path-evaluation-simplified/7
       
 10463  */
       
 10464 function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
       
 10465   ensureSafeMemberName(key0, fullExp);
       
 10466   ensureSafeMemberName(key1, fullExp);
       
 10467   ensureSafeMemberName(key2, fullExp);
       
 10468   ensureSafeMemberName(key3, fullExp);
       
 10469   ensureSafeMemberName(key4, fullExp);
       
 10470 
       
 10471   return !options.unwrapPromises
       
 10472       ? function cspSafeGetter(scope, locals) {
       
 10473           var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope;
       
 10474 
       
 10475           if (pathVal == null) return pathVal;
       
 10476           pathVal = pathVal[key0];
       
 10477 
       
 10478           if (!key1) return pathVal;
       
 10479           if (pathVal == null) return undefined;
       
 10480           pathVal = pathVal[key1];
       
 10481 
       
 10482           if (!key2) return pathVal;
       
 10483           if (pathVal == null) return undefined;
       
 10484           pathVal = pathVal[key2];
       
 10485 
       
 10486           if (!key3) return pathVal;
       
 10487           if (pathVal == null) return undefined;
       
 10488           pathVal = pathVal[key3];
       
 10489 
       
 10490           if (!key4) return pathVal;
       
 10491           if (pathVal == null) return undefined;
       
 10492           pathVal = pathVal[key4];
       
 10493 
       
 10494           return pathVal;
       
 10495         }
       
 10496       : function cspSafePromiseEnabledGetter(scope, locals) {
       
 10497           var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope,
       
 10498               promise;
       
 10499 
       
 10500           if (pathVal == null) return pathVal;
       
 10501 
       
 10502           pathVal = pathVal[key0];
       
 10503           if (pathVal && pathVal.then) {
       
 10504             promiseWarning(fullExp);
       
 10505             if (!("$$v" in pathVal)) {
       
 10506               promise = pathVal;
       
 10507               promise.$$v = undefined;
       
 10508               promise.then(function(val) { promise.$$v = val; });
       
 10509             }
       
 10510             pathVal = pathVal.$$v;
       
 10511           }
       
 10512 
       
 10513           if (!key1) return pathVal;
       
 10514           if (pathVal == null) return undefined;
       
 10515           pathVal = pathVal[key1];
       
 10516           if (pathVal && pathVal.then) {
       
 10517             promiseWarning(fullExp);
       
 10518             if (!("$$v" in pathVal)) {
       
 10519               promise = pathVal;
       
 10520               promise.$$v = undefined;
       
 10521               promise.then(function(val) { promise.$$v = val; });
       
 10522             }
       
 10523             pathVal = pathVal.$$v;
       
 10524           }
       
 10525 
       
 10526           if (!key2) return pathVal;
       
 10527           if (pathVal == null) return undefined;
       
 10528           pathVal = pathVal[key2];
       
 10529           if (pathVal && pathVal.then) {
       
 10530             promiseWarning(fullExp);
       
 10531             if (!("$$v" in pathVal)) {
       
 10532               promise = pathVal;
       
 10533               promise.$$v = undefined;
       
 10534               promise.then(function(val) { promise.$$v = val; });
       
 10535             }
       
 10536             pathVal = pathVal.$$v;
       
 10537           }
       
 10538 
       
 10539           if (!key3) return pathVal;
       
 10540           if (pathVal == null) return undefined;
       
 10541           pathVal = pathVal[key3];
       
 10542           if (pathVal && pathVal.then) {
       
 10543             promiseWarning(fullExp);
       
 10544             if (!("$$v" in pathVal)) {
       
 10545               promise = pathVal;
       
 10546               promise.$$v = undefined;
       
 10547               promise.then(function(val) { promise.$$v = val; });
       
 10548             }
       
 10549             pathVal = pathVal.$$v;
       
 10550           }
       
 10551 
       
 10552           if (!key4) return pathVal;
       
 10553           if (pathVal == null) return undefined;
       
 10554           pathVal = pathVal[key4];
       
 10555           if (pathVal && pathVal.then) {
       
 10556             promiseWarning(fullExp);
       
 10557             if (!("$$v" in pathVal)) {
       
 10558               promise = pathVal;
       
 10559               promise.$$v = undefined;
       
 10560               promise.then(function(val) { promise.$$v = val; });
       
 10561             }
       
 10562             pathVal = pathVal.$$v;
       
 10563           }
       
 10564           return pathVal;
       
 10565         };
       
 10566 }
       
 10567 
       
 10568 function simpleGetterFn1(key0, fullExp) {
       
 10569   ensureSafeMemberName(key0, fullExp);
       
 10570 
       
 10571   return function simpleGetterFn1(scope, locals) {
       
 10572     if (scope == null) return undefined;
       
 10573     return ((locals && locals.hasOwnProperty(key0)) ? locals : scope)[key0];
       
 10574   };
       
 10575 }
       
 10576 
       
 10577 function simpleGetterFn2(key0, key1, fullExp) {
       
 10578   ensureSafeMemberName(key0, fullExp);
       
 10579   ensureSafeMemberName(key1, fullExp);
       
 10580 
       
 10581   return function simpleGetterFn2(scope, locals) {
       
 10582     if (scope == null) return undefined;
       
 10583     scope = ((locals && locals.hasOwnProperty(key0)) ? locals : scope)[key0];
       
 10584     return scope == null ? undefined : scope[key1];
       
 10585   };
       
 10586 }
       
 10587 
       
 10588 function getterFn(path, options, fullExp) {
       
 10589   // Check whether the cache has this getter already.
       
 10590   // We can use hasOwnProperty directly on the cache because we ensure,
       
 10591   // see below, that the cache never stores a path called 'hasOwnProperty'
       
 10592   if (getterFnCache.hasOwnProperty(path)) {
       
 10593     return getterFnCache[path];
       
 10594   }
       
 10595 
       
 10596   var pathKeys = path.split('.'),
       
 10597       pathKeysLength = pathKeys.length,
       
 10598       fn;
       
 10599 
       
 10600   // When we have only 1 or 2 tokens, use optimized special case closures.
       
 10601   // http://jsperf.com/angularjs-parse-getter/6
       
 10602   if (!options.unwrapPromises && pathKeysLength === 1) {
       
 10603     fn = simpleGetterFn1(pathKeys[0], fullExp);
       
 10604   } else if (!options.unwrapPromises && pathKeysLength === 2) {
       
 10605     fn = simpleGetterFn2(pathKeys[0], pathKeys[1], fullExp);
       
 10606   } else if (options.csp) {
       
 10607     if (pathKeysLength < 6) {
       
 10608       fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp,
       
 10609                           options);
       
 10610     } else {
       
 10611       fn = function(scope, locals) {
       
 10612         var i = 0, val;
       
 10613         do {
       
 10614           val = cspSafeGetterFn(pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++],
       
 10615                                 pathKeys[i++], fullExp, options)(scope, locals);
       
 10616 
       
 10617           locals = undefined; // clear after first iteration
       
 10618           scope = val;
       
 10619         } while (i < pathKeysLength);
       
 10620         return val;
       
 10621       };
       
 10622     }
       
 10623   } else {
       
 10624     var code = 'var p;\n';
       
 10625     forEach(pathKeys, function(key, index) {
       
 10626       ensureSafeMemberName(key, fullExp);
       
 10627       code += 'if(s == null) return undefined;\n' +
       
 10628               's='+ (index
       
 10629                       // we simply dereference 's' on any .dot notation
       
 10630                       ? 's'
       
 10631                       // but if we are first then we check locals first, and if so read it first
       
 10632                       : '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' +
       
 10633               (options.unwrapPromises
       
 10634                 ? 'if (s && s.then) {\n' +
       
 10635                   ' pw("' + fullExp.replace(/(["\r\n])/g, '\\$1') + '");\n' +
       
 10636                   ' if (!("$$v" in s)) {\n' +
       
 10637                     ' p=s;\n' +
       
 10638                     ' p.$$v = undefined;\n' +
       
 10639                     ' p.then(function(v) {p.$$v=v;});\n' +
       
 10640                     '}\n' +
       
 10641                   ' s=s.$$v\n' +
       
 10642                 '}\n'
       
 10643                 : '');
       
 10644     });
       
 10645     code += 'return s;';
       
 10646 
       
 10647     /* jshint -W054 */
       
 10648     var evaledFnGetter = new Function('s', 'k', 'pw', code); // s=scope, k=locals, pw=promiseWarning
       
 10649     /* jshint +W054 */
       
 10650     evaledFnGetter.toString = valueFn(code);
       
 10651     fn = options.unwrapPromises ? function(scope, locals) {
       
 10652       return evaledFnGetter(scope, locals, promiseWarning);
       
 10653     } : evaledFnGetter;
       
 10654   }
       
 10655 
       
 10656   // Only cache the value if it's not going to mess up the cache object
       
 10657   // This is more performant that using Object.prototype.hasOwnProperty.call
       
 10658   if (path !== 'hasOwnProperty') {
       
 10659     getterFnCache[path] = fn;
       
 10660   }
       
 10661   return fn;
       
 10662 }
       
 10663 
       
 10664 ///////////////////////////////////
       
 10665 
       
 10666 /**
       
 10667  * @ngdoc service
       
 10668  * @name $parse
       
 10669  * @kind function
       
 10670  *
       
 10671  * @description
       
 10672  *
       
 10673  * Converts Angular {@link guide/expression expression} into a function.
       
 10674  *
       
 10675  * ```js
       
 10676  *   var getter = $parse('user.name');
       
 10677  *   var setter = getter.assign;
       
 10678  *   var context = {user:{name:'angular'}};
       
 10679  *   var locals = {user:{name:'local'}};
       
 10680  *
       
 10681  *   expect(getter(context)).toEqual('angular');
       
 10682  *   setter(context, 'newValue');
       
 10683  *   expect(context.user.name).toEqual('newValue');
       
 10684  *   expect(getter(context, locals)).toEqual('local');
       
 10685  * ```
       
 10686  *
       
 10687  *
       
 10688  * @param {string} expression String expression to compile.
       
 10689  * @returns {function(context, locals)} a function which represents the compiled expression:
       
 10690  *
       
 10691  *    * `context` – `{object}` – an object against which any expressions embedded in the strings
       
 10692  *      are evaluated against (typically a scope object).
       
 10693  *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
       
 10694  *      `context`.
       
 10695  *
       
 10696  *    The returned function also has the following properties:
       
 10697  *      * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
       
 10698  *        literal.
       
 10699  *      * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
       
 10700  *        constant literals.
       
 10701  *      * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
       
 10702  *        set to a function to change its value on the given context.
       
 10703  *
       
 10704  */
       
 10705 
       
 10706 
       
 10707 /**
       
 10708  * @ngdoc provider
       
 10709  * @name $parseProvider
       
 10710  * @function
       
 10711  *
       
 10712  * @description
       
 10713  * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
       
 10714  *  service.
       
 10715  */
       
 10716 function $ParseProvider() {
       
 10717   var cache = {};
       
 10718 
       
 10719   var $parseOptions = {
       
 10720     csp: false,
       
 10721     unwrapPromises: false,
       
 10722     logPromiseWarnings: true
       
 10723   };
       
 10724 
       
 10725 
       
 10726   /**
       
 10727    * @deprecated Promise unwrapping via $parse is deprecated and will be removed in the future.
       
 10728    *
       
 10729    * @ngdoc method
       
 10730    * @name $parseProvider#unwrapPromises
       
 10731    * @description
       
 10732    *
       
 10733    * **This feature is deprecated, see deprecation notes below for more info**
       
 10734    *
       
 10735    * If set to true (default is false), $parse will unwrap promises automatically when a promise is
       
 10736    * found at any part of the expression. In other words, if set to true, the expression will always
       
 10737    * result in a non-promise value.
       
 10738    *
       
 10739    * While the promise is unresolved, it's treated as undefined, but once resolved and fulfilled,
       
 10740    * the fulfillment value is used in place of the promise while evaluating the expression.
       
 10741    *
       
 10742    * **Deprecation notice**
       
 10743    *
       
 10744    * This is a feature that didn't prove to be wildly useful or popular, primarily because of the
       
 10745    * dichotomy between data access in templates (accessed as raw values) and controller code
       
 10746    * (accessed as promises).
       
 10747    *
       
 10748    * In most code we ended up resolving promises manually in controllers anyway and thus unifying
       
 10749    * the model access there.
       
 10750    *
       
 10751    * Other downsides of automatic promise unwrapping:
       
 10752    *
       
 10753    * - when building components it's often desirable to receive the raw promises
       
 10754    * - adds complexity and slows down expression evaluation
       
 10755    * - makes expression code pre-generation unattractive due to the amount of code that needs to be
       
 10756    *   generated
       
 10757    * - makes IDE auto-completion and tool support hard
       
 10758    *
       
 10759    * **Warning Logs**
       
 10760    *
       
 10761    * If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a
       
 10762    * promise (to reduce the noise, each expression is logged only once). To disable this logging use
       
 10763    * `$parseProvider.logPromiseWarnings(false)` api.
       
 10764    *
       
 10765    *
       
 10766    * @param {boolean=} value New value.
       
 10767    * @returns {boolean|self} Returns the current setting when used as getter and self if used as
       
 10768    *                         setter.
       
 10769    */
       
 10770   this.unwrapPromises = function(value) {
       
 10771     if (isDefined(value)) {
       
 10772       $parseOptions.unwrapPromises = !!value;
       
 10773       return this;
       
 10774     } else {
       
 10775       return $parseOptions.unwrapPromises;
       
 10776     }
       
 10777   };
       
 10778 
       
 10779 
       
 10780   /**
       
 10781    * @deprecated Promise unwrapping via $parse is deprecated and will be removed in the future.
       
 10782    *
       
 10783    * @ngdoc method
       
 10784    * @name $parseProvider#logPromiseWarnings
       
 10785    * @description
       
 10786    *
       
 10787    * Controls whether Angular should log a warning on any encounter of a promise in an expression.
       
 10788    *
       
 10789    * The default is set to `true`.
       
 10790    *
       
 10791    * This setting applies only if `$parseProvider.unwrapPromises` setting is set to true as well.
       
 10792    *
       
 10793    * @param {boolean=} value New value.
       
 10794    * @returns {boolean|self} Returns the current setting when used as getter and self if used as
       
 10795    *                         setter.
       
 10796    */
       
 10797  this.logPromiseWarnings = function(value) {
       
 10798     if (isDefined(value)) {
       
 10799       $parseOptions.logPromiseWarnings = value;
       
 10800       return this;
       
 10801     } else {
       
 10802       return $parseOptions.logPromiseWarnings;
       
 10803     }
       
 10804   };
       
 10805 
       
 10806 
       
 10807   this.$get = ['$filter', '$sniffer', '$log', function($filter, $sniffer, $log) {
       
 10808     $parseOptions.csp = $sniffer.csp;
       
 10809 
       
 10810     promiseWarning = function promiseWarningFn(fullExp) {
       
 10811       if (!$parseOptions.logPromiseWarnings || promiseWarningCache.hasOwnProperty(fullExp)) return;
       
 10812       promiseWarningCache[fullExp] = true;
       
 10813       $log.warn('[$parse] Promise found in the expression `' + fullExp + '`. ' +
       
 10814           'Automatic unwrapping of promises in Angular expressions is deprecated.');
       
 10815     };
       
 10816 
       
 10817     return function(exp) {
       
 10818       var parsedExpression;
       
 10819 
       
 10820       switch (typeof exp) {
       
 10821         case 'string':
       
 10822 
       
 10823           if (cache.hasOwnProperty(exp)) {
       
 10824             return cache[exp];
       
 10825           }
       
 10826 
       
 10827           var lexer = new Lexer($parseOptions);
       
 10828           var parser = new Parser(lexer, $filter, $parseOptions);
       
 10829           parsedExpression = parser.parse(exp, false);
       
 10830 
       
 10831           if (exp !== 'hasOwnProperty') {
       
 10832             // Only cache the value if it's not going to mess up the cache object
       
 10833             // This is more performant that using Object.prototype.hasOwnProperty.call
       
 10834             cache[exp] = parsedExpression;
       
 10835           }
       
 10836 
       
 10837           return parsedExpression;
       
 10838 
       
 10839         case 'function':
       
 10840           return exp;
       
 10841 
       
 10842         default:
       
 10843           return noop;
       
 10844       }
       
 10845     };
       
 10846   }];
       
 10847 }
       
 10848 
       
 10849 /**
       
 10850  * @ngdoc service
       
 10851  * @name $q
       
 10852  * @requires $rootScope
       
 10853  *
       
 10854  * @description
       
 10855  * A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q).
       
 10856  *
       
 10857  * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
       
 10858  * interface for interacting with an object that represents the result of an action that is
       
 10859  * performed asynchronously, and may or may not be finished at any given point in time.
       
 10860  *
       
 10861  * From the perspective of dealing with error handling, deferred and promise APIs are to
       
 10862  * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
       
 10863  *
       
 10864  * ```js
       
 10865  *   // for the purpose of this example let's assume that variables `$q`, `scope` and `okToGreet`
       
 10866  *   // are available in the current lexical scope (they could have been injected or passed in).
       
 10867  *
       
 10868  *   function asyncGreet(name) {
       
 10869  *     var deferred = $q.defer();
       
 10870  *
       
 10871  *     setTimeout(function() {
       
 10872  *       // since this fn executes async in a future turn of the event loop, we need to wrap
       
 10873  *       // our code into an $apply call so that the model changes are properly observed.
       
 10874  *       scope.$apply(function() {
       
 10875  *         deferred.notify('About to greet ' + name + '.');
       
 10876  *
       
 10877  *         if (okToGreet(name)) {
       
 10878  *           deferred.resolve('Hello, ' + name + '!');
       
 10879  *         } else {
       
 10880  *           deferred.reject('Greeting ' + name + ' is not allowed.');
       
 10881  *         }
       
 10882  *       });
       
 10883  *     }, 1000);
       
 10884  *
       
 10885  *     return deferred.promise;
       
 10886  *   }
       
 10887  *
       
 10888  *   var promise = asyncGreet('Robin Hood');
       
 10889  *   promise.then(function(greeting) {
       
 10890  *     alert('Success: ' + greeting);
       
 10891  *   }, function(reason) {
       
 10892  *     alert('Failed: ' + reason);
       
 10893  *   }, function(update) {
       
 10894  *     alert('Got notification: ' + update);
       
 10895  *   });
       
 10896  * ```
       
 10897  *
       
 10898  * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
       
 10899  * comes in the way of guarantees that promise and deferred APIs make, see
       
 10900  * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
       
 10901  *
       
 10902  * Additionally the promise api allows for composition that is very hard to do with the
       
 10903  * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
       
 10904  * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
       
 10905  * section on serial or parallel joining of promises.
       
 10906  *
       
 10907  *
       
 10908  * # The Deferred API
       
 10909  *
       
 10910  * A new instance of deferred is constructed by calling `$q.defer()`.
       
 10911  *
       
 10912  * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
       
 10913  * that can be used for signaling the successful or unsuccessful completion, as well as the status
       
 10914  * of the task.
       
 10915  *
       
 10916  * **Methods**
       
 10917  *
       
 10918  * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
       
 10919  *   constructed via `$q.reject`, the promise will be rejected instead.
       
 10920  * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
       
 10921  *   resolving it with a rejection constructed via `$q.reject`.
       
 10922  * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
       
 10923  *   multiple times before the promise is either resolved or rejected.
       
 10924  *
       
 10925  * **Properties**
       
 10926  *
       
 10927  * - promise – `{Promise}` – promise object associated with this deferred.
       
 10928  *
       
 10929  *
       
 10930  * # The Promise API
       
 10931  *
       
 10932  * A new promise instance is created when a deferred instance is created and can be retrieved by
       
 10933  * calling `deferred.promise`.
       
 10934  *
       
 10935  * The purpose of the promise object is to allow for interested parties to get access to the result
       
 10936  * of the deferred task when it completes.
       
 10937  *
       
 10938  * **Methods**
       
 10939  *
       
 10940  * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
       
 10941  *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
       
 10942  *   as soon as the result is available. The callbacks are called with a single argument: the result
       
 10943  *   or rejection reason. Additionally, the notify callback may be called zero or more times to
       
 10944  *   provide a progress indication, before the promise is resolved or rejected.
       
 10945  *
       
 10946  *   This method *returns a new promise* which is resolved or rejected via the return value of the
       
 10947  *   `successCallback`, `errorCallback`. It also notifies via the return value of the
       
 10948  *   `notifyCallback` method. The promise can not be resolved or rejected from the notifyCallback
       
 10949  *   method.
       
 10950  *
       
 10951  * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
       
 10952  *
       
 10953  * - `finally(callback)` – allows you to observe either the fulfillment or rejection of a promise,
       
 10954  *   but to do so without modifying the final value. This is useful to release resources or do some
       
 10955  *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
       
 10956  *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
       
 10957  *   more information.
       
 10958  *
       
 10959  *   Because `finally` is a reserved word in JavaScript and reserved keywords are not supported as
       
 10960  *   property names by ES3, you'll need to invoke the method like `promise['finally'](callback)` to
       
 10961  *   make your code IE8 and Android 2.x compatible.
       
 10962  *
       
 10963  * # Chaining promises
       
 10964  *
       
 10965  * Because calling the `then` method of a promise returns a new derived promise, it is easily
       
 10966  * possible to create a chain of promises:
       
 10967  *
       
 10968  * ```js
       
 10969  *   promiseB = promiseA.then(function(result) {
       
 10970  *     return result + 1;
       
 10971  *   });
       
 10972  *
       
 10973  *   // promiseB will be resolved immediately after promiseA is resolved and its value
       
 10974  *   // will be the result of promiseA incremented by 1
       
 10975  * ```
       
 10976  *
       
 10977  * It is possible to create chains of any length and since a promise can be resolved with another
       
 10978  * promise (which will defer its resolution further), it is possible to pause/defer resolution of
       
 10979  * the promises at any point in the chain. This makes it possible to implement powerful APIs like
       
 10980  * $http's response interceptors.
       
 10981  *
       
 10982  *
       
 10983  * # Differences between Kris Kowal's Q and $q
       
 10984  *
       
 10985  *  There are two main differences:
       
 10986  *
       
 10987  * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
       
 10988  *   mechanism in angular, which means faster propagation of resolution or rejection into your
       
 10989  *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
       
 10990  * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
       
 10991  *   all the important functionality needed for common async tasks.
       
 10992  *
       
 10993  *  # Testing
       
 10994  *
       
 10995  *  ```js
       
 10996  *    it('should simulate promise', inject(function($q, $rootScope) {
       
 10997  *      var deferred = $q.defer();
       
 10998  *      var promise = deferred.promise;
       
 10999  *      var resolvedValue;
       
 11000  *
       
 11001  *      promise.then(function(value) { resolvedValue = value; });
       
 11002  *      expect(resolvedValue).toBeUndefined();
       
 11003  *
       
 11004  *      // Simulate resolving of promise
       
 11005  *      deferred.resolve(123);
       
 11006  *      // Note that the 'then' function does not get called synchronously.
       
 11007  *      // This is because we want the promise API to always be async, whether or not
       
 11008  *      // it got called synchronously or asynchronously.
       
 11009  *      expect(resolvedValue).toBeUndefined();
       
 11010  *
       
 11011  *      // Propagate promise resolution to 'then' functions using $apply().
       
 11012  *      $rootScope.$apply();
       
 11013  *      expect(resolvedValue).toEqual(123);
       
 11014  *    }));
       
 11015  *  ```
       
 11016  */
       
 11017 function $QProvider() {
       
 11018 
       
 11019   this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
       
 11020     return qFactory(function(callback) {
       
 11021       $rootScope.$evalAsync(callback);
       
 11022     }, $exceptionHandler);
       
 11023   }];
       
 11024 }
       
 11025 
       
 11026 
       
 11027 /**
       
 11028  * Constructs a promise manager.
       
 11029  *
       
 11030  * @param {function(Function)} nextTick Function for executing functions in the next turn.
       
 11031  * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
       
 11032  *     debugging purposes.
       
 11033  * @returns {object} Promise manager.
       
 11034  */
       
 11035 function qFactory(nextTick, exceptionHandler) {
       
 11036 
       
 11037   /**
       
 11038    * @ngdoc method
       
 11039    * @name $q#defer
       
 11040    * @function
       
 11041    *
       
 11042    * @description
       
 11043    * Creates a `Deferred` object which represents a task which will finish in the future.
       
 11044    *
       
 11045    * @returns {Deferred} Returns a new instance of deferred.
       
 11046    */
       
 11047   var defer = function() {
       
 11048     var pending = [],
       
 11049         value, deferred;
       
 11050 
       
 11051     deferred = {
       
 11052 
       
 11053       resolve: function(val) {
       
 11054         if (pending) {
       
 11055           var callbacks = pending;
       
 11056           pending = undefined;
       
 11057           value = ref(val);
       
 11058 
       
 11059           if (callbacks.length) {
       
 11060             nextTick(function() {
       
 11061               var callback;
       
 11062               for (var i = 0, ii = callbacks.length; i < ii; i++) {
       
 11063                 callback = callbacks[i];
       
 11064                 value.then(callback[0], callback[1], callback[2]);
       
 11065               }
       
 11066             });
       
 11067           }
       
 11068         }
       
 11069       },
       
 11070 
       
 11071 
       
 11072       reject: function(reason) {
       
 11073         deferred.resolve(createInternalRejectedPromise(reason));
       
 11074       },
       
 11075 
       
 11076 
       
 11077       notify: function(progress) {
       
 11078         if (pending) {
       
 11079           var callbacks = pending;
       
 11080 
       
 11081           if (pending.length) {
       
 11082             nextTick(function() {
       
 11083               var callback;
       
 11084               for (var i = 0, ii = callbacks.length; i < ii; i++) {
       
 11085                 callback = callbacks[i];
       
 11086                 callback[2](progress);
       
 11087               }
       
 11088             });
       
 11089           }
       
 11090         }
       
 11091       },
       
 11092 
       
 11093 
       
 11094       promise: {
       
 11095         then: function(callback, errback, progressback) {
       
 11096           var result = defer();
       
 11097 
       
 11098           var wrappedCallback = function(value) {
       
 11099             try {
       
 11100               result.resolve((isFunction(callback) ? callback : defaultCallback)(value));
       
 11101             } catch(e) {
       
 11102               result.reject(e);
       
 11103               exceptionHandler(e);
       
 11104             }
       
 11105           };
       
 11106 
       
 11107           var wrappedErrback = function(reason) {
       
 11108             try {
       
 11109               result.resolve((isFunction(errback) ? errback : defaultErrback)(reason));
       
 11110             } catch(e) {
       
 11111               result.reject(e);
       
 11112               exceptionHandler(e);
       
 11113             }
       
 11114           };
       
 11115 
       
 11116           var wrappedProgressback = function(progress) {
       
 11117             try {
       
 11118               result.notify((isFunction(progressback) ? progressback : defaultCallback)(progress));
       
 11119             } catch(e) {
       
 11120               exceptionHandler(e);
       
 11121             }
       
 11122           };
       
 11123 
       
 11124           if (pending) {
       
 11125             pending.push([wrappedCallback, wrappedErrback, wrappedProgressback]);
       
 11126           } else {
       
 11127             value.then(wrappedCallback, wrappedErrback, wrappedProgressback);
       
 11128           }
       
 11129 
       
 11130           return result.promise;
       
 11131         },
       
 11132 
       
 11133         "catch": function(callback) {
       
 11134           return this.then(null, callback);
       
 11135         },
       
 11136 
       
 11137         "finally": function(callback) {
       
 11138 
       
 11139           function makePromise(value, resolved) {
       
 11140             var result = defer();
       
 11141             if (resolved) {
       
 11142               result.resolve(value);
       
 11143             } else {
       
 11144               result.reject(value);
       
 11145             }
       
 11146             return result.promise;
       
 11147           }
       
 11148 
       
 11149           function handleCallback(value, isResolved) {
       
 11150             var callbackOutput = null;
       
 11151             try {
       
 11152               callbackOutput = (callback ||defaultCallback)();
       
 11153             } catch(e) {
       
 11154               return makePromise(e, false);
       
 11155             }
       
 11156             if (callbackOutput && isFunction(callbackOutput.then)) {
       
 11157               return callbackOutput.then(function() {
       
 11158                 return makePromise(value, isResolved);
       
 11159               }, function(error) {
       
 11160                 return makePromise(error, false);
       
 11161               });
       
 11162             } else {
       
 11163               return makePromise(value, isResolved);
       
 11164             }
       
 11165           }
       
 11166 
       
 11167           return this.then(function(value) {
       
 11168             return handleCallback(value, true);
       
 11169           }, function(error) {
       
 11170             return handleCallback(error, false);
       
 11171           });
       
 11172         }
       
 11173       }
       
 11174     };
       
 11175 
       
 11176     return deferred;
       
 11177   };
       
 11178 
       
 11179 
       
 11180   var ref = function(value) {
       
 11181     if (value && isFunction(value.then)) return value;
       
 11182     return {
       
 11183       then: function(callback) {
       
 11184         var result = defer();
       
 11185         nextTick(function() {
       
 11186           result.resolve(callback(value));
       
 11187         });
       
 11188         return result.promise;
       
 11189       }
       
 11190     };
       
 11191   };
       
 11192 
       
 11193 
       
 11194   /**
       
 11195    * @ngdoc method
       
 11196    * @name $q#reject
       
 11197    * @function
       
 11198    *
       
 11199    * @description
       
 11200    * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
       
 11201    * used to forward rejection in a chain of promises. If you are dealing with the last promise in
       
 11202    * a promise chain, you don't need to worry about it.
       
 11203    *
       
 11204    * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
       
 11205    * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
       
 11206    * a promise error callback and you want to forward the error to the promise derived from the
       
 11207    * current promise, you have to "rethrow" the error by returning a rejection constructed via
       
 11208    * `reject`.
       
 11209    *
       
 11210    * ```js
       
 11211    *   promiseB = promiseA.then(function(result) {
       
 11212    *     // success: do something and resolve promiseB
       
 11213    *     //          with the old or a new result
       
 11214    *     return result;
       
 11215    *   }, function(reason) {
       
 11216    *     // error: handle the error if possible and
       
 11217    *     //        resolve promiseB with newPromiseOrValue,
       
 11218    *     //        otherwise forward the rejection to promiseB
       
 11219    *     if (canHandle(reason)) {
       
 11220    *      // handle the error and recover
       
 11221    *      return newPromiseOrValue;
       
 11222    *     }
       
 11223    *     return $q.reject(reason);
       
 11224    *   });
       
 11225    * ```
       
 11226    *
       
 11227    * @param {*} reason Constant, message, exception or an object representing the rejection reason.
       
 11228    * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
       
 11229    */
       
 11230   var reject = function(reason) {
       
 11231     var result = defer();
       
 11232     result.reject(reason);
       
 11233     return result.promise;
       
 11234   };
       
 11235 
       
 11236   var createInternalRejectedPromise = function(reason) {
       
 11237     return {
       
 11238       then: function(callback, errback) {
       
 11239         var result = defer();
       
 11240         nextTick(function() {
       
 11241           try {
       
 11242             result.resolve((isFunction(errback) ? errback : defaultErrback)(reason));
       
 11243           } catch(e) {
       
 11244             result.reject(e);
       
 11245             exceptionHandler(e);
       
 11246           }
       
 11247         });
       
 11248         return result.promise;
       
 11249       }
       
 11250     };
       
 11251   };
       
 11252 
       
 11253 
       
 11254   /**
       
 11255    * @ngdoc method
       
 11256    * @name $q#when
       
 11257    * @function
       
 11258    *
       
 11259    * @description
       
 11260    * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
       
 11261    * This is useful when you are dealing with an object that might or might not be a promise, or if
       
 11262    * the promise comes from a source that can't be trusted.
       
 11263    *
       
 11264    * @param {*} value Value or a promise
       
 11265    * @returns {Promise} Returns a promise of the passed value or promise
       
 11266    */
       
 11267   var when = function(value, callback, errback, progressback) {
       
 11268     var result = defer(),
       
 11269         done;
       
 11270 
       
 11271     var wrappedCallback = function(value) {
       
 11272       try {
       
 11273         return (isFunction(callback) ? callback : defaultCallback)(value);
       
 11274       } catch (e) {
       
 11275         exceptionHandler(e);
       
 11276         return reject(e);
       
 11277       }
       
 11278     };
       
 11279 
       
 11280     var wrappedErrback = function(reason) {
       
 11281       try {
       
 11282         return (isFunction(errback) ? errback : defaultErrback)(reason);
       
 11283       } catch (e) {
       
 11284         exceptionHandler(e);
       
 11285         return reject(e);
       
 11286       }
       
 11287     };
       
 11288 
       
 11289     var wrappedProgressback = function(progress) {
       
 11290       try {
       
 11291         return (isFunction(progressback) ? progressback : defaultCallback)(progress);
       
 11292       } catch (e) {
       
 11293         exceptionHandler(e);
       
 11294       }
       
 11295     };
       
 11296 
       
 11297     nextTick(function() {
       
 11298       ref(value).then(function(value) {
       
 11299         if (done) return;
       
 11300         done = true;
       
 11301         result.resolve(ref(value).then(wrappedCallback, wrappedErrback, wrappedProgressback));
       
 11302       }, function(reason) {
       
 11303         if (done) return;
       
 11304         done = true;
       
 11305         result.resolve(wrappedErrback(reason));
       
 11306       }, function(progress) {
       
 11307         if (done) return;
       
 11308         result.notify(wrappedProgressback(progress));
       
 11309       });
       
 11310     });
       
 11311 
       
 11312     return result.promise;
       
 11313   };
       
 11314 
       
 11315 
       
 11316   function defaultCallback(value) {
       
 11317     return value;
       
 11318   }
       
 11319 
       
 11320 
       
 11321   function defaultErrback(reason) {
       
 11322     return reject(reason);
       
 11323   }
       
 11324 
       
 11325 
       
 11326   /**
       
 11327    * @ngdoc method
       
 11328    * @name $q#all
       
 11329    * @function
       
 11330    *
       
 11331    * @description
       
 11332    * Combines multiple promises into a single promise that is resolved when all of the input
       
 11333    * promises are resolved.
       
 11334    *
       
 11335    * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
       
 11336    * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
       
 11337    *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
       
 11338    *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
       
 11339    *   with the same rejection value.
       
 11340    */
       
 11341   function all(promises) {
       
 11342     var deferred = defer(),
       
 11343         counter = 0,
       
 11344         results = isArray(promises) ? [] : {};
       
 11345 
       
 11346     forEach(promises, function(promise, key) {
       
 11347       counter++;
       
 11348       ref(promise).then(function(value) {
       
 11349         if (results.hasOwnProperty(key)) return;
       
 11350         results[key] = value;
       
 11351         if (!(--counter)) deferred.resolve(results);
       
 11352       }, function(reason) {
       
 11353         if (results.hasOwnProperty(key)) return;
       
 11354         deferred.reject(reason);
       
 11355       });
       
 11356     });
       
 11357 
       
 11358     if (counter === 0) {
       
 11359       deferred.resolve(results);
       
 11360     }
       
 11361 
       
 11362     return deferred.promise;
       
 11363   }
       
 11364 
       
 11365   return {
       
 11366     defer: defer,
       
 11367     reject: reject,
       
 11368     when: when,
       
 11369     all: all
       
 11370   };
       
 11371 }
       
 11372 
       
 11373 function $$RAFProvider(){ //rAF
       
 11374   this.$get = ['$window', '$timeout', function($window, $timeout) {
       
 11375     var requestAnimationFrame = $window.requestAnimationFrame ||
       
 11376                                 $window.webkitRequestAnimationFrame ||
       
 11377                                 $window.mozRequestAnimationFrame;
       
 11378 
       
 11379     var cancelAnimationFrame = $window.cancelAnimationFrame ||
       
 11380                                $window.webkitCancelAnimationFrame ||
       
 11381                                $window.mozCancelAnimationFrame ||
       
 11382                                $window.webkitCancelRequestAnimationFrame;
       
 11383 
       
 11384     var rafSupported = !!requestAnimationFrame;
       
 11385     var raf = rafSupported
       
 11386       ? function(fn) {
       
 11387           var id = requestAnimationFrame(fn);
       
 11388           return function() {
       
 11389             cancelAnimationFrame(id);
       
 11390           };
       
 11391         }
       
 11392       : function(fn) {
       
 11393           var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
       
 11394           return function() {
       
 11395             $timeout.cancel(timer);
       
 11396           };
       
 11397         };
       
 11398 
       
 11399     raf.supported = rafSupported;
       
 11400 
       
 11401     return raf;
       
 11402   }];
       
 11403 }
       
 11404 
       
 11405 /**
       
 11406  * DESIGN NOTES
       
 11407  *
       
 11408  * The design decisions behind the scope are heavily favored for speed and memory consumption.
       
 11409  *
       
 11410  * The typical use of scope is to watch the expressions, which most of the time return the same
       
 11411  * value as last time so we optimize the operation.
       
 11412  *
       
 11413  * Closures construction is expensive in terms of speed as well as memory:
       
 11414  *   - No closures, instead use prototypical inheritance for API
       
 11415  *   - Internal state needs to be stored on scope directly, which means that private state is
       
 11416  *     exposed as $$____ properties
       
 11417  *
       
 11418  * Loop operations are optimized by using while(count--) { ... }
       
 11419  *   - this means that in order to keep the same order of execution as addition we have to add
       
 11420  *     items to the array at the beginning (shift) instead of at the end (push)
       
 11421  *
       
 11422  * Child scopes are created and removed often
       
 11423  *   - Using an array would be slow since inserts in middle are expensive so we use linked list
       
 11424  *
       
 11425  * There are few watches then a lot of observers. This is why you don't want the observer to be
       
 11426  * implemented in the same way as watch. Watch requires return of initialization function which
       
 11427  * are expensive to construct.
       
 11428  */
       
 11429 
       
 11430 
       
 11431 /**
       
 11432  * @ngdoc provider
       
 11433  * @name $rootScopeProvider
       
 11434  * @description
       
 11435  *
       
 11436  * Provider for the $rootScope service.
       
 11437  */
       
 11438 
       
 11439 /**
       
 11440  * @ngdoc method
       
 11441  * @name $rootScopeProvider#digestTtl
       
 11442  * @description
       
 11443  *
       
 11444  * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
       
 11445  * assuming that the model is unstable.
       
 11446  *
       
 11447  * The current default is 10 iterations.
       
 11448  *
       
 11449  * In complex applications it's possible that the dependencies between `$watch`s will result in
       
 11450  * several digest iterations. However if an application needs more than the default 10 digest
       
 11451  * iterations for its model to stabilize then you should investigate what is causing the model to
       
 11452  * continuously change during the digest.
       
 11453  *
       
 11454  * Increasing the TTL could have performance implications, so you should not change it without
       
 11455  * proper justification.
       
 11456  *
       
 11457  * @param {number} limit The number of digest iterations.
       
 11458  */
       
 11459 
       
 11460 
       
 11461 /**
       
 11462  * @ngdoc service
       
 11463  * @name $rootScope
       
 11464  * @description
       
 11465  *
       
 11466  * Every application has a single root {@link ng.$rootScope.Scope scope}.
       
 11467  * All other scopes are descendant scopes of the root scope. Scopes provide separation
       
 11468  * between the model and the view, via a mechanism for watching the model for changes.
       
 11469  * They also provide an event emission/broadcast and subscription facility. See the
       
 11470  * {@link guide/scope developer guide on scopes}.
       
 11471  */
       
 11472 function $RootScopeProvider(){
       
 11473   var TTL = 10;
       
 11474   var $rootScopeMinErr = minErr('$rootScope');
       
 11475   var lastDirtyWatch = null;
       
 11476 
       
 11477   this.digestTtl = function(value) {
       
 11478     if (arguments.length) {
       
 11479       TTL = value;
       
 11480     }
       
 11481     return TTL;
       
 11482   };
       
 11483 
       
 11484   this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
       
 11485       function( $injector,   $exceptionHandler,   $parse,   $browser) {
       
 11486 
       
 11487     /**
       
 11488      * @ngdoc type
       
 11489      * @name $rootScope.Scope
       
 11490      *
       
 11491      * @description
       
 11492      * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
       
 11493      * {@link auto.$injector $injector}. Child scopes are created using the
       
 11494      * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
       
 11495      * compiled HTML template is executed.)
       
 11496      *
       
 11497      * Here is a simple scope snippet to show how you can interact with the scope.
       
 11498      * ```html
       
 11499      * <file src="./test/ng/rootScopeSpec.js" tag="docs1" />
       
 11500      * ```
       
 11501      *
       
 11502      * # Inheritance
       
 11503      * A scope can inherit from a parent scope, as in this example:
       
 11504      * ```js
       
 11505          var parent = $rootScope;
       
 11506          var child = parent.$new();
       
 11507 
       
 11508          parent.salutation = "Hello";
       
 11509          child.name = "World";
       
 11510          expect(child.salutation).toEqual('Hello');
       
 11511 
       
 11512          child.salutation = "Welcome";
       
 11513          expect(child.salutation).toEqual('Welcome');
       
 11514          expect(parent.salutation).toEqual('Hello');
       
 11515      * ```
       
 11516      *
       
 11517      *
       
 11518      * @param {Object.<string, function()>=} providers Map of service factory which need to be
       
 11519      *                                       provided for the current scope. Defaults to {@link ng}.
       
 11520      * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
       
 11521      *                              append/override services provided by `providers`. This is handy
       
 11522      *                              when unit-testing and having the need to override a default
       
 11523      *                              service.
       
 11524      * @returns {Object} Newly created scope.
       
 11525      *
       
 11526      */
       
 11527     function Scope() {
       
 11528       this.$id = nextUid();
       
 11529       this.$$phase = this.$parent = this.$$watchers =
       
 11530                      this.$$nextSibling = this.$$prevSibling =
       
 11531                      this.$$childHead = this.$$childTail = null;
       
 11532       this['this'] = this.$root =  this;
       
 11533       this.$$destroyed = false;
       
 11534       this.$$asyncQueue = [];
       
 11535       this.$$postDigestQueue = [];
       
 11536       this.$$listeners = {};
       
 11537       this.$$listenerCount = {};
       
 11538       this.$$isolateBindings = {};
       
 11539     }
       
 11540 
       
 11541     /**
       
 11542      * @ngdoc property
       
 11543      * @name $rootScope.Scope#$id
       
 11544      * @returns {number} Unique scope ID (monotonically increasing alphanumeric sequence) useful for
       
 11545      *   debugging.
       
 11546      */
       
 11547 
       
 11548 
       
 11549     Scope.prototype = {
       
 11550       constructor: Scope,
       
 11551       /**
       
 11552        * @ngdoc method
       
 11553        * @name $rootScope.Scope#$new
       
 11554        * @function
       
 11555        *
       
 11556        * @description
       
 11557        * Creates a new child {@link ng.$rootScope.Scope scope}.
       
 11558        *
       
 11559        * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} and
       
 11560        * {@link ng.$rootScope.Scope#$digest $digest()} events. The scope can be removed from the
       
 11561        * scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
       
 11562        *
       
 11563        * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
       
 11564        * desired for the scope and its child scopes to be permanently detached from the parent and
       
 11565        * thus stop participating in model change detection and listener notification by invoking.
       
 11566        *
       
 11567        * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
       
 11568        *         parent scope. The scope is isolated, as it can not see parent scope properties.
       
 11569        *         When creating widgets, it is useful for the widget to not accidentally read parent
       
 11570        *         state.
       
 11571        *
       
 11572        * @returns {Object} The newly created child scope.
       
 11573        *
       
 11574        */
       
 11575       $new: function(isolate) {
       
 11576         var ChildScope,
       
 11577             child;
       
 11578 
       
 11579         if (isolate) {
       
 11580           child = new Scope();
       
 11581           child.$root = this.$root;
       
 11582           // ensure that there is just one async queue per $rootScope and its children
       
 11583           child.$$asyncQueue = this.$$asyncQueue;
       
 11584           child.$$postDigestQueue = this.$$postDigestQueue;
       
 11585         } else {
       
 11586           ChildScope = function() {}; // should be anonymous; This is so that when the minifier munges
       
 11587             // the name it does not become random set of chars. This will then show up as class
       
 11588             // name in the web inspector.
       
 11589           ChildScope.prototype = this;
       
 11590           child = new ChildScope();
       
 11591           child.$id = nextUid();
       
 11592         }
       
 11593         child['this'] = child;
       
 11594         child.$$listeners = {};
       
 11595         child.$$listenerCount = {};
       
 11596         child.$parent = this;
       
 11597         child.$$watchers = child.$$nextSibling = child.$$childHead = child.$$childTail = null;
       
 11598         child.$$prevSibling = this.$$childTail;
       
 11599         if (this.$$childHead) {
       
 11600           this.$$childTail.$$nextSibling = child;
       
 11601           this.$$childTail = child;
       
 11602         } else {
       
 11603           this.$$childHead = this.$$childTail = child;
       
 11604         }
       
 11605         return child;
       
 11606       },
       
 11607 
       
 11608       /**
       
 11609        * @ngdoc method
       
 11610        * @name $rootScope.Scope#$watch
       
 11611        * @function
       
 11612        *
       
 11613        * @description
       
 11614        * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
       
 11615        *
       
 11616        * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
       
 11617        *   $digest()} and should return the value that will be watched. (Since
       
 11618        *   {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the
       
 11619        *   `watchExpression` can execute multiple times per
       
 11620        *   {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.)
       
 11621        * - The `listener` is called only when the value from the current `watchExpression` and the
       
 11622        *   previous call to `watchExpression` are not equal (with the exception of the initial run,
       
 11623        *   see below). The inequality is determined according to
       
 11624        *   {@link angular.equals} function. To save the value of the object for later comparison,
       
 11625        *   the {@link angular.copy} function is used. It also means that watching complex options
       
 11626        *   will have adverse memory and performance implications.
       
 11627        * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
       
 11628        *   This is achieved by rerunning the watchers until no changes are detected. The rerun
       
 11629        *   iteration limit is 10 to prevent an infinite loop deadlock.
       
 11630        *
       
 11631        *
       
 11632        * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
       
 11633        * you can register a `watchExpression` function with no `listener`. (Since `watchExpression`
       
 11634        * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a
       
 11635        * change is detected, be prepared for multiple calls to your listener.)
       
 11636        *
       
 11637        * After a watcher is registered with the scope, the `listener` fn is called asynchronously
       
 11638        * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
       
 11639        * watcher. In rare cases, this is undesirable because the listener is called when the result
       
 11640        * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
       
 11641        * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
       
 11642        * listener was called due to initialization.
       
 11643        *
       
 11644        * The example below contains an illustration of using a function as your $watch listener
       
 11645        *
       
 11646        *
       
 11647        * # Example
       
 11648        * ```js
       
 11649            // let's assume that scope was dependency injected as the $rootScope
       
 11650            var scope = $rootScope;
       
 11651            scope.name = 'misko';
       
 11652            scope.counter = 0;
       
 11653 
       
 11654            expect(scope.counter).toEqual(0);
       
 11655            scope.$watch('name', function(newValue, oldValue) {
       
 11656              scope.counter = scope.counter + 1;
       
 11657            });
       
 11658            expect(scope.counter).toEqual(0);
       
 11659 
       
 11660            scope.$digest();
       
 11661            // no variable change
       
 11662            expect(scope.counter).toEqual(0);
       
 11663 
       
 11664            scope.name = 'adam';
       
 11665            scope.$digest();
       
 11666            expect(scope.counter).toEqual(1);
       
 11667 
       
 11668 
       
 11669 
       
 11670            // Using a listener function
       
 11671            var food;
       
 11672            scope.foodCounter = 0;
       
 11673            expect(scope.foodCounter).toEqual(0);
       
 11674            scope.$watch(
       
 11675              // This is the listener function
       
 11676              function() { return food; },
       
 11677              // This is the change handler
       
 11678              function(newValue, oldValue) {
       
 11679                if ( newValue !== oldValue ) {
       
 11680                  // Only increment the counter if the value changed
       
 11681                  scope.foodCounter = scope.foodCounter + 1;
       
 11682                }
       
 11683              }
       
 11684            );
       
 11685            // No digest has been run so the counter will be zero
       
 11686            expect(scope.foodCounter).toEqual(0);
       
 11687 
       
 11688            // Run the digest but since food has not changed count will still be zero
       
 11689            scope.$digest();
       
 11690            expect(scope.foodCounter).toEqual(0);
       
 11691 
       
 11692            // Update food and run digest.  Now the counter will increment
       
 11693            food = 'cheeseburger';
       
 11694            scope.$digest();
       
 11695            expect(scope.foodCounter).toEqual(1);
       
 11696 
       
 11697        * ```
       
 11698        *
       
 11699        *
       
 11700        *
       
 11701        * @param {(function()|string)} watchExpression Expression that is evaluated on each
       
 11702        *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
       
 11703        *    a call to the `listener`.
       
 11704        *
       
 11705        *    - `string`: Evaluated as {@link guide/expression expression}
       
 11706        *    - `function(scope)`: called with current `scope` as a parameter.
       
 11707        * @param {(function()|string)=} listener Callback called whenever the return value of
       
 11708        *   the `watchExpression` changes.
       
 11709        *
       
 11710        *    - `string`: Evaluated as {@link guide/expression expression}
       
 11711        *    - `function(newValue, oldValue, scope)`: called with current and previous values as
       
 11712        *      parameters.
       
 11713        *
       
 11714        * @param {boolean=} objectEquality Compare object for equality rather than for reference.
       
 11715        * @returns {function()} Returns a deregistration function for this listener.
       
 11716        */
       
 11717       $watch: function(watchExp, listener, objectEquality) {
       
 11718         var scope = this,
       
 11719             get = compileToFn(watchExp, 'watch'),
       
 11720             array = scope.$$watchers,
       
 11721             watcher = {
       
 11722               fn: listener,
       
 11723               last: initWatchVal,
       
 11724               get: get,
       
 11725               exp: watchExp,
       
 11726               eq: !!objectEquality
       
 11727             };
       
 11728 
       
 11729         lastDirtyWatch = null;
       
 11730 
       
 11731         // in the case user pass string, we need to compile it, do we really need this ?
       
 11732         if (!isFunction(listener)) {
       
 11733           var listenFn = compileToFn(listener || noop, 'listener');
       
 11734           watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);};
       
 11735         }
       
 11736 
       
 11737         if (typeof watchExp == 'string' && get.constant) {
       
 11738           var originalFn = watcher.fn;
       
 11739           watcher.fn = function(newVal, oldVal, scope) {
       
 11740             originalFn.call(this, newVal, oldVal, scope);
       
 11741             arrayRemove(array, watcher);
       
 11742           };
       
 11743         }
       
 11744 
       
 11745         if (!array) {
       
 11746           array = scope.$$watchers = [];
       
 11747         }
       
 11748         // we use unshift since we use a while loop in $digest for speed.
       
 11749         // the while loop reads in reverse order.
       
 11750         array.unshift(watcher);
       
 11751 
       
 11752         return function() {
       
 11753           arrayRemove(array, watcher);
       
 11754           lastDirtyWatch = null;
       
 11755         };
       
 11756       },
       
 11757 
       
 11758 
       
 11759       /**
       
 11760        * @ngdoc method
       
 11761        * @name $rootScope.Scope#$watchCollection
       
 11762        * @function
       
 11763        *
       
 11764        * @description
       
 11765        * Shallow watches the properties of an object and fires whenever any of the properties change
       
 11766        * (for arrays, this implies watching the array items; for object maps, this implies watching
       
 11767        * the properties). If a change is detected, the `listener` callback is fired.
       
 11768        *
       
 11769        * - The `obj` collection is observed via standard $watch operation and is examined on every
       
 11770        *   call to $digest() to see if any items have been added, removed, or moved.
       
 11771        * - The `listener` is called whenever anything within the `obj` has changed. Examples include
       
 11772        *   adding, removing, and moving items belonging to an object or array.
       
 11773        *
       
 11774        *
       
 11775        * # Example
       
 11776        * ```js
       
 11777           $scope.names = ['igor', 'matias', 'misko', 'james'];
       
 11778           $scope.dataCount = 4;
       
 11779 
       
 11780           $scope.$watchCollection('names', function(newNames, oldNames) {
       
 11781             $scope.dataCount = newNames.length;
       
 11782           });
       
 11783 
       
 11784           expect($scope.dataCount).toEqual(4);
       
 11785           $scope.$digest();
       
 11786 
       
 11787           //still at 4 ... no changes
       
 11788           expect($scope.dataCount).toEqual(4);
       
 11789 
       
 11790           $scope.names.pop();
       
 11791           $scope.$digest();
       
 11792 
       
 11793           //now there's been a change
       
 11794           expect($scope.dataCount).toEqual(3);
       
 11795        * ```
       
 11796        *
       
 11797        *
       
 11798        * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
       
 11799        *    expression value should evaluate to an object or an array which is observed on each
       
 11800        *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
       
 11801        *    collection will trigger a call to the `listener`.
       
 11802        *
       
 11803        * @param {function(newCollection, oldCollection, scope)} listener a callback function called
       
 11804        *    when a change is detected.
       
 11805        *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
       
 11806        *    - The `oldCollection` object is a copy of the former collection data.
       
 11807        *      Due to performance considerations, the`oldCollection` value is computed only if the
       
 11808        *      `listener` function declares two or more arguments.
       
 11809        *    - The `scope` argument refers to the current scope.
       
 11810        *
       
 11811        * @returns {function()} Returns a de-registration function for this listener. When the
       
 11812        *    de-registration function is executed, the internal watch operation is terminated.
       
 11813        */
       
 11814       $watchCollection: function(obj, listener) {
       
 11815         var self = this;
       
 11816         // the current value, updated on each dirty-check run
       
 11817         var newValue;
       
 11818         // a shallow copy of the newValue from the last dirty-check run,
       
 11819         // updated to match newValue during dirty-check run
       
 11820         var oldValue;
       
 11821         // a shallow copy of the newValue from when the last change happened
       
 11822         var veryOldValue;
       
 11823         // only track veryOldValue if the listener is asking for it
       
 11824         var trackVeryOldValue = (listener.length > 1);
       
 11825         var changeDetected = 0;
       
 11826         var objGetter = $parse(obj);
       
 11827         var internalArray = [];
       
 11828         var internalObject = {};
       
 11829         var initRun = true;
       
 11830         var oldLength = 0;
       
 11831 
       
 11832         function $watchCollectionWatch() {
       
 11833           newValue = objGetter(self);
       
 11834           var newLength, key;
       
 11835 
       
 11836           if (!isObject(newValue)) { // if primitive
       
 11837             if (oldValue !== newValue) {
       
 11838               oldValue = newValue;
       
 11839               changeDetected++;
       
 11840             }
       
 11841           } else if (isArrayLike(newValue)) {
       
 11842             if (oldValue !== internalArray) {
       
 11843               // we are transitioning from something which was not an array into array.
       
 11844               oldValue = internalArray;
       
 11845               oldLength = oldValue.length = 0;
       
 11846               changeDetected++;
       
 11847             }
       
 11848 
       
 11849             newLength = newValue.length;
       
 11850 
       
 11851             if (oldLength !== newLength) {
       
 11852               // if lengths do not match we need to trigger change notification
       
 11853               changeDetected++;
       
 11854               oldValue.length = oldLength = newLength;
       
 11855             }
       
 11856             // copy the items to oldValue and look for changes.
       
 11857             for (var i = 0; i < newLength; i++) {
       
 11858               var bothNaN = (oldValue[i] !== oldValue[i]) &&
       
 11859                   (newValue[i] !== newValue[i]);
       
 11860               if (!bothNaN && (oldValue[i] !== newValue[i])) {
       
 11861                 changeDetected++;
       
 11862                 oldValue[i] = newValue[i];
       
 11863               }
       
 11864             }
       
 11865           } else {
       
 11866             if (oldValue !== internalObject) {
       
 11867               // we are transitioning from something which was not an object into object.
       
 11868               oldValue = internalObject = {};
       
 11869               oldLength = 0;
       
 11870               changeDetected++;
       
 11871             }
       
 11872             // copy the items to oldValue and look for changes.
       
 11873             newLength = 0;
       
 11874             for (key in newValue) {
       
 11875               if (newValue.hasOwnProperty(key)) {
       
 11876                 newLength++;
       
 11877                 if (oldValue.hasOwnProperty(key)) {
       
 11878                   if (oldValue[key] !== newValue[key]) {
       
 11879                     changeDetected++;
       
 11880                     oldValue[key] = newValue[key];
       
 11881                   }
       
 11882                 } else {
       
 11883                   oldLength++;
       
 11884                   oldValue[key] = newValue[key];
       
 11885                   changeDetected++;
       
 11886                 }
       
 11887               }
       
 11888             }
       
 11889             if (oldLength > newLength) {
       
 11890               // we used to have more keys, need to find them and destroy them.
       
 11891               changeDetected++;
       
 11892               for(key in oldValue) {
       
 11893                 if (oldValue.hasOwnProperty(key) && !newValue.hasOwnProperty(key)) {
       
 11894                   oldLength--;
       
 11895                   delete oldValue[key];
       
 11896                 }
       
 11897               }
       
 11898             }
       
 11899           }
       
 11900           return changeDetected;
       
 11901         }
       
 11902 
       
 11903         function $watchCollectionAction() {
       
 11904           if (initRun) {
       
 11905             initRun = false;
       
 11906             listener(newValue, newValue, self);
       
 11907           } else {
       
 11908             listener(newValue, veryOldValue, self);
       
 11909           }
       
 11910 
       
 11911           // make a copy for the next time a collection is changed
       
 11912           if (trackVeryOldValue) {
       
 11913             if (!isObject(newValue)) {
       
 11914               //primitive
       
 11915               veryOldValue = newValue;
       
 11916             } else if (isArrayLike(newValue)) {
       
 11917               veryOldValue = new Array(newValue.length);
       
 11918               for (var i = 0; i < newValue.length; i++) {
       
 11919                 veryOldValue[i] = newValue[i];
       
 11920               }
       
 11921             } else { // if object
       
 11922               veryOldValue = {};
       
 11923               for (var key in newValue) {
       
 11924                 if (hasOwnProperty.call(newValue, key)) {
       
 11925                   veryOldValue[key] = newValue[key];
       
 11926                 }
       
 11927               }
       
 11928             }
       
 11929           }
       
 11930         }
       
 11931 
       
 11932         return this.$watch($watchCollectionWatch, $watchCollectionAction);
       
 11933       },
       
 11934 
       
 11935       /**
       
 11936        * @ngdoc method
       
 11937        * @name $rootScope.Scope#$digest
       
 11938        * @function
       
 11939        *
       
 11940        * @description
       
 11941        * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
       
 11942        * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
       
 11943        * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
       
 11944        * until no more listeners are firing. This means that it is possible to get into an infinite
       
 11945        * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
       
 11946        * iterations exceeds 10.
       
 11947        *
       
 11948        * Usually, you don't call `$digest()` directly in
       
 11949        * {@link ng.directive:ngController controllers} or in
       
 11950        * {@link ng.$compileProvider#directive directives}.
       
 11951        * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
       
 11952        * a {@link ng.$compileProvider#directive directives}), which will force a `$digest()`.
       
 11953        *
       
 11954        * If you want to be notified whenever `$digest()` is called,
       
 11955        * you can register a `watchExpression` function with
       
 11956        * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
       
 11957        *
       
 11958        * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
       
 11959        *
       
 11960        * # Example
       
 11961        * ```js
       
 11962            var scope = ...;
       
 11963            scope.name = 'misko';
       
 11964            scope.counter = 0;
       
 11965 
       
 11966            expect(scope.counter).toEqual(0);
       
 11967            scope.$watch('name', function(newValue, oldValue) {
       
 11968              scope.counter = scope.counter + 1;
       
 11969            });
       
 11970            expect(scope.counter).toEqual(0);
       
 11971 
       
 11972            scope.$digest();
       
 11973            // no variable change
       
 11974            expect(scope.counter).toEqual(0);
       
 11975 
       
 11976            scope.name = 'adam';
       
 11977            scope.$digest();
       
 11978            expect(scope.counter).toEqual(1);
       
 11979        * ```
       
 11980        *
       
 11981        */
       
 11982       $digest: function() {
       
 11983         var watch, value, last,
       
 11984             watchers,
       
 11985             asyncQueue = this.$$asyncQueue,
       
 11986             postDigestQueue = this.$$postDigestQueue,
       
 11987             length,
       
 11988             dirty, ttl = TTL,
       
 11989             next, current, target = this,
       
 11990             watchLog = [],
       
 11991             logIdx, logMsg, asyncTask;
       
 11992 
       
 11993         beginPhase('$digest');
       
 11994 
       
 11995         lastDirtyWatch = null;
       
 11996 
       
 11997         do { // "while dirty" loop
       
 11998           dirty = false;
       
 11999           current = target;
       
 12000 
       
 12001           while(asyncQueue.length) {
       
 12002             try {
       
 12003               asyncTask = asyncQueue.shift();
       
 12004               asyncTask.scope.$eval(asyncTask.expression);
       
 12005             } catch (e) {
       
 12006               clearPhase();
       
 12007               $exceptionHandler(e);
       
 12008             }
       
 12009             lastDirtyWatch = null;
       
 12010           }
       
 12011 
       
 12012           traverseScopesLoop:
       
 12013           do { // "traverse the scopes" loop
       
 12014             if ((watchers = current.$$watchers)) {
       
 12015               // process our watches
       
 12016               length = watchers.length;
       
 12017               while (length--) {
       
 12018                 try {
       
 12019                   watch = watchers[length];
       
 12020                   // Most common watches are on primitives, in which case we can short
       
 12021                   // circuit it with === operator, only when === fails do we use .equals
       
 12022                   if (watch) {
       
 12023                     if ((value = watch.get(current)) !== (last = watch.last) &&
       
 12024                         !(watch.eq
       
 12025                             ? equals(value, last)
       
 12026                             : (typeof value == 'number' && typeof last == 'number'
       
 12027                                && isNaN(value) && isNaN(last)))) {
       
 12028                       dirty = true;
       
 12029                       lastDirtyWatch = watch;
       
 12030                       watch.last = watch.eq ? copy(value) : value;
       
 12031                       watch.fn(value, ((last === initWatchVal) ? value : last), current);
       
 12032                       if (ttl < 5) {
       
 12033                         logIdx = 4 - ttl;
       
 12034                         if (!watchLog[logIdx]) watchLog[logIdx] = [];
       
 12035                         logMsg = (isFunction(watch.exp))
       
 12036                             ? 'fn: ' + (watch.exp.name || watch.exp.toString())
       
 12037                             : watch.exp;
       
 12038                         logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last);
       
 12039                         watchLog[logIdx].push(logMsg);
       
 12040                       }
       
 12041                     } else if (watch === lastDirtyWatch) {
       
 12042                       // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
       
 12043                       // have already been tested.
       
 12044                       dirty = false;
       
 12045                       break traverseScopesLoop;
       
 12046                     }
       
 12047                   }
       
 12048                 } catch (e) {
       
 12049                   clearPhase();
       
 12050                   $exceptionHandler(e);
       
 12051                 }
       
 12052               }
       
 12053             }
       
 12054 
       
 12055             // Insanity Warning: scope depth-first traversal
       
 12056             // yes, this code is a bit crazy, but it works and we have tests to prove it!
       
 12057             // this piece should be kept in sync with the traversal in $broadcast
       
 12058             if (!(next = (current.$$childHead ||
       
 12059                 (current !== target && current.$$nextSibling)))) {
       
 12060               while(current !== target && !(next = current.$$nextSibling)) {
       
 12061                 current = current.$parent;
       
 12062               }
       
 12063             }
       
 12064           } while ((current = next));
       
 12065 
       
 12066           // `break traverseScopesLoop;` takes us to here
       
 12067 
       
 12068           if((dirty || asyncQueue.length) && !(ttl--)) {
       
 12069             clearPhase();
       
 12070             throw $rootScopeMinErr('infdig',
       
 12071                 '{0} $digest() iterations reached. Aborting!\n' +
       
 12072                 'Watchers fired in the last 5 iterations: {1}',
       
 12073                 TTL, toJson(watchLog));
       
 12074           }
       
 12075 
       
 12076         } while (dirty || asyncQueue.length);
       
 12077 
       
 12078         clearPhase();
       
 12079 
       
 12080         while(postDigestQueue.length) {
       
 12081           try {
       
 12082             postDigestQueue.shift()();
       
 12083           } catch (e) {
       
 12084             $exceptionHandler(e);
       
 12085           }
       
 12086         }
       
 12087       },
       
 12088 
       
 12089 
       
 12090       /**
       
 12091        * @ngdoc event
       
 12092        * @name $rootScope.Scope#$destroy
       
 12093        * @eventType broadcast on scope being destroyed
       
 12094        *
       
 12095        * @description
       
 12096        * Broadcasted when a scope and its children are being destroyed.
       
 12097        *
       
 12098        * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
       
 12099        * clean up DOM bindings before an element is removed from the DOM.
       
 12100        */
       
 12101 
       
 12102       /**
       
 12103        * @ngdoc method
       
 12104        * @name $rootScope.Scope#$destroy
       
 12105        * @function
       
 12106        *
       
 12107        * @description
       
 12108        * Removes the current scope (and all of its children) from the parent scope. Removal implies
       
 12109        * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
       
 12110        * propagate to the current scope and its children. Removal also implies that the current
       
 12111        * scope is eligible for garbage collection.
       
 12112        *
       
 12113        * The `$destroy()` is usually used by directives such as
       
 12114        * {@link ng.directive:ngRepeat ngRepeat} for managing the
       
 12115        * unrolling of the loop.
       
 12116        *
       
 12117        * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
       
 12118        * Application code can register a `$destroy` event handler that will give it a chance to
       
 12119        * perform any necessary cleanup.
       
 12120        *
       
 12121        * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
       
 12122        * clean up DOM bindings before an element is removed from the DOM.
       
 12123        */
       
 12124       $destroy: function() {
       
 12125         // we can't destroy the root scope or a scope that has been already destroyed
       
 12126         if (this.$$destroyed) return;
       
 12127         var parent = this.$parent;
       
 12128 
       
 12129         this.$broadcast('$destroy');
       
 12130         this.$$destroyed = true;
       
 12131         if (this === $rootScope) return;
       
 12132 
       
 12133         forEach(this.$$listenerCount, bind(null, decrementListenerCount, this));
       
 12134 
       
 12135         if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
       
 12136         if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
       
 12137         if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
       
 12138         if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
       
 12139 
       
 12140         // This is bogus code that works around Chrome's GC leak
       
 12141         // see: https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
       
 12142         this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
       
 12143             this.$$childTail = null;
       
 12144       },
       
 12145 
       
 12146       /**
       
 12147        * @ngdoc method
       
 12148        * @name $rootScope.Scope#$eval
       
 12149        * @function
       
 12150        *
       
 12151        * @description
       
 12152        * Executes the `expression` on the current scope and returns the result. Any exceptions in
       
 12153        * the expression are propagated (uncaught). This is useful when evaluating Angular
       
 12154        * expressions.
       
 12155        *
       
 12156        * # Example
       
 12157        * ```js
       
 12158            var scope = ng.$rootScope.Scope();
       
 12159            scope.a = 1;
       
 12160            scope.b = 2;
       
 12161 
       
 12162            expect(scope.$eval('a+b')).toEqual(3);
       
 12163            expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
       
 12164        * ```
       
 12165        *
       
 12166        * @param {(string|function())=} expression An angular expression to be executed.
       
 12167        *
       
 12168        *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
       
 12169        *    - `function(scope)`: execute the function with the current `scope` parameter.
       
 12170        *
       
 12171        * @param {(object)=} locals Local variables object, useful for overriding values in scope.
       
 12172        * @returns {*} The result of evaluating the expression.
       
 12173        */
       
 12174       $eval: function(expr, locals) {
       
 12175         return $parse(expr)(this, locals);
       
 12176       },
       
 12177 
       
 12178       /**
       
 12179        * @ngdoc method
       
 12180        * @name $rootScope.Scope#$evalAsync
       
 12181        * @function
       
 12182        *
       
 12183        * @description
       
 12184        * Executes the expression on the current scope at a later point in time.
       
 12185        *
       
 12186        * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
       
 12187        * that:
       
 12188        *
       
 12189        *   - it will execute after the function that scheduled the evaluation (preferably before DOM
       
 12190        *     rendering).
       
 12191        *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
       
 12192        *     `expression` execution.
       
 12193        *
       
 12194        * Any exceptions from the execution of the expression are forwarded to the
       
 12195        * {@link ng.$exceptionHandler $exceptionHandler} service.
       
 12196        *
       
 12197        * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
       
 12198        * will be scheduled. However, it is encouraged to always call code that changes the model
       
 12199        * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
       
 12200        *
       
 12201        * @param {(string|function())=} expression An angular expression to be executed.
       
 12202        *
       
 12203        *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
       
 12204        *    - `function(scope)`: execute the function with the current `scope` parameter.
       
 12205        *
       
 12206        */
       
 12207       $evalAsync: function(expr) {
       
 12208         // if we are outside of an $digest loop and this is the first time we are scheduling async
       
 12209         // task also schedule async auto-flush
       
 12210         if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) {
       
 12211           $browser.defer(function() {
       
 12212             if ($rootScope.$$asyncQueue.length) {
       
 12213               $rootScope.$digest();
       
 12214             }
       
 12215           });
       
 12216         }
       
 12217 
       
 12218         this.$$asyncQueue.push({scope: this, expression: expr});
       
 12219       },
       
 12220 
       
 12221       $$postDigest : function(fn) {
       
 12222         this.$$postDigestQueue.push(fn);
       
 12223       },
       
 12224 
       
 12225       /**
       
 12226        * @ngdoc method
       
 12227        * @name $rootScope.Scope#$apply
       
 12228        * @function
       
 12229        *
       
 12230        * @description
       
 12231        * `$apply()` is used to execute an expression in angular from outside of the angular
       
 12232        * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
       
 12233        * Because we are calling into the angular framework we need to perform proper scope life
       
 12234        * cycle of {@link ng.$exceptionHandler exception handling},
       
 12235        * {@link ng.$rootScope.Scope#$digest executing watches}.
       
 12236        *
       
 12237        * ## Life cycle
       
 12238        *
       
 12239        * # Pseudo-Code of `$apply()`
       
 12240        * ```js
       
 12241            function $apply(expr) {
       
 12242              try {
       
 12243                return $eval(expr);
       
 12244              } catch (e) {
       
 12245                $exceptionHandler(e);
       
 12246              } finally {
       
 12247                $root.$digest();
       
 12248              }
       
 12249            }
       
 12250        * ```
       
 12251        *
       
 12252        *
       
 12253        * Scope's `$apply()` method transitions through the following stages:
       
 12254        *
       
 12255        * 1. The {@link guide/expression expression} is executed using the
       
 12256        *    {@link ng.$rootScope.Scope#$eval $eval()} method.
       
 12257        * 2. Any exceptions from the execution of the expression are forwarded to the
       
 12258        *    {@link ng.$exceptionHandler $exceptionHandler} service.
       
 12259        * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
       
 12260        *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
       
 12261        *
       
 12262        *
       
 12263        * @param {(string|function())=} exp An angular expression to be executed.
       
 12264        *
       
 12265        *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
       
 12266        *    - `function(scope)`: execute the function with current `scope` parameter.
       
 12267        *
       
 12268        * @returns {*} The result of evaluating the expression.
       
 12269        */
       
 12270       $apply: function(expr) {
       
 12271         try {
       
 12272           beginPhase('$apply');
       
 12273           return this.$eval(expr);
       
 12274         } catch (e) {
       
 12275           $exceptionHandler(e);
       
 12276         } finally {
       
 12277           clearPhase();
       
 12278           try {
       
 12279             $rootScope.$digest();
       
 12280           } catch (e) {
       
 12281             $exceptionHandler(e);
       
 12282             throw e;
       
 12283           }
       
 12284         }
       
 12285       },
       
 12286 
       
 12287       /**
       
 12288        * @ngdoc method
       
 12289        * @name $rootScope.Scope#$on
       
 12290        * @function
       
 12291        *
       
 12292        * @description
       
 12293        * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
       
 12294        * discussion of event life cycle.
       
 12295        *
       
 12296        * The event listener function format is: `function(event, args...)`. The `event` object
       
 12297        * passed into the listener has the following attributes:
       
 12298        *
       
 12299        *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
       
 12300        *     `$broadcast`-ed.
       
 12301        *   - `currentScope` - `{Scope}`: the current scope which is handling the event.
       
 12302        *   - `name` - `{string}`: name of the event.
       
 12303        *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
       
 12304        *     further event propagation (available only for events that were `$emit`-ed).
       
 12305        *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
       
 12306        *     to true.
       
 12307        *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
       
 12308        *
       
 12309        * @param {string} name Event name to listen on.
       
 12310        * @param {function(event, ...args)} listener Function to call when the event is emitted.
       
 12311        * @returns {function()} Returns a deregistration function for this listener.
       
 12312        */
       
 12313       $on: function(name, listener) {
       
 12314         var namedListeners = this.$$listeners[name];
       
 12315         if (!namedListeners) {
       
 12316           this.$$listeners[name] = namedListeners = [];
       
 12317         }
       
 12318         namedListeners.push(listener);
       
 12319 
       
 12320         var current = this;
       
 12321         do {
       
 12322           if (!current.$$listenerCount[name]) {
       
 12323             current.$$listenerCount[name] = 0;
       
 12324           }
       
 12325           current.$$listenerCount[name]++;
       
 12326         } while ((current = current.$parent));
       
 12327 
       
 12328         var self = this;
       
 12329         return function() {
       
 12330           namedListeners[indexOf(namedListeners, listener)] = null;
       
 12331           decrementListenerCount(self, 1, name);
       
 12332         };
       
 12333       },
       
 12334 
       
 12335 
       
 12336       /**
       
 12337        * @ngdoc method
       
 12338        * @name $rootScope.Scope#$emit
       
 12339        * @function
       
 12340        *
       
 12341        * @description
       
 12342        * Dispatches an event `name` upwards through the scope hierarchy notifying the
       
 12343        * registered {@link ng.$rootScope.Scope#$on} listeners.
       
 12344        *
       
 12345        * The event life cycle starts at the scope on which `$emit` was called. All
       
 12346        * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
       
 12347        * notified. Afterwards, the event traverses upwards toward the root scope and calls all
       
 12348        * registered listeners along the way. The event will stop propagating if one of the listeners
       
 12349        * cancels it.
       
 12350        *
       
 12351        * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
       
 12352        * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
       
 12353        *
       
 12354        * @param {string} name Event name to emit.
       
 12355        * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
       
 12356        * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
       
 12357        */
       
 12358       $emit: function(name, args) {
       
 12359         var empty = [],
       
 12360             namedListeners,
       
 12361             scope = this,
       
 12362             stopPropagation = false,
       
 12363             event = {
       
 12364               name: name,
       
 12365               targetScope: scope,
       
 12366               stopPropagation: function() {stopPropagation = true;},
       
 12367               preventDefault: function() {
       
 12368                 event.defaultPrevented = true;
       
 12369               },
       
 12370               defaultPrevented: false
       
 12371             },
       
 12372             listenerArgs = concat([event], arguments, 1),
       
 12373             i, length;
       
 12374 
       
 12375         do {
       
 12376           namedListeners = scope.$$listeners[name] || empty;
       
 12377           event.currentScope = scope;
       
 12378           for (i=0, length=namedListeners.length; i<length; i++) {
       
 12379 
       
 12380             // if listeners were deregistered, defragment the array
       
 12381             if (!namedListeners[i]) {
       
 12382               namedListeners.splice(i, 1);
       
 12383               i--;
       
 12384               length--;
       
 12385               continue;
       
 12386             }
       
 12387             try {
       
 12388               //allow all listeners attached to the current scope to run
       
 12389               namedListeners[i].apply(null, listenerArgs);
       
 12390             } catch (e) {
       
 12391               $exceptionHandler(e);
       
 12392             }
       
 12393           }
       
 12394           //if any listener on the current scope stops propagation, prevent bubbling
       
 12395           if (stopPropagation) return event;
       
 12396           //traverse upwards
       
 12397           scope = scope.$parent;
       
 12398         } while (scope);
       
 12399 
       
 12400         return event;
       
 12401       },
       
 12402 
       
 12403 
       
 12404       /**
       
 12405        * @ngdoc method
       
 12406        * @name $rootScope.Scope#$broadcast
       
 12407        * @function
       
 12408        *
       
 12409        * @description
       
 12410        * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
       
 12411        * registered {@link ng.$rootScope.Scope#$on} listeners.
       
 12412        *
       
 12413        * The event life cycle starts at the scope on which `$broadcast` was called. All
       
 12414        * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
       
 12415        * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
       
 12416        * scope and calls all registered listeners along the way. The event cannot be canceled.
       
 12417        *
       
 12418        * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
       
 12419        * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
       
 12420        *
       
 12421        * @param {string} name Event name to broadcast.
       
 12422        * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
       
 12423        * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
       
 12424        */
       
 12425       $broadcast: function(name, args) {
       
 12426         var target = this,
       
 12427             current = target,
       
 12428             next = target,
       
 12429             event = {
       
 12430               name: name,
       
 12431               targetScope: target,
       
 12432               preventDefault: function() {
       
 12433                 event.defaultPrevented = true;
       
 12434               },
       
 12435               defaultPrevented: false
       
 12436             },
       
 12437             listenerArgs = concat([event], arguments, 1),
       
 12438             listeners, i, length;
       
 12439 
       
 12440         //down while you can, then up and next sibling or up and next sibling until back at root
       
 12441         while ((current = next)) {
       
 12442           event.currentScope = current;
       
 12443           listeners = current.$$listeners[name] || [];
       
 12444           for (i=0, length = listeners.length; i<length; i++) {
       
 12445             // if listeners were deregistered, defragment the array
       
 12446             if (!listeners[i]) {
       
 12447               listeners.splice(i, 1);
       
 12448               i--;
       
 12449               length--;
       
 12450               continue;
       
 12451             }
       
 12452 
       
 12453             try {
       
 12454               listeners[i].apply(null, listenerArgs);
       
 12455             } catch(e) {
       
 12456               $exceptionHandler(e);
       
 12457             }
       
 12458           }
       
 12459 
       
 12460           // Insanity Warning: scope depth-first traversal
       
 12461           // yes, this code is a bit crazy, but it works and we have tests to prove it!
       
 12462           // this piece should be kept in sync with the traversal in $digest
       
 12463           // (though it differs due to having the extra check for $$listenerCount)
       
 12464           if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
       
 12465               (current !== target && current.$$nextSibling)))) {
       
 12466             while(current !== target && !(next = current.$$nextSibling)) {
       
 12467               current = current.$parent;
       
 12468             }
       
 12469           }
       
 12470         }
       
 12471 
       
 12472         return event;
       
 12473       }
       
 12474     };
       
 12475 
       
 12476     var $rootScope = new Scope();
       
 12477 
       
 12478     return $rootScope;
       
 12479 
       
 12480 
       
 12481     function beginPhase(phase) {
       
 12482       if ($rootScope.$$phase) {
       
 12483         throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
       
 12484       }
       
 12485 
       
 12486       $rootScope.$$phase = phase;
       
 12487     }
       
 12488 
       
 12489     function clearPhase() {
       
 12490       $rootScope.$$phase = null;
       
 12491     }
       
 12492 
       
 12493     function compileToFn(exp, name) {
       
 12494       var fn = $parse(exp);
       
 12495       assertArgFn(fn, name);
       
 12496       return fn;
       
 12497     }
       
 12498 
       
 12499     function decrementListenerCount(current, count, name) {
       
 12500       do {
       
 12501         current.$$listenerCount[name] -= count;
       
 12502 
       
 12503         if (current.$$listenerCount[name] === 0) {
       
 12504           delete current.$$listenerCount[name];
       
 12505         }
       
 12506       } while ((current = current.$parent));
       
 12507     }
       
 12508 
       
 12509     /**
       
 12510      * function used as an initial value for watchers.
       
 12511      * because it's unique we can easily tell it apart from other values
       
 12512      */
       
 12513     function initWatchVal() {}
       
 12514   }];
       
 12515 }
       
 12516 
       
 12517 /**
       
 12518  * @description
       
 12519  * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
       
 12520  */
       
 12521 function $$SanitizeUriProvider() {
       
 12522   var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
       
 12523     imgSrcSanitizationWhitelist = /^\s*(https?|ftp|file):|data:image\//;
       
 12524 
       
 12525   /**
       
 12526    * @description
       
 12527    * Retrieves or overrides the default regular expression that is used for whitelisting of safe
       
 12528    * urls during a[href] sanitization.
       
 12529    *
       
 12530    * The sanitization is a security measure aimed at prevent XSS attacks via html links.
       
 12531    *
       
 12532    * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
       
 12533    * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
       
 12534    * regular expression. If a match is found, the original url is written into the dom. Otherwise,
       
 12535    * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
       
 12536    *
       
 12537    * @param {RegExp=} regexp New regexp to whitelist urls with.
       
 12538    * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
       
 12539    *    chaining otherwise.
       
 12540    */
       
 12541   this.aHrefSanitizationWhitelist = function(regexp) {
       
 12542     if (isDefined(regexp)) {
       
 12543       aHrefSanitizationWhitelist = regexp;
       
 12544       return this;
       
 12545     }
       
 12546     return aHrefSanitizationWhitelist;
       
 12547   };
       
 12548 
       
 12549 
       
 12550   /**
       
 12551    * @description
       
 12552    * Retrieves or overrides the default regular expression that is used for whitelisting of safe
       
 12553    * urls during img[src] sanitization.
       
 12554    *
       
 12555    * The sanitization is a security measure aimed at prevent XSS attacks via html links.
       
 12556    *
       
 12557    * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
       
 12558    * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
       
 12559    * regular expression. If a match is found, the original url is written into the dom. Otherwise,
       
 12560    * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
       
 12561    *
       
 12562    * @param {RegExp=} regexp New regexp to whitelist urls with.
       
 12563    * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
       
 12564    *    chaining otherwise.
       
 12565    */
       
 12566   this.imgSrcSanitizationWhitelist = function(regexp) {
       
 12567     if (isDefined(regexp)) {
       
 12568       imgSrcSanitizationWhitelist = regexp;
       
 12569       return this;
       
 12570     }
       
 12571     return imgSrcSanitizationWhitelist;
       
 12572   };
       
 12573 
       
 12574   this.$get = function() {
       
 12575     return function sanitizeUri(uri, isImage) {
       
 12576       var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
       
 12577       var normalizedVal;
       
 12578       // NOTE: urlResolve() doesn't support IE < 8 so we don't sanitize for that case.
       
 12579       if (!msie || msie >= 8 ) {
       
 12580         normalizedVal = urlResolve(uri).href;
       
 12581         if (normalizedVal !== '' && !normalizedVal.match(regex)) {
       
 12582           return 'unsafe:'+normalizedVal;
       
 12583         }
       
 12584       }
       
 12585       return uri;
       
 12586     };
       
 12587   };
       
 12588 }
       
 12589 
       
 12590 var $sceMinErr = minErr('$sce');
       
 12591 
       
 12592 var SCE_CONTEXTS = {
       
 12593   HTML: 'html',
       
 12594   CSS: 'css',
       
 12595   URL: 'url',
       
 12596   // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
       
 12597   // url.  (e.g. ng-include, script src, templateUrl)
       
 12598   RESOURCE_URL: 'resourceUrl',
       
 12599   JS: 'js'
       
 12600 };
       
 12601 
       
 12602 // Helper functions follow.
       
 12603 
       
 12604 // Copied from:
       
 12605 // http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962
       
 12606 // Prereq: s is a string.
       
 12607 function escapeForRegexp(s) {
       
 12608   return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
       
 12609            replace(/\x08/g, '\\x08');
       
 12610 }
       
 12611 
       
 12612 
       
 12613 function adjustMatcher(matcher) {
       
 12614   if (matcher === 'self') {
       
 12615     return matcher;
       
 12616   } else if (isString(matcher)) {
       
 12617     // Strings match exactly except for 2 wildcards - '*' and '**'.
       
 12618     // '*' matches any character except those from the set ':/.?&'.
       
 12619     // '**' matches any character (like .* in a RegExp).
       
 12620     // More than 2 *'s raises an error as it's ill defined.
       
 12621     if (matcher.indexOf('***') > -1) {
       
 12622       throw $sceMinErr('iwcard',
       
 12623           'Illegal sequence *** in string matcher.  String: {0}', matcher);
       
 12624     }
       
 12625     matcher = escapeForRegexp(matcher).
       
 12626                   replace('\\*\\*', '.*').
       
 12627                   replace('\\*', '[^:/.?&;]*');
       
 12628     return new RegExp('^' + matcher + '$');
       
 12629   } else if (isRegExp(matcher)) {
       
 12630     // The only other type of matcher allowed is a Regexp.
       
 12631     // Match entire URL / disallow partial matches.
       
 12632     // Flags are reset (i.e. no global, ignoreCase or multiline)
       
 12633     return new RegExp('^' + matcher.source + '$');
       
 12634   } else {
       
 12635     throw $sceMinErr('imatcher',
       
 12636         'Matchers may only be "self", string patterns or RegExp objects');
       
 12637   }
       
 12638 }
       
 12639 
       
 12640 
       
 12641 function adjustMatchers(matchers) {
       
 12642   var adjustedMatchers = [];
       
 12643   if (isDefined(matchers)) {
       
 12644     forEach(matchers, function(matcher) {
       
 12645       adjustedMatchers.push(adjustMatcher(matcher));
       
 12646     });
       
 12647   }
       
 12648   return adjustedMatchers;
       
 12649 }
       
 12650 
       
 12651 
       
 12652 /**
       
 12653  * @ngdoc service
       
 12654  * @name $sceDelegate
       
 12655  * @function
       
 12656  *
       
 12657  * @description
       
 12658  *
       
 12659  * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
       
 12660  * Contextual Escaping (SCE)} services to AngularJS.
       
 12661  *
       
 12662  * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
       
 12663  * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
       
 12664  * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
       
 12665  * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
       
 12666  * work because `$sce` delegates to `$sceDelegate` for these operations.
       
 12667  *
       
 12668  * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
       
 12669  *
       
 12670  * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
       
 12671  * can override it completely to change the behavior of `$sce`, the common case would
       
 12672  * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
       
 12673  * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
       
 12674  * templates.  Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
       
 12675  * $sceDelegateProvider.resourceUrlWhitelist} and {@link
       
 12676  * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
       
 12677  */
       
 12678 
       
 12679 /**
       
 12680  * @ngdoc provider
       
 12681  * @name $sceDelegateProvider
       
 12682  * @description
       
 12683  *
       
 12684  * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
       
 12685  * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure
       
 12686  * that the URLs used for sourcing Angular templates are safe.  Refer {@link
       
 12687  * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
       
 12688  * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
       
 12689  *
       
 12690  * For the general details about this service in Angular, read the main page for {@link ng.$sce
       
 12691  * Strict Contextual Escaping (SCE)}.
       
 12692  *
       
 12693  * **Example**:  Consider the following case. <a name="example"></a>
       
 12694  *
       
 12695  * - your app is hosted at url `http://myapp.example.com/`
       
 12696  * - but some of your templates are hosted on other domains you control such as
       
 12697  *   `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc.
       
 12698  * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
       
 12699  *
       
 12700  * Here is what a secure configuration for this scenario might look like:
       
 12701  *
       
 12702  * <pre class="prettyprint">
       
 12703  *    angular.module('myApp', []).config(function($sceDelegateProvider) {
       
 12704  *      $sceDelegateProvider.resourceUrlWhitelist([
       
 12705  *        // Allow same origin resource loads.
       
 12706  *        'self',
       
 12707  *        // Allow loading from our assets domain.  Notice the difference between * and **.
       
 12708  *        'http://srv*.assets.example.com/**']);
       
 12709  *
       
 12710  *      // The blacklist overrides the whitelist so the open redirect here is blocked.
       
 12711  *      $sceDelegateProvider.resourceUrlBlacklist([
       
 12712  *        'http://myapp.example.com/clickThru**']);
       
 12713  *      });
       
 12714  * </pre>
       
 12715  */
       
 12716 
       
 12717 function $SceDelegateProvider() {
       
 12718   this.SCE_CONTEXTS = SCE_CONTEXTS;
       
 12719 
       
 12720   // Resource URLs can also be trusted by policy.
       
 12721   var resourceUrlWhitelist = ['self'],
       
 12722       resourceUrlBlacklist = [];
       
 12723 
       
 12724   /**
       
 12725    * @ngdoc method
       
 12726    * @name $sceDelegateProvider#resourceUrlWhitelist
       
 12727    * @function
       
 12728    *
       
 12729    * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
       
 12730    *     provided.  This must be an array or null.  A snapshot of this array is used so further
       
 12731    *     changes to the array are ignored.
       
 12732    *
       
 12733    *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
       
 12734    *     allowed in this array.
       
 12735    *
       
 12736    *     Note: **an empty whitelist array will block all URLs**!
       
 12737    *
       
 12738    * @return {Array} the currently set whitelist array.
       
 12739    *
       
 12740    * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
       
 12741    * same origin resource requests.
       
 12742    *
       
 12743    * @description
       
 12744    * Sets/Gets the whitelist of trusted resource URLs.
       
 12745    */
       
 12746   this.resourceUrlWhitelist = function (value) {
       
 12747     if (arguments.length) {
       
 12748       resourceUrlWhitelist = adjustMatchers(value);
       
 12749     }
       
 12750     return resourceUrlWhitelist;
       
 12751   };
       
 12752 
       
 12753   /**
       
 12754    * @ngdoc method
       
 12755    * @name $sceDelegateProvider#resourceUrlBlacklist
       
 12756    * @function
       
 12757    *
       
 12758    * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
       
 12759    *     provided.  This must be an array or null.  A snapshot of this array is used so further
       
 12760    *     changes to the array are ignored.
       
 12761    *
       
 12762    *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
       
 12763    *     allowed in this array.
       
 12764    *
       
 12765    *     The typical usage for the blacklist is to **block
       
 12766    *     [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
       
 12767    *     these would otherwise be trusted but actually return content from the redirected domain.
       
 12768    *
       
 12769    *     Finally, **the blacklist overrides the whitelist** and has the final say.
       
 12770    *
       
 12771    * @return {Array} the currently set blacklist array.
       
 12772    *
       
 12773    * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
       
 12774    * is no blacklist.)
       
 12775    *
       
 12776    * @description
       
 12777    * Sets/Gets the blacklist of trusted resource URLs.
       
 12778    */
       
 12779 
       
 12780   this.resourceUrlBlacklist = function (value) {
       
 12781     if (arguments.length) {
       
 12782       resourceUrlBlacklist = adjustMatchers(value);
       
 12783     }
       
 12784     return resourceUrlBlacklist;
       
 12785   };
       
 12786 
       
 12787   this.$get = ['$injector', function($injector) {
       
 12788 
       
 12789     var htmlSanitizer = function htmlSanitizer(html) {
       
 12790       throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
       
 12791     };
       
 12792 
       
 12793     if ($injector.has('$sanitize')) {
       
 12794       htmlSanitizer = $injector.get('$sanitize');
       
 12795     }
       
 12796 
       
 12797 
       
 12798     function matchUrl(matcher, parsedUrl) {
       
 12799       if (matcher === 'self') {
       
 12800         return urlIsSameOrigin(parsedUrl);
       
 12801       } else {
       
 12802         // definitely a regex.  See adjustMatchers()
       
 12803         return !!matcher.exec(parsedUrl.href);
       
 12804       }
       
 12805     }
       
 12806 
       
 12807     function isResourceUrlAllowedByPolicy(url) {
       
 12808       var parsedUrl = urlResolve(url.toString());
       
 12809       var i, n, allowed = false;
       
 12810       // Ensure that at least one item from the whitelist allows this url.
       
 12811       for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
       
 12812         if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
       
 12813           allowed = true;
       
 12814           break;
       
 12815         }
       
 12816       }
       
 12817       if (allowed) {
       
 12818         // Ensure that no item from the blacklist blocked this url.
       
 12819         for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
       
 12820           if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
       
 12821             allowed = false;
       
 12822             break;
       
 12823           }
       
 12824         }
       
 12825       }
       
 12826       return allowed;
       
 12827     }
       
 12828 
       
 12829     function generateHolderType(Base) {
       
 12830       var holderType = function TrustedValueHolderType(trustedValue) {
       
 12831         this.$$unwrapTrustedValue = function() {
       
 12832           return trustedValue;
       
 12833         };
       
 12834       };
       
 12835       if (Base) {
       
 12836         holderType.prototype = new Base();
       
 12837       }
       
 12838       holderType.prototype.valueOf = function sceValueOf() {
       
 12839         return this.$$unwrapTrustedValue();
       
 12840       };
       
 12841       holderType.prototype.toString = function sceToString() {
       
 12842         return this.$$unwrapTrustedValue().toString();
       
 12843       };
       
 12844       return holderType;
       
 12845     }
       
 12846 
       
 12847     var trustedValueHolderBase = generateHolderType(),
       
 12848         byType = {};
       
 12849 
       
 12850     byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
       
 12851     byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
       
 12852     byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
       
 12853     byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
       
 12854     byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
       
 12855 
       
 12856     /**
       
 12857      * @ngdoc method
       
 12858      * @name $sceDelegate#trustAs
       
 12859      *
       
 12860      * @description
       
 12861      * Returns an object that is trusted by angular for use in specified strict
       
 12862      * contextual escaping contexts (such as ng-bind-html, ng-include, any src
       
 12863      * attribute interpolation, any dom event binding attribute interpolation
       
 12864      * such as for onclick,  etc.) that uses the provided value.
       
 12865      * See {@link ng.$sce $sce} for enabling strict contextual escaping.
       
 12866      *
       
 12867      * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
       
 12868      *   resourceUrl, html, js and css.
       
 12869      * @param {*} value The value that that should be considered trusted/safe.
       
 12870      * @returns {*} A value that can be used to stand in for the provided `value` in places
       
 12871      * where Angular expects a $sce.trustAs() return value.
       
 12872      */
       
 12873     function trustAs(type, trustedValue) {
       
 12874       var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
       
 12875       if (!Constructor) {
       
 12876         throw $sceMinErr('icontext',
       
 12877             'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
       
 12878             type, trustedValue);
       
 12879       }
       
 12880       if (trustedValue === null || trustedValue === undefined || trustedValue === '') {
       
 12881         return trustedValue;
       
 12882       }
       
 12883       // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
       
 12884       // mutable objects, we ensure here that the value passed in is actually a string.
       
 12885       if (typeof trustedValue !== 'string') {
       
 12886         throw $sceMinErr('itype',
       
 12887             'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
       
 12888             type);
       
 12889       }
       
 12890       return new Constructor(trustedValue);
       
 12891     }
       
 12892 
       
 12893     /**
       
 12894      * @ngdoc method
       
 12895      * @name $sceDelegate#valueOf
       
 12896      *
       
 12897      * @description
       
 12898      * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
       
 12899      * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
       
 12900      * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
       
 12901      *
       
 12902      * If the passed parameter is not a value that had been returned by {@link
       
 12903      * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
       
 12904      *
       
 12905      * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
       
 12906      *      call or anything else.
       
 12907      * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
       
 12908      *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
       
 12909      *     `value` unchanged.
       
 12910      */
       
 12911     function valueOf(maybeTrusted) {
       
 12912       if (maybeTrusted instanceof trustedValueHolderBase) {
       
 12913         return maybeTrusted.$$unwrapTrustedValue();
       
 12914       } else {
       
 12915         return maybeTrusted;
       
 12916       }
       
 12917     }
       
 12918 
       
 12919     /**
       
 12920      * @ngdoc method
       
 12921      * @name $sceDelegate#getTrusted
       
 12922      *
       
 12923      * @description
       
 12924      * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
       
 12925      * returns the originally supplied value if the queried context type is a supertype of the
       
 12926      * created type.  If this condition isn't satisfied, throws an exception.
       
 12927      *
       
 12928      * @param {string} type The kind of context in which this value is to be used.
       
 12929      * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
       
 12930      *     `$sceDelegate.trustAs`} call.
       
 12931      * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
       
 12932      *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.
       
 12933      */
       
 12934     function getTrusted(type, maybeTrusted) {
       
 12935       if (maybeTrusted === null || maybeTrusted === undefined || maybeTrusted === '') {
       
 12936         return maybeTrusted;
       
 12937       }
       
 12938       var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
       
 12939       if (constructor && maybeTrusted instanceof constructor) {
       
 12940         return maybeTrusted.$$unwrapTrustedValue();
       
 12941       }
       
 12942       // If we get here, then we may only take one of two actions.
       
 12943       // 1. sanitize the value for the requested type, or
       
 12944       // 2. throw an exception.
       
 12945       if (type === SCE_CONTEXTS.RESOURCE_URL) {
       
 12946         if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
       
 12947           return maybeTrusted;
       
 12948         } else {
       
 12949           throw $sceMinErr('insecurl',
       
 12950               'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
       
 12951               maybeTrusted.toString());
       
 12952         }
       
 12953       } else if (type === SCE_CONTEXTS.HTML) {
       
 12954         return htmlSanitizer(maybeTrusted);
       
 12955       }
       
 12956       throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
       
 12957     }
       
 12958 
       
 12959     return { trustAs: trustAs,
       
 12960              getTrusted: getTrusted,
       
 12961              valueOf: valueOf };
       
 12962   }];
       
 12963 }
       
 12964 
       
 12965 
       
 12966 /**
       
 12967  * @ngdoc provider
       
 12968  * @name $sceProvider
       
 12969  * @description
       
 12970  *
       
 12971  * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
       
 12972  * -   enable/disable Strict Contextual Escaping (SCE) in a module
       
 12973  * -   override the default implementation with a custom delegate
       
 12974  *
       
 12975  * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
       
 12976  */
       
 12977 
       
 12978 /* jshint maxlen: false*/
       
 12979 
       
 12980 /**
       
 12981  * @ngdoc service
       
 12982  * @name $sce
       
 12983  * @function
       
 12984  *
       
 12985  * @description
       
 12986  *
       
 12987  * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
       
 12988  *
       
 12989  * # Strict Contextual Escaping
       
 12990  *
       
 12991  * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
       
 12992  * contexts to result in a value that is marked as safe to use for that context.  One example of
       
 12993  * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer
       
 12994  * to these contexts as privileged or SCE contexts.
       
 12995  *
       
 12996  * As of version 1.2, Angular ships with SCE enabled by default.
       
 12997  *
       
 12998  * Note:  When enabled (the default), IE8 in quirks mode is not supported.  In this mode, IE8 allows
       
 12999  * one to execute arbitrary javascript by the use of the expression() syntax.  Refer
       
 13000  * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
       
 13001  * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
       
 13002  * to the top of your HTML document.
       
 13003  *
       
 13004  * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
       
 13005  * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
       
 13006  *
       
 13007  * Here's an example of a binding in a privileged context:
       
 13008  *
       
 13009  * <pre class="prettyprint">
       
 13010  *     <input ng-model="userHtml">
       
 13011  *     <div ng-bind-html="userHtml">
       
 13012  * </pre>
       
 13013  *
       
 13014  * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
       
 13015  * disabled, this application allows the user to render arbitrary HTML into the DIV.
       
 13016  * In a more realistic example, one may be rendering user comments, blog articles, etc. via
       
 13017  * bindings.  (HTML is just one example of a context where rendering user controlled input creates
       
 13018  * security vulnerabilities.)
       
 13019  *
       
 13020  * For the case of HTML, you might use a library, either on the client side, or on the server side,
       
 13021  * to sanitize unsafe HTML before binding to the value and rendering it in the document.
       
 13022  *
       
 13023  * How would you ensure that every place that used these types of bindings was bound to a value that
       
 13024  * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
       
 13025  * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
       
 13026  * properties/fields and forgot to update the binding to the sanitized value?
       
 13027  *
       
 13028  * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
       
 13029  * determine that something explicitly says it's safe to use a value for binding in that
       
 13030  * context.  You can then audit your code (a simple grep would do) to ensure that this is only done
       
 13031  * for those values that you can easily tell are safe - because they were received from your server,
       
 13032  * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps
       
 13033  * allowing only the files in a specific directory to do this.  Ensuring that the internal API
       
 13034  * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
       
 13035  *
       
 13036  * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
       
 13037  * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
       
 13038  * obtain values that will be accepted by SCE / privileged contexts.
       
 13039  *
       
 13040  *
       
 13041  * ## How does it work?
       
 13042  *
       
 13043  * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
       
 13044  * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link
       
 13045  * ng.$sce#parse $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
       
 13046  * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
       
 13047  *
       
 13048  * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
       
 13049  * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
       
 13050  * simplified):
       
 13051  *
       
 13052  * <pre class="prettyprint">
       
 13053  *   var ngBindHtmlDirective = ['$sce', function($sce) {
       
 13054  *     return function(scope, element, attr) {
       
 13055  *       scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
       
 13056  *         element.html(value || '');
       
 13057  *       });
       
 13058  *     };
       
 13059  *   }];
       
 13060  * </pre>
       
 13061  *
       
 13062  * ## Impact on loading templates
       
 13063  *
       
 13064  * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
       
 13065  * `templateUrl`'s specified by {@link guide/directive directives}.
       
 13066  *
       
 13067  * By default, Angular only loads templates from the same domain and protocol as the application
       
 13068  * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl
       
 13069  * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
       
 13070  * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
       
 13071  * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
       
 13072  *
       
 13073  * *Please note*:
       
 13074  * The browser's
       
 13075  * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
       
 13076  * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
       
 13077  * policy apply in addition to this and may further restrict whether the template is successfully
       
 13078  * loaded.  This means that without the right CORS policy, loading templates from a different domain
       
 13079  * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
       
 13080  * browsers.
       
 13081  *
       
 13082  * ## This feels like too much overhead for the developer?
       
 13083  *
       
 13084  * It's important to remember that SCE only applies to interpolation expressions.
       
 13085  *
       
 13086  * If your expressions are constant literals, they're automatically trusted and you don't need to
       
 13087  * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
       
 13088  * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
       
 13089  *
       
 13090  * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
       
 13091  * through {@link ng.$sce#getTrusted $sce.getTrusted}.  SCE doesn't play a role here.
       
 13092  *
       
 13093  * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
       
 13094  * templates in `ng-include` from your application's domain without having to even know about SCE.
       
 13095  * It blocks loading templates from other domains or loading templates over http from an https
       
 13096  * served document.  You can change these by setting your own custom {@link
       
 13097  * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
       
 13098  * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
       
 13099  *
       
 13100  * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
       
 13101  * application that's secure and can be audited to verify that with much more ease than bolting
       
 13102  * security onto an application later.
       
 13103  *
       
 13104  * <a name="contexts"></a>
       
 13105  * ## What trusted context types are supported?
       
 13106  *
       
 13107  * | Context             | Notes          |
       
 13108  * |---------------------|----------------|
       
 13109  * | `$sce.HTML`         | For HTML that's safe to source into the application.  The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. |
       
 13110  * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
       
 13111  * | `$sce.URL`          | For URLs that are safe to follow as links.  Currently unused (`<a href=` and `<img src=` sanitize their urls and don't constitute an SCE context. |
       
 13112  * | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contens are also safe to include in your application.  Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.)  <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
       
 13113  * | `$sce.JS`           | For JavaScript that is safe to execute in your application's context.  Currently unused.  Feel free to use it in your own directives. |
       
 13114  *
       
 13115  * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
       
 13116  *
       
 13117  *  Each element in these arrays must be one of the following:
       
 13118  *
       
 13119  *  - **'self'**
       
 13120  *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
       
 13121  *      domain** as the application document using the **same protocol**.
       
 13122  *  - **String** (except the special value `'self'`)
       
 13123  *    - The string is matched against the full *normalized / absolute URL* of the resource
       
 13124  *      being tested (substring matches are not good enough.)
       
 13125  *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
       
 13126  *      match themselves.
       
 13127  *    - `*`: matches zero or more occurrences of any character other than one of the following 6
       
 13128  *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and ';'.  It's a useful wildcard for use
       
 13129  *      in a whitelist.
       
 13130  *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not
       
 13131  *      not appropriate to use in for a scheme, domain, etc. as it would match too much.  (e.g.
       
 13132  *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
       
 13133  *      not have been the intention.)  It's usage at the very end of the path is ok.  (e.g.
       
 13134  *      http://foo.example.com/templates/**).
       
 13135  *  - **RegExp** (*see caveat below*)
       
 13136  *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
       
 13137  *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
       
 13138  *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
       
 13139  *      have good test coverage.).  For instance, the use of `.` in the regex is correct only in a
       
 13140  *      small number of cases.  A `.` character in the regex used when matching the scheme or a
       
 13141  *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
       
 13142  *      is highly recommended to use the string patterns and only fall back to regular expressions
       
 13143  *      if they as a last resort.
       
 13144  *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
       
 13145  *      matched against the **entire** *normalized / absolute URL* of the resource being tested
       
 13146  *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
       
 13147  *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
       
 13148  *    - If you are generating your JavaScript from some other templating engine (not
       
 13149  *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
       
 13150  *      remember to escape your regular expression (and be aware that you might need more than
       
 13151  *      one level of escaping depending on your templating engine and the way you interpolated
       
 13152  *      the value.)  Do make use of your platform's escaping mechanism as it might be good
       
 13153  *      enough before coding your own.  e.g. Ruby has
       
 13154  *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
       
 13155  *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
       
 13156  *      Javascript lacks a similar built in function for escaping.  Take a look at Google
       
 13157  *      Closure library's [goog.string.regExpEscape(s)](
       
 13158  *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
       
 13159  *
       
 13160  * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
       
 13161  *
       
 13162  * ## Show me an example using SCE.
       
 13163  *
       
 13164  * @example
       
 13165 <example module="mySceApp" deps="angular-sanitize.js">
       
 13166 <file name="index.html">
       
 13167   <div ng-controller="myAppController as myCtrl">
       
 13168     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
       
 13169     <b>User comments</b><br>
       
 13170     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
       
 13171     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
       
 13172     exploit.
       
 13173     <div class="well">
       
 13174       <div ng-repeat="userComment in myCtrl.userComments">
       
 13175         <b>{{userComment.name}}</b>:
       
 13176         <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
       
 13177         <br>
       
 13178       </div>
       
 13179     </div>
       
 13180   </div>
       
 13181 </file>
       
 13182 
       
 13183 <file name="script.js">
       
 13184   var mySceApp = angular.module('mySceApp', ['ngSanitize']);
       
 13185 
       
 13186   mySceApp.controller("myAppController", function myAppController($http, $templateCache, $sce) {
       
 13187     var self = this;
       
 13188     $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
       
 13189       self.userComments = userComments;
       
 13190     });
       
 13191     self.explicitlyTrustedHtml = $sce.trustAsHtml(
       
 13192         '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
       
 13193         'sanitization.&quot;">Hover over this text.</span>');
       
 13194   });
       
 13195 </file>
       
 13196 
       
 13197 <file name="test_data.json">
       
 13198 [
       
 13199   { "name": "Alice",
       
 13200     "htmlComment":
       
 13201         "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
       
 13202   },
       
 13203   { "name": "Bob",
       
 13204     "htmlComment": "<i>Yes!</i>  Am I the only other one?"
       
 13205   }
       
 13206 ]
       
 13207 </file>
       
 13208 
       
 13209 <file name="protractor.js" type="protractor">
       
 13210   describe('SCE doc demo', function() {
       
 13211     it('should sanitize untrusted values', function() {
       
 13212       expect(element(by.css('.htmlComment')).getInnerHtml())
       
 13213           .toBe('<span>Is <i>anyone</i> reading this?</span>');
       
 13214     });
       
 13215 
       
 13216     it('should NOT sanitize explicitly trusted values', function() {
       
 13217       expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
       
 13218           '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
       
 13219           'sanitization.&quot;">Hover over this text.</span>');
       
 13220     });
       
 13221   });
       
 13222 </file>
       
 13223 </example>
       
 13224  *
       
 13225  *
       
 13226  *
       
 13227  * ## Can I disable SCE completely?
       
 13228  *
       
 13229  * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
       
 13230  * for little coding overhead.  It will be much harder to take an SCE disabled application and
       
 13231  * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
       
 13232  * for cases where you have a lot of existing code that was written before SCE was introduced and
       
 13233  * you're migrating them a module at a time.
       
 13234  *
       
 13235  * That said, here's how you can completely disable SCE:
       
 13236  *
       
 13237  * <pre class="prettyprint">
       
 13238  *   angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
       
 13239  *     // Completely disable SCE.  For demonstration purposes only!
       
 13240  *     // Do not use in new projects.
       
 13241  *     $sceProvider.enabled(false);
       
 13242  *   });
       
 13243  * </pre>
       
 13244  *
       
 13245  */
       
 13246 /* jshint maxlen: 100 */
       
 13247 
       
 13248 function $SceProvider() {
       
 13249   var enabled = true;
       
 13250 
       
 13251   /**
       
 13252    * @ngdoc method
       
 13253    * @name $sceProvider#enabled
       
 13254    * @function
       
 13255    *
       
 13256    * @param {boolean=} value If provided, then enables/disables SCE.
       
 13257    * @return {boolean} true if SCE is enabled, false otherwise.
       
 13258    *
       
 13259    * @description
       
 13260    * Enables/disables SCE and returns the current value.
       
 13261    */
       
 13262   this.enabled = function (value) {
       
 13263     if (arguments.length) {
       
 13264       enabled = !!value;
       
 13265     }
       
 13266     return enabled;
       
 13267   };
       
 13268 
       
 13269 
       
 13270   /* Design notes on the default implementation for SCE.
       
 13271    *
       
 13272    * The API contract for the SCE delegate
       
 13273    * -------------------------------------
       
 13274    * The SCE delegate object must provide the following 3 methods:
       
 13275    *
       
 13276    * - trustAs(contextEnum, value)
       
 13277    *     This method is used to tell the SCE service that the provided value is OK to use in the
       
 13278    *     contexts specified by contextEnum.  It must return an object that will be accepted by
       
 13279    *     getTrusted() for a compatible contextEnum and return this value.
       
 13280    *
       
 13281    * - valueOf(value)
       
 13282    *     For values that were not produced by trustAs(), return them as is.  For values that were
       
 13283    *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
       
 13284    *     trustAs is wrapping the given values into some type, this operation unwraps it when given
       
 13285    *     such a value.
       
 13286    *
       
 13287    * - getTrusted(contextEnum, value)
       
 13288    *     This function should return the a value that is safe to use in the context specified by
       
 13289    *     contextEnum or throw and exception otherwise.
       
 13290    *
       
 13291    * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
       
 13292    * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
       
 13293    * instance, an implementation could maintain a registry of all trusted objects by context.  In
       
 13294    * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
       
 13295    * return the same object passed in if it was found in the registry under a compatible context or
       
 13296    * throw an exception otherwise.  An implementation might only wrap values some of the time based
       
 13297    * on some criteria.  getTrusted() might return a value and not throw an exception for special
       
 13298    * constants or objects even if not wrapped.  All such implementations fulfill this contract.
       
 13299    *
       
 13300    *
       
 13301    * A note on the inheritance model for SCE contexts
       
 13302    * ------------------------------------------------
       
 13303    * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
       
 13304    * is purely an implementation details.
       
 13305    *
       
 13306    * The contract is simply this:
       
 13307    *
       
 13308    *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
       
 13309    *     will also succeed.
       
 13310    *
       
 13311    * Inheritance happens to capture this in a natural way.  In some future, we
       
 13312    * may not use inheritance anymore.  That is OK because no code outside of
       
 13313    * sce.js and sceSpecs.js would need to be aware of this detail.
       
 13314    */
       
 13315 
       
 13316   this.$get = ['$parse', '$sniffer', '$sceDelegate', function(
       
 13317                 $parse,   $sniffer,   $sceDelegate) {
       
 13318     // Prereq: Ensure that we're not running in IE8 quirks mode.  In that mode, IE allows
       
 13319     // the "expression(javascript expression)" syntax which is insecure.
       
 13320     if (enabled && $sniffer.msie && $sniffer.msieDocumentMode < 8) {
       
 13321       throw $sceMinErr('iequirks',
       
 13322         'Strict Contextual Escaping does not support Internet Explorer version < 9 in quirks ' +
       
 13323         'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
       
 13324         'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
       
 13325     }
       
 13326 
       
 13327     var sce = copy(SCE_CONTEXTS);
       
 13328 
       
 13329     /**
       
 13330      * @ngdoc method
       
 13331      * @name $sce#isEnabled
       
 13332      * @function
       
 13333      *
       
 13334      * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you
       
 13335      * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
       
 13336      *
       
 13337      * @description
       
 13338      * Returns a boolean indicating if SCE is enabled.
       
 13339      */
       
 13340     sce.isEnabled = function () {
       
 13341       return enabled;
       
 13342     };
       
 13343     sce.trustAs = $sceDelegate.trustAs;
       
 13344     sce.getTrusted = $sceDelegate.getTrusted;
       
 13345     sce.valueOf = $sceDelegate.valueOf;
       
 13346 
       
 13347     if (!enabled) {
       
 13348       sce.trustAs = sce.getTrusted = function(type, value) { return value; };
       
 13349       sce.valueOf = identity;
       
 13350     }
       
 13351 
       
 13352     /**
       
 13353      * @ngdoc method
       
 13354      * @name $sce#parse
       
 13355      *
       
 13356      * @description
       
 13357      * Converts Angular {@link guide/expression expression} into a function.  This is like {@link
       
 13358      * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
       
 13359      * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
       
 13360      * *result*)}
       
 13361      *
       
 13362      * @param {string} type The kind of SCE context in which this result will be used.
       
 13363      * @param {string} expression String expression to compile.
       
 13364      * @returns {function(context, locals)} a function which represents the compiled expression:
       
 13365      *
       
 13366      *    * `context` – `{object}` – an object against which any expressions embedded in the strings
       
 13367      *      are evaluated against (typically a scope object).
       
 13368      *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
       
 13369      *      `context`.
       
 13370      */
       
 13371     sce.parseAs = function sceParseAs(type, expr) {
       
 13372       var parsed = $parse(expr);
       
 13373       if (parsed.literal && parsed.constant) {
       
 13374         return parsed;
       
 13375       } else {
       
 13376         return function sceParseAsTrusted(self, locals) {
       
 13377           return sce.getTrusted(type, parsed(self, locals));
       
 13378         };
       
 13379       }
       
 13380     };
       
 13381 
       
 13382     /**
       
 13383      * @ngdoc method
       
 13384      * @name $sce#trustAs
       
 13385      *
       
 13386      * @description
       
 13387      * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.  As such,
       
 13388      * returns an object that is trusted by angular for use in specified strict contextual
       
 13389      * escaping contexts (such as ng-bind-html, ng-include, any src attribute
       
 13390      * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)
       
 13391      * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual
       
 13392      * escaping.
       
 13393      *
       
 13394      * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
       
 13395      *   resource_url, html, js and css.
       
 13396      * @param {*} value The value that that should be considered trusted/safe.
       
 13397      * @returns {*} A value that can be used to stand in for the provided `value` in places
       
 13398      * where Angular expects a $sce.trustAs() return value.
       
 13399      */
       
 13400 
       
 13401     /**
       
 13402      * @ngdoc method
       
 13403      * @name $sce#trustAsHtml
       
 13404      *
       
 13405      * @description
       
 13406      * Shorthand method.  `$sce.trustAsHtml(value)` →
       
 13407      *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
       
 13408      *
       
 13409      * @param {*} value The value to trustAs.
       
 13410      * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
       
 13411      *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives
       
 13412      *     only accept expressions that are either literal constants or are the
       
 13413      *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
       
 13414      */
       
 13415 
       
 13416     /**
       
 13417      * @ngdoc method
       
 13418      * @name $sce#trustAsUrl
       
 13419      *
       
 13420      * @description
       
 13421      * Shorthand method.  `$sce.trustAsUrl(value)` →
       
 13422      *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
       
 13423      *
       
 13424      * @param {*} value The value to trustAs.
       
 13425      * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
       
 13426      *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives
       
 13427      *     only accept expressions that are either literal constants or are the
       
 13428      *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
       
 13429      */
       
 13430 
       
 13431     /**
       
 13432      * @ngdoc method
       
 13433      * @name $sce#trustAsResourceUrl
       
 13434      *
       
 13435      * @description
       
 13436      * Shorthand method.  `$sce.trustAsResourceUrl(value)` →
       
 13437      *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
       
 13438      *
       
 13439      * @param {*} value The value to trustAs.
       
 13440      * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
       
 13441      *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives
       
 13442      *     only accept expressions that are either literal constants or are the return
       
 13443      *     value of {@link ng.$sce#trustAs $sce.trustAs}.)
       
 13444      */
       
 13445 
       
 13446     /**
       
 13447      * @ngdoc method
       
 13448      * @name $sce#trustAsJs
       
 13449      *
       
 13450      * @description
       
 13451      * Shorthand method.  `$sce.trustAsJs(value)` →
       
 13452      *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
       
 13453      *
       
 13454      * @param {*} value The value to trustAs.
       
 13455      * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
       
 13456      *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives
       
 13457      *     only accept expressions that are either literal constants or are the
       
 13458      *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
       
 13459      */
       
 13460 
       
 13461     /**
       
 13462      * @ngdoc method
       
 13463      * @name $sce#getTrusted
       
 13464      *
       
 13465      * @description
       
 13466      * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,
       
 13467      * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
       
 13468      * originally supplied value if the queried context type is a supertype of the created type.
       
 13469      * If this condition isn't satisfied, throws an exception.
       
 13470      *
       
 13471      * @param {string} type The kind of context in which this value is to be used.
       
 13472      * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
       
 13473      *                         call.
       
 13474      * @returns {*} The value the was originally provided to
       
 13475      *              {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
       
 13476      *              Otherwise, throws an exception.
       
 13477      */
       
 13478 
       
 13479     /**
       
 13480      * @ngdoc method
       
 13481      * @name $sce#getTrustedHtml
       
 13482      *
       
 13483      * @description
       
 13484      * Shorthand method.  `$sce.getTrustedHtml(value)` →
       
 13485      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
       
 13486      *
       
 13487      * @param {*} value The value to pass to `$sce.getTrusted`.
       
 13488      * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
       
 13489      */
       
 13490 
       
 13491     /**
       
 13492      * @ngdoc method
       
 13493      * @name $sce#getTrustedCss
       
 13494      *
       
 13495      * @description
       
 13496      * Shorthand method.  `$sce.getTrustedCss(value)` →
       
 13497      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
       
 13498      *
       
 13499      * @param {*} value The value to pass to `$sce.getTrusted`.
       
 13500      * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
       
 13501      */
       
 13502 
       
 13503     /**
       
 13504      * @ngdoc method
       
 13505      * @name $sce#getTrustedUrl
       
 13506      *
       
 13507      * @description
       
 13508      * Shorthand method.  `$sce.getTrustedUrl(value)` →
       
 13509      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
       
 13510      *
       
 13511      * @param {*} value The value to pass to `$sce.getTrusted`.
       
 13512      * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
       
 13513      */
       
 13514 
       
 13515     /**
       
 13516      * @ngdoc method
       
 13517      * @name $sce#getTrustedResourceUrl
       
 13518      *
       
 13519      * @description
       
 13520      * Shorthand method.  `$sce.getTrustedResourceUrl(value)` →
       
 13521      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
       
 13522      *
       
 13523      * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
       
 13524      * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
       
 13525      */
       
 13526 
       
 13527     /**
       
 13528      * @ngdoc method
       
 13529      * @name $sce#getTrustedJs
       
 13530      *
       
 13531      * @description
       
 13532      * Shorthand method.  `$sce.getTrustedJs(value)` →
       
 13533      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
       
 13534      *
       
 13535      * @param {*} value The value to pass to `$sce.getTrusted`.
       
 13536      * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
       
 13537      */
       
 13538 
       
 13539     /**
       
 13540      * @ngdoc method
       
 13541      * @name $sce#parseAsHtml
       
 13542      *
       
 13543      * @description
       
 13544      * Shorthand method.  `$sce.parseAsHtml(expression string)` →
       
 13545      *     {@link ng.$sce#parse `$sce.parseAs($sce.HTML, value)`}
       
 13546      *
       
 13547      * @param {string} expression String expression to compile.
       
 13548      * @returns {function(context, locals)} a function which represents the compiled expression:
       
 13549      *
       
 13550      *    * `context` – `{object}` – an object against which any expressions embedded in the strings
       
 13551      *      are evaluated against (typically a scope object).
       
 13552      *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
       
 13553      *      `context`.
       
 13554      */
       
 13555 
       
 13556     /**
       
 13557      * @ngdoc method
       
 13558      * @name $sce#parseAsCss
       
 13559      *
       
 13560      * @description
       
 13561      * Shorthand method.  `$sce.parseAsCss(value)` →
       
 13562      *     {@link ng.$sce#parse `$sce.parseAs($sce.CSS, value)`}
       
 13563      *
       
 13564      * @param {string} expression String expression to compile.
       
 13565      * @returns {function(context, locals)} a function which represents the compiled expression:
       
 13566      *
       
 13567      *    * `context` – `{object}` – an object against which any expressions embedded in the strings
       
 13568      *      are evaluated against (typically a scope object).
       
 13569      *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
       
 13570      *      `context`.
       
 13571      */
       
 13572 
       
 13573     /**
       
 13574      * @ngdoc method
       
 13575      * @name $sce#parseAsUrl
       
 13576      *
       
 13577      * @description
       
 13578      * Shorthand method.  `$sce.parseAsUrl(value)` →
       
 13579      *     {@link ng.$sce#parse `$sce.parseAs($sce.URL, value)`}
       
 13580      *
       
 13581      * @param {string} expression String expression to compile.
       
 13582      * @returns {function(context, locals)} a function which represents the compiled expression:
       
 13583      *
       
 13584      *    * `context` – `{object}` – an object against which any expressions embedded in the strings
       
 13585      *      are evaluated against (typically a scope object).
       
 13586      *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
       
 13587      *      `context`.
       
 13588      */
       
 13589 
       
 13590     /**
       
 13591      * @ngdoc method
       
 13592      * @name $sce#parseAsResourceUrl
       
 13593      *
       
 13594      * @description
       
 13595      * Shorthand method.  `$sce.parseAsResourceUrl(value)` →
       
 13596      *     {@link ng.$sce#parse `$sce.parseAs($sce.RESOURCE_URL, value)`}
       
 13597      *
       
 13598      * @param {string} expression String expression to compile.
       
 13599      * @returns {function(context, locals)} a function which represents the compiled expression:
       
 13600      *
       
 13601      *    * `context` – `{object}` – an object against which any expressions embedded in the strings
       
 13602      *      are evaluated against (typically a scope object).
       
 13603      *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
       
 13604      *      `context`.
       
 13605      */
       
 13606 
       
 13607     /**
       
 13608      * @ngdoc method
       
 13609      * @name $sce#parseAsJs
       
 13610      *
       
 13611      * @description
       
 13612      * Shorthand method.  `$sce.parseAsJs(value)` →
       
 13613      *     {@link ng.$sce#parse `$sce.parseAs($sce.JS, value)`}
       
 13614      *
       
 13615      * @param {string} expression String expression to compile.
       
 13616      * @returns {function(context, locals)} a function which represents the compiled expression:
       
 13617      *
       
 13618      *    * `context` – `{object}` – an object against which any expressions embedded in the strings
       
 13619      *      are evaluated against (typically a scope object).
       
 13620      *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
       
 13621      *      `context`.
       
 13622      */
       
 13623 
       
 13624     // Shorthand delegations.
       
 13625     var parse = sce.parseAs,
       
 13626         getTrusted = sce.getTrusted,
       
 13627         trustAs = sce.trustAs;
       
 13628 
       
 13629     forEach(SCE_CONTEXTS, function (enumValue, name) {
       
 13630       var lName = lowercase(name);
       
 13631       sce[camelCase("parse_as_" + lName)] = function (expr) {
       
 13632         return parse(enumValue, expr);
       
 13633       };
       
 13634       sce[camelCase("get_trusted_" + lName)] = function (value) {
       
 13635         return getTrusted(enumValue, value);
       
 13636       };
       
 13637       sce[camelCase("trust_as_" + lName)] = function (value) {
       
 13638         return trustAs(enumValue, value);
       
 13639       };
       
 13640     });
       
 13641 
       
 13642     return sce;
       
 13643   }];
       
 13644 }
       
 13645 
       
 13646 /**
       
 13647  * !!! This is an undocumented "private" service !!!
       
 13648  *
       
 13649  * @name $sniffer
       
 13650  * @requires $window
       
 13651  * @requires $document
       
 13652  *
       
 13653  * @property {boolean} history Does the browser support html5 history api ?
       
 13654  * @property {boolean} hashchange Does the browser support hashchange event ?
       
 13655  * @property {boolean} transitions Does the browser support CSS transition events ?
       
 13656  * @property {boolean} animations Does the browser support CSS animation events ?
       
 13657  *
       
 13658  * @description
       
 13659  * This is very simple implementation of testing browser's features.
       
 13660  */
       
 13661 function $SnifferProvider() {
       
 13662   this.$get = ['$window', '$document', function($window, $document) {
       
 13663     var eventSupport = {},
       
 13664         android =
       
 13665           int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
       
 13666         boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
       
 13667         document = $document[0] || {},
       
 13668         documentMode = document.documentMode,
       
 13669         vendorPrefix,
       
 13670         vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/,
       
 13671         bodyStyle = document.body && document.body.style,
       
 13672         transitions = false,
       
 13673         animations = false,
       
 13674         match;
       
 13675 
       
 13676     if (bodyStyle) {
       
 13677       for(var prop in bodyStyle) {
       
 13678         if(match = vendorRegex.exec(prop)) {
       
 13679           vendorPrefix = match[0];
       
 13680           vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
       
 13681           break;
       
 13682         }
       
 13683       }
       
 13684 
       
 13685       if(!vendorPrefix) {
       
 13686         vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
       
 13687       }
       
 13688 
       
 13689       transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
       
 13690       animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
       
 13691 
       
 13692       if (android && (!transitions||!animations)) {
       
 13693         transitions = isString(document.body.style.webkitTransition);
       
 13694         animations = isString(document.body.style.webkitAnimation);
       
 13695       }
       
 13696     }
       
 13697 
       
 13698 
       
 13699     return {
       
 13700       // Android has history.pushState, but it does not update location correctly
       
 13701       // so let's not use the history API at all.
       
 13702       // http://code.google.com/p/android/issues/detail?id=17471
       
 13703       // https://github.com/angular/angular.js/issues/904
       
 13704 
       
 13705       // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
       
 13706       // so let's not use the history API also
       
 13707       // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
       
 13708       // jshint -W018
       
 13709       history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
       
 13710       // jshint +W018
       
 13711       hashchange: 'onhashchange' in $window &&
       
 13712                   // IE8 compatible mode lies
       
 13713                   (!documentMode || documentMode > 7),
       
 13714       hasEvent: function(event) {
       
 13715         // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
       
 13716         // it. In particular the event is not fired when backspace or delete key are pressed or
       
 13717         // when cut operation is performed.
       
 13718         if (event == 'input' && msie == 9) return false;
       
 13719 
       
 13720         if (isUndefined(eventSupport[event])) {
       
 13721           var divElm = document.createElement('div');
       
 13722           eventSupport[event] = 'on' + event in divElm;
       
 13723         }
       
 13724 
       
 13725         return eventSupport[event];
       
 13726       },
       
 13727       csp: csp(),
       
 13728       vendorPrefix: vendorPrefix,
       
 13729       transitions : transitions,
       
 13730       animations : animations,
       
 13731       android: android,
       
 13732       msie : msie,
       
 13733       msieDocumentMode: documentMode
       
 13734     };
       
 13735   }];
       
 13736 }
       
 13737 
       
 13738 function $TimeoutProvider() {
       
 13739   this.$get = ['$rootScope', '$browser', '$q', '$exceptionHandler',
       
 13740        function($rootScope,   $browser,   $q,   $exceptionHandler) {
       
 13741     var deferreds = {};
       
 13742 
       
 13743 
       
 13744      /**
       
 13745       * @ngdoc service
       
 13746       * @name $timeout
       
 13747       *
       
 13748       * @description
       
 13749       * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
       
 13750       * block and delegates any exceptions to
       
 13751       * {@link ng.$exceptionHandler $exceptionHandler} service.
       
 13752       *
       
 13753       * The return value of registering a timeout function is a promise, which will be resolved when
       
 13754       * the timeout is reached and the timeout function is executed.
       
 13755       *
       
 13756       * To cancel a timeout request, call `$timeout.cancel(promise)`.
       
 13757       *
       
 13758       * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
       
 13759       * synchronously flush the queue of deferred functions.
       
 13760       *
       
 13761       * @param {function()} fn A function, whose execution should be delayed.
       
 13762       * @param {number=} [delay=0] Delay in milliseconds.
       
 13763       * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
       
 13764       *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
       
 13765       * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
       
 13766       *   promise will be resolved with is the return value of the `fn` function.
       
 13767       *
       
 13768       */
       
 13769     function timeout(fn, delay, invokeApply) {
       
 13770       var deferred = $q.defer(),
       
 13771           promise = deferred.promise,
       
 13772           skipApply = (isDefined(invokeApply) && !invokeApply),
       
 13773           timeoutId;
       
 13774 
       
 13775       timeoutId = $browser.defer(function() {
       
 13776         try {
       
 13777           deferred.resolve(fn());
       
 13778         } catch(e) {
       
 13779           deferred.reject(e);
       
 13780           $exceptionHandler(e);
       
 13781         }
       
 13782         finally {
       
 13783           delete deferreds[promise.$$timeoutId];
       
 13784         }
       
 13785 
       
 13786         if (!skipApply) $rootScope.$apply();
       
 13787       }, delay);
       
 13788 
       
 13789       promise.$$timeoutId = timeoutId;
       
 13790       deferreds[timeoutId] = deferred;
       
 13791 
       
 13792       return promise;
       
 13793     }
       
 13794 
       
 13795 
       
 13796      /**
       
 13797       * @ngdoc method
       
 13798       * @name $timeout#cancel
       
 13799       *
       
 13800       * @description
       
 13801       * Cancels a task associated with the `promise`. As a result of this, the promise will be
       
 13802       * resolved with a rejection.
       
 13803       *
       
 13804       * @param {Promise=} promise Promise returned by the `$timeout` function.
       
 13805       * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
       
 13806       *   canceled.
       
 13807       */
       
 13808     timeout.cancel = function(promise) {
       
 13809       if (promise && promise.$$timeoutId in deferreds) {
       
 13810         deferreds[promise.$$timeoutId].reject('canceled');
       
 13811         delete deferreds[promise.$$timeoutId];
       
 13812         return $browser.defer.cancel(promise.$$timeoutId);
       
 13813       }
       
 13814       return false;
       
 13815     };
       
 13816 
       
 13817     return timeout;
       
 13818   }];
       
 13819 }
       
 13820 
       
 13821 // NOTE:  The usage of window and document instead of $window and $document here is
       
 13822 // deliberate.  This service depends on the specific behavior of anchor nodes created by the
       
 13823 // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
       
 13824 // cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
       
 13825 // doesn't know about mocked locations and resolves URLs to the real document - which is
       
 13826 // exactly the behavior needed here.  There is little value is mocking these out for this
       
 13827 // service.
       
 13828 var urlParsingNode = document.createElement("a");
       
 13829 var originUrl = urlResolve(window.location.href, true);
       
 13830 
       
 13831 
       
 13832 /**
       
 13833  *
       
 13834  * Implementation Notes for non-IE browsers
       
 13835  * ----------------------------------------
       
 13836  * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
       
 13837  * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
       
 13838  * URL will be resolved into an absolute URL in the context of the application document.
       
 13839  * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
       
 13840  * properties are all populated to reflect the normalized URL.  This approach has wide
       
 13841  * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc.  See
       
 13842  * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
       
 13843  *
       
 13844  * Implementation Notes for IE
       
 13845  * ---------------------------
       
 13846  * IE >= 8 and <= 10 normalizes the URL when assigned to the anchor node similar to the other
       
 13847  * browsers.  However, the parsed components will not be set if the URL assigned did not specify
       
 13848  * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
       
 13849  * work around that by performing the parsing in a 2nd step by taking a previously normalized
       
 13850  * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
       
 13851  * properties such as protocol, hostname, port, etc.
       
 13852  *
       
 13853  * IE7 does not normalize the URL when assigned to an anchor node.  (Apparently, it does, if one
       
 13854  * uses the inner HTML approach to assign the URL as part of an HTML snippet -
       
 13855  * http://stackoverflow.com/a/472729)  However, setting img[src] does normalize the URL.
       
 13856  * Unfortunately, setting img[src] to something like "javascript:foo" on IE throws an exception.
       
 13857  * Since the primary usage for normalizing URLs is to sanitize such URLs, we can't use that
       
 13858  * method and IE < 8 is unsupported.
       
 13859  *
       
 13860  * References:
       
 13861  *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
       
 13862  *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
       
 13863  *   http://url.spec.whatwg.org/#urlutils
       
 13864  *   https://github.com/angular/angular.js/pull/2902
       
 13865  *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
       
 13866  *
       
 13867  * @function
       
 13868  * @param {string} url The URL to be parsed.
       
 13869  * @description Normalizes and parses a URL.
       
 13870  * @returns {object} Returns the normalized URL as a dictionary.
       
 13871  *
       
 13872  *   | member name   | Description    |
       
 13873  *   |---------------|----------------|
       
 13874  *   | href          | A normalized version of the provided URL if it was not an absolute URL |
       
 13875  *   | protocol      | The protocol including the trailing colon                              |
       
 13876  *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
       
 13877  *   | search        | The search params, minus the question mark                             |
       
 13878  *   | hash          | The hash string, minus the hash symbol
       
 13879  *   | hostname      | The hostname
       
 13880  *   | port          | The port, without ":"
       
 13881  *   | pathname      | The pathname, beginning with "/"
       
 13882  *
       
 13883  */
       
 13884 function urlResolve(url, base) {
       
 13885   var href = url;
       
 13886 
       
 13887   if (msie) {
       
 13888     // Normalize before parse.  Refer Implementation Notes on why this is
       
 13889     // done in two steps on IE.
       
 13890     urlParsingNode.setAttribute("href", href);
       
 13891     href = urlParsingNode.href;
       
 13892   }
       
 13893 
       
 13894   urlParsingNode.setAttribute('href', href);
       
 13895 
       
 13896   // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
       
 13897   return {
       
 13898     href: urlParsingNode.href,
       
 13899     protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
       
 13900     host: urlParsingNode.host,
       
 13901     search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
       
 13902     hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
       
 13903     hostname: urlParsingNode.hostname,
       
 13904     port: urlParsingNode.port,
       
 13905     pathname: (urlParsingNode.pathname.charAt(0) === '/')
       
 13906       ? urlParsingNode.pathname
       
 13907       : '/' + urlParsingNode.pathname
       
 13908   };
       
 13909 }
       
 13910 
       
 13911 /**
       
 13912  * Parse a request URL and determine whether this is a same-origin request as the application document.
       
 13913  *
       
 13914  * @param {string|object} requestUrl The url of the request as a string that will be resolved
       
 13915  * or a parsed URL object.
       
 13916  * @returns {boolean} Whether the request is for the same origin as the application document.
       
 13917  */
       
 13918 function urlIsSameOrigin(requestUrl) {
       
 13919   var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
       
 13920   return (parsed.protocol === originUrl.protocol &&
       
 13921           parsed.host === originUrl.host);
       
 13922 }
       
 13923 
       
 13924 /**
       
 13925  * @ngdoc service
       
 13926  * @name $window
       
 13927  *
       
 13928  * @description
       
 13929  * A reference to the browser's `window` object. While `window`
       
 13930  * is globally available in JavaScript, it causes testability problems, because
       
 13931  * it is a global variable. In angular we always refer to it through the
       
 13932  * `$window` service, so it may be overridden, removed or mocked for testing.
       
 13933  *
       
 13934  * Expressions, like the one defined for the `ngClick` directive in the example
       
 13935  * below, are evaluated with respect to the current scope.  Therefore, there is
       
 13936  * no risk of inadvertently coding in a dependency on a global value in such an
       
 13937  * expression.
       
 13938  *
       
 13939  * @example
       
 13940    <example>
       
 13941      <file name="index.html">
       
 13942        <script>
       
 13943          function Ctrl($scope, $window) {
       
 13944            $scope.greeting = 'Hello, World!';
       
 13945            $scope.doGreeting = function(greeting) {
       
 13946                $window.alert(greeting);
       
 13947            };
       
 13948          }
       
 13949        </script>
       
 13950        <div ng-controller="Ctrl">
       
 13951          <input type="text" ng-model="greeting" />
       
 13952          <button ng-click="doGreeting(greeting)">ALERT</button>
       
 13953        </div>
       
 13954      </file>
       
 13955      <file name="protractor.js" type="protractor">
       
 13956       it('should display the greeting in the input box', function() {
       
 13957        element(by.model('greeting')).sendKeys('Hello, E2E Tests');
       
 13958        // If we click the button it will block the test runner
       
 13959        // element(':button').click();
       
 13960       });
       
 13961      </file>
       
 13962    </example>
       
 13963  */
       
 13964 function $WindowProvider(){
       
 13965   this.$get = valueFn(window);
       
 13966 }
       
 13967 
       
 13968 /**
       
 13969  * @ngdoc provider
       
 13970  * @name $filterProvider
       
 13971  * @description
       
 13972  *
       
 13973  * Filters are just functions which transform input to an output. However filters need to be
       
 13974  * Dependency Injected. To achieve this a filter definition consists of a factory function which is
       
 13975  * annotated with dependencies and is responsible for creating a filter function.
       
 13976  *
       
 13977  * ```js
       
 13978  *   // Filter registration
       
 13979  *   function MyModule($provide, $filterProvider) {
       
 13980  *     // create a service to demonstrate injection (not always needed)
       
 13981  *     $provide.value('greet', function(name){
       
 13982  *       return 'Hello ' + name + '!';
       
 13983  *     });
       
 13984  *
       
 13985  *     // register a filter factory which uses the
       
 13986  *     // greet service to demonstrate DI.
       
 13987  *     $filterProvider.register('greet', function(greet){
       
 13988  *       // return the filter function which uses the greet service
       
 13989  *       // to generate salutation
       
 13990  *       return function(text) {
       
 13991  *         // filters need to be forgiving so check input validity
       
 13992  *         return text && greet(text) || text;
       
 13993  *       };
       
 13994  *     });
       
 13995  *   }
       
 13996  * ```
       
 13997  *
       
 13998  * The filter function is registered with the `$injector` under the filter name suffix with
       
 13999  * `Filter`.
       
 14000  *
       
 14001  * ```js
       
 14002  *   it('should be the same instance', inject(
       
 14003  *     function($filterProvider) {
       
 14004  *       $filterProvider.register('reverse', function(){
       
 14005  *         return ...;
       
 14006  *       });
       
 14007  *     },
       
 14008  *     function($filter, reverseFilter) {
       
 14009  *       expect($filter('reverse')).toBe(reverseFilter);
       
 14010  *     });
       
 14011  * ```
       
 14012  *
       
 14013  *
       
 14014  * For more information about how angular filters work, and how to create your own filters, see
       
 14015  * {@link guide/filter Filters} in the Angular Developer Guide.
       
 14016  */
       
 14017 /**
       
 14018  * @ngdoc method
       
 14019  * @name $filterProvider#register
       
 14020  * @description
       
 14021  * Register filter factory function.
       
 14022  *
       
 14023  * @param {String} name Name of the filter.
       
 14024  * @param {Function} fn The filter factory function which is injectable.
       
 14025  */
       
 14026 
       
 14027 
       
 14028 /**
       
 14029  * @ngdoc service
       
 14030  * @name $filter
       
 14031  * @function
       
 14032  * @description
       
 14033  * Filters are used for formatting data displayed to the user.
       
 14034  *
       
 14035  * The general syntax in templates is as follows:
       
 14036  *
       
 14037  *         {{ expression [| filter_name[:parameter_value] ... ] }}
       
 14038  *
       
 14039  * @param {String} name Name of the filter function to retrieve
       
 14040  * @return {Function} the filter function
       
 14041  */
       
 14042 $FilterProvider.$inject = ['$provide'];
       
 14043 function $FilterProvider($provide) {
       
 14044   var suffix = 'Filter';
       
 14045 
       
 14046   /**
       
 14047    * @ngdoc method
       
 14048    * @name $controllerProvider#register
       
 14049    * @param {string|Object} name Name of the filter function, or an object map of filters where
       
 14050    *    the keys are the filter names and the values are the filter factories.
       
 14051    * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
       
 14052    *    of the registered filter instances.
       
 14053    */
       
 14054   function register(name, factory) {
       
 14055     if(isObject(name)) {
       
 14056       var filters = {};
       
 14057       forEach(name, function(filter, key) {
       
 14058         filters[key] = register(key, filter);
       
 14059       });
       
 14060       return filters;
       
 14061     } else {
       
 14062       return $provide.factory(name + suffix, factory);
       
 14063     }
       
 14064   }
       
 14065   this.register = register;
       
 14066 
       
 14067   this.$get = ['$injector', function($injector) {
       
 14068     return function(name) {
       
 14069       return $injector.get(name + suffix);
       
 14070     };
       
 14071   }];
       
 14072 
       
 14073   ////////////////////////////////////////
       
 14074 
       
 14075   /* global
       
 14076     currencyFilter: false,
       
 14077     dateFilter: false,
       
 14078     filterFilter: false,
       
 14079     jsonFilter: false,
       
 14080     limitToFilter: false,
       
 14081     lowercaseFilter: false,
       
 14082     numberFilter: false,
       
 14083     orderByFilter: false,
       
 14084     uppercaseFilter: false,
       
 14085   */
       
 14086 
       
 14087   register('currency', currencyFilter);
       
 14088   register('date', dateFilter);
       
 14089   register('filter', filterFilter);
       
 14090   register('json', jsonFilter);
       
 14091   register('limitTo', limitToFilter);
       
 14092   register('lowercase', lowercaseFilter);
       
 14093   register('number', numberFilter);
       
 14094   register('orderBy', orderByFilter);
       
 14095   register('uppercase', uppercaseFilter);
       
 14096 }
       
 14097 
       
 14098 /**
       
 14099  * @ngdoc filter
       
 14100  * @name filter
       
 14101  * @function
       
 14102  *
       
 14103  * @description
       
 14104  * Selects a subset of items from `array` and returns it as a new array.
       
 14105  *
       
 14106  * @param {Array} array The source array.
       
 14107  * @param {string|Object|function()} expression The predicate to be used for selecting items from
       
 14108  *   `array`.
       
 14109  *
       
 14110  *   Can be one of:
       
 14111  *
       
 14112  *   - `string`: The string is evaluated as an expression and the resulting value is used for substring match against
       
 14113  *     the contents of the `array`. All strings or objects with string properties in `array` that contain this string
       
 14114  *     will be returned. The predicate can be negated by prefixing the string with `!`.
       
 14115  *
       
 14116  *   - `Object`: A pattern object can be used to filter specific properties on objects contained
       
 14117  *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
       
 14118  *     which have property `name` containing "M" and property `phone` containing "1". A special
       
 14119  *     property name `$` can be used (as in `{$:"text"}`) to accept a match against any
       
 14120  *     property of the object. That's equivalent to the simple substring match with a `string`
       
 14121  *     as described above.
       
 14122  *
       
 14123  *   - `function(value)`: A predicate function can be used to write arbitrary filters. The function is
       
 14124  *     called for each element of `array`. The final result is an array of those elements that
       
 14125  *     the predicate returned true for.
       
 14126  *
       
 14127  * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
       
 14128  *     determining if the expected value (from the filter expression) and actual value (from
       
 14129  *     the object in the array) should be considered a match.
       
 14130  *
       
 14131  *   Can be one of:
       
 14132  *
       
 14133  *     - `function(actual, expected)`:
       
 14134  *       The function will be given the object value and the predicate value to compare and
       
 14135  *       should return true if the item should be included in filtered result.
       
 14136  *
       
 14137  *     - `true`: A shorthand for `function(actual, expected) { return angular.equals(expected, actual)}`.
       
 14138  *       this is essentially strict comparison of expected and actual.
       
 14139  *
       
 14140  *     - `false|undefined`: A short hand for a function which will look for a substring match in case
       
 14141  *       insensitive way.
       
 14142  *
       
 14143  * @example
       
 14144    <example>
       
 14145      <file name="index.html">
       
 14146        <div ng-init="friends = [{name:'John', phone:'555-1276'},
       
 14147                                 {name:'Mary', phone:'800-BIG-MARY'},
       
 14148                                 {name:'Mike', phone:'555-4321'},
       
 14149                                 {name:'Adam', phone:'555-5678'},
       
 14150                                 {name:'Julie', phone:'555-8765'},
       
 14151                                 {name:'Juliette', phone:'555-5678'}]"></div>
       
 14152 
       
 14153        Search: <input ng-model="searchText">
       
 14154        <table id="searchTextResults">
       
 14155          <tr><th>Name</th><th>Phone</th></tr>
       
 14156          <tr ng-repeat="friend in friends | filter:searchText">
       
 14157            <td>{{friend.name}}</td>
       
 14158            <td>{{friend.phone}}</td>
       
 14159          </tr>
       
 14160        </table>
       
 14161        <hr>
       
 14162        Any: <input ng-model="search.$"> <br>
       
 14163        Name only <input ng-model="search.name"><br>
       
 14164        Phone only <input ng-model="search.phone"><br>
       
 14165        Equality <input type="checkbox" ng-model="strict"><br>
       
 14166        <table id="searchObjResults">
       
 14167          <tr><th>Name</th><th>Phone</th></tr>
       
 14168          <tr ng-repeat="friendObj in friends | filter:search:strict">
       
 14169            <td>{{friendObj.name}}</td>
       
 14170            <td>{{friendObj.phone}}</td>
       
 14171          </tr>
       
 14172        </table>
       
 14173      </file>
       
 14174      <file name="protractor.js" type="protractor">
       
 14175        var expectFriendNames = function(expectedNames, key) {
       
 14176          element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
       
 14177            arr.forEach(function(wd, i) {
       
 14178              expect(wd.getText()).toMatch(expectedNames[i]);
       
 14179            });
       
 14180          });
       
 14181        };
       
 14182 
       
 14183        it('should search across all fields when filtering with a string', function() {
       
 14184          var searchText = element(by.model('searchText'));
       
 14185          searchText.clear();
       
 14186          searchText.sendKeys('m');
       
 14187          expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
       
 14188 
       
 14189          searchText.clear();
       
 14190          searchText.sendKeys('76');
       
 14191          expectFriendNames(['John', 'Julie'], 'friend');
       
 14192        });
       
 14193 
       
 14194        it('should search in specific fields when filtering with a predicate object', function() {
       
 14195          var searchAny = element(by.model('search.$'));
       
 14196          searchAny.clear();
       
 14197          searchAny.sendKeys('i');
       
 14198          expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
       
 14199        });
       
 14200        it('should use a equal comparison when comparator is true', function() {
       
 14201          var searchName = element(by.model('search.name'));
       
 14202          var strict = element(by.model('strict'));
       
 14203          searchName.clear();
       
 14204          searchName.sendKeys('Julie');
       
 14205          strict.click();
       
 14206          expectFriendNames(['Julie'], 'friendObj');
       
 14207        });
       
 14208      </file>
       
 14209    </example>
       
 14210  */
       
 14211 function filterFilter() {
       
 14212   return function(array, expression, comparator) {
       
 14213     if (!isArray(array)) return array;
       
 14214 
       
 14215     var comparatorType = typeof(comparator),
       
 14216         predicates = [];
       
 14217 
       
 14218     predicates.check = function(value) {
       
 14219       for (var j = 0; j < predicates.length; j++) {
       
 14220         if(!predicates[j](value)) {
       
 14221           return false;
       
 14222         }
       
 14223       }
       
 14224       return true;
       
 14225     };
       
 14226 
       
 14227     if (comparatorType !== 'function') {
       
 14228       if (comparatorType === 'boolean' && comparator) {
       
 14229         comparator = function(obj, text) {
       
 14230           return angular.equals(obj, text);
       
 14231         };
       
 14232       } else {
       
 14233         comparator = function(obj, text) {
       
 14234           if (obj && text && typeof obj === 'object' && typeof text === 'object') {
       
 14235             for (var objKey in obj) {
       
 14236               if (objKey.charAt(0) !== '$' && hasOwnProperty.call(obj, objKey) &&
       
 14237                   comparator(obj[objKey], text[objKey])) {
       
 14238                 return true;
       
 14239               }
       
 14240             }
       
 14241             return false;
       
 14242           }
       
 14243           text = (''+text).toLowerCase();
       
 14244           return (''+obj).toLowerCase().indexOf(text) > -1;
       
 14245         };
       
 14246       }
       
 14247     }
       
 14248 
       
 14249     var search = function(obj, text){
       
 14250       if (typeof text == 'string' && text.charAt(0) === '!') {
       
 14251         return !search(obj, text.substr(1));
       
 14252       }
       
 14253       switch (typeof obj) {
       
 14254         case "boolean":
       
 14255         case "number":
       
 14256         case "string":
       
 14257           return comparator(obj, text);
       
 14258         case "object":
       
 14259           switch (typeof text) {
       
 14260             case "object":
       
 14261               return comparator(obj, text);
       
 14262             default:
       
 14263               for ( var objKey in obj) {
       
 14264                 if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
       
 14265                   return true;
       
 14266                 }
       
 14267               }
       
 14268               break;
       
 14269           }
       
 14270           return false;
       
 14271         case "array":
       
 14272           for ( var i = 0; i < obj.length; i++) {
       
 14273             if (search(obj[i], text)) {
       
 14274               return true;
       
 14275             }
       
 14276           }
       
 14277           return false;
       
 14278         default:
       
 14279           return false;
       
 14280       }
       
 14281     };
       
 14282     switch (typeof expression) {
       
 14283       case "boolean":
       
 14284       case "number":
       
 14285       case "string":
       
 14286         // Set up expression object and fall through
       
 14287         expression = {$:expression};
       
 14288         // jshint -W086
       
 14289       case "object":
       
 14290         // jshint +W086
       
 14291         for (var key in expression) {
       
 14292           (function(path) {
       
 14293             if (typeof expression[path] == 'undefined') return;
       
 14294             predicates.push(function(value) {
       
 14295               return search(path == '$' ? value : (value && value[path]), expression[path]);
       
 14296             });
       
 14297           })(key);
       
 14298         }
       
 14299         break;
       
 14300       case 'function':
       
 14301         predicates.push(expression);
       
 14302         break;
       
 14303       default:
       
 14304         return array;
       
 14305     }
       
 14306     var filtered = [];
       
 14307     for ( var j = 0; j < array.length; j++) {
       
 14308       var value = array[j];
       
 14309       if (predicates.check(value)) {
       
 14310         filtered.push(value);
       
 14311       }
       
 14312     }
       
 14313     return filtered;
       
 14314   };
       
 14315 }
       
 14316 
       
 14317 /**
       
 14318  * @ngdoc filter
       
 14319  * @name currency
       
 14320  * @function
       
 14321  *
       
 14322  * @description
       
 14323  * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
       
 14324  * symbol for current locale is used.
       
 14325  *
       
 14326  * @param {number} amount Input to filter.
       
 14327  * @param {string=} symbol Currency symbol or identifier to be displayed.
       
 14328  * @returns {string} Formatted number.
       
 14329  *
       
 14330  *
       
 14331  * @example
       
 14332    <example>
       
 14333      <file name="index.html">
       
 14334        <script>
       
 14335          function Ctrl($scope) {
       
 14336            $scope.amount = 1234.56;
       
 14337          }
       
 14338        </script>
       
 14339        <div ng-controller="Ctrl">
       
 14340          <input type="number" ng-model="amount"> <br>
       
 14341          default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
       
 14342          custom currency identifier (USD$): <span>{{amount | currency:"USD$"}}</span>
       
 14343        </div>
       
 14344      </file>
       
 14345      <file name="protractor.js" type="protractor">
       
 14346        it('should init with 1234.56', function() {
       
 14347          expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
       
 14348          expect(element(by.binding('amount | currency:"USD$"')).getText()).toBe('USD$1,234.56');
       
 14349        });
       
 14350        it('should update', function() {
       
 14351          if (browser.params.browser == 'safari') {
       
 14352            // Safari does not understand the minus key. See
       
 14353            // https://github.com/angular/protractor/issues/481
       
 14354            return;
       
 14355          }
       
 14356          element(by.model('amount')).clear();
       
 14357          element(by.model('amount')).sendKeys('-1234');
       
 14358          expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)');
       
 14359          expect(element(by.binding('amount | currency:"USD$"')).getText()).toBe('(USD$1,234.00)');
       
 14360        });
       
 14361      </file>
       
 14362    </example>
       
 14363  */
       
 14364 currencyFilter.$inject = ['$locale'];
       
 14365 function currencyFilter($locale) {
       
 14366   var formats = $locale.NUMBER_FORMATS;
       
 14367   return function(amount, currencySymbol){
       
 14368     if (isUndefined(currencySymbol)) currencySymbol = formats.CURRENCY_SYM;
       
 14369     return formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, 2).
       
 14370                 replace(/\u00A4/g, currencySymbol);
       
 14371   };
       
 14372 }
       
 14373 
       
 14374 /**
       
 14375  * @ngdoc filter
       
 14376  * @name number
       
 14377  * @function
       
 14378  *
       
 14379  * @description
       
 14380  * Formats a number as text.
       
 14381  *
       
 14382  * If the input is not a number an empty string is returned.
       
 14383  *
       
 14384  * @param {number|string} number Number to format.
       
 14385  * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
       
 14386  * If this is not provided then the fraction size is computed from the current locale's number
       
 14387  * formatting pattern. In the case of the default locale, it will be 3.
       
 14388  * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
       
 14389  *
       
 14390  * @example
       
 14391    <example>
       
 14392      <file name="index.html">
       
 14393        <script>
       
 14394          function Ctrl($scope) {
       
 14395            $scope.val = 1234.56789;
       
 14396          }
       
 14397        </script>
       
 14398        <div ng-controller="Ctrl">
       
 14399          Enter number: <input ng-model='val'><br>
       
 14400          Default formatting: <span id='number-default'>{{val | number}}</span><br>
       
 14401          No fractions: <span>{{val | number:0}}</span><br>
       
 14402          Negative number: <span>{{-val | number:4}}</span>
       
 14403        </div>
       
 14404      </file>
       
 14405      <file name="protractor.js" type="protractor">
       
 14406        it('should format numbers', function() {
       
 14407          expect(element(by.id('number-default')).getText()).toBe('1,234.568');
       
 14408          expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
       
 14409          expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
       
 14410        });
       
 14411 
       
 14412        it('should update', function() {
       
 14413          element(by.model('val')).clear();
       
 14414          element(by.model('val')).sendKeys('3374.333');
       
 14415          expect(element(by.id('number-default')).getText()).toBe('3,374.333');
       
 14416          expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
       
 14417          expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
       
 14418       });
       
 14419      </file>
       
 14420    </example>
       
 14421  */
       
 14422 
       
 14423 
       
 14424 numberFilter.$inject = ['$locale'];
       
 14425 function numberFilter($locale) {
       
 14426   var formats = $locale.NUMBER_FORMATS;
       
 14427   return function(number, fractionSize) {
       
 14428     return formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
       
 14429       fractionSize);
       
 14430   };
       
 14431 }
       
 14432 
       
 14433 var DECIMAL_SEP = '.';
       
 14434 function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
       
 14435   if (number == null || !isFinite(number) || isObject(number)) return '';
       
 14436 
       
 14437   var isNegative = number < 0;
       
 14438   number = Math.abs(number);
       
 14439   var numStr = number + '',
       
 14440       formatedText = '',
       
 14441       parts = [];
       
 14442 
       
 14443   var hasExponent = false;
       
 14444   if (numStr.indexOf('e') !== -1) {
       
 14445     var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
       
 14446     if (match && match[2] == '-' && match[3] > fractionSize + 1) {
       
 14447       numStr = '0';
       
 14448     } else {
       
 14449       formatedText = numStr;
       
 14450       hasExponent = true;
       
 14451     }
       
 14452   }
       
 14453 
       
 14454   if (!hasExponent) {
       
 14455     var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
       
 14456 
       
 14457     // determine fractionSize if it is not specified
       
 14458     if (isUndefined(fractionSize)) {
       
 14459       fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
       
 14460     }
       
 14461 
       
 14462     var pow = Math.pow(10, fractionSize);
       
 14463     number = Math.round(number * pow) / pow;
       
 14464     var fraction = ('' + number).split(DECIMAL_SEP);
       
 14465     var whole = fraction[0];
       
 14466     fraction = fraction[1] || '';
       
 14467 
       
 14468     var i, pos = 0,
       
 14469         lgroup = pattern.lgSize,
       
 14470         group = pattern.gSize;
       
 14471 
       
 14472     if (whole.length >= (lgroup + group)) {
       
 14473       pos = whole.length - lgroup;
       
 14474       for (i = 0; i < pos; i++) {
       
 14475         if ((pos - i)%group === 0 && i !== 0) {
       
 14476           formatedText += groupSep;
       
 14477         }
       
 14478         formatedText += whole.charAt(i);
       
 14479       }
       
 14480     }
       
 14481 
       
 14482     for (i = pos; i < whole.length; i++) {
       
 14483       if ((whole.length - i)%lgroup === 0 && i !== 0) {
       
 14484         formatedText += groupSep;
       
 14485       }
       
 14486       formatedText += whole.charAt(i);
       
 14487     }
       
 14488 
       
 14489     // format fraction part.
       
 14490     while(fraction.length < fractionSize) {
       
 14491       fraction += '0';
       
 14492     }
       
 14493 
       
 14494     if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
       
 14495   } else {
       
 14496 
       
 14497     if (fractionSize > 0 && number > -1 && number < 1) {
       
 14498       formatedText = number.toFixed(fractionSize);
       
 14499     }
       
 14500   }
       
 14501 
       
 14502   parts.push(isNegative ? pattern.negPre : pattern.posPre);
       
 14503   parts.push(formatedText);
       
 14504   parts.push(isNegative ? pattern.negSuf : pattern.posSuf);
       
 14505   return parts.join('');
       
 14506 }
       
 14507 
       
 14508 function padNumber(num, digits, trim) {
       
 14509   var neg = '';
       
 14510   if (num < 0) {
       
 14511     neg =  '-';
       
 14512     num = -num;
       
 14513   }
       
 14514   num = '' + num;
       
 14515   while(num.length < digits) num = '0' + num;
       
 14516   if (trim)
       
 14517     num = num.substr(num.length - digits);
       
 14518   return neg + num;
       
 14519 }
       
 14520 
       
 14521 
       
 14522 function dateGetter(name, size, offset, trim) {
       
 14523   offset = offset || 0;
       
 14524   return function(date) {
       
 14525     var value = date['get' + name]();
       
 14526     if (offset > 0 || value > -offset)
       
 14527       value += offset;
       
 14528     if (value === 0 && offset == -12 ) value = 12;
       
 14529     return padNumber(value, size, trim);
       
 14530   };
       
 14531 }
       
 14532 
       
 14533 function dateStrGetter(name, shortForm) {
       
 14534   return function(date, formats) {
       
 14535     var value = date['get' + name]();
       
 14536     var get = uppercase(shortForm ? ('SHORT' + name) : name);
       
 14537 
       
 14538     return formats[get][value];
       
 14539   };
       
 14540 }
       
 14541 
       
 14542 function timeZoneGetter(date) {
       
 14543   var zone = -1 * date.getTimezoneOffset();
       
 14544   var paddedZone = (zone >= 0) ? "+" : "";
       
 14545 
       
 14546   paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
       
 14547                 padNumber(Math.abs(zone % 60), 2);
       
 14548 
       
 14549   return paddedZone;
       
 14550 }
       
 14551 
       
 14552 function ampmGetter(date, formats) {
       
 14553   return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
       
 14554 }
       
 14555 
       
 14556 var DATE_FORMATS = {
       
 14557   yyyy: dateGetter('FullYear', 4),
       
 14558     yy: dateGetter('FullYear', 2, 0, true),
       
 14559      y: dateGetter('FullYear', 1),
       
 14560   MMMM: dateStrGetter('Month'),
       
 14561    MMM: dateStrGetter('Month', true),
       
 14562     MM: dateGetter('Month', 2, 1),
       
 14563      M: dateGetter('Month', 1, 1),
       
 14564     dd: dateGetter('Date', 2),
       
 14565      d: dateGetter('Date', 1),
       
 14566     HH: dateGetter('Hours', 2),
       
 14567      H: dateGetter('Hours', 1),
       
 14568     hh: dateGetter('Hours', 2, -12),
       
 14569      h: dateGetter('Hours', 1, -12),
       
 14570     mm: dateGetter('Minutes', 2),
       
 14571      m: dateGetter('Minutes', 1),
       
 14572     ss: dateGetter('Seconds', 2),
       
 14573      s: dateGetter('Seconds', 1),
       
 14574      // while ISO 8601 requires fractions to be prefixed with `.` or `,`
       
 14575      // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
       
 14576    sss: dateGetter('Milliseconds', 3),
       
 14577   EEEE: dateStrGetter('Day'),
       
 14578    EEE: dateStrGetter('Day', true),
       
 14579      a: ampmGetter,
       
 14580      Z: timeZoneGetter
       
 14581 };
       
 14582 
       
 14583 var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/,
       
 14584     NUMBER_STRING = /^\-?\d+$/;
       
 14585 
       
 14586 /**
       
 14587  * @ngdoc filter
       
 14588  * @name date
       
 14589  * @function
       
 14590  *
       
 14591  * @description
       
 14592  *   Formats `date` to a string based on the requested `format`.
       
 14593  *
       
 14594  *   `format` string can be composed of the following elements:
       
 14595  *
       
 14596  *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
       
 14597  *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
       
 14598  *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
       
 14599  *   * `'MMMM'`: Month in year (January-December)
       
 14600  *   * `'MMM'`: Month in year (Jan-Dec)
       
 14601  *   * `'MM'`: Month in year, padded (01-12)
       
 14602  *   * `'M'`: Month in year (1-12)
       
 14603  *   * `'dd'`: Day in month, padded (01-31)
       
 14604  *   * `'d'`: Day in month (1-31)
       
 14605  *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
       
 14606  *   * `'EEE'`: Day in Week, (Sun-Sat)
       
 14607  *   * `'HH'`: Hour in day, padded (00-23)
       
 14608  *   * `'H'`: Hour in day (0-23)
       
 14609  *   * `'hh'`: Hour in am/pm, padded (01-12)
       
 14610  *   * `'h'`: Hour in am/pm, (1-12)
       
 14611  *   * `'mm'`: Minute in hour, padded (00-59)
       
 14612  *   * `'m'`: Minute in hour (0-59)
       
 14613  *   * `'ss'`: Second in minute, padded (00-59)
       
 14614  *   * `'s'`: Second in minute (0-59)
       
 14615  *   * `'.sss' or ',sss'`: Millisecond in second, padded (000-999)
       
 14616  *   * `'a'`: am/pm marker
       
 14617  *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
       
 14618  *
       
 14619  *   `format` string can also be one of the following predefined
       
 14620  *   {@link guide/i18n localizable formats}:
       
 14621  *
       
 14622  *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
       
 14623  *     (e.g. Sep 3, 2010 12:05:08 pm)
       
 14624  *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 pm)
       
 14625  *   * `'fullDate'`: equivalent to `'EEEE, MMMM d,y'` for en_US  locale
       
 14626  *     (e.g. Friday, September 3, 2010)
       
 14627  *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
       
 14628  *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
       
 14629  *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
       
 14630  *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 pm)
       
 14631  *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 pm)
       
 14632  *
       
 14633  *   `format` string can contain literal values. These need to be quoted with single quotes (e.g.
       
 14634  *   `"h 'in the morning'"`). In order to output single quote, use two single quotes in a sequence
       
 14635  *   (e.g. `"h 'o''clock'"`).
       
 14636  *
       
 14637  * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
       
 14638  *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and its
       
 14639  *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
       
 14640  *    specified in the string input, the time is considered to be in the local timezone.
       
 14641  * @param {string=} format Formatting rules (see Description). If not specified,
       
 14642  *    `mediumDate` is used.
       
 14643  * @returns {string} Formatted string or the input if input is not recognized as date/millis.
       
 14644  *
       
 14645  * @example
       
 14646    <example>
       
 14647      <file name="index.html">
       
 14648        <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
       
 14649            <span>{{1288323623006 | date:'medium'}}</span><br>
       
 14650        <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
       
 14651           <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
       
 14652        <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
       
 14653           <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
       
 14654      </file>
       
 14655      <file name="protractor.js" type="protractor">
       
 14656        it('should format date', function() {
       
 14657          expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
       
 14658             toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
       
 14659          expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
       
 14660             toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
       
 14661          expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
       
 14662             toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
       
 14663        });
       
 14664      </file>
       
 14665    </example>
       
 14666  */
       
 14667 dateFilter.$inject = ['$locale'];
       
 14668 function dateFilter($locale) {
       
 14669 
       
 14670 
       
 14671   var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
       
 14672                      // 1        2       3         4          5          6          7          8  9     10      11
       
 14673   function jsonStringToDate(string) {
       
 14674     var match;
       
 14675     if (match = string.match(R_ISO8601_STR)) {
       
 14676       var date = new Date(0),
       
 14677           tzHour = 0,
       
 14678           tzMin  = 0,
       
 14679           dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
       
 14680           timeSetter = match[8] ? date.setUTCHours : date.setHours;
       
 14681 
       
 14682       if (match[9]) {
       
 14683         tzHour = int(match[9] + match[10]);
       
 14684         tzMin = int(match[9] + match[11]);
       
 14685       }
       
 14686       dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3]));
       
 14687       var h = int(match[4]||0) - tzHour;
       
 14688       var m = int(match[5]||0) - tzMin;
       
 14689       var s = int(match[6]||0);
       
 14690       var ms = Math.round(parseFloat('0.' + (match[7]||0)) * 1000);
       
 14691       timeSetter.call(date, h, m, s, ms);
       
 14692       return date;
       
 14693     }
       
 14694     return string;
       
 14695   }
       
 14696 
       
 14697 
       
 14698   return function(date, format) {
       
 14699     var text = '',
       
 14700         parts = [],
       
 14701         fn, match;
       
 14702 
       
 14703     format = format || 'mediumDate';
       
 14704     format = $locale.DATETIME_FORMATS[format] || format;
       
 14705     if (isString(date)) {
       
 14706       if (NUMBER_STRING.test(date)) {
       
 14707         date = int(date);
       
 14708       } else {
       
 14709         date = jsonStringToDate(date);
       
 14710       }
       
 14711     }
       
 14712 
       
 14713     if (isNumber(date)) {
       
 14714       date = new Date(date);
       
 14715     }
       
 14716 
       
 14717     if (!isDate(date)) {
       
 14718       return date;
       
 14719     }
       
 14720 
       
 14721     while(format) {
       
 14722       match = DATE_FORMATS_SPLIT.exec(format);
       
 14723       if (match) {
       
 14724         parts = concat(parts, match, 1);
       
 14725         format = parts.pop();
       
 14726       } else {
       
 14727         parts.push(format);
       
 14728         format = null;
       
 14729       }
       
 14730     }
       
 14731 
       
 14732     forEach(parts, function(value){
       
 14733       fn = DATE_FORMATS[value];
       
 14734       text += fn ? fn(date, $locale.DATETIME_FORMATS)
       
 14735                  : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
       
 14736     });
       
 14737 
       
 14738     return text;
       
 14739   };
       
 14740 }
       
 14741 
       
 14742 
       
 14743 /**
       
 14744  * @ngdoc filter
       
 14745  * @name json
       
 14746  * @function
       
 14747  *
       
 14748  * @description
       
 14749  *   Allows you to convert a JavaScript object into JSON string.
       
 14750  *
       
 14751  *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
       
 14752  *   the binding is automatically converted to JSON.
       
 14753  *
       
 14754  * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
       
 14755  * @returns {string} JSON string.
       
 14756  *
       
 14757  *
       
 14758  * @example
       
 14759    <example>
       
 14760      <file name="index.html">
       
 14761        <pre>{{ {'name':'value'} | json }}</pre>
       
 14762      </file>
       
 14763      <file name="protractor.js" type="protractor">
       
 14764        it('should jsonify filtered objects', function() {
       
 14765          expect(element(by.binding("{'name':'value'}")).getText()).toMatch(/\{\n  "name": ?"value"\n}/);
       
 14766        });
       
 14767      </file>
       
 14768    </example>
       
 14769  *
       
 14770  */
       
 14771 function jsonFilter() {
       
 14772   return function(object) {
       
 14773     return toJson(object, true);
       
 14774   };
       
 14775 }
       
 14776 
       
 14777 
       
 14778 /**
       
 14779  * @ngdoc filter
       
 14780  * @name lowercase
       
 14781  * @function
       
 14782  * @description
       
 14783  * Converts string to lowercase.
       
 14784  * @see angular.lowercase
       
 14785  */
       
 14786 var lowercaseFilter = valueFn(lowercase);
       
 14787 
       
 14788 
       
 14789 /**
       
 14790  * @ngdoc filter
       
 14791  * @name uppercase
       
 14792  * @function
       
 14793  * @description
       
 14794  * Converts string to uppercase.
       
 14795  * @see angular.uppercase
       
 14796  */
       
 14797 var uppercaseFilter = valueFn(uppercase);
       
 14798 
       
 14799 /**
       
 14800  * @ngdoc filter
       
 14801  * @name limitTo
       
 14802  * @function
       
 14803  *
       
 14804  * @description
       
 14805  * Creates a new array or string containing only a specified number of elements. The elements
       
 14806  * are taken from either the beginning or the end of the source array or string, as specified by
       
 14807  * the value and sign (positive or negative) of `limit`.
       
 14808  *
       
 14809  * @param {Array|string} input Source array or string to be limited.
       
 14810  * @param {string|number} limit The length of the returned array or string. If the `limit` number
       
 14811  *     is positive, `limit` number of items from the beginning of the source array/string are copied.
       
 14812  *     If the number is negative, `limit` number  of items from the end of the source array/string
       
 14813  *     are copied. The `limit` will be trimmed if it exceeds `array.length`
       
 14814  * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
       
 14815  *     had less than `limit` elements.
       
 14816  *
       
 14817  * @example
       
 14818    <example>
       
 14819      <file name="index.html">
       
 14820        <script>
       
 14821          function Ctrl($scope) {
       
 14822            $scope.numbers = [1,2,3,4,5,6,7,8,9];
       
 14823            $scope.letters = "abcdefghi";
       
 14824            $scope.numLimit = 3;
       
 14825            $scope.letterLimit = 3;
       
 14826          }
       
 14827        </script>
       
 14828        <div ng-controller="Ctrl">
       
 14829          Limit {{numbers}} to: <input type="integer" ng-model="numLimit">
       
 14830          <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
       
 14831          Limit {{letters}} to: <input type="integer" ng-model="letterLimit">
       
 14832          <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
       
 14833        </div>
       
 14834      </file>
       
 14835      <file name="protractor.js" type="protractor">
       
 14836        var numLimitInput = element(by.model('numLimit'));
       
 14837        var letterLimitInput = element(by.model('letterLimit'));
       
 14838        var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
       
 14839        var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
       
 14840 
       
 14841        it('should limit the number array to first three items', function() {
       
 14842          expect(numLimitInput.getAttribute('value')).toBe('3');
       
 14843          expect(letterLimitInput.getAttribute('value')).toBe('3');
       
 14844          expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
       
 14845          expect(limitedLetters.getText()).toEqual('Output letters: abc');
       
 14846        });
       
 14847 
       
 14848        it('should update the output when -3 is entered', function() {
       
 14849          numLimitInput.clear();
       
 14850          numLimitInput.sendKeys('-3');
       
 14851          letterLimitInput.clear();
       
 14852          letterLimitInput.sendKeys('-3');
       
 14853          expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
       
 14854          expect(limitedLetters.getText()).toEqual('Output letters: ghi');
       
 14855        });
       
 14856 
       
 14857        it('should not exceed the maximum size of input array', function() {
       
 14858          numLimitInput.clear();
       
 14859          numLimitInput.sendKeys('100');
       
 14860          letterLimitInput.clear();
       
 14861          letterLimitInput.sendKeys('100');
       
 14862          expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
       
 14863          expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
       
 14864        });
       
 14865      </file>
       
 14866    </example>
       
 14867  */
       
 14868 function limitToFilter(){
       
 14869   return function(input, limit) {
       
 14870     if (!isArray(input) && !isString(input)) return input;
       
 14871 
       
 14872     limit = int(limit);
       
 14873 
       
 14874     if (isString(input)) {
       
 14875       //NaN check on limit
       
 14876       if (limit) {
       
 14877         return limit >= 0 ? input.slice(0, limit) : input.slice(limit, input.length);
       
 14878       } else {
       
 14879         return "";
       
 14880       }
       
 14881     }
       
 14882 
       
 14883     var out = [],
       
 14884       i, n;
       
 14885 
       
 14886     // if abs(limit) exceeds maximum length, trim it
       
 14887     if (limit > input.length)
       
 14888       limit = input.length;
       
 14889     else if (limit < -input.length)
       
 14890       limit = -input.length;
       
 14891 
       
 14892     if (limit > 0) {
       
 14893       i = 0;
       
 14894       n = limit;
       
 14895     } else {
       
 14896       i = input.length + limit;
       
 14897       n = input.length;
       
 14898     }
       
 14899 
       
 14900     for (; i<n; i++) {
       
 14901       out.push(input[i]);
       
 14902     }
       
 14903 
       
 14904     return out;
       
 14905   };
       
 14906 }
       
 14907 
       
 14908 /**
       
 14909  * @ngdoc filter
       
 14910  * @name orderBy
       
 14911  * @function
       
 14912  *
       
 14913  * @description
       
 14914  * Orders a specified `array` by the `expression` predicate.
       
 14915  *
       
 14916  * @param {Array} array The array to sort.
       
 14917  * @param {function(*)|string|Array.<(function(*)|string)>} expression A predicate to be
       
 14918  *    used by the comparator to determine the order of elements.
       
 14919  *
       
 14920  *    Can be one of:
       
 14921  *
       
 14922  *    - `function`: Getter function. The result of this function will be sorted using the
       
 14923  *      `<`, `=`, `>` operator.
       
 14924  *    - `string`: An Angular expression which evaluates to an object to order by, such as 'name'
       
 14925  *      to sort by a property called 'name'. Optionally prefixed with `+` or `-` to control
       
 14926  *      ascending or descending sort order (for example, +name or -name).
       
 14927  *    - `Array`: An array of function or string predicates. The first predicate in the array
       
 14928  *      is used for sorting, but when two items are equivalent, the next predicate is used.
       
 14929  *
       
 14930  * @param {boolean=} reverse Reverse the order the array.
       
 14931  * @returns {Array} Sorted copy of the source array.
       
 14932  *
       
 14933  * @example
       
 14934    <example>
       
 14935      <file name="index.html">
       
 14936        <script>
       
 14937          function Ctrl($scope) {
       
 14938            $scope.friends =
       
 14939                [{name:'John', phone:'555-1212', age:10},
       
 14940                 {name:'Mary', phone:'555-9876', age:19},
       
 14941                 {name:'Mike', phone:'555-4321', age:21},
       
 14942                 {name:'Adam', phone:'555-5678', age:35},
       
 14943                 {name:'Julie', phone:'555-8765', age:29}]
       
 14944            $scope.predicate = '-age';
       
 14945          }
       
 14946        </script>
       
 14947        <div ng-controller="Ctrl">
       
 14948          <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
       
 14949          <hr/>
       
 14950          [ <a href="" ng-click="predicate=''">unsorted</a> ]
       
 14951          <table class="friend">
       
 14952            <tr>
       
 14953              <th><a href="" ng-click="predicate = 'name'; reverse=false">Name</a>
       
 14954                  (<a href="" ng-click="predicate = '-name'; reverse=false">^</a>)</th>
       
 14955              <th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
       
 14956              <th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th>
       
 14957            </tr>
       
 14958            <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
       
 14959              <td>{{friend.name}}</td>
       
 14960              <td>{{friend.phone}}</td>
       
 14961              <td>{{friend.age}}</td>
       
 14962            </tr>
       
 14963          </table>
       
 14964        </div>
       
 14965      </file>
       
 14966    </example>
       
 14967  */
       
 14968 orderByFilter.$inject = ['$parse'];
       
 14969 function orderByFilter($parse){
       
 14970   return function(array, sortPredicate, reverseOrder) {
       
 14971     if (!isArray(array)) return array;
       
 14972     if (!sortPredicate) return array;
       
 14973     sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
       
 14974     sortPredicate = map(sortPredicate, function(predicate){
       
 14975       var descending = false, get = predicate || identity;
       
 14976       if (isString(predicate)) {
       
 14977         if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
       
 14978           descending = predicate.charAt(0) == '-';
       
 14979           predicate = predicate.substring(1);
       
 14980         }
       
 14981         get = $parse(predicate);
       
 14982         if (get.constant) {
       
 14983           var key = get();
       
 14984           return reverseComparator(function(a,b) {
       
 14985             return compare(a[key], b[key]);
       
 14986           }, descending);
       
 14987         }
       
 14988       }
       
 14989       return reverseComparator(function(a,b){
       
 14990         return compare(get(a),get(b));
       
 14991       }, descending);
       
 14992     });
       
 14993     var arrayCopy = [];
       
 14994     for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); }
       
 14995     return arrayCopy.sort(reverseComparator(comparator, reverseOrder));
       
 14996 
       
 14997     function comparator(o1, o2){
       
 14998       for ( var i = 0; i < sortPredicate.length; i++) {
       
 14999         var comp = sortPredicate[i](o1, o2);
       
 15000         if (comp !== 0) return comp;
       
 15001       }
       
 15002       return 0;
       
 15003     }
       
 15004     function reverseComparator(comp, descending) {
       
 15005       return toBoolean(descending)
       
 15006           ? function(a,b){return comp(b,a);}
       
 15007           : comp;
       
 15008     }
       
 15009     function compare(v1, v2){
       
 15010       var t1 = typeof v1;
       
 15011       var t2 = typeof v2;
       
 15012       if (t1 == t2) {
       
 15013         if (t1 == "string") {
       
 15014            v1 = v1.toLowerCase();
       
 15015            v2 = v2.toLowerCase();
       
 15016         }
       
 15017         if (v1 === v2) return 0;
       
 15018         return v1 < v2 ? -1 : 1;
       
 15019       } else {
       
 15020         return t1 < t2 ? -1 : 1;
       
 15021       }
       
 15022     }
       
 15023   };
       
 15024 }
       
 15025 
       
 15026 function ngDirective(directive) {
       
 15027   if (isFunction(directive)) {
       
 15028     directive = {
       
 15029       link: directive
       
 15030     };
       
 15031   }
       
 15032   directive.restrict = directive.restrict || 'AC';
       
 15033   return valueFn(directive);
       
 15034 }
       
 15035 
       
 15036 /**
       
 15037  * @ngdoc directive
       
 15038  * @name a
       
 15039  * @restrict E
       
 15040  *
       
 15041  * @description
       
 15042  * Modifies the default behavior of the html A tag so that the default action is prevented when
       
 15043  * the href attribute is empty.
       
 15044  *
       
 15045  * This change permits the easy creation of action links with the `ngClick` directive
       
 15046  * without changing the location or causing page reloads, e.g.:
       
 15047  * `<a href="" ng-click="list.addItem()">Add Item</a>`
       
 15048  */
       
 15049 var htmlAnchorDirective = valueFn({
       
 15050   restrict: 'E',
       
 15051   compile: function(element, attr) {
       
 15052 
       
 15053     if (msie <= 8) {
       
 15054 
       
 15055       // turn <a href ng-click="..">link</a> into a stylable link in IE
       
 15056       // but only if it doesn't have name attribute, in which case it's an anchor
       
 15057       if (!attr.href && !attr.name) {
       
 15058         attr.$set('href', '');
       
 15059       }
       
 15060 
       
 15061       // add a comment node to anchors to workaround IE bug that causes element content to be reset
       
 15062       // to new attribute content if attribute is updated with value containing @ and element also
       
 15063       // contains value with @
       
 15064       // see issue #1949
       
 15065       element.append(document.createComment('IE fix'));
       
 15066     }
       
 15067 
       
 15068     if (!attr.href && !attr.xlinkHref && !attr.name) {
       
 15069       return function(scope, element) {
       
 15070         // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
       
 15071         var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
       
 15072                    'xlink:href' : 'href';
       
 15073         element.on('click', function(event){
       
 15074           // if we have no href url, then don't navigate anywhere.
       
 15075           if (!element.attr(href)) {
       
 15076             event.preventDefault();
       
 15077           }
       
 15078         });
       
 15079       };
       
 15080     }
       
 15081   }
       
 15082 });
       
 15083 
       
 15084 /**
       
 15085  * @ngdoc directive
       
 15086  * @name ngHref
       
 15087  * @restrict A
       
 15088  * @priority 99
       
 15089  *
       
 15090  * @description
       
 15091  * Using Angular markup like `{{hash}}` in an href attribute will
       
 15092  * make the link go to the wrong URL if the user clicks it before
       
 15093  * Angular has a chance to replace the `{{hash}}` markup with its
       
 15094  * value. Until Angular replaces the markup the link will be broken
       
 15095  * and will most likely return a 404 error.
       
 15096  *
       
 15097  * The `ngHref` directive solves this problem.
       
 15098  *
       
 15099  * The wrong way to write it:
       
 15100  * ```html
       
 15101  * <a href="http://www.gravatar.com/avatar/{{hash}}"/>
       
 15102  * ```
       
 15103  *
       
 15104  * The correct way to write it:
       
 15105  * ```html
       
 15106  * <a ng-href="http://www.gravatar.com/avatar/{{hash}}"/>
       
 15107  * ```
       
 15108  *
       
 15109  * @element A
       
 15110  * @param {template} ngHref any string which can contain `{{}}` markup.
       
 15111  *
       
 15112  * @example
       
 15113  * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
       
 15114  * in links and their different behaviors:
       
 15115     <example>
       
 15116       <file name="index.html">
       
 15117         <input ng-model="value" /><br />
       
 15118         <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
       
 15119         <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
       
 15120         <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
       
 15121         <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
       
 15122         <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
       
 15123         <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
       
 15124       </file>
       
 15125       <file name="protractor.js" type="protractor">
       
 15126         it('should execute ng-click but not reload when href without value', function() {
       
 15127           element(by.id('link-1')).click();
       
 15128           expect(element(by.model('value')).getAttribute('value')).toEqual('1');
       
 15129           expect(element(by.id('link-1')).getAttribute('href')).toBe('');
       
 15130         });
       
 15131 
       
 15132         it('should execute ng-click but not reload when href empty string', function() {
       
 15133           element(by.id('link-2')).click();
       
 15134           expect(element(by.model('value')).getAttribute('value')).toEqual('2');
       
 15135           expect(element(by.id('link-2')).getAttribute('href')).toBe('');
       
 15136         });
       
 15137 
       
 15138         it('should execute ng-click and change url when ng-href specified', function() {
       
 15139           expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
       
 15140 
       
 15141           element(by.id('link-3')).click();
       
 15142 
       
 15143           // At this point, we navigate away from an Angular page, so we need
       
 15144           // to use browser.driver to get the base webdriver.
       
 15145 
       
 15146           browser.wait(function() {
       
 15147             return browser.driver.getCurrentUrl().then(function(url) {
       
 15148               return url.match(/\/123$/);
       
 15149             });
       
 15150           }, 1000, 'page should navigate to /123');
       
 15151         });
       
 15152 
       
 15153         xit('should execute ng-click but not reload when href empty string and name specified', function() {
       
 15154           element(by.id('link-4')).click();
       
 15155           expect(element(by.model('value')).getAttribute('value')).toEqual('4');
       
 15156           expect(element(by.id('link-4')).getAttribute('href')).toBe('');
       
 15157         });
       
 15158 
       
 15159         it('should execute ng-click but not reload when no href but name specified', function() {
       
 15160           element(by.id('link-5')).click();
       
 15161           expect(element(by.model('value')).getAttribute('value')).toEqual('5');
       
 15162           expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
       
 15163         });
       
 15164 
       
 15165         it('should only change url when only ng-href', function() {
       
 15166           element(by.model('value')).clear();
       
 15167           element(by.model('value')).sendKeys('6');
       
 15168           expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
       
 15169 
       
 15170           element(by.id('link-6')).click();
       
 15171 
       
 15172           // At this point, we navigate away from an Angular page, so we need
       
 15173           // to use browser.driver to get the base webdriver.
       
 15174           browser.wait(function() {
       
 15175             return browser.driver.getCurrentUrl().then(function(url) {
       
 15176               return url.match(/\/6$/);
       
 15177             });
       
 15178           }, 1000, 'page should navigate to /6');
       
 15179         });
       
 15180       </file>
       
 15181     </example>
       
 15182  */
       
 15183 
       
 15184 /**
       
 15185  * @ngdoc directive
       
 15186  * @name ngSrc
       
 15187  * @restrict A
       
 15188  * @priority 99
       
 15189  *
       
 15190  * @description
       
 15191  * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
       
 15192  * work right: The browser will fetch from the URL with the literal
       
 15193  * text `{{hash}}` until Angular replaces the expression inside
       
 15194  * `{{hash}}`. The `ngSrc` directive solves this problem.
       
 15195  *
       
 15196  * The buggy way to write it:
       
 15197  * ```html
       
 15198  * <img src="http://www.gravatar.com/avatar/{{hash}}"/>
       
 15199  * ```
       
 15200  *
       
 15201  * The correct way to write it:
       
 15202  * ```html
       
 15203  * <img ng-src="http://www.gravatar.com/avatar/{{hash}}"/>
       
 15204  * ```
       
 15205  *
       
 15206  * @element IMG
       
 15207  * @param {template} ngSrc any string which can contain `{{}}` markup.
       
 15208  */
       
 15209 
       
 15210 /**
       
 15211  * @ngdoc directive
       
 15212  * @name ngSrcset
       
 15213  * @restrict A
       
 15214  * @priority 99
       
 15215  *
       
 15216  * @description
       
 15217  * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
       
 15218  * work right: The browser will fetch from the URL with the literal
       
 15219  * text `{{hash}}` until Angular replaces the expression inside
       
 15220  * `{{hash}}`. The `ngSrcset` directive solves this problem.
       
 15221  *
       
 15222  * The buggy way to write it:
       
 15223  * ```html
       
 15224  * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
       
 15225  * ```
       
 15226  *
       
 15227  * The correct way to write it:
       
 15228  * ```html
       
 15229  * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
       
 15230  * ```
       
 15231  *
       
 15232  * @element IMG
       
 15233  * @param {template} ngSrcset any string which can contain `{{}}` markup.
       
 15234  */
       
 15235 
       
 15236 /**
       
 15237  * @ngdoc directive
       
 15238  * @name ngDisabled
       
 15239  * @restrict A
       
 15240  * @priority 100
       
 15241  *
       
 15242  * @description
       
 15243  *
       
 15244  * The following markup will make the button enabled on Chrome/Firefox but not on IE8 and older IEs:
       
 15245  * ```html
       
 15246  * <div ng-init="scope = { isDisabled: false }">
       
 15247  *  <button disabled="{{scope.isDisabled}}">Disabled</button>
       
 15248  * </div>
       
 15249  * ```
       
 15250  *
       
 15251  * The HTML specification does not require browsers to preserve the values of boolean attributes
       
 15252  * such as disabled. (Their presence means true and their absence means false.)
       
 15253  * If we put an Angular interpolation expression into such an attribute then the
       
 15254  * binding information would be lost when the browser removes the attribute.
       
 15255  * The `ngDisabled` directive solves this problem for the `disabled` attribute.
       
 15256  * This complementary directive is not removed by the browser and so provides
       
 15257  * a permanent reliable place to store the binding information.
       
 15258  *
       
 15259  * @example
       
 15260     <example>
       
 15261       <file name="index.html">
       
 15262         Click me to toggle: <input type="checkbox" ng-model="checked"><br/>
       
 15263         <button ng-model="button" ng-disabled="checked">Button</button>
       
 15264       </file>
       
 15265       <file name="protractor.js" type="protractor">
       
 15266         it('should toggle button', function() {
       
 15267           expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
       
 15268           element(by.model('checked')).click();
       
 15269           expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
       
 15270         });
       
 15271       </file>
       
 15272     </example>
       
 15273  *
       
 15274  * @element INPUT
       
 15275  * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
       
 15276  *     then special attribute "disabled" will be set on the element
       
 15277  */
       
 15278 
       
 15279 
       
 15280 /**
       
 15281  * @ngdoc directive
       
 15282  * @name ngChecked
       
 15283  * @restrict A
       
 15284  * @priority 100
       
 15285  *
       
 15286  * @description
       
 15287  * The HTML specification does not require browsers to preserve the values of boolean attributes
       
 15288  * such as checked. (Their presence means true and their absence means false.)
       
 15289  * If we put an Angular interpolation expression into such an attribute then the
       
 15290  * binding information would be lost when the browser removes the attribute.
       
 15291  * The `ngChecked` directive solves this problem for the `checked` attribute.
       
 15292  * This complementary directive is not removed by the browser and so provides
       
 15293  * a permanent reliable place to store the binding information.
       
 15294  * @example
       
 15295     <example>
       
 15296       <file name="index.html">
       
 15297         Check me to check both: <input type="checkbox" ng-model="master"><br/>
       
 15298         <input id="checkSlave" type="checkbox" ng-checked="master">
       
 15299       </file>
       
 15300       <file name="protractor.js" type="protractor">
       
 15301         it('should check both checkBoxes', function() {
       
 15302           expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
       
 15303           element(by.model('master')).click();
       
 15304           expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
       
 15305         });
       
 15306       </file>
       
 15307     </example>
       
 15308  *
       
 15309  * @element INPUT
       
 15310  * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
       
 15311  *     then special attribute "checked" will be set on the element
       
 15312  */
       
 15313 
       
 15314 
       
 15315 /**
       
 15316  * @ngdoc directive
       
 15317  * @name ngReadonly
       
 15318  * @restrict A
       
 15319  * @priority 100
       
 15320  *
       
 15321  * @description
       
 15322  * The HTML specification does not require browsers to preserve the values of boolean attributes
       
 15323  * such as readonly. (Their presence means true and their absence means false.)
       
 15324  * If we put an Angular interpolation expression into such an attribute then the
       
 15325  * binding information would be lost when the browser removes the attribute.
       
 15326  * The `ngReadonly` directive solves this problem for the `readonly` attribute.
       
 15327  * This complementary directive is not removed by the browser and so provides
       
 15328  * a permanent reliable place to store the binding information.
       
 15329  * @example
       
 15330     <example>
       
 15331       <file name="index.html">
       
 15332         Check me to make text readonly: <input type="checkbox" ng-model="checked"><br/>
       
 15333         <input type="text" ng-readonly="checked" value="I'm Angular"/>
       
 15334       </file>
       
 15335       <file name="protractor.js" type="protractor">
       
 15336         it('should toggle readonly attr', function() {
       
 15337           expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
       
 15338           element(by.model('checked')).click();
       
 15339           expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
       
 15340         });
       
 15341       </file>
       
 15342     </example>
       
 15343  *
       
 15344  * @element INPUT
       
 15345  * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
       
 15346  *     then special attribute "readonly" will be set on the element
       
 15347  */
       
 15348 
       
 15349 
       
 15350 /**
       
 15351  * @ngdoc directive
       
 15352  * @name ngSelected
       
 15353  * @restrict A
       
 15354  * @priority 100
       
 15355  *
       
 15356  * @description
       
 15357  * The HTML specification does not require browsers to preserve the values of boolean attributes
       
 15358  * such as selected. (Their presence means true and their absence means false.)
       
 15359  * If we put an Angular interpolation expression into such an attribute then the
       
 15360  * binding information would be lost when the browser removes the attribute.
       
 15361  * The `ngSelected` directive solves this problem for the `selected` attribute.
       
 15362  * This complementary directive is not removed by the browser and so provides
       
 15363  * a permanent reliable place to store the binding information.
       
 15364  *
       
 15365  * @example
       
 15366     <example>
       
 15367       <file name="index.html">
       
 15368         Check me to select: <input type="checkbox" ng-model="selected"><br/>
       
 15369         <select>
       
 15370           <option>Hello!</option>
       
 15371           <option id="greet" ng-selected="selected">Greetings!</option>
       
 15372         </select>
       
 15373       </file>
       
 15374       <file name="protractor.js" type="protractor">
       
 15375         it('should select Greetings!', function() {
       
 15376           expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
       
 15377           element(by.model('selected')).click();
       
 15378           expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
       
 15379         });
       
 15380       </file>
       
 15381     </example>
       
 15382  *
       
 15383  * @element OPTION
       
 15384  * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
       
 15385  *     then special attribute "selected" will be set on the element
       
 15386  */
       
 15387 
       
 15388 /**
       
 15389  * @ngdoc directive
       
 15390  * @name ngOpen
       
 15391  * @restrict A
       
 15392  * @priority 100
       
 15393  *
       
 15394  * @description
       
 15395  * The HTML specification does not require browsers to preserve the values of boolean attributes
       
 15396  * such as open. (Their presence means true and their absence means false.)
       
 15397  * If we put an Angular interpolation expression into such an attribute then the
       
 15398  * binding information would be lost when the browser removes the attribute.
       
 15399  * The `ngOpen` directive solves this problem for the `open` attribute.
       
 15400  * This complementary directive is not removed by the browser and so provides
       
 15401  * a permanent reliable place to store the binding information.
       
 15402  * @example
       
 15403      <example>
       
 15404        <file name="index.html">
       
 15405          Check me check multiple: <input type="checkbox" ng-model="open"><br/>
       
 15406          <details id="details" ng-open="open">
       
 15407             <summary>Show/Hide me</summary>
       
 15408          </details>
       
 15409        </file>
       
 15410        <file name="protractor.js" type="protractor">
       
 15411          it('should toggle open', function() {
       
 15412            expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
       
 15413            element(by.model('open')).click();
       
 15414            expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
       
 15415          });
       
 15416        </file>
       
 15417      </example>
       
 15418  *
       
 15419  * @element DETAILS
       
 15420  * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
       
 15421  *     then special attribute "open" will be set on the element
       
 15422  */
       
 15423 
       
 15424 var ngAttributeAliasDirectives = {};
       
 15425 
       
 15426 
       
 15427 // boolean attrs are evaluated
       
 15428 forEach(BOOLEAN_ATTR, function(propName, attrName) {
       
 15429   // binding to multiple is not supported
       
 15430   if (propName == "multiple") return;
       
 15431 
       
 15432   var normalized = directiveNormalize('ng-' + attrName);
       
 15433   ngAttributeAliasDirectives[normalized] = function() {
       
 15434     return {
       
 15435       priority: 100,
       
 15436       link: function(scope, element, attr) {
       
 15437         scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
       
 15438           attr.$set(attrName, !!value);
       
 15439         });
       
 15440       }
       
 15441     };
       
 15442   };
       
 15443 });
       
 15444 
       
 15445 
       
 15446 // ng-src, ng-srcset, ng-href are interpolated
       
 15447 forEach(['src', 'srcset', 'href'], function(attrName) {
       
 15448   var normalized = directiveNormalize('ng-' + attrName);
       
 15449   ngAttributeAliasDirectives[normalized] = function() {
       
 15450     return {
       
 15451       priority: 99, // it needs to run after the attributes are interpolated
       
 15452       link: function(scope, element, attr) {
       
 15453         var propName = attrName,
       
 15454             name = attrName;
       
 15455 
       
 15456         if (attrName === 'href' &&
       
 15457             toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
       
 15458           name = 'xlinkHref';
       
 15459           attr.$attr[name] = 'xlink:href';
       
 15460           propName = null;
       
 15461         }
       
 15462 
       
 15463         attr.$observe(normalized, function(value) {
       
 15464           if (!value)
       
 15465              return;
       
 15466 
       
 15467           attr.$set(name, value);
       
 15468 
       
 15469           // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
       
 15470           // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
       
 15471           // to set the property as well to achieve the desired effect.
       
 15472           // we use attr[attrName] value since $set can sanitize the url.
       
 15473           if (msie && propName) element.prop(propName, attr[name]);
       
 15474         });
       
 15475       }
       
 15476     };
       
 15477   };
       
 15478 });
       
 15479 
       
 15480 /* global -nullFormCtrl */
       
 15481 var nullFormCtrl = {
       
 15482   $addControl: noop,
       
 15483   $removeControl: noop,
       
 15484   $setValidity: noop,
       
 15485   $setDirty: noop,
       
 15486   $setPristine: noop
       
 15487 };
       
 15488 
       
 15489 /**
       
 15490  * @ngdoc type
       
 15491  * @name form.FormController
       
 15492  *
       
 15493  * @property {boolean} $pristine True if user has not interacted with the form yet.
       
 15494  * @property {boolean} $dirty True if user has already interacted with the form.
       
 15495  * @property {boolean} $valid True if all of the containing forms and controls are valid.
       
 15496  * @property {boolean} $invalid True if at least one containing control or form is invalid.
       
 15497  *
       
 15498  * @property {Object} $error Is an object hash, containing references to all invalid controls or
       
 15499  *  forms, where:
       
 15500  *
       
 15501  *  - keys are validation tokens (error names),
       
 15502  *  - values are arrays of controls or forms that are invalid for given error name.
       
 15503  *
       
 15504  *
       
 15505  *  Built-in validation tokens:
       
 15506  *
       
 15507  *  - `email`
       
 15508  *  - `max`
       
 15509  *  - `maxlength`
       
 15510  *  - `min`
       
 15511  *  - `minlength`
       
 15512  *  - `number`
       
 15513  *  - `pattern`
       
 15514  *  - `required`
       
 15515  *  - `url`
       
 15516  *
       
 15517  * @description
       
 15518  * `FormController` keeps track of all its controls and nested forms as well as state of them,
       
 15519  * such as being valid/invalid or dirty/pristine.
       
 15520  *
       
 15521  * Each {@link ng.directive:form form} directive creates an instance
       
 15522  * of `FormController`.
       
 15523  *
       
 15524  */
       
 15525 //asks for $scope to fool the BC controller module
       
 15526 FormController.$inject = ['$element', '$attrs', '$scope', '$animate'];
       
 15527 function FormController(element, attrs, $scope, $animate) {
       
 15528   var form = this,
       
 15529       parentForm = element.parent().controller('form') || nullFormCtrl,
       
 15530       invalidCount = 0, // used to easily determine if we are valid
       
 15531       errors = form.$error = {},
       
 15532       controls = [];
       
 15533 
       
 15534   // init state
       
 15535   form.$name = attrs.name || attrs.ngForm;
       
 15536   form.$dirty = false;
       
 15537   form.$pristine = true;
       
 15538   form.$valid = true;
       
 15539   form.$invalid = false;
       
 15540 
       
 15541   parentForm.$addControl(form);
       
 15542 
       
 15543   // Setup initial state of the control
       
 15544   element.addClass(PRISTINE_CLASS);
       
 15545   toggleValidCss(true);
       
 15546 
       
 15547   // convenience method for easy toggling of classes
       
 15548   function toggleValidCss(isValid, validationErrorKey) {
       
 15549     validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
       
 15550     $animate.removeClass(element, (isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey);
       
 15551     $animate.addClass(element, (isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey);
       
 15552   }
       
 15553 
       
 15554   /**
       
 15555    * @ngdoc method
       
 15556    * @name form.FormController#$addControl
       
 15557    *
       
 15558    * @description
       
 15559    * Register a control with the form.
       
 15560    *
       
 15561    * Input elements using ngModelController do this automatically when they are linked.
       
 15562    */
       
 15563   form.$addControl = function(control) {
       
 15564     // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
       
 15565     // and not added to the scope.  Now we throw an error.
       
 15566     assertNotHasOwnProperty(control.$name, 'input');
       
 15567     controls.push(control);
       
 15568 
       
 15569     if (control.$name) {
       
 15570       form[control.$name] = control;
       
 15571     }
       
 15572   };
       
 15573 
       
 15574   /**
       
 15575    * @ngdoc method
       
 15576    * @name form.FormController#$removeControl
       
 15577    *
       
 15578    * @description
       
 15579    * Deregister a control from the form.
       
 15580    *
       
 15581    * Input elements using ngModelController do this automatically when they are destroyed.
       
 15582    */
       
 15583   form.$removeControl = function(control) {
       
 15584     if (control.$name && form[control.$name] === control) {
       
 15585       delete form[control.$name];
       
 15586     }
       
 15587     forEach(errors, function(queue, validationToken) {
       
 15588       form.$setValidity(validationToken, true, control);
       
 15589     });
       
 15590 
       
 15591     arrayRemove(controls, control);
       
 15592   };
       
 15593 
       
 15594   /**
       
 15595    * @ngdoc method
       
 15596    * @name form.FormController#$setValidity
       
 15597    *
       
 15598    * @description
       
 15599    * Sets the validity of a form control.
       
 15600    *
       
 15601    * This method will also propagate to parent forms.
       
 15602    */
       
 15603   form.$setValidity = function(validationToken, isValid, control) {
       
 15604     var queue = errors[validationToken];
       
 15605 
       
 15606     if (isValid) {
       
 15607       if (queue) {
       
 15608         arrayRemove(queue, control);
       
 15609         if (!queue.length) {
       
 15610           invalidCount--;
       
 15611           if (!invalidCount) {
       
 15612             toggleValidCss(isValid);
       
 15613             form.$valid = true;
       
 15614             form.$invalid = false;
       
 15615           }
       
 15616           errors[validationToken] = false;
       
 15617           toggleValidCss(true, validationToken);
       
 15618           parentForm.$setValidity(validationToken, true, form);
       
 15619         }
       
 15620       }
       
 15621 
       
 15622     } else {
       
 15623       if (!invalidCount) {
       
 15624         toggleValidCss(isValid);
       
 15625       }
       
 15626       if (queue) {
       
 15627         if (includes(queue, control)) return;
       
 15628       } else {
       
 15629         errors[validationToken] = queue = [];
       
 15630         invalidCount++;
       
 15631         toggleValidCss(false, validationToken);
       
 15632         parentForm.$setValidity(validationToken, false, form);
       
 15633       }
       
 15634       queue.push(control);
       
 15635 
       
 15636       form.$valid = false;
       
 15637       form.$invalid = true;
       
 15638     }
       
 15639   };
       
 15640 
       
 15641   /**
       
 15642    * @ngdoc method
       
 15643    * @name form.FormController#$setDirty
       
 15644    *
       
 15645    * @description
       
 15646    * Sets the form to a dirty state.
       
 15647    *
       
 15648    * This method can be called to add the 'ng-dirty' class and set the form to a dirty
       
 15649    * state (ng-dirty class). This method will also propagate to parent forms.
       
 15650    */
       
 15651   form.$setDirty = function() {
       
 15652     $animate.removeClass(element, PRISTINE_CLASS);
       
 15653     $animate.addClass(element, DIRTY_CLASS);
       
 15654     form.$dirty = true;
       
 15655     form.$pristine = false;
       
 15656     parentForm.$setDirty();
       
 15657   };
       
 15658 
       
 15659   /**
       
 15660    * @ngdoc method
       
 15661    * @name form.FormController#$setPristine
       
 15662    *
       
 15663    * @description
       
 15664    * Sets the form to its pristine state.
       
 15665    *
       
 15666    * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
       
 15667    * state (ng-pristine class). This method will also propagate to all the controls contained
       
 15668    * in this form.
       
 15669    *
       
 15670    * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
       
 15671    * saving or resetting it.
       
 15672    */
       
 15673   form.$setPristine = function () {
       
 15674     $animate.removeClass(element, DIRTY_CLASS);
       
 15675     $animate.addClass(element, PRISTINE_CLASS);
       
 15676     form.$dirty = false;
       
 15677     form.$pristine = true;
       
 15678     forEach(controls, function(control) {
       
 15679       control.$setPristine();
       
 15680     });
       
 15681   };
       
 15682 }
       
 15683 
       
 15684 
       
 15685 /**
       
 15686  * @ngdoc directive
       
 15687  * @name ngForm
       
 15688  * @restrict EAC
       
 15689  *
       
 15690  * @description
       
 15691  * Nestable alias of {@link ng.directive:form `form`} directive. HTML
       
 15692  * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
       
 15693  * sub-group of controls needs to be determined.
       
 15694  *
       
 15695  * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
       
 15696  *                       related scope, under this name.
       
 15697  *
       
 15698  */
       
 15699 
       
 15700  /**
       
 15701  * @ngdoc directive
       
 15702  * @name form
       
 15703  * @restrict E
       
 15704  *
       
 15705  * @description
       
 15706  * Directive that instantiates
       
 15707  * {@link form.FormController FormController}.
       
 15708  *
       
 15709  * If the `name` attribute is specified, the form controller is published onto the current scope under
       
 15710  * this name.
       
 15711  *
       
 15712  * # Alias: {@link ng.directive:ngForm `ngForm`}
       
 15713  *
       
 15714  * In Angular forms can be nested. This means that the outer form is valid when all of the child
       
 15715  * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
       
 15716  * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
       
 15717  * `<form>` but can be nested.  This allows you to have nested forms, which is very useful when
       
 15718  * using Angular validation directives in forms that are dynamically generated using the
       
 15719  * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
       
 15720  * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
       
 15721  * `ngForm` directive and nest these in an outer `form` element.
       
 15722  *
       
 15723  *
       
 15724  * # CSS classes
       
 15725  *  - `ng-valid` is set if the form is valid.
       
 15726  *  - `ng-invalid` is set if the form is invalid.
       
 15727  *  - `ng-pristine` is set if the form is pristine.
       
 15728  *  - `ng-dirty` is set if the form is dirty.
       
 15729  *
       
 15730  * Keep in mind that ngAnimate can detect each of these classes when added and removed.
       
 15731  *
       
 15732  *
       
 15733  * # Submitting a form and preventing the default action
       
 15734  *
       
 15735  * Since the role of forms in client-side Angular applications is different than in classical
       
 15736  * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
       
 15737  * page reload that sends the data to the server. Instead some javascript logic should be triggered
       
 15738  * to handle the form submission in an application-specific way.
       
 15739  *
       
 15740  * For this reason, Angular prevents the default action (form submission to the server) unless the
       
 15741  * `<form>` element has an `action` attribute specified.
       
 15742  *
       
 15743  * You can use one of the following two ways to specify what javascript method should be called when
       
 15744  * a form is submitted:
       
 15745  *
       
 15746  * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
       
 15747  * - {@link ng.directive:ngClick ngClick} directive on the first
       
 15748   *  button or input field of type submit (input[type=submit])
       
 15749  *
       
 15750  * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
       
 15751  * or {@link ng.directive:ngClick ngClick} directives.
       
 15752  * This is because of the following form submission rules in the HTML specification:
       
 15753  *
       
 15754  * - If a form has only one input field then hitting enter in this field triggers form submit
       
 15755  * (`ngSubmit`)
       
 15756  * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
       
 15757  * doesn't trigger submit
       
 15758  * - if a form has one or more input fields and one or more buttons or input[type=submit] then
       
 15759  * hitting enter in any of the input fields will trigger the click handler on the *first* button or
       
 15760  * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
       
 15761  *
       
 15762  * @param {string=} name Name of the form. If specified, the form controller will be published into
       
 15763  *                       related scope, under this name.
       
 15764  *
       
 15765  * ## Animation Hooks
       
 15766  *
       
 15767  * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
       
 15768  * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
       
 15769  * other validations that are performed within the form. Animations in ngForm are similar to how
       
 15770  * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
       
 15771  * as JS animations.
       
 15772  *
       
 15773  * The following example shows a simple way to utilize CSS transitions to style a form element
       
 15774  * that has been rendered as invalid after it has been validated:
       
 15775  *
       
 15776  * <pre>
       
 15777  * //be sure to include ngAnimate as a module to hook into more
       
 15778  * //advanced animations
       
 15779  * .my-form {
       
 15780  *   transition:0.5s linear all;
       
 15781  *   background: white;
       
 15782  * }
       
 15783  * .my-form.ng-invalid {
       
 15784  *   background: red;
       
 15785  *   color:white;
       
 15786  * }
       
 15787  * </pre>
       
 15788  *
       
 15789  * @example
       
 15790     <example deps="angular-animate.js" animations="true" fixBase="true">
       
 15791       <file name="index.html">
       
 15792        <script>
       
 15793          function Ctrl($scope) {
       
 15794            $scope.userType = 'guest';
       
 15795          }
       
 15796        </script>
       
 15797        <style>
       
 15798         .my-form {
       
 15799           -webkit-transition:all linear 0.5s;
       
 15800           transition:all linear 0.5s;
       
 15801           background: transparent;
       
 15802         }
       
 15803         .my-form.ng-invalid {
       
 15804           background: red;
       
 15805         }
       
 15806        </style>
       
 15807        <form name="myForm" ng-controller="Ctrl" class="my-form">
       
 15808          userType: <input name="input" ng-model="userType" required>
       
 15809          <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
       
 15810          <tt>userType = {{userType}}</tt><br>
       
 15811          <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br>
       
 15812          <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br>
       
 15813          <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
       
 15814          <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
       
 15815         </form>
       
 15816       </file>
       
 15817       <file name="protractor.js" type="protractor">
       
 15818         it('should initialize to model', function() {
       
 15819           var userType = element(by.binding('userType'));
       
 15820           var valid = element(by.binding('myForm.input.$valid'));
       
 15821 
       
 15822           expect(userType.getText()).toContain('guest');
       
 15823           expect(valid.getText()).toContain('true');
       
 15824         });
       
 15825 
       
 15826         it('should be invalid if empty', function() {
       
 15827           var userType = element(by.binding('userType'));
       
 15828           var valid = element(by.binding('myForm.input.$valid'));
       
 15829           var userInput = element(by.model('userType'));
       
 15830 
       
 15831           userInput.clear();
       
 15832           userInput.sendKeys('');
       
 15833 
       
 15834           expect(userType.getText()).toEqual('userType =');
       
 15835           expect(valid.getText()).toContain('false');
       
 15836         });
       
 15837       </file>
       
 15838     </example>
       
 15839  *
       
 15840  */
       
 15841 var formDirectiveFactory = function(isNgForm) {
       
 15842   return ['$timeout', function($timeout) {
       
 15843     var formDirective = {
       
 15844       name: 'form',
       
 15845       restrict: isNgForm ? 'EAC' : 'E',
       
 15846       controller: FormController,
       
 15847       compile: function() {
       
 15848         return {
       
 15849           pre: function(scope, formElement, attr, controller) {
       
 15850             if (!attr.action) {
       
 15851               // we can't use jq events because if a form is destroyed during submission the default
       
 15852               // action is not prevented. see #1238
       
 15853               //
       
 15854               // IE 9 is not affected because it doesn't fire a submit event and try to do a full
       
 15855               // page reload if the form was destroyed by submission of the form via a click handler
       
 15856               // on a button in the form. Looks like an IE9 specific bug.
       
 15857               var preventDefaultListener = function(event) {
       
 15858                 event.preventDefault
       
 15859                   ? event.preventDefault()
       
 15860                   : event.returnValue = false; // IE
       
 15861               };
       
 15862 
       
 15863               addEventListenerFn(formElement[0], 'submit', preventDefaultListener);
       
 15864 
       
 15865               // unregister the preventDefault listener so that we don't not leak memory but in a
       
 15866               // way that will achieve the prevention of the default action.
       
 15867               formElement.on('$destroy', function() {
       
 15868                 $timeout(function() {
       
 15869                   removeEventListenerFn(formElement[0], 'submit', preventDefaultListener);
       
 15870                 }, 0, false);
       
 15871               });
       
 15872             }
       
 15873 
       
 15874             var parentFormCtrl = formElement.parent().controller('form'),
       
 15875                 alias = attr.name || attr.ngForm;
       
 15876 
       
 15877             if (alias) {
       
 15878               setter(scope, alias, controller, alias);
       
 15879             }
       
 15880             if (parentFormCtrl) {
       
 15881               formElement.on('$destroy', function() {
       
 15882                 parentFormCtrl.$removeControl(controller);
       
 15883                 if (alias) {
       
 15884                   setter(scope, alias, undefined, alias);
       
 15885                 }
       
 15886                 extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
       
 15887               });
       
 15888             }
       
 15889           }
       
 15890         };
       
 15891       }
       
 15892     };
       
 15893 
       
 15894     return formDirective;
       
 15895   }];
       
 15896 };
       
 15897 
       
 15898 var formDirective = formDirectiveFactory();
       
 15899 var ngFormDirective = formDirectiveFactory(true);
       
 15900 
       
 15901 /* global
       
 15902 
       
 15903     -VALID_CLASS,
       
 15904     -INVALID_CLASS,
       
 15905     -PRISTINE_CLASS,
       
 15906     -DIRTY_CLASS
       
 15907 */
       
 15908 
       
 15909 var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
       
 15910 var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@[a-z0-9-]+(\.[a-z0-9-]+)*$/i;
       
 15911 var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
       
 15912 
       
 15913 var inputType = {
       
 15914 
       
 15915   /**
       
 15916    * @ngdoc input
       
 15917    * @name input[text]
       
 15918    *
       
 15919    * @description
       
 15920    * Standard HTML text input with angular data binding.
       
 15921    *
       
 15922    * @param {string} ngModel Assignable angular expression to data-bind to.
       
 15923    * @param {string=} name Property name of the form under which the control is published.
       
 15924    * @param {string=} required Adds `required` validation error key if the value is not entered.
       
 15925    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
       
 15926    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
       
 15927    *    `required` when you want to data-bind to the `required` attribute.
       
 15928    * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
       
 15929    *    minlength.
       
 15930    * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
       
 15931    *    maxlength.
       
 15932    * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
       
 15933    *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
       
 15934    *    patterns defined as scope expressions.
       
 15935    * @param {string=} ngChange Angular expression to be executed when input changes due to user
       
 15936    *    interaction with the input element.
       
 15937    * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
       
 15938    *
       
 15939    * @example
       
 15940       <example name="text-input-directive">
       
 15941         <file name="index.html">
       
 15942          <script>
       
 15943            function Ctrl($scope) {
       
 15944              $scope.text = 'guest';
       
 15945              $scope.word = /^\s*\w*\s*$/;
       
 15946            }
       
 15947          </script>
       
 15948          <form name="myForm" ng-controller="Ctrl">
       
 15949            Single word: <input type="text" name="input" ng-model="text"
       
 15950                                ng-pattern="word" required ng-trim="false">
       
 15951            <span class="error" ng-show="myForm.input.$error.required">
       
 15952              Required!</span>
       
 15953            <span class="error" ng-show="myForm.input.$error.pattern">
       
 15954              Single word only!</span>
       
 15955 
       
 15956            <tt>text = {{text}}</tt><br/>
       
 15957            <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
       
 15958            <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
       
 15959            <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
       
 15960            <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
       
 15961           </form>
       
 15962         </file>
       
 15963         <file name="protractor.js" type="protractor">
       
 15964           var text = element(by.binding('text'));
       
 15965           var valid = element(by.binding('myForm.input.$valid'));
       
 15966           var input = element(by.model('text'));
       
 15967 
       
 15968           it('should initialize to model', function() {
       
 15969             expect(text.getText()).toContain('guest');
       
 15970             expect(valid.getText()).toContain('true');
       
 15971           });
       
 15972 
       
 15973           it('should be invalid if empty', function() {
       
 15974             input.clear();
       
 15975             input.sendKeys('');
       
 15976 
       
 15977             expect(text.getText()).toEqual('text =');
       
 15978             expect(valid.getText()).toContain('false');
       
 15979           });
       
 15980 
       
 15981           it('should be invalid if multi word', function() {
       
 15982             input.clear();
       
 15983             input.sendKeys('hello world');
       
 15984 
       
 15985             expect(valid.getText()).toContain('false');
       
 15986           });
       
 15987         </file>
       
 15988       </example>
       
 15989    */
       
 15990   'text': textInputType,
       
 15991 
       
 15992 
       
 15993   /**
       
 15994    * @ngdoc input
       
 15995    * @name input[number]
       
 15996    *
       
 15997    * @description
       
 15998    * Text input with number validation and transformation. Sets the `number` validation
       
 15999    * error if not a valid number.
       
 16000    *
       
 16001    * @param {string} ngModel Assignable angular expression to data-bind to.
       
 16002    * @param {string=} name Property name of the form under which the control is published.
       
 16003    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
       
 16004    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
       
 16005    * @param {string=} required Sets `required` validation error key if the value is not entered.
       
 16006    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
       
 16007    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
       
 16008    *    `required` when you want to data-bind to the `required` attribute.
       
 16009    * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
       
 16010    *    minlength.
       
 16011    * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
       
 16012    *    maxlength.
       
 16013    * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
       
 16014    *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
       
 16015    *    patterns defined as scope expressions.
       
 16016    * @param {string=} ngChange Angular expression to be executed when input changes due to user
       
 16017    *    interaction with the input element.
       
 16018    *
       
 16019    * @example
       
 16020       <example name="number-input-directive">
       
 16021         <file name="index.html">
       
 16022          <script>
       
 16023            function Ctrl($scope) {
       
 16024              $scope.value = 12;
       
 16025            }
       
 16026          </script>
       
 16027          <form name="myForm" ng-controller="Ctrl">
       
 16028            Number: <input type="number" name="input" ng-model="value"
       
 16029                           min="0" max="99" required>
       
 16030            <span class="error" ng-show="myForm.input.$error.required">
       
 16031              Required!</span>
       
 16032            <span class="error" ng-show="myForm.input.$error.number">
       
 16033              Not valid number!</span>
       
 16034            <tt>value = {{value}}</tt><br/>
       
 16035            <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
       
 16036            <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
       
 16037            <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
       
 16038            <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
       
 16039           </form>
       
 16040         </file>
       
 16041         <file name="protractor.js" type="protractor">
       
 16042           var value = element(by.binding('value'));
       
 16043           var valid = element(by.binding('myForm.input.$valid'));
       
 16044           var input = element(by.model('value'));
       
 16045 
       
 16046           it('should initialize to model', function() {
       
 16047             expect(value.getText()).toContain('12');
       
 16048             expect(valid.getText()).toContain('true');
       
 16049           });
       
 16050 
       
 16051           it('should be invalid if empty', function() {
       
 16052             input.clear();
       
 16053             input.sendKeys('');
       
 16054             expect(value.getText()).toEqual('value =');
       
 16055             expect(valid.getText()).toContain('false');
       
 16056           });
       
 16057 
       
 16058           it('should be invalid if over max', function() {
       
 16059             input.clear();
       
 16060             input.sendKeys('123');
       
 16061             expect(value.getText()).toEqual('value =');
       
 16062             expect(valid.getText()).toContain('false');
       
 16063           });
       
 16064         </file>
       
 16065       </example>
       
 16066    */
       
 16067   'number': numberInputType,
       
 16068 
       
 16069 
       
 16070   /**
       
 16071    * @ngdoc input
       
 16072    * @name input[url]
       
 16073    *
       
 16074    * @description
       
 16075    * Text input with URL validation. Sets the `url` validation error key if the content is not a
       
 16076    * valid URL.
       
 16077    *
       
 16078    * @param {string} ngModel Assignable angular expression to data-bind to.
       
 16079    * @param {string=} name Property name of the form under which the control is published.
       
 16080    * @param {string=} required Sets `required` validation error key if the value is not entered.
       
 16081    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
       
 16082    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
       
 16083    *    `required` when you want to data-bind to the `required` attribute.
       
 16084    * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
       
 16085    *    minlength.
       
 16086    * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
       
 16087    *    maxlength.
       
 16088    * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
       
 16089    *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
       
 16090    *    patterns defined as scope expressions.
       
 16091    * @param {string=} ngChange Angular expression to be executed when input changes due to user
       
 16092    *    interaction with the input element.
       
 16093    *
       
 16094    * @example
       
 16095       <example name="url-input-directive">
       
 16096         <file name="index.html">
       
 16097          <script>
       
 16098            function Ctrl($scope) {
       
 16099              $scope.text = 'http://google.com';
       
 16100            }
       
 16101          </script>
       
 16102          <form name="myForm" ng-controller="Ctrl">
       
 16103            URL: <input type="url" name="input" ng-model="text" required>
       
 16104            <span class="error" ng-show="myForm.input.$error.required">
       
 16105              Required!</span>
       
 16106            <span class="error" ng-show="myForm.input.$error.url">
       
 16107              Not valid url!</span>
       
 16108            <tt>text = {{text}}</tt><br/>
       
 16109            <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
       
 16110            <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
       
 16111            <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
       
 16112            <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
       
 16113            <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
       
 16114           </form>
       
 16115         </file>
       
 16116         <file name="protractor.js" type="protractor">
       
 16117           var text = element(by.binding('text'));
       
 16118           var valid = element(by.binding('myForm.input.$valid'));
       
 16119           var input = element(by.model('text'));
       
 16120 
       
 16121           it('should initialize to model', function() {
       
 16122             expect(text.getText()).toContain('http://google.com');
       
 16123             expect(valid.getText()).toContain('true');
       
 16124           });
       
 16125 
       
 16126           it('should be invalid if empty', function() {
       
 16127             input.clear();
       
 16128             input.sendKeys('');
       
 16129 
       
 16130             expect(text.getText()).toEqual('text =');
       
 16131             expect(valid.getText()).toContain('false');
       
 16132           });
       
 16133 
       
 16134           it('should be invalid if not url', function() {
       
 16135             input.clear();
       
 16136             input.sendKeys('box');
       
 16137 
       
 16138             expect(valid.getText()).toContain('false');
       
 16139           });
       
 16140         </file>
       
 16141       </example>
       
 16142    */
       
 16143   'url': urlInputType,
       
 16144 
       
 16145 
       
 16146   /**
       
 16147    * @ngdoc input
       
 16148    * @name input[email]
       
 16149    *
       
 16150    * @description
       
 16151    * Text input with email validation. Sets the `email` validation error key if not a valid email
       
 16152    * address.
       
 16153    *
       
 16154    * @param {string} ngModel Assignable angular expression to data-bind to.
       
 16155    * @param {string=} name Property name of the form under which the control is published.
       
 16156    * @param {string=} required Sets `required` validation error key if the value is not entered.
       
 16157    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
       
 16158    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
       
 16159    *    `required` when you want to data-bind to the `required` attribute.
       
 16160    * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
       
 16161    *    minlength.
       
 16162    * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
       
 16163    *    maxlength.
       
 16164    * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
       
 16165    *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
       
 16166    *    patterns defined as scope expressions.
       
 16167    * @param {string=} ngChange Angular expression to be executed when input changes due to user
       
 16168    *    interaction with the input element.
       
 16169    *
       
 16170    * @example
       
 16171       <example name="email-input-directive">
       
 16172         <file name="index.html">
       
 16173          <script>
       
 16174            function Ctrl($scope) {
       
 16175              $scope.text = 'me@example.com';
       
 16176            }
       
 16177          </script>
       
 16178            <form name="myForm" ng-controller="Ctrl">
       
 16179              Email: <input type="email" name="input" ng-model="text" required>
       
 16180              <span class="error" ng-show="myForm.input.$error.required">
       
 16181                Required!</span>
       
 16182              <span class="error" ng-show="myForm.input.$error.email">
       
 16183                Not valid email!</span>
       
 16184              <tt>text = {{text}}</tt><br/>
       
 16185              <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
       
 16186              <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
       
 16187              <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
       
 16188              <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
       
 16189              <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
       
 16190            </form>
       
 16191          </file>
       
 16192         <file name="protractor.js" type="protractor">
       
 16193           var text = element(by.binding('text'));
       
 16194           var valid = element(by.binding('myForm.input.$valid'));
       
 16195           var input = element(by.model('text'));
       
 16196 
       
 16197           it('should initialize to model', function() {
       
 16198             expect(text.getText()).toContain('me@example.com');
       
 16199             expect(valid.getText()).toContain('true');
       
 16200           });
       
 16201 
       
 16202           it('should be invalid if empty', function() {
       
 16203             input.clear();
       
 16204             input.sendKeys('');
       
 16205             expect(text.getText()).toEqual('text =');
       
 16206             expect(valid.getText()).toContain('false');
       
 16207           });
       
 16208 
       
 16209           it('should be invalid if not email', function() {
       
 16210             input.clear();
       
 16211             input.sendKeys('xxx');
       
 16212 
       
 16213             expect(valid.getText()).toContain('false');
       
 16214           });
       
 16215         </file>
       
 16216       </example>
       
 16217    */
       
 16218   'email': emailInputType,
       
 16219 
       
 16220 
       
 16221   /**
       
 16222    * @ngdoc input
       
 16223    * @name input[radio]
       
 16224    *
       
 16225    * @description
       
 16226    * HTML radio button.
       
 16227    *
       
 16228    * @param {string} ngModel Assignable angular expression to data-bind to.
       
 16229    * @param {string} value The value to which the expression should be set when selected.
       
 16230    * @param {string=} name Property name of the form under which the control is published.
       
 16231    * @param {string=} ngChange Angular expression to be executed when input changes due to user
       
 16232    *    interaction with the input element.
       
 16233    * @param {string} ngValue Angular expression which sets the value to which the expression should
       
 16234    *    be set when selected.
       
 16235    *
       
 16236    * @example
       
 16237       <example name="radio-input-directive">
       
 16238         <file name="index.html">
       
 16239          <script>
       
 16240            function Ctrl($scope) {
       
 16241              $scope.color = 'blue';
       
 16242              $scope.specialValue = {
       
 16243                "id": "12345",
       
 16244                "value": "green"
       
 16245              };
       
 16246            }
       
 16247          </script>
       
 16248          <form name="myForm" ng-controller="Ctrl">
       
 16249            <input type="radio" ng-model="color" value="red">  Red <br/>
       
 16250            <input type="radio" ng-model="color" ng-value="specialValue"> Green <br/>
       
 16251            <input type="radio" ng-model="color" value="blue"> Blue <br/>
       
 16252            <tt>color = {{color | json}}</tt><br/>
       
 16253           </form>
       
 16254           Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
       
 16255         </file>
       
 16256         <file name="protractor.js" type="protractor">
       
 16257           it('should change state', function() {
       
 16258             var color = element(by.binding('color'));
       
 16259 
       
 16260             expect(color.getText()).toContain('blue');
       
 16261 
       
 16262             element.all(by.model('color')).get(0).click();
       
 16263 
       
 16264             expect(color.getText()).toContain('red');
       
 16265           });
       
 16266         </file>
       
 16267       </example>
       
 16268    */
       
 16269   'radio': radioInputType,
       
 16270 
       
 16271 
       
 16272   /**
       
 16273    * @ngdoc input
       
 16274    * @name input[checkbox]
       
 16275    *
       
 16276    * @description
       
 16277    * HTML checkbox.
       
 16278    *
       
 16279    * @param {string} ngModel Assignable angular expression to data-bind to.
       
 16280    * @param {string=} name Property name of the form under which the control is published.
       
 16281    * @param {string=} ngTrueValue The value to which the expression should be set when selected.
       
 16282    * @param {string=} ngFalseValue The value to which the expression should be set when not selected.
       
 16283    * @param {string=} ngChange Angular expression to be executed when input changes due to user
       
 16284    *    interaction with the input element.
       
 16285    *
       
 16286    * @example
       
 16287       <example name="checkbox-input-directive">
       
 16288         <file name="index.html">
       
 16289          <script>
       
 16290            function Ctrl($scope) {
       
 16291              $scope.value1 = true;
       
 16292              $scope.value2 = 'YES'
       
 16293            }
       
 16294          </script>
       
 16295          <form name="myForm" ng-controller="Ctrl">
       
 16296            Value1: <input type="checkbox" ng-model="value1"> <br/>
       
 16297            Value2: <input type="checkbox" ng-model="value2"
       
 16298                           ng-true-value="YES" ng-false-value="NO"> <br/>
       
 16299            <tt>value1 = {{value1}}</tt><br/>
       
 16300            <tt>value2 = {{value2}}</tt><br/>
       
 16301           </form>
       
 16302         </file>
       
 16303         <file name="protractor.js" type="protractor">
       
 16304           it('should change state', function() {
       
 16305             var value1 = element(by.binding('value1'));
       
 16306             var value2 = element(by.binding('value2'));
       
 16307 
       
 16308             expect(value1.getText()).toContain('true');
       
 16309             expect(value2.getText()).toContain('YES');
       
 16310 
       
 16311             element(by.model('value1')).click();
       
 16312             element(by.model('value2')).click();
       
 16313 
       
 16314             expect(value1.getText()).toContain('false');
       
 16315             expect(value2.getText()).toContain('NO');
       
 16316           });
       
 16317         </file>
       
 16318       </example>
       
 16319    */
       
 16320   'checkbox': checkboxInputType,
       
 16321 
       
 16322   'hidden': noop,
       
 16323   'button': noop,
       
 16324   'submit': noop,
       
 16325   'reset': noop,
       
 16326   'file': noop
       
 16327 };
       
 16328 
       
 16329 // A helper function to call $setValidity and return the value / undefined,
       
 16330 // a pattern that is repeated a lot in the input validation logic.
       
 16331 function validate(ctrl, validatorName, validity, value){
       
 16332   ctrl.$setValidity(validatorName, validity);
       
 16333   return validity ? value : undefined;
       
 16334 }
       
 16335 
       
 16336 
       
 16337 function addNativeHtml5Validators(ctrl, validatorName, element) {
       
 16338   var validity = element.prop('validity');
       
 16339   if (isObject(validity)) {
       
 16340     var validator = function(value) {
       
 16341       // Don't overwrite previous validation, don't consider valueMissing to apply (ng-required can
       
 16342       // perform the required validation)
       
 16343       if (!ctrl.$error[validatorName] && (validity.badInput || validity.customError ||
       
 16344           validity.typeMismatch) && !validity.valueMissing) {
       
 16345         ctrl.$setValidity(validatorName, false);
       
 16346         return;
       
 16347       }
       
 16348       return value;
       
 16349     };
       
 16350     ctrl.$parsers.push(validator);
       
 16351     ctrl.$formatters.push(validator);
       
 16352   }
       
 16353 }
       
 16354 
       
 16355 function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
       
 16356   var validity = element.prop('validity');
       
 16357   // In composition mode, users are still inputing intermediate text buffer,
       
 16358   // hold the listener until composition is done.
       
 16359   // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
       
 16360   if (!$sniffer.android) {
       
 16361     var composing = false;
       
 16362 
       
 16363     element.on('compositionstart', function(data) {
       
 16364       composing = true;
       
 16365     });
       
 16366 
       
 16367     element.on('compositionend', function() {
       
 16368       composing = false;
       
 16369       listener();
       
 16370     });
       
 16371   }
       
 16372 
       
 16373   var listener = function() {
       
 16374     if (composing) return;
       
 16375     var value = element.val();
       
 16376 
       
 16377     // By default we will trim the value
       
 16378     // If the attribute ng-trim exists we will avoid trimming
       
 16379     // e.g. <input ng-model="foo" ng-trim="false">
       
 16380     if (toBoolean(attr.ngTrim || 'T')) {
       
 16381       value = trim(value);
       
 16382     }
       
 16383 
       
 16384     if (ctrl.$viewValue !== value ||
       
 16385         // If the value is still empty/falsy, and there is no `required` error, run validators
       
 16386         // again. This enables HTML5 constraint validation errors to affect Angular validation
       
 16387         // even when the first character entered causes an error.
       
 16388         (validity && value === '' && !validity.valueMissing)) {
       
 16389       if (scope.$$phase) {
       
 16390         ctrl.$setViewValue(value);
       
 16391       } else {
       
 16392         scope.$apply(function() {
       
 16393           ctrl.$setViewValue(value);
       
 16394         });
       
 16395       }
       
 16396     }
       
 16397   };
       
 16398 
       
 16399   // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
       
 16400   // input event on backspace, delete or cut
       
 16401   if ($sniffer.hasEvent('input')) {
       
 16402     element.on('input', listener);
       
 16403   } else {
       
 16404     var timeout;
       
 16405 
       
 16406     var deferListener = function() {
       
 16407       if (!timeout) {
       
 16408         timeout = $browser.defer(function() {
       
 16409           listener();
       
 16410           timeout = null;
       
 16411         });
       
 16412       }
       
 16413     };
       
 16414 
       
 16415     element.on('keydown', function(event) {
       
 16416       var key = event.keyCode;
       
 16417 
       
 16418       // ignore
       
 16419       //    command            modifiers                   arrows
       
 16420       if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
       
 16421 
       
 16422       deferListener();
       
 16423     });
       
 16424 
       
 16425     // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
       
 16426     if ($sniffer.hasEvent('paste')) {
       
 16427       element.on('paste cut', deferListener);
       
 16428     }
       
 16429   }
       
 16430 
       
 16431   // if user paste into input using mouse on older browser
       
 16432   // or form autocomplete on newer browser, we need "change" event to catch it
       
 16433   element.on('change', listener);
       
 16434 
       
 16435   ctrl.$render = function() {
       
 16436     element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);
       
 16437   };
       
 16438 
       
 16439   // pattern validator
       
 16440   var pattern = attr.ngPattern,
       
 16441       patternValidator,
       
 16442       match;
       
 16443 
       
 16444   if (pattern) {
       
 16445     var validateRegex = function(regexp, value) {
       
 16446       return validate(ctrl, 'pattern', ctrl.$isEmpty(value) || regexp.test(value), value);
       
 16447     };
       
 16448     match = pattern.match(/^\/(.*)\/([gim]*)$/);
       
 16449     if (match) {
       
 16450       pattern = new RegExp(match[1], match[2]);
       
 16451       patternValidator = function(value) {
       
 16452         return validateRegex(pattern, value);
       
 16453       };
       
 16454     } else {
       
 16455       patternValidator = function(value) {
       
 16456         var patternObj = scope.$eval(pattern);
       
 16457 
       
 16458         if (!patternObj || !patternObj.test) {
       
 16459           throw minErr('ngPattern')('noregexp',
       
 16460             'Expected {0} to be a RegExp but was {1}. Element: {2}', pattern,
       
 16461             patternObj, startingTag(element));
       
 16462         }
       
 16463         return validateRegex(patternObj, value);
       
 16464       };
       
 16465     }
       
 16466 
       
 16467     ctrl.$formatters.push(patternValidator);
       
 16468     ctrl.$parsers.push(patternValidator);
       
 16469   }
       
 16470 
       
 16471   // min length validator
       
 16472   if (attr.ngMinlength) {
       
 16473     var minlength = int(attr.ngMinlength);
       
 16474     var minLengthValidator = function(value) {
       
 16475       return validate(ctrl, 'minlength', ctrl.$isEmpty(value) || value.length >= minlength, value);
       
 16476     };
       
 16477 
       
 16478     ctrl.$parsers.push(minLengthValidator);
       
 16479     ctrl.$formatters.push(minLengthValidator);
       
 16480   }
       
 16481 
       
 16482   // max length validator
       
 16483   if (attr.ngMaxlength) {
       
 16484     var maxlength = int(attr.ngMaxlength);
       
 16485     var maxLengthValidator = function(value) {
       
 16486       return validate(ctrl, 'maxlength', ctrl.$isEmpty(value) || value.length <= maxlength, value);
       
 16487     };
       
 16488 
       
 16489     ctrl.$parsers.push(maxLengthValidator);
       
 16490     ctrl.$formatters.push(maxLengthValidator);
       
 16491   }
       
 16492 }
       
 16493 
       
 16494 function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
       
 16495   textInputType(scope, element, attr, ctrl, $sniffer, $browser);
       
 16496 
       
 16497   ctrl.$parsers.push(function(value) {
       
 16498     var empty = ctrl.$isEmpty(value);
       
 16499     if (empty || NUMBER_REGEXP.test(value)) {
       
 16500       ctrl.$setValidity('number', true);
       
 16501       return value === '' ? null : (empty ? value : parseFloat(value));
       
 16502     } else {
       
 16503       ctrl.$setValidity('number', false);
       
 16504       return undefined;
       
 16505     }
       
 16506   });
       
 16507 
       
 16508   addNativeHtml5Validators(ctrl, 'number', element);
       
 16509 
       
 16510   ctrl.$formatters.push(function(value) {
       
 16511     return ctrl.$isEmpty(value) ? '' : '' + value;
       
 16512   });
       
 16513 
       
 16514   if (attr.min) {
       
 16515     var minValidator = function(value) {
       
 16516       var min = parseFloat(attr.min);
       
 16517       return validate(ctrl, 'min', ctrl.$isEmpty(value) || value >= min, value);
       
 16518     };
       
 16519 
       
 16520     ctrl.$parsers.push(minValidator);
       
 16521     ctrl.$formatters.push(minValidator);
       
 16522   }
       
 16523 
       
 16524   if (attr.max) {
       
 16525     var maxValidator = function(value) {
       
 16526       var max = parseFloat(attr.max);
       
 16527       return validate(ctrl, 'max', ctrl.$isEmpty(value) || value <= max, value);
       
 16528     };
       
 16529 
       
 16530     ctrl.$parsers.push(maxValidator);
       
 16531     ctrl.$formatters.push(maxValidator);
       
 16532   }
       
 16533 
       
 16534   ctrl.$formatters.push(function(value) {
       
 16535     return validate(ctrl, 'number', ctrl.$isEmpty(value) || isNumber(value), value);
       
 16536   });
       
 16537 }
       
 16538 
       
 16539 function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
       
 16540   textInputType(scope, element, attr, ctrl, $sniffer, $browser);
       
 16541 
       
 16542   var urlValidator = function(value) {
       
 16543     return validate(ctrl, 'url', ctrl.$isEmpty(value) || URL_REGEXP.test(value), value);
       
 16544   };
       
 16545 
       
 16546   ctrl.$formatters.push(urlValidator);
       
 16547   ctrl.$parsers.push(urlValidator);
       
 16548 }
       
 16549 
       
 16550 function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
       
 16551   textInputType(scope, element, attr, ctrl, $sniffer, $browser);
       
 16552 
       
 16553   var emailValidator = function(value) {
       
 16554     return validate(ctrl, 'email', ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value), value);
       
 16555   };
       
 16556 
       
 16557   ctrl.$formatters.push(emailValidator);
       
 16558   ctrl.$parsers.push(emailValidator);
       
 16559 }
       
 16560 
       
 16561 function radioInputType(scope, element, attr, ctrl) {
       
 16562   // make the name unique, if not defined
       
 16563   if (isUndefined(attr.name)) {
       
 16564     element.attr('name', nextUid());
       
 16565   }
       
 16566 
       
 16567   element.on('click', function() {
       
 16568     if (element[0].checked) {
       
 16569       scope.$apply(function() {
       
 16570         ctrl.$setViewValue(attr.value);
       
 16571       });
       
 16572     }
       
 16573   });
       
 16574 
       
 16575   ctrl.$render = function() {
       
 16576     var value = attr.value;
       
 16577     element[0].checked = (value == ctrl.$viewValue);
       
 16578   };
       
 16579 
       
 16580   attr.$observe('value', ctrl.$render);
       
 16581 }
       
 16582 
       
 16583 function checkboxInputType(scope, element, attr, ctrl) {
       
 16584   var trueValue = attr.ngTrueValue,
       
 16585       falseValue = attr.ngFalseValue;
       
 16586 
       
 16587   if (!isString(trueValue)) trueValue = true;
       
 16588   if (!isString(falseValue)) falseValue = false;
       
 16589 
       
 16590   element.on('click', function() {
       
 16591     scope.$apply(function() {
       
 16592       ctrl.$setViewValue(element[0].checked);
       
 16593     });
       
 16594   });
       
 16595 
       
 16596   ctrl.$render = function() {
       
 16597     element[0].checked = ctrl.$viewValue;
       
 16598   };
       
 16599 
       
 16600   // Override the standard `$isEmpty` because a value of `false` means empty in a checkbox.
       
 16601   ctrl.$isEmpty = function(value) {
       
 16602     return value !== trueValue;
       
 16603   };
       
 16604 
       
 16605   ctrl.$formatters.push(function(value) {
       
 16606     return value === trueValue;
       
 16607   });
       
 16608 
       
 16609   ctrl.$parsers.push(function(value) {
       
 16610     return value ? trueValue : falseValue;
       
 16611   });
       
 16612 }
       
 16613 
       
 16614 
       
 16615 /**
       
 16616  * @ngdoc directive
       
 16617  * @name textarea
       
 16618  * @restrict E
       
 16619  *
       
 16620  * @description
       
 16621  * HTML textarea element control with angular data-binding. The data-binding and validation
       
 16622  * properties of this element are exactly the same as those of the
       
 16623  * {@link ng.directive:input input element}.
       
 16624  *
       
 16625  * @param {string} ngModel Assignable angular expression to data-bind to.
       
 16626  * @param {string=} name Property name of the form under which the control is published.
       
 16627  * @param {string=} required Sets `required` validation error key if the value is not entered.
       
 16628  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
       
 16629  *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
       
 16630  *    `required` when you want to data-bind to the `required` attribute.
       
 16631  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
       
 16632  *    minlength.
       
 16633  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
       
 16634  *    maxlength.
       
 16635  * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
       
 16636  *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
       
 16637  *    patterns defined as scope expressions.
       
 16638  * @param {string=} ngChange Angular expression to be executed when input changes due to user
       
 16639  *    interaction with the input element.
       
 16640  */
       
 16641 
       
 16642 
       
 16643 /**
       
 16644  * @ngdoc directive
       
 16645  * @name input
       
 16646  * @restrict E
       
 16647  *
       
 16648  * @description
       
 16649  * HTML input element control with angular data-binding. Input control follows HTML5 input types
       
 16650  * and polyfills the HTML5 validation behavior for older browsers.
       
 16651  *
       
 16652  * @param {string} ngModel Assignable angular expression to data-bind to.
       
 16653  * @param {string=} name Property name of the form under which the control is published.
       
 16654  * @param {string=} required Sets `required` validation error key if the value is not entered.
       
 16655  * @param {boolean=} ngRequired Sets `required` attribute if set to true
       
 16656  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
       
 16657  *    minlength.
       
 16658  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
       
 16659  *    maxlength.
       
 16660  * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
       
 16661  *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
       
 16662  *    patterns defined as scope expressions.
       
 16663  * @param {string=} ngChange Angular expression to be executed when input changes due to user
       
 16664  *    interaction with the input element.
       
 16665  *
       
 16666  * @example
       
 16667     <example name="input-directive">
       
 16668       <file name="index.html">
       
 16669        <script>
       
 16670          function Ctrl($scope) {
       
 16671            $scope.user = {name: 'guest', last: 'visitor'};
       
 16672          }
       
 16673        </script>
       
 16674        <div ng-controller="Ctrl">
       
 16675          <form name="myForm">
       
 16676            User name: <input type="text" name="userName" ng-model="user.name" required>
       
 16677            <span class="error" ng-show="myForm.userName.$error.required">
       
 16678              Required!</span><br>
       
 16679            Last name: <input type="text" name="lastName" ng-model="user.last"
       
 16680              ng-minlength="3" ng-maxlength="10">
       
 16681            <span class="error" ng-show="myForm.lastName.$error.minlength">
       
 16682              Too short!</span>
       
 16683            <span class="error" ng-show="myForm.lastName.$error.maxlength">
       
 16684              Too long!</span><br>
       
 16685          </form>
       
 16686          <hr>
       
 16687          <tt>user = {{user}}</tt><br/>
       
 16688          <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br>
       
 16689          <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br>
       
 16690          <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br>
       
 16691          <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br>
       
 16692          <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
       
 16693          <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
       
 16694          <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br>
       
 16695          <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br>
       
 16696        </div>
       
 16697       </file>
       
 16698       <file name="protractor.js" type="protractor">
       
 16699         var user = element(by.binding('{{user}}'));
       
 16700         var userNameValid = element(by.binding('myForm.userName.$valid'));
       
 16701         var lastNameValid = element(by.binding('myForm.lastName.$valid'));
       
 16702         var lastNameError = element(by.binding('myForm.lastName.$error'));
       
 16703         var formValid = element(by.binding('myForm.$valid'));
       
 16704         var userNameInput = element(by.model('user.name'));
       
 16705         var userLastInput = element(by.model('user.last'));
       
 16706 
       
 16707         it('should initialize to model', function() {
       
 16708           expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
       
 16709           expect(userNameValid.getText()).toContain('true');
       
 16710           expect(formValid.getText()).toContain('true');
       
 16711         });
       
 16712 
       
 16713         it('should be invalid if empty when required', function() {
       
 16714           userNameInput.clear();
       
 16715           userNameInput.sendKeys('');
       
 16716 
       
 16717           expect(user.getText()).toContain('{"last":"visitor"}');
       
 16718           expect(userNameValid.getText()).toContain('false');
       
 16719           expect(formValid.getText()).toContain('false');
       
 16720         });
       
 16721 
       
 16722         it('should be valid if empty when min length is set', function() {
       
 16723           userLastInput.clear();
       
 16724           userLastInput.sendKeys('');
       
 16725 
       
 16726           expect(user.getText()).toContain('{"name":"guest","last":""}');
       
 16727           expect(lastNameValid.getText()).toContain('true');
       
 16728           expect(formValid.getText()).toContain('true');
       
 16729         });
       
 16730 
       
 16731         it('should be invalid if less than required min length', function() {
       
 16732           userLastInput.clear();
       
 16733           userLastInput.sendKeys('xx');
       
 16734 
       
 16735           expect(user.getText()).toContain('{"name":"guest"}');
       
 16736           expect(lastNameValid.getText()).toContain('false');
       
 16737           expect(lastNameError.getText()).toContain('minlength');
       
 16738           expect(formValid.getText()).toContain('false');
       
 16739         });
       
 16740 
       
 16741         it('should be invalid if longer than max length', function() {
       
 16742           userLastInput.clear();
       
 16743           userLastInput.sendKeys('some ridiculously long name');
       
 16744 
       
 16745           expect(user.getText()).toContain('{"name":"guest"}');
       
 16746           expect(lastNameValid.getText()).toContain('false');
       
 16747           expect(lastNameError.getText()).toContain('maxlength');
       
 16748           expect(formValid.getText()).toContain('false');
       
 16749         });
       
 16750       </file>
       
 16751     </example>
       
 16752  */
       
 16753 var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) {
       
 16754   return {
       
 16755     restrict: 'E',
       
 16756     require: '?ngModel',
       
 16757     link: function(scope, element, attr, ctrl) {
       
 16758       if (ctrl) {
       
 16759         (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl, $sniffer,
       
 16760                                                             $browser);
       
 16761       }
       
 16762     }
       
 16763   };
       
 16764 }];
       
 16765 
       
 16766 var VALID_CLASS = 'ng-valid',
       
 16767     INVALID_CLASS = 'ng-invalid',
       
 16768     PRISTINE_CLASS = 'ng-pristine',
       
 16769     DIRTY_CLASS = 'ng-dirty';
       
 16770 
       
 16771 /**
       
 16772  * @ngdoc type
       
 16773  * @name ngModel.NgModelController
       
 16774  *
       
 16775  * @property {string} $viewValue Actual string value in the view.
       
 16776  * @property {*} $modelValue The value in the model, that the control is bound to.
       
 16777  * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
       
 16778        the control reads value from the DOM.  Each function is called, in turn, passing the value
       
 16779        through to the next. The last return value is used to populate the model.
       
 16780        Used to sanitize / convert the value as well as validation. For validation,
       
 16781        the parsers should update the validity state using
       
 16782        {@link ngModel.NgModelController#$setValidity $setValidity()},
       
 16783        and return `undefined` for invalid values.
       
 16784 
       
 16785  *
       
 16786  * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
       
 16787        the model value changes. Each function is called, in turn, passing the value through to the
       
 16788        next. Used to format / convert values for display in the control and validation.
       
 16789  *      ```js
       
 16790  *      function formatter(value) {
       
 16791  *        if (value) {
       
 16792  *          return value.toUpperCase();
       
 16793  *        }
       
 16794  *      }
       
 16795  *      ngModel.$formatters.push(formatter);
       
 16796  *      ```
       
 16797  *
       
 16798  * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
       
 16799  *     view value has changed. It is called with no arguments, and its return value is ignored.
       
 16800  *     This can be used in place of additional $watches against the model value.
       
 16801  *
       
 16802  * @property {Object} $error An object hash with all errors as keys.
       
 16803  *
       
 16804  * @property {boolean} $pristine True if user has not interacted with the control yet.
       
 16805  * @property {boolean} $dirty True if user has already interacted with the control.
       
 16806  * @property {boolean} $valid True if there is no error.
       
 16807  * @property {boolean} $invalid True if at least one error on the control.
       
 16808  *
       
 16809  * @description
       
 16810  *
       
 16811  * `NgModelController` provides API for the `ng-model` directive. The controller contains
       
 16812  * services for data-binding, validation, CSS updates, and value formatting and parsing. It
       
 16813  * purposefully does not contain any logic which deals with DOM rendering or listening to
       
 16814  * DOM events. Such DOM related logic should be provided by other directives which make use of
       
 16815  * `NgModelController` for data-binding.
       
 16816  *
       
 16817  * ## Custom Control Example
       
 16818  * This example shows how to use `NgModelController` with a custom control to achieve
       
 16819  * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
       
 16820  * collaborate together to achieve the desired result.
       
 16821  *
       
 16822  * Note that `contenteditable` is an HTML5 attribute, which tells the browser to let the element
       
 16823  * contents be edited in place by the user.  This will not work on older browsers.
       
 16824  *
       
 16825  * <example name="NgModelController" module="customControl">
       
 16826     <file name="style.css">
       
 16827       [contenteditable] {
       
 16828         border: 1px solid black;
       
 16829         background-color: white;
       
 16830         min-height: 20px;
       
 16831       }
       
 16832 
       
 16833       .ng-invalid {
       
 16834         border: 1px solid red;
       
 16835       }
       
 16836 
       
 16837     </file>
       
 16838     <file name="script.js">
       
 16839       angular.module('customControl', []).
       
 16840         directive('contenteditable', function() {
       
 16841           return {
       
 16842             restrict: 'A', // only activate on element attribute
       
 16843             require: '?ngModel', // get a hold of NgModelController
       
 16844             link: function(scope, element, attrs, ngModel) {
       
 16845               if(!ngModel) return; // do nothing if no ng-model
       
 16846 
       
 16847               // Specify how UI should be updated
       
 16848               ngModel.$render = function() {
       
 16849                 element.html(ngModel.$viewValue || '');
       
 16850               };
       
 16851 
       
 16852               // Listen for change events to enable binding
       
 16853               element.on('blur keyup change', function() {
       
 16854                 scope.$apply(read);
       
 16855               });
       
 16856               read(); // initialize
       
 16857 
       
 16858               // Write data to the model
       
 16859               function read() {
       
 16860                 var html = element.html();
       
 16861                 // When we clear the content editable the browser leaves a <br> behind
       
 16862                 // If strip-br attribute is provided then we strip this out
       
 16863                 if( attrs.stripBr && html == '<br>' ) {
       
 16864                   html = '';
       
 16865                 }
       
 16866                 ngModel.$setViewValue(html);
       
 16867               }
       
 16868             }
       
 16869           };
       
 16870         });
       
 16871     </file>
       
 16872     <file name="index.html">
       
 16873       <form name="myForm">
       
 16874        <div contenteditable
       
 16875             name="myWidget" ng-model="userContent"
       
 16876             strip-br="true"
       
 16877             required>Change me!</div>
       
 16878         <span ng-show="myForm.myWidget.$error.required">Required!</span>
       
 16879        <hr>
       
 16880        <textarea ng-model="userContent"></textarea>
       
 16881       </form>
       
 16882     </file>
       
 16883     <file name="protractor.js" type="protractor">
       
 16884     it('should data-bind and become invalid', function() {
       
 16885       if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
       
 16886         // SafariDriver can't handle contenteditable
       
 16887         // and Firefox driver can't clear contenteditables very well
       
 16888         return;
       
 16889       }
       
 16890       var contentEditable = element(by.css('[contenteditable]'));
       
 16891       var content = 'Change me!';
       
 16892 
       
 16893       expect(contentEditable.getText()).toEqual(content);
       
 16894 
       
 16895       contentEditable.clear();
       
 16896       contentEditable.sendKeys(protractor.Key.BACK_SPACE);
       
 16897       expect(contentEditable.getText()).toEqual('');
       
 16898       expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
       
 16899     });
       
 16900     </file>
       
 16901  * </example>
       
 16902  *
       
 16903  *
       
 16904  */
       
 16905 var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate',
       
 16906     function($scope, $exceptionHandler, $attr, $element, $parse, $animate) {
       
 16907   this.$viewValue = Number.NaN;
       
 16908   this.$modelValue = Number.NaN;
       
 16909   this.$parsers = [];
       
 16910   this.$formatters = [];
       
 16911   this.$viewChangeListeners = [];
       
 16912   this.$pristine = true;
       
 16913   this.$dirty = false;
       
 16914   this.$valid = true;
       
 16915   this.$invalid = false;
       
 16916   this.$name = $attr.name;
       
 16917 
       
 16918   var ngModelGet = $parse($attr.ngModel),
       
 16919       ngModelSet = ngModelGet.assign;
       
 16920 
       
 16921   if (!ngModelSet) {
       
 16922     throw minErr('ngModel')('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
       
 16923         $attr.ngModel, startingTag($element));
       
 16924   }
       
 16925 
       
 16926   /**
       
 16927    * @ngdoc method
       
 16928    * @name ngModel.NgModelController#$render
       
 16929    *
       
 16930    * @description
       
 16931    * Called when the view needs to be updated. It is expected that the user of the ng-model
       
 16932    * directive will implement this method.
       
 16933    */
       
 16934   this.$render = noop;
       
 16935 
       
 16936   /**
       
 16937    * @ngdoc method
       
 16938    * @name ngModel.NgModelController#$isEmpty
       
 16939    *
       
 16940    * @description
       
 16941    * This is called when we need to determine if the value of the input is empty.
       
 16942    *
       
 16943    * For instance, the required directive does this to work out if the input has data or not.
       
 16944    * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
       
 16945    *
       
 16946    * You can override this for input directives whose concept of being empty is different to the
       
 16947    * default. The `checkboxInputType` directive does this because in its case a value of `false`
       
 16948    * implies empty.
       
 16949    *
       
 16950    * @param {*} value Reference to check.
       
 16951    * @returns {boolean} True if `value` is empty.
       
 16952    */
       
 16953   this.$isEmpty = function(value) {
       
 16954     return isUndefined(value) || value === '' || value === null || value !== value;
       
 16955   };
       
 16956 
       
 16957   var parentForm = $element.inheritedData('$formController') || nullFormCtrl,
       
 16958       invalidCount = 0, // used to easily determine if we are valid
       
 16959       $error = this.$error = {}; // keep invalid keys here
       
 16960 
       
 16961 
       
 16962   // Setup initial state of the control
       
 16963   $element.addClass(PRISTINE_CLASS);
       
 16964   toggleValidCss(true);
       
 16965 
       
 16966   // convenience method for easy toggling of classes
       
 16967   function toggleValidCss(isValid, validationErrorKey) {
       
 16968     validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
       
 16969     $animate.removeClass($element, (isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey);
       
 16970     $animate.addClass($element, (isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey);
       
 16971   }
       
 16972 
       
 16973   /**
       
 16974    * @ngdoc method
       
 16975    * @name ngModel.NgModelController#$setValidity
       
 16976    *
       
 16977    * @description
       
 16978    * Change the validity state, and notifies the form when the control changes validity. (i.e. it
       
 16979    * does not notify form if given validator is already marked as invalid).
       
 16980    *
       
 16981    * This method should be called by validators - i.e. the parser or formatter functions.
       
 16982    *
       
 16983    * @param {string} validationErrorKey Name of the validator. the `validationErrorKey` will assign
       
 16984    *        to `$error[validationErrorKey]=isValid` so that it is available for data-binding.
       
 16985    *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
       
 16986    *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
       
 16987    *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
       
 16988    * @param {boolean} isValid Whether the current state is valid (true) or invalid (false).
       
 16989    */
       
 16990   this.$setValidity = function(validationErrorKey, isValid) {
       
 16991     // Purposeful use of ! here to cast isValid to boolean in case it is undefined
       
 16992     // jshint -W018
       
 16993     if ($error[validationErrorKey] === !isValid) return;
       
 16994     // jshint +W018
       
 16995 
       
 16996     if (isValid) {
       
 16997       if ($error[validationErrorKey]) invalidCount--;
       
 16998       if (!invalidCount) {
       
 16999         toggleValidCss(true);
       
 17000         this.$valid = true;
       
 17001         this.$invalid = false;
       
 17002       }
       
 17003     } else {
       
 17004       toggleValidCss(false);
       
 17005       this.$invalid = true;
       
 17006       this.$valid = false;
       
 17007       invalidCount++;
       
 17008     }
       
 17009 
       
 17010     $error[validationErrorKey] = !isValid;
       
 17011     toggleValidCss(isValid, validationErrorKey);
       
 17012 
       
 17013     parentForm.$setValidity(validationErrorKey, isValid, this);
       
 17014   };
       
 17015 
       
 17016   /**
       
 17017    * @ngdoc method
       
 17018    * @name ngModel.NgModelController#$setPristine
       
 17019    *
       
 17020    * @description
       
 17021    * Sets the control to its pristine state.
       
 17022    *
       
 17023    * This method can be called to remove the 'ng-dirty' class and set the control to its pristine
       
 17024    * state (ng-pristine class).
       
 17025    */
       
 17026   this.$setPristine = function () {
       
 17027     this.$dirty = false;
       
 17028     this.$pristine = true;
       
 17029     $animate.removeClass($element, DIRTY_CLASS);
       
 17030     $animate.addClass($element, PRISTINE_CLASS);
       
 17031   };
       
 17032 
       
 17033   /**
       
 17034    * @ngdoc method
       
 17035    * @name ngModel.NgModelController#$setViewValue
       
 17036    *
       
 17037    * @description
       
 17038    * Update the view value.
       
 17039    *
       
 17040    * This method should be called when the view value changes, typically from within a DOM event handler.
       
 17041    * For example {@link ng.directive:input input} and
       
 17042    * {@link ng.directive:select select} directives call it.
       
 17043    *
       
 17044    * It will update the $viewValue, then pass this value through each of the functions in `$parsers`,
       
 17045    * which includes any validators. The value that comes out of this `$parsers` pipeline, be applied to
       
 17046    * `$modelValue` and the **expression** specified in the `ng-model` attribute.
       
 17047    *
       
 17048    * Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called.
       
 17049    *
       
 17050    * Note that calling this function does not trigger a `$digest`.
       
 17051    *
       
 17052    * @param {string} value Value from the view.
       
 17053    */
       
 17054   this.$setViewValue = function(value) {
       
 17055     this.$viewValue = value;
       
 17056 
       
 17057     // change to dirty
       
 17058     if (this.$pristine) {
       
 17059       this.$dirty = true;
       
 17060       this.$pristine = false;
       
 17061       $animate.removeClass($element, PRISTINE_CLASS);
       
 17062       $animate.addClass($element, DIRTY_CLASS);
       
 17063       parentForm.$setDirty();
       
 17064     }
       
 17065 
       
 17066     forEach(this.$parsers, function(fn) {
       
 17067       value = fn(value);
       
 17068     });
       
 17069 
       
 17070     if (this.$modelValue !== value) {
       
 17071       this.$modelValue = value;
       
 17072       ngModelSet($scope, value);
       
 17073       forEach(this.$viewChangeListeners, function(listener) {
       
 17074         try {
       
 17075           listener();
       
 17076         } catch(e) {
       
 17077           $exceptionHandler(e);
       
 17078         }
       
 17079       });
       
 17080     }
       
 17081   };
       
 17082 
       
 17083   // model -> value
       
 17084   var ctrl = this;
       
 17085 
       
 17086   $scope.$watch(function ngModelWatch() {
       
 17087     var value = ngModelGet($scope);
       
 17088 
       
 17089     // if scope model value and ngModel value are out of sync
       
 17090     if (ctrl.$modelValue !== value) {
       
 17091 
       
 17092       var formatters = ctrl.$formatters,
       
 17093           idx = formatters.length;
       
 17094 
       
 17095       ctrl.$modelValue = value;
       
 17096       while(idx--) {
       
 17097         value = formatters[idx](value);
       
 17098       }
       
 17099 
       
 17100       if (ctrl.$viewValue !== value) {
       
 17101         ctrl.$viewValue = value;
       
 17102         ctrl.$render();
       
 17103       }
       
 17104     }
       
 17105 
       
 17106     return value;
       
 17107   });
       
 17108 }];
       
 17109 
       
 17110 
       
 17111 /**
       
 17112  * @ngdoc directive
       
 17113  * @name ngModel
       
 17114  *
       
 17115  * @element input
       
 17116  *
       
 17117  * @description
       
 17118  * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
       
 17119  * property on the scope using {@link ngModel.NgModelController NgModelController},
       
 17120  * which is created and exposed by this directive.
       
 17121  *
       
 17122  * `ngModel` is responsible for:
       
 17123  *
       
 17124  * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
       
 17125  *   require.
       
 17126  * - Providing validation behavior (i.e. required, number, email, url).
       
 17127  * - Keeping the state of the control (valid/invalid, dirty/pristine, validation errors).
       
 17128  * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`) including animations.
       
 17129  * - Registering the control with its parent {@link ng.directive:form form}.
       
 17130  *
       
 17131  * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
       
 17132  * current scope. If the property doesn't already exist on this scope, it will be created
       
 17133  * implicitly and added to the scope.
       
 17134  *
       
 17135  * For best practices on using `ngModel`, see:
       
 17136  *
       
 17137  *  - [https://github.com/angular/angular.js/wiki/Understanding-Scopes]
       
 17138  *
       
 17139  * For basic examples, how to use `ngModel`, see:
       
 17140  *
       
 17141  *  - {@link ng.directive:input input}
       
 17142  *    - {@link input[text] text}
       
 17143  *    - {@link input[checkbox] checkbox}
       
 17144  *    - {@link input[radio] radio}
       
 17145  *    - {@link input[number] number}
       
 17146  *    - {@link input[email] email}
       
 17147  *    - {@link input[url] url}
       
 17148  *  - {@link ng.directive:select select}
       
 17149  *  - {@link ng.directive:textarea textarea}
       
 17150  *
       
 17151  * # CSS classes
       
 17152  * The following CSS classes are added and removed on the associated input/select/textarea element
       
 17153  * depending on the validity of the model.
       
 17154  *
       
 17155  *  - `ng-valid` is set if the model is valid.
       
 17156  *  - `ng-invalid` is set if the model is invalid.
       
 17157  *  - `ng-pristine` is set if the model is pristine.
       
 17158  *  - `ng-dirty` is set if the model is dirty.
       
 17159  *
       
 17160  * Keep in mind that ngAnimate can detect each of these classes when added and removed.
       
 17161  *
       
 17162  * ## Animation Hooks
       
 17163  *
       
 17164  * Animations within models are triggered when any of the associated CSS classes are added and removed
       
 17165  * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
       
 17166  * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
       
 17167  * The animations that are triggered within ngModel are similar to how they work in ngClass and
       
 17168  * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
       
 17169  *
       
 17170  * The following example shows a simple way to utilize CSS transitions to style an input element
       
 17171  * that has been rendered as invalid after it has been validated:
       
 17172  *
       
 17173  * <pre>
       
 17174  * //be sure to include ngAnimate as a module to hook into more
       
 17175  * //advanced animations
       
 17176  * .my-input {
       
 17177  *   transition:0.5s linear all;
       
 17178  *   background: white;
       
 17179  * }
       
 17180  * .my-input.ng-invalid {
       
 17181  *   background: red;
       
 17182  *   color:white;
       
 17183  * }
       
 17184  * </pre>
       
 17185  *
       
 17186  * @example
       
 17187  * <example deps="angular-animate.js" animations="true" fixBase="true">
       
 17188      <file name="index.html">
       
 17189        <script>
       
 17190         function Ctrl($scope) {
       
 17191           $scope.val = '1';
       
 17192         }
       
 17193        </script>
       
 17194        <style>
       
 17195          .my-input {
       
 17196            -webkit-transition:all linear 0.5s;
       
 17197            transition:all linear 0.5s;
       
 17198            background: transparent;
       
 17199          }
       
 17200          .my-input.ng-invalid {
       
 17201            color:white;
       
 17202            background: red;
       
 17203          }
       
 17204        </style>
       
 17205        Update input to see transitions when valid/invalid.
       
 17206        Integer is a valid value.
       
 17207        <form name="testForm" ng-controller="Ctrl">
       
 17208          <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input" />
       
 17209        </form>
       
 17210      </file>
       
 17211  * </example>
       
 17212  */
       
 17213 var ngModelDirective = function() {
       
 17214   return {
       
 17215     require: ['ngModel', '^?form'],
       
 17216     controller: NgModelController,
       
 17217     link: function(scope, element, attr, ctrls) {
       
 17218       // notify others, especially parent forms
       
 17219 
       
 17220       var modelCtrl = ctrls[0],
       
 17221           formCtrl = ctrls[1] || nullFormCtrl;
       
 17222 
       
 17223       formCtrl.$addControl(modelCtrl);
       
 17224 
       
 17225       scope.$on('$destroy', function() {
       
 17226         formCtrl.$removeControl(modelCtrl);
       
 17227       });
       
 17228     }
       
 17229   };
       
 17230 };
       
 17231 
       
 17232 
       
 17233 /**
       
 17234  * @ngdoc directive
       
 17235  * @name ngChange
       
 17236  *
       
 17237  * @description
       
 17238  * Evaluate the given expression when the user changes the input.
       
 17239  * The expression is evaluated immediately, unlike the JavaScript onchange event
       
 17240  * which only triggers at the end of a change (usually, when the user leaves the
       
 17241  * form element or presses the return key).
       
 17242  * The expression is not evaluated when the value change is coming from the model.
       
 17243  *
       
 17244  * Note, this directive requires `ngModel` to be present.
       
 17245  *
       
 17246  * @element input
       
 17247  * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
       
 17248  * in input value.
       
 17249  *
       
 17250  * @example
       
 17251  * <example name="ngChange-directive">
       
 17252  *   <file name="index.html">
       
 17253  *     <script>
       
 17254  *       function Controller($scope) {
       
 17255  *         $scope.counter = 0;
       
 17256  *         $scope.change = function() {
       
 17257  *           $scope.counter++;
       
 17258  *         };
       
 17259  *       }
       
 17260  *     </script>
       
 17261  *     <div ng-controller="Controller">
       
 17262  *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
       
 17263  *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
       
 17264  *       <label for="ng-change-example2">Confirmed</label><br />
       
 17265  *       <tt>debug = {{confirmed}}</tt><br/>
       
 17266  *       <tt>counter = {{counter}}</tt><br/>
       
 17267  *     </div>
       
 17268  *   </file>
       
 17269  *   <file name="protractor.js" type="protractor">
       
 17270  *     var counter = element(by.binding('counter'));
       
 17271  *     var debug = element(by.binding('confirmed'));
       
 17272  *
       
 17273  *     it('should evaluate the expression if changing from view', function() {
       
 17274  *       expect(counter.getText()).toContain('0');
       
 17275  *
       
 17276  *       element(by.id('ng-change-example1')).click();
       
 17277  *
       
 17278  *       expect(counter.getText()).toContain('1');
       
 17279  *       expect(debug.getText()).toContain('true');
       
 17280  *     });
       
 17281  *
       
 17282  *     it('should not evaluate the expression if changing from model', function() {
       
 17283  *       element(by.id('ng-change-example2')).click();
       
 17284 
       
 17285  *       expect(counter.getText()).toContain('0');
       
 17286  *       expect(debug.getText()).toContain('true');
       
 17287  *     });
       
 17288  *   </file>
       
 17289  * </example>
       
 17290  */
       
 17291 var ngChangeDirective = valueFn({
       
 17292   require: 'ngModel',
       
 17293   link: function(scope, element, attr, ctrl) {
       
 17294     ctrl.$viewChangeListeners.push(function() {
       
 17295       scope.$eval(attr.ngChange);
       
 17296     });
       
 17297   }
       
 17298 });
       
 17299 
       
 17300 
       
 17301 var requiredDirective = function() {
       
 17302   return {
       
 17303     require: '?ngModel',
       
 17304     link: function(scope, elm, attr, ctrl) {
       
 17305       if (!ctrl) return;
       
 17306       attr.required = true; // force truthy in case we are on non input element
       
 17307 
       
 17308       var validator = function(value) {
       
 17309         if (attr.required && ctrl.$isEmpty(value)) {
       
 17310           ctrl.$setValidity('required', false);
       
 17311           return;
       
 17312         } else {
       
 17313           ctrl.$setValidity('required', true);
       
 17314           return value;
       
 17315         }
       
 17316       };
       
 17317 
       
 17318       ctrl.$formatters.push(validator);
       
 17319       ctrl.$parsers.unshift(validator);
       
 17320 
       
 17321       attr.$observe('required', function() {
       
 17322         validator(ctrl.$viewValue);
       
 17323       });
       
 17324     }
       
 17325   };
       
 17326 };
       
 17327 
       
 17328 
       
 17329 /**
       
 17330  * @ngdoc directive
       
 17331  * @name ngList
       
 17332  *
       
 17333  * @description
       
 17334  * Text input that converts between a delimited string and an array of strings. The delimiter
       
 17335  * can be a fixed string (by default a comma) or a regular expression.
       
 17336  *
       
 17337  * @element input
       
 17338  * @param {string=} ngList optional delimiter that should be used to split the value. If
       
 17339  *   specified in form `/something/` then the value will be converted into a regular expression.
       
 17340  *
       
 17341  * @example
       
 17342     <example name="ngList-directive">
       
 17343       <file name="index.html">
       
 17344        <script>
       
 17345          function Ctrl($scope) {
       
 17346            $scope.names = ['igor', 'misko', 'vojta'];
       
 17347          }
       
 17348        </script>
       
 17349        <form name="myForm" ng-controller="Ctrl">
       
 17350          List: <input name="namesInput" ng-model="names" ng-list required>
       
 17351          <span class="error" ng-show="myForm.namesInput.$error.required">
       
 17352            Required!</span>
       
 17353          <br>
       
 17354          <tt>names = {{names}}</tt><br/>
       
 17355          <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
       
 17356          <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
       
 17357          <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
       
 17358          <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
       
 17359         </form>
       
 17360       </file>
       
 17361       <file name="protractor.js" type="protractor">
       
 17362         var listInput = element(by.model('names'));
       
 17363         var names = element(by.binding('{{names}}'));
       
 17364         var valid = element(by.binding('myForm.namesInput.$valid'));
       
 17365         var error = element(by.css('span.error'));
       
 17366 
       
 17367         it('should initialize to model', function() {
       
 17368           expect(names.getText()).toContain('["igor","misko","vojta"]');
       
 17369           expect(valid.getText()).toContain('true');
       
 17370           expect(error.getCssValue('display')).toBe('none');
       
 17371         });
       
 17372 
       
 17373         it('should be invalid if empty', function() {
       
 17374           listInput.clear();
       
 17375           listInput.sendKeys('');
       
 17376 
       
 17377           expect(names.getText()).toContain('');
       
 17378           expect(valid.getText()).toContain('false');
       
 17379           expect(error.getCssValue('display')).not.toBe('none');        });
       
 17380       </file>
       
 17381     </example>
       
 17382  */
       
 17383 var ngListDirective = function() {
       
 17384   return {
       
 17385     require: 'ngModel',
       
 17386     link: function(scope, element, attr, ctrl) {
       
 17387       var match = /\/(.*)\//.exec(attr.ngList),
       
 17388           separator = match && new RegExp(match[1]) || attr.ngList || ',';
       
 17389 
       
 17390       var parse = function(viewValue) {
       
 17391         // If the viewValue is invalid (say required but empty) it will be `undefined`
       
 17392         if (isUndefined(viewValue)) return;
       
 17393 
       
 17394         var list = [];
       
 17395 
       
 17396         if (viewValue) {
       
 17397           forEach(viewValue.split(separator), function(value) {
       
 17398             if (value) list.push(trim(value));
       
 17399           });
       
 17400         }
       
 17401 
       
 17402         return list;
       
 17403       };
       
 17404 
       
 17405       ctrl.$parsers.push(parse);
       
 17406       ctrl.$formatters.push(function(value) {
       
 17407         if (isArray(value)) {
       
 17408           return value.join(', ');
       
 17409         }
       
 17410 
       
 17411         return undefined;
       
 17412       });
       
 17413 
       
 17414       // Override the standard $isEmpty because an empty array means the input is empty.
       
 17415       ctrl.$isEmpty = function(value) {
       
 17416         return !value || !value.length;
       
 17417       };
       
 17418     }
       
 17419   };
       
 17420 };
       
 17421 
       
 17422 
       
 17423 var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
       
 17424 /**
       
 17425  * @ngdoc directive
       
 17426  * @name ngValue
       
 17427  *
       
 17428  * @description
       
 17429  * Binds the given expression to the value of `input[select]` or `input[radio]`, so
       
 17430  * that when the element is selected, the `ngModel` of that element is set to the
       
 17431  * bound value.
       
 17432  *
       
 17433  * `ngValue` is useful when dynamically generating lists of radio buttons using `ng-repeat`, as
       
 17434  * shown below.
       
 17435  *
       
 17436  * @element input
       
 17437  * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
       
 17438  *   of the `input` element
       
 17439  *
       
 17440  * @example
       
 17441     <example name="ngValue-directive">
       
 17442       <file name="index.html">
       
 17443        <script>
       
 17444           function Ctrl($scope) {
       
 17445             $scope.names = ['pizza', 'unicorns', 'robots'];
       
 17446             $scope.my = { favorite: 'unicorns' };
       
 17447           }
       
 17448        </script>
       
 17449         <form ng-controller="Ctrl">
       
 17450           <h2>Which is your favorite?</h2>
       
 17451             <label ng-repeat="name in names" for="{{name}}">
       
 17452               {{name}}
       
 17453               <input type="radio"
       
 17454                      ng-model="my.favorite"
       
 17455                      ng-value="name"
       
 17456                      id="{{name}}"
       
 17457                      name="favorite">
       
 17458             </label>
       
 17459           <div>You chose {{my.favorite}}</div>
       
 17460         </form>
       
 17461       </file>
       
 17462       <file name="protractor.js" type="protractor">
       
 17463         var favorite = element(by.binding('my.favorite'));
       
 17464 
       
 17465         it('should initialize to model', function() {
       
 17466           expect(favorite.getText()).toContain('unicorns');
       
 17467         });
       
 17468         it('should bind the values to the inputs', function() {
       
 17469           element.all(by.model('my.favorite')).get(0).click();
       
 17470           expect(favorite.getText()).toContain('pizza');
       
 17471         });
       
 17472       </file>
       
 17473     </example>
       
 17474  */
       
 17475 var ngValueDirective = function() {
       
 17476   return {
       
 17477     priority: 100,
       
 17478     compile: function(tpl, tplAttr) {
       
 17479       if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
       
 17480         return function ngValueConstantLink(scope, elm, attr) {
       
 17481           attr.$set('value', scope.$eval(attr.ngValue));
       
 17482         };
       
 17483       } else {
       
 17484         return function ngValueLink(scope, elm, attr) {
       
 17485           scope.$watch(attr.ngValue, function valueWatchAction(value) {
       
 17486             attr.$set('value', value);
       
 17487           });
       
 17488         };
       
 17489       }
       
 17490     }
       
 17491   };
       
 17492 };
       
 17493 
       
 17494 /**
       
 17495  * @ngdoc directive
       
 17496  * @name ngBind
       
 17497  * @restrict AC
       
 17498  *
       
 17499  * @description
       
 17500  * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
       
 17501  * with the value of a given expression, and to update the text content when the value of that
       
 17502  * expression changes.
       
 17503  *
       
 17504  * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
       
 17505  * `{{ expression }}` which is similar but less verbose.
       
 17506  *
       
 17507  * It is preferable to use `ngBind` instead of `{{ expression }}` when a template is momentarily
       
 17508  * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
       
 17509  * element attribute, it makes the bindings invisible to the user while the page is loading.
       
 17510  *
       
 17511  * An alternative solution to this problem would be using the
       
 17512  * {@link ng.directive:ngCloak ngCloak} directive.
       
 17513  *
       
 17514  *
       
 17515  * @element ANY
       
 17516  * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
       
 17517  *
       
 17518  * @example
       
 17519  * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
       
 17520    <example>
       
 17521      <file name="index.html">
       
 17522        <script>
       
 17523          function Ctrl($scope) {
       
 17524            $scope.name = 'Whirled';
       
 17525          }
       
 17526        </script>
       
 17527        <div ng-controller="Ctrl">
       
 17528          Enter name: <input type="text" ng-model="name"><br>
       
 17529          Hello <span ng-bind="name"></span>!
       
 17530        </div>
       
 17531      </file>
       
 17532      <file name="protractor.js" type="protractor">
       
 17533        it('should check ng-bind', function() {
       
 17534          var nameInput = element(by.model('name'));
       
 17535 
       
 17536          expect(element(by.binding('name')).getText()).toBe('Whirled');
       
 17537          nameInput.clear();
       
 17538          nameInput.sendKeys('world');
       
 17539          expect(element(by.binding('name')).getText()).toBe('world');
       
 17540        });
       
 17541      </file>
       
 17542    </example>
       
 17543  */
       
 17544 var ngBindDirective = ngDirective(function(scope, element, attr) {
       
 17545   element.addClass('ng-binding').data('$binding', attr.ngBind);
       
 17546   scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
       
 17547     // We are purposefully using == here rather than === because we want to
       
 17548     // catch when value is "null or undefined"
       
 17549     // jshint -W041
       
 17550     element.text(value == undefined ? '' : value);
       
 17551   });
       
 17552 });
       
 17553 
       
 17554 
       
 17555 /**
       
 17556  * @ngdoc directive
       
 17557  * @name ngBindTemplate
       
 17558  *
       
 17559  * @description
       
 17560  * The `ngBindTemplate` directive specifies that the element
       
 17561  * text content should be replaced with the interpolation of the template
       
 17562  * in the `ngBindTemplate` attribute.
       
 17563  * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
       
 17564  * expressions. This directive is needed since some HTML elements
       
 17565  * (such as TITLE and OPTION) cannot contain SPAN elements.
       
 17566  *
       
 17567  * @element ANY
       
 17568  * @param {string} ngBindTemplate template of form
       
 17569  *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
       
 17570  *
       
 17571  * @example
       
 17572  * Try it here: enter text in text box and watch the greeting change.
       
 17573    <example>
       
 17574      <file name="index.html">
       
 17575        <script>
       
 17576          function Ctrl($scope) {
       
 17577            $scope.salutation = 'Hello';
       
 17578            $scope.name = 'World';
       
 17579          }
       
 17580        </script>
       
 17581        <div ng-controller="Ctrl">
       
 17582         Salutation: <input type="text" ng-model="salutation"><br>
       
 17583         Name: <input type="text" ng-model="name"><br>
       
 17584         <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
       
 17585        </div>
       
 17586      </file>
       
 17587      <file name="protractor.js" type="protractor">
       
 17588        it('should check ng-bind', function() {
       
 17589          var salutationElem = element(by.binding('salutation'));
       
 17590          var salutationInput = element(by.model('salutation'));
       
 17591          var nameInput = element(by.model('name'));
       
 17592 
       
 17593          expect(salutationElem.getText()).toBe('Hello World!');
       
 17594 
       
 17595          salutationInput.clear();
       
 17596          salutationInput.sendKeys('Greetings');
       
 17597          nameInput.clear();
       
 17598          nameInput.sendKeys('user');
       
 17599 
       
 17600          expect(salutationElem.getText()).toBe('Greetings user!');
       
 17601        });
       
 17602      </file>
       
 17603    </example>
       
 17604  */
       
 17605 var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
       
 17606   return function(scope, element, attr) {
       
 17607     // TODO: move this to scenario runner
       
 17608     var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
       
 17609     element.addClass('ng-binding').data('$binding', interpolateFn);
       
 17610     attr.$observe('ngBindTemplate', function(value) {
       
 17611       element.text(value);
       
 17612     });
       
 17613   };
       
 17614 }];
       
 17615 
       
 17616 
       
 17617 /**
       
 17618  * @ngdoc directive
       
 17619  * @name ngBindHtml
       
 17620  *
       
 17621  * @description
       
 17622  * Creates a binding that will innerHTML the result of evaluating the `expression` into the current
       
 17623  * element in a secure way.  By default, the innerHTML-ed content will be sanitized using the {@link
       
 17624  * ngSanitize.$sanitize $sanitize} service.  To utilize this functionality, ensure that `$sanitize`
       
 17625  * is available, for example, by including {@link ngSanitize} in your module's dependencies (not in
       
 17626  * core Angular.)  You may also bypass sanitization for values you know are safe. To do so, bind to
       
 17627  * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example
       
 17628  * under {@link ng.$sce#Example Strict Contextual Escaping (SCE)}.
       
 17629  *
       
 17630  * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
       
 17631  * will have an exception (instead of an exploit.)
       
 17632  *
       
 17633  * @element ANY
       
 17634  * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
       
 17635  *
       
 17636  * @example
       
 17637    Try it here: enter text in text box and watch the greeting change.
       
 17638 
       
 17639    <example module="ngBindHtmlExample" deps="angular-sanitize.js">
       
 17640      <file name="index.html">
       
 17641        <div ng-controller="ngBindHtmlCtrl">
       
 17642         <p ng-bind-html="myHTML"></p>
       
 17643        </div>
       
 17644      </file>
       
 17645 
       
 17646      <file name="script.js">
       
 17647        angular.module('ngBindHtmlExample', ['ngSanitize'])
       
 17648 
       
 17649        .controller('ngBindHtmlCtrl', ['$scope', function ngBindHtmlCtrl($scope) {
       
 17650          $scope.myHTML =
       
 17651             'I am an <code>HTML</code>string with <a href="#">links!</a> and other <em>stuff</em>';
       
 17652        }]);
       
 17653      </file>
       
 17654 
       
 17655      <file name="protractor.js" type="protractor">
       
 17656        it('should check ng-bind-html', function() {
       
 17657          expect(element(by.binding('myHTML')).getText()).toBe(
       
 17658              'I am an HTMLstring with links! and other stuff');
       
 17659        });
       
 17660      </file>
       
 17661    </example>
       
 17662  */
       
 17663 var ngBindHtmlDirective = ['$sce', '$parse', function($sce, $parse) {
       
 17664   return function(scope, element, attr) {
       
 17665     element.addClass('ng-binding').data('$binding', attr.ngBindHtml);
       
 17666 
       
 17667     var parsed = $parse(attr.ngBindHtml);
       
 17668     function getStringValue() { return (parsed(scope) || '').toString(); }
       
 17669 
       
 17670     scope.$watch(getStringValue, function ngBindHtmlWatchAction(value) {
       
 17671       element.html($sce.getTrustedHtml(parsed(scope)) || '');
       
 17672     });
       
 17673   };
       
 17674 }];
       
 17675 
       
 17676 function classDirective(name, selector) {
       
 17677   name = 'ngClass' + name;
       
 17678   return function() {
       
 17679     return {
       
 17680       restrict: 'AC',
       
 17681       link: function(scope, element, attr) {
       
 17682         var oldVal;
       
 17683 
       
 17684         scope.$watch(attr[name], ngClassWatchAction, true);
       
 17685 
       
 17686         attr.$observe('class', function(value) {
       
 17687           ngClassWatchAction(scope.$eval(attr[name]));
       
 17688         });
       
 17689 
       
 17690 
       
 17691         if (name !== 'ngClass') {
       
 17692           scope.$watch('$index', function($index, old$index) {
       
 17693             // jshint bitwise: false
       
 17694             var mod = $index & 1;
       
 17695             if (mod !== old$index & 1) {
       
 17696               var classes = flattenClasses(scope.$eval(attr[name]));
       
 17697               mod === selector ?
       
 17698                 attr.$addClass(classes) :
       
 17699                 attr.$removeClass(classes);
       
 17700             }
       
 17701           });
       
 17702         }
       
 17703 
       
 17704 
       
 17705         function ngClassWatchAction(newVal) {
       
 17706           if (selector === true || scope.$index % 2 === selector) {
       
 17707             var newClasses = flattenClasses(newVal || '');
       
 17708             if(!oldVal) {
       
 17709               attr.$addClass(newClasses);
       
 17710             } else if(!equals(newVal,oldVal)) {
       
 17711               attr.$updateClass(newClasses, flattenClasses(oldVal));
       
 17712             }
       
 17713           }
       
 17714           oldVal = copy(newVal);
       
 17715         }
       
 17716 
       
 17717 
       
 17718         function flattenClasses(classVal) {
       
 17719           if(isArray(classVal)) {
       
 17720             return classVal.join(' ');
       
 17721           } else if (isObject(classVal)) {
       
 17722             var classes = [], i = 0;
       
 17723             forEach(classVal, function(v, k) {
       
 17724               if (v) {
       
 17725                 classes.push(k);
       
 17726               }
       
 17727             });
       
 17728             return classes.join(' ');
       
 17729           }
       
 17730 
       
 17731           return classVal;
       
 17732         }
       
 17733       }
       
 17734     };
       
 17735   };
       
 17736 }
       
 17737 
       
 17738 /**
       
 17739  * @ngdoc directive
       
 17740  * @name ngClass
       
 17741  * @restrict AC
       
 17742  *
       
 17743  * @description
       
 17744  * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
       
 17745  * an expression that represents all classes to be added.
       
 17746  *
       
 17747  * The directive operates in three different ways, depending on which of three types the expression
       
 17748  * evaluates to:
       
 17749  *
       
 17750  * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
       
 17751  * names.
       
 17752  *
       
 17753  * 2. If the expression evaluates to an array, each element of the array should be a string that is
       
 17754  * one or more space-delimited class names.
       
 17755  *
       
 17756  * 3. If the expression evaluates to an object, then for each key-value pair of the
       
 17757  * object with a truthy value the corresponding key is used as a class name.
       
 17758  *
       
 17759  * The directive won't add duplicate classes if a particular class was already set.
       
 17760  *
       
 17761  * When the expression changes, the previously added classes are removed and only then the
       
 17762  * new classes are added.
       
 17763  *
       
 17764  * @animations
       
 17765  * add - happens just before the class is applied to the element
       
 17766  * remove - happens just before the class is removed from the element
       
 17767  *
       
 17768  * @element ANY
       
 17769  * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
       
 17770  *   of the evaluation can be a string representing space delimited class
       
 17771  *   names, an array, or a map of class names to boolean values. In the case of a map, the
       
 17772  *   names of the properties whose values are truthy will be added as css classes to the
       
 17773  *   element.
       
 17774  *
       
 17775  * @example Example that demonstrates basic bindings via ngClass directive.
       
 17776    <example>
       
 17777      <file name="index.html">
       
 17778        <p ng-class="{strike: deleted, bold: important, red: error}">Map Syntax Example</p>
       
 17779        <input type="checkbox" ng-model="deleted"> deleted (apply "strike" class)<br>
       
 17780        <input type="checkbox" ng-model="important"> important (apply "bold" class)<br>
       
 17781        <input type="checkbox" ng-model="error"> error (apply "red" class)
       
 17782        <hr>
       
 17783        <p ng-class="style">Using String Syntax</p>
       
 17784        <input type="text" ng-model="style" placeholder="Type: bold strike red">
       
 17785        <hr>
       
 17786        <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
       
 17787        <input ng-model="style1" placeholder="Type: bold, strike or red"><br>
       
 17788        <input ng-model="style2" placeholder="Type: bold, strike or red"><br>
       
 17789        <input ng-model="style3" placeholder="Type: bold, strike or red"><br>
       
 17790      </file>
       
 17791      <file name="style.css">
       
 17792        .strike {
       
 17793          text-decoration: line-through;
       
 17794        }
       
 17795        .bold {
       
 17796            font-weight: bold;
       
 17797        }
       
 17798        .red {
       
 17799            color: red;
       
 17800        }
       
 17801      </file>
       
 17802      <file name="protractor.js" type="protractor">
       
 17803        var ps = element.all(by.css('p'));
       
 17804 
       
 17805        it('should let you toggle the class', function() {
       
 17806 
       
 17807          expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
       
 17808          expect(ps.first().getAttribute('class')).not.toMatch(/red/);
       
 17809 
       
 17810          element(by.model('important')).click();
       
 17811          expect(ps.first().getAttribute('class')).toMatch(/bold/);
       
 17812 
       
 17813          element(by.model('error')).click();
       
 17814          expect(ps.first().getAttribute('class')).toMatch(/red/);
       
 17815        });
       
 17816 
       
 17817        it('should let you toggle string example', function() {
       
 17818          expect(ps.get(1).getAttribute('class')).toBe('');
       
 17819          element(by.model('style')).clear();
       
 17820          element(by.model('style')).sendKeys('red');
       
 17821          expect(ps.get(1).getAttribute('class')).toBe('red');
       
 17822        });
       
 17823 
       
 17824        it('array example should have 3 classes', function() {
       
 17825          expect(ps.last().getAttribute('class')).toBe('');
       
 17826          element(by.model('style1')).sendKeys('bold');
       
 17827          element(by.model('style2')).sendKeys('strike');
       
 17828          element(by.model('style3')).sendKeys('red');
       
 17829          expect(ps.last().getAttribute('class')).toBe('bold strike red');
       
 17830        });
       
 17831      </file>
       
 17832    </example>
       
 17833 
       
 17834    ## Animations
       
 17835 
       
 17836    The example below demonstrates how to perform animations using ngClass.
       
 17837 
       
 17838    <example module="ngAnimate" deps="angular-animate.js" animations="true">
       
 17839      <file name="index.html">
       
 17840       <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
       
 17841       <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
       
 17842       <br>
       
 17843       <span class="base-class" ng-class="myVar">Sample Text</span>
       
 17844      </file>
       
 17845      <file name="style.css">
       
 17846        .base-class {
       
 17847          -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
       
 17848          transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
       
 17849        }
       
 17850 
       
 17851        .base-class.my-class {
       
 17852          color: red;
       
 17853          font-size:3em;
       
 17854        }
       
 17855      </file>
       
 17856      <file name="protractor.js" type="protractor">
       
 17857        it('should check ng-class', function() {
       
 17858          expect(element(by.css('.base-class')).getAttribute('class')).not.
       
 17859            toMatch(/my-class/);
       
 17860 
       
 17861          element(by.id('setbtn')).click();
       
 17862 
       
 17863          expect(element(by.css('.base-class')).getAttribute('class')).
       
 17864            toMatch(/my-class/);
       
 17865 
       
 17866          element(by.id('clearbtn')).click();
       
 17867 
       
 17868          expect(element(by.css('.base-class')).getAttribute('class')).not.
       
 17869            toMatch(/my-class/);
       
 17870        });
       
 17871      </file>
       
 17872    </example>
       
 17873 
       
 17874 
       
 17875    ## ngClass and pre-existing CSS3 Transitions/Animations
       
 17876    The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
       
 17877    Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
       
 17878    any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
       
 17879    to view the step by step details of {@link ngAnimate.$animate#addclass $animate.addClass} and
       
 17880    {@link ngAnimate.$animate#removeclass $animate.removeClass}.
       
 17881  */
       
 17882 var ngClassDirective = classDirective('', true);
       
 17883 
       
 17884 /**
       
 17885  * @ngdoc directive
       
 17886  * @name ngClassOdd
       
 17887  * @restrict AC
       
 17888  *
       
 17889  * @description
       
 17890  * The `ngClassOdd` and `ngClassEven` directives work exactly as
       
 17891  * {@link ng.directive:ngClass ngClass}, except they work in
       
 17892  * conjunction with `ngRepeat` and take effect only on odd (even) rows.
       
 17893  *
       
 17894  * This directive can be applied only within the scope of an
       
 17895  * {@link ng.directive:ngRepeat ngRepeat}.
       
 17896  *
       
 17897  * @element ANY
       
 17898  * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
       
 17899  *   of the evaluation can be a string representing space delimited class names or an array.
       
 17900  *
       
 17901  * @example
       
 17902    <example>
       
 17903      <file name="index.html">
       
 17904         <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
       
 17905           <li ng-repeat="name in names">
       
 17906            <span ng-class-odd="'odd'" ng-class-even="'even'">
       
 17907              {{name}}
       
 17908            </span>
       
 17909           </li>
       
 17910         </ol>
       
 17911      </file>
       
 17912      <file name="style.css">
       
 17913        .odd {
       
 17914          color: red;
       
 17915        }
       
 17916        .even {
       
 17917          color: blue;
       
 17918        }
       
 17919      </file>
       
 17920      <file name="protractor.js" type="protractor">
       
 17921        it('should check ng-class-odd and ng-class-even', function() {
       
 17922          expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
       
 17923            toMatch(/odd/);
       
 17924          expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
       
 17925            toMatch(/even/);
       
 17926        });
       
 17927      </file>
       
 17928    </example>
       
 17929  */
       
 17930 var ngClassOddDirective = classDirective('Odd', 0);
       
 17931 
       
 17932 /**
       
 17933  * @ngdoc directive
       
 17934  * @name ngClassEven
       
 17935  * @restrict AC
       
 17936  *
       
 17937  * @description
       
 17938  * The `ngClassOdd` and `ngClassEven` directives work exactly as
       
 17939  * {@link ng.directive:ngClass ngClass}, except they work in
       
 17940  * conjunction with `ngRepeat` and take effect only on odd (even) rows.
       
 17941  *
       
 17942  * This directive can be applied only within the scope of an
       
 17943  * {@link ng.directive:ngRepeat ngRepeat}.
       
 17944  *
       
 17945  * @element ANY
       
 17946  * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
       
 17947  *   result of the evaluation can be a string representing space delimited class names or an array.
       
 17948  *
       
 17949  * @example
       
 17950    <example>
       
 17951      <file name="index.html">
       
 17952         <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
       
 17953           <li ng-repeat="name in names">
       
 17954            <span ng-class-odd="'odd'" ng-class-even="'even'">
       
 17955              {{name}} &nbsp; &nbsp; &nbsp;
       
 17956            </span>
       
 17957           </li>
       
 17958         </ol>
       
 17959      </file>
       
 17960      <file name="style.css">
       
 17961        .odd {
       
 17962          color: red;
       
 17963        }
       
 17964        .even {
       
 17965          color: blue;
       
 17966        }
       
 17967      </file>
       
 17968      <file name="protractor.js" type="protractor">
       
 17969        it('should check ng-class-odd and ng-class-even', function() {
       
 17970          expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
       
 17971            toMatch(/odd/);
       
 17972          expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
       
 17973            toMatch(/even/);
       
 17974        });
       
 17975      </file>
       
 17976    </example>
       
 17977  */
       
 17978 var ngClassEvenDirective = classDirective('Even', 1);
       
 17979 
       
 17980 /**
       
 17981  * @ngdoc directive
       
 17982  * @name ngCloak
       
 17983  * @restrict AC
       
 17984  *
       
 17985  * @description
       
 17986  * The `ngCloak` directive is used to prevent the Angular html template from being briefly
       
 17987  * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
       
 17988  * directive to avoid the undesirable flicker effect caused by the html template display.
       
 17989  *
       
 17990  * The directive can be applied to the `<body>` element, but the preferred usage is to apply
       
 17991  * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
       
 17992  * of the browser view.
       
 17993  *
       
 17994  * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
       
 17995  * `angular.min.js`.
       
 17996  * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
       
 17997  *
       
 17998  * ```css
       
 17999  * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
       
 18000  *   display: none !important;
       
 18001  * }
       
 18002  * ```
       
 18003  *
       
 18004  * When this css rule is loaded by the browser, all html elements (including their children) that
       
 18005  * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
       
 18006  * during the compilation of the template it deletes the `ngCloak` element attribute, making
       
 18007  * the compiled element visible.
       
 18008  *
       
 18009  * For the best result, the `angular.js` script must be loaded in the head section of the html
       
 18010  * document; alternatively, the css rule above must be included in the external stylesheet of the
       
 18011  * application.
       
 18012  *
       
 18013  * Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they
       
 18014  * cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css
       
 18015  * class `ng-cloak` in addition to the `ngCloak` directive as shown in the example below.
       
 18016  *
       
 18017  * @element ANY
       
 18018  *
       
 18019  * @example
       
 18020    <example>
       
 18021      <file name="index.html">
       
 18022         <div id="template1" ng-cloak>{{ 'hello' }}</div>
       
 18023         <div id="template2" ng-cloak class="ng-cloak">{{ 'hello IE7' }}</div>
       
 18024      </file>
       
 18025      <file name="protractor.js" type="protractor">
       
 18026        it('should remove the template directive and css class', function() {
       
 18027          expect($('#template1').getAttribute('ng-cloak')).
       
 18028            toBeNull();
       
 18029          expect($('#template2').getAttribute('ng-cloak')).
       
 18030            toBeNull();
       
 18031        });
       
 18032      </file>
       
 18033    </example>
       
 18034  *
       
 18035  */
       
 18036 var ngCloakDirective = ngDirective({
       
 18037   compile: function(element, attr) {
       
 18038     attr.$set('ngCloak', undefined);
       
 18039     element.removeClass('ng-cloak');
       
 18040   }
       
 18041 });
       
 18042 
       
 18043 /**
       
 18044  * @ngdoc directive
       
 18045  * @name ngController
       
 18046  *
       
 18047  * @description
       
 18048  * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
       
 18049  * supports the principles behind the Model-View-Controller design pattern.
       
 18050  *
       
 18051  * MVC components in angular:
       
 18052  *
       
 18053  * * Model — The Model is scope properties; scopes are attached to the DOM where scope properties
       
 18054  *   are accessed through bindings.
       
 18055  * * View — The template (HTML with data bindings) that is rendered into the View.
       
 18056  * * Controller — The `ngController` directive specifies a Controller class; the class contains business
       
 18057  *   logic behind the application to decorate the scope with functions and values
       
 18058  *
       
 18059  * Note that you can also attach controllers to the DOM by declaring it in a route definition
       
 18060  * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
       
 18061  * again using `ng-controller` in the template itself.  This will cause the controller to be attached
       
 18062  * and executed twice.
       
 18063  *
       
 18064  * @element ANY
       
 18065  * @scope
       
 18066  * @param {expression} ngController Name of a globally accessible constructor function or an
       
 18067  *     {@link guide/expression expression} that on the current scope evaluates to a
       
 18068  *     constructor function. The controller instance can be published into a scope property
       
 18069  *     by specifying `as propertyName`.
       
 18070  *
       
 18071  * @example
       
 18072  * Here is a simple form for editing user contact information. Adding, removing, clearing, and
       
 18073  * greeting are methods declared on the controller (see source tab). These methods can
       
 18074  * easily be called from the angular markup. Notice that the scope becomes the `this` for the
       
 18075  * controller's instance. This allows for easy access to the view data from the controller. Also
       
 18076  * notice that any changes to the data are automatically reflected in the View without the need
       
 18077  * for a manual update. The example is shown in two different declaration styles you may use
       
 18078  * according to preference.
       
 18079    <example>
       
 18080      <file name="index.html">
       
 18081       <script>
       
 18082         function SettingsController1() {
       
 18083           this.name = "John Smith";
       
 18084           this.contacts = [
       
 18085             {type: 'phone', value: '408 555 1212'},
       
 18086             {type: 'email', value: 'john.smith@example.org'} ];
       
 18087           };
       
 18088 
       
 18089         SettingsController1.prototype.greet = function() {
       
 18090           alert(this.name);
       
 18091         };
       
 18092 
       
 18093         SettingsController1.prototype.addContact = function() {
       
 18094           this.contacts.push({type: 'email', value: 'yourname@example.org'});
       
 18095         };
       
 18096 
       
 18097         SettingsController1.prototype.removeContact = function(contactToRemove) {
       
 18098          var index = this.contacts.indexOf(contactToRemove);
       
 18099           this.contacts.splice(index, 1);
       
 18100         };
       
 18101 
       
 18102         SettingsController1.prototype.clearContact = function(contact) {
       
 18103           contact.type = 'phone';
       
 18104           contact.value = '';
       
 18105         };
       
 18106       </script>
       
 18107       <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
       
 18108         Name: <input type="text" ng-model="settings.name"/>
       
 18109         [ <a href="" ng-click="settings.greet()">greet</a> ]<br/>
       
 18110         Contact:
       
 18111         <ul>
       
 18112           <li ng-repeat="contact in settings.contacts">
       
 18113             <select ng-model="contact.type">
       
 18114                <option>phone</option>
       
 18115                <option>email</option>
       
 18116             </select>
       
 18117             <input type="text" ng-model="contact.value"/>
       
 18118             [ <a href="" ng-click="settings.clearContact(contact)">clear</a>
       
 18119             | <a href="" ng-click="settings.removeContact(contact)">X</a> ]
       
 18120           </li>
       
 18121           <li>[ <a href="" ng-click="settings.addContact()">add</a> ]</li>
       
 18122        </ul>
       
 18123       </div>
       
 18124      </file>
       
 18125      <file name="protractor.js" type="protractor">
       
 18126        it('should check controller as', function() {
       
 18127          var container = element(by.id('ctrl-as-exmpl'));
       
 18128 
       
 18129          expect(container.findElement(by.model('settings.name'))
       
 18130              .getAttribute('value')).toBe('John Smith');
       
 18131 
       
 18132          var firstRepeat =
       
 18133              container.findElement(by.repeater('contact in settings.contacts').row(0));
       
 18134          var secondRepeat =
       
 18135              container.findElement(by.repeater('contact in settings.contacts').row(1));
       
 18136 
       
 18137          expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
       
 18138              .toBe('408 555 1212');
       
 18139          expect(secondRepeat.findElement(by.model('contact.value')).getAttribute('value'))
       
 18140              .toBe('john.smith@example.org');
       
 18141 
       
 18142          firstRepeat.findElement(by.linkText('clear')).click();
       
 18143 
       
 18144          expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
       
 18145              .toBe('');
       
 18146 
       
 18147          container.findElement(by.linkText('add')).click();
       
 18148 
       
 18149          expect(container.findElement(by.repeater('contact in settings.contacts').row(2))
       
 18150              .findElement(by.model('contact.value'))
       
 18151              .getAttribute('value'))
       
 18152              .toBe('yourname@example.org');
       
 18153        });
       
 18154      </file>
       
 18155    </example>
       
 18156     <example>
       
 18157      <file name="index.html">
       
 18158       <script>
       
 18159         function SettingsController2($scope) {
       
 18160           $scope.name = "John Smith";
       
 18161           $scope.contacts = [
       
 18162             {type:'phone', value:'408 555 1212'},
       
 18163             {type:'email', value:'john.smith@example.org'} ];
       
 18164 
       
 18165           $scope.greet = function() {
       
 18166            alert(this.name);
       
 18167           };
       
 18168 
       
 18169           $scope.addContact = function() {
       
 18170            this.contacts.push({type:'email', value:'yourname@example.org'});
       
 18171           };
       
 18172 
       
 18173           $scope.removeContact = function(contactToRemove) {
       
 18174            var index = this.contacts.indexOf(contactToRemove);
       
 18175            this.contacts.splice(index, 1);
       
 18176           };
       
 18177 
       
 18178           $scope.clearContact = function(contact) {
       
 18179            contact.type = 'phone';
       
 18180            contact.value = '';
       
 18181           };
       
 18182         }
       
 18183       </script>
       
 18184       <div id="ctrl-exmpl" ng-controller="SettingsController2">
       
 18185         Name: <input type="text" ng-model="name"/>
       
 18186         [ <a href="" ng-click="greet()">greet</a> ]<br/>
       
 18187         Contact:
       
 18188         <ul>
       
 18189           <li ng-repeat="contact in contacts">
       
 18190             <select ng-model="contact.type">
       
 18191                <option>phone</option>
       
 18192                <option>email</option>
       
 18193             </select>
       
 18194             <input type="text" ng-model="contact.value"/>
       
 18195             [ <a href="" ng-click="clearContact(contact)">clear</a>
       
 18196             | <a href="" ng-click="removeContact(contact)">X</a> ]
       
 18197           </li>
       
 18198           <li>[ <a href="" ng-click="addContact()">add</a> ]</li>
       
 18199        </ul>
       
 18200       </div>
       
 18201      </file>
       
 18202      <file name="protractor.js" type="protractor">
       
 18203        it('should check controller', function() {
       
 18204          var container = element(by.id('ctrl-exmpl'));
       
 18205 
       
 18206          expect(container.findElement(by.model('name'))
       
 18207              .getAttribute('value')).toBe('John Smith');
       
 18208 
       
 18209          var firstRepeat =
       
 18210              container.findElement(by.repeater('contact in contacts').row(0));
       
 18211          var secondRepeat =
       
 18212              container.findElement(by.repeater('contact in contacts').row(1));
       
 18213 
       
 18214          expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
       
 18215              .toBe('408 555 1212');
       
 18216          expect(secondRepeat.findElement(by.model('contact.value')).getAttribute('value'))
       
 18217              .toBe('john.smith@example.org');
       
 18218 
       
 18219          firstRepeat.findElement(by.linkText('clear')).click();
       
 18220 
       
 18221          expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
       
 18222              .toBe('');
       
 18223 
       
 18224          container.findElement(by.linkText('add')).click();
       
 18225 
       
 18226          expect(container.findElement(by.repeater('contact in contacts').row(2))
       
 18227              .findElement(by.model('contact.value'))
       
 18228              .getAttribute('value'))
       
 18229              .toBe('yourname@example.org');
       
 18230        });
       
 18231      </file>
       
 18232    </example>
       
 18233 
       
 18234  */
       
 18235 var ngControllerDirective = [function() {
       
 18236   return {
       
 18237     scope: true,
       
 18238     controller: '@',
       
 18239     priority: 500
       
 18240   };
       
 18241 }];
       
 18242 
       
 18243 /**
       
 18244  * @ngdoc directive
       
 18245  * @name ngCsp
       
 18246  *
       
 18247  * @element html
       
 18248  * @description
       
 18249  * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
       
 18250  *
       
 18251  * This is necessary when developing things like Google Chrome Extensions.
       
 18252  *
       
 18253  * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
       
 18254  * For us to be compatible, we just need to implement the "getterFn" in $parse without violating
       
 18255  * any of these restrictions.
       
 18256  *
       
 18257  * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp`
       
 18258  * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will
       
 18259  * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
       
 18260  * be raised.
       
 18261  *
       
 18262  * CSP forbids JavaScript to inline stylesheet rules. In non CSP mode Angular automatically
       
 18263  * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}).
       
 18264  * To make those directives work in CSP mode, include the `angular-csp.css` manually.
       
 18265  *
       
 18266  * In order to use this feature put the `ngCsp` directive on the root element of the application.
       
 18267  *
       
 18268  * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
       
 18269  *
       
 18270  * @example
       
 18271  * This example shows how to apply the `ngCsp` directive to the `html` tag.
       
 18272    ```html
       
 18273      <!doctype html>
       
 18274      <html ng-app ng-csp>
       
 18275      ...
       
 18276      ...
       
 18277      </html>
       
 18278    ```
       
 18279  */
       
 18280 
       
 18281 // ngCsp is not implemented as a proper directive any more, because we need it be processed while we bootstrap
       
 18282 // the system (before $parse is instantiated), for this reason we just have a csp() fn that looks for ng-csp attribute
       
 18283 // anywhere in the current doc
       
 18284 
       
 18285 /**
       
 18286  * @ngdoc directive
       
 18287  * @name ngClick
       
 18288  *
       
 18289  * @description
       
 18290  * The ngClick directive allows you to specify custom behavior when
       
 18291  * an element is clicked.
       
 18292  *
       
 18293  * @element ANY
       
 18294  * @priority 0
       
 18295  * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
       
 18296  * click. (Event object is available as `$event`)
       
 18297  *
       
 18298  * @example
       
 18299    <example>
       
 18300      <file name="index.html">
       
 18301       <button ng-click="count = count + 1" ng-init="count=0">
       
 18302         Increment
       
 18303       </button>
       
 18304       count: {{count}}
       
 18305      </file>
       
 18306      <file name="protractor.js" type="protractor">
       
 18307        it('should check ng-click', function() {
       
 18308          expect(element(by.binding('count')).getText()).toMatch('0');
       
 18309          element(by.css('button')).click();
       
 18310          expect(element(by.binding('count')).getText()).toMatch('1');
       
 18311        });
       
 18312      </file>
       
 18313    </example>
       
 18314  */
       
 18315 /*
       
 18316  * A directive that allows creation of custom onclick handlers that are defined as angular
       
 18317  * expressions and are compiled and executed within the current scope.
       
 18318  *
       
 18319  * Events that are handled via these handler are always configured not to propagate further.
       
 18320  */
       
 18321 var ngEventDirectives = {};
       
 18322 forEach(
       
 18323   'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
       
 18324   function(name) {
       
 18325     var directiveName = directiveNormalize('ng-' + name);
       
 18326     ngEventDirectives[directiveName] = ['$parse', function($parse) {
       
 18327       return {
       
 18328         compile: function($element, attr) {
       
 18329           var fn = $parse(attr[directiveName]);
       
 18330           return function(scope, element, attr) {
       
 18331             element.on(lowercase(name), function(event) {
       
 18332               scope.$apply(function() {
       
 18333                 fn(scope, {$event:event});
       
 18334               });
       
 18335             });
       
 18336           };
       
 18337         }
       
 18338       };
       
 18339     }];
       
 18340   }
       
 18341 );
       
 18342 
       
 18343 /**
       
 18344  * @ngdoc directive
       
 18345  * @name ngDblclick
       
 18346  *
       
 18347  * @description
       
 18348  * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
       
 18349  *
       
 18350  * @element ANY
       
 18351  * @priority 0
       
 18352  * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
       
 18353  * a dblclick. (The Event object is available as `$event`)
       
 18354  *
       
 18355  * @example
       
 18356    <example>
       
 18357      <file name="index.html">
       
 18358       <button ng-dblclick="count = count + 1" ng-init="count=0">
       
 18359         Increment (on double click)
       
 18360       </button>
       
 18361       count: {{count}}
       
 18362      </file>
       
 18363    </example>
       
 18364  */
       
 18365 
       
 18366 
       
 18367 /**
       
 18368  * @ngdoc directive
       
 18369  * @name ngMousedown
       
 18370  *
       
 18371  * @description
       
 18372  * The ngMousedown directive allows you to specify custom behavior on mousedown event.
       
 18373  *
       
 18374  * @element ANY
       
 18375  * @priority 0
       
 18376  * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
       
 18377  * mousedown. (Event object is available as `$event`)
       
 18378  *
       
 18379  * @example
       
 18380    <example>
       
 18381      <file name="index.html">
       
 18382       <button ng-mousedown="count = count + 1" ng-init="count=0">
       
 18383         Increment (on mouse down)
       
 18384       </button>
       
 18385       count: {{count}}
       
 18386      </file>
       
 18387    </example>
       
 18388  */
       
 18389 
       
 18390 
       
 18391 /**
       
 18392  * @ngdoc directive
       
 18393  * @name ngMouseup
       
 18394  *
       
 18395  * @description
       
 18396  * Specify custom behavior on mouseup event.
       
 18397  *
       
 18398  * @element ANY
       
 18399  * @priority 0
       
 18400  * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
       
 18401  * mouseup. (Event object is available as `$event`)
       
 18402  *
       
 18403  * @example
       
 18404    <example>
       
 18405      <file name="index.html">
       
 18406       <button ng-mouseup="count = count + 1" ng-init="count=0">
       
 18407         Increment (on mouse up)
       
 18408       </button>
       
 18409       count: {{count}}
       
 18410      </file>
       
 18411    </example>
       
 18412  */
       
 18413 
       
 18414 /**
       
 18415  * @ngdoc directive
       
 18416  * @name ngMouseover
       
 18417  *
       
 18418  * @description
       
 18419  * Specify custom behavior on mouseover event.
       
 18420  *
       
 18421  * @element ANY
       
 18422  * @priority 0
       
 18423  * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
       
 18424  * mouseover. (Event object is available as `$event`)
       
 18425  *
       
 18426  * @example
       
 18427    <example>
       
 18428      <file name="index.html">
       
 18429       <button ng-mouseover="count = count + 1" ng-init="count=0">
       
 18430         Increment (when mouse is over)
       
 18431       </button>
       
 18432       count: {{count}}
       
 18433      </file>
       
 18434    </example>
       
 18435  */
       
 18436 
       
 18437 
       
 18438 /**
       
 18439  * @ngdoc directive
       
 18440  * @name ngMouseenter
       
 18441  *
       
 18442  * @description
       
 18443  * Specify custom behavior on mouseenter event.
       
 18444  *
       
 18445  * @element ANY
       
 18446  * @priority 0
       
 18447  * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
       
 18448  * mouseenter. (Event object is available as `$event`)
       
 18449  *
       
 18450  * @example
       
 18451    <example>
       
 18452      <file name="index.html">
       
 18453       <button ng-mouseenter="count = count + 1" ng-init="count=0">
       
 18454         Increment (when mouse enters)
       
 18455       </button>
       
 18456       count: {{count}}
       
 18457      </file>
       
 18458    </example>
       
 18459  */
       
 18460 
       
 18461 
       
 18462 /**
       
 18463  * @ngdoc directive
       
 18464  * @name ngMouseleave
       
 18465  *
       
 18466  * @description
       
 18467  * Specify custom behavior on mouseleave event.
       
 18468  *
       
 18469  * @element ANY
       
 18470  * @priority 0
       
 18471  * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
       
 18472  * mouseleave. (Event object is available as `$event`)
       
 18473  *
       
 18474  * @example
       
 18475    <example>
       
 18476      <file name="index.html">
       
 18477       <button ng-mouseleave="count = count + 1" ng-init="count=0">
       
 18478         Increment (when mouse leaves)
       
 18479       </button>
       
 18480       count: {{count}}
       
 18481      </file>
       
 18482    </example>
       
 18483  */
       
 18484 
       
 18485 
       
 18486 /**
       
 18487  * @ngdoc directive
       
 18488  * @name ngMousemove
       
 18489  *
       
 18490  * @description
       
 18491  * Specify custom behavior on mousemove event.
       
 18492  *
       
 18493  * @element ANY
       
 18494  * @priority 0
       
 18495  * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
       
 18496  * mousemove. (Event object is available as `$event`)
       
 18497  *
       
 18498  * @example
       
 18499    <example>
       
 18500      <file name="index.html">
       
 18501       <button ng-mousemove="count = count + 1" ng-init="count=0">
       
 18502         Increment (when mouse moves)
       
 18503       </button>
       
 18504       count: {{count}}
       
 18505      </file>
       
 18506    </example>
       
 18507  */
       
 18508 
       
 18509 
       
 18510 /**
       
 18511  * @ngdoc directive
       
 18512  * @name ngKeydown
       
 18513  *
       
 18514  * @description
       
 18515  * Specify custom behavior on keydown event.
       
 18516  *
       
 18517  * @element ANY
       
 18518  * @priority 0
       
 18519  * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
       
 18520  * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
       
 18521  *
       
 18522  * @example
       
 18523    <example>
       
 18524      <file name="index.html">
       
 18525       <input ng-keydown="count = count + 1" ng-init="count=0">
       
 18526       key down count: {{count}}
       
 18527      </file>
       
 18528    </example>
       
 18529  */
       
 18530 
       
 18531 
       
 18532 /**
       
 18533  * @ngdoc directive
       
 18534  * @name ngKeyup
       
 18535  *
       
 18536  * @description
       
 18537  * Specify custom behavior on keyup event.
       
 18538  *
       
 18539  * @element ANY
       
 18540  * @priority 0
       
 18541  * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
       
 18542  * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
       
 18543  *
       
 18544  * @example
       
 18545    <example>
       
 18546      <file name="index.html">
       
 18547       <input ng-keyup="count = count + 1" ng-init="count=0">
       
 18548       key up count: {{count}}
       
 18549      </file>
       
 18550    </example>
       
 18551  */
       
 18552 
       
 18553 
       
 18554 /**
       
 18555  * @ngdoc directive
       
 18556  * @name ngKeypress
       
 18557  *
       
 18558  * @description
       
 18559  * Specify custom behavior on keypress event.
       
 18560  *
       
 18561  * @element ANY
       
 18562  * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
       
 18563  * keypress. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
       
 18564  *
       
 18565  * @example
       
 18566    <example>
       
 18567      <file name="index.html">
       
 18568       <input ng-keypress="count = count + 1" ng-init="count=0">
       
 18569       key press count: {{count}}
       
 18570      </file>
       
 18571    </example>
       
 18572  */
       
 18573 
       
 18574 
       
 18575 /**
       
 18576  * @ngdoc directive
       
 18577  * @name ngSubmit
       
 18578  *
       
 18579  * @description
       
 18580  * Enables binding angular expressions to onsubmit events.
       
 18581  *
       
 18582  * Additionally it prevents the default action (which for form means sending the request to the
       
 18583  * server and reloading the current page), but only if the form does not contain `action`,
       
 18584  * `data-action`, or `x-action` attributes.
       
 18585  *
       
 18586  * @element form
       
 18587  * @priority 0
       
 18588  * @param {expression} ngSubmit {@link guide/expression Expression} to eval. (Event object is available as `$event`)
       
 18589  *
       
 18590  * @example
       
 18591    <example>
       
 18592      <file name="index.html">
       
 18593       <script>
       
 18594         function Ctrl($scope) {
       
 18595           $scope.list = [];
       
 18596           $scope.text = 'hello';
       
 18597           $scope.submit = function() {
       
 18598             if ($scope.text) {
       
 18599               $scope.list.push(this.text);
       
 18600               $scope.text = '';
       
 18601             }
       
 18602           };
       
 18603         }
       
 18604       </script>
       
 18605       <form ng-submit="submit()" ng-controller="Ctrl">
       
 18606         Enter text and hit enter:
       
 18607         <input type="text" ng-model="text" name="text" />
       
 18608         <input type="submit" id="submit" value="Submit" />
       
 18609         <pre>list={{list}}</pre>
       
 18610       </form>
       
 18611      </file>
       
 18612      <file name="protractor.js" type="protractor">
       
 18613        it('should check ng-submit', function() {
       
 18614          expect(element(by.binding('list')).getText()).toBe('list=[]');
       
 18615          element(by.css('#submit')).click();
       
 18616          expect(element(by.binding('list')).getText()).toContain('hello');
       
 18617          expect(element(by.input('text')).getAttribute('value')).toBe('');
       
 18618        });
       
 18619        it('should ignore empty strings', function() {
       
 18620          expect(element(by.binding('list')).getText()).toBe('list=[]');
       
 18621          element(by.css('#submit')).click();
       
 18622          element(by.css('#submit')).click();
       
 18623          expect(element(by.binding('list')).getText()).toContain('hello');
       
 18624         });
       
 18625      </file>
       
 18626    </example>
       
 18627  */
       
 18628 
       
 18629 /**
       
 18630  * @ngdoc directive
       
 18631  * @name ngFocus
       
 18632  *
       
 18633  * @description
       
 18634  * Specify custom behavior on focus event.
       
 18635  *
       
 18636  * @element window, input, select, textarea, a
       
 18637  * @priority 0
       
 18638  * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
       
 18639  * focus. (Event object is available as `$event`)
       
 18640  *
       
 18641  * @example
       
 18642  * See {@link ng.directive:ngClick ngClick}
       
 18643  */
       
 18644 
       
 18645 /**
       
 18646  * @ngdoc directive
       
 18647  * @name ngBlur
       
 18648  *
       
 18649  * @description
       
 18650  * Specify custom behavior on blur event.
       
 18651  *
       
 18652  * @element window, input, select, textarea, a
       
 18653  * @priority 0
       
 18654  * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
       
 18655  * blur. (Event object is available as `$event`)
       
 18656  *
       
 18657  * @example
       
 18658  * See {@link ng.directive:ngClick ngClick}
       
 18659  */
       
 18660 
       
 18661 /**
       
 18662  * @ngdoc directive
       
 18663  * @name ngCopy
       
 18664  *
       
 18665  * @description
       
 18666  * Specify custom behavior on copy event.
       
 18667  *
       
 18668  * @element window, input, select, textarea, a
       
 18669  * @priority 0
       
 18670  * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
       
 18671  * copy. (Event object is available as `$event`)
       
 18672  *
       
 18673  * @example
       
 18674    <example>
       
 18675      <file name="index.html">
       
 18676       <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
       
 18677       copied: {{copied}}
       
 18678      </file>
       
 18679    </example>
       
 18680  */
       
 18681 
       
 18682 /**
       
 18683  * @ngdoc directive
       
 18684  * @name ngCut
       
 18685  *
       
 18686  * @description
       
 18687  * Specify custom behavior on cut event.
       
 18688  *
       
 18689  * @element window, input, select, textarea, a
       
 18690  * @priority 0
       
 18691  * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
       
 18692  * cut. (Event object is available as `$event`)
       
 18693  *
       
 18694  * @example
       
 18695    <example>
       
 18696      <file name="index.html">
       
 18697       <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
       
 18698       cut: {{cut}}
       
 18699      </file>
       
 18700    </example>
       
 18701  */
       
 18702 
       
 18703 /**
       
 18704  * @ngdoc directive
       
 18705  * @name ngPaste
       
 18706  *
       
 18707  * @description
       
 18708  * Specify custom behavior on paste event.
       
 18709  *
       
 18710  * @element window, input, select, textarea, a
       
 18711  * @priority 0
       
 18712  * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
       
 18713  * paste. (Event object is available as `$event`)
       
 18714  *
       
 18715  * @example
       
 18716    <example>
       
 18717      <file name="index.html">
       
 18718       <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
       
 18719       pasted: {{paste}}
       
 18720      </file>
       
 18721    </example>
       
 18722  */
       
 18723 
       
 18724 /**
       
 18725  * @ngdoc directive
       
 18726  * @name ngIf
       
 18727  * @restrict A
       
 18728  *
       
 18729  * @description
       
 18730  * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
       
 18731  * {expression}. If the expression assigned to `ngIf` evaluates to a false
       
 18732  * value then the element is removed from the DOM, otherwise a clone of the
       
 18733  * element is reinserted into the DOM.
       
 18734  *
       
 18735  * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
       
 18736  * element in the DOM rather than changing its visibility via the `display` css property.  A common
       
 18737  * case when this difference is significant is when using css selectors that rely on an element's
       
 18738  * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
       
 18739  *
       
 18740  * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
       
 18741  * is created when the element is restored.  The scope created within `ngIf` inherits from
       
 18742  * its parent scope using
       
 18743  * [prototypal inheritance](https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance).
       
 18744  * An important implication of this is if `ngModel` is used within `ngIf` to bind to
       
 18745  * a javascript primitive defined in the parent scope. In this case any modifications made to the
       
 18746  * variable within the child scope will override (hide) the value in the parent scope.
       
 18747  *
       
 18748  * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
       
 18749  * is if an element's class attribute is directly modified after it's compiled, using something like
       
 18750  * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
       
 18751  * the added class will be lost because the original compiled state is used to regenerate the element.
       
 18752  *
       
 18753  * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
       
 18754  * and `leave` effects.
       
 18755  *
       
 18756  * @animations
       
 18757  * enter - happens just after the ngIf contents change and a new DOM element is created and injected into the ngIf container
       
 18758  * leave - happens just before the ngIf contents are removed from the DOM
       
 18759  *
       
 18760  * @element ANY
       
 18761  * @scope
       
 18762  * @priority 600
       
 18763  * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
       
 18764  *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
       
 18765  *     element is added to the DOM tree.
       
 18766  *
       
 18767  * @example
       
 18768   <example module="ngAnimate" deps="angular-animate.js" animations="true">
       
 18769     <file name="index.html">
       
 18770       Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /><br/>
       
 18771       Show when checked:
       
 18772       <span ng-if="checked" class="animate-if">
       
 18773         I'm removed when the checkbox is unchecked.
       
 18774       </span>
       
 18775     </file>
       
 18776     <file name="animations.css">
       
 18777       .animate-if {
       
 18778         background:white;
       
 18779         border:1px solid black;
       
 18780         padding:10px;
       
 18781       }
       
 18782 
       
 18783       .animate-if.ng-enter, .animate-if.ng-leave {
       
 18784         -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
       
 18785         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
       
 18786       }
       
 18787 
       
 18788       .animate-if.ng-enter,
       
 18789       .animate-if.ng-leave.ng-leave-active {
       
 18790         opacity:0;
       
 18791       }
       
 18792 
       
 18793       .animate-if.ng-leave,
       
 18794       .animate-if.ng-enter.ng-enter-active {
       
 18795         opacity:1;
       
 18796       }
       
 18797     </file>
       
 18798   </example>
       
 18799  */
       
 18800 var ngIfDirective = ['$animate', function($animate) {
       
 18801   return {
       
 18802     transclude: 'element',
       
 18803     priority: 600,
       
 18804     terminal: true,
       
 18805     restrict: 'A',
       
 18806     $$tlb: true,
       
 18807     link: function ($scope, $element, $attr, ctrl, $transclude) {
       
 18808         var block, childScope, previousElements;
       
 18809         $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
       
 18810 
       
 18811           if (toBoolean(value)) {
       
 18812             if (!childScope) {
       
 18813               childScope = $scope.$new();
       
 18814               $transclude(childScope, function (clone) {
       
 18815                 clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
       
 18816                 // Note: We only need the first/last node of the cloned nodes.
       
 18817                 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
       
 18818                 // by a directive with templateUrl when it's template arrives.
       
 18819                 block = {
       
 18820                   clone: clone
       
 18821                 };
       
 18822                 $animate.enter(clone, $element.parent(), $element);
       
 18823               });
       
 18824             }
       
 18825           } else {
       
 18826             if(previousElements) {
       
 18827               previousElements.remove();
       
 18828               previousElements = null;
       
 18829             }
       
 18830             if(childScope) {
       
 18831               childScope.$destroy();
       
 18832               childScope = null;
       
 18833             }
       
 18834             if(block) {
       
 18835               previousElements = getBlockElements(block.clone);
       
 18836               $animate.leave(previousElements, function() {
       
 18837                 previousElements = null;
       
 18838               });
       
 18839               block = null;
       
 18840             }
       
 18841           }
       
 18842         });
       
 18843     }
       
 18844   };
       
 18845 }];
       
 18846 
       
 18847 /**
       
 18848  * @ngdoc directive
       
 18849  * @name ngInclude
       
 18850  * @restrict ECA
       
 18851  *
       
 18852  * @description
       
 18853  * Fetches, compiles and includes an external HTML fragment.
       
 18854  *
       
 18855  * By default, the template URL is restricted to the same domain and protocol as the
       
 18856  * application document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
       
 18857  * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
       
 18858  * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
       
 18859  * [wrap them](ng.$sce#trustAsResourceUrl) as trusted values. Refer to Angular's {@link
       
 18860  * ng.$sce Strict Contextual Escaping}.
       
 18861  *
       
 18862  * In addition, the browser's
       
 18863  * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
       
 18864  * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
       
 18865  * policy may further restrict whether the template is successfully loaded.
       
 18866  * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
       
 18867  * access on some browsers.
       
 18868  *
       
 18869  * @animations
       
 18870  * enter - animation is used to bring new content into the browser.
       
 18871  * leave - animation is used to animate existing content away.
       
 18872  *
       
 18873  * The enter and leave animation occur concurrently.
       
 18874  *
       
 18875  * @scope
       
 18876  * @priority 400
       
 18877  *
       
 18878  * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
       
 18879  *                 make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
       
 18880  * @param {string=} onload Expression to evaluate when a new partial is loaded.
       
 18881  *
       
 18882  * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
       
 18883  *                  $anchorScroll} to scroll the viewport after the content is loaded.
       
 18884  *
       
 18885  *                  - If the attribute is not set, disable scrolling.
       
 18886  *                  - If the attribute is set without value, enable scrolling.
       
 18887  *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
       
 18888  *
       
 18889  * @example
       
 18890   <example module="ngAnimate" deps="angular-animate.js" animations="true">
       
 18891     <file name="index.html">
       
 18892      <div ng-controller="Ctrl">
       
 18893        <select ng-model="template" ng-options="t.name for t in templates">
       
 18894         <option value="">(blank)</option>
       
 18895        </select>
       
 18896        url of the template: <tt>{{template.url}}</tt>
       
 18897        <hr/>
       
 18898        <div class="slide-animate-container">
       
 18899          <div class="slide-animate" ng-include="template.url"></div>
       
 18900        </div>
       
 18901      </div>
       
 18902     </file>
       
 18903     <file name="script.js">
       
 18904       function Ctrl($scope) {
       
 18905         $scope.templates =
       
 18906           [ { name: 'template1.html', url: 'template1.html'},
       
 18907             { name: 'template2.html', url: 'template2.html'} ];
       
 18908         $scope.template = $scope.templates[0];
       
 18909       }
       
 18910      </file>
       
 18911     <file name="template1.html">
       
 18912       Content of template1.html
       
 18913     </file>
       
 18914     <file name="template2.html">
       
 18915       Content of template2.html
       
 18916     </file>
       
 18917     <file name="animations.css">
       
 18918       .slide-animate-container {
       
 18919         position:relative;
       
 18920         background:white;
       
 18921         border:1px solid black;
       
 18922         height:40px;
       
 18923         overflow:hidden;
       
 18924       }
       
 18925 
       
 18926       .slide-animate {
       
 18927         padding:10px;
       
 18928       }
       
 18929 
       
 18930       .slide-animate.ng-enter, .slide-animate.ng-leave {
       
 18931         -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
       
 18932         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
       
 18933 
       
 18934         position:absolute;
       
 18935         top:0;
       
 18936         left:0;
       
 18937         right:0;
       
 18938         bottom:0;
       
 18939         display:block;
       
 18940         padding:10px;
       
 18941       }
       
 18942 
       
 18943       .slide-animate.ng-enter {
       
 18944         top:-50px;
       
 18945       }
       
 18946       .slide-animate.ng-enter.ng-enter-active {
       
 18947         top:0;
       
 18948       }
       
 18949 
       
 18950       .slide-animate.ng-leave {
       
 18951         top:0;
       
 18952       }
       
 18953       .slide-animate.ng-leave.ng-leave-active {
       
 18954         top:50px;
       
 18955       }
       
 18956     </file>
       
 18957     <file name="protractor.js" type="protractor">
       
 18958       var templateSelect = element(by.model('template'));
       
 18959       var includeElem = element(by.css('[ng-include]'));
       
 18960 
       
 18961       it('should load template1.html', function() {
       
 18962         expect(includeElem.getText()).toMatch(/Content of template1.html/);
       
 18963       });
       
 18964 
       
 18965       it('should load template2.html', function() {
       
 18966         if (browser.params.browser == 'firefox') {
       
 18967           // Firefox can't handle using selects
       
 18968           // See https://github.com/angular/protractor/issues/480
       
 18969           return;
       
 18970         }
       
 18971         templateSelect.click();
       
 18972         templateSelect.element.all(by.css('option')).get(2).click();
       
 18973         expect(includeElem.getText()).toMatch(/Content of template2.html/);
       
 18974       });
       
 18975 
       
 18976       it('should change to blank', function() {
       
 18977         if (browser.params.browser == 'firefox') {
       
 18978           // Firefox can't handle using selects
       
 18979           return;
       
 18980         }
       
 18981         templateSelect.click();
       
 18982         templateSelect.element.all(by.css('option')).get(0).click();
       
 18983         expect(includeElem.isPresent()).toBe(false);
       
 18984       });
       
 18985     </file>
       
 18986   </example>
       
 18987  */
       
 18988 
       
 18989 
       
 18990 /**
       
 18991  * @ngdoc event
       
 18992  * @name ngInclude#$includeContentRequested
       
 18993  * @eventType emit on the scope ngInclude was declared in
       
 18994  * @description
       
 18995  * Emitted every time the ngInclude content is requested.
       
 18996  */
       
 18997 
       
 18998 
       
 18999 /**
       
 19000  * @ngdoc event
       
 19001  * @name ngInclude#$includeContentLoaded
       
 19002  * @eventType emit on the current ngInclude scope
       
 19003  * @description
       
 19004  * Emitted every time the ngInclude content is reloaded.
       
 19005  */
       
 19006 var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$animate', '$sce',
       
 19007                   function($http,   $templateCache,   $anchorScroll,   $animate,   $sce) {
       
 19008   return {
       
 19009     restrict: 'ECA',
       
 19010     priority: 400,
       
 19011     terminal: true,
       
 19012     transclude: 'element',
       
 19013     controller: angular.noop,
       
 19014     compile: function(element, attr) {
       
 19015       var srcExp = attr.ngInclude || attr.src,
       
 19016           onloadExp = attr.onload || '',
       
 19017           autoScrollExp = attr.autoscroll;
       
 19018 
       
 19019       return function(scope, $element, $attr, ctrl, $transclude) {
       
 19020         var changeCounter = 0,
       
 19021             currentScope,
       
 19022             previousElement,
       
 19023             currentElement;
       
 19024 
       
 19025         var cleanupLastIncludeContent = function() {
       
 19026           if(previousElement) {
       
 19027             previousElement.remove();
       
 19028             previousElement = null;
       
 19029           }
       
 19030           if(currentScope) {
       
 19031             currentScope.$destroy();
       
 19032             currentScope = null;
       
 19033           }
       
 19034           if(currentElement) {
       
 19035             $animate.leave(currentElement, function() {
       
 19036               previousElement = null;
       
 19037             });
       
 19038             previousElement = currentElement;
       
 19039             currentElement = null;
       
 19040           }
       
 19041         };
       
 19042 
       
 19043         scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) {
       
 19044           var afterAnimation = function() {
       
 19045             if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
       
 19046               $anchorScroll();
       
 19047             }
       
 19048           };
       
 19049           var thisChangeId = ++changeCounter;
       
 19050 
       
 19051           if (src) {
       
 19052             $http.get(src, {cache: $templateCache}).success(function(response) {
       
 19053               if (thisChangeId !== changeCounter) return;
       
 19054               var newScope = scope.$new();
       
 19055               ctrl.template = response;
       
 19056 
       
 19057               // Note: This will also link all children of ng-include that were contained in the original
       
 19058               // html. If that content contains controllers, ... they could pollute/change the scope.
       
 19059               // However, using ng-include on an element with additional content does not make sense...
       
 19060               // Note: We can't remove them in the cloneAttchFn of $transclude as that
       
 19061               // function is called before linking the content, which would apply child
       
 19062               // directives to non existing elements.
       
 19063               var clone = $transclude(newScope, function(clone) {
       
 19064                 cleanupLastIncludeContent();
       
 19065                 $animate.enter(clone, null, $element, afterAnimation);
       
 19066               });
       
 19067 
       
 19068               currentScope = newScope;
       
 19069               currentElement = clone;
       
 19070 
       
 19071               currentScope.$emit('$includeContentLoaded');
       
 19072               scope.$eval(onloadExp);
       
 19073             }).error(function() {
       
 19074               if (thisChangeId === changeCounter) cleanupLastIncludeContent();
       
 19075             });
       
 19076             scope.$emit('$includeContentRequested');
       
 19077           } else {
       
 19078             cleanupLastIncludeContent();
       
 19079             ctrl.template = null;
       
 19080           }
       
 19081         });
       
 19082       };
       
 19083     }
       
 19084   };
       
 19085 }];
       
 19086 
       
 19087 // This directive is called during the $transclude call of the first `ngInclude` directive.
       
 19088 // It will replace and compile the content of the element with the loaded template.
       
 19089 // We need this directive so that the element content is already filled when
       
 19090 // the link function of another directive on the same element as ngInclude
       
 19091 // is called.
       
 19092 var ngIncludeFillContentDirective = ['$compile',
       
 19093   function($compile) {
       
 19094     return {
       
 19095       restrict: 'ECA',
       
 19096       priority: -400,
       
 19097       require: 'ngInclude',
       
 19098       link: function(scope, $element, $attr, ctrl) {
       
 19099         $element.html(ctrl.template);
       
 19100         $compile($element.contents())(scope);
       
 19101       }
       
 19102     };
       
 19103   }];
       
 19104 
       
 19105 /**
       
 19106  * @ngdoc directive
       
 19107  * @name ngInit
       
 19108  * @restrict AC
       
 19109  *
       
 19110  * @description
       
 19111  * The `ngInit` directive allows you to evaluate an expression in the
       
 19112  * current scope.
       
 19113  *
       
 19114  * <div class="alert alert-error">
       
 19115  * The only appropriate use of `ngInit` is for aliasing special properties of
       
 19116  * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you
       
 19117  * should use {@link guide/controller controllers} rather than `ngInit`
       
 19118  * to initialize values on a scope.
       
 19119  * </div>
       
 19120  * <div class="alert alert-warning">
       
 19121  * **Note**: If you have assignment in `ngInit` along with {@link ng.$filter `$filter`}, make
       
 19122  * sure you have parenthesis for correct precedence:
       
 19123  * <pre class="prettyprint">
       
 19124  *   <div ng-init="test1 = (data | orderBy:'name')"></div>
       
 19125  * </pre>
       
 19126  * </div>
       
 19127  *
       
 19128  * @priority 450
       
 19129  *
       
 19130  * @element ANY
       
 19131  * @param {expression} ngInit {@link guide/expression Expression} to eval.
       
 19132  *
       
 19133  * @example
       
 19134    <example>
       
 19135      <file name="index.html">
       
 19136    <script>
       
 19137      function Ctrl($scope) {
       
 19138        $scope.list = [['a', 'b'], ['c', 'd']];
       
 19139      }
       
 19140    </script>
       
 19141    <div ng-controller="Ctrl">
       
 19142      <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
       
 19143        <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
       
 19144           <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
       
 19145        </div>
       
 19146      </div>
       
 19147    </div>
       
 19148      </file>
       
 19149      <file name="protractor.js" type="protractor">
       
 19150        it('should alias index positions', function() {
       
 19151          var elements = element.all(by.css('.example-init'));
       
 19152          expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
       
 19153          expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
       
 19154          expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
       
 19155          expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
       
 19156        });
       
 19157      </file>
       
 19158    </example>
       
 19159  */
       
 19160 var ngInitDirective = ngDirective({
       
 19161   priority: 450,
       
 19162   compile: function() {
       
 19163     return {
       
 19164       pre: function(scope, element, attrs) {
       
 19165         scope.$eval(attrs.ngInit);
       
 19166       }
       
 19167     };
       
 19168   }
       
 19169 });
       
 19170 
       
 19171 /**
       
 19172  * @ngdoc directive
       
 19173  * @name ngNonBindable
       
 19174  * @restrict AC
       
 19175  * @priority 1000
       
 19176  *
       
 19177  * @description
       
 19178  * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
       
 19179  * DOM element. This is useful if the element contains what appears to be Angular directives and
       
 19180  * bindings but which should be ignored by Angular. This could be the case if you have a site that
       
 19181  * displays snippets of code, for instance.
       
 19182  *
       
 19183  * @element ANY
       
 19184  *
       
 19185  * @example
       
 19186  * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
       
 19187  * but the one wrapped in `ngNonBindable` is left alone.
       
 19188  *
       
 19189  * @example
       
 19190     <example>
       
 19191       <file name="index.html">
       
 19192         <div>Normal: {{1 + 2}}</div>
       
 19193         <div ng-non-bindable>Ignored: {{1 + 2}}</div>
       
 19194       </file>
       
 19195       <file name="protractor.js" type="protractor">
       
 19196        it('should check ng-non-bindable', function() {
       
 19197          expect(element(by.binding('1 + 2')).getText()).toContain('3');
       
 19198          expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
       
 19199        });
       
 19200       </file>
       
 19201     </example>
       
 19202  */
       
 19203 var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
       
 19204 
       
 19205 /**
       
 19206  * @ngdoc directive
       
 19207  * @name ngPluralize
       
 19208  * @restrict EA
       
 19209  *
       
 19210  * @description
       
 19211  * `ngPluralize` is a directive that displays messages according to en-US localization rules.
       
 19212  * These rules are bundled with angular.js, but can be overridden
       
 19213  * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
       
 19214  * by specifying the mappings between
       
 19215  * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
       
 19216  * and the strings to be displayed.
       
 19217  *
       
 19218  * # Plural categories and explicit number rules
       
 19219  * There are two
       
 19220  * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
       
 19221  * in Angular's default en-US locale: "one" and "other".
       
 19222  *
       
 19223  * While a plural category may match many numbers (for example, in en-US locale, "other" can match
       
 19224  * any number that is not 1), an explicit number rule can only match one number. For example, the
       
 19225  * explicit number rule for "3" matches the number 3. There are examples of plural categories
       
 19226  * and explicit number rules throughout the rest of this documentation.
       
 19227  *
       
 19228  * # Configuring ngPluralize
       
 19229  * You configure ngPluralize by providing 2 attributes: `count` and `when`.
       
 19230  * You can also provide an optional attribute, `offset`.
       
 19231  *
       
 19232  * The value of the `count` attribute can be either a string or an {@link guide/expression
       
 19233  * Angular expression}; these are evaluated on the current scope for its bound value.
       
 19234  *
       
 19235  * The `when` attribute specifies the mappings between plural categories and the actual
       
 19236  * string to be displayed. The value of the attribute should be a JSON object.
       
 19237  *
       
 19238  * The following example shows how to configure ngPluralize:
       
 19239  *
       
 19240  * ```html
       
 19241  * <ng-pluralize count="personCount"
       
 19242                  when="{'0': 'Nobody is viewing.',
       
 19243  *                      'one': '1 person is viewing.',
       
 19244  *                      'other': '{} people are viewing.'}">
       
 19245  * </ng-pluralize>
       
 19246  *```
       
 19247  *
       
 19248  * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
       
 19249  * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
       
 19250  * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
       
 19251  * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
       
 19252  * show "a dozen people are viewing".
       
 19253  *
       
 19254  * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
       
 19255  * into pluralized strings. In the previous example, Angular will replace `{}` with
       
 19256  * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
       
 19257  * for <span ng-non-bindable>{{numberExpression}}</span>.
       
 19258  *
       
 19259  * # Configuring ngPluralize with offset
       
 19260  * The `offset` attribute allows further customization of pluralized text, which can result in
       
 19261  * a better user experience. For example, instead of the message "4 people are viewing this document",
       
 19262  * you might display "John, Kate and 2 others are viewing this document".
       
 19263  * The offset attribute allows you to offset a number by any desired value.
       
 19264  * Let's take a look at an example:
       
 19265  *
       
 19266  * ```html
       
 19267  * <ng-pluralize count="personCount" offset=2
       
 19268  *               when="{'0': 'Nobody is viewing.',
       
 19269  *                      '1': '{{person1}} is viewing.',
       
 19270  *                      '2': '{{person1}} and {{person2}} are viewing.',
       
 19271  *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
       
 19272  *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
       
 19273  * </ng-pluralize>
       
 19274  * ```
       
 19275  *
       
 19276  * Notice that we are still using two plural categories(one, other), but we added
       
 19277  * three explicit number rules 0, 1 and 2.
       
 19278  * When one person, perhaps John, views the document, "John is viewing" will be shown.
       
 19279  * When three people view the document, no explicit number rule is found, so
       
 19280  * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
       
 19281  * In this case, plural category 'one' is matched and "John, Marry and one other person are viewing"
       
 19282  * is shown.
       
 19283  *
       
 19284  * Note that when you specify offsets, you must provide explicit number rules for
       
 19285  * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
       
 19286  * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
       
 19287  * plural categories "one" and "other".
       
 19288  *
       
 19289  * @param {string|expression} count The variable to be bound to.
       
 19290  * @param {string} when The mapping between plural category to its corresponding strings.
       
 19291  * @param {number=} offset Offset to deduct from the total number.
       
 19292  *
       
 19293  * @example
       
 19294     <example>
       
 19295       <file name="index.html">
       
 19296         <script>
       
 19297           function Ctrl($scope) {
       
 19298             $scope.person1 = 'Igor';
       
 19299             $scope.person2 = 'Misko';
       
 19300             $scope.personCount = 1;
       
 19301           }
       
 19302         </script>
       
 19303         <div ng-controller="Ctrl">
       
 19304           Person 1:<input type="text" ng-model="person1" value="Igor" /><br/>
       
 19305           Person 2:<input type="text" ng-model="person2" value="Misko" /><br/>
       
 19306           Number of People:<input type="text" ng-model="personCount" value="1" /><br/>
       
 19307 
       
 19308           <!--- Example with simple pluralization rules for en locale --->
       
 19309           Without Offset:
       
 19310           <ng-pluralize count="personCount"
       
 19311                         when="{'0': 'Nobody is viewing.',
       
 19312                                'one': '1 person is viewing.',
       
 19313                                'other': '{} people are viewing.'}">
       
 19314           </ng-pluralize><br>
       
 19315 
       
 19316           <!--- Example with offset --->
       
 19317           With Offset(2):
       
 19318           <ng-pluralize count="personCount" offset=2
       
 19319                         when="{'0': 'Nobody is viewing.',
       
 19320                                '1': '{{person1}} is viewing.',
       
 19321                                '2': '{{person1}} and {{person2}} are viewing.',
       
 19322                                'one': '{{person1}}, {{person2}} and one other person are viewing.',
       
 19323                                'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
       
 19324           </ng-pluralize>
       
 19325         </div>
       
 19326       </file>
       
 19327       <file name="protractor.js" type="protractor">
       
 19328         it('should show correct pluralized string', function() {
       
 19329           var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
       
 19330           var withOffset = element.all(by.css('ng-pluralize')).get(1);
       
 19331           var countInput = element(by.model('personCount'));
       
 19332 
       
 19333           expect(withoutOffset.getText()).toEqual('1 person is viewing.');
       
 19334           expect(withOffset.getText()).toEqual('Igor is viewing.');
       
 19335 
       
 19336           countInput.clear();
       
 19337           countInput.sendKeys('0');
       
 19338 
       
 19339           expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
       
 19340           expect(withOffset.getText()).toEqual('Nobody is viewing.');
       
 19341 
       
 19342           countInput.clear();
       
 19343           countInput.sendKeys('2');
       
 19344 
       
 19345           expect(withoutOffset.getText()).toEqual('2 people are viewing.');
       
 19346           expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
       
 19347 
       
 19348           countInput.clear();
       
 19349           countInput.sendKeys('3');
       
 19350 
       
 19351           expect(withoutOffset.getText()).toEqual('3 people are viewing.');
       
 19352           expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
       
 19353 
       
 19354           countInput.clear();
       
 19355           countInput.sendKeys('4');
       
 19356 
       
 19357           expect(withoutOffset.getText()).toEqual('4 people are viewing.');
       
 19358           expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
       
 19359         });
       
 19360         it('should show data-bound names', function() {
       
 19361           var withOffset = element.all(by.css('ng-pluralize')).get(1);
       
 19362           var personCount = element(by.model('personCount'));
       
 19363           var person1 = element(by.model('person1'));
       
 19364           var person2 = element(by.model('person2'));
       
 19365           personCount.clear();
       
 19366           personCount.sendKeys('4');
       
 19367           person1.clear();
       
 19368           person1.sendKeys('Di');
       
 19369           person2.clear();
       
 19370           person2.sendKeys('Vojta');
       
 19371           expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
       
 19372         });
       
 19373       </file>
       
 19374     </example>
       
 19375  */
       
 19376 var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) {
       
 19377   var BRACE = /{}/g;
       
 19378   return {
       
 19379     restrict: 'EA',
       
 19380     link: function(scope, element, attr) {
       
 19381       var numberExp = attr.count,
       
 19382           whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
       
 19383           offset = attr.offset || 0,
       
 19384           whens = scope.$eval(whenExp) || {},
       
 19385           whensExpFns = {},
       
 19386           startSymbol = $interpolate.startSymbol(),
       
 19387           endSymbol = $interpolate.endSymbol(),
       
 19388           isWhen = /^when(Minus)?(.+)$/;
       
 19389 
       
 19390       forEach(attr, function(expression, attributeName) {
       
 19391         if (isWhen.test(attributeName)) {
       
 19392           whens[lowercase(attributeName.replace('when', '').replace('Minus', '-'))] =
       
 19393             element.attr(attr.$attr[attributeName]);
       
 19394         }
       
 19395       });
       
 19396       forEach(whens, function(expression, key) {
       
 19397         whensExpFns[key] =
       
 19398           $interpolate(expression.replace(BRACE, startSymbol + numberExp + '-' +
       
 19399             offset + endSymbol));
       
 19400       });
       
 19401 
       
 19402       scope.$watch(function ngPluralizeWatch() {
       
 19403         var value = parseFloat(scope.$eval(numberExp));
       
 19404 
       
 19405         if (!isNaN(value)) {
       
 19406           //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise,
       
 19407           //check it against pluralization rules in $locale service
       
 19408           if (!(value in whens)) value = $locale.pluralCat(value - offset);
       
 19409            return whensExpFns[value](scope, element, true);
       
 19410         } else {
       
 19411           return '';
       
 19412         }
       
 19413       }, function ngPluralizeWatchAction(newVal) {
       
 19414         element.text(newVal);
       
 19415       });
       
 19416     }
       
 19417   };
       
 19418 }];
       
 19419 
       
 19420 /**
       
 19421  * @ngdoc directive
       
 19422  * @name ngRepeat
       
 19423  *
       
 19424  * @description
       
 19425  * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
       
 19426  * instance gets its own scope, where the given loop variable is set to the current collection item,
       
 19427  * and `$index` is set to the item index or key.
       
 19428  *
       
 19429  * Special properties are exposed on the local scope of each template instance, including:
       
 19430  *
       
 19431  * | Variable  | Type            | Details                                                                     |
       
 19432  * |-----------|-----------------|-----------------------------------------------------------------------------|
       
 19433  * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
       
 19434  * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
       
 19435  * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
       
 19436  * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
       
 19437  * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
       
 19438  * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
       
 19439  *
       
 19440  * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
       
 19441  * This may be useful when, for instance, nesting ngRepeats.
       
 19442  *
       
 19443  * # Special repeat start and end points
       
 19444  * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
       
 19445  * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
       
 19446  * The **ng-repeat-start** directive works the same as **ng-repeat**, but will repeat all the HTML code (including the tag it's defined on)
       
 19447  * up to and including the ending HTML tag where **ng-repeat-end** is placed.
       
 19448  *
       
 19449  * The example below makes use of this feature:
       
 19450  * ```html
       
 19451  *   <header ng-repeat-start="item in items">
       
 19452  *     Header {{ item }}
       
 19453  *   </header>
       
 19454  *   <div class="body">
       
 19455  *     Body {{ item }}
       
 19456  *   </div>
       
 19457  *   <footer ng-repeat-end>
       
 19458  *     Footer {{ item }}
       
 19459  *   </footer>
       
 19460  * ```
       
 19461  *
       
 19462  * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
       
 19463  * ```html
       
 19464  *   <header>
       
 19465  *     Header A
       
 19466  *   </header>
       
 19467  *   <div class="body">
       
 19468  *     Body A
       
 19469  *   </div>
       
 19470  *   <footer>
       
 19471  *     Footer A
       
 19472  *   </footer>
       
 19473  *   <header>
       
 19474  *     Header B
       
 19475  *   </header>
       
 19476  *   <div class="body">
       
 19477  *     Body B
       
 19478  *   </div>
       
 19479  *   <footer>
       
 19480  *     Footer B
       
 19481  *   </footer>
       
 19482  * ```
       
 19483  *
       
 19484  * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
       
 19485  * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
       
 19486  *
       
 19487  * @animations
       
 19488  * **.enter** - when a new item is added to the list or when an item is revealed after a filter
       
 19489  *
       
 19490  * **.leave** - when an item is removed from the list or when an item is filtered out
       
 19491  *
       
 19492  * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
       
 19493  *
       
 19494  * @element ANY
       
 19495  * @scope
       
 19496  * @priority 1000
       
 19497  * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
       
 19498  *   formats are currently supported:
       
 19499  *
       
 19500  *   * `variable in expression` – where variable is the user defined loop variable and `expression`
       
 19501  *     is a scope expression giving the collection to enumerate.
       
 19502  *
       
 19503  *     For example: `album in artist.albums`.
       
 19504  *
       
 19505  *   * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
       
 19506  *     and `expression` is the scope expression giving the collection to enumerate.
       
 19507  *
       
 19508  *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
       
 19509  *
       
 19510  *   * `variable in expression track by tracking_expression` – You can also provide an optional tracking function
       
 19511  *     which can be used to associate the objects in the collection with the DOM elements. If no tracking function
       
 19512  *     is specified the ng-repeat associates elements by identity in the collection. It is an error to have
       
 19513  *     more than one tracking function to resolve to the same key. (This would mean that two distinct objects are
       
 19514  *     mapped to the same DOM element, which is not possible.)  Filters should be applied to the expression,
       
 19515  *     before specifying a tracking expression.
       
 19516  *
       
 19517  *     For example: `item in items` is equivalent to `item in items track by $id(item)'. This implies that the DOM elements
       
 19518  *     will be associated by item identity in the array.
       
 19519  *
       
 19520  *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
       
 19521  *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
       
 19522  *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM
       
 19523  *     element in the same way in the DOM.
       
 19524  *
       
 19525  *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
       
 19526  *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`
       
 19527  *     property is same.
       
 19528  *
       
 19529  *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
       
 19530  *     to items in conjunction with a tracking expression.
       
 19531  *
       
 19532  * @example
       
 19533  * This example initializes the scope to a list of names and
       
 19534  * then uses `ngRepeat` to display every person:
       
 19535   <example module="ngAnimate" deps="angular-animate.js" animations="true">
       
 19536     <file name="index.html">
       
 19537       <div ng-init="friends = [
       
 19538         {name:'John', age:25, gender:'boy'},
       
 19539         {name:'Jessie', age:30, gender:'girl'},
       
 19540         {name:'Johanna', age:28, gender:'girl'},
       
 19541         {name:'Joy', age:15, gender:'girl'},
       
 19542         {name:'Mary', age:28, gender:'girl'},
       
 19543         {name:'Peter', age:95, gender:'boy'},
       
 19544         {name:'Sebastian', age:50, gender:'boy'},
       
 19545         {name:'Erika', age:27, gender:'girl'},
       
 19546         {name:'Patrick', age:40, gender:'boy'},
       
 19547         {name:'Samantha', age:60, gender:'girl'}
       
 19548       ]">
       
 19549         I have {{friends.length}} friends. They are:
       
 19550         <input type="search" ng-model="q" placeholder="filter friends..." />
       
 19551         <ul class="example-animate-container">
       
 19552           <li class="animate-repeat" ng-repeat="friend in friends | filter:q">
       
 19553             [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
       
 19554           </li>
       
 19555         </ul>
       
 19556       </div>
       
 19557     </file>
       
 19558     <file name="animations.css">
       
 19559       .example-animate-container {
       
 19560         background:white;
       
 19561         border:1px solid black;
       
 19562         list-style:none;
       
 19563         margin:0;
       
 19564         padding:0 10px;
       
 19565       }
       
 19566 
       
 19567       .animate-repeat {
       
 19568         line-height:40px;
       
 19569         list-style:none;
       
 19570         box-sizing:border-box;
       
 19571       }
       
 19572 
       
 19573       .animate-repeat.ng-move,
       
 19574       .animate-repeat.ng-enter,
       
 19575       .animate-repeat.ng-leave {
       
 19576         -webkit-transition:all linear 0.5s;
       
 19577         transition:all linear 0.5s;
       
 19578       }
       
 19579 
       
 19580       .animate-repeat.ng-leave.ng-leave-active,
       
 19581       .animate-repeat.ng-move,
       
 19582       .animate-repeat.ng-enter {
       
 19583         opacity:0;
       
 19584         max-height:0;
       
 19585       }
       
 19586 
       
 19587       .animate-repeat.ng-leave,
       
 19588       .animate-repeat.ng-move.ng-move-active,
       
 19589       .animate-repeat.ng-enter.ng-enter-active {
       
 19590         opacity:1;
       
 19591         max-height:40px;
       
 19592       }
       
 19593     </file>
       
 19594     <file name="protractor.js" type="protractor">
       
 19595       var friends = element.all(by.repeater('friend in friends'));
       
 19596 
       
 19597       it('should render initial data set', function() {
       
 19598         expect(friends.count()).toBe(10);
       
 19599         expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
       
 19600         expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
       
 19601         expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
       
 19602         expect(element(by.binding('friends.length')).getText())
       
 19603             .toMatch("I have 10 friends. They are:");
       
 19604       });
       
 19605 
       
 19606        it('should update repeater when filter predicate changes', function() {
       
 19607          expect(friends.count()).toBe(10);
       
 19608 
       
 19609          element(by.model('q')).sendKeys('ma');
       
 19610 
       
 19611          expect(friends.count()).toBe(2);
       
 19612          expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
       
 19613          expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
       
 19614        });
       
 19615       </file>
       
 19616     </example>
       
 19617  */
       
 19618 var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
       
 19619   var NG_REMOVED = '$$NG_REMOVED';
       
 19620   var ngRepeatMinErr = minErr('ngRepeat');
       
 19621   return {
       
 19622     transclude: 'element',
       
 19623     priority: 1000,
       
 19624     terminal: true,
       
 19625     $$tlb: true,
       
 19626     link: function($scope, $element, $attr, ctrl, $transclude){
       
 19627         var expression = $attr.ngRepeat;
       
 19628         var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),
       
 19629           trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn,
       
 19630           lhs, rhs, valueIdentifier, keyIdentifier,
       
 19631           hashFnLocals = {$id: hashKey};
       
 19632 
       
 19633         if (!match) {
       
 19634           throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
       
 19635             expression);
       
 19636         }
       
 19637 
       
 19638         lhs = match[1];
       
 19639         rhs = match[2];
       
 19640         trackByExp = match[3];
       
 19641 
       
 19642         if (trackByExp) {
       
 19643           trackByExpGetter = $parse(trackByExp);
       
 19644           trackByIdExpFn = function(key, value, index) {
       
 19645             // assign key, value, and $index to the locals so that they can be used in hash functions
       
 19646             if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
       
 19647             hashFnLocals[valueIdentifier] = value;
       
 19648             hashFnLocals.$index = index;
       
 19649             return trackByExpGetter($scope, hashFnLocals);
       
 19650           };
       
 19651         } else {
       
 19652           trackByIdArrayFn = function(key, value) {
       
 19653             return hashKey(value);
       
 19654           };
       
 19655           trackByIdObjFn = function(key) {
       
 19656             return key;
       
 19657           };
       
 19658         }
       
 19659 
       
 19660         match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);
       
 19661         if (!match) {
       
 19662           throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
       
 19663                                                                     lhs);
       
 19664         }
       
 19665         valueIdentifier = match[3] || match[1];
       
 19666         keyIdentifier = match[2];
       
 19667 
       
 19668         // Store a list of elements from previous run. This is a hash where key is the item from the
       
 19669         // iterator, and the value is objects with following properties.
       
 19670         //   - scope: bound scope
       
 19671         //   - element: previous element.
       
 19672         //   - index: position
       
 19673         var lastBlockMap = {};
       
 19674 
       
 19675         //watch props
       
 19676         $scope.$watchCollection(rhs, function ngRepeatAction(collection){
       
 19677           var index, length,
       
 19678               previousNode = $element[0],     // current position of the node
       
 19679               nextNode,
       
 19680               // Same as lastBlockMap but it has the current state. It will become the
       
 19681               // lastBlockMap on the next iteration.
       
 19682               nextBlockMap = {},
       
 19683               arrayLength,
       
 19684               childScope,
       
 19685               key, value, // key/value of iteration
       
 19686               trackById,
       
 19687               trackByIdFn,
       
 19688               collectionKeys,
       
 19689               block,       // last object information {scope, element, id}
       
 19690               nextBlockOrder = [],
       
 19691               elementsToRemove;
       
 19692 
       
 19693 
       
 19694           if (isArrayLike(collection)) {
       
 19695             collectionKeys = collection;
       
 19696             trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
       
 19697           } else {
       
 19698             trackByIdFn = trackByIdExpFn || trackByIdObjFn;
       
 19699             // if object, extract keys, sort them and use to determine order of iteration over obj props
       
 19700             collectionKeys = [];
       
 19701             for (key in collection) {
       
 19702               if (collection.hasOwnProperty(key) && key.charAt(0) != '$') {
       
 19703                 collectionKeys.push(key);
       
 19704               }
       
 19705             }
       
 19706             collectionKeys.sort();
       
 19707           }
       
 19708 
       
 19709           arrayLength = collectionKeys.length;
       
 19710 
       
 19711           // locate existing items
       
 19712           length = nextBlockOrder.length = collectionKeys.length;
       
 19713           for(index = 0; index < length; index++) {
       
 19714            key = (collection === collectionKeys) ? index : collectionKeys[index];
       
 19715            value = collection[key];
       
 19716            trackById = trackByIdFn(key, value, index);
       
 19717            assertNotHasOwnProperty(trackById, '`track by` id');
       
 19718            if(lastBlockMap.hasOwnProperty(trackById)) {
       
 19719              block = lastBlockMap[trackById];
       
 19720              delete lastBlockMap[trackById];
       
 19721              nextBlockMap[trackById] = block;
       
 19722              nextBlockOrder[index] = block;
       
 19723            } else if (nextBlockMap.hasOwnProperty(trackById)) {
       
 19724              // restore lastBlockMap
       
 19725              forEach(nextBlockOrder, function(block) {
       
 19726                if (block && block.scope) lastBlockMap[block.id] = block;
       
 19727              });
       
 19728              // This is a duplicate and we need to throw an error
       
 19729              throw ngRepeatMinErr('dupes', "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}",
       
 19730                                                                                                                                                     expression,       trackById);
       
 19731            } else {
       
 19732              // new never before seen block
       
 19733              nextBlockOrder[index] = { id: trackById };
       
 19734              nextBlockMap[trackById] = false;
       
 19735            }
       
 19736          }
       
 19737 
       
 19738           // remove existing items
       
 19739           for (key in lastBlockMap) {
       
 19740             // lastBlockMap is our own object so we don't need to use special hasOwnPropertyFn
       
 19741             if (lastBlockMap.hasOwnProperty(key)) {
       
 19742               block = lastBlockMap[key];
       
 19743               elementsToRemove = getBlockElements(block.clone);
       
 19744               $animate.leave(elementsToRemove);
       
 19745               forEach(elementsToRemove, function(element) { element[NG_REMOVED] = true; });
       
 19746               block.scope.$destroy();
       
 19747             }
       
 19748           }
       
 19749 
       
 19750           // we are not using forEach for perf reasons (trying to avoid #call)
       
 19751           for (index = 0, length = collectionKeys.length; index < length; index++) {
       
 19752             key = (collection === collectionKeys) ? index : collectionKeys[index];
       
 19753             value = collection[key];
       
 19754             block = nextBlockOrder[index];
       
 19755             if (nextBlockOrder[index - 1]) previousNode = getBlockEnd(nextBlockOrder[index - 1]);
       
 19756 
       
 19757             if (block.scope) {
       
 19758               // if we have already seen this object, then we need to reuse the
       
 19759               // associated scope/element
       
 19760               childScope = block.scope;
       
 19761 
       
 19762               nextNode = previousNode;
       
 19763               do {
       
 19764                 nextNode = nextNode.nextSibling;
       
 19765               } while(nextNode && nextNode[NG_REMOVED]);
       
 19766 
       
 19767               if (getBlockStart(block) != nextNode) {
       
 19768                 // existing item which got moved
       
 19769                 $animate.move(getBlockElements(block.clone), null, jqLite(previousNode));
       
 19770               }
       
 19771               previousNode = getBlockEnd(block);
       
 19772             } else {
       
 19773               // new item which we don't know about
       
 19774               childScope = $scope.$new();
       
 19775             }
       
 19776 
       
 19777             childScope[valueIdentifier] = value;
       
 19778             if (keyIdentifier) childScope[keyIdentifier] = key;
       
 19779             childScope.$index = index;
       
 19780             childScope.$first = (index === 0);
       
 19781             childScope.$last = (index === (arrayLength - 1));
       
 19782             childScope.$middle = !(childScope.$first || childScope.$last);
       
 19783             // jshint bitwise: false
       
 19784             childScope.$odd = !(childScope.$even = (index&1) === 0);
       
 19785             // jshint bitwise: true
       
 19786 
       
 19787             if (!block.scope) {
       
 19788               $transclude(childScope, function(clone) {
       
 19789                 clone[clone.length++] = document.createComment(' end ngRepeat: ' + expression + ' ');
       
 19790                 $animate.enter(clone, null, jqLite(previousNode));
       
 19791                 previousNode = clone;
       
 19792                 block.scope = childScope;
       
 19793                 // Note: We only need the first/last node of the cloned nodes.
       
 19794                 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
       
 19795                 // by a directive with templateUrl when it's template arrives.
       
 19796                 block.clone = clone;
       
 19797                 nextBlockMap[block.id] = block;
       
 19798               });
       
 19799             }
       
 19800           }
       
 19801           lastBlockMap = nextBlockMap;
       
 19802         });
       
 19803     }
       
 19804   };
       
 19805 
       
 19806   function getBlockStart(block) {
       
 19807     return block.clone[0];
       
 19808   }
       
 19809 
       
 19810   function getBlockEnd(block) {
       
 19811     return block.clone[block.clone.length - 1];
       
 19812   }
       
 19813 }];
       
 19814 
       
 19815 /**
       
 19816  * @ngdoc directive
       
 19817  * @name ngShow
       
 19818  *
       
 19819  * @description
       
 19820  * The `ngShow` directive shows or hides the given HTML element based on the expression
       
 19821  * provided to the ngShow attribute. The element is shown or hidden by removing or adding
       
 19822  * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
       
 19823  * in AngularJS and sets the display style to none (using an !important flag).
       
 19824  * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
       
 19825  *
       
 19826  * ```html
       
 19827  * <!-- when $scope.myValue is truthy (element is visible) -->
       
 19828  * <div ng-show="myValue"></div>
       
 19829  *
       
 19830  * <!-- when $scope.myValue is falsy (element is hidden) -->
       
 19831  * <div ng-show="myValue" class="ng-hide"></div>
       
 19832  * ```
       
 19833  *
       
 19834  * When the ngShow expression evaluates to false then the ng-hide CSS class is added to the class attribute
       
 19835  * on the element causing it to become hidden. When true, the ng-hide CSS class is removed
       
 19836  * from the element causing the element not to appear hidden.
       
 19837  *
       
 19838  * ## Why is !important used?
       
 19839  *
       
 19840  * You may be wondering why !important is used for the .ng-hide CSS class. This is because the `.ng-hide` selector
       
 19841  * can be easily overridden by heavier selectors. For example, something as simple
       
 19842  * as changing the display style on a HTML list item would make hidden elements appear visible.
       
 19843  * This also becomes a bigger issue when dealing with CSS frameworks.
       
 19844  *
       
 19845  * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
       
 19846  * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
       
 19847  * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
       
 19848  *
       
 19849  * ### Overriding .ng-hide
       
 19850  *
       
 19851  * If you wish to change the hide behavior with ngShow/ngHide then this can be achieved by
       
 19852  * restating the styles for the .ng-hide class in CSS:
       
 19853  * ```css
       
 19854  * .ng-hide {
       
 19855  *   //!annotate CSS Specificity|Not to worry, this will override the AngularJS default...
       
 19856  *   display:block!important;
       
 19857  *
       
 19858  *   //this is just another form of hiding an element
       
 19859  *   position:absolute;
       
 19860  *   top:-9999px;
       
 19861  *   left:-9999px;
       
 19862  * }
       
 19863  * ```
       
 19864  *
       
 19865  * Just remember to include the important flag so the CSS override will function.
       
 19866  *
       
 19867  * <div class="alert alert-warning">
       
 19868  * **Note:** Here is a list of values that ngShow will consider as a falsy value (case insensitive):<br />
       
 19869  * "f" / "0" / "false" / "no" / "n" / "[]"
       
 19870  * </div>
       
 19871  *
       
 19872  * ## A note about animations with ngShow
       
 19873  *
       
 19874  * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
       
 19875  * is true and false. This system works like the animation system present with ngClass except that
       
 19876  * you must also include the !important flag to override the display property
       
 19877  * so that you can perform an animation when the element is hidden during the time of the animation.
       
 19878  *
       
 19879  * ```css
       
 19880  * //
       
 19881  * //a working example can be found at the bottom of this page
       
 19882  * //
       
 19883  * .my-element.ng-hide-add, .my-element.ng-hide-remove {
       
 19884  *   transition:0.5s linear all;
       
 19885  *   display:block!important;
       
 19886  * }
       
 19887  *
       
 19888  * .my-element.ng-hide-add { ... }
       
 19889  * .my-element.ng-hide-add.ng-hide-add-active { ... }
       
 19890  * .my-element.ng-hide-remove { ... }
       
 19891  * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
       
 19892  * ```
       
 19893  *
       
 19894  * @animations
       
 19895  * addClass: .ng-hide - happens after the ngShow expression evaluates to a truthy value and the just before contents are set to visible
       
 19896  * removeClass: .ng-hide - happens after the ngShow expression evaluates to a non truthy value and just before the contents are set to hidden
       
 19897  *
       
 19898  * @element ANY
       
 19899  * @param {expression} ngShow If the {@link guide/expression expression} is truthy
       
 19900  *     then the element is shown or hidden respectively.
       
 19901  *
       
 19902  * @example
       
 19903   <example module="ngAnimate" deps="angular-animate.js" animations="true">
       
 19904     <file name="index.html">
       
 19905       Click me: <input type="checkbox" ng-model="checked"><br/>
       
 19906       <div>
       
 19907         Show:
       
 19908         <div class="check-element animate-show" ng-show="checked">
       
 19909           <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
       
 19910         </div>
       
 19911       </div>
       
 19912       <div>
       
 19913         Hide:
       
 19914         <div class="check-element animate-show" ng-hide="checked">
       
 19915           <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
       
 19916         </div>
       
 19917       </div>
       
 19918     </file>
       
 19919     <file name="glyphicons.css">
       
 19920       @import url(//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css);
       
 19921     </file>
       
 19922     <file name="animations.css">
       
 19923       .animate-show {
       
 19924         -webkit-transition:all linear 0.5s;
       
 19925         transition:all linear 0.5s;
       
 19926         line-height:20px;
       
 19927         opacity:1;
       
 19928         padding:10px;
       
 19929         border:1px solid black;
       
 19930         background:white;
       
 19931       }
       
 19932 
       
 19933       .animate-show.ng-hide-add,
       
 19934       .animate-show.ng-hide-remove {
       
 19935         display:block!important;
       
 19936       }
       
 19937 
       
 19938       .animate-show.ng-hide {
       
 19939         line-height:0;
       
 19940         opacity:0;
       
 19941         padding:0 10px;
       
 19942       }
       
 19943 
       
 19944       .check-element {
       
 19945         padding:10px;
       
 19946         border:1px solid black;
       
 19947         background:white;
       
 19948       }
       
 19949     </file>
       
 19950     <file name="protractor.js" type="protractor">
       
 19951       var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
       
 19952       var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
       
 19953 
       
 19954       it('should check ng-show / ng-hide', function() {
       
 19955         expect(thumbsUp.isDisplayed()).toBeFalsy();
       
 19956         expect(thumbsDown.isDisplayed()).toBeTruthy();
       
 19957 
       
 19958         element(by.model('checked')).click();
       
 19959 
       
 19960         expect(thumbsUp.isDisplayed()).toBeTruthy();
       
 19961         expect(thumbsDown.isDisplayed()).toBeFalsy();
       
 19962       });
       
 19963     </file>
       
 19964   </example>
       
 19965  */
       
 19966 var ngShowDirective = ['$animate', function($animate) {
       
 19967   return function(scope, element, attr) {
       
 19968     scope.$watch(attr.ngShow, function ngShowWatchAction(value){
       
 19969       $animate[toBoolean(value) ? 'removeClass' : 'addClass'](element, 'ng-hide');
       
 19970     });
       
 19971   };
       
 19972 }];
       
 19973 
       
 19974 
       
 19975 /**
       
 19976  * @ngdoc directive
       
 19977  * @name ngHide
       
 19978  *
       
 19979  * @description
       
 19980  * The `ngHide` directive shows or hides the given HTML element based on the expression
       
 19981  * provided to the ngHide attribute. The element is shown or hidden by removing or adding
       
 19982  * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
       
 19983  * in AngularJS and sets the display style to none (using an !important flag).
       
 19984  * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
       
 19985  *
       
 19986  * ```hrml
       
 19987  * <!-- when $scope.myValue is truthy (element is hidden) -->
       
 19988  * <div ng-hide="myValue"></div>
       
 19989  *
       
 19990  * <!-- when $scope.myValue is falsy (element is visible) -->
       
 19991  * <div ng-hide="myValue" class="ng-hide"></div>
       
 19992  * ```
       
 19993  *
       
 19994  * When the ngHide expression evaluates to true then the .ng-hide CSS class is added to the class attribute
       
 19995  * on the element causing it to become hidden. When false, the ng-hide CSS class is removed
       
 19996  * from the element causing the element not to appear hidden.
       
 19997  *
       
 19998  * ## Why is !important used?
       
 19999  *
       
 20000  * You may be wondering why !important is used for the .ng-hide CSS class. This is because the `.ng-hide` selector
       
 20001  * can be easily overridden by heavier selectors. For example, something as simple
       
 20002  * as changing the display style on a HTML list item would make hidden elements appear visible.
       
 20003  * This also becomes a bigger issue when dealing with CSS frameworks.
       
 20004  *
       
 20005  * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
       
 20006  * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
       
 20007  * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
       
 20008  *
       
 20009  * ### Overriding .ng-hide
       
 20010  *
       
 20011  * If you wish to change the hide behavior with ngShow/ngHide then this can be achieved by
       
 20012  * restating the styles for the .ng-hide class in CSS:
       
 20013  * ```css
       
 20014  * .ng-hide {
       
 20015  *   //!annotate CSS Specificity|Not to worry, this will override the AngularJS default...
       
 20016  *   display:block!important;
       
 20017  *
       
 20018  *   //this is just another form of hiding an element
       
 20019  *   position:absolute;
       
 20020  *   top:-9999px;
       
 20021  *   left:-9999px;
       
 20022  * }
       
 20023  * ```
       
 20024  *
       
 20025  * Just remember to include the important flag so the CSS override will function.
       
 20026  *
       
 20027  * <div class="alert alert-warning">
       
 20028  * **Note:** Here is a list of values that ngHide will consider as a falsy value (case insensitive):<br />
       
 20029  * "f" / "0" / "false" / "no" / "n" / "[]"
       
 20030  * </div>
       
 20031  *
       
 20032  * ## A note about animations with ngHide
       
 20033  *
       
 20034  * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
       
 20035  * is true and false. This system works like the animation system present with ngClass, except that
       
 20036  * you must also include the !important flag to override the display property so
       
 20037  * that you can perform an animation when the element is hidden during the time of the animation.
       
 20038  *
       
 20039  * ```css
       
 20040  * //
       
 20041  * //a working example can be found at the bottom of this page
       
 20042  * //
       
 20043  * .my-element.ng-hide-add, .my-element.ng-hide-remove {
       
 20044  *   transition:0.5s linear all;
       
 20045  *   display:block!important;
       
 20046  * }
       
 20047  *
       
 20048  * .my-element.ng-hide-add { ... }
       
 20049  * .my-element.ng-hide-add.ng-hide-add-active { ... }
       
 20050  * .my-element.ng-hide-remove { ... }
       
 20051  * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
       
 20052  * ```
       
 20053  *
       
 20054  * @animations
       
 20055  * removeClass: .ng-hide - happens after the ngHide expression evaluates to a truthy value and just before the contents are set to hidden
       
 20056  * addClass: .ng-hide - happens after the ngHide expression evaluates to a non truthy value and just before the contents are set to visible
       
 20057  *
       
 20058  * @element ANY
       
 20059  * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
       
 20060  *     the element is shown or hidden respectively.
       
 20061  *
       
 20062  * @example
       
 20063   <example module="ngAnimate" deps="angular-animate.js" animations="true">
       
 20064     <file name="index.html">
       
 20065       Click me: <input type="checkbox" ng-model="checked"><br/>
       
 20066       <div>
       
 20067         Show:
       
 20068         <div class="check-element animate-hide" ng-show="checked">
       
 20069           <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
       
 20070         </div>
       
 20071       </div>
       
 20072       <div>
       
 20073         Hide:
       
 20074         <div class="check-element animate-hide" ng-hide="checked">
       
 20075           <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
       
 20076         </div>
       
 20077       </div>
       
 20078     </file>
       
 20079     <file name="glyphicons.css">
       
 20080       @import url(//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css);
       
 20081     </file>
       
 20082     <file name="animations.css">
       
 20083       .animate-hide {
       
 20084         -webkit-transition:all linear 0.5s;
       
 20085         transition:all linear 0.5s;
       
 20086         line-height:20px;
       
 20087         opacity:1;
       
 20088         padding:10px;
       
 20089         border:1px solid black;
       
 20090         background:white;
       
 20091       }
       
 20092 
       
 20093       .animate-hide.ng-hide-add,
       
 20094       .animate-hide.ng-hide-remove {
       
 20095         display:block!important;
       
 20096       }
       
 20097 
       
 20098       .animate-hide.ng-hide {
       
 20099         line-height:0;
       
 20100         opacity:0;
       
 20101         padding:0 10px;
       
 20102       }
       
 20103 
       
 20104       .check-element {
       
 20105         padding:10px;
       
 20106         border:1px solid black;
       
 20107         background:white;
       
 20108       }
       
 20109     </file>
       
 20110     <file name="protractor.js" type="protractor">
       
 20111       var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
       
 20112       var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
       
 20113 
       
 20114       it('should check ng-show / ng-hide', function() {
       
 20115         expect(thumbsUp.isDisplayed()).toBeFalsy();
       
 20116         expect(thumbsDown.isDisplayed()).toBeTruthy();
       
 20117 
       
 20118         element(by.model('checked')).click();
       
 20119 
       
 20120         expect(thumbsUp.isDisplayed()).toBeTruthy();
       
 20121         expect(thumbsDown.isDisplayed()).toBeFalsy();
       
 20122       });
       
 20123     </file>
       
 20124   </example>
       
 20125  */
       
 20126 var ngHideDirective = ['$animate', function($animate) {
       
 20127   return function(scope, element, attr) {
       
 20128     scope.$watch(attr.ngHide, function ngHideWatchAction(value){
       
 20129       $animate[toBoolean(value) ? 'addClass' : 'removeClass'](element, 'ng-hide');
       
 20130     });
       
 20131   };
       
 20132 }];
       
 20133 
       
 20134 /**
       
 20135  * @ngdoc directive
       
 20136  * @name ngStyle
       
 20137  * @restrict AC
       
 20138  *
       
 20139  * @description
       
 20140  * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
       
 20141  *
       
 20142  * @element ANY
       
 20143  * @param {expression} ngStyle {@link guide/expression Expression} which evals to an
       
 20144  *      object whose keys are CSS style names and values are corresponding values for those CSS
       
 20145  *      keys.
       
 20146  *
       
 20147  * @example
       
 20148    <example>
       
 20149      <file name="index.html">
       
 20150         <input type="button" value="set" ng-click="myStyle={color:'red'}">
       
 20151         <input type="button" value="clear" ng-click="myStyle={}">
       
 20152         <br/>
       
 20153         <span ng-style="myStyle">Sample Text</span>
       
 20154         <pre>myStyle={{myStyle}}</pre>
       
 20155      </file>
       
 20156      <file name="style.css">
       
 20157        span {
       
 20158          color: black;
       
 20159        }
       
 20160      </file>
       
 20161      <file name="protractor.js" type="protractor">
       
 20162        var colorSpan = element(by.css('span'));
       
 20163 
       
 20164        it('should check ng-style', function() {
       
 20165          expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
       
 20166          element(by.css('input[value=set]')).click();
       
 20167          expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
       
 20168          element(by.css('input[value=clear]')).click();
       
 20169          expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
       
 20170        });
       
 20171      </file>
       
 20172    </example>
       
 20173  */
       
 20174 var ngStyleDirective = ngDirective(function(scope, element, attr) {
       
 20175   scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
       
 20176     if (oldStyles && (newStyles !== oldStyles)) {
       
 20177       forEach(oldStyles, function(val, style) { element.css(style, '');});
       
 20178     }
       
 20179     if (newStyles) element.css(newStyles);
       
 20180   }, true);
       
 20181 });
       
 20182 
       
 20183 /**
       
 20184  * @ngdoc directive
       
 20185  * @name ngSwitch
       
 20186  * @restrict EA
       
 20187  *
       
 20188  * @description
       
 20189  * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
       
 20190  * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
       
 20191  * as specified in the template.
       
 20192  *
       
 20193  * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
       
 20194  * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
       
 20195  * matches the value obtained from the evaluated expression. In other words, you define a container element
       
 20196  * (where you place the directive), place an expression on the **`on="..."` attribute**
       
 20197  * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
       
 20198  * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
       
 20199  * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
       
 20200  * attribute is displayed.
       
 20201  *
       
 20202  * <div class="alert alert-info">
       
 20203  * Be aware that the attribute values to match against cannot be expressions. They are interpreted
       
 20204  * as literal string values to match against.
       
 20205  * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
       
 20206  * value of the expression `$scope.someVal`.
       
 20207  * </div>
       
 20208 
       
 20209  * @animations
       
 20210  * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
       
 20211  * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
       
 20212  *
       
 20213  * @usage
       
 20214  * <ANY ng-switch="expression">
       
 20215  *   <ANY ng-switch-when="matchValue1">...</ANY>
       
 20216  *   <ANY ng-switch-when="matchValue2">...</ANY>
       
 20217  *   <ANY ng-switch-default>...</ANY>
       
 20218  * </ANY>
       
 20219  *
       
 20220  *
       
 20221  * @scope
       
 20222  * @priority 800
       
 20223  * @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
       
 20224  * On child elements add:
       
 20225  *
       
 20226  * * `ngSwitchWhen`: the case statement to match against. If match then this
       
 20227  *   case will be displayed. If the same match appears multiple times, all the
       
 20228  *   elements will be displayed.
       
 20229  * * `ngSwitchDefault`: the default case when no other case match. If there
       
 20230  *   are multiple default cases, all of them will be displayed when no other
       
 20231  *   case match.
       
 20232  *
       
 20233  *
       
 20234  * @example
       
 20235   <example module="ngAnimate" deps="angular-animate.js" animations="true">
       
 20236     <file name="index.html">
       
 20237       <div ng-controller="Ctrl">
       
 20238         <select ng-model="selection" ng-options="item for item in items">
       
 20239         </select>
       
 20240         <tt>selection={{selection}}</tt>
       
 20241         <hr/>
       
 20242         <div class="animate-switch-container"
       
 20243           ng-switch on="selection">
       
 20244             <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
       
 20245             <div class="animate-switch" ng-switch-when="home">Home Span</div>
       
 20246             <div class="animate-switch" ng-switch-default>default</div>
       
 20247         </div>
       
 20248       </div>
       
 20249     </file>
       
 20250     <file name="script.js">
       
 20251       function Ctrl($scope) {
       
 20252         $scope.items = ['settings', 'home', 'other'];
       
 20253         $scope.selection = $scope.items[0];
       
 20254       }
       
 20255     </file>
       
 20256     <file name="animations.css">
       
 20257       .animate-switch-container {
       
 20258         position:relative;
       
 20259         background:white;
       
 20260         border:1px solid black;
       
 20261         height:40px;
       
 20262         overflow:hidden;
       
 20263       }
       
 20264 
       
 20265       .animate-switch {
       
 20266         padding:10px;
       
 20267       }
       
 20268 
       
 20269       .animate-switch.ng-animate {
       
 20270         -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
       
 20271         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
       
 20272 
       
 20273         position:absolute;
       
 20274         top:0;
       
 20275         left:0;
       
 20276         right:0;
       
 20277         bottom:0;
       
 20278       }
       
 20279 
       
 20280       .animate-switch.ng-leave.ng-leave-active,
       
 20281       .animate-switch.ng-enter {
       
 20282         top:-50px;
       
 20283       }
       
 20284       .animate-switch.ng-leave,
       
 20285       .animate-switch.ng-enter.ng-enter-active {
       
 20286         top:0;
       
 20287       }
       
 20288     </file>
       
 20289     <file name="protractor.js" type="protractor">
       
 20290       var switchElem = element(by.css('[ng-switch]'));
       
 20291       var select = element(by.model('selection'));
       
 20292 
       
 20293       it('should start in settings', function() {
       
 20294         expect(switchElem.getText()).toMatch(/Settings Div/);
       
 20295       });
       
 20296       it('should change to home', function() {
       
 20297         select.element.all(by.css('option')).get(1).click();
       
 20298         expect(switchElem.getText()).toMatch(/Home Span/);
       
 20299       });
       
 20300       it('should select default', function() {
       
 20301         select.element.all(by.css('option')).get(2).click();
       
 20302         expect(switchElem.getText()).toMatch(/default/);
       
 20303       });
       
 20304     </file>
       
 20305   </example>
       
 20306  */
       
 20307 var ngSwitchDirective = ['$animate', function($animate) {
       
 20308   return {
       
 20309     restrict: 'EA',
       
 20310     require: 'ngSwitch',
       
 20311 
       
 20312     // asks for $scope to fool the BC controller module
       
 20313     controller: ['$scope', function ngSwitchController() {
       
 20314      this.cases = {};
       
 20315     }],
       
 20316     link: function(scope, element, attr, ngSwitchController) {
       
 20317       var watchExpr = attr.ngSwitch || attr.on,
       
 20318           selectedTranscludes,
       
 20319           selectedElements,
       
 20320           previousElements,
       
 20321           selectedScopes = [];
       
 20322 
       
 20323       scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
       
 20324         var i, ii = selectedScopes.length;
       
 20325         if(ii > 0) {
       
 20326           if(previousElements) {
       
 20327             for (i = 0; i < ii; i++) {
       
 20328               previousElements[i].remove();
       
 20329             }
       
 20330             previousElements = null;
       
 20331           }
       
 20332 
       
 20333           previousElements = [];
       
 20334           for (i= 0; i<ii; i++) {
       
 20335             var selected = selectedElements[i];
       
 20336             selectedScopes[i].$destroy();
       
 20337             previousElements[i] = selected;
       
 20338             $animate.leave(selected, function() {
       
 20339               previousElements.splice(i, 1);
       
 20340               if(previousElements.length === 0) {
       
 20341                 previousElements = null;
       
 20342               }
       
 20343             });
       
 20344           }
       
 20345         }
       
 20346 
       
 20347         selectedElements = [];
       
 20348         selectedScopes = [];
       
 20349 
       
 20350         if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
       
 20351           scope.$eval(attr.change);
       
 20352           forEach(selectedTranscludes, function(selectedTransclude) {
       
 20353             var selectedScope = scope.$new();
       
 20354             selectedScopes.push(selectedScope);
       
 20355             selectedTransclude.transclude(selectedScope, function(caseElement) {
       
 20356               var anchor = selectedTransclude.element;
       
 20357 
       
 20358               selectedElements.push(caseElement);
       
 20359               $animate.enter(caseElement, anchor.parent(), anchor);
       
 20360             });
       
 20361           });
       
 20362         }
       
 20363       });
       
 20364     }
       
 20365   };
       
 20366 }];
       
 20367 
       
 20368 var ngSwitchWhenDirective = ngDirective({
       
 20369   transclude: 'element',
       
 20370   priority: 800,
       
 20371   require: '^ngSwitch',
       
 20372   link: function(scope, element, attrs, ctrl, $transclude) {
       
 20373     ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
       
 20374     ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
       
 20375   }
       
 20376 });
       
 20377 
       
 20378 var ngSwitchDefaultDirective = ngDirective({
       
 20379   transclude: 'element',
       
 20380   priority: 800,
       
 20381   require: '^ngSwitch',
       
 20382   link: function(scope, element, attr, ctrl, $transclude) {
       
 20383     ctrl.cases['?'] = (ctrl.cases['?'] || []);
       
 20384     ctrl.cases['?'].push({ transclude: $transclude, element: element });
       
 20385    }
       
 20386 });
       
 20387 
       
 20388 /**
       
 20389  * @ngdoc directive
       
 20390  * @name ngTransclude
       
 20391  * @restrict AC
       
 20392  *
       
 20393  * @description
       
 20394  * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
       
 20395  *
       
 20396  * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
       
 20397  *
       
 20398  * @element ANY
       
 20399  *
       
 20400  * @example
       
 20401    <example module="transclude">
       
 20402      <file name="index.html">
       
 20403        <script>
       
 20404          function Ctrl($scope) {
       
 20405            $scope.title = 'Lorem Ipsum';
       
 20406            $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
       
 20407          }
       
 20408 
       
 20409          angular.module('transclude', [])
       
 20410           .directive('pane', function(){
       
 20411              return {
       
 20412                restrict: 'E',
       
 20413                transclude: true,
       
 20414                scope: { title:'@' },
       
 20415                template: '<div style="border: 1px solid black;">' +
       
 20416                            '<div style="background-color: gray">{{title}}</div>' +
       
 20417                            '<div ng-transclude></div>' +
       
 20418                          '</div>'
       
 20419              };
       
 20420          });
       
 20421        </script>
       
 20422        <div ng-controller="Ctrl">
       
 20423          <input ng-model="title"><br>
       
 20424          <textarea ng-model="text"></textarea> <br/>
       
 20425          <pane title="{{title}}">{{text}}</pane>
       
 20426        </div>
       
 20427      </file>
       
 20428      <file name="protractor.js" type="protractor">
       
 20429         it('should have transcluded', function() {
       
 20430           var titleElement = element(by.model('title'));
       
 20431           titleElement.clear();
       
 20432           titleElement.sendKeys('TITLE');
       
 20433           var textElement = element(by.model('text'));
       
 20434           textElement.clear();
       
 20435           textElement.sendKeys('TEXT');
       
 20436           expect(element(by.binding('title')).getText()).toEqual('TITLE');
       
 20437           expect(element(by.binding('text')).getText()).toEqual('TEXT');
       
 20438         });
       
 20439      </file>
       
 20440    </example>
       
 20441  *
       
 20442  */
       
 20443 var ngTranscludeDirective = ngDirective({
       
 20444   link: function($scope, $element, $attrs, controller, $transclude) {
       
 20445     if (!$transclude) {
       
 20446       throw minErr('ngTransclude')('orphan',
       
 20447        'Illegal use of ngTransclude directive in the template! ' +
       
 20448        'No parent directive that requires a transclusion found. ' +
       
 20449        'Element: {0}',
       
 20450        startingTag($element));
       
 20451     }
       
 20452 
       
 20453     $transclude(function(clone) {
       
 20454       $element.empty();
       
 20455       $element.append(clone);
       
 20456     });
       
 20457   }
       
 20458 });
       
 20459 
       
 20460 /**
       
 20461  * @ngdoc directive
       
 20462  * @name script
       
 20463  * @restrict E
       
 20464  *
       
 20465  * @description
       
 20466  * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
       
 20467  * template can be used by {@link ng.directive:ngInclude `ngInclude`},
       
 20468  * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
       
 20469  * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
       
 20470  * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
       
 20471  *
       
 20472  * @param {string} type Must be set to `'text/ng-template'`.
       
 20473  * @param {string} id Cache name of the template.
       
 20474  *
       
 20475  * @example
       
 20476   <example>
       
 20477     <file name="index.html">
       
 20478       <script type="text/ng-template" id="/tpl.html">
       
 20479         Content of the template.
       
 20480       </script>
       
 20481 
       
 20482       <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
       
 20483       <div id="tpl-content" ng-include src="currentTpl"></div>
       
 20484     </file>
       
 20485     <file name="protractor.js" type="protractor">
       
 20486       it('should load template defined inside script tag', function() {
       
 20487         element(by.css('#tpl-link')).click();
       
 20488         expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
       
 20489       });
       
 20490     </file>
       
 20491   </example>
       
 20492  */
       
 20493 var scriptDirective = ['$templateCache', function($templateCache) {
       
 20494   return {
       
 20495     restrict: 'E',
       
 20496     terminal: true,
       
 20497     compile: function(element, attr) {
       
 20498       if (attr.type == 'text/ng-template') {
       
 20499         var templateUrl = attr.id,
       
 20500             // IE is not consistent, in scripts we have to read .text but in other nodes we have to read .textContent
       
 20501             text = element[0].text;
       
 20502 
       
 20503         $templateCache.put(templateUrl, text);
       
 20504       }
       
 20505     }
       
 20506   };
       
 20507 }];
       
 20508 
       
 20509 var ngOptionsMinErr = minErr('ngOptions');
       
 20510 /**
       
 20511  * @ngdoc directive
       
 20512  * @name select
       
 20513  * @restrict E
       
 20514  *
       
 20515  * @description
       
 20516  * HTML `SELECT` element with angular data-binding.
       
 20517  *
       
 20518  * # `ngOptions`
       
 20519  *
       
 20520  * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
       
 20521  * elements for the `<select>` element using the array or object obtained by evaluating the
       
 20522  * `ngOptions` comprehension_expression.
       
 20523  *
       
 20524  * When an item in the `<select>` menu is selected, the array element or object property
       
 20525  * represented by the selected option will be bound to the model identified by the `ngModel`
       
 20526  * directive.
       
 20527  *
       
 20528  * <div class="alert alert-warning">
       
 20529  * **Note:** `ngModel` compares by reference, not value. This is important when binding to an
       
 20530  * array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/).
       
 20531  * </div>
       
 20532  *
       
 20533  * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
       
 20534  * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
       
 20535  * option. See example below for demonstration.
       
 20536  *
       
 20537  * <div class="alert alert-warning">
       
 20538  * **Note:** `ngOptions` provides an iterator facility for the `<option>` element which should be used instead
       
 20539  * of {@link ng.directive:ngRepeat ngRepeat} when you want the
       
 20540  * `select` model to be bound to a non-string value. This is because an option element can only
       
 20541  * be bound to string values at present.
       
 20542  * </div>
       
 20543  *
       
 20544  * @param {string} ngModel Assignable angular expression to data-bind to.
       
 20545  * @param {string=} name Property name of the form under which the control is published.
       
 20546  * @param {string=} required The control is considered valid only if value is entered.
       
 20547  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
       
 20548  *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
       
 20549  *    `required` when you want to data-bind to the `required` attribute.
       
 20550  * @param {comprehension_expression=} ngOptions in one of the following forms:
       
 20551  *
       
 20552  *   * for array data sources:
       
 20553  *     * `label` **`for`** `value` **`in`** `array`
       
 20554  *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
       
 20555  *     * `label`  **`group by`** `group` **`for`** `value` **`in`** `array`
       
 20556  *     * `select` **`as`** `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
       
 20557  *   * for object data sources:
       
 20558  *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
       
 20559  *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
       
 20560  *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
       
 20561  *     * `select` **`as`** `label` **`group by`** `group`
       
 20562  *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
       
 20563  *
       
 20564  * Where:
       
 20565  *
       
 20566  *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
       
 20567  *   * `value`: local variable which will refer to each item in the `array` or each property value
       
 20568  *      of `object` during iteration.
       
 20569  *   * `key`: local variable which will refer to a property name in `object` during iteration.
       
 20570  *   * `label`: The result of this expression will be the label for `<option>` element. The
       
 20571  *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
       
 20572  *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
       
 20573  *      element. If not specified, `select` expression will default to `value`.
       
 20574  *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
       
 20575  *      DOM element.
       
 20576  *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
       
 20577  *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
       
 20578  *     `value` variable (e.g. `value.propertyName`).
       
 20579  *
       
 20580  * @example
       
 20581     <example>
       
 20582       <file name="index.html">
       
 20583         <script>
       
 20584         function MyCntrl($scope) {
       
 20585           $scope.colors = [
       
 20586             {name:'black', shade:'dark'},
       
 20587             {name:'white', shade:'light'},
       
 20588             {name:'red', shade:'dark'},
       
 20589             {name:'blue', shade:'dark'},
       
 20590             {name:'yellow', shade:'light'}
       
 20591           ];
       
 20592           $scope.color = $scope.colors[2]; // red
       
 20593         }
       
 20594         </script>
       
 20595         <div ng-controller="MyCntrl">
       
 20596           <ul>
       
 20597             <li ng-repeat="color in colors">
       
 20598               Name: <input ng-model="color.name">
       
 20599               [<a href ng-click="colors.splice($index, 1)">X</a>]
       
 20600             </li>
       
 20601             <li>
       
 20602               [<a href ng-click="colors.push({})">add</a>]
       
 20603             </li>
       
 20604           </ul>
       
 20605           <hr/>
       
 20606           Color (null not allowed):
       
 20607           <select ng-model="color" ng-options="c.name for c in colors"></select><br>
       
 20608 
       
 20609           Color (null allowed):
       
 20610           <span  class="nullable">
       
 20611             <select ng-model="color" ng-options="c.name for c in colors">
       
 20612               <option value="">-- choose color --</option>
       
 20613             </select>
       
 20614           </span><br/>
       
 20615 
       
 20616           Color grouped by shade:
       
 20617           <select ng-model="color" ng-options="c.name group by c.shade for c in colors">
       
 20618           </select><br/>
       
 20619 
       
 20620 
       
 20621           Select <a href ng-click="color={name:'not in list'}">bogus</a>.<br>
       
 20622           <hr/>
       
 20623           Currently selected: {{ {selected_color:color}  }}
       
 20624           <div style="border:solid 1px black; height:20px"
       
 20625                ng-style="{'background-color':color.name}">
       
 20626           </div>
       
 20627         </div>
       
 20628       </file>
       
 20629       <file name="protractor.js" type="protractor">
       
 20630          it('should check ng-options', function() {
       
 20631            expect(element(by.binding('{selected_color:color}')).getText()).toMatch('red');
       
 20632            element.all(by.select('color')).first().click();
       
 20633            element.all(by.css('select[ng-model="color"] option')).first().click();
       
 20634            expect(element(by.binding('{selected_color:color}')).getText()).toMatch('black');
       
 20635            element(by.css('.nullable select[ng-model="color"]')).click();
       
 20636            element.all(by.css('.nullable select[ng-model="color"] option')).first().click();
       
 20637            expect(element(by.binding('{selected_color:color}')).getText()).toMatch('null');
       
 20638          });
       
 20639       </file>
       
 20640     </example>
       
 20641  */
       
 20642 
       
 20643 var ngOptionsDirective = valueFn({ terminal: true });
       
 20644 // jshint maxlen: false
       
 20645 var selectDirective = ['$compile', '$parse', function($compile,   $parse) {
       
 20646                          //000011111111110000000000022222222220000000000000000000003333333333000000000000004444444444444440000000005555555555555550000000666666666666666000000000000000777777777700000000000000000008888888888
       
 20647   var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,
       
 20648       nullModelCtrl = {$setViewValue: noop};
       
 20649 // jshint maxlen: 100
       
 20650 
       
 20651   return {
       
 20652     restrict: 'E',
       
 20653     require: ['select', '?ngModel'],
       
 20654     controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
       
 20655       var self = this,
       
 20656           optionsMap = {},
       
 20657           ngModelCtrl = nullModelCtrl,
       
 20658           nullOption,
       
 20659           unknownOption;
       
 20660 
       
 20661 
       
 20662       self.databound = $attrs.ngModel;
       
 20663 
       
 20664 
       
 20665       self.init = function(ngModelCtrl_, nullOption_, unknownOption_) {
       
 20666         ngModelCtrl = ngModelCtrl_;
       
 20667         nullOption = nullOption_;
       
 20668         unknownOption = unknownOption_;
       
 20669       };
       
 20670 
       
 20671 
       
 20672       self.addOption = function(value) {
       
 20673         assertNotHasOwnProperty(value, '"option value"');
       
 20674         optionsMap[value] = true;
       
 20675 
       
 20676         if (ngModelCtrl.$viewValue == value) {
       
 20677           $element.val(value);
       
 20678           if (unknownOption.parent()) unknownOption.remove();
       
 20679         }
       
 20680       };
       
 20681 
       
 20682 
       
 20683       self.removeOption = function(value) {
       
 20684         if (this.hasOption(value)) {
       
 20685           delete optionsMap[value];
       
 20686           if (ngModelCtrl.$viewValue == value) {
       
 20687             this.renderUnknownOption(value);
       
 20688           }
       
 20689         }
       
 20690       };
       
 20691 
       
 20692 
       
 20693       self.renderUnknownOption = function(val) {
       
 20694         var unknownVal = '? ' + hashKey(val) + ' ?';
       
 20695         unknownOption.val(unknownVal);
       
 20696         $element.prepend(unknownOption);
       
 20697         $element.val(unknownVal);
       
 20698         unknownOption.prop('selected', true); // needed for IE
       
 20699       };
       
 20700 
       
 20701 
       
 20702       self.hasOption = function(value) {
       
 20703         return optionsMap.hasOwnProperty(value);
       
 20704       };
       
 20705 
       
 20706       $scope.$on('$destroy', function() {
       
 20707         // disable unknown option so that we don't do work when the whole select is being destroyed
       
 20708         self.renderUnknownOption = noop;
       
 20709       });
       
 20710     }],
       
 20711 
       
 20712     link: function(scope, element, attr, ctrls) {
       
 20713       // if ngModel is not defined, we don't need to do anything
       
 20714       if (!ctrls[1]) return;
       
 20715 
       
 20716       var selectCtrl = ctrls[0],
       
 20717           ngModelCtrl = ctrls[1],
       
 20718           multiple = attr.multiple,
       
 20719           optionsExp = attr.ngOptions,
       
 20720           nullOption = false, // if false, user will not be able to select it (used by ngOptions)
       
 20721           emptyOption,
       
 20722           // we can't just jqLite('<option>') since jqLite is not smart enough
       
 20723           // to create it in <select> and IE barfs otherwise.
       
 20724           optionTemplate = jqLite(document.createElement('option')),
       
 20725           optGroupTemplate =jqLite(document.createElement('optgroup')),
       
 20726           unknownOption = optionTemplate.clone();
       
 20727 
       
 20728       // find "null" option
       
 20729       for(var i = 0, children = element.children(), ii = children.length; i < ii; i++) {
       
 20730         if (children[i].value === '') {
       
 20731           emptyOption = nullOption = children.eq(i);
       
 20732           break;
       
 20733         }
       
 20734       }
       
 20735 
       
 20736       selectCtrl.init(ngModelCtrl, nullOption, unknownOption);
       
 20737 
       
 20738       // required validator
       
 20739       if (multiple) {
       
 20740         ngModelCtrl.$isEmpty = function(value) {
       
 20741           return !value || value.length === 0;
       
 20742         };
       
 20743       }
       
 20744 
       
 20745       if (optionsExp) setupAsOptions(scope, element, ngModelCtrl);
       
 20746       else if (multiple) setupAsMultiple(scope, element, ngModelCtrl);
       
 20747       else setupAsSingle(scope, element, ngModelCtrl, selectCtrl);
       
 20748 
       
 20749 
       
 20750       ////////////////////////////
       
 20751 
       
 20752 
       
 20753 
       
 20754       function setupAsSingle(scope, selectElement, ngModelCtrl, selectCtrl) {
       
 20755         ngModelCtrl.$render = function() {
       
 20756           var viewValue = ngModelCtrl.$viewValue;
       
 20757 
       
 20758           if (selectCtrl.hasOption(viewValue)) {
       
 20759             if (unknownOption.parent()) unknownOption.remove();
       
 20760             selectElement.val(viewValue);
       
 20761             if (viewValue === '') emptyOption.prop('selected', true); // to make IE9 happy
       
 20762           } else {
       
 20763             if (isUndefined(viewValue) && emptyOption) {
       
 20764               selectElement.val('');
       
 20765             } else {
       
 20766               selectCtrl.renderUnknownOption(viewValue);
       
 20767             }
       
 20768           }
       
 20769         };
       
 20770 
       
 20771         selectElement.on('change', function() {
       
 20772           scope.$apply(function() {
       
 20773             if (unknownOption.parent()) unknownOption.remove();
       
 20774             ngModelCtrl.$setViewValue(selectElement.val());
       
 20775           });
       
 20776         });
       
 20777       }
       
 20778 
       
 20779       function setupAsMultiple(scope, selectElement, ctrl) {
       
 20780         var lastView;
       
 20781         ctrl.$render = function() {
       
 20782           var items = new HashMap(ctrl.$viewValue);
       
 20783           forEach(selectElement.find('option'), function(option) {
       
 20784             option.selected = isDefined(items.get(option.value));
       
 20785           });
       
 20786         };
       
 20787 
       
 20788         // we have to do it on each watch since ngModel watches reference, but
       
 20789         // we need to work of an array, so we need to see if anything was inserted/removed
       
 20790         scope.$watch(function selectMultipleWatch() {
       
 20791           if (!equals(lastView, ctrl.$viewValue)) {
       
 20792             lastView = copy(ctrl.$viewValue);
       
 20793             ctrl.$render();
       
 20794           }
       
 20795         });
       
 20796 
       
 20797         selectElement.on('change', function() {
       
 20798           scope.$apply(function() {
       
 20799             var array = [];
       
 20800             forEach(selectElement.find('option'), function(option) {
       
 20801               if (option.selected) {
       
 20802                 array.push(option.value);
       
 20803               }
       
 20804             });
       
 20805             ctrl.$setViewValue(array);
       
 20806           });
       
 20807         });
       
 20808       }
       
 20809 
       
 20810       function setupAsOptions(scope, selectElement, ctrl) {
       
 20811         var match;
       
 20812 
       
 20813         if (!(match = optionsExp.match(NG_OPTIONS_REGEXP))) {
       
 20814           throw ngOptionsMinErr('iexp',
       
 20815             "Expected expression in form of " +
       
 20816             "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
       
 20817             " but got '{0}'. Element: {1}",
       
 20818             optionsExp, startingTag(selectElement));
       
 20819         }
       
 20820 
       
 20821         var displayFn = $parse(match[2] || match[1]),
       
 20822             valueName = match[4] || match[6],
       
 20823             keyName = match[5],
       
 20824             groupByFn = $parse(match[3] || ''),
       
 20825             valueFn = $parse(match[2] ? match[1] : valueName),
       
 20826             valuesFn = $parse(match[7]),
       
 20827             track = match[8],
       
 20828             trackFn = track ? $parse(match[8]) : null,
       
 20829             // This is an array of array of existing option groups in DOM.
       
 20830             // We try to reuse these if possible
       
 20831             // - optionGroupsCache[0] is the options with no option group
       
 20832             // - optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element
       
 20833             optionGroupsCache = [[{element: selectElement, label:''}]];
       
 20834 
       
 20835         if (nullOption) {
       
 20836           // compile the element since there might be bindings in it
       
 20837           $compile(nullOption)(scope);
       
 20838 
       
 20839           // remove the class, which is added automatically because we recompile the element and it
       
 20840           // becomes the compilation root
       
 20841           nullOption.removeClass('ng-scope');
       
 20842 
       
 20843           // we need to remove it before calling selectElement.empty() because otherwise IE will
       
 20844           // remove the label from the element. wtf?
       
 20845           nullOption.remove();
       
 20846         }
       
 20847 
       
 20848         // clear contents, we'll add what's needed based on the model
       
 20849         selectElement.empty();
       
 20850 
       
 20851         selectElement.on('change', function() {
       
 20852           scope.$apply(function() {
       
 20853             var optionGroup,
       
 20854                 collection = valuesFn(scope) || [],
       
 20855                 locals = {},
       
 20856                 key, value, optionElement, index, groupIndex, length, groupLength, trackIndex;
       
 20857 
       
 20858             if (multiple) {
       
 20859               value = [];
       
 20860               for (groupIndex = 0, groupLength = optionGroupsCache.length;
       
 20861                    groupIndex < groupLength;
       
 20862                    groupIndex++) {
       
 20863                 // list of options for that group. (first item has the parent)
       
 20864                 optionGroup = optionGroupsCache[groupIndex];
       
 20865 
       
 20866                 for(index = 1, length = optionGroup.length; index < length; index++) {
       
 20867                   if ((optionElement = optionGroup[index].element)[0].selected) {
       
 20868                     key = optionElement.val();
       
 20869                     if (keyName) locals[keyName] = key;
       
 20870                     if (trackFn) {
       
 20871                       for (trackIndex = 0; trackIndex < collection.length; trackIndex++) {
       
 20872                         locals[valueName] = collection[trackIndex];
       
 20873                         if (trackFn(scope, locals) == key) break;
       
 20874                       }
       
 20875                     } else {
       
 20876                       locals[valueName] = collection[key];
       
 20877                     }
       
 20878                     value.push(valueFn(scope, locals));
       
 20879                   }
       
 20880                 }
       
 20881               }
       
 20882             } else {
       
 20883               key = selectElement.val();
       
 20884               if (key == '?') {
       
 20885                 value = undefined;
       
 20886               } else if (key === ''){
       
 20887                 value = null;
       
 20888               } else {
       
 20889                 if (trackFn) {
       
 20890                   for (trackIndex = 0; trackIndex < collection.length; trackIndex++) {
       
 20891                     locals[valueName] = collection[trackIndex];
       
 20892                     if (trackFn(scope, locals) == key) {
       
 20893                       value = valueFn(scope, locals);
       
 20894                       break;
       
 20895                     }
       
 20896                   }
       
 20897                 } else {
       
 20898                   locals[valueName] = collection[key];
       
 20899                   if (keyName) locals[keyName] = key;
       
 20900                   value = valueFn(scope, locals);
       
 20901                 }
       
 20902               }
       
 20903               // Update the null option's selected property here so $render cleans it up correctly
       
 20904               if (optionGroupsCache[0].length > 1) {
       
 20905                 if (optionGroupsCache[0][1].id !== key) {
       
 20906                   optionGroupsCache[0][1].selected = false;
       
 20907                 }
       
 20908               }
       
 20909             }
       
 20910             ctrl.$setViewValue(value);
       
 20911           });
       
 20912         });
       
 20913 
       
 20914         ctrl.$render = render;
       
 20915 
       
 20916         // TODO(vojta): can't we optimize this ?
       
 20917         scope.$watch(render);
       
 20918 
       
 20919         function render() {
       
 20920               // Temporary location for the option groups before we render them
       
 20921           var optionGroups = {'':[]},
       
 20922               optionGroupNames = [''],
       
 20923               optionGroupName,
       
 20924               optionGroup,
       
 20925               option,
       
 20926               existingParent, existingOptions, existingOption,
       
 20927               modelValue = ctrl.$modelValue,
       
 20928               values = valuesFn(scope) || [],
       
 20929               keys = keyName ? sortedKeys(values) : values,
       
 20930               key,
       
 20931               groupLength, length,
       
 20932               groupIndex, index,
       
 20933               locals = {},
       
 20934               selected,
       
 20935               selectedSet = false, // nothing is selected yet
       
 20936               lastElement,
       
 20937               element,
       
 20938               label;
       
 20939 
       
 20940           if (multiple) {
       
 20941             if (trackFn && isArray(modelValue)) {
       
 20942               selectedSet = new HashMap([]);
       
 20943               for (var trackIndex = 0; trackIndex < modelValue.length; trackIndex++) {
       
 20944                 locals[valueName] = modelValue[trackIndex];
       
 20945                 selectedSet.put(trackFn(scope, locals), modelValue[trackIndex]);
       
 20946               }
       
 20947             } else {
       
 20948               selectedSet = new HashMap(modelValue);
       
 20949             }
       
 20950           }
       
 20951 
       
 20952           // We now build up the list of options we need (we merge later)
       
 20953           for (index = 0; length = keys.length, index < length; index++) {
       
 20954 
       
 20955             key = index;
       
 20956             if (keyName) {
       
 20957               key = keys[index];
       
 20958               if ( key.charAt(0) === '$' ) continue;
       
 20959               locals[keyName] = key;
       
 20960             }
       
 20961 
       
 20962             locals[valueName] = values[key];
       
 20963 
       
 20964             optionGroupName = groupByFn(scope, locals) || '';
       
 20965             if (!(optionGroup = optionGroups[optionGroupName])) {
       
 20966               optionGroup = optionGroups[optionGroupName] = [];
       
 20967               optionGroupNames.push(optionGroupName);
       
 20968             }
       
 20969             if (multiple) {
       
 20970               selected = isDefined(
       
 20971                 selectedSet.remove(trackFn ? trackFn(scope, locals) : valueFn(scope, locals))
       
 20972               );
       
 20973             } else {
       
 20974               if (trackFn) {
       
 20975                 var modelCast = {};
       
 20976                 modelCast[valueName] = modelValue;
       
 20977                 selected = trackFn(scope, modelCast) === trackFn(scope, locals);
       
 20978               } else {
       
 20979                 selected = modelValue === valueFn(scope, locals);
       
 20980               }
       
 20981               selectedSet = selectedSet || selected; // see if at least one item is selected
       
 20982             }
       
 20983             label = displayFn(scope, locals); // what will be seen by the user
       
 20984 
       
 20985             // doing displayFn(scope, locals) || '' overwrites zero values
       
 20986             label = isDefined(label) ? label : '';
       
 20987             optionGroup.push({
       
 20988               // either the index into array or key from object
       
 20989               id: trackFn ? trackFn(scope, locals) : (keyName ? keys[index] : index),
       
 20990               label: label,
       
 20991               selected: selected                   // determine if we should be selected
       
 20992             });
       
 20993           }
       
 20994           if (!multiple) {
       
 20995             if (nullOption || modelValue === null) {
       
 20996               // insert null option if we have a placeholder, or the model is null
       
 20997               optionGroups[''].unshift({id:'', label:'', selected:!selectedSet});
       
 20998             } else if (!selectedSet) {
       
 20999               // option could not be found, we have to insert the undefined item
       
 21000               optionGroups[''].unshift({id:'?', label:'', selected:true});
       
 21001             }
       
 21002           }
       
 21003 
       
 21004           // Now we need to update the list of DOM nodes to match the optionGroups we computed above
       
 21005           for (groupIndex = 0, groupLength = optionGroupNames.length;
       
 21006                groupIndex < groupLength;
       
 21007                groupIndex++) {
       
 21008             // current option group name or '' if no group
       
 21009             optionGroupName = optionGroupNames[groupIndex];
       
 21010 
       
 21011             // list of options for that group. (first item has the parent)
       
 21012             optionGroup = optionGroups[optionGroupName];
       
 21013 
       
 21014             if (optionGroupsCache.length <= groupIndex) {
       
 21015               // we need to grow the optionGroups
       
 21016               existingParent = {
       
 21017                 element: optGroupTemplate.clone().attr('label', optionGroupName),
       
 21018                 label: optionGroup.label
       
 21019               };
       
 21020               existingOptions = [existingParent];
       
 21021               optionGroupsCache.push(existingOptions);
       
 21022               selectElement.append(existingParent.element);
       
 21023             } else {
       
 21024               existingOptions = optionGroupsCache[groupIndex];
       
 21025               existingParent = existingOptions[0];  // either SELECT (no group) or OPTGROUP element
       
 21026 
       
 21027               // update the OPTGROUP label if not the same.
       
 21028               if (existingParent.label != optionGroupName) {
       
 21029                 existingParent.element.attr('label', existingParent.label = optionGroupName);
       
 21030               }
       
 21031             }
       
 21032 
       
 21033             lastElement = null;  // start at the beginning
       
 21034             for(index = 0, length = optionGroup.length; index < length; index++) {
       
 21035               option = optionGroup[index];
       
 21036               if ((existingOption = existingOptions[index+1])) {
       
 21037                 // reuse elements
       
 21038                 lastElement = existingOption.element;
       
 21039                 if (existingOption.label !== option.label) {
       
 21040                   lastElement.text(existingOption.label = option.label);
       
 21041                 }
       
 21042                 if (existingOption.id !== option.id) {
       
 21043                   lastElement.val(existingOption.id = option.id);
       
 21044                 }
       
 21045                 // lastElement.prop('selected') provided by jQuery has side-effects
       
 21046                 if (existingOption.selected !== option.selected) {
       
 21047                   lastElement.prop('selected', (existingOption.selected = option.selected));
       
 21048                 }
       
 21049               } else {
       
 21050                 // grow elements
       
 21051 
       
 21052                 // if it's a null option
       
 21053                 if (option.id === '' && nullOption) {
       
 21054                   // put back the pre-compiled element
       
 21055                   element = nullOption;
       
 21056                 } else {
       
 21057                   // jQuery(v1.4.2) Bug: We should be able to chain the method calls, but
       
 21058                   // in this version of jQuery on some browser the .text() returns a string
       
 21059                   // rather then the element.
       
 21060                   (element = optionTemplate.clone())
       
 21061                       .val(option.id)
       
 21062                       .attr('selected', option.selected)
       
 21063                       .text(option.label);
       
 21064                 }
       
 21065 
       
 21066                 existingOptions.push(existingOption = {
       
 21067                     element: element,
       
 21068                     label: option.label,
       
 21069                     id: option.id,
       
 21070                     selected: option.selected
       
 21071                 });
       
 21072                 if (lastElement) {
       
 21073                   lastElement.after(element);
       
 21074                 } else {
       
 21075                   existingParent.element.append(element);
       
 21076                 }
       
 21077                 lastElement = element;
       
 21078               }
       
 21079             }
       
 21080             // remove any excessive OPTIONs in a group
       
 21081             index++; // increment since the existingOptions[0] is parent element not OPTION
       
 21082             while(existingOptions.length > index) {
       
 21083               existingOptions.pop().element.remove();
       
 21084             }
       
 21085           }
       
 21086           // remove any excessive OPTGROUPs from select
       
 21087           while(optionGroupsCache.length > groupIndex) {
       
 21088             optionGroupsCache.pop()[0].element.remove();
       
 21089           }
       
 21090         }
       
 21091       }
       
 21092     }
       
 21093   };
       
 21094 }];
       
 21095 
       
 21096 var optionDirective = ['$interpolate', function($interpolate) {
       
 21097   var nullSelectCtrl = {
       
 21098     addOption: noop,
       
 21099     removeOption: noop
       
 21100   };
       
 21101 
       
 21102   return {
       
 21103     restrict: 'E',
       
 21104     priority: 100,
       
 21105     compile: function(element, attr) {
       
 21106       if (isUndefined(attr.value)) {
       
 21107         var interpolateFn = $interpolate(element.text(), true);
       
 21108         if (!interpolateFn) {
       
 21109           attr.$set('value', element.text());
       
 21110         }
       
 21111       }
       
 21112 
       
 21113       return function (scope, element, attr) {
       
 21114         var selectCtrlName = '$selectController',
       
 21115             parent = element.parent(),
       
 21116             selectCtrl = parent.data(selectCtrlName) ||
       
 21117               parent.parent().data(selectCtrlName); // in case we are in optgroup
       
 21118 
       
 21119         if (selectCtrl && selectCtrl.databound) {
       
 21120           // For some reason Opera defaults to true and if not overridden this messes up the repeater.
       
 21121           // We don't want the view to drive the initialization of the model anyway.
       
 21122           element.prop('selected', false);
       
 21123         } else {
       
 21124           selectCtrl = nullSelectCtrl;
       
 21125         }
       
 21126 
       
 21127         if (interpolateFn) {
       
 21128           scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {
       
 21129             attr.$set('value', newVal);
       
 21130             if (newVal !== oldVal) selectCtrl.removeOption(oldVal);
       
 21131             selectCtrl.addOption(newVal);
       
 21132           });
       
 21133         } else {
       
 21134           selectCtrl.addOption(attr.value);
       
 21135         }
       
 21136 
       
 21137         element.on('$destroy', function() {
       
 21138           selectCtrl.removeOption(attr.value);
       
 21139         });
       
 21140       };
       
 21141     }
       
 21142   };
       
 21143 }];
       
 21144 
       
 21145 var styleDirective = valueFn({
       
 21146   restrict: 'E',
       
 21147   terminal: true
       
 21148 });
       
 21149 
       
 21150   if (window.angular.bootstrap) {
       
 21151     //AngularJS is already loaded, so we can return here...
       
 21152     console.log('WARNING: Tried to load angular more than once.');
       
 21153     return;
       
 21154   }
       
 21155 
       
 21156   //try to bind to jquery now so that one can write angular.element().read()
       
 21157   //but we will rebind on bootstrap again.
       
 21158   bindJQuery();
       
 21159 
       
 21160   publishExternalAPI(angular);
       
 21161 
       
 21162   jqLite(document).ready(function() {
       
 21163     angularInit(document, bootstrap);
       
 21164   });
       
 21165 
       
 21166 })(window, document);
       
 21167 
       
 21168 !angular.$$csp() && angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng\\:form{display:block;}.ng-animate-block-transitions{transition:0s all!important;-webkit-transition:0s all!important;}</style>');