psr2 fixes to prior commit
[openemr.git] / public / assets / angular-1-5-8 / angular.js
blob54f65588f46a50f01b03b0c071a1b9ac997d5ca2
1 /**
2  * @license AngularJS v1.5.8
3  * (c) 2010-2016 Google, Inc. http://angularjs.org
4  * License: MIT
5  */
6 (function(window) {'use strict';
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  * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
34  *   error from returned function, for cases when a particular type of error is useful.
35  * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
36  */
38 function minErr(module, ErrorConstructor) {
39   ErrorConstructor = ErrorConstructor || Error;
40   return function() {
41     var SKIP_INDEXES = 2;
43     var templateArgs = arguments,
44       code = templateArgs[0],
45       message = '[' + (module ? module + ':' : '') + code + '] ',
46       template = templateArgs[1],
47       paramPrefix, i;
49     message += template.replace(/\{\d+\}/g, function(match) {
50       var index = +match.slice(1, -1),
51         shiftedIndex = index + SKIP_INDEXES;
53       if (shiftedIndex < templateArgs.length) {
54         return toDebugString(templateArgs[shiftedIndex]);
55       }
57       return match;
58     });
60     message += '\nhttp://errors.angularjs.org/1.5.8/' +
61       (module ? module + '/' : '') + code;
63     for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
64       message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
65         encodeURIComponent(toDebugString(templateArgs[i]));
66     }
68     return new ErrorConstructor(message);
69   };
72 /* We need to tell jshint what variables are being exported */
73 /* global angular: true,
74   msie: true,
75   jqLite: true,
76   jQuery: true,
77   slice: true,
78   splice: true,
79   push: true,
80   toString: true,
81   ngMinErr: true,
82   angularModule: true,
83   uid: true,
84   REGEX_STRING_REGEXP: true,
85   VALIDITY_STATE_PROPERTY: true,
87   lowercase: true,
88   uppercase: true,
89   manualLowercase: true,
90   manualUppercase: true,
91   nodeName_: true,
92   isArrayLike: true,
93   forEach: true,
94   forEachSorted: true,
95   reverseParams: true,
96   nextUid: true,
97   setHashKey: true,
98   extend: true,
99   toInt: true,
100   inherit: true,
101   merge: true,
102   noop: true,
103   identity: true,
104   valueFn: true,
105   isUndefined: true,
106   isDefined: true,
107   isObject: true,
108   isBlankObject: true,
109   isString: true,
110   isNumber: true,
111   isDate: true,
112   isArray: true,
113   isFunction: true,
114   isRegExp: true,
115   isWindow: true,
116   isScope: true,
117   isFile: true,
118   isFormData: true,
119   isBlob: true,
120   isBoolean: true,
121   isPromiseLike: true,
122   trim: true,
123   escapeForRegexp: true,
124   isElement: true,
125   makeMap: true,
126   includes: true,
127   arrayRemove: true,
128   copy: true,
129   equals: true,
130   csp: true,
131   jq: true,
132   concat: true,
133   sliceArgs: true,
134   bind: true,
135   toJsonReplacer: true,
136   toJson: true,
137   fromJson: true,
138   convertTimezoneToLocal: true,
139   timezoneToOffset: true,
140   startingTag: true,
141   tryDecodeURIComponent: true,
142   parseKeyValue: true,
143   toKeyValue: true,
144   encodeUriSegment: true,
145   encodeUriQuery: true,
146   angularInit: true,
147   bootstrap: true,
148   getTestability: true,
149   snake_case: true,
150   bindJQuery: true,
151   assertArg: true,
152   assertArgFn: true,
153   assertNotHasOwnProperty: true,
154   getter: true,
155   getBlockNodes: true,
156   hasOwnProperty: true,
157   createMap: true,
159   NODE_TYPE_ELEMENT: true,
160   NODE_TYPE_ATTRIBUTE: true,
161   NODE_TYPE_TEXT: true,
162   NODE_TYPE_COMMENT: true,
163   NODE_TYPE_DOCUMENT: true,
164   NODE_TYPE_DOCUMENT_FRAGMENT: true,
167 ////////////////////////////////////
170  * @ngdoc module
171  * @name ng
172  * @module ng
173  * @installation
174  * @description
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.
182  * <div doc-module-components="ng"></div>
183  */
185 var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
187 // The name of a form control's ValidityState property.
188 // This is used so that it's possible for internal tests to create mock ValidityStates.
189 var VALIDITY_STATE_PROPERTY = 'validity';
191 var hasOwnProperty = Object.prototype.hasOwnProperty;
193 var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
194 var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
197 var manualLowercase = function(s) {
198   /* jshint bitwise: false */
199   return isString(s)
200       ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
201       : s;
203 var manualUppercase = function(s) {
204   /* jshint bitwise: false */
205   return isString(s)
206       ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
207       : s;
211 // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
212 // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
213 // with correct but slower alternatives. See https://github.com/angular/angular.js/issues/11387
214 if ('i' !== 'I'.toLowerCase()) {
215   lowercase = manualLowercase;
216   uppercase = manualUppercase;
221     msie,             // holds major version number for IE, or NaN if UA is not IE.
222     jqLite,           // delay binding since jQuery could be loaded after us.
223     jQuery,           // delay binding
224     slice             = [].slice,
225     splice            = [].splice,
226     push              = [].push,
227     toString          = Object.prototype.toString,
228     getPrototypeOf    = Object.getPrototypeOf,
229     ngMinErr          = minErr('ng'),
231     /** @name angular */
232     angular           = window.angular || (window.angular = {}),
233     angularModule,
234     uid               = 0;
237  * documentMode is an IE-only property
238  * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
239  */
240 msie = window.document.documentMode;
244  * @private
245  * @param {*} obj
246  * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
247  *                   String ...)
248  */
249 function isArrayLike(obj) {
251   // `null`, `undefined` and `window` are not array-like
252   if (obj == null || isWindow(obj)) return false;
254   // arrays, strings and jQuery/jqLite objects are array like
255   // * jqLite is either the jQuery or jqLite constructor function
256   // * we have to check the existence of jqLite first as this method is called
257   //   via the forEach method when constructing the jqLite object in the first place
258   if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
260   // Support: iOS 8.2 (not reproducible in simulator)
261   // "length" in obj used to prevent JIT error (gh-11508)
262   var length = "length" in Object(obj) && obj.length;
264   // NodeList objects (with `item` method) and
265   // other objects with suitable length characteristics are array-like
266   return isNumber(length) &&
267     (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item == 'function');
272  * @ngdoc function
273  * @name angular.forEach
274  * @module ng
275  * @kind function
277  * @description
278  * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
279  * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
280  * is the value of an object property or an array element, `key` is the object property key or
281  * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
283  * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
284  * using the `hasOwnProperty` method.
286  * Unlike ES262's
287  * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
288  * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
289  * return the value provided.
291    ```js
292      var values = {name: 'misko', gender: 'male'};
293      var log = [];
294      angular.forEach(values, function(value, key) {
295        this.push(key + ': ' + value);
296      }, log);
297      expect(log).toEqual(['name: misko', 'gender: male']);
298    ```
300  * @param {Object|Array} obj Object to iterate over.
301  * @param {Function} iterator Iterator function.
302  * @param {Object=} context Object to become context (`this`) for the iterator function.
303  * @returns {Object|Array} Reference to `obj`.
304  */
306 function forEach(obj, iterator, context) {
307   var key, length;
308   if (obj) {
309     if (isFunction(obj)) {
310       for (key in obj) {
311         // Need to check if hasOwnProperty exists,
312         // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
313         if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
314           iterator.call(context, obj[key], key, obj);
315         }
316       }
317     } else if (isArray(obj) || isArrayLike(obj)) {
318       var isPrimitive = typeof obj !== 'object';
319       for (key = 0, length = obj.length; key < length; key++) {
320         if (isPrimitive || key in obj) {
321           iterator.call(context, obj[key], key, obj);
322         }
323       }
324     } else if (obj.forEach && obj.forEach !== forEach) {
325         obj.forEach(iterator, context, obj);
326     } else if (isBlankObject(obj)) {
327       // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
328       for (key in obj) {
329         iterator.call(context, obj[key], key, obj);
330       }
331     } else if (typeof obj.hasOwnProperty === 'function') {
332       // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
333       for (key in obj) {
334         if (obj.hasOwnProperty(key)) {
335           iterator.call(context, obj[key], key, obj);
336         }
337       }
338     } else {
339       // Slow path for objects which do not have a method `hasOwnProperty`
340       for (key in obj) {
341         if (hasOwnProperty.call(obj, key)) {
342           iterator.call(context, obj[key], key, obj);
343         }
344       }
345     }
346   }
347   return obj;
350 function forEachSorted(obj, iterator, context) {
351   var keys = Object.keys(obj).sort();
352   for (var i = 0; i < keys.length; i++) {
353     iterator.call(context, obj[keys[i]], keys[i]);
354   }
355   return keys;
360  * when using forEach the params are value, key, but it is often useful to have key, value.
361  * @param {function(string, *)} iteratorFn
362  * @returns {function(*, string)}
363  */
364 function reverseParams(iteratorFn) {
365   return function(value, key) {iteratorFn(key, value);};
369  * A consistent way of creating unique IDs in angular.
371  * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
372  * we hit number precision issues in JavaScript.
374  * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
376  * @returns {number} an unique alpha-numeric string
377  */
378 function nextUid() {
379   return ++uid;
384  * Set or clear the hashkey for an object.
385  * @param obj object
386  * @param h the hashkey (!truthy to delete the hashkey)
387  */
388 function setHashKey(obj, h) {
389   if (h) {
390     obj.$$hashKey = h;
391   } else {
392     delete obj.$$hashKey;
393   }
397 function baseExtend(dst, objs, deep) {
398   var h = dst.$$hashKey;
400   for (var i = 0, ii = objs.length; i < ii; ++i) {
401     var obj = objs[i];
402     if (!isObject(obj) && !isFunction(obj)) continue;
403     var keys = Object.keys(obj);
404     for (var j = 0, jj = keys.length; j < jj; j++) {
405       var key = keys[j];
406       var src = obj[key];
408       if (deep && isObject(src)) {
409         if (isDate(src)) {
410           dst[key] = new Date(src.valueOf());
411         } else if (isRegExp(src)) {
412           dst[key] = new RegExp(src);
413         } else if (src.nodeName) {
414           dst[key] = src.cloneNode(true);
415         } else if (isElement(src)) {
416           dst[key] = src.clone();
417         } else {
418           if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
419           baseExtend(dst[key], [src], true);
420         }
421       } else {
422         dst[key] = src;
423       }
424     }
425   }
427   setHashKey(dst, h);
428   return dst;
432  * @ngdoc function
433  * @name angular.extend
434  * @module ng
435  * @kind function
437  * @description
438  * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
439  * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
440  * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
442  * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
443  * {@link angular.merge} for this.
445  * @param {Object} dst Destination object.
446  * @param {...Object} src Source object(s).
447  * @returns {Object} Reference to `dst`.
448  */
449 function extend(dst) {
450   return baseExtend(dst, slice.call(arguments, 1), false);
455 * @ngdoc function
456 * @name angular.merge
457 * @module ng
458 * @kind function
460 * @description
461 * Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
462 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
463 * by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
465 * Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
466 * objects, performing a deep copy.
468 * @param {Object} dst Destination object.
469 * @param {...Object} src Source object(s).
470 * @returns {Object} Reference to `dst`.
472 function merge(dst) {
473   return baseExtend(dst, slice.call(arguments, 1), true);
478 function toInt(str) {
479   return parseInt(str, 10);
483 function inherit(parent, extra) {
484   return extend(Object.create(parent), extra);
488  * @ngdoc function
489  * @name angular.noop
490  * @module ng
491  * @kind function
493  * @description
494  * A function that performs no operations. This function can be useful when writing code in the
495  * functional style.
496    ```js
497      function foo(callback) {
498        var result = calculateResult();
499        (callback || angular.noop)(result);
500      }
501    ```
502  */
503 function noop() {}
504 noop.$inject = [];
508  * @ngdoc function
509  * @name angular.identity
510  * @module ng
511  * @kind function
513  * @description
514  * A function that returns its first argument. This function is useful when writing code in the
515  * functional style.
517    ```js
518    function transformer(transformationFn, value) {
519      return (transformationFn || angular.identity)(value);
520    };
522    // E.g.
523    function getResult(fn, input) {
524      return (fn || angular.identity)(input);
525    };
527    getResult(function(n) { return n * 2; }, 21);   // returns 42
528    getResult(null, 21);                            // returns 21
529    getResult(undefined, 21);                       // returns 21
530    ```
532  * @param {*} value to be returned.
533  * @returns {*} the value passed in.
534  */
535 function identity($) {return $;}
536 identity.$inject = [];
539 function valueFn(value) {return function valueRef() {return value;};}
541 function hasCustomToString(obj) {
542   return isFunction(obj.toString) && obj.toString !== toString;
547  * @ngdoc function
548  * @name angular.isUndefined
549  * @module ng
550  * @kind function
552  * @description
553  * Determines if a reference is undefined.
555  * @param {*} value Reference to check.
556  * @returns {boolean} True if `value` is undefined.
557  */
558 function isUndefined(value) {return typeof value === 'undefined';}
562  * @ngdoc function
563  * @name angular.isDefined
564  * @module ng
565  * @kind function
567  * @description
568  * Determines if a reference is defined.
570  * @param {*} value Reference to check.
571  * @returns {boolean} True if `value` is defined.
572  */
573 function isDefined(value) {return typeof value !== 'undefined';}
577  * @ngdoc function
578  * @name angular.isObject
579  * @module ng
580  * @kind function
582  * @description
583  * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
584  * considered to be objects. Note that JavaScript arrays are objects.
586  * @param {*} value Reference to check.
587  * @returns {boolean} True if `value` is an `Object` but not `null`.
588  */
589 function isObject(value) {
590   // http://jsperf.com/isobject4
591   return value !== null && typeof value === 'object';
596  * Determine if a value is an object with a null prototype
598  * @returns {boolean} True if `value` is an `Object` with a null prototype
599  */
600 function isBlankObject(value) {
601   return value !== null && typeof value === 'object' && !getPrototypeOf(value);
606  * @ngdoc function
607  * @name angular.isString
608  * @module ng
609  * @kind function
611  * @description
612  * Determines if a reference is a `String`.
614  * @param {*} value Reference to check.
615  * @returns {boolean} True if `value` is a `String`.
616  */
617 function isString(value) {return typeof value === 'string';}
621  * @ngdoc function
622  * @name angular.isNumber
623  * @module ng
624  * @kind function
626  * @description
627  * Determines if a reference is a `Number`.
629  * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
631  * If you wish to exclude these then you can use the native
632  * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
633  * method.
635  * @param {*} value Reference to check.
636  * @returns {boolean} True if `value` is a `Number`.
637  */
638 function isNumber(value) {return typeof value === 'number';}
642  * @ngdoc function
643  * @name angular.isDate
644  * @module ng
645  * @kind function
647  * @description
648  * Determines if a value is a date.
650  * @param {*} value Reference to check.
651  * @returns {boolean} True if `value` is a `Date`.
652  */
653 function isDate(value) {
654   return toString.call(value) === '[object Date]';
659  * @ngdoc function
660  * @name angular.isArray
661  * @module ng
662  * @kind function
664  * @description
665  * Determines if a reference is an `Array`.
667  * @param {*} value Reference to check.
668  * @returns {boolean} True if `value` is an `Array`.
669  */
670 var isArray = Array.isArray;
673  * @ngdoc function
674  * @name angular.isFunction
675  * @module ng
676  * @kind function
678  * @description
679  * Determines if a reference is a `Function`.
681  * @param {*} value Reference to check.
682  * @returns {boolean} True if `value` is a `Function`.
683  */
684 function isFunction(value) {return typeof value === 'function';}
688  * Determines if a value is a regular expression object.
690  * @private
691  * @param {*} value Reference to check.
692  * @returns {boolean} True if `value` is a `RegExp`.
693  */
694 function isRegExp(value) {
695   return toString.call(value) === '[object RegExp]';
700  * Checks if `obj` is a window object.
702  * @private
703  * @param {*} obj Object to check
704  * @returns {boolean} True if `obj` is a window obj.
705  */
706 function isWindow(obj) {
707   return obj && obj.window === obj;
711 function isScope(obj) {
712   return obj && obj.$evalAsync && obj.$watch;
716 function isFile(obj) {
717   return toString.call(obj) === '[object File]';
721 function isFormData(obj) {
722   return toString.call(obj) === '[object FormData]';
726 function isBlob(obj) {
727   return toString.call(obj) === '[object Blob]';
731 function isBoolean(value) {
732   return typeof value === 'boolean';
736 function isPromiseLike(obj) {
737   return obj && isFunction(obj.then);
741 var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
742 function isTypedArray(value) {
743   return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
746 function isArrayBuffer(obj) {
747   return toString.call(obj) === '[object ArrayBuffer]';
751 var trim = function(value) {
752   return isString(value) ? value.trim() : value;
755 // Copied from:
756 // http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
757 // Prereq: s is a string.
758 var escapeForRegexp = function(s) {
759   return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
760            replace(/\x08/g, '\\x08');
765  * @ngdoc function
766  * @name angular.isElement
767  * @module ng
768  * @kind function
770  * @description
771  * Determines if a reference is a DOM element (or wrapped jQuery element).
773  * @param {*} value Reference to check.
774  * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
775  */
776 function isElement(node) {
777   return !!(node &&
778     (node.nodeName  // We are a direct element.
779     || (node.prop && node.attr && node.find)));  // We have an on and find method part of jQuery API.
783  * @param str 'key1,key2,...'
784  * @returns {object} in the form of {key1:true, key2:true, ...}
785  */
786 function makeMap(str) {
787   var obj = {}, items = str.split(','), i;
788   for (i = 0; i < items.length; i++) {
789     obj[items[i]] = true;
790   }
791   return obj;
795 function nodeName_(element) {
796   return lowercase(element.nodeName || (element[0] && element[0].nodeName));
799 function includes(array, obj) {
800   return Array.prototype.indexOf.call(array, obj) != -1;
803 function arrayRemove(array, value) {
804   var index = array.indexOf(value);
805   if (index >= 0) {
806     array.splice(index, 1);
807   }
808   return index;
812  * @ngdoc function
813  * @name angular.copy
814  * @module ng
815  * @kind function
817  * @description
818  * Creates a deep copy of `source`, which should be an object or an array.
820  * * If no destination is supplied, a copy of the object or array is created.
821  * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
822  *   are deleted and then all elements/properties from the source are copied to it.
823  * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
824  * * If `source` is identical to `destination` an exception will be thrown.
826  * <br />
827  * <div class="alert alert-warning">
828  *   Only enumerable properties are taken into account. Non-enumerable properties (both on `source`
829  *   and on `destination`) will be ignored.
830  * </div>
832  * @param {*} source The source that will be used to make a copy.
833  *                   Can be any type, including primitives, `null`, and `undefined`.
834  * @param {(Object|Array)=} destination Destination into which the source is copied. If
835  *     provided, must be of the same type as `source`.
836  * @returns {*} The copy or updated `destination`, if `destination` was specified.
838  * @example
839   <example module="copyExample">
840     <file name="index.html">
841       <div ng-controller="ExampleController">
842         <form novalidate class="simple-form">
843           <label>Name: <input type="text" ng-model="user.name" /></label><br />
844           <label>Age:  <input type="number" ng-model="user.age" /></label><br />
845           Gender: <label><input type="radio" ng-model="user.gender" value="male" />male</label>
846                   <label><input type="radio" ng-model="user.gender" value="female" />female</label><br />
847           <button ng-click="reset()">RESET</button>
848           <button ng-click="update(user)">SAVE</button>
849         </form>
850         <pre>form = {{user | json}}</pre>
851         <pre>master = {{master | json}}</pre>
852       </div>
853     </file>
854     <file name="script.js">
855       // Module: copyExample
856       angular.
857         module('copyExample', []).
858         controller('ExampleController', ['$scope', function($scope) {
859           $scope.master = {};
861           $scope.reset = function() {
862             // Example with 1 argument
863             $scope.user = angular.copy($scope.master);
864           };
866           $scope.update = function(user) {
867             // Example with 2 arguments
868             angular.copy(user, $scope.master);
869           };
871           $scope.reset();
872         }]);
873     </file>
874   </example>
875  */
876 function copy(source, destination) {
877   var stackSource = [];
878   var stackDest = [];
880   if (destination) {
881     if (isTypedArray(destination) || isArrayBuffer(destination)) {
882       throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
883     }
884     if (source === destination) {
885       throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
886     }
888     // Empty the destination object
889     if (isArray(destination)) {
890       destination.length = 0;
891     } else {
892       forEach(destination, function(value, key) {
893         if (key !== '$$hashKey') {
894           delete destination[key];
895         }
896       });
897     }
899     stackSource.push(source);
900     stackDest.push(destination);
901     return copyRecurse(source, destination);
902   }
904   return copyElement(source);
906   function copyRecurse(source, destination) {
907     var h = destination.$$hashKey;
908     var key;
909     if (isArray(source)) {
910       for (var i = 0, ii = source.length; i < ii; i++) {
911         destination.push(copyElement(source[i]));
912       }
913     } else if (isBlankObject(source)) {
914       // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
915       for (key in source) {
916         destination[key] = copyElement(source[key]);
917       }
918     } else if (source && typeof source.hasOwnProperty === 'function') {
919       // Slow path, which must rely on hasOwnProperty
920       for (key in source) {
921         if (source.hasOwnProperty(key)) {
922           destination[key] = copyElement(source[key]);
923         }
924       }
925     } else {
926       // Slowest path --- hasOwnProperty can't be called as a method
927       for (key in source) {
928         if (hasOwnProperty.call(source, key)) {
929           destination[key] = copyElement(source[key]);
930         }
931       }
932     }
933     setHashKey(destination, h);
934     return destination;
935   }
937   function copyElement(source) {
938     // Simple values
939     if (!isObject(source)) {
940       return source;
941     }
943     // Already copied values
944     var index = stackSource.indexOf(source);
945     if (index !== -1) {
946       return stackDest[index];
947     }
949     if (isWindow(source) || isScope(source)) {
950       throw ngMinErr('cpws',
951         "Can't copy! Making copies of Window or Scope instances is not supported.");
952     }
954     var needsRecurse = false;
955     var destination = copyType(source);
957     if (destination === undefined) {
958       destination = isArray(source) ? [] : Object.create(getPrototypeOf(source));
959       needsRecurse = true;
960     }
962     stackSource.push(source);
963     stackDest.push(destination);
965     return needsRecurse
966       ? copyRecurse(source, destination)
967       : destination;
968   }
970   function copyType(source) {
971     switch (toString.call(source)) {
972       case '[object Int8Array]':
973       case '[object Int16Array]':
974       case '[object Int32Array]':
975       case '[object Float32Array]':
976       case '[object Float64Array]':
977       case '[object Uint8Array]':
978       case '[object Uint8ClampedArray]':
979       case '[object Uint16Array]':
980       case '[object Uint32Array]':
981         return new source.constructor(copyElement(source.buffer), source.byteOffset, source.length);
983       case '[object ArrayBuffer]':
984         //Support: IE10
985         if (!source.slice) {
986           var copied = new ArrayBuffer(source.byteLength);
987           new Uint8Array(copied).set(new Uint8Array(source));
988           return copied;
989         }
990         return source.slice(0);
992       case '[object Boolean]':
993       case '[object Number]':
994       case '[object String]':
995       case '[object Date]':
996         return new source.constructor(source.valueOf());
998       case '[object RegExp]':
999         var re = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
1000         re.lastIndex = source.lastIndex;
1001         return re;
1003       case '[object Blob]':
1004         return new source.constructor([source], {type: source.type});
1005     }
1007     if (isFunction(source.cloneNode)) {
1008       return source.cloneNode(true);
1009     }
1010   }
1015  * @ngdoc function
1016  * @name angular.equals
1017  * @module ng
1018  * @kind function
1020  * @description
1021  * Determines if two objects or two values are equivalent. Supports value types, regular
1022  * expressions, arrays and objects.
1024  * Two objects or values are considered equivalent if at least one of the following is true:
1026  * * Both objects or values pass `===` comparison.
1027  * * Both objects or values are of the same type and all of their properties are equal by
1028  *   comparing them with `angular.equals`.
1029  * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
1030  * * Both values represent the same regular expression (In JavaScript,
1031  *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
1032  *   representation matches).
1034  * During a property comparison, properties of `function` type and properties with names
1035  * that begin with `$` are ignored.
1037  * Scope and DOMWindow objects are being compared only by identify (`===`).
1039  * @param {*} o1 Object or value to compare.
1040  * @param {*} o2 Object or value to compare.
1041  * @returns {boolean} True if arguments are equal.
1043  * @example
1044    <example module="equalsExample" name="equalsExample">
1045      <file name="index.html">
1046       <div ng-controller="ExampleController">
1047         <form novalidate>
1048           <h3>User 1</h3>
1049           Name: <input type="text" ng-model="user1.name">
1050           Age: <input type="number" ng-model="user1.age">
1052           <h3>User 2</h3>
1053           Name: <input type="text" ng-model="user2.name">
1054           Age: <input type="number" ng-model="user2.age">
1056           <div>
1057             <br/>
1058             <input type="button" value="Compare" ng-click="compare()">
1059           </div>
1060           User 1: <pre>{{user1 | json}}</pre>
1061           User 2: <pre>{{user2 | json}}</pre>
1062           Equal: <pre>{{result}}</pre>
1063         </form>
1064       </div>
1065     </file>
1066     <file name="script.js">
1067         angular.module('equalsExample', []).controller('ExampleController', ['$scope', function($scope) {
1068           $scope.user1 = {};
1069           $scope.user2 = {};
1070           $scope.result;
1071           $scope.compare = function() {
1072             $scope.result = angular.equals($scope.user1, $scope.user2);
1073           };
1074         }]);
1075     </file>
1076   </example>
1077  */
1078 function equals(o1, o2) {
1079   if (o1 === o2) return true;
1080   if (o1 === null || o2 === null) return false;
1081   if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1082   var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1083   if (t1 == t2 && t1 == 'object') {
1084     if (isArray(o1)) {
1085       if (!isArray(o2)) return false;
1086       if ((length = o1.length) == o2.length) {
1087         for (key = 0; key < length; key++) {
1088           if (!equals(o1[key], o2[key])) return false;
1089         }
1090         return true;
1091       }
1092     } else if (isDate(o1)) {
1093       if (!isDate(o2)) return false;
1094       return equals(o1.getTime(), o2.getTime());
1095     } else if (isRegExp(o1)) {
1096       if (!isRegExp(o2)) return false;
1097       return o1.toString() == o2.toString();
1098     } else {
1099       if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1100         isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1101       keySet = createMap();
1102       for (key in o1) {
1103         if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1104         if (!equals(o1[key], o2[key])) return false;
1105         keySet[key] = true;
1106       }
1107       for (key in o2) {
1108         if (!(key in keySet) &&
1109             key.charAt(0) !== '$' &&
1110             isDefined(o2[key]) &&
1111             !isFunction(o2[key])) return false;
1112       }
1113       return true;
1114     }
1115   }
1116   return false;
1119 var csp = function() {
1120   if (!isDefined(csp.rules)) {
1123     var ngCspElement = (window.document.querySelector('[ng-csp]') ||
1124                     window.document.querySelector('[data-ng-csp]'));
1126     if (ngCspElement) {
1127       var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
1128                     ngCspElement.getAttribute('data-ng-csp');
1129       csp.rules = {
1130         noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
1131         noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
1132       };
1133     } else {
1134       csp.rules = {
1135         noUnsafeEval: noUnsafeEval(),
1136         noInlineStyle: false
1137       };
1138     }
1139   }
1141   return csp.rules;
1143   function noUnsafeEval() {
1144     try {
1145       /* jshint -W031, -W054 */
1146       new Function('');
1147       /* jshint +W031, +W054 */
1148       return false;
1149     } catch (e) {
1150       return true;
1151     }
1152   }
1156  * @ngdoc directive
1157  * @module ng
1158  * @name ngJq
1160  * @element ANY
1161  * @param {string=} ngJq the name of the library available under `window`
1162  * to be used for angular.element
1163  * @description
1164  * Use this directive to force the angular.element library.  This should be
1165  * used to force either jqLite by leaving ng-jq blank or setting the name of
1166  * the jquery variable under window (eg. jQuery).
1168  * Since angular looks for this directive when it is loaded (doesn't wait for the
1169  * DOMContentLoaded event), it must be placed on an element that comes before the script
1170  * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1171  * others ignored.
1173  * @example
1174  * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1175  ```html
1176  <!doctype html>
1177  <html ng-app ng-jq>
1178  ...
1179  ...
1180  </html>
1181  ```
1182  * @example
1183  * This example shows how to use a jQuery based library of a different name.
1184  * The library name must be available at the top most 'window'.
1185  ```html
1186  <!doctype html>
1187  <html ng-app ng-jq="jQueryLib">
1188  ...
1189  ...
1190  </html>
1191  ```
1192  */
1193 var jq = function() {
1194   if (isDefined(jq.name_)) return jq.name_;
1195   var el;
1196   var i, ii = ngAttrPrefixes.length, prefix, name;
1197   for (i = 0; i < ii; ++i) {
1198     prefix = ngAttrPrefixes[i];
1199     if (el = window.document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
1200       name = el.getAttribute(prefix + 'jq');
1201       break;
1202     }
1203   }
1205   return (jq.name_ = name);
1208 function concat(array1, array2, index) {
1209   return array1.concat(slice.call(array2, index));
1212 function sliceArgs(args, startIndex) {
1213   return slice.call(args, startIndex || 0);
1217 /* jshint -W101 */
1219  * @ngdoc function
1220  * @name angular.bind
1221  * @module ng
1222  * @kind function
1224  * @description
1225  * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1226  * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1227  * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1228  * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1230  * @param {Object} self Context which `fn` should be evaluated in.
1231  * @param {function()} fn Function to be bound.
1232  * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1233  * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1234  */
1235 /* jshint +W101 */
1236 function bind(self, fn) {
1237   var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1238   if (isFunction(fn) && !(fn instanceof RegExp)) {
1239     return curryArgs.length
1240       ? function() {
1241           return arguments.length
1242             ? fn.apply(self, concat(curryArgs, arguments, 0))
1243             : fn.apply(self, curryArgs);
1244         }
1245       : function() {
1246           return arguments.length
1247             ? fn.apply(self, arguments)
1248             : fn.call(self);
1249         };
1250   } else {
1251     // In IE, native methods are not functions so they cannot be bound (note: they don't need to be).
1252     return fn;
1253   }
1257 function toJsonReplacer(key, value) {
1258   var val = value;
1260   if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1261     val = undefined;
1262   } else if (isWindow(value)) {
1263     val = '$WINDOW';
1264   } else if (value &&  window.document === value) {
1265     val = '$DOCUMENT';
1266   } else if (isScope(value)) {
1267     val = '$SCOPE';
1268   }
1270   return val;
1275  * @ngdoc function
1276  * @name angular.toJson
1277  * @module ng
1278  * @kind function
1280  * @description
1281  * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1282  * stripped since angular uses this notation internally.
1284  * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1285  * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1286  *    If set to an integer, the JSON output will contain that many spaces per indentation.
1287  * @returns {string|undefined} JSON-ified string representing `obj`.
1288  * @knownIssue
1290  * The Safari browser throws a `RangeError` instead of returning `null` when it tries to stringify a `Date`
1291  * object with an invalid date value. The only reliable way to prevent this is to monkeypatch the
1292  * `Date.prototype.toJSON` method as follows:
1294  * ```
1295  * var _DatetoJSON = Date.prototype.toJSON;
1296  * Date.prototype.toJSON = function() {
1297  *   try {
1298  *     return _DatetoJSON.call(this);
1299  *   } catch(e) {
1300  *     if (e instanceof RangeError) {
1301  *       return null;
1302  *     }
1303  *     throw e;
1304  *   }
1305  * };
1306  * ```
1308  * See https://github.com/angular/angular.js/pull/14221 for more information.
1309  */
1310 function toJson(obj, pretty) {
1311   if (isUndefined(obj)) return undefined;
1312   if (!isNumber(pretty)) {
1313     pretty = pretty ? 2 : null;
1314   }
1315   return JSON.stringify(obj, toJsonReplacer, pretty);
1320  * @ngdoc function
1321  * @name angular.fromJson
1322  * @module ng
1323  * @kind function
1325  * @description
1326  * Deserializes a JSON string.
1328  * @param {string} json JSON string to deserialize.
1329  * @returns {Object|Array|string|number} Deserialized JSON string.
1330  */
1331 function fromJson(json) {
1332   return isString(json)
1333       ? JSON.parse(json)
1334       : json;
1338 var ALL_COLONS = /:/g;
1339 function timezoneToOffset(timezone, fallback) {
1340   // IE/Edge do not "understand" colon (`:`) in timezone
1341   timezone = timezone.replace(ALL_COLONS, '');
1342   var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1343   return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1347 function addDateMinutes(date, minutes) {
1348   date = new Date(date.getTime());
1349   date.setMinutes(date.getMinutes() + minutes);
1350   return date;
1354 function convertTimezoneToLocal(date, timezone, reverse) {
1355   reverse = reverse ? -1 : 1;
1356   var dateTimezoneOffset = date.getTimezoneOffset();
1357   var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
1358   return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
1363  * @returns {string} Returns the string representation of the element.
1364  */
1365 function startingTag(element) {
1366   element = jqLite(element).clone();
1367   try {
1368     // turns out IE does not let you set .html() on elements which
1369     // are not allowed to have children. So we just ignore it.
1370     element.empty();
1371   } catch (e) {}
1372   var elemHtml = jqLite('<div>').append(element).html();
1373   try {
1374     return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1375         elemHtml.
1376           match(/^(<[^>]+>)/)[1].
1377           replace(/^<([\w\-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);});
1378   } catch (e) {
1379     return lowercase(elemHtml);
1380   }
1385 /////////////////////////////////////////////////
1388  * Tries to decode the URI component without throwing an exception.
1390  * @private
1391  * @param str value potential URI component to check.
1392  * @returns {boolean} True if `value` can be decoded
1393  * with the decodeURIComponent function.
1394  */
1395 function tryDecodeURIComponent(value) {
1396   try {
1397     return decodeURIComponent(value);
1398   } catch (e) {
1399     // Ignore any invalid uri component.
1400   }
1405  * Parses an escaped url query string into key-value pairs.
1406  * @returns {Object.<string,boolean|Array>}
1407  */
1408 function parseKeyValue(/**string*/keyValue) {
1409   var obj = {};
1410   forEach((keyValue || "").split('&'), function(keyValue) {
1411     var splitPoint, key, val;
1412     if (keyValue) {
1413       key = keyValue = keyValue.replace(/\+/g,'%20');
1414       splitPoint = keyValue.indexOf('=');
1415       if (splitPoint !== -1) {
1416         key = keyValue.substring(0, splitPoint);
1417         val = keyValue.substring(splitPoint + 1);
1418       }
1419       key = tryDecodeURIComponent(key);
1420       if (isDefined(key)) {
1421         val = isDefined(val) ? tryDecodeURIComponent(val) : true;
1422         if (!hasOwnProperty.call(obj, key)) {
1423           obj[key] = val;
1424         } else if (isArray(obj[key])) {
1425           obj[key].push(val);
1426         } else {
1427           obj[key] = [obj[key],val];
1428         }
1429       }
1430     }
1431   });
1432   return obj;
1435 function toKeyValue(obj) {
1436   var parts = [];
1437   forEach(obj, function(value, key) {
1438     if (isArray(value)) {
1439       forEach(value, function(arrayValue) {
1440         parts.push(encodeUriQuery(key, true) +
1441                    (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1442       });
1443     } else {
1444     parts.push(encodeUriQuery(key, true) +
1445                (value === true ? '' : '=' + encodeUriQuery(value, true)));
1446     }
1447   });
1448   return parts.length ? parts.join('&') : '';
1453  * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1454  * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1455  * segments:
1456  *    segment       = *pchar
1457  *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1458  *    pct-encoded   = "%" HEXDIG HEXDIG
1459  *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1460  *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1461  *                     / "*" / "+" / "," / ";" / "="
1462  */
1463 function encodeUriSegment(val) {
1464   return encodeUriQuery(val, true).
1465              replace(/%26/gi, '&').
1466              replace(/%3D/gi, '=').
1467              replace(/%2B/gi, '+');
1472  * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1473  * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1474  * encoded per http://tools.ietf.org/html/rfc3986:
1475  *    query       = *( pchar / "/" / "?" )
1476  *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1477  *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1478  *    pct-encoded   = "%" HEXDIG HEXDIG
1479  *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1480  *                     / "*" / "+" / "," / ";" / "="
1481  */
1482 function encodeUriQuery(val, pctEncodeSpaces) {
1483   return encodeURIComponent(val).
1484              replace(/%40/gi, '@').
1485              replace(/%3A/gi, ':').
1486              replace(/%24/g, '$').
1487              replace(/%2C/gi, ',').
1488              replace(/%3B/gi, ';').
1489              replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1492 var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1494 function getNgAttribute(element, ngAttr) {
1495   var attr, i, ii = ngAttrPrefixes.length;
1496   for (i = 0; i < ii; ++i) {
1497     attr = ngAttrPrefixes[i] + ngAttr;
1498     if (isString(attr = element.getAttribute(attr))) {
1499       return attr;
1500     }
1501   }
1502   return null;
1506  * @ngdoc directive
1507  * @name ngApp
1508  * @module ng
1510  * @element ANY
1511  * @param {angular.Module} ngApp an optional application
1512  *   {@link angular.module module} name to load.
1513  * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1514  *   created in "strict-di" mode. This means that the application will fail to invoke functions which
1515  *   do not use explicit function annotation (and are thus unsuitable for minification), as described
1516  *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1517  *   tracking down the root of these bugs.
1519  * @description
1521  * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1522  * designates the **root element** of the application and is typically placed near the root element
1523  * of the page - e.g. on the `<body>` or `<html>` tags.
1525  * There are a few things to keep in mind when using `ngApp`:
1526  * - only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1527  *   found in the document will be used to define the root element to auto-bootstrap as an
1528  *   application. To run multiple applications in an HTML document you must manually bootstrap them using
1529  *   {@link angular.bootstrap} instead.
1530  * - AngularJS applications cannot be nested within each other.
1531  * - Do not use a directive that uses {@link ng.$compile#transclusion transclusion} on the same element as `ngApp`.
1532  *   This includes directives such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and
1533  *   {@link ngRoute.ngView `ngView`}.
1534  *   Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
1535  *   causing animations to stop working and making the injector inaccessible from outside the app.
1537  * You can specify an **AngularJS module** to be used as the root module for the application.  This
1538  * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1539  * should contain the application code needed or have dependencies on other modules that will
1540  * contain the code. See {@link angular.module} for more information.
1542  * In the example below if the `ngApp` directive were not placed on the `html` element then the
1543  * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1544  * would not be resolved to `3`.
1546  * `ngApp` is the easiest, and most common way to bootstrap an application.
1548  <example module="ngAppDemo">
1549    <file name="index.html">
1550    <div ng-controller="ngAppDemoController">
1551      I can add: {{a}} + {{b}} =  {{ a+b }}
1552    </div>
1553    </file>
1554    <file name="script.js">
1555    angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1556      $scope.a = 1;
1557      $scope.b = 2;
1558    });
1559    </file>
1560  </example>
1562  * Using `ngStrictDi`, you would see something like this:
1564  <example ng-app-included="true">
1565    <file name="index.html">
1566    <div ng-app="ngAppStrictDemo" ng-strict-di>
1567        <div ng-controller="GoodController1">
1568            I can add: {{a}} + {{b}} =  {{ a+b }}
1570            <p>This renders because the controller does not fail to
1571               instantiate, by using explicit annotation style (see
1572               script.js for details)
1573            </p>
1574        </div>
1576        <div ng-controller="GoodController2">
1577            Name: <input ng-model="name"><br />
1578            Hello, {{name}}!
1580            <p>This renders because the controller does not fail to
1581               instantiate, by using explicit annotation style
1582               (see script.js for details)
1583            </p>
1584        </div>
1586        <div ng-controller="BadController">
1587            I can add: {{a}} + {{b}} =  {{ a+b }}
1589            <p>The controller could not be instantiated, due to relying
1590               on automatic function annotations (which are disabled in
1591               strict mode). As such, the content of this section is not
1592               interpolated, and there should be an error in your web console.
1593            </p>
1594        </div>
1595    </div>
1596    </file>
1597    <file name="script.js">
1598    angular.module('ngAppStrictDemo', [])
1599      // BadController will fail to instantiate, due to relying on automatic function annotation,
1600      // rather than an explicit annotation
1601      .controller('BadController', function($scope) {
1602        $scope.a = 1;
1603        $scope.b = 2;
1604      })
1605      // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1606      // due to using explicit annotations using the array style and $inject property, respectively.
1607      .controller('GoodController1', ['$scope', function($scope) {
1608        $scope.a = 1;
1609        $scope.b = 2;
1610      }])
1611      .controller('GoodController2', GoodController2);
1612      function GoodController2($scope) {
1613        $scope.name = "World";
1614      }
1615      GoodController2.$inject = ['$scope'];
1616    </file>
1617    <file name="style.css">
1618    div[ng-controller] {
1619        margin-bottom: 1em;
1620        -webkit-border-radius: 4px;
1621        border-radius: 4px;
1622        border: 1px solid;
1623        padding: .5em;
1624    }
1625    div[ng-controller^=Good] {
1626        border-color: #d6e9c6;
1627        background-color: #dff0d8;
1628        color: #3c763d;
1629    }
1630    div[ng-controller^=Bad] {
1631        border-color: #ebccd1;
1632        background-color: #f2dede;
1633        color: #a94442;
1634        margin-bottom: 0;
1635    }
1636    </file>
1637  </example>
1638  */
1639 function angularInit(element, bootstrap) {
1640   var appElement,
1641       module,
1642       config = {};
1644   // The element `element` has priority over any other element.
1645   forEach(ngAttrPrefixes, function(prefix) {
1646     var name = prefix + 'app';
1648     if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1649       appElement = element;
1650       module = element.getAttribute(name);
1651     }
1652   });
1653   forEach(ngAttrPrefixes, function(prefix) {
1654     var name = prefix + 'app';
1655     var candidate;
1657     if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1658       appElement = candidate;
1659       module = candidate.getAttribute(name);
1660     }
1661   });
1662   if (appElement) {
1663     config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1664     bootstrap(appElement, module ? [module] : [], config);
1665   }
1669  * @ngdoc function
1670  * @name angular.bootstrap
1671  * @module ng
1672  * @description
1673  * Use this function to manually start up angular application.
1675  * For more information, see the {@link guide/bootstrap Bootstrap guide}.
1677  * Angular will detect if it has been loaded into the browser more than once and only allow the
1678  * first loaded script to be bootstrapped and will report a warning to the browser console for
1679  * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1680  * multiple instances of Angular try to work on the DOM.
1682  * <div class="alert alert-warning">
1683  * **Note:** Protractor based end-to-end tests cannot use this function to bootstrap manually.
1684  * They must use {@link ng.directive:ngApp ngApp}.
1685  * </div>
1687  * <div class="alert alert-warning">
1688  * **Note:** Do not bootstrap the app on an element with a directive that uses {@link ng.$compile#transclusion transclusion},
1689  * such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}.
1690  * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
1691  * causing animations to stop working and making the injector inaccessible from outside the app.
1692  * </div>
1694  * ```html
1695  * <!doctype html>
1696  * <html>
1697  * <body>
1698  * <div ng-controller="WelcomeController">
1699  *   {{greeting}}
1700  * </div>
1702  * <script src="angular.js"></script>
1703  * <script>
1704  *   var app = angular.module('demo', [])
1705  *   .controller('WelcomeController', function($scope) {
1706  *       $scope.greeting = 'Welcome!';
1707  *   });
1708  *   angular.bootstrap(document, ['demo']);
1709  * </script>
1710  * </body>
1711  * </html>
1712  * ```
1714  * @param {DOMElement} element DOM element which is the root of angular application.
1715  * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1716  *     Each item in the array should be the name of a predefined module or a (DI annotated)
1717  *     function that will be invoked by the injector as a `config` block.
1718  *     See: {@link angular.module modules}
1719  * @param {Object=} config an object for defining configuration options for the application. The
1720  *     following keys are supported:
1722  * * `strictDi` - disable automatic function annotation for the application. This is meant to
1723  *   assist in finding bugs which break minified code. Defaults to `false`.
1725  * @returns {auto.$injector} Returns the newly created injector for this app.
1726  */
1727 function bootstrap(element, modules, config) {
1728   if (!isObject(config)) config = {};
1729   var defaultConfig = {
1730     strictDi: false
1731   };
1732   config = extend(defaultConfig, config);
1733   var doBootstrap = function() {
1734     element = jqLite(element);
1736     if (element.injector()) {
1737       var tag = (element[0] === window.document) ? 'document' : startingTag(element);
1738       // Encode angle brackets to prevent input from being sanitized to empty string #8683.
1739       throw ngMinErr(
1740           'btstrpd',
1741           "App already bootstrapped with this element '{0}'",
1742           tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
1743     }
1745     modules = modules || [];
1746     modules.unshift(['$provide', function($provide) {
1747       $provide.value('$rootElement', element);
1748     }]);
1750     if (config.debugInfoEnabled) {
1751       // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1752       modules.push(['$compileProvider', function($compileProvider) {
1753         $compileProvider.debugInfoEnabled(true);
1754       }]);
1755     }
1757     modules.unshift('ng');
1758     var injector = createInjector(modules, config.strictDi);
1759     injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1760        function bootstrapApply(scope, element, compile, injector) {
1761         scope.$apply(function() {
1762           element.data('$injector', injector);
1763           compile(element)(scope);
1764         });
1765       }]
1766     );
1767     return injector;
1768   };
1770   var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1771   var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1773   if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1774     config.debugInfoEnabled = true;
1775     window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1776   }
1778   if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1779     return doBootstrap();
1780   }
1782   window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1783   angular.resumeBootstrap = function(extraModules) {
1784     forEach(extraModules, function(module) {
1785       modules.push(module);
1786     });
1787     return doBootstrap();
1788   };
1790   if (isFunction(angular.resumeDeferredBootstrap)) {
1791     angular.resumeDeferredBootstrap();
1792   }
1796  * @ngdoc function
1797  * @name angular.reloadWithDebugInfo
1798  * @module ng
1799  * @description
1800  * Use this function to reload the current application with debug information turned on.
1801  * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1803  * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1804  */
1805 function reloadWithDebugInfo() {
1806   window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1807   window.location.reload();
1811  * @name angular.getTestability
1812  * @module ng
1813  * @description
1814  * Get the testability service for the instance of Angular on the given
1815  * element.
1816  * @param {DOMElement} element DOM element which is the root of angular application.
1817  */
1818 function getTestability(rootElement) {
1819   var injector = angular.element(rootElement).injector();
1820   if (!injector) {
1821     throw ngMinErr('test',
1822       'no injector found for element argument to getTestability');
1823   }
1824   return injector.get('$$testability');
1827 var SNAKE_CASE_REGEXP = /[A-Z]/g;
1828 function snake_case(name, separator) {
1829   separator = separator || '_';
1830   return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1831     return (pos ? separator : '') + letter.toLowerCase();
1832   });
1835 var bindJQueryFired = false;
1836 function bindJQuery() {
1837   var originalCleanData;
1839   if (bindJQueryFired) {
1840     return;
1841   }
1843   // bind to jQuery if present;
1844   var jqName = jq();
1845   jQuery = isUndefined(jqName) ? window.jQuery :   // use jQuery (if present)
1846            !jqName             ? undefined     :   // use jqLite
1847                                  window[jqName];   // use jQuery specified by `ngJq`
1849   // Use jQuery if it exists with proper functionality, otherwise default to us.
1850   // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1851   // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1852   // versions. It will not work for sure with jQuery <1.7, though.
1853   if (jQuery && jQuery.fn.on) {
1854     jqLite = jQuery;
1855     extend(jQuery.fn, {
1856       scope: JQLitePrototype.scope,
1857       isolateScope: JQLitePrototype.isolateScope,
1858       controller: JQLitePrototype.controller,
1859       injector: JQLitePrototype.injector,
1860       inheritedData: JQLitePrototype.inheritedData
1861     });
1863     // All nodes removed from the DOM via various jQuery APIs like .remove()
1864     // are passed through jQuery.cleanData. Monkey-patch this method to fire
1865     // the $destroy event on all removed nodes.
1866     originalCleanData = jQuery.cleanData;
1867     jQuery.cleanData = function(elems) {
1868       var events;
1869       for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1870         events = jQuery._data(elem, "events");
1871         if (events && events.$destroy) {
1872           jQuery(elem).triggerHandler('$destroy');
1873         }
1874       }
1875       originalCleanData(elems);
1876     };
1877   } else {
1878     jqLite = JQLite;
1879   }
1881   angular.element = jqLite;
1883   // Prevent double-proxying.
1884   bindJQueryFired = true;
1888  * throw error if the argument is falsy.
1889  */
1890 function assertArg(arg, name, reason) {
1891   if (!arg) {
1892     throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1893   }
1894   return arg;
1897 function assertArgFn(arg, name, acceptArrayAnnotation) {
1898   if (acceptArrayAnnotation && isArray(arg)) {
1899       arg = arg[arg.length - 1];
1900   }
1902   assertArg(isFunction(arg), name, 'not a function, got ' +
1903       (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1904   return arg;
1908  * throw error if the name given is hasOwnProperty
1909  * @param  {String} name    the name to test
1910  * @param  {String} context the context in which the name is used, such as module or directive
1911  */
1912 function assertNotHasOwnProperty(name, context) {
1913   if (name === 'hasOwnProperty') {
1914     throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1915   }
1919  * Return the value accessible from the object by path. Any undefined traversals are ignored
1920  * @param {Object} obj starting object
1921  * @param {String} path path to traverse
1922  * @param {boolean} [bindFnToScope=true]
1923  * @returns {Object} value as accessible by path
1924  */
1925 //TODO(misko): this function needs to be removed
1926 function getter(obj, path, bindFnToScope) {
1927   if (!path) return obj;
1928   var keys = path.split('.');
1929   var key;
1930   var lastInstance = obj;
1931   var len = keys.length;
1933   for (var i = 0; i < len; i++) {
1934     key = keys[i];
1935     if (obj) {
1936       obj = (lastInstance = obj)[key];
1937     }
1938   }
1939   if (!bindFnToScope && isFunction(obj)) {
1940     return bind(lastInstance, obj);
1941   }
1942   return obj;
1946  * Return the DOM siblings between the first and last node in the given array.
1947  * @param {Array} array like object
1948  * @returns {Array} the inputted object or a jqLite collection containing the nodes
1949  */
1950 function getBlockNodes(nodes) {
1951   // TODO(perf): update `nodes` instead of creating a new object?
1952   var node = nodes[0];
1953   var endNode = nodes[nodes.length - 1];
1954   var blockNodes;
1956   for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
1957     if (blockNodes || nodes[i] !== node) {
1958       if (!blockNodes) {
1959         blockNodes = jqLite(slice.call(nodes, 0, i));
1960       }
1961       blockNodes.push(node);
1962     }
1963   }
1965   return blockNodes || nodes;
1970  * Creates a new object without a prototype. This object is useful for lookup without having to
1971  * guard against prototypically inherited properties via hasOwnProperty.
1973  * Related micro-benchmarks:
1974  * - http://jsperf.com/object-create2
1975  * - http://jsperf.com/proto-map-lookup/2
1976  * - http://jsperf.com/for-in-vs-object-keys2
1978  * @returns {Object}
1979  */
1980 function createMap() {
1981   return Object.create(null);
1984 var NODE_TYPE_ELEMENT = 1;
1985 var NODE_TYPE_ATTRIBUTE = 2;
1986 var NODE_TYPE_TEXT = 3;
1987 var NODE_TYPE_COMMENT = 8;
1988 var NODE_TYPE_DOCUMENT = 9;
1989 var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
1992  * @ngdoc type
1993  * @name angular.Module
1994  * @module ng
1995  * @description
1997  * Interface for configuring angular {@link angular.module modules}.
1998  */
2000 function setupModuleLoader(window) {
2002   var $injectorMinErr = minErr('$injector');
2003   var ngMinErr = minErr('ng');
2005   function ensure(obj, name, factory) {
2006     return obj[name] || (obj[name] = factory());
2007   }
2009   var angular = ensure(window, 'angular', Object);
2011   // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
2012   angular.$$minErr = angular.$$minErr || minErr;
2014   return ensure(angular, 'module', function() {
2015     /** @type {Object.<string, angular.Module>} */
2016     var modules = {};
2018     /**
2019      * @ngdoc function
2020      * @name angular.module
2021      * @module ng
2022      * @description
2023      *
2024      * The `angular.module` is a global place for creating, registering and retrieving Angular
2025      * modules.
2026      * All modules (angular core or 3rd party) that should be available to an application must be
2027      * registered using this mechanism.
2028      *
2029      * Passing one argument retrieves an existing {@link angular.Module},
2030      * whereas passing more than one argument creates a new {@link angular.Module}
2031      *
2032      *
2033      * # Module
2034      *
2035      * A module is a collection of services, directives, controllers, filters, and configuration information.
2036      * `angular.module` is used to configure the {@link auto.$injector $injector}.
2037      *
2038      * ```js
2039      * // Create a new module
2040      * var myModule = angular.module('myModule', []);
2041      *
2042      * // register a new service
2043      * myModule.value('appName', 'MyCoolApp');
2044      *
2045      * // configure existing services inside initialization blocks.
2046      * myModule.config(['$locationProvider', function($locationProvider) {
2047      *   // Configure existing providers
2048      *   $locationProvider.hashPrefix('!');
2049      * }]);
2050      * ```
2051      *
2052      * Then you can create an injector and load your modules like this:
2053      *
2054      * ```js
2055      * var injector = angular.injector(['ng', 'myModule'])
2056      * ```
2057      *
2058      * However it's more likely that you'll just use
2059      * {@link ng.directive:ngApp ngApp} or
2060      * {@link angular.bootstrap} to simplify this process for you.
2061      *
2062      * @param {!string} name The name of the module to create or retrieve.
2063      * @param {!Array.<string>=} requires If specified then new module is being created. If
2064      *        unspecified then the module is being retrieved for further configuration.
2065      * @param {Function=} configFn Optional configuration function for the module. Same as
2066      *        {@link angular.Module#config Module#config()}.
2067      * @returns {angular.Module} new module with the {@link angular.Module} api.
2068      */
2069     return function module(name, requires, configFn) {
2070       var assertNotHasOwnProperty = function(name, context) {
2071         if (name === 'hasOwnProperty') {
2072           throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
2073         }
2074       };
2076       assertNotHasOwnProperty(name, 'module');
2077       if (requires && modules.hasOwnProperty(name)) {
2078         modules[name] = null;
2079       }
2080       return ensure(modules, name, function() {
2081         if (!requires) {
2082           throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
2083              "the module name or forgot to load it. If registering a module ensure that you " +
2084              "specify the dependencies as the second argument.", name);
2085         }
2087         /** @type {!Array.<Array.<*>>} */
2088         var invokeQueue = [];
2090         /** @type {!Array.<Function>} */
2091         var configBlocks = [];
2093         /** @type {!Array.<Function>} */
2094         var runBlocks = [];
2096         var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
2098         /** @type {angular.Module} */
2099         var moduleInstance = {
2100           // Private state
2101           _invokeQueue: invokeQueue,
2102           _configBlocks: configBlocks,
2103           _runBlocks: runBlocks,
2105           /**
2106            * @ngdoc property
2107            * @name angular.Module#requires
2108            * @module ng
2109            *
2110            * @description
2111            * Holds the list of modules which the injector will load before the current module is
2112            * loaded.
2113            */
2114           requires: requires,
2116           /**
2117            * @ngdoc property
2118            * @name angular.Module#name
2119            * @module ng
2120            *
2121            * @description
2122            * Name of the module.
2123            */
2124           name: name,
2127           /**
2128            * @ngdoc method
2129            * @name angular.Module#provider
2130            * @module ng
2131            * @param {string} name service name
2132            * @param {Function} providerType Construction function for creating new instance of the
2133            *                                service.
2134            * @description
2135            * See {@link auto.$provide#provider $provide.provider()}.
2136            */
2137           provider: invokeLaterAndSetModuleName('$provide', 'provider'),
2139           /**
2140            * @ngdoc method
2141            * @name angular.Module#factory
2142            * @module ng
2143            * @param {string} name service name
2144            * @param {Function} providerFunction Function for creating new instance of the service.
2145            * @description
2146            * See {@link auto.$provide#factory $provide.factory()}.
2147            */
2148           factory: invokeLaterAndSetModuleName('$provide', 'factory'),
2150           /**
2151            * @ngdoc method
2152            * @name angular.Module#service
2153            * @module ng
2154            * @param {string} name service name
2155            * @param {Function} constructor A constructor function that will be instantiated.
2156            * @description
2157            * See {@link auto.$provide#service $provide.service()}.
2158            */
2159           service: invokeLaterAndSetModuleName('$provide', 'service'),
2161           /**
2162            * @ngdoc method
2163            * @name angular.Module#value
2164            * @module ng
2165            * @param {string} name service name
2166            * @param {*} object Service instance object.
2167            * @description
2168            * See {@link auto.$provide#value $provide.value()}.
2169            */
2170           value: invokeLater('$provide', 'value'),
2172           /**
2173            * @ngdoc method
2174            * @name angular.Module#constant
2175            * @module ng
2176            * @param {string} name constant name
2177            * @param {*} object Constant value.
2178            * @description
2179            * Because the constants are fixed, they get applied before other provide methods.
2180            * See {@link auto.$provide#constant $provide.constant()}.
2181            */
2182           constant: invokeLater('$provide', 'constant', 'unshift'),
2184            /**
2185            * @ngdoc method
2186            * @name angular.Module#decorator
2187            * @module ng
2188            * @param {string} name The name of the service to decorate.
2189            * @param {Function} decorFn This function will be invoked when the service needs to be
2190            *                           instantiated and should return the decorated service instance.
2191            * @description
2192            * See {@link auto.$provide#decorator $provide.decorator()}.
2193            */
2194           decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
2196           /**
2197            * @ngdoc method
2198            * @name angular.Module#animation
2199            * @module ng
2200            * @param {string} name animation name
2201            * @param {Function} animationFactory Factory function for creating new instance of an
2202            *                                    animation.
2203            * @description
2204            *
2205            * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
2206            *
2207            *
2208            * Defines an animation hook that can be later used with
2209            * {@link $animate $animate} service and directives that use this service.
2210            *
2211            * ```js
2212            * module.animation('.animation-name', function($inject1, $inject2) {
2213            *   return {
2214            *     eventName : function(element, done) {
2215            *       //code to run the animation
2216            *       //once complete, then run done()
2217            *       return function cancellationFunction(element) {
2218            *         //code to cancel the animation
2219            *       }
2220            *     }
2221            *   }
2222            * })
2223            * ```
2224            *
2225            * See {@link ng.$animateProvider#register $animateProvider.register()} and
2226            * {@link ngAnimate ngAnimate module} for more information.
2227            */
2228           animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
2230           /**
2231            * @ngdoc method
2232            * @name angular.Module#filter
2233            * @module ng
2234            * @param {string} name Filter name - this must be a valid angular expression identifier
2235            * @param {Function} filterFactory Factory function for creating new instance of filter.
2236            * @description
2237            * See {@link ng.$filterProvider#register $filterProvider.register()}.
2238            *
2239            * <div class="alert alert-warning">
2240            * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
2241            * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
2242            * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
2243            * (`myapp_subsection_filterx`).
2244            * </div>
2245            */
2246           filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
2248           /**
2249            * @ngdoc method
2250            * @name angular.Module#controller
2251            * @module ng
2252            * @param {string|Object} name Controller name, or an object map of controllers where the
2253            *    keys are the names and the values are the constructors.
2254            * @param {Function} constructor Controller constructor function.
2255            * @description
2256            * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
2257            */
2258           controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
2260           /**
2261            * @ngdoc method
2262            * @name angular.Module#directive
2263            * @module ng
2264            * @param {string|Object} name Directive name, or an object map of directives where the
2265            *    keys are the names and the values are the factories.
2266            * @param {Function} directiveFactory Factory function for creating new instance of
2267            * directives.
2268            * @description
2269            * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
2270            */
2271           directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
2273           /**
2274            * @ngdoc method
2275            * @name angular.Module#component
2276            * @module ng
2277            * @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp)
2278            * @param {Object} options Component definition object (a simplified
2279            *    {@link ng.$compile#directive-definition-object directive definition object})
2280            *
2281            * @description
2282            * See {@link ng.$compileProvider#component $compileProvider.component()}.
2283            */
2284           component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
2286           /**
2287            * @ngdoc method
2288            * @name angular.Module#config
2289            * @module ng
2290            * @param {Function} configFn Execute this function on module load. Useful for service
2291            *    configuration.
2292            * @description
2293            * Use this method to register work which needs to be performed on module loading.
2294            * For more about how to configure services, see
2295            * {@link providers#provider-recipe Provider Recipe}.
2296            */
2297           config: config,
2299           /**
2300            * @ngdoc method
2301            * @name angular.Module#run
2302            * @module ng
2303            * @param {Function} initializationFn Execute this function after injector creation.
2304            *    Useful for application initialization.
2305            * @description
2306            * Use this method to register work which should be performed when the injector is done
2307            * loading all modules.
2308            */
2309           run: function(block) {
2310             runBlocks.push(block);
2311             return this;
2312           }
2313         };
2315         if (configFn) {
2316           config(configFn);
2317         }
2319         return moduleInstance;
2321         /**
2322          * @param {string} provider
2323          * @param {string} method
2324          * @param {String=} insertMethod
2325          * @returns {angular.Module}
2326          */
2327         function invokeLater(provider, method, insertMethod, queue) {
2328           if (!queue) queue = invokeQueue;
2329           return function() {
2330             queue[insertMethod || 'push']([provider, method, arguments]);
2331             return moduleInstance;
2332           };
2333         }
2335         /**
2336          * @param {string} provider
2337          * @param {string} method
2338          * @returns {angular.Module}
2339          */
2340         function invokeLaterAndSetModuleName(provider, method) {
2341           return function(recipeName, factoryFunction) {
2342             if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2343             invokeQueue.push([provider, method, arguments]);
2344             return moduleInstance;
2345           };
2346         }
2347       });
2348     };
2349   });
2353 /* global shallowCopy: true */
2356  * Creates a shallow copy of an object, an array or a primitive.
2358  * Assumes that there are no proto properties for objects.
2359  */
2360 function shallowCopy(src, dst) {
2361   if (isArray(src)) {
2362     dst = dst || [];
2364     for (var i = 0, ii = src.length; i < ii; i++) {
2365       dst[i] = src[i];
2366     }
2367   } else if (isObject(src)) {
2368     dst = dst || {};
2370     for (var key in src) {
2371       if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
2372         dst[key] = src[key];
2373       }
2374     }
2375   }
2377   return dst || src;
2380 /* global toDebugString: true */
2382 function serializeObject(obj) {
2383   var seen = [];
2385   return JSON.stringify(obj, function(key, val) {
2386     val = toJsonReplacer(key, val);
2387     if (isObject(val)) {
2389       if (seen.indexOf(val) >= 0) return '...';
2391       seen.push(val);
2392     }
2393     return val;
2394   });
2397 function toDebugString(obj) {
2398   if (typeof obj === 'function') {
2399     return obj.toString().replace(/ \{[\s\S]*$/, '');
2400   } else if (isUndefined(obj)) {
2401     return 'undefined';
2402   } else if (typeof obj !== 'string') {
2403     return serializeObject(obj);
2404   }
2405   return obj;
2408 /* global angularModule: true,
2409   version: true,
2411   $CompileProvider,
2413   htmlAnchorDirective,
2414   inputDirective,
2415   inputDirective,
2416   formDirective,
2417   scriptDirective,
2418   selectDirective,
2419   styleDirective,
2420   optionDirective,
2421   ngBindDirective,
2422   ngBindHtmlDirective,
2423   ngBindTemplateDirective,
2424   ngClassDirective,
2425   ngClassEvenDirective,
2426   ngClassOddDirective,
2427   ngCloakDirective,
2428   ngControllerDirective,
2429   ngFormDirective,
2430   ngHideDirective,
2431   ngIfDirective,
2432   ngIncludeDirective,
2433   ngIncludeFillContentDirective,
2434   ngInitDirective,
2435   ngNonBindableDirective,
2436   ngPluralizeDirective,
2437   ngRepeatDirective,
2438   ngShowDirective,
2439   ngStyleDirective,
2440   ngSwitchDirective,
2441   ngSwitchWhenDirective,
2442   ngSwitchDefaultDirective,
2443   ngOptionsDirective,
2444   ngTranscludeDirective,
2445   ngModelDirective,
2446   ngListDirective,
2447   ngChangeDirective,
2448   patternDirective,
2449   patternDirective,
2450   requiredDirective,
2451   requiredDirective,
2452   minlengthDirective,
2453   minlengthDirective,
2454   maxlengthDirective,
2455   maxlengthDirective,
2456   ngValueDirective,
2457   ngModelOptionsDirective,
2458   ngAttributeAliasDirectives,
2459   ngEventDirectives,
2461   $AnchorScrollProvider,
2462   $AnimateProvider,
2463   $CoreAnimateCssProvider,
2464   $$CoreAnimateJsProvider,
2465   $$CoreAnimateQueueProvider,
2466   $$AnimateRunnerFactoryProvider,
2467   $$AnimateAsyncRunFactoryProvider,
2468   $BrowserProvider,
2469   $CacheFactoryProvider,
2470   $ControllerProvider,
2471   $DateProvider,
2472   $DocumentProvider,
2473   $ExceptionHandlerProvider,
2474   $FilterProvider,
2475   $$ForceReflowProvider,
2476   $InterpolateProvider,
2477   $IntervalProvider,
2478   $$HashMapProvider,
2479   $HttpProvider,
2480   $HttpParamSerializerProvider,
2481   $HttpParamSerializerJQLikeProvider,
2482   $HttpBackendProvider,
2483   $xhrFactoryProvider,
2484   $jsonpCallbacksProvider,
2485   $LocationProvider,
2486   $LogProvider,
2487   $ParseProvider,
2488   $RootScopeProvider,
2489   $QProvider,
2490   $$QProvider,
2491   $$SanitizeUriProvider,
2492   $SceProvider,
2493   $SceDelegateProvider,
2494   $SnifferProvider,
2495   $TemplateCacheProvider,
2496   $TemplateRequestProvider,
2497   $$TestabilityProvider,
2498   $TimeoutProvider,
2499   $$RAFProvider,
2500   $WindowProvider,
2501   $$jqLiteProvider,
2502   $$CookieReaderProvider
2507  * @ngdoc object
2508  * @name angular.version
2509  * @module ng
2510  * @description
2511  * An object that contains information about the current AngularJS version.
2513  * This object has the following properties:
2515  * - `full` â€“ `{string}` â€“ Full version string, such as "0.9.18".
2516  * - `major` â€“ `{number}` â€“ Major version number, such as "0".
2517  * - `minor` â€“ `{number}` â€“ Minor version number, such as "9".
2518  * - `dot` â€“ `{number}` â€“ Dot version number, such as "18".
2519  * - `codeName` â€“ `{string}` â€“ Code name of the release, such as "jiggling-armfat".
2520  */
2521 var version = {
2522   full: '1.5.8',    // all of these placeholder strings will be replaced by grunt's
2523   major: 1,    // package task
2524   minor: 5,
2525   dot: 8,
2526   codeName: 'arbitrary-fallbacks'
2530 function publishExternalAPI(angular) {
2531   extend(angular, {
2532     'bootstrap': bootstrap,
2533     'copy': copy,
2534     'extend': extend,
2535     'merge': merge,
2536     'equals': equals,
2537     'element': jqLite,
2538     'forEach': forEach,
2539     'injector': createInjector,
2540     'noop': noop,
2541     'bind': bind,
2542     'toJson': toJson,
2543     'fromJson': fromJson,
2544     'identity': identity,
2545     'isUndefined': isUndefined,
2546     'isDefined': isDefined,
2547     'isString': isString,
2548     'isFunction': isFunction,
2549     'isObject': isObject,
2550     'isNumber': isNumber,
2551     'isElement': isElement,
2552     'isArray': isArray,
2553     'version': version,
2554     'isDate': isDate,
2555     'lowercase': lowercase,
2556     'uppercase': uppercase,
2557     'callbacks': {$$counter: 0},
2558     'getTestability': getTestability,
2559     '$$minErr': minErr,
2560     '$$csp': csp,
2561     'reloadWithDebugInfo': reloadWithDebugInfo
2562   });
2564   angularModule = setupModuleLoader(window);
2566   angularModule('ng', ['ngLocale'], ['$provide',
2567     function ngModule($provide) {
2568       // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2569       $provide.provider({
2570         $$sanitizeUri: $$SanitizeUriProvider
2571       });
2572       $provide.provider('$compile', $CompileProvider).
2573         directive({
2574             a: htmlAnchorDirective,
2575             input: inputDirective,
2576             textarea: inputDirective,
2577             form: formDirective,
2578             script: scriptDirective,
2579             select: selectDirective,
2580             style: styleDirective,
2581             option: optionDirective,
2582             ngBind: ngBindDirective,
2583             ngBindHtml: ngBindHtmlDirective,
2584             ngBindTemplate: ngBindTemplateDirective,
2585             ngClass: ngClassDirective,
2586             ngClassEven: ngClassEvenDirective,
2587             ngClassOdd: ngClassOddDirective,
2588             ngCloak: ngCloakDirective,
2589             ngController: ngControllerDirective,
2590             ngForm: ngFormDirective,
2591             ngHide: ngHideDirective,
2592             ngIf: ngIfDirective,
2593             ngInclude: ngIncludeDirective,
2594             ngInit: ngInitDirective,
2595             ngNonBindable: ngNonBindableDirective,
2596             ngPluralize: ngPluralizeDirective,
2597             ngRepeat: ngRepeatDirective,
2598             ngShow: ngShowDirective,
2599             ngStyle: ngStyleDirective,
2600             ngSwitch: ngSwitchDirective,
2601             ngSwitchWhen: ngSwitchWhenDirective,
2602             ngSwitchDefault: ngSwitchDefaultDirective,
2603             ngOptions: ngOptionsDirective,
2604             ngTransclude: ngTranscludeDirective,
2605             ngModel: ngModelDirective,
2606             ngList: ngListDirective,
2607             ngChange: ngChangeDirective,
2608             pattern: patternDirective,
2609             ngPattern: patternDirective,
2610             required: requiredDirective,
2611             ngRequired: requiredDirective,
2612             minlength: minlengthDirective,
2613             ngMinlength: minlengthDirective,
2614             maxlength: maxlengthDirective,
2615             ngMaxlength: maxlengthDirective,
2616             ngValue: ngValueDirective,
2617             ngModelOptions: ngModelOptionsDirective
2618         }).
2619         directive({
2620           ngInclude: ngIncludeFillContentDirective
2621         }).
2622         directive(ngAttributeAliasDirectives).
2623         directive(ngEventDirectives);
2624       $provide.provider({
2625         $anchorScroll: $AnchorScrollProvider,
2626         $animate: $AnimateProvider,
2627         $animateCss: $CoreAnimateCssProvider,
2628         $$animateJs: $$CoreAnimateJsProvider,
2629         $$animateQueue: $$CoreAnimateQueueProvider,
2630         $$AnimateRunner: $$AnimateRunnerFactoryProvider,
2631         $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
2632         $browser: $BrowserProvider,
2633         $cacheFactory: $CacheFactoryProvider,
2634         $controller: $ControllerProvider,
2635         $document: $DocumentProvider,
2636         $exceptionHandler: $ExceptionHandlerProvider,
2637         $filter: $FilterProvider,
2638         $$forceReflow: $$ForceReflowProvider,
2639         $interpolate: $InterpolateProvider,
2640         $interval: $IntervalProvider,
2641         $http: $HttpProvider,
2642         $httpParamSerializer: $HttpParamSerializerProvider,
2643         $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2644         $httpBackend: $HttpBackendProvider,
2645         $xhrFactory: $xhrFactoryProvider,
2646         $jsonpCallbacks: $jsonpCallbacksProvider,
2647         $location: $LocationProvider,
2648         $log: $LogProvider,
2649         $parse: $ParseProvider,
2650         $rootScope: $RootScopeProvider,
2651         $q: $QProvider,
2652         $$q: $$QProvider,
2653         $sce: $SceProvider,
2654         $sceDelegate: $SceDelegateProvider,
2655         $sniffer: $SnifferProvider,
2656         $templateCache: $TemplateCacheProvider,
2657         $templateRequest: $TemplateRequestProvider,
2658         $$testability: $$TestabilityProvider,
2659         $timeout: $TimeoutProvider,
2660         $window: $WindowProvider,
2661         $$rAF: $$RAFProvider,
2662         $$jqLite: $$jqLiteProvider,
2663         $$HashMap: $$HashMapProvider,
2664         $$cookieReader: $$CookieReaderProvider
2665       });
2666     }
2667   ]);
2670 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2671  *     Any commits to this file should be reviewed with security in mind.  *
2672  *   Changes to this file can potentially create security vulnerabilities. *
2673  *          An approval from 2 Core members with history of modifying      *
2674  *                         this file is required.                          *
2675  *                                                                         *
2676  *  Does the change somehow allow for arbitrary javascript to be executed? *
2677  *    Or allows for someone to change the prototype of built-in objects?   *
2678  *     Or gives undesired access to variables likes document or window?    *
2679  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2681 /* global JQLitePrototype: true,
2682   addEventListenerFn: true,
2683   removeEventListenerFn: true,
2684   BOOLEAN_ATTR: true,
2685   ALIASED_ATTR: true,
2688 //////////////////////////////////
2689 //JQLite
2690 //////////////////////////////////
2693  * @ngdoc function
2694  * @name angular.element
2695  * @module ng
2696  * @kind function
2698  * @description
2699  * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2701  * If jQuery is available, `angular.element` is an alias for the
2702  * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2703  * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
2705  * jqLite is a tiny, API-compatible subset of jQuery that allows
2706  * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
2707  * commonly needed functionality with the goal of having a very small footprint.
2709  * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
2710  * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
2711  * specific version of jQuery if multiple versions exist on the page.
2713  * <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or
2714  * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
2716  * <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
2717  * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
2718  * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
2720  * ## Angular's jqLite
2721  * jqLite provides only the following jQuery methods:
2723  * - [`addClass()`](http://api.jquery.com/addClass/) - Does not support a function as first argument
2724  * - [`after()`](http://api.jquery.com/after/)
2725  * - [`append()`](http://api.jquery.com/append/)
2726  * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2727  * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2728  * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2729  * - [`clone()`](http://api.jquery.com/clone/)
2730  * - [`contents()`](http://api.jquery.com/contents/)
2731  * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`.
2732  *   As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing.
2733  * - [`data()`](http://api.jquery.com/data/)
2734  * - [`detach()`](http://api.jquery.com/detach/)
2735  * - [`empty()`](http://api.jquery.com/empty/)
2736  * - [`eq()`](http://api.jquery.com/eq/)
2737  * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2738  * - [`hasClass()`](http://api.jquery.com/hasClass/)
2739  * - [`html()`](http://api.jquery.com/html/)
2740  * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2741  * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2742  * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
2743  * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2744  * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2745  * - [`prepend()`](http://api.jquery.com/prepend/)
2746  * - [`prop()`](http://api.jquery.com/prop/)
2747  * - [`ready()`](http://api.jquery.com/ready/)
2748  * - [`remove()`](http://api.jquery.com/remove/)
2749  * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2750  * - [`removeClass()`](http://api.jquery.com/removeClass/) - Does not support a function as first argument
2751  * - [`removeData()`](http://api.jquery.com/removeData/)
2752  * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2753  * - [`text()`](http://api.jquery.com/text/)
2754  * - [`toggleClass()`](http://api.jquery.com/toggleClass/) - Does not support a function as first argument
2755  * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers
2756  * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
2757  * - [`val()`](http://api.jquery.com/val/)
2758  * - [`wrap()`](http://api.jquery.com/wrap/)
2760  * ## jQuery/jqLite Extras
2761  * Angular also provides the following additional methods and events to both jQuery and jqLite:
2763  * ### Events
2764  * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2765  *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
2766  *    element before it is removed.
2768  * ### Methods
2769  * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2770  *   retrieves controller associated with the `ngController` directive. If `name` is provided as
2771  *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
2772  *   `'ngModel'`).
2773  * - `injector()` - retrieves the injector of the current element or its parent.
2774  * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2775  *   element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2776  *   be enabled.
2777  * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2778  *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
2779  *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
2780  *   Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2781  * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2782  *   parent element is reached.
2784  * @knownIssue You cannot spy on `angular.element` if you are using Jasmine version 1.x. See
2785  * https://github.com/angular/angular.js/issues/14251 for more information.
2787  * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2788  * @returns {Object} jQuery object.
2789  */
2791 JQLite.expando = 'ng339';
2793 var jqCache = JQLite.cache = {},
2794     jqId = 1,
2795     addEventListenerFn = function(element, type, fn) {
2796       element.addEventListener(type, fn, false);
2797     },
2798     removeEventListenerFn = function(element, type, fn) {
2799       element.removeEventListener(type, fn, false);
2800     };
2803  * !!! This is an undocumented "private" function !!!
2804  */
2805 JQLite._data = function(node) {
2806   //jQuery always returns an object on cache miss
2807   return this.cache[node[this.expando]] || {};
2810 function jqNextId() { return ++jqId; }
2813 var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2814 var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2815 var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2816 var jqLiteMinErr = minErr('jqLite');
2819  * Converts snake_case to camelCase.
2820  * Also there is special case for Moz prefix starting with upper case letter.
2821  * @param name Name to normalize
2822  */
2823 function camelCase(name) {
2824   return name.
2825     replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2826       return offset ? letter.toUpperCase() : letter;
2827     }).
2828     replace(MOZ_HACK_REGEXP, 'Moz$1');
2831 var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
2832 var HTML_REGEXP = /<|&#?\w+;/;
2833 var TAG_NAME_REGEXP = /<([\w:-]+)/;
2834 var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
2836 var wrapMap = {
2837   'option': [1, '<select multiple="multiple">', '</select>'],
2839   'thead': [1, '<table>', '</table>'],
2840   'col': [2, '<table><colgroup>', '</colgroup></table>'],
2841   'tr': [2, '<table><tbody>', '</tbody></table>'],
2842   'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2843   '_default': [0, "", ""]
2846 wrapMap.optgroup = wrapMap.option;
2847 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2848 wrapMap.th = wrapMap.td;
2851 function jqLiteIsTextNode(html) {
2852   return !HTML_REGEXP.test(html);
2855 function jqLiteAcceptsData(node) {
2856   // The window object can accept data but has no nodeType
2857   // Otherwise we are only interested in elements (1) and documents (9)
2858   var nodeType = node.nodeType;
2859   return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2862 function jqLiteHasData(node) {
2863   for (var key in jqCache[node.ng339]) {
2864     return true;
2865   }
2866   return false;
2869 function jqLiteCleanData(nodes) {
2870   for (var i = 0, ii = nodes.length; i < ii; i++) {
2871     jqLiteRemoveData(nodes[i]);
2872   }
2875 function jqLiteBuildFragment(html, context) {
2876   var tmp, tag, wrap,
2877       fragment = context.createDocumentFragment(),
2878       nodes = [], i;
2880   if (jqLiteIsTextNode(html)) {
2881     // Convert non-html into a text node
2882     nodes.push(context.createTextNode(html));
2883   } else {
2884     // Convert html into DOM nodes
2885     tmp = fragment.appendChild(context.createElement("div"));
2886     tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2887     wrap = wrapMap[tag] || wrapMap._default;
2888     tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2890     // Descend through wrappers to the right content
2891     i = wrap[0];
2892     while (i--) {
2893       tmp = tmp.lastChild;
2894     }
2896     nodes = concat(nodes, tmp.childNodes);
2898     tmp = fragment.firstChild;
2899     tmp.textContent = "";
2900   }
2902   // Remove wrapper from fragment
2903   fragment.textContent = "";
2904   fragment.innerHTML = ""; // Clear inner HTML
2905   forEach(nodes, function(node) {
2906     fragment.appendChild(node);
2907   });
2909   return fragment;
2912 function jqLiteParseHTML(html, context) {
2913   context = context || window.document;
2914   var parsed;
2916   if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2917     return [context.createElement(parsed[1])];
2918   }
2920   if ((parsed = jqLiteBuildFragment(html, context))) {
2921     return parsed.childNodes;
2922   }
2924   return [];
2927 function jqLiteWrapNode(node, wrapper) {
2928   var parent = node.parentNode;
2930   if (parent) {
2931     parent.replaceChild(wrapper, node);
2932   }
2934   wrapper.appendChild(node);
2938 // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
2939 var jqLiteContains = window.Node.prototype.contains || function(arg) {
2940   // jshint bitwise: false
2941   return !!(this.compareDocumentPosition(arg) & 16);
2942   // jshint bitwise: true
2945 /////////////////////////////////////////////
2946 function JQLite(element) {
2947   if (element instanceof JQLite) {
2948     return element;
2949   }
2951   var argIsString;
2953   if (isString(element)) {
2954     element = trim(element);
2955     argIsString = true;
2956   }
2957   if (!(this instanceof JQLite)) {
2958     if (argIsString && element.charAt(0) != '<') {
2959       throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2960     }
2961     return new JQLite(element);
2962   }
2964   if (argIsString) {
2965     jqLiteAddNodes(this, jqLiteParseHTML(element));
2966   } else {
2967     jqLiteAddNodes(this, element);
2968   }
2971 function jqLiteClone(element) {
2972   return element.cloneNode(true);
2975 function jqLiteDealoc(element, onlyDescendants) {
2976   if (!onlyDescendants) jqLiteRemoveData(element);
2978   if (element.querySelectorAll) {
2979     var descendants = element.querySelectorAll('*');
2980     for (var i = 0, l = descendants.length; i < l; i++) {
2981       jqLiteRemoveData(descendants[i]);
2982     }
2983   }
2986 function jqLiteOff(element, type, fn, unsupported) {
2987   if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2989   var expandoStore = jqLiteExpandoStore(element);
2990   var events = expandoStore && expandoStore.events;
2991   var handle = expandoStore && expandoStore.handle;
2993   if (!handle) return; //no listeners registered
2995   if (!type) {
2996     for (type in events) {
2997       if (type !== '$destroy') {
2998         removeEventListenerFn(element, type, handle);
2999       }
3000       delete events[type];
3001     }
3002   } else {
3004     var removeHandler = function(type) {
3005       var listenerFns = events[type];
3006       if (isDefined(fn)) {
3007         arrayRemove(listenerFns || [], fn);
3008       }
3009       if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
3010         removeEventListenerFn(element, type, handle);
3011         delete events[type];
3012       }
3013     };
3015     forEach(type.split(' '), function(type) {
3016       removeHandler(type);
3017       if (MOUSE_EVENT_MAP[type]) {
3018         removeHandler(MOUSE_EVENT_MAP[type]);
3019       }
3020     });
3021   }
3024 function jqLiteRemoveData(element, name) {
3025   var expandoId = element.ng339;
3026   var expandoStore = expandoId && jqCache[expandoId];
3028   if (expandoStore) {
3029     if (name) {
3030       delete expandoStore.data[name];
3031       return;
3032     }
3034     if (expandoStore.handle) {
3035       if (expandoStore.events.$destroy) {
3036         expandoStore.handle({}, '$destroy');
3037       }
3038       jqLiteOff(element);
3039     }
3040     delete jqCache[expandoId];
3041     element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
3042   }
3046 function jqLiteExpandoStore(element, createIfNecessary) {
3047   var expandoId = element.ng339,
3048       expandoStore = expandoId && jqCache[expandoId];
3050   if (createIfNecessary && !expandoStore) {
3051     element.ng339 = expandoId = jqNextId();
3052     expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
3053   }
3055   return expandoStore;
3059 function jqLiteData(element, key, value) {
3060   if (jqLiteAcceptsData(element)) {
3062     var isSimpleSetter = isDefined(value);
3063     var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
3064     var massGetter = !key;
3065     var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
3066     var data = expandoStore && expandoStore.data;
3068     if (isSimpleSetter) { // data('key', value)
3069       data[key] = value;
3070     } else {
3071       if (massGetter) {  // data()
3072         return data;
3073       } else {
3074         if (isSimpleGetter) { // data('key')
3075           // don't force creation of expandoStore if it doesn't exist yet
3076           return data && data[key];
3077         } else { // mass-setter: data({key1: val1, key2: val2})
3078           extend(data, key);
3079         }
3080       }
3081     }
3082   }
3085 function jqLiteHasClass(element, selector) {
3086   if (!element.getAttribute) return false;
3087   return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
3088       indexOf(" " + selector + " ") > -1);
3091 function jqLiteRemoveClass(element, cssClasses) {
3092   if (cssClasses && element.setAttribute) {
3093     forEach(cssClasses.split(' '), function(cssClass) {
3094       element.setAttribute('class', trim(
3095           (" " + (element.getAttribute('class') || '') + " ")
3096           .replace(/[\n\t]/g, " ")
3097           .replace(" " + trim(cssClass) + " ", " "))
3098       );
3099     });
3100   }
3103 function jqLiteAddClass(element, cssClasses) {
3104   if (cssClasses && element.setAttribute) {
3105     var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
3106                             .replace(/[\n\t]/g, " ");
3108     forEach(cssClasses.split(' '), function(cssClass) {
3109       cssClass = trim(cssClass);
3110       if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
3111         existingClasses += cssClass + ' ';
3112       }
3113     });
3115     element.setAttribute('class', trim(existingClasses));
3116   }
3120 function jqLiteAddNodes(root, elements) {
3121   // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
3123   if (elements) {
3125     // if a Node (the most common case)
3126     if (elements.nodeType) {
3127       root[root.length++] = elements;
3128     } else {
3129       var length = elements.length;
3131       // if an Array or NodeList and not a Window
3132       if (typeof length === 'number' && elements.window !== elements) {
3133         if (length) {
3134           for (var i = 0; i < length; i++) {
3135             root[root.length++] = elements[i];
3136           }
3137         }
3138       } else {
3139         root[root.length++] = elements;
3140       }
3141     }
3142   }
3146 function jqLiteController(element, name) {
3147   return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
3150 function jqLiteInheritedData(element, name, value) {
3151   // if element is the document object work with the html element instead
3152   // this makes $(document).scope() possible
3153   if (element.nodeType == NODE_TYPE_DOCUMENT) {
3154     element = element.documentElement;
3155   }
3156   var names = isArray(name) ? name : [name];
3158   while (element) {
3159     for (var i = 0, ii = names.length; i < ii; i++) {
3160       if (isDefined(value = jqLite.data(element, names[i]))) return value;
3161     }
3163     // If dealing with a document fragment node with a host element, and no parent, use the host
3164     // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
3165     // to lookup parent controllers.
3166     element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
3167   }
3170 function jqLiteEmpty(element) {
3171   jqLiteDealoc(element, true);
3172   while (element.firstChild) {
3173     element.removeChild(element.firstChild);
3174   }
3177 function jqLiteRemove(element, keepData) {
3178   if (!keepData) jqLiteDealoc(element);
3179   var parent = element.parentNode;
3180   if (parent) parent.removeChild(element);
3184 function jqLiteDocumentLoaded(action, win) {
3185   win = win || window;
3186   if (win.document.readyState === 'complete') {
3187     // Force the action to be run async for consistent behavior
3188     // from the action's point of view
3189     // i.e. it will definitely not be in a $apply
3190     win.setTimeout(action);
3191   } else {
3192     // No need to unbind this handler as load is only ever called once
3193     jqLite(win).on('load', action);
3194   }
3197 //////////////////////////////////////////
3198 // Functions which are declared directly.
3199 //////////////////////////////////////////
3200 var JQLitePrototype = JQLite.prototype = {
3201   ready: function(fn) {
3202     var fired = false;
3204     function trigger() {
3205       if (fired) return;
3206       fired = true;
3207       fn();
3208     }
3210     // check if document is already loaded
3211     if (window.document.readyState === 'complete') {
3212       window.setTimeout(trigger);
3213     } else {
3214       this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
3215       // we can not use jqLite since we are not done loading and jQuery could be loaded later.
3216       // jshint -W064
3217       JQLite(window).on('load', trigger); // fallback to window.onload for others
3218       // jshint +W064
3219     }
3220   },
3221   toString: function() {
3222     var value = [];
3223     forEach(this, function(e) { value.push('' + e);});
3224     return '[' + value.join(', ') + ']';
3225   },
3227   eq: function(index) {
3228       return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3229   },
3231   length: 0,
3232   push: push,
3233   sort: [].sort,
3234   splice: [].splice
3237 //////////////////////////////////////////
3238 // Functions iterating getter/setters.
3239 // these functions return self on setter and
3240 // value on get.
3241 //////////////////////////////////////////
3242 var BOOLEAN_ATTR = {};
3243 forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3244   BOOLEAN_ATTR[lowercase(value)] = value;
3246 var BOOLEAN_ELEMENTS = {};
3247 forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3248   BOOLEAN_ELEMENTS[value] = true;
3250 var ALIASED_ATTR = {
3251   'ngMinlength': 'minlength',
3252   'ngMaxlength': 'maxlength',
3253   'ngMin': 'min',
3254   'ngMax': 'max',
3255   'ngPattern': 'pattern'
3258 function getBooleanAttrName(element, name) {
3259   // check dom last since we will most likely fail on name
3260   var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3262   // booleanAttr is here twice to minimize DOM access
3263   return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3266 function getAliasedAttrName(name) {
3267   return ALIASED_ATTR[name];
3270 forEach({
3271   data: jqLiteData,
3272   removeData: jqLiteRemoveData,
3273   hasData: jqLiteHasData,
3274   cleanData: jqLiteCleanData
3275 }, function(fn, name) {
3276   JQLite[name] = fn;
3279 forEach({
3280   data: jqLiteData,
3281   inheritedData: jqLiteInheritedData,
3283   scope: function(element) {
3284     // Can't use jqLiteData here directly so we stay compatible with jQuery!
3285     return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3286   },
3288   isolateScope: function(element) {
3289     // Can't use jqLiteData here directly so we stay compatible with jQuery!
3290     return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3291   },
3293   controller: jqLiteController,
3295   injector: function(element) {
3296     return jqLiteInheritedData(element, '$injector');
3297   },
3299   removeAttr: function(element, name) {
3300     element.removeAttribute(name);
3301   },
3303   hasClass: jqLiteHasClass,
3305   css: function(element, name, value) {
3306     name = camelCase(name);
3308     if (isDefined(value)) {
3309       element.style[name] = value;
3310     } else {
3311       return element.style[name];
3312     }
3313   },
3315   attr: function(element, name, value) {
3316     var nodeType = element.nodeType;
3317     if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
3318       return;
3319     }
3320     var lowercasedName = lowercase(name);
3321     if (BOOLEAN_ATTR[lowercasedName]) {
3322       if (isDefined(value)) {
3323         if (!!value) {
3324           element[name] = true;
3325           element.setAttribute(name, lowercasedName);
3326         } else {
3327           element[name] = false;
3328           element.removeAttribute(lowercasedName);
3329         }
3330       } else {
3331         return (element[name] ||
3332                  (element.attributes.getNamedItem(name) || noop).specified)
3333                ? lowercasedName
3334                : undefined;
3335       }
3336     } else if (isDefined(value)) {
3337       element.setAttribute(name, value);
3338     } else if (element.getAttribute) {
3339       // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
3340       // some elements (e.g. Document) don't have get attribute, so return undefined
3341       var ret = element.getAttribute(name, 2);
3342       // normalize non-existing attributes to undefined (as jQuery)
3343       return ret === null ? undefined : ret;
3344     }
3345   },
3347   prop: function(element, name, value) {
3348     if (isDefined(value)) {
3349       element[name] = value;
3350     } else {
3351       return element[name];
3352     }
3353   },
3355   text: (function() {
3356     getText.$dv = '';
3357     return getText;
3359     function getText(element, value) {
3360       if (isUndefined(value)) {
3361         var nodeType = element.nodeType;
3362         return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3363       }
3364       element.textContent = value;
3365     }
3366   })(),
3368   val: function(element, value) {
3369     if (isUndefined(value)) {
3370       if (element.multiple && nodeName_(element) === 'select') {
3371         var result = [];
3372         forEach(element.options, function(option) {
3373           if (option.selected) {
3374             result.push(option.value || option.text);
3375           }
3376         });
3377         return result.length === 0 ? null : result;
3378       }
3379       return element.value;
3380     }
3381     element.value = value;
3382   },
3384   html: function(element, value) {
3385     if (isUndefined(value)) {
3386       return element.innerHTML;
3387     }
3388     jqLiteDealoc(element, true);
3389     element.innerHTML = value;
3390   },
3392   empty: jqLiteEmpty
3393 }, function(fn, name) {
3394   /**
3395    * Properties: writes return selection, reads return first value
3396    */
3397   JQLite.prototype[name] = function(arg1, arg2) {
3398     var i, key;
3399     var nodeCount = this.length;
3401     // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3402     // in a way that survives minification.
3403     // jqLiteEmpty takes no arguments but is a setter.
3404     if (fn !== jqLiteEmpty &&
3405         (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3406       if (isObject(arg1)) {
3408         // we are a write, but the object properties are the key/values
3409         for (i = 0; i < nodeCount; i++) {
3410           if (fn === jqLiteData) {
3411             // data() takes the whole object in jQuery
3412             fn(this[i], arg1);
3413           } else {
3414             for (key in arg1) {
3415               fn(this[i], key, arg1[key]);
3416             }
3417           }
3418         }
3419         // return self for chaining
3420         return this;
3421       } else {
3422         // we are a read, so read the first child.
3423         // TODO: do we still need this?
3424         var value = fn.$dv;
3425         // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3426         var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3427         for (var j = 0; j < jj; j++) {
3428           var nodeValue = fn(this[j], arg1, arg2);
3429           value = value ? value + nodeValue : nodeValue;
3430         }
3431         return value;
3432       }
3433     } else {
3434       // we are a write, so apply to all children
3435       for (i = 0; i < nodeCount; i++) {
3436         fn(this[i], arg1, arg2);
3437       }
3438       // return self for chaining
3439       return this;
3440     }
3441   };
3444 function createEventHandler(element, events) {
3445   var eventHandler = function(event, type) {
3446     // jQuery specific api
3447     event.isDefaultPrevented = function() {
3448       return event.defaultPrevented;
3449     };
3451     var eventFns = events[type || event.type];
3452     var eventFnsLength = eventFns ? eventFns.length : 0;
3454     if (!eventFnsLength) return;
3456     if (isUndefined(event.immediatePropagationStopped)) {
3457       var originalStopImmediatePropagation = event.stopImmediatePropagation;
3458       event.stopImmediatePropagation = function() {
3459         event.immediatePropagationStopped = true;
3461         if (event.stopPropagation) {
3462           event.stopPropagation();
3463         }
3465         if (originalStopImmediatePropagation) {
3466           originalStopImmediatePropagation.call(event);
3467         }
3468       };
3469     }
3471     event.isImmediatePropagationStopped = function() {
3472       return event.immediatePropagationStopped === true;
3473     };
3475     // Some events have special handlers that wrap the real handler
3476     var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
3478     // Copy event handlers in case event handlers array is modified during execution.
3479     if ((eventFnsLength > 1)) {
3480       eventFns = shallowCopy(eventFns);
3481     }
3483     for (var i = 0; i < eventFnsLength; i++) {
3484       if (!event.isImmediatePropagationStopped()) {
3485         handlerWrapper(element, event, eventFns[i]);
3486       }
3487     }
3488   };
3490   // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3491   //       events on `element`
3492   eventHandler.elem = element;
3493   return eventHandler;
3496 function defaultHandlerWrapper(element, event, handler) {
3497   handler.call(element, event);
3500 function specialMouseHandlerWrapper(target, event, handler) {
3501   // Refer to jQuery's implementation of mouseenter & mouseleave
3502   // Read about mouseenter and mouseleave:
3503   // http://www.quirksmode.org/js/events_mouse.html#link8
3504   var related = event.relatedTarget;
3505   // For mousenter/leave call the handler if related is outside the target.
3506   // NB: No relatedTarget if the mouse left/entered the browser window
3507   if (!related || (related !== target && !jqLiteContains.call(target, related))) {
3508     handler.call(target, event);
3509   }
3512 //////////////////////////////////////////
3513 // Functions iterating traversal.
3514 // These functions chain results into a single
3515 // selector.
3516 //////////////////////////////////////////
3517 forEach({
3518   removeData: jqLiteRemoveData,
3520   on: function jqLiteOn(element, type, fn, unsupported) {
3521     if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3523     // Do not add event handlers to non-elements because they will not be cleaned up.
3524     if (!jqLiteAcceptsData(element)) {
3525       return;
3526     }
3528     var expandoStore = jqLiteExpandoStore(element, true);
3529     var events = expandoStore.events;
3530     var handle = expandoStore.handle;
3532     if (!handle) {
3533       handle = expandoStore.handle = createEventHandler(element, events);
3534     }
3536     // http://jsperf.com/string-indexof-vs-split
3537     var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3538     var i = types.length;
3540     var addHandler = function(type, specialHandlerWrapper, noEventListener) {
3541       var eventFns = events[type];
3543       if (!eventFns) {
3544         eventFns = events[type] = [];
3545         eventFns.specialHandlerWrapper = specialHandlerWrapper;
3546         if (type !== '$destroy' && !noEventListener) {
3547           addEventListenerFn(element, type, handle);
3548         }
3549       }
3551       eventFns.push(fn);
3552     };
3554     while (i--) {
3555       type = types[i];
3556       if (MOUSE_EVENT_MAP[type]) {
3557         addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
3558         addHandler(type, undefined, true);
3559       } else {
3560         addHandler(type);
3561       }
3562     }
3563   },
3565   off: jqLiteOff,
3567   one: function(element, type, fn) {
3568     element = jqLite(element);
3570     //add the listener twice so that when it is called
3571     //you can remove the original function and still be
3572     //able to call element.off(ev, fn) normally
3573     element.on(type, function onFn() {
3574       element.off(type, fn);
3575       element.off(type, onFn);
3576     });
3577     element.on(type, fn);
3578   },
3580   replaceWith: function(element, replaceNode) {
3581     var index, parent = element.parentNode;
3582     jqLiteDealoc(element);
3583     forEach(new JQLite(replaceNode), function(node) {
3584       if (index) {
3585         parent.insertBefore(node, index.nextSibling);
3586       } else {
3587         parent.replaceChild(node, element);
3588       }
3589       index = node;
3590     });
3591   },
3593   children: function(element) {
3594     var children = [];
3595     forEach(element.childNodes, function(element) {
3596       if (element.nodeType === NODE_TYPE_ELEMENT) {
3597         children.push(element);
3598       }
3599     });
3600     return children;
3601   },
3603   contents: function(element) {
3604     return element.contentDocument || element.childNodes || [];
3605   },
3607   append: function(element, node) {
3608     var nodeType = element.nodeType;
3609     if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3611     node = new JQLite(node);
3613     for (var i = 0, ii = node.length; i < ii; i++) {
3614       var child = node[i];
3615       element.appendChild(child);
3616     }
3617   },
3619   prepend: function(element, node) {
3620     if (element.nodeType === NODE_TYPE_ELEMENT) {
3621       var index = element.firstChild;
3622       forEach(new JQLite(node), function(child) {
3623         element.insertBefore(child, index);
3624       });
3625     }
3626   },
3628   wrap: function(element, wrapNode) {
3629     jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]);
3630   },
3632   remove: jqLiteRemove,
3634   detach: function(element) {
3635     jqLiteRemove(element, true);
3636   },
3638   after: function(element, newElement) {
3639     var index = element, parent = element.parentNode;
3640     newElement = new JQLite(newElement);
3642     for (var i = 0, ii = newElement.length; i < ii; i++) {
3643       var node = newElement[i];
3644       parent.insertBefore(node, index.nextSibling);
3645       index = node;
3646     }
3647   },
3649   addClass: jqLiteAddClass,
3650   removeClass: jqLiteRemoveClass,
3652   toggleClass: function(element, selector, condition) {
3653     if (selector) {
3654       forEach(selector.split(' '), function(className) {
3655         var classCondition = condition;
3656         if (isUndefined(classCondition)) {
3657           classCondition = !jqLiteHasClass(element, className);
3658         }
3659         (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3660       });
3661     }
3662   },
3664   parent: function(element) {
3665     var parent = element.parentNode;
3666     return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3667   },
3669   next: function(element) {
3670     return element.nextElementSibling;
3671   },
3673   find: function(element, selector) {
3674     if (element.getElementsByTagName) {
3675       return element.getElementsByTagName(selector);
3676     } else {
3677       return [];
3678     }
3679   },
3681   clone: jqLiteClone,
3683   triggerHandler: function(element, event, extraParameters) {
3685     var dummyEvent, eventFnsCopy, handlerArgs;
3686     var eventName = event.type || event;
3687     var expandoStore = jqLiteExpandoStore(element);
3688     var events = expandoStore && expandoStore.events;
3689     var eventFns = events && events[eventName];
3691     if (eventFns) {
3692       // Create a dummy event to pass to the handlers
3693       dummyEvent = {
3694         preventDefault: function() { this.defaultPrevented = true; },
3695         isDefaultPrevented: function() { return this.defaultPrevented === true; },
3696         stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3697         isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3698         stopPropagation: noop,
3699         type: eventName,
3700         target: element
3701       };
3703       // If a custom event was provided then extend our dummy event with it
3704       if (event.type) {
3705         dummyEvent = extend(dummyEvent, event);
3706       }
3708       // Copy event handlers in case event handlers array is modified during execution.
3709       eventFnsCopy = shallowCopy(eventFns);
3710       handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3712       forEach(eventFnsCopy, function(fn) {
3713         if (!dummyEvent.isImmediatePropagationStopped()) {
3714           fn.apply(element, handlerArgs);
3715         }
3716       });
3717     }
3718   }
3719 }, function(fn, name) {
3720   /**
3721    * chaining functions
3722    */
3723   JQLite.prototype[name] = function(arg1, arg2, arg3) {
3724     var value;
3726     for (var i = 0, ii = this.length; i < ii; i++) {
3727       if (isUndefined(value)) {
3728         value = fn(this[i], arg1, arg2, arg3);
3729         if (isDefined(value)) {
3730           // any function which returns a value needs to be wrapped
3731           value = jqLite(value);
3732         }
3733       } else {
3734         jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3735       }
3736     }
3737     return isDefined(value) ? value : this;
3738   };
3740   // bind legacy bind/unbind to on/off
3741   JQLite.prototype.bind = JQLite.prototype.on;
3742   JQLite.prototype.unbind = JQLite.prototype.off;
3746 // Provider for private $$jqLite service
3747 function $$jqLiteProvider() {
3748   this.$get = function $$jqLite() {
3749     return extend(JQLite, {
3750       hasClass: function(node, classes) {
3751         if (node.attr) node = node[0];
3752         return jqLiteHasClass(node, classes);
3753       },
3754       addClass: function(node, classes) {
3755         if (node.attr) node = node[0];
3756         return jqLiteAddClass(node, classes);
3757       },
3758       removeClass: function(node, classes) {
3759         if (node.attr) node = node[0];
3760         return jqLiteRemoveClass(node, classes);
3761       }
3762     });
3763   };
3767  * Computes a hash of an 'obj'.
3768  * Hash of a:
3769  *  string is string
3770  *  number is number as string
3771  *  object is either result of calling $$hashKey function on the object or uniquely generated id,
3772  *         that is also assigned to the $$hashKey property of the object.
3774  * @param obj
3775  * @returns {string} hash string such that the same input will have the same hash string.
3776  *         The resulting string key is in 'type:hashKey' format.
3777  */
3778 function hashKey(obj, nextUidFn) {
3779   var key = obj && obj.$$hashKey;
3781   if (key) {
3782     if (typeof key === 'function') {
3783       key = obj.$$hashKey();
3784     }
3785     return key;
3786   }
3788   var objType = typeof obj;
3789   if (objType == 'function' || (objType == 'object' && obj !== null)) {
3790     key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3791   } else {
3792     key = objType + ':' + obj;
3793   }
3795   return key;
3799  * HashMap which can use objects as keys
3800  */
3801 function HashMap(array, isolatedUid) {
3802   if (isolatedUid) {
3803     var uid = 0;
3804     this.nextUid = function() {
3805       return ++uid;
3806     };
3807   }
3808   forEach(array, this.put, this);
3810 HashMap.prototype = {
3811   /**
3812    * Store key value pair
3813    * @param key key to store can be any type
3814    * @param value value to store can be any type
3815    */
3816   put: function(key, value) {
3817     this[hashKey(key, this.nextUid)] = value;
3818   },
3820   /**
3821    * @param key
3822    * @returns {Object} the value for the key
3823    */
3824   get: function(key) {
3825     return this[hashKey(key, this.nextUid)];
3826   },
3828   /**
3829    * Remove the key/value pair
3830    * @param key
3831    */
3832   remove: function(key) {
3833     var value = this[key = hashKey(key, this.nextUid)];
3834     delete this[key];
3835     return value;
3836   }
3839 var $$HashMapProvider = [function() {
3840   this.$get = [function() {
3841     return HashMap;
3842   }];
3846  * @ngdoc function
3847  * @module ng
3848  * @name angular.injector
3849  * @kind function
3851  * @description
3852  * Creates an injector object that can be used for retrieving services as well as for
3853  * dependency injection (see {@link guide/di dependency injection}).
3855  * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3856  *     {@link angular.module}. The `ng` module must be explicitly added.
3857  * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3858  *     disallows argument name annotation inference.
3859  * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3861  * @example
3862  * Typical usage
3863  * ```js
3864  *   // create an injector
3865  *   var $injector = angular.injector(['ng']);
3867  *   // use the injector to kick off your application
3868  *   // use the type inference to auto inject arguments, or use implicit injection
3869  *   $injector.invoke(function($rootScope, $compile, $document) {
3870  *     $compile($document)($rootScope);
3871  *     $rootScope.$digest();
3872  *   });
3873  * ```
3875  * Sometimes you want to get access to the injector of a currently running Angular app
3876  * from outside Angular. Perhaps, you want to inject and compile some markup after the
3877  * application has been bootstrapped. You can do this using the extra `injector()` added
3878  * to JQuery/jqLite elements. See {@link angular.element}.
3880  * *This is fairly rare but could be the case if a third party library is injecting the
3881  * markup.*
3883  * In the following example a new block of HTML containing a `ng-controller`
3884  * directive is added to the end of the document body by JQuery. We then compile and link
3885  * it into the current AngularJS scope.
3887  * ```js
3888  * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3889  * $(document.body).append($div);
3891  * angular.element(document).injector().invoke(function($compile) {
3892  *   var scope = angular.element($div).scope();
3893  *   $compile($div)(scope);
3894  * });
3895  * ```
3896  */
3900  * @ngdoc module
3901  * @name auto
3902  * @installation
3903  * @description
3905  * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3906  */
3908 var ARROW_ARG = /^([^\(]+?)=>/;
3909 var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
3910 var FN_ARG_SPLIT = /,/;
3911 var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3912 var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3913 var $injectorMinErr = minErr('$injector');
3915 function stringifyFn(fn) {
3916   // Support: Chrome 50-51 only
3917   // Creating a new string by adding `' '` at the end, to hack around some bug in Chrome v50/51
3918   // (See https://github.com/angular/angular.js/issues/14487.)
3919   // TODO (gkalpak): Remove workaround when Chrome v52 is released
3920   return Function.prototype.toString.call(fn) + ' ';
3923 function extractArgs(fn) {
3924   var fnText = stringifyFn(fn).replace(STRIP_COMMENTS, ''),
3925       args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
3926   return args;
3929 function anonFn(fn) {
3930   // For anonymous functions, showing at the very least the function signature can help in
3931   // debugging.
3932   var args = extractArgs(fn);
3933   if (args) {
3934     return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3935   }
3936   return 'fn';
3939 function annotate(fn, strictDi, name) {
3940   var $inject,
3941       argDecl,
3942       last;
3944   if (typeof fn === 'function') {
3945     if (!($inject = fn.$inject)) {
3946       $inject = [];
3947       if (fn.length) {
3948         if (strictDi) {
3949           if (!isString(name) || !name) {
3950             name = fn.name || anonFn(fn);
3951           }
3952           throw $injectorMinErr('strictdi',
3953             '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3954         }
3955         argDecl = extractArgs(fn);
3956         forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3957           arg.replace(FN_ARG, function(all, underscore, name) {
3958             $inject.push(name);
3959           });
3960         });
3961       }
3962       fn.$inject = $inject;
3963     }
3964   } else if (isArray(fn)) {
3965     last = fn.length - 1;
3966     assertArgFn(fn[last], 'fn');
3967     $inject = fn.slice(0, last);
3968   } else {
3969     assertArgFn(fn, 'fn', true);
3970   }
3971   return $inject;
3974 ///////////////////////////////////////
3977  * @ngdoc service
3978  * @name $injector
3980  * @description
3982  * `$injector` is used to retrieve object instances as defined by
3983  * {@link auto.$provide provider}, instantiate types, invoke methods,
3984  * and load modules.
3986  * The following always holds true:
3988  * ```js
3989  *   var $injector = angular.injector();
3990  *   expect($injector.get('$injector')).toBe($injector);
3991  *   expect($injector.invoke(function($injector) {
3992  *     return $injector;
3993  *   })).toBe($injector);
3994  * ```
3996  * # Injection Function Annotation
3998  * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3999  * following are all valid ways of annotating function with injection arguments and are equivalent.
4001  * ```js
4002  *   // inferred (only works if code not minified/obfuscated)
4003  *   $injector.invoke(function(serviceA){});
4005  *   // annotated
4006  *   function explicit(serviceA) {};
4007  *   explicit.$inject = ['serviceA'];
4008  *   $injector.invoke(explicit);
4010  *   // inline
4011  *   $injector.invoke(['serviceA', function(serviceA){}]);
4012  * ```
4014  * ## Inference
4016  * In JavaScript calling `toString()` on a function returns the function definition. The definition
4017  * can then be parsed and the function arguments can be extracted. This method of discovering
4018  * annotations is disallowed when the injector is in strict mode.
4019  * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
4020  * argument names.
4022  * ## `$inject` Annotation
4023  * By adding an `$inject` property onto a function the injection parameters can be specified.
4025  * ## Inline
4026  * As an array of injection names, where the last item in the array is the function to call.
4027  */
4030  * @ngdoc method
4031  * @name $injector#get
4033  * @description
4034  * Return an instance of the service.
4036  * @param {string} name The name of the instance to retrieve.
4037  * @param {string=} caller An optional string to provide the origin of the function call for error messages.
4038  * @return {*} The instance.
4039  */
4042  * @ngdoc method
4043  * @name $injector#invoke
4045  * @description
4046  * Invoke the method and supply the method arguments from the `$injector`.
4048  * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
4049  *   injected according to the {@link guide/di $inject Annotation} rules.
4050  * @param {Object=} self The `this` for the invoked method.
4051  * @param {Object=} locals Optional object. If preset then any argument names are read from this
4052  *                         object first, before the `$injector` is consulted.
4053  * @returns {*} the value returned by the invoked `fn` function.
4054  */
4057  * @ngdoc method
4058  * @name $injector#has
4060  * @description
4061  * Allows the user to query if the particular service exists.
4063  * @param {string} name Name of the service to query.
4064  * @returns {boolean} `true` if injector has given service.
4065  */
4068  * @ngdoc method
4069  * @name $injector#instantiate
4070  * @description
4071  * Create a new instance of JS type. The method takes a constructor function, invokes the new
4072  * operator, and supplies all of the arguments to the constructor function as specified by the
4073  * constructor annotation.
4075  * @param {Function} Type Annotated constructor function.
4076  * @param {Object=} locals Optional object. If preset then any argument names are read from this
4077  * object first, before the `$injector` is consulted.
4078  * @returns {Object} new instance of `Type`.
4079  */
4082  * @ngdoc method
4083  * @name $injector#annotate
4085  * @description
4086  * Returns an array of service names which the function is requesting for injection. This API is
4087  * used by the injector to determine which services need to be injected into the function when the
4088  * function is invoked. There are three ways in which the function can be annotated with the needed
4089  * dependencies.
4091  * # Argument names
4093  * The simplest form is to extract the dependencies from the arguments of the function. This is done
4094  * by converting the function into a string using `toString()` method and extracting the argument
4095  * names.
4096  * ```js
4097  *   // Given
4098  *   function MyController($scope, $route) {
4099  *     // ...
4100  *   }
4102  *   // Then
4103  *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4104  * ```
4106  * You can disallow this method by using strict injection mode.
4108  * This method does not work with code minification / obfuscation. For this reason the following
4109  * annotation strategies are supported.
4111  * # The `$inject` property
4113  * If a function has an `$inject` property and its value is an array of strings, then the strings
4114  * represent names of services to be injected into the function.
4115  * ```js
4116  *   // Given
4117  *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
4118  *     // ...
4119  *   }
4120  *   // Define function dependencies
4121  *   MyController['$inject'] = ['$scope', '$route'];
4123  *   // Then
4124  *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4125  * ```
4127  * # The array notation
4129  * It is often desirable to inline Injected functions and that's when setting the `$inject` property
4130  * is very inconvenient. In these situations using the array notation to specify the dependencies in
4131  * a way that survives minification is a better choice:
4133  * ```js
4134  *   // We wish to write this (not minification / obfuscation safe)
4135  *   injector.invoke(function($compile, $rootScope) {
4136  *     // ...
4137  *   });
4139  *   // We are forced to write break inlining
4140  *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
4141  *     // ...
4142  *   };
4143  *   tmpFn.$inject = ['$compile', '$rootScope'];
4144  *   injector.invoke(tmpFn);
4146  *   // To better support inline function the inline annotation is supported
4147  *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
4148  *     // ...
4149  *   }]);
4151  *   // Therefore
4152  *   expect(injector.annotate(
4153  *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
4154  *    ).toEqual(['$compile', '$rootScope']);
4155  * ```
4157  * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
4158  * be retrieved as described above.
4160  * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
4162  * @returns {Array.<string>} The names of the services which the function requires.
4163  */
4169  * @ngdoc service
4170  * @name $provide
4172  * @description
4174  * The {@link auto.$provide $provide} service has a number of methods for registering components
4175  * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
4176  * {@link angular.Module}.
4178  * An Angular **service** is a singleton object created by a **service factory**.  These **service
4179  * factories** are functions which, in turn, are created by a **service provider**.
4180  * The **service providers** are constructor functions. When instantiated they must contain a
4181  * property called `$get`, which holds the **service factory** function.
4183  * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
4184  * correct **service provider**, instantiating it and then calling its `$get` **service factory**
4185  * function to get the instance of the **service**.
4187  * Often services have no configuration options and there is no need to add methods to the service
4188  * provider.  The provider will be no more than a constructor function with a `$get` property. For
4189  * these cases the {@link auto.$provide $provide} service has additional helper methods to register
4190  * services without specifying a provider.
4192  * * {@link auto.$provide#provider provider(name, provider)} - registers a **service provider** with the
4193  *     {@link auto.$injector $injector}
4194  * * {@link auto.$provide#constant constant(name, obj)} - registers a value/object that can be accessed by
4195  *     providers and services.
4196  * * {@link auto.$provide#value value(name, obj)} - registers a value/object that can only be accessed by
4197  *     services, not providers.
4198  * * {@link auto.$provide#factory factory(name, fn)} - registers a service **factory function**
4199  *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
4200  *     given factory function.
4201  * * {@link auto.$provide#service service(name, Fn)} - registers a **constructor function**
4202  *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
4203  *      a new object using the given constructor function.
4204  * * {@link auto.$provide#decorator decorator(name, decorFn)} - registers a **decorator function** that
4205  *      will be able to modify or replace the implementation of another service.
4207  * See the individual methods for more information and examples.
4208  */
4211  * @ngdoc method
4212  * @name $provide#provider
4213  * @description
4215  * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
4216  * are constructor functions, whose instances are responsible for "providing" a factory for a
4217  * service.
4219  * Service provider names start with the name of the service they provide followed by `Provider`.
4220  * For example, the {@link ng.$log $log} service has a provider called
4221  * {@link ng.$logProvider $logProvider}.
4223  * Service provider objects can have additional methods which allow configuration of the provider
4224  * and its service. Importantly, you can configure what kind of service is created by the `$get`
4225  * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
4226  * method {@link ng.$logProvider#debugEnabled debugEnabled}
4227  * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
4228  * console or not.
4230  * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
4231                         'Provider'` key.
4232  * @param {(Object|function())} provider If the provider is:
4234  *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
4235  *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
4236  *   - `Constructor`: a new instance of the provider will be created using
4237  *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
4239  * @returns {Object} registered provider instance
4241  * @example
4243  * The following example shows how to create a simple event tracking service and register it using
4244  * {@link auto.$provide#provider $provide.provider()}.
4246  * ```js
4247  *  // Define the eventTracker provider
4248  *  function EventTrackerProvider() {
4249  *    var trackingUrl = '/track';
4251  *    // A provider method for configuring where the tracked events should been saved
4252  *    this.setTrackingUrl = function(url) {
4253  *      trackingUrl = url;
4254  *    };
4256  *    // The service factory function
4257  *    this.$get = ['$http', function($http) {
4258  *      var trackedEvents = {};
4259  *      return {
4260  *        // Call this to track an event
4261  *        event: function(event) {
4262  *          var count = trackedEvents[event] || 0;
4263  *          count += 1;
4264  *          trackedEvents[event] = count;
4265  *          return count;
4266  *        },
4267  *        // Call this to save the tracked events to the trackingUrl
4268  *        save: function() {
4269  *          $http.post(trackingUrl, trackedEvents);
4270  *        }
4271  *      };
4272  *    }];
4273  *  }
4275  *  describe('eventTracker', function() {
4276  *    var postSpy;
4278  *    beforeEach(module(function($provide) {
4279  *      // Register the eventTracker provider
4280  *      $provide.provider('eventTracker', EventTrackerProvider);
4281  *    }));
4283  *    beforeEach(module(function(eventTrackerProvider) {
4284  *      // Configure eventTracker provider
4285  *      eventTrackerProvider.setTrackingUrl('/custom-track');
4286  *    }));
4288  *    it('tracks events', inject(function(eventTracker) {
4289  *      expect(eventTracker.event('login')).toEqual(1);
4290  *      expect(eventTracker.event('login')).toEqual(2);
4291  *    }));
4293  *    it('saves to the tracking url', inject(function(eventTracker, $http) {
4294  *      postSpy = spyOn($http, 'post');
4295  *      eventTracker.event('login');
4296  *      eventTracker.save();
4297  *      expect(postSpy).toHaveBeenCalled();
4298  *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4299  *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4300  *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4301  *    }));
4302  *  });
4303  * ```
4304  */
4307  * @ngdoc method
4308  * @name $provide#factory
4309  * @description
4311  * Register a **service factory**, which will be called to return the service instance.
4312  * This is short for registering a service where its provider consists of only a `$get` property,
4313  * which is the given service factory function.
4314  * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4315  * configure your service in a provider.
4317  * @param {string} name The name of the instance.
4318  * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4319  *                      Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4320  * @returns {Object} registered provider instance
4322  * @example
4323  * Here is an example of registering a service
4324  * ```js
4325  *   $provide.factory('ping', ['$http', function($http) {
4326  *     return function ping() {
4327  *       return $http.send('/ping');
4328  *     };
4329  *   }]);
4330  * ```
4331  * You would then inject and use this service like this:
4332  * ```js
4333  *   someModule.controller('Ctrl', ['ping', function(ping) {
4334  *     ping();
4335  *   }]);
4336  * ```
4337  */
4341  * @ngdoc method
4342  * @name $provide#service
4343  * @description
4345  * Register a **service constructor**, which will be invoked with `new` to create the service
4346  * instance.
4347  * This is short for registering a service where its provider's `$get` property is a factory
4348  * function that returns an instance instantiated by the injector from the service constructor
4349  * function.
4351  * Internally it looks a bit like this:
4353  * ```
4354  * {
4355  *   $get: function() {
4356  *     return $injector.instantiate(constructor);
4357  *   }
4358  * }
4359  * ```
4362  * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4363  * as a type/class.
4365  * @param {string} name The name of the instance.
4366  * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4367  *     that will be instantiated.
4368  * @returns {Object} registered provider instance
4370  * @example
4371  * Here is an example of registering a service using
4372  * {@link auto.$provide#service $provide.service(class)}.
4373  * ```js
4374  *   var Ping = function($http) {
4375  *     this.$http = $http;
4376  *   };
4378  *   Ping.$inject = ['$http'];
4380  *   Ping.prototype.send = function() {
4381  *     return this.$http.get('/ping');
4382  *   };
4383  *   $provide.service('ping', Ping);
4384  * ```
4385  * You would then inject and use this service like this:
4386  * ```js
4387  *   someModule.controller('Ctrl', ['ping', function(ping) {
4388  *     ping.send();
4389  *   }]);
4390  * ```
4391  */
4395  * @ngdoc method
4396  * @name $provide#value
4397  * @description
4399  * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4400  * number, an array, an object or a function. This is short for registering a service where its
4401  * provider's `$get` property is a factory function that takes no arguments and returns the **value
4402  * service**. That also means it is not possible to inject other services into a value service.
4404  * Value services are similar to constant services, except that they cannot be injected into a
4405  * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4406  * an Angular {@link auto.$provide#decorator decorator}.
4408  * @param {string} name The name of the instance.
4409  * @param {*} value The value.
4410  * @returns {Object} registered provider instance
4412  * @example
4413  * Here are some examples of creating value services.
4414  * ```js
4415  *   $provide.value('ADMIN_USER', 'admin');
4417  *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4419  *   $provide.value('halfOf', function(value) {
4420  *     return value / 2;
4421  *   });
4422  * ```
4423  */
4427  * @ngdoc method
4428  * @name $provide#constant
4429  * @description
4431  * Register a **constant service** with the {@link auto.$injector $injector}, such as a string,
4432  * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not
4433  * possible to inject other services into a constant.
4435  * But unlike {@link auto.$provide#value value}, a constant can be
4436  * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4437  * be overridden by an Angular {@link auto.$provide#decorator decorator}.
4439  * @param {string} name The name of the constant.
4440  * @param {*} value The constant value.
4441  * @returns {Object} registered instance
4443  * @example
4444  * Here a some examples of creating constants:
4445  * ```js
4446  *   $provide.constant('SHARD_HEIGHT', 306);
4448  *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4450  *   $provide.constant('double', function(value) {
4451  *     return value * 2;
4452  *   });
4453  * ```
4454  */
4458  * @ngdoc method
4459  * @name $provide#decorator
4460  * @description
4462  * Register a **decorator function** with the {@link auto.$injector $injector}. A decorator function
4463  * intercepts the creation of a service, allowing it to override or modify the behavior of the
4464  * service. The return value of the decorator function may be the original service, or a new service
4465  * that replaces (or wraps and delegates to) the original service.
4467  * You can find out more about using decorators in the {@link guide/decorators} guide.
4469  * @param {string} name The name of the service to decorate.
4470  * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4471  *    provided and should return the decorated service instance. The function is called using
4472  *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4473  *    Local injection arguments:
4475  *    * `$delegate` - The original service instance, which can be replaced, monkey patched, configured,
4476  *      decorated or delegated to.
4478  * @example
4479  * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4480  * calls to {@link ng.$log#error $log.warn()}.
4481  * ```js
4482  *   $provide.decorator('$log', ['$delegate', function($delegate) {
4483  *     $delegate.warn = $delegate.error;
4484  *     return $delegate;
4485  *   }]);
4486  * ```
4487  */
4490 function createInjector(modulesToLoad, strictDi) {
4491   strictDi = (strictDi === true);
4492   var INSTANTIATING = {},
4493       providerSuffix = 'Provider',
4494       path = [],
4495       loadedModules = new HashMap([], true),
4496       providerCache = {
4497         $provide: {
4498             provider: supportObject(provider),
4499             factory: supportObject(factory),
4500             service: supportObject(service),
4501             value: supportObject(value),
4502             constant: supportObject(constant),
4503             decorator: decorator
4504           }
4505       },
4506       providerInjector = (providerCache.$injector =
4507           createInternalInjector(providerCache, function(serviceName, caller) {
4508             if (angular.isString(caller)) {
4509               path.push(caller);
4510             }
4511             throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
4512           })),
4513       instanceCache = {},
4514       protoInstanceInjector =
4515           createInternalInjector(instanceCache, function(serviceName, caller) {
4516             var provider = providerInjector.get(serviceName + providerSuffix, caller);
4517             return instanceInjector.invoke(
4518                 provider.$get, provider, undefined, serviceName);
4519           }),
4520       instanceInjector = protoInstanceInjector;
4522   providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
4523   var runBlocks = loadModules(modulesToLoad);
4524   instanceInjector = protoInstanceInjector.get('$injector');
4525   instanceInjector.strictDi = strictDi;
4526   forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });
4528   return instanceInjector;
4530   ////////////////////////////////////
4531   // $provider
4532   ////////////////////////////////////
4534   function supportObject(delegate) {
4535     return function(key, value) {
4536       if (isObject(key)) {
4537         forEach(key, reverseParams(delegate));
4538       } else {
4539         return delegate(key, value);
4540       }
4541     };
4542   }
4544   function provider(name, provider_) {
4545     assertNotHasOwnProperty(name, 'service');
4546     if (isFunction(provider_) || isArray(provider_)) {
4547       provider_ = providerInjector.instantiate(provider_);
4548     }
4549     if (!provider_.$get) {
4550       throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4551     }
4552     return providerCache[name + providerSuffix] = provider_;
4553   }
4555   function enforceReturnValue(name, factory) {
4556     return function enforcedReturnValue() {
4557       var result = instanceInjector.invoke(factory, this);
4558       if (isUndefined(result)) {
4559         throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4560       }
4561       return result;
4562     };
4563   }
4565   function factory(name, factoryFn, enforce) {
4566     return provider(name, {
4567       $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4568     });
4569   }
4571   function service(name, constructor) {
4572     return factory(name, ['$injector', function($injector) {
4573       return $injector.instantiate(constructor);
4574     }]);
4575   }
4577   function value(name, val) { return factory(name, valueFn(val), false); }
4579   function constant(name, value) {
4580     assertNotHasOwnProperty(name, 'constant');
4581     providerCache[name] = value;
4582     instanceCache[name] = value;
4583   }
4585   function decorator(serviceName, decorFn) {
4586     var origProvider = providerInjector.get(serviceName + providerSuffix),
4587         orig$get = origProvider.$get;
4589     origProvider.$get = function() {
4590       var origInstance = instanceInjector.invoke(orig$get, origProvider);
4591       return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4592     };
4593   }
4595   ////////////////////////////////////
4596   // Module Loading
4597   ////////////////////////////////////
4598   function loadModules(modulesToLoad) {
4599     assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
4600     var runBlocks = [], moduleFn;
4601     forEach(modulesToLoad, function(module) {
4602       if (loadedModules.get(module)) return;
4603       loadedModules.put(module, true);
4605       function runInvokeQueue(queue) {
4606         var i, ii;
4607         for (i = 0, ii = queue.length; i < ii; i++) {
4608           var invokeArgs = queue[i],
4609               provider = providerInjector.get(invokeArgs[0]);
4611           provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4612         }
4613       }
4615       try {
4616         if (isString(module)) {
4617           moduleFn = angularModule(module);
4618           runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4619           runInvokeQueue(moduleFn._invokeQueue);
4620           runInvokeQueue(moduleFn._configBlocks);
4621         } else if (isFunction(module)) {
4622             runBlocks.push(providerInjector.invoke(module));
4623         } else if (isArray(module)) {
4624             runBlocks.push(providerInjector.invoke(module));
4625         } else {
4626           assertArgFn(module, 'module');
4627         }
4628       } catch (e) {
4629         if (isArray(module)) {
4630           module = module[module.length - 1];
4631         }
4632         if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4633           // Safari & FF's stack traces don't contain error.message content
4634           // unlike those of Chrome and IE
4635           // So if stack doesn't contain message, we create a new string that contains both.
4636           // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4637           /* jshint -W022 */
4638           e = e.message + '\n' + e.stack;
4639         }
4640         throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4641                   module, e.stack || e.message || e);
4642       }
4643     });
4644     return runBlocks;
4645   }
4647   ////////////////////////////////////
4648   // internal Injector
4649   ////////////////////////////////////
4651   function createInternalInjector(cache, factory) {
4653     function getService(serviceName, caller) {
4654       if (cache.hasOwnProperty(serviceName)) {
4655         if (cache[serviceName] === INSTANTIATING) {
4656           throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4657                     serviceName + ' <- ' + path.join(' <- '));
4658         }
4659         return cache[serviceName];
4660       } else {
4661         try {
4662           path.unshift(serviceName);
4663           cache[serviceName] = INSTANTIATING;
4664           return cache[serviceName] = factory(serviceName, caller);
4665         } catch (err) {
4666           if (cache[serviceName] === INSTANTIATING) {
4667             delete cache[serviceName];
4668           }
4669           throw err;
4670         } finally {
4671           path.shift();
4672         }
4673       }
4674     }
4677     function injectionArgs(fn, locals, serviceName) {
4678       var args = [],
4679           $inject = createInjector.$$annotate(fn, strictDi, serviceName);
4681       for (var i = 0, length = $inject.length; i < length; i++) {
4682         var key = $inject[i];
4683         if (typeof key !== 'string') {
4684           throw $injectorMinErr('itkn',
4685                   'Incorrect injection token! Expected service name as string, got {0}', key);
4686         }
4687         args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
4688                                                          getService(key, serviceName));
4689       }
4690       return args;
4691     }
4693     function isClass(func) {
4694       // IE 9-11 do not support classes and IE9 leaks with the code below.
4695       if (msie <= 11) {
4696         return false;
4697       }
4698       // Support: Edge 12-13 only
4699       // See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/6156135/
4700       return typeof func === 'function'
4701         && /^(?:class\b|constructor\()/.test(stringifyFn(func));
4702     }
4704     function invoke(fn, self, locals, serviceName) {
4705       if (typeof locals === 'string') {
4706         serviceName = locals;
4707         locals = null;
4708       }
4710       var args = injectionArgs(fn, locals, serviceName);
4711       if (isArray(fn)) {
4712         fn = fn[fn.length - 1];
4713       }
4715       if (!isClass(fn)) {
4716         // http://jsperf.com/angularjs-invoke-apply-vs-switch
4717         // #5388
4718         return fn.apply(self, args);
4719       } else {
4720         args.unshift(null);
4721         return new (Function.prototype.bind.apply(fn, args))();
4722       }
4723     }
4726     function instantiate(Type, locals, serviceName) {
4727       // Check if Type is annotated and use just the given function at n-1 as parameter
4728       // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4729       var ctor = (isArray(Type) ? Type[Type.length - 1] : Type);
4730       var args = injectionArgs(Type, locals, serviceName);
4731       // Empty object at position 0 is ignored for invocation with `new`, but required.
4732       args.unshift(null);
4733       return new (Function.prototype.bind.apply(ctor, args))();
4734     }
4737     return {
4738       invoke: invoke,
4739       instantiate: instantiate,
4740       get: getService,
4741       annotate: createInjector.$$annotate,
4742       has: function(name) {
4743         return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4744       }
4745     };
4746   }
4749 createInjector.$$annotate = annotate;
4752  * @ngdoc provider
4753  * @name $anchorScrollProvider
4755  * @description
4756  * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4757  * {@link ng.$location#hash $location.hash()} changes.
4758  */
4759 function $AnchorScrollProvider() {
4761   var autoScrollingEnabled = true;
4763   /**
4764    * @ngdoc method
4765    * @name $anchorScrollProvider#disableAutoScrolling
4766    *
4767    * @description
4768    * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4769    * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4770    * Use this method to disable automatic scrolling.
4771    *
4772    * If automatic scrolling is disabled, one must explicitly call
4773    * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4774    * current hash.
4775    */
4776   this.disableAutoScrolling = function() {
4777     autoScrollingEnabled = false;
4778   };
4780   /**
4781    * @ngdoc service
4782    * @name $anchorScroll
4783    * @kind function
4784    * @requires $window
4785    * @requires $location
4786    * @requires $rootScope
4787    *
4788    * @description
4789    * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
4790    * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
4791    * in the
4792    * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#an-indicated-part-of-the-document).
4793    *
4794    * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4795    * match any anchor whenever it changes. This can be disabled by calling
4796    * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4797    *
4798    * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4799    * vertical scroll-offset (either fixed or dynamic).
4800    *
4801    * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
4802    *                       {@link ng.$location#hash $location.hash()} will be used.
4803    *
4804    * @property {(number|function|jqLite)} yOffset
4805    * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4806    * positioned elements at the top of the page, such as navbars, headers etc.
4807    *
4808    * `yOffset` can be specified in various ways:
4809    * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4810    * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4811    *   a number representing the offset (in pixels).<br /><br />
4812    * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4813    *   the top of the page to the element's bottom will be used as offset.<br />
4814    *   **Note**: The element will be taken into account only as long as its `position` is set to
4815    *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4816    *   their height and/or positioning according to the viewport's size.
4817    *
4818    * <br />
4819    * <div class="alert alert-warning">
4820    * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4821    * not some child element.
4822    * </div>
4823    *
4824    * @example
4825      <example module="anchorScrollExample">
4826        <file name="index.html">
4827          <div id="scrollArea" ng-controller="ScrollController">
4828            <a ng-click="gotoBottom()">Go to bottom</a>
4829            <a id="bottom"></a> You're at the bottom!
4830          </div>
4831        </file>
4832        <file name="script.js">
4833          angular.module('anchorScrollExample', [])
4834            .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4835              function ($scope, $location, $anchorScroll) {
4836                $scope.gotoBottom = function() {
4837                  // set the location.hash to the id of
4838                  // the element you wish to scroll to.
4839                  $location.hash('bottom');
4841                  // call $anchorScroll()
4842                  $anchorScroll();
4843                };
4844              }]);
4845        </file>
4846        <file name="style.css">
4847          #scrollArea {
4848            height: 280px;
4849            overflow: auto;
4850          }
4852          #bottom {
4853            display: block;
4854            margin-top: 2000px;
4855          }
4856        </file>
4857      </example>
4858    *
4859    * <hr />
4860    * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4861    * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4862    *
4863    * @example
4864      <example module="anchorScrollOffsetExample">
4865        <file name="index.html">
4866          <div class="fixed-header" ng-controller="headerCtrl">
4867            <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4868              Go to anchor {{x}}
4869            </a>
4870          </div>
4871          <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4872            Anchor {{x}} of 5
4873          </div>
4874        </file>
4875        <file name="script.js">
4876          angular.module('anchorScrollOffsetExample', [])
4877            .run(['$anchorScroll', function($anchorScroll) {
4878              $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
4879            }])
4880            .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4881              function ($anchorScroll, $location, $scope) {
4882                $scope.gotoAnchor = function(x) {
4883                  var newHash = 'anchor' + x;
4884                  if ($location.hash() !== newHash) {
4885                    // set the $location.hash to `newHash` and
4886                    // $anchorScroll will automatically scroll to it
4887                    $location.hash('anchor' + x);
4888                  } else {
4889                    // call $anchorScroll() explicitly,
4890                    // since $location.hash hasn't changed
4891                    $anchorScroll();
4892                  }
4893                };
4894              }
4895            ]);
4896        </file>
4897        <file name="style.css">
4898          body {
4899            padding-top: 50px;
4900          }
4902          .anchor {
4903            border: 2px dashed DarkOrchid;
4904            padding: 10px 10px 200px 10px;
4905          }
4907          .fixed-header {
4908            background-color: rgba(0, 0, 0, 0.2);
4909            height: 50px;
4910            position: fixed;
4911            top: 0; left: 0; right: 0;
4912          }
4914          .fixed-header > a {
4915            display: inline-block;
4916            margin: 5px 15px;
4917          }
4918        </file>
4919      </example>
4920    */
4921   this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4922     var document = $window.document;
4924     // Helper function to get first anchor from a NodeList
4925     // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4926     //  and working in all supported browsers.)
4927     function getFirstAnchor(list) {
4928       var result = null;
4929       Array.prototype.some.call(list, function(element) {
4930         if (nodeName_(element) === 'a') {
4931           result = element;
4932           return true;
4933         }
4934       });
4935       return result;
4936     }
4938     function getYOffset() {
4940       var offset = scroll.yOffset;
4942       if (isFunction(offset)) {
4943         offset = offset();
4944       } else if (isElement(offset)) {
4945         var elem = offset[0];
4946         var style = $window.getComputedStyle(elem);
4947         if (style.position !== 'fixed') {
4948           offset = 0;
4949         } else {
4950           offset = elem.getBoundingClientRect().bottom;
4951         }
4952       } else if (!isNumber(offset)) {
4953         offset = 0;
4954       }
4956       return offset;
4957     }
4959     function scrollTo(elem) {
4960       if (elem) {
4961         elem.scrollIntoView();
4963         var offset = getYOffset();
4965         if (offset) {
4966           // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4967           // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4968           // top of the viewport.
4969           //
4970           // IF the number of pixels from the top of `elem` to the end of the page's content is less
4971           // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4972           // way down the page.
4973           //
4974           // This is often the case for elements near the bottom of the page.
4975           //
4976           // In such cases we do not need to scroll the whole `offset` up, just the difference between
4977           // the top of the element and the offset, which is enough to align the top of `elem` at the
4978           // desired position.
4979           var elemTop = elem.getBoundingClientRect().top;
4980           $window.scrollBy(0, elemTop - offset);
4981         }
4982       } else {
4983         $window.scrollTo(0, 0);
4984       }
4985     }
4987     function scroll(hash) {
4988       hash = isString(hash) ? hash : $location.hash();
4989       var elm;
4991       // empty hash, scroll to the top of the page
4992       if (!hash) scrollTo(null);
4994       // element with given id
4995       else if ((elm = document.getElementById(hash))) scrollTo(elm);
4997       // first anchor with given name :-D
4998       else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
5000       // no element and hash == 'top', scroll to the top of the page
5001       else if (hash === 'top') scrollTo(null);
5002     }
5004     // does not scroll when user clicks on anchor link that is currently on
5005     // (no url change, no $location.hash() change), browser native does scroll
5006     if (autoScrollingEnabled) {
5007       $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
5008         function autoScrollWatchAction(newVal, oldVal) {
5009           // skip the initial scroll if $location.hash is empty
5010           if (newVal === oldVal && newVal === '') return;
5012           jqLiteDocumentLoaded(function() {
5013             $rootScope.$evalAsync(scroll);
5014           });
5015         });
5016     }
5018     return scroll;
5019   }];
5022 var $animateMinErr = minErr('$animate');
5023 var ELEMENT_NODE = 1;
5024 var NG_ANIMATE_CLASSNAME = 'ng-animate';
5026 function mergeClasses(a,b) {
5027   if (!a && !b) return '';
5028   if (!a) return b;
5029   if (!b) return a;
5030   if (isArray(a)) a = a.join(' ');
5031   if (isArray(b)) b = b.join(' ');
5032   return a + ' ' + b;
5035 function extractElementNode(element) {
5036   for (var i = 0; i < element.length; i++) {
5037     var elm = element[i];
5038     if (elm.nodeType === ELEMENT_NODE) {
5039       return elm;
5040     }
5041   }
5044 function splitClasses(classes) {
5045   if (isString(classes)) {
5046     classes = classes.split(' ');
5047   }
5049   // Use createMap() to prevent class assumptions involving property names in
5050   // Object.prototype
5051   var obj = createMap();
5052   forEach(classes, function(klass) {
5053     // sometimes the split leaves empty string values
5054     // incase extra spaces were applied to the options
5055     if (klass.length) {
5056       obj[klass] = true;
5057     }
5058   });
5059   return obj;
5062 // if any other type of options value besides an Object value is
5063 // passed into the $animate.method() animation then this helper code
5064 // will be run which will ignore it. While this patch is not the
5065 // greatest solution to this, a lot of existing plugins depend on
5066 // $animate to either call the callback (< 1.2) or return a promise
5067 // that can be changed. This helper function ensures that the options
5068 // are wiped clean incase a callback function is provided.
5069 function prepareAnimateOptions(options) {
5070   return isObject(options)
5071       ? options
5072       : {};
5075 var $$CoreAnimateJsProvider = function() {
5076   this.$get = noop;
5079 // this is prefixed with Core since it conflicts with
5080 // the animateQueueProvider defined in ngAnimate/animateQueue.js
5081 var $$CoreAnimateQueueProvider = function() {
5082   var postDigestQueue = new HashMap();
5083   var postDigestElements = [];
5085   this.$get = ['$$AnimateRunner', '$rootScope',
5086        function($$AnimateRunner,   $rootScope) {
5087     return {
5088       enabled: noop,
5089       on: noop,
5090       off: noop,
5091       pin: noop,
5093       push: function(element, event, options, domOperation) {
5094         domOperation        && domOperation();
5096         options = options || {};
5097         options.from        && element.css(options.from);
5098         options.to          && element.css(options.to);
5100         if (options.addClass || options.removeClass) {
5101           addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
5102         }
5104         var runner = new $$AnimateRunner(); // jshint ignore:line
5106         // since there are no animations to run the runner needs to be
5107         // notified that the animation call is complete.
5108         runner.complete();
5109         return runner;
5110       }
5111     };
5114     function updateData(data, classes, value) {
5115       var changed = false;
5116       if (classes) {
5117         classes = isString(classes) ? classes.split(' ') :
5118                   isArray(classes) ? classes : [];
5119         forEach(classes, function(className) {
5120           if (className) {
5121             changed = true;
5122             data[className] = value;
5123           }
5124         });
5125       }
5126       return changed;
5127     }
5129     function handleCSSClassChanges() {
5130       forEach(postDigestElements, function(element) {
5131         var data = postDigestQueue.get(element);
5132         if (data) {
5133           var existing = splitClasses(element.attr('class'));
5134           var toAdd = '';
5135           var toRemove = '';
5136           forEach(data, function(status, className) {
5137             var hasClass = !!existing[className];
5138             if (status !== hasClass) {
5139               if (status) {
5140                 toAdd += (toAdd.length ? ' ' : '') + className;
5141               } else {
5142                 toRemove += (toRemove.length ? ' ' : '') + className;
5143               }
5144             }
5145           });
5147           forEach(element, function(elm) {
5148             toAdd    && jqLiteAddClass(elm, toAdd);
5149             toRemove && jqLiteRemoveClass(elm, toRemove);
5150           });
5151           postDigestQueue.remove(element);
5152         }
5153       });
5154       postDigestElements.length = 0;
5155     }
5158     function addRemoveClassesPostDigest(element, add, remove) {
5159       var data = postDigestQueue.get(element) || {};
5161       var classesAdded = updateData(data, add, true);
5162       var classesRemoved = updateData(data, remove, false);
5164       if (classesAdded || classesRemoved) {
5166         postDigestQueue.put(element, data);
5167         postDigestElements.push(element);
5169         if (postDigestElements.length === 1) {
5170           $rootScope.$$postDigest(handleCSSClassChanges);
5171         }
5172       }
5173     }
5174   }];
5178  * @ngdoc provider
5179  * @name $animateProvider
5181  * @description
5182  * Default implementation of $animate that doesn't perform any animations, instead just
5183  * synchronously performs DOM updates and resolves the returned runner promise.
5185  * In order to enable animations the `ngAnimate` module has to be loaded.
5187  * To see the functional implementation check out `src/ngAnimate/animate.js`.
5188  */
5189 var $AnimateProvider = ['$provide', function($provide) {
5190   var provider = this;
5192   this.$$registeredAnimations = Object.create(null);
5194    /**
5195    * @ngdoc method
5196    * @name $animateProvider#register
5197    *
5198    * @description
5199    * Registers a new injectable animation factory function. The factory function produces the
5200    * animation object which contains callback functions for each event that is expected to be
5201    * animated.
5202    *
5203    *   * `eventFn`: `function(element, ... , doneFunction, options)`
5204    *   The element to animate, the `doneFunction` and the options fed into the animation. Depending
5205    *   on the type of animation additional arguments will be injected into the animation function. The
5206    *   list below explains the function signatures for the different animation methods:
5207    *
5208    *   - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
5209    *   - addClass: function(element, addedClasses, doneFunction, options)
5210    *   - removeClass: function(element, removedClasses, doneFunction, options)
5211    *   - enter, leave, move: function(element, doneFunction, options)
5212    *   - animate: function(element, fromStyles, toStyles, doneFunction, options)
5213    *
5214    *   Make sure to trigger the `doneFunction` once the animation is fully complete.
5215    *
5216    * ```js
5217    *   return {
5218    *     //enter, leave, move signature
5219    *     eventFn : function(element, done, options) {
5220    *       //code to run the animation
5221    *       //once complete, then run done()
5222    *       return function endFunction(wasCancelled) {
5223    *         //code to cancel the animation
5224    *       }
5225    *     }
5226    *   }
5227    * ```
5228    *
5229    * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
5230    * @param {Function} factory The factory function that will be executed to return the animation
5231    *                           object.
5232    */
5233   this.register = function(name, factory) {
5234     if (name && name.charAt(0) !== '.') {
5235       throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
5236     }
5238     var key = name + '-animation';
5239     provider.$$registeredAnimations[name.substr(1)] = key;
5240     $provide.factory(key, factory);
5241   };
5243   /**
5244    * @ngdoc method
5245    * @name $animateProvider#classNameFilter
5246    *
5247    * @description
5248    * Sets and/or returns the CSS class regular expression that is checked when performing
5249    * an animation. Upon bootstrap the classNameFilter value is not set at all and will
5250    * therefore enable $animate to attempt to perform an animation on any element that is triggered.
5251    * When setting the `classNameFilter` value, animations will only be performed on elements
5252    * that successfully match the filter expression. This in turn can boost performance
5253    * for low-powered devices as well as applications containing a lot of structural operations.
5254    * @param {RegExp=} expression The className expression which will be checked against all animations
5255    * @return {RegExp} The current CSS className expression value. If null then there is no expression value
5256    */
5257   this.classNameFilter = function(expression) {
5258     if (arguments.length === 1) {
5259       this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
5260       if (this.$$classNameFilter) {
5261         var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
5262         if (reservedRegex.test(this.$$classNameFilter.toString())) {
5263           throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
5265         }
5266       }
5267     }
5268     return this.$$classNameFilter;
5269   };
5271   this.$get = ['$$animateQueue', function($$animateQueue) {
5272     function domInsert(element, parentElement, afterElement) {
5273       // if for some reason the previous element was removed
5274       // from the dom sometime before this code runs then let's
5275       // just stick to using the parent element as the anchor
5276       if (afterElement) {
5277         var afterNode = extractElementNode(afterElement);
5278         if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
5279           afterElement = null;
5280         }
5281       }
5282       afterElement ? afterElement.after(element) : parentElement.prepend(element);
5283     }
5285     /**
5286      * @ngdoc service
5287      * @name $animate
5288      * @description The $animate service exposes a series of DOM utility methods that provide support
5289      * for animation hooks. The default behavior is the application of DOM operations, however,
5290      * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5291      * to ensure that animation runs with the triggered DOM operation.
5292      *
5293      * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
5294      * included and only when it is active then the animation hooks that `$animate` triggers will be
5295      * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5296      * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
5297      * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
5298      *
5299      * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
5300      *
5301      * To learn more about enabling animation support, click here to visit the
5302      * {@link ngAnimate ngAnimate module page}.
5303      */
5304     return {
5305       // we don't call it directly since non-existant arguments may
5306       // be interpreted as null within the sub enabled function
5308       /**
5309        *
5310        * @ngdoc method
5311        * @name $animate#on
5312        * @kind function
5313        * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
5314        *    has fired on the given element or among any of its children. Once the listener is fired, the provided callback
5315        *    is fired with the following params:
5316        *
5317        * ```js
5318        * $animate.on('enter', container,
5319        *    function callback(element, phase) {
5320        *      // cool we detected an enter animation within the container
5321        *    }
5322        * );
5323        * ```
5324        *
5325        * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
5326        * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
5327        *     as well as among its children
5328        * @param {Function} callback the callback function that will be fired when the listener is triggered
5329        *
5330        * The arguments present in the callback function are:
5331        * * `element` - The captured DOM element that the animation was fired on.
5332        * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
5333        */
5334       on: $$animateQueue.on,
5336       /**
5337        *
5338        * @ngdoc method
5339        * @name $animate#off
5340        * @kind function
5341        * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
5342        * can be used in three different ways depending on the arguments:
5343        *
5344        * ```js
5345        * // remove all the animation event listeners listening for `enter`
5346        * $animate.off('enter');
5347        *
5348        * // remove listeners for all animation events from the container element
5349        * $animate.off(container);
5350        *
5351        * // remove all the animation event listeners listening for `enter` on the given element and its children
5352        * $animate.off('enter', container);
5353        *
5354        * // remove the event listener function provided by `callback` that is set
5355        * // to listen for `enter` on the given `container` as well as its children
5356        * $animate.off('enter', container, callback);
5357        * ```
5358        *
5359        * @param {string|DOMElement} event|container the animation event (e.g. enter, leave, move,
5360        * addClass, removeClass, etc...), or the container element. If it is the element, all other
5361        * arguments are ignored.
5362        * @param {DOMElement=} container the container element the event listener was placed on
5363        * @param {Function=} callback the callback function that was registered as the listener
5364        */
5365       off: $$animateQueue.off,
5367       /**
5368        * @ngdoc method
5369        * @name $animate#pin
5370        * @kind function
5371        * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
5372        *    outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
5373        *    element despite being outside the realm of the application or within another application. Say for example if the application
5374        *    was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
5375        *    as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
5376        *    that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
5377        *
5378        *    Note that this feature is only active when the `ngAnimate` module is used.
5379        *
5380        * @param {DOMElement} element the external element that will be pinned
5381        * @param {DOMElement} parentElement the host parent element that will be associated with the external element
5382        */
5383       pin: $$animateQueue.pin,
5385       /**
5386        *
5387        * @ngdoc method
5388        * @name $animate#enabled
5389        * @kind function
5390        * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
5391        * function can be called in four ways:
5392        *
5393        * ```js
5394        * // returns true or false
5395        * $animate.enabled();
5396        *
5397        * // changes the enabled state for all animations
5398        * $animate.enabled(false);
5399        * $animate.enabled(true);
5400        *
5401        * // returns true or false if animations are enabled for an element
5402        * $animate.enabled(element);
5403        *
5404        * // changes the enabled state for an element and its children
5405        * $animate.enabled(element, true);
5406        * $animate.enabled(element, false);
5407        * ```
5408        *
5409        * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
5410        * @param {boolean=} enabled whether or not the animations will be enabled for the element
5411        *
5412        * @return {boolean} whether or not animations are enabled
5413        */
5414       enabled: $$animateQueue.enabled,
5416       /**
5417        * @ngdoc method
5418        * @name $animate#cancel
5419        * @kind function
5420        * @description Cancels the provided animation.
5421        *
5422        * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
5423        */
5424       cancel: function(runner) {
5425         runner.end && runner.end();
5426       },
5428       /**
5429        *
5430        * @ngdoc method
5431        * @name $animate#enter
5432        * @kind function
5433        * @description Inserts the element into the DOM either after the `after` element (if provided) or
5434        *   as the first child within the `parent` element and then triggers an animation.
5435        *   A promise is returned that will be resolved during the next digest once the animation
5436        *   has completed.
5437        *
5438        * @param {DOMElement} element the element which will be inserted into the DOM
5439        * @param {DOMElement} parent the parent element which will append the element as
5440        *   a child (so long as the after element is not present)
5441        * @param {DOMElement=} after the sibling element after which the element will be appended
5442        * @param {object=} options an optional collection of options/styles that will be applied to the element.
5443        *   The object can have the following properties:
5444        *
5445        *   - **addClass** - `{string}` - space-separated CSS classes to add to element
5446        *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5447        *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5448        *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5449        *
5450        * @return {Promise} the animation callback promise
5451        */
5452       enter: function(element, parent, after, options) {
5453         parent = parent && jqLite(parent);
5454         after = after && jqLite(after);
5455         parent = parent || after.parent();
5456         domInsert(element, parent, after);
5457         return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
5458       },
5460       /**
5461        *
5462        * @ngdoc method
5463        * @name $animate#move
5464        * @kind function
5465        * @description Inserts (moves) the element into its new position in the DOM either after
5466        *   the `after` element (if provided) or as the first child within the `parent` element
5467        *   and then triggers an animation. A promise is returned that will be resolved
5468        *   during the next digest once the animation has completed.
5469        *
5470        * @param {DOMElement} element the element which will be moved into the new DOM position
5471        * @param {DOMElement} parent the parent element which will append the element as
5472        *   a child (so long as the after element is not present)
5473        * @param {DOMElement=} after the sibling element after which the element will be appended
5474        * @param {object=} options an optional collection of options/styles that will be applied to the element.
5475        *   The object can have the following properties:
5476        *
5477        *   - **addClass** - `{string}` - space-separated CSS classes to add to element
5478        *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5479        *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5480        *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5481        *
5482        * @return {Promise} the animation callback promise
5483        */
5484       move: function(element, parent, after, options) {
5485         parent = parent && jqLite(parent);
5486         after = after && jqLite(after);
5487         parent = parent || after.parent();
5488         domInsert(element, parent, after);
5489         return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
5490       },
5492       /**
5493        * @ngdoc method
5494        * @name $animate#leave
5495        * @kind function
5496        * @description Triggers an animation and then removes the element from the DOM.
5497        * When the function is called a promise is returned that will be resolved during the next
5498        * digest once the animation has completed.
5499        *
5500        * @param {DOMElement} element the element which will be removed from the DOM
5501        * @param {object=} options an optional collection of options/styles that will be applied to the element.
5502        *   The object can have the following properties:
5503        *
5504        *   - **addClass** - `{string}` - space-separated CSS classes to add to element
5505        *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5506        *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5507        *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5508        *
5509        * @return {Promise} the animation callback promise
5510        */
5511       leave: function(element, options) {
5512         return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
5513           element.remove();
5514         });
5515       },
5517       /**
5518        * @ngdoc method
5519        * @name $animate#addClass
5520        * @kind function
5521        *
5522        * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
5523        *   execution, the addClass operation will only be handled after the next digest and it will not trigger an
5524        *   animation if element already contains the CSS class or if the class is removed at a later step.
5525        *   Note that class-based animations are treated differently compared to structural animations
5526        *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5527        *   depending if CSS or JavaScript animations are used.
5528        *
5529        * @param {DOMElement} element the element which the CSS classes will be applied to
5530        * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
5531        * @param {object=} options an optional collection of options/styles that will be applied to the element.
5532        *   The object can have the following properties:
5533        *
5534        *   - **addClass** - `{string}` - space-separated CSS classes to add to element
5535        *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5536        *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5537        *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5538        *
5539        * @return {Promise} the animation callback promise
5540        */
5541       addClass: function(element, className, options) {
5542         options = prepareAnimateOptions(options);
5543         options.addClass = mergeClasses(options.addclass, className);
5544         return $$animateQueue.push(element, 'addClass', options);
5545       },
5547       /**
5548        * @ngdoc method
5549        * @name $animate#removeClass
5550        * @kind function
5551        *
5552        * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
5553        *   execution, the removeClass operation will only be handled after the next digest and it will not trigger an
5554        *   animation if element does not contain the CSS class or if the class is added at a later step.
5555        *   Note that class-based animations are treated differently compared to structural animations
5556        *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5557        *   depending if CSS or JavaScript animations are used.
5558        *
5559        * @param {DOMElement} element the element which the CSS classes will be applied to
5560        * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
5561        * @param {object=} options an optional collection of options/styles that will be applied to the element.
5562        *   The object can have the following properties:
5563        *
5564        *   - **addClass** - `{string}` - space-separated CSS classes to add to element
5565        *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5566        *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5567        *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5568        *
5569        * @return {Promise} the animation callback promise
5570        */
5571       removeClass: function(element, className, options) {
5572         options = prepareAnimateOptions(options);
5573         options.removeClass = mergeClasses(options.removeClass, className);
5574         return $$animateQueue.push(element, 'removeClass', options);
5575       },
5577       /**
5578        * @ngdoc method
5579        * @name $animate#setClass
5580        * @kind function
5581        *
5582        * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
5583        *    triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
5584        *    `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
5585        *    passed. Note that class-based animations are treated differently compared to structural animations
5586        *    (like enter, move and leave) since the CSS classes may be added/removed at different points
5587        *    depending if CSS or JavaScript animations are used.
5588        *
5589        * @param {DOMElement} element the element which the CSS classes will be applied to
5590        * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
5591        * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
5592        * @param {object=} options an optional collection of options/styles that will be applied to the element.
5593        *   The object can have the following properties:
5594        *
5595        *   - **addClass** - `{string}` - space-separated CSS classes to add to element
5596        *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5597        *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5598        *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5599        *
5600        * @return {Promise} the animation callback promise
5601        */
5602       setClass: function(element, add, remove, options) {
5603         options = prepareAnimateOptions(options);
5604         options.addClass = mergeClasses(options.addClass, add);
5605         options.removeClass = mergeClasses(options.removeClass, remove);
5606         return $$animateQueue.push(element, 'setClass', options);
5607       },
5609       /**
5610        * @ngdoc method
5611        * @name $animate#animate
5612        * @kind function
5613        *
5614        * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
5615        * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take
5616        * on the provided styles. For example, if a transition animation is set for the given classNamem, then the provided `from` and
5617        * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding
5618        * style in `to`, the style in `from` is applied immediately, and no animation is run.
5619        * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate`
5620        * method (or as part of the `options` parameter):
5621        *
5622        * ```js
5623        * ngModule.animation('.my-inline-animation', function() {
5624        *   return {
5625        *     animate : function(element, from, to, done, options) {
5626        *       //animation
5627        *       done();
5628        *     }
5629        *   }
5630        * });
5631        * ```
5632        *
5633        * @param {DOMElement} element the element which the CSS styles will be applied to
5634        * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
5635        * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
5636        * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
5637        *    this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
5638        *    (Note that if no animation is detected then this value will not be applied to the element.)
5639        * @param {object=} options an optional collection of options/styles that will be applied to the element.
5640        *   The object can have the following properties:
5641        *
5642        *   - **addClass** - `{string}` - space-separated CSS classes to add to element
5643        *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5644        *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5645        *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5646        *
5647        * @return {Promise} the animation callback promise
5648        */
5649       animate: function(element, from, to, className, options) {
5650         options = prepareAnimateOptions(options);
5651         options.from = options.from ? extend(options.from, from) : from;
5652         options.to   = options.to   ? extend(options.to, to)     : to;
5654         className = className || 'ng-inline-animate';
5655         options.tempClasses = mergeClasses(options.tempClasses, className);
5656         return $$animateQueue.push(element, 'animate', options);
5657       }
5658     };
5659   }];
5662 var $$AnimateAsyncRunFactoryProvider = function() {
5663   this.$get = ['$$rAF', function($$rAF) {
5664     var waitQueue = [];
5666     function waitForTick(fn) {
5667       waitQueue.push(fn);
5668       if (waitQueue.length > 1) return;
5669       $$rAF(function() {
5670         for (var i = 0; i < waitQueue.length; i++) {
5671           waitQueue[i]();
5672         }
5673         waitQueue = [];
5674       });
5675     }
5677     return function() {
5678       var passed = false;
5679       waitForTick(function() {
5680         passed = true;
5681       });
5682       return function(callback) {
5683         passed ? callback() : waitForTick(callback);
5684       };
5685     };
5686   }];
5689 var $$AnimateRunnerFactoryProvider = function() {
5690   this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$document', '$timeout',
5691        function($q,   $sniffer,   $$animateAsyncRun,   $document,   $timeout) {
5693     var INITIAL_STATE = 0;
5694     var DONE_PENDING_STATE = 1;
5695     var DONE_COMPLETE_STATE = 2;
5697     AnimateRunner.chain = function(chain, callback) {
5698       var index = 0;
5700       next();
5701       function next() {
5702         if (index === chain.length) {
5703           callback(true);
5704           return;
5705         }
5707         chain[index](function(response) {
5708           if (response === false) {
5709             callback(false);
5710             return;
5711           }
5712           index++;
5713           next();
5714         });
5715       }
5716     };
5718     AnimateRunner.all = function(runners, callback) {
5719       var count = 0;
5720       var status = true;
5721       forEach(runners, function(runner) {
5722         runner.done(onProgress);
5723       });
5725       function onProgress(response) {
5726         status = status && response;
5727         if (++count === runners.length) {
5728           callback(status);
5729         }
5730       }
5731     };
5733     function AnimateRunner(host) {
5734       this.setHost(host);
5736       var rafTick = $$animateAsyncRun();
5737       var timeoutTick = function(fn) {
5738         $timeout(fn, 0, false);
5739       };
5741       this._doneCallbacks = [];
5742       this._tick = function(fn) {
5743         var doc = $document[0];
5745         // the document may not be ready or attached
5746         // to the module for some internal tests
5747         if (doc && doc.hidden) {
5748           timeoutTick(fn);
5749         } else {
5750           rafTick(fn);
5751         }
5752       };
5753       this._state = 0;
5754     }
5756     AnimateRunner.prototype = {
5757       setHost: function(host) {
5758         this.host = host || {};
5759       },
5761       done: function(fn) {
5762         if (this._state === DONE_COMPLETE_STATE) {
5763           fn();
5764         } else {
5765           this._doneCallbacks.push(fn);
5766         }
5767       },
5769       progress: noop,
5771       getPromise: function() {
5772         if (!this.promise) {
5773           var self = this;
5774           this.promise = $q(function(resolve, reject) {
5775             self.done(function(status) {
5776               status === false ? reject() : resolve();
5777             });
5778           });
5779         }
5780         return this.promise;
5781       },
5783       then: function(resolveHandler, rejectHandler) {
5784         return this.getPromise().then(resolveHandler, rejectHandler);
5785       },
5787       'catch': function(handler) {
5788         return this.getPromise()['catch'](handler);
5789       },
5791       'finally': function(handler) {
5792         return this.getPromise()['finally'](handler);
5793       },
5795       pause: function() {
5796         if (this.host.pause) {
5797           this.host.pause();
5798         }
5799       },
5801       resume: function() {
5802         if (this.host.resume) {
5803           this.host.resume();
5804         }
5805       },
5807       end: function() {
5808         if (this.host.end) {
5809           this.host.end();
5810         }
5811         this._resolve(true);
5812       },
5814       cancel: function() {
5815         if (this.host.cancel) {
5816           this.host.cancel();
5817         }
5818         this._resolve(false);
5819       },
5821       complete: function(response) {
5822         var self = this;
5823         if (self._state === INITIAL_STATE) {
5824           self._state = DONE_PENDING_STATE;
5825           self._tick(function() {
5826             self._resolve(response);
5827           });
5828         }
5829       },
5831       _resolve: function(response) {
5832         if (this._state !== DONE_COMPLETE_STATE) {
5833           forEach(this._doneCallbacks, function(fn) {
5834             fn(response);
5835           });
5836           this._doneCallbacks.length = 0;
5837           this._state = DONE_COMPLETE_STATE;
5838         }
5839       }
5840     };
5842     return AnimateRunner;
5843   }];
5847  * @ngdoc service
5848  * @name $animateCss
5849  * @kind object
5851  * @description
5852  * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
5853  * then the `$animateCss` service will actually perform animations.
5855  * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
5856  */
5857 var $CoreAnimateCssProvider = function() {
5858   this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
5860     return function(element, initialOptions) {
5861       // all of the animation functions should create
5862       // a copy of the options data, however, if a
5863       // parent service has already created a copy then
5864       // we should stick to using that
5865       var options = initialOptions || {};
5866       if (!options.$$prepared) {
5867         options = copy(options);
5868       }
5870       // there is no point in applying the styles since
5871       // there is no animation that goes on at all in
5872       // this version of $animateCss.
5873       if (options.cleanupStyles) {
5874         options.from = options.to = null;
5875       }
5877       if (options.from) {
5878         element.css(options.from);
5879         options.from = null;
5880       }
5882       /* jshint newcap: false */
5883       var closed, runner = new $$AnimateRunner();
5884       return {
5885         start: run,
5886         end: run
5887       };
5889       function run() {
5890         $$rAF(function() {
5891           applyAnimationContents();
5892           if (!closed) {
5893             runner.complete();
5894           }
5895           closed = true;
5896         });
5897         return runner;
5898       }
5900       function applyAnimationContents() {
5901         if (options.addClass) {
5902           element.addClass(options.addClass);
5903           options.addClass = null;
5904         }
5905         if (options.removeClass) {
5906           element.removeClass(options.removeClass);
5907           options.removeClass = null;
5908         }
5909         if (options.to) {
5910           element.css(options.to);
5911           options.to = null;
5912         }
5913       }
5914     };
5915   }];
5918 /* global stripHash: true */
5921  * ! This is a private undocumented service !
5923  * @name $browser
5924  * @requires $log
5925  * @description
5926  * This object has two goals:
5928  * - hide all the global state in the browser caused by the window object
5929  * - abstract away all the browser specific features and inconsistencies
5931  * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
5932  * service, which can be used for convenient testing of the application without the interaction with
5933  * the real browser apis.
5934  */
5936  * @param {object} window The global window object.
5937  * @param {object} document jQuery wrapped document.
5938  * @param {object} $log window.console or an object with the same interface.
5939  * @param {object} $sniffer $sniffer service
5940  */
5941 function Browser(window, document, $log, $sniffer) {
5942   var self = this,
5943       location = window.location,
5944       history = window.history,
5945       setTimeout = window.setTimeout,
5946       clearTimeout = window.clearTimeout,
5947       pendingDeferIds = {};
5949   self.isMock = false;
5951   var outstandingRequestCount = 0;
5952   var outstandingRequestCallbacks = [];
5954   // TODO(vojta): remove this temporary api
5955   self.$$completeOutstandingRequest = completeOutstandingRequest;
5956   self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
5958   /**
5959    * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
5960    * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
5961    */
5962   function completeOutstandingRequest(fn) {
5963     try {
5964       fn.apply(null, sliceArgs(arguments, 1));
5965     } finally {
5966       outstandingRequestCount--;
5967       if (outstandingRequestCount === 0) {
5968         while (outstandingRequestCallbacks.length) {
5969           try {
5970             outstandingRequestCallbacks.pop()();
5971           } catch (e) {
5972             $log.error(e);
5973           }
5974         }
5975       }
5976     }
5977   }
5979   function getHash(url) {
5980     var index = url.indexOf('#');
5981     return index === -1 ? '' : url.substr(index);
5982   }
5984   /**
5985    * @private
5986    * Note: this method is used only by scenario runner
5987    * TODO(vojta): prefix this method with $$ ?
5988    * @param {function()} callback Function that will be called when no outstanding request
5989    */
5990   self.notifyWhenNoOutstandingRequests = function(callback) {
5991     if (outstandingRequestCount === 0) {
5992       callback();
5993     } else {
5994       outstandingRequestCallbacks.push(callback);
5995     }
5996   };
5998   //////////////////////////////////////////////////////////////
5999   // URL API
6000   //////////////////////////////////////////////////////////////
6002   var cachedState, lastHistoryState,
6003       lastBrowserUrl = location.href,
6004       baseElement = document.find('base'),
6005       pendingLocation = null,
6006       getCurrentState = !$sniffer.history ? noop : function getCurrentState() {
6007         try {
6008           return history.state;
6009         } catch (e) {
6010           // MSIE can reportedly throw when there is no state (UNCONFIRMED).
6011         }
6012       };
6014   cacheState();
6015   lastHistoryState = cachedState;
6017   /**
6018    * @name $browser#url
6019    *
6020    * @description
6021    * GETTER:
6022    * Without any argument, this method just returns current value of location.href.
6023    *
6024    * SETTER:
6025    * With at least one argument, this method sets url to new value.
6026    * If html5 history api supported, pushState/replaceState is used, otherwise
6027    * location.href/location.replace is used.
6028    * Returns its own instance to allow chaining
6029    *
6030    * NOTE: this api is intended for use only by the $location service. Please use the
6031    * {@link ng.$location $location service} to change url.
6032    *
6033    * @param {string} url New url (when used as setter)
6034    * @param {boolean=} replace Should new url replace current history record?
6035    * @param {object=} state object to use with pushState/replaceState
6036    */
6037   self.url = function(url, replace, state) {
6038     // In modern browsers `history.state` is `null` by default; treating it separately
6039     // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
6040     // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
6041     if (isUndefined(state)) {
6042       state = null;
6043     }
6045     // Android Browser BFCache causes location, history reference to become stale.
6046     if (location !== window.location) location = window.location;
6047     if (history !== window.history) history = window.history;
6049     // setter
6050     if (url) {
6051       var sameState = lastHistoryState === state;
6053       // Don't change anything if previous and current URLs and states match. This also prevents
6054       // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
6055       // See https://github.com/angular/angular.js/commit/ffb2701
6056       if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
6057         return self;
6058       }
6059       var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
6060       lastBrowserUrl = url;
6061       lastHistoryState = state;
6062       // Don't use history API if only the hash changed
6063       // due to a bug in IE10/IE11 which leads
6064       // to not firing a `hashchange` nor `popstate` event
6065       // in some cases (see #9143).
6066       if ($sniffer.history && (!sameBase || !sameState)) {
6067         history[replace ? 'replaceState' : 'pushState'](state, '', url);
6068         cacheState();
6069         // Do the assignment again so that those two variables are referentially identical.
6070         lastHistoryState = cachedState;
6071       } else {
6072         if (!sameBase) {
6073           pendingLocation = url;
6074         }
6075         if (replace) {
6076           location.replace(url);
6077         } else if (!sameBase) {
6078           location.href = url;
6079         } else {
6080           location.hash = getHash(url);
6081         }
6082         if (location.href !== url) {
6083           pendingLocation = url;
6084         }
6085       }
6086       if (pendingLocation) {
6087         pendingLocation = url;
6088       }
6089       return self;
6090     // getter
6091     } else {
6092       // - pendingLocation is needed as browsers don't allow to read out
6093       //   the new location.href if a reload happened or if there is a bug like in iOS 9 (see
6094       //   https://openradar.appspot.com/22186109).
6095       // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
6096       return pendingLocation || location.href.replace(/%27/g,"'");
6097     }
6098   };
6100   /**
6101    * @name $browser#state
6102    *
6103    * @description
6104    * This method is a getter.
6105    *
6106    * Return history.state or null if history.state is undefined.
6107    *
6108    * @returns {object} state
6109    */
6110   self.state = function() {
6111     return cachedState;
6112   };
6114   var urlChangeListeners = [],
6115       urlChangeInit = false;
6117   function cacheStateAndFireUrlChange() {
6118     pendingLocation = null;
6119     cacheState();
6120     fireUrlChange();
6121   }
6123   // This variable should be used *only* inside the cacheState function.
6124   var lastCachedState = null;
6125   function cacheState() {
6126     // This should be the only place in $browser where `history.state` is read.
6127     cachedState = getCurrentState();
6128     cachedState = isUndefined(cachedState) ? null : cachedState;
6130     // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
6131     if (equals(cachedState, lastCachedState)) {
6132       cachedState = lastCachedState;
6133     }
6134     lastCachedState = cachedState;
6135   }
6137   function fireUrlChange() {
6138     if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
6139       return;
6140     }
6142     lastBrowserUrl = self.url();
6143     lastHistoryState = cachedState;
6144     forEach(urlChangeListeners, function(listener) {
6145       listener(self.url(), cachedState);
6146     });
6147   }
6149   /**
6150    * @name $browser#onUrlChange
6151    *
6152    * @description
6153    * Register callback function that will be called, when url changes.
6154    *
6155    * It's only called when the url is changed from outside of angular:
6156    * - user types different url into address bar
6157    * - user clicks on history (forward/back) button
6158    * - user clicks on a link
6159    *
6160    * It's not called when url is changed by $browser.url() method
6161    *
6162    * The listener gets called with new url as parameter.
6163    *
6164    * NOTE: this api is intended for use only by the $location service. Please use the
6165    * {@link ng.$location $location service} to monitor url changes in angular apps.
6166    *
6167    * @param {function(string)} listener Listener function to be called when url changes.
6168    * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
6169    */
6170   self.onUrlChange = function(callback) {
6171     // TODO(vojta): refactor to use node's syntax for events
6172     if (!urlChangeInit) {
6173       // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
6174       // don't fire popstate when user change the address bar and don't fire hashchange when url
6175       // changed by push/replaceState
6177       // html5 history api - popstate event
6178       if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
6179       // hashchange event
6180       jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
6182       urlChangeInit = true;
6183     }
6185     urlChangeListeners.push(callback);
6186     return callback;
6187   };
6189   /**
6190    * @private
6191    * Remove popstate and hashchange handler from window.
6192    *
6193    * NOTE: this api is intended for use only by $rootScope.
6194    */
6195   self.$$applicationDestroyed = function() {
6196     jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
6197   };
6199   /**
6200    * Checks whether the url has changed outside of Angular.
6201    * Needs to be exported to be able to check for changes that have been done in sync,
6202    * as hashchange/popstate events fire in async.
6203    */
6204   self.$$checkUrlChange = fireUrlChange;
6206   //////////////////////////////////////////////////////////////
6207   // Misc API
6208   //////////////////////////////////////////////////////////////
6210   /**
6211    * @name $browser#baseHref
6212    *
6213    * @description
6214    * Returns current <base href>
6215    * (always relative - without domain)
6216    *
6217    * @returns {string} The current base href
6218    */
6219   self.baseHref = function() {
6220     var href = baseElement.attr('href');
6221     return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
6222   };
6224   /**
6225    * @name $browser#defer
6226    * @param {function()} fn A function, who's execution should be deferred.
6227    * @param {number=} [delay=0] of milliseconds to defer the function execution.
6228    * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
6229    *
6230    * @description
6231    * Executes a fn asynchronously via `setTimeout(fn, delay)`.
6232    *
6233    * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
6234    * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
6235    * via `$browser.defer.flush()`.
6236    *
6237    */
6238   self.defer = function(fn, delay) {
6239     var timeoutId;
6240     outstandingRequestCount++;
6241     timeoutId = setTimeout(function() {
6242       delete pendingDeferIds[timeoutId];
6243       completeOutstandingRequest(fn);
6244     }, delay || 0);
6245     pendingDeferIds[timeoutId] = true;
6246     return timeoutId;
6247   };
6250   /**
6251    * @name $browser#defer.cancel
6252    *
6253    * @description
6254    * Cancels a deferred task identified with `deferId`.
6255    *
6256    * @param {*} deferId Token returned by the `$browser.defer` function.
6257    * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
6258    *                    canceled.
6259    */
6260   self.defer.cancel = function(deferId) {
6261     if (pendingDeferIds[deferId]) {
6262       delete pendingDeferIds[deferId];
6263       clearTimeout(deferId);
6264       completeOutstandingRequest(noop);
6265       return true;
6266     }
6267     return false;
6268   };
6272 function $BrowserProvider() {
6273   this.$get = ['$window', '$log', '$sniffer', '$document',
6274       function($window, $log, $sniffer, $document) {
6275         return new Browser($window, $document, $log, $sniffer);
6276       }];
6280  * @ngdoc service
6281  * @name $cacheFactory
6283  * @description
6284  * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
6285  * them.
6287  * ```js
6289  *  var cache = $cacheFactory('cacheId');
6290  *  expect($cacheFactory.get('cacheId')).toBe(cache);
6291  *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
6293  *  cache.put("key", "value");
6294  *  cache.put("another key", "another value");
6296  *  // We've specified no options on creation
6297  *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
6299  * ```
6302  * @param {string} cacheId Name or id of the newly created cache.
6303  * @param {object=} options Options object that specifies the cache behavior. Properties:
6305  *   - `{number=}` `capacity` â€” turns the cache into LRU cache.
6307  * @returns {object} Newly created cache object with the following set of methods:
6309  * - `{object}` `info()` â€” Returns id, size, and options of cache.
6310  * - `{{*}}` `put({string} key, {*} value)` â€” Puts a new key-value pair into the cache and returns
6311  *   it.
6312  * - `{{*}}` `get({string} key)` â€” Returns cached value for `key` or undefined for cache miss.
6313  * - `{void}` `remove({string} key)` â€” Removes a key-value pair from the cache.
6314  * - `{void}` `removeAll()` â€” Removes all cached values.
6315  * - `{void}` `destroy()` â€” Removes references to this cache from $cacheFactory.
6317  * @example
6318    <example module="cacheExampleApp">
6319      <file name="index.html">
6320        <div ng-controller="CacheController">
6321          <input ng-model="newCacheKey" placeholder="Key">
6322          <input ng-model="newCacheValue" placeholder="Value">
6323          <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
6325          <p ng-if="keys.length">Cached Values</p>
6326          <div ng-repeat="key in keys">
6327            <span ng-bind="key"></span>
6328            <span>: </span>
6329            <b ng-bind="cache.get(key)"></b>
6330          </div>
6332          <p>Cache Info</p>
6333          <div ng-repeat="(key, value) in cache.info()">
6334            <span ng-bind="key"></span>
6335            <span>: </span>
6336            <b ng-bind="value"></b>
6337          </div>
6338        </div>
6339      </file>
6340      <file name="script.js">
6341        angular.module('cacheExampleApp', []).
6342          controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
6343            $scope.keys = [];
6344            $scope.cache = $cacheFactory('cacheId');
6345            $scope.put = function(key, value) {
6346              if (angular.isUndefined($scope.cache.get(key))) {
6347                $scope.keys.push(key);
6348              }
6349              $scope.cache.put(key, angular.isUndefined(value) ? null : value);
6350            };
6351          }]);
6352      </file>
6353      <file name="style.css">
6354        p {
6355          margin: 10px 0 3px;
6356        }
6357      </file>
6358    </example>
6359  */
6360 function $CacheFactoryProvider() {
6362   this.$get = function() {
6363     var caches = {};
6365     function cacheFactory(cacheId, options) {
6366       if (cacheId in caches) {
6367         throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
6368       }
6370       var size = 0,
6371           stats = extend({}, options, {id: cacheId}),
6372           data = createMap(),
6373           capacity = (options && options.capacity) || Number.MAX_VALUE,
6374           lruHash = createMap(),
6375           freshEnd = null,
6376           staleEnd = null;
6378       /**
6379        * @ngdoc type
6380        * @name $cacheFactory.Cache
6381        *
6382        * @description
6383        * A cache object used to store and retrieve data, primarily used by
6384        * {@link $http $http} and the {@link ng.directive:script script} directive to cache
6385        * templates and other data.
6386        *
6387        * ```js
6388        *  angular.module('superCache')
6389        *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {
6390        *      return $cacheFactory('super-cache');
6391        *    }]);
6392        * ```
6393        *
6394        * Example test:
6395        *
6396        * ```js
6397        *  it('should behave like a cache', inject(function(superCache) {
6398        *    superCache.put('key', 'value');
6399        *    superCache.put('another key', 'another value');
6400        *
6401        *    expect(superCache.info()).toEqual({
6402        *      id: 'super-cache',
6403        *      size: 2
6404        *    });
6405        *
6406        *    superCache.remove('another key');
6407        *    expect(superCache.get('another key')).toBeUndefined();
6408        *
6409        *    superCache.removeAll();
6410        *    expect(superCache.info()).toEqual({
6411        *      id: 'super-cache',
6412        *      size: 0
6413        *    });
6414        *  }));
6415        * ```
6416        */
6417       return caches[cacheId] = {
6419         /**
6420          * @ngdoc method
6421          * @name $cacheFactory.Cache#put
6422          * @kind function
6423          *
6424          * @description
6425          * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
6426          * retrieved later, and incrementing the size of the cache if the key was not already
6427          * present in the cache. If behaving like an LRU cache, it will also remove stale
6428          * entries from the set.
6429          *
6430          * It will not insert undefined values into the cache.
6431          *
6432          * @param {string} key the key under which the cached data is stored.
6433          * @param {*} value the value to store alongside the key. If it is undefined, the key
6434          *    will not be stored.
6435          * @returns {*} the value stored.
6436          */
6437         put: function(key, value) {
6438           if (isUndefined(value)) return;
6439           if (capacity < Number.MAX_VALUE) {
6440             var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
6442             refresh(lruEntry);
6443           }
6445           if (!(key in data)) size++;
6446           data[key] = value;
6448           if (size > capacity) {
6449             this.remove(staleEnd.key);
6450           }
6452           return value;
6453         },
6455         /**
6456          * @ngdoc method
6457          * @name $cacheFactory.Cache#get
6458          * @kind function
6459          *
6460          * @description
6461          * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
6462          *
6463          * @param {string} key the key of the data to be retrieved
6464          * @returns {*} the value stored.
6465          */
6466         get: function(key) {
6467           if (capacity < Number.MAX_VALUE) {
6468             var lruEntry = lruHash[key];
6470             if (!lruEntry) return;
6472             refresh(lruEntry);
6473           }
6475           return data[key];
6476         },
6479         /**
6480          * @ngdoc method
6481          * @name $cacheFactory.Cache#remove
6482          * @kind function
6483          *
6484          * @description
6485          * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
6486          *
6487          * @param {string} key the key of the entry to be removed
6488          */
6489         remove: function(key) {
6490           if (capacity < Number.MAX_VALUE) {
6491             var lruEntry = lruHash[key];
6493             if (!lruEntry) return;
6495             if (lruEntry == freshEnd) freshEnd = lruEntry.p;
6496             if (lruEntry == staleEnd) staleEnd = lruEntry.n;
6497             link(lruEntry.n,lruEntry.p);
6499             delete lruHash[key];
6500           }
6502           if (!(key in data)) return;
6504           delete data[key];
6505           size--;
6506         },
6509         /**
6510          * @ngdoc method
6511          * @name $cacheFactory.Cache#removeAll
6512          * @kind function
6513          *
6514          * @description
6515          * Clears the cache object of any entries.
6516          */
6517         removeAll: function() {
6518           data = createMap();
6519           size = 0;
6520           lruHash = createMap();
6521           freshEnd = staleEnd = null;
6522         },
6525         /**
6526          * @ngdoc method
6527          * @name $cacheFactory.Cache#destroy
6528          * @kind function
6529          *
6530          * @description
6531          * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
6532          * removing it from the {@link $cacheFactory $cacheFactory} set.
6533          */
6534         destroy: function() {
6535           data = null;
6536           stats = null;
6537           lruHash = null;
6538           delete caches[cacheId];
6539         },
6542         /**
6543          * @ngdoc method
6544          * @name $cacheFactory.Cache#info
6545          * @kind function
6546          *
6547          * @description
6548          * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
6549          *
6550          * @returns {object} an object with the following properties:
6551          *   <ul>
6552          *     <li>**id**: the id of the cache instance</li>
6553          *     <li>**size**: the number of entries kept in the cache instance</li>
6554          *     <li>**...**: any additional properties from the options object when creating the
6555          *       cache.</li>
6556          *   </ul>
6557          */
6558         info: function() {
6559           return extend({}, stats, {size: size});
6560         }
6561       };
6564       /**
6565        * makes the `entry` the freshEnd of the LRU linked list
6566        */
6567       function refresh(entry) {
6568         if (entry != freshEnd) {
6569           if (!staleEnd) {
6570             staleEnd = entry;
6571           } else if (staleEnd == entry) {
6572             staleEnd = entry.n;
6573           }
6575           link(entry.n, entry.p);
6576           link(entry, freshEnd);
6577           freshEnd = entry;
6578           freshEnd.n = null;
6579         }
6580       }
6583       /**
6584        * bidirectionally links two entries of the LRU linked list
6585        */
6586       function link(nextEntry, prevEntry) {
6587         if (nextEntry != prevEntry) {
6588           if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
6589           if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
6590         }
6591       }
6592     }
6595   /**
6596    * @ngdoc method
6597    * @name $cacheFactory#info
6598    *
6599    * @description
6600    * Get information about all the caches that have been created
6601    *
6602    * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
6603    */
6604     cacheFactory.info = function() {
6605       var info = {};
6606       forEach(caches, function(cache, cacheId) {
6607         info[cacheId] = cache.info();
6608       });
6609       return info;
6610     };
6613   /**
6614    * @ngdoc method
6615    * @name $cacheFactory#get
6616    *
6617    * @description
6618    * Get access to a cache object by the `cacheId` used when it was created.
6619    *
6620    * @param {string} cacheId Name or id of a cache to access.
6621    * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
6622    */
6623     cacheFactory.get = function(cacheId) {
6624       return caches[cacheId];
6625     };
6628     return cacheFactory;
6629   };
6633  * @ngdoc service
6634  * @name $templateCache
6636  * @description
6637  * The first time a template is used, it is loaded in the template cache for quick retrieval. You
6638  * can load templates directly into the cache in a `script` tag, or by consuming the
6639  * `$templateCache` service directly.
6641  * Adding via the `script` tag:
6643  * ```html
6644  *   <script type="text/ng-template" id="templateId.html">
6645  *     <p>This is the content of the template</p>
6646  *   </script>
6647  * ```
6649  * **Note:** the `script` tag containing the template does not need to be included in the `head` of
6650  * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
6651  * element with ng-app attribute), otherwise the template will be ignored.
6653  * Adding via the `$templateCache` service:
6655  * ```js
6656  * var myApp = angular.module('myApp', []);
6657  * myApp.run(function($templateCache) {
6658  *   $templateCache.put('templateId.html', 'This is the content of the template');
6659  * });
6660  * ```
6662  * To retrieve the template later, simply use it in your HTML:
6663  * ```html
6664  * <div ng-include=" 'templateId.html' "></div>
6665  * ```
6667  * or get it via Javascript:
6668  * ```js
6669  * $templateCache.get('templateId.html')
6670  * ```
6672  * See {@link ng.$cacheFactory $cacheFactory}.
6674  */
6675 function $TemplateCacheProvider() {
6676   this.$get = ['$cacheFactory', function($cacheFactory) {
6677     return $cacheFactory('templates');
6678   }];
6681 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6682  *     Any commits to this file should be reviewed with security in mind.  *
6683  *   Changes to this file can potentially create security vulnerabilities. *
6684  *          An approval from 2 Core members with history of modifying      *
6685  *                         this file is required.                          *
6686  *                                                                         *
6687  *  Does the change somehow allow for arbitrary javascript to be executed? *
6688  *    Or allows for someone to change the prototype of built-in objects?   *
6689  *     Or gives undesired access to variables likes document or window?    *
6690  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
6692 /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
6694  * DOM-related variables:
6696  * - "node" - DOM Node
6697  * - "element" - DOM Element or Node
6698  * - "$node" or "$element" - jqLite-wrapped node or element
6701  * Compiler related stuff:
6703  * - "linkFn" - linking fn of a single directive
6704  * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
6705  * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
6706  * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
6707  */
6711  * @ngdoc service
6712  * @name $compile
6713  * @kind function
6715  * @description
6716  * Compiles an HTML string or DOM into a template and produces a template function, which
6717  * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
6719  * The compilation is a process of walking the DOM tree and matching DOM elements to
6720  * {@link ng.$compileProvider#directive directives}.
6722  * <div class="alert alert-warning">
6723  * **Note:** This document is an in-depth reference of all directive options.
6724  * For a gentle introduction to directives with examples of common use cases,
6725  * see the {@link guide/directive directive guide}.
6726  * </div>
6728  * ## Comprehensive Directive API
6730  * There are many different options for a directive.
6732  * The difference resides in the return value of the factory function.
6733  * You can either return a {@link $compile#directive-definition-object Directive Definition Object (see below)}
6734  * that defines the directive properties, or just the `postLink` function (all other properties will have
6735  * the default values).
6737  * <div class="alert alert-success">
6738  * **Best Practice:** It's recommended to use the "directive definition object" form.
6739  * </div>
6741  * Here's an example directive declared with a Directive Definition Object:
6743  * ```js
6744  *   var myModule = angular.module(...);
6746  *   myModule.directive('directiveName', function factory(injectables) {
6747  *     var directiveDefinitionObject = {
6748  *       priority: 0,
6749  *       template: '<div></div>', // or // function(tElement, tAttrs) { ... },
6750  *       // or
6751  *       // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
6752  *       transclude: false,
6753  *       restrict: 'A',
6754  *       templateNamespace: 'html',
6755  *       scope: false,
6756  *       controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
6757  *       controllerAs: 'stringIdentifier',
6758  *       bindToController: false,
6759  *       require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
6760  *       compile: function compile(tElement, tAttrs, transclude) {
6761  *         return {
6762  *           pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6763  *           post: function postLink(scope, iElement, iAttrs, controller) { ... }
6764  *         }
6765  *         // or
6766  *         // return function postLink( ... ) { ... }
6767  *       },
6768  *       // or
6769  *       // link: {
6770  *       //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6771  *       //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
6772  *       // }
6773  *       // or
6774  *       // link: function postLink( ... ) { ... }
6775  *     };
6776  *     return directiveDefinitionObject;
6777  *   });
6778  * ```
6780  * <div class="alert alert-warning">
6781  * **Note:** Any unspecified options will use the default value. You can see the default values below.
6782  * </div>
6784  * Therefore the above can be simplified as:
6786  * ```js
6787  *   var myModule = angular.module(...);
6789  *   myModule.directive('directiveName', function factory(injectables) {
6790  *     var directiveDefinitionObject = {
6791  *       link: function postLink(scope, iElement, iAttrs) { ... }
6792  *     };
6793  *     return directiveDefinitionObject;
6794  *     // or
6795  *     // return function postLink(scope, iElement, iAttrs) { ... }
6796  *   });
6797  * ```
6799  * ### Life-cycle hooks
6800  * Directive controllers can provide the following methods that are called by Angular at points in the life-cycle of the
6801  * directive:
6802  * * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
6803  *   had their bindings initialized (and before the pre &amp; post linking functions for the directives on
6804  *   this element). This is a good place to put initialization code for your controller.
6805  * * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
6806  *   `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
6807  *   object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
6808  *   component such as cloning the bound value to prevent accidental mutation of the outer value.
6809  * * `$doCheck()` - Called on each turn of the digest cycle. Provides an opportunity to detect and act on
6810  *   changes. Any actions that you wish to take in response to the changes that you detect must be
6811  *   invoked from this hook; implementing this has no effect on when `$onChanges` is called. For example, this hook
6812  *   could be useful if you wish to perform a deep equality check, or to check a Date object, changes to which would not
6813  *   be detected by Angular's change detector and thus not trigger `$onChanges`. This hook is invoked with no arguments;
6814  *   if detecting changes, you must store the previous value(s) for comparison to the current values.
6815  * * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
6816  *   external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
6817  *   the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
6818  *   components will have their `$onDestroy()` hook called before child components.
6819  * * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
6820  *   function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
6821  *   Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
6822  *   they are waiting for their template to load asynchronously and their own compilation and linking has been
6823  *   suspended until that occurs.
6825  * #### Comparison with Angular 2 life-cycle hooks
6826  * Angular 2 also uses life-cycle hooks for its components. While the Angular 1 life-cycle hooks are similar there are
6827  * some differences that you should be aware of, especially when it comes to moving your code from Angular 1 to Angular 2:
6829  * * Angular 1 hooks are prefixed with `$`, such as `$onInit`. Angular 2 hooks are prefixed with `ng`, such as `ngOnInit`.
6830  * * Angular 1 hooks can be defined on the controller prototype or added to the controller inside its constructor.
6831  *   In Angular 2 you can only define hooks on the prototype of the Component class.
6832  * * Due to the differences in change-detection, you may get many more calls to `$doCheck` in Angular 1 than you would to
6833  *   `ngDoCheck` in Angular 2
6834  * * Changes to the model inside `$doCheck` will trigger new turns of the digest loop, which will cause the changes to be
6835  *   propagated throughout the application.
6836  *   Angular 2 does not allow the `ngDoCheck` hook to trigger a change outside of the component. It will either throw an
6837  *   error or do nothing depending upon the state of `enableProdMode()`.
6839  * #### Life-cycle hook examples
6841  * This example shows how you can check for mutations to a Date object even though the identity of the object
6842  * has not changed.
6844  * <example name="doCheckDateExample" module="do-check-module">
6845  *   <file name="app.js">
6846  *     angular.module('do-check-module', [])
6847  *       .component('app', {
6848  *         template:
6849  *           'Month: <input ng-model="$ctrl.month" ng-change="$ctrl.updateDate()">' +
6850  *           'Date: {{ $ctrl.date }}' +
6851  *           '<test date="$ctrl.date"></test>',
6852  *         controller: function() {
6853  *           this.date = new Date();
6854  *           this.month = this.date.getMonth();
6855  *           this.updateDate = function() {
6856  *             this.date.setMonth(this.month);
6857  *           };
6858  *         }
6859  *       })
6860  *       .component('test', {
6861  *         bindings: { date: '<' },
6862  *         template:
6863  *           '<pre>{{ $ctrl.log | json }}</pre>',
6864  *         controller: function() {
6865  *           var previousValue;
6866  *           this.log = [];
6867  *           this.$doCheck = function() {
6868  *             var currentValue = this.date && this.date.valueOf();
6869  *             if (previousValue !== currentValue) {
6870  *               this.log.push('doCheck: date mutated: ' + this.date);
6871  *               previousValue = currentValue;
6872  *             }
6873  *           };
6874  *         }
6875  *       });
6876  *   </file>
6877  *   <file name="index.html">
6878  *     <app></app>
6879  *   </file>
6880  * </example>
6882  * This example show how you might use `$doCheck` to trigger changes in your component's inputs even if the
6883  * actual identity of the component doesn't change. (Be aware that cloning and deep equality checks on large
6884  * arrays or objects can have a negative impact on your application performance)
6886  * <example name="doCheckArrayExample" module="do-check-module">
6887  *   <file name="index.html">
6888  *     <div ng-init="items = []">
6889  *       <button ng-click="items.push(items.length)">Add Item</button>
6890  *       <button ng-click="items = []">Reset Items</button>
6891  *       <pre>{{ items }}</pre>
6892  *       <test items="items"></test>
6893  *     </div>
6894  *   </file>
6895  *   <file name="app.js">
6896  *      angular.module('do-check-module', [])
6897  *        .component('test', {
6898  *          bindings: { items: '<' },
6899  *          template:
6900  *            '<pre>{{ $ctrl.log | json }}</pre>',
6901  *          controller: function() {
6902  *            this.log = [];
6904  *            this.$doCheck = function() {
6905  *              if (this.items_ref !== this.items) {
6906  *                this.log.push('doCheck: items changed');
6907  *                this.items_ref = this.items;
6908  *              }
6909  *              if (!angular.equals(this.items_clone, this.items)) {
6910  *                this.log.push('doCheck: items mutated');
6911  *                this.items_clone = angular.copy(this.items);
6912  *              }
6913  *            };
6914  *          }
6915  *        });
6916  *   </file>
6917  * </example>
6920  * ### Directive Definition Object
6922  * The directive definition object provides instructions to the {@link ng.$compile
6923  * compiler}. The attributes are:
6925  * #### `multiElement`
6926  * When this property is set to true, the HTML compiler will collect DOM nodes between
6927  * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
6928  * together as the directive elements. It is recommended that this feature be used on directives
6929  * which are not strictly behavioral (such as {@link ngClick}), and which
6930  * do not manipulate or replace child nodes (such as {@link ngInclude}).
6932  * #### `priority`
6933  * When there are multiple directives defined on a single DOM element, sometimes it
6934  * is necessary to specify the order in which the directives are applied. The `priority` is used
6935  * to sort the directives before their `compile` functions get called. Priority is defined as a
6936  * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
6937  * are also run in priority order, but post-link functions are run in reverse order. The order
6938  * of directives with the same priority is undefined. The default priority is `0`.
6940  * #### `terminal`
6941  * If set to true then the current `priority` will be the last set of directives
6942  * which will execute (any directives at the current priority will still execute
6943  * as the order of execution on same `priority` is undefined). Note that expressions
6944  * and other directives used in the directive's template will also be excluded from execution.
6946  * #### `scope`
6947  * The scope property can be `true`, an object or a falsy value:
6949  * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
6951  * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
6952  * the directive's element. If multiple directives on the same element request a new scope,
6953  * only one new scope is created. The new scope rule does not apply for the root of the template
6954  * since the root of the template always gets a new scope.
6956  * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
6957  * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
6958  * scope. This is useful when creating reusable components, which should not accidentally read or modify
6959  * data in the parent scope.
6961  * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
6962  * directive's element. These local properties are useful for aliasing values for templates. The keys in
6963  * the object hash map to the name of the property on the isolate scope; the values define how the property
6964  * is bound to the parent scope, via matching attributes on the directive's element:
6966  * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
6967  *   always a string since DOM attributes are strings. If no `attr` name is specified then the
6968  *   attribute name is assumed to be the same as the local name. Given `<my-component
6969  *   my-attr="hello {{name}}">` and the isolate scope definition `scope: { localName:'@myAttr' }`,
6970  *   the directive's scope property `localName` will reflect the interpolated value of `hello
6971  *   {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's
6972  *   scope. The `name` is read from the parent scope (not the directive's scope).
6974  * * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression
6975  *   passed via the attribute `attr`. The expression is evaluated in the context of the parent scope.
6976  *   If no `attr` name is specified then the attribute name is assumed to be the same as the local
6977  *   name. Given `<my-component my-attr="parentModel">` and the isolate scope definition `scope: {
6978  *   localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the
6979  *   value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in
6980  *   `localModel` and vice versa. Optional attributes should be marked as such with a question mark:
6981  *   `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't
6982  *   optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`})
6983  *   will be thrown upon discovering changes to the local value, since it will be impossible to sync
6984  *   them back to the parent scope. By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
6985  *   method is used for tracking changes, and the equality check is based on object identity.
6986  *   However, if an object literal or an array literal is passed as the binding expression, the
6987  *   equality check is done by value (using the {@link angular.equals} function). It's also possible
6988  *   to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection
6989  *   `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional).
6991   * * `<` or `<attr` - set up a one-way (one-directional) binding between a local scope property and an
6992  *   expression passed via the attribute `attr`. The expression is evaluated in the context of the
6993  *   parent scope. If no `attr` name is specified then the attribute name is assumed to be the same as the
6994  *   local name. You can also make the binding optional by adding `?`: `<?` or `<?attr`.
6996  *   For example, given `<my-component my-attr="parentModel">` and directive definition of
6997  *   `scope: { localModel:'<myAttr' }`, then the isolated scope property `localModel` will reflect the
6998  *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
6999  *   in `localModel`, but changes in `localModel` will not reflect in `parentModel`. There are however
7000  *   two caveats:
7001  *     1. one-way binding does not copy the value from the parent to the isolate scope, it simply
7002  *     sets the same value. That means if your bound value is an object, changes to its properties
7003  *     in the isolated scope will be reflected in the parent scope (because both reference the same object).
7004  *     2. one-way binding watches changes to the **identity** of the parent value. That means the
7005  *     {@link ng.$rootScope.Scope#$watch `$watch`} on the parent value only fires if the reference
7006  *     to the value has changed. In most cases, this should not be of concern, but can be important
7007  *     to know if you one-way bind to an object, and then replace that object in the isolated scope.
7008  *     If you now change a property of the object in your parent scope, the change will not be
7009  *     propagated to the isolated scope, because the identity of the object on the parent scope
7010  *     has not changed. Instead you must assign a new object.
7012  *   One-way binding is useful if you do not plan to propagate changes to your isolated scope bindings
7013  *   back to the parent. However, it does not make this completely impossible.
7015  * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. If
7016  *   no `attr` name is specified then the attribute name is assumed to be the same as the local name.
7017  *   Given `<my-component my-attr="count = count + value">` and the isolate scope definition `scope: {
7018  *   localFn:'&myAttr' }`, the isolate scope property `localFn` will point to a function wrapper for
7019  *   the `count = count + value` expression. Often it's desirable to pass data from the isolated scope
7020  *   via an expression to the parent scope. This can be done by passing a map of local variable names
7021  *   and values into the expression wrapper fn. For example, if the expression is `increment(amount)`
7022  *   then we can specify the amount value by calling the `localFn` as `localFn({amount: 22})`.
7024  * In general it's possible to apply more than one directive to one element, but there might be limitations
7025  * depending on the type of scope required by the directives. The following points will help explain these limitations.
7026  * For simplicity only two directives are taken into account, but it is also applicable for several directives:
7028  * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
7029  * * **child scope** + **no scope** =>  Both directives will share one single child scope
7030  * * **child scope** + **child scope** =>  Both directives will share one single child scope
7031  * * **isolated scope** + **no scope** =>  The isolated directive will use it's own created isolated scope. The other directive will use
7032  * its parent's scope
7033  * * **isolated scope** + **child scope** =>  **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
7034  * be applied to the same element.
7035  * * **isolated scope** + **isolated scope**  =>  **Won't work!** Only one scope can be related to one element. Therefore these directives
7036  * cannot be applied to the same element.
7039  * #### `bindToController`
7040  * This property is used to bind scope properties directly to the controller. It can be either
7041  * `true` or an object hash with the same format as the `scope` property. Additionally, a controller
7042  * alias must be set, either by using `controllerAs: 'myAlias'` or by specifying the alias in the controller
7043  * definition: `controller: 'myCtrl as myAlias'`.
7045  * When an isolate scope is used for a directive (see above), `bindToController: true` will
7046  * allow a component to have its properties bound to the controller, rather than to scope.
7048  * After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller
7049  * properties. You can access these bindings once they have been initialized by providing a controller method called
7050  * `$onInit`, which is called after all the controllers on an element have been constructed and had their bindings
7051  * initialized.
7053  * <div class="alert alert-warning">
7054  * **Deprecation warning:** although bindings for non-ES6 class controllers are currently
7055  * bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization
7056  * code that relies upon bindings inside a `$onInit` method on the controller, instead.
7057  * </div>
7059  * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
7060  * This will set up the scope bindings to the controller directly. Note that `scope` can still be used
7061  * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate
7062  * scope (useful for component directives).
7064  * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`.
7067  * #### `controller`
7068  * Controller constructor function. The controller is instantiated before the
7069  * pre-linking phase and can be accessed by other directives (see
7070  * `require` attribute). This allows the directives to communicate with each other and augment
7071  * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
7073  * * `$scope` - Current scope associated with the element
7074  * * `$element` - Current element
7075  * * `$attrs` - Current attributes object for the element
7076  * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
7077  *   `function([scope], cloneLinkingFn, futureParentElement, slotName)`:
7078  *    * `scope`: (optional) override the scope.
7079  *    * `cloneLinkingFn`: (optional) argument to create clones of the original transcluded content.
7080  *    * `futureParentElement` (optional):
7081  *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
7082  *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
7083  *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
7084  *          and when the `cloneLinkinFn` is passed,
7085  *          as those elements need to created and cloned in a special way when they are defined outside their
7086  *          usual containers (e.g. like `<svg>`).
7087  *        * See also the `directive.templateNamespace` property.
7088  *    * `slotName`: (optional) the name of the slot to transclude. If falsy (e.g. `null`, `undefined` or `''`)
7089  *      then the default translusion is provided.
7090  *    The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns
7091  *    `true` if the specified slot contains content (i.e. one or more DOM nodes).
7093  * #### `require`
7094  * Require another directive and inject its controller as the fourth argument to the linking function. The
7095  * `require` property can be a string, an array or an object:
7096  * * a **string** containing the name of the directive to pass to the linking function
7097  * * an **array** containing the names of directives to pass to the linking function. The argument passed to the
7098  * linking function will be an array of controllers in the same order as the names in the `require` property
7099  * * an **object** whose property values are the names of the directives to pass to the linking function. The argument
7100  * passed to the linking function will also be an object with matching keys, whose values will hold the corresponding
7101  * controllers.
7103  * If the `require` property is an object and `bindToController` is truthy, then the required controllers are
7104  * bound to the controller using the keys of the `require` property. This binding occurs after all the controllers
7105  * have been constructed but before `$onInit` is called.
7106  * If the name of the required controller is the same as the local name (the key), the name can be
7107  * omitted. For example, `{parentDir: '^^'}` is equivalent to `{parentDir: '^^parentDir'}`.
7108  * See the {@link $compileProvider#component} helper for an example of how this can be used.
7109  * If no such required directive(s) can be found, or if the directive does not have a controller, then an error is
7110  * raised (unless no link function is specified and the required controllers are not being bound to the directive
7111  * controller, in which case error checking is skipped). The name can be prefixed with:
7113  * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
7114  * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
7115  * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
7116  * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
7117  * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
7118  *   `null` to the `link` fn if not found.
7119  * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
7120  *   `null` to the `link` fn if not found.
7123  * #### `controllerAs`
7124  * Identifier name for a reference to the controller in the directive's scope.
7125  * This allows the controller to be referenced from the directive template. This is especially
7126  * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
7127  * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
7128  * `controllerAs` reference might overwrite a property that already exists on the parent scope.
7131  * #### `restrict`
7132  * String of subset of `EACM` which restricts the directive to a specific directive
7133  * declaration style. If omitted, the defaults (elements and attributes) are used.
7135  * * `E` - Element name (default): `<my-directive></my-directive>`
7136  * * `A` - Attribute (default): `<div my-directive="exp"></div>`
7137  * * `C` - Class: `<div class="my-directive: exp;"></div>`
7138  * * `M` - Comment: `<!-- directive: my-directive exp -->`
7141  * #### `templateNamespace`
7142  * String representing the document type used by the markup in the template.
7143  * AngularJS needs this information as those elements need to be created and cloned
7144  * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
7146  * * `html` - All root nodes in the template are HTML. Root nodes may also be
7147  *   top-level elements such as `<svg>` or `<math>`.
7148  * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
7149  * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
7151  * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
7153  * #### `template`
7154  * HTML markup that may:
7155  * * Replace the contents of the directive's element (default).
7156  * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
7157  * * Wrap the contents of the directive's element (if `transclude` is true).
7159  * Value may be:
7161  * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
7162  * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
7163  *   function api below) and returns a string value.
7166  * #### `templateUrl`
7167  * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
7169  * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
7170  * for later when the template has been resolved.  In the meantime it will continue to compile and link
7171  * sibling and parent elements as though this element had not contained any directives.
7173  * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
7174  * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
7175  * case when only one deeply nested directive has `templateUrl`.
7177  * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
7179  * You can specify `templateUrl` as a string representing the URL or as a function which takes two
7180  * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
7181  * a string value representing the url.  In either case, the template URL is passed through {@link
7182  * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
7185  * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
7186  * specify what the template should replace. Defaults to `false`.
7188  * * `true` - the template will replace the directive's element.
7189  * * `false` - the template will replace the contents of the directive's element.
7191  * The replacement process migrates all of the attributes / classes from the old element to the new
7192  * one. See the {@link guide/directive#template-expanding-directive
7193  * Directives Guide} for an example.
7195  * There are very few scenarios where element replacement is required for the application function,
7196  * the main one being reusable custom components that are used within SVG contexts
7197  * (because SVG doesn't work with custom elements in the DOM tree).
7199  * #### `transclude`
7200  * Extract the contents of the element where the directive appears and make it available to the directive.
7201  * The contents are compiled and provided to the directive as a **transclusion function**. See the
7202  * {@link $compile#transclusion Transclusion} section below.
7205  * #### `compile`
7207  * ```js
7208  *   function compile(tElement, tAttrs, transclude) { ... }
7209  * ```
7211  * The compile function deals with transforming the template DOM. Since most directives do not do
7212  * template transformation, it is not used often. The compile function takes the following arguments:
7214  *   * `tElement` - template element - The element where the directive has been declared. It is
7215  *     safe to do template transformation on the element and child elements only.
7217  *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
7218  *     between all directive compile functions.
7220  *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
7222  * <div class="alert alert-warning">
7223  * **Note:** The template instance and the link instance may be different objects if the template has
7224  * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
7225  * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
7226  * should be done in a linking function rather than in a compile function.
7227  * </div>
7229  * <div class="alert alert-warning">
7230  * **Note:** The compile function cannot handle directives that recursively use themselves in their
7231  * own templates or compile functions. Compiling these directives results in an infinite loop and
7232  * stack overflow errors.
7234  * This can be avoided by manually using $compile in the postLink function to imperatively compile
7235  * a directive's template instead of relying on automatic template compilation via `template` or
7236  * `templateUrl` declaration or manual compilation inside the compile function.
7237  * </div>
7239  * <div class="alert alert-danger">
7240  * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
7241  *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
7242  *   to the link function instead.
7243  * </div>
7245  * A compile function can have a return value which can be either a function or an object.
7247  * * returning a (post-link) function - is equivalent to registering the linking function via the
7248  *   `link` property of the config object when the compile function is empty.
7250  * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
7251  *   control when a linking function should be called during the linking phase. See info about
7252  *   pre-linking and post-linking functions below.
7255  * #### `link`
7256  * This property is used only if the `compile` property is not defined.
7258  * ```js
7259  *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
7260  * ```
7262  * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
7263  * executed after the template has been cloned. This is where most of the directive logic will be
7264  * put.
7266  *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
7267  *     directive for registering {@link ng.$rootScope.Scope#$watch watches}.
7269  *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
7270  *     manipulate the children of the element only in `postLink` function since the children have
7271  *     already been linked.
7273  *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
7274  *     between all directive linking functions.
7276  *   * `controller` - the directive's required controller instance(s) - Instances are shared
7277  *     among all directives, which allows the directives to use the controllers as a communication
7278  *     channel. The exact value depends on the directive's `require` property:
7279  *       * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
7280  *       * `string`: the controller instance
7281  *       * `array`: array of controller instances
7283  *     If a required controller cannot be found, and it is optional, the instance is `null`,
7284  *     otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
7286  *     Note that you can also require the directive's own controller - it will be made available like
7287  *     any other controller.
7289  *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
7290  *     This is the same as the `$transclude` parameter of directive controllers,
7291  *     see {@link ng.$compile#-controller- the controller section for details}.
7292  *     `function([scope], cloneLinkingFn, futureParentElement)`.
7294  * #### Pre-linking function
7296  * Executed before the child elements are linked. Not safe to do DOM transformation since the
7297  * compiler linking function will fail to locate the correct elements for linking.
7299  * #### Post-linking function
7301  * Executed after the child elements are linked.
7303  * Note that child elements that contain `templateUrl` directives will not have been compiled
7304  * and linked since they are waiting for their template to load asynchronously and their own
7305  * compilation and linking has been suspended until that occurs.
7307  * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
7308  * for their async templates to be resolved.
7311  * ### Transclusion
7313  * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
7314  * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
7315  * scope from where they were taken.
7317  * Transclusion is used (often with {@link ngTransclude}) to insert the
7318  * original contents of a directive's element into a specified place in the template of the directive.
7319  * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
7320  * content has access to the properties on the scope from which it was taken, even if the directive
7321  * has isolated scope.
7322  * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
7324  * This makes it possible for the widget to have private state for its template, while the transcluded
7325  * content has access to its originating scope.
7327  * <div class="alert alert-warning">
7328  * **Note:** When testing an element transclude directive you must not place the directive at the root of the
7329  * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
7330  * Testing Transclusion Directives}.
7331  * </div>
7333  * There are three kinds of transclusion depending upon whether you want to transclude just the contents of the
7334  * directive's element, the entire element or multiple parts of the element contents:
7336  * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
7337  * * `'element'` - transclude the whole of the directive's element including any directives on this
7338  *   element that defined at a lower priority than this directive. When used, the `template`
7339  *   property is ignored.
7340  * * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template.
7342  * **Mult-slot transclusion** is declared by providing an object for the `transclude` property.
7344  * This object is a map where the keys are the name of the slot to fill and the value is an element selector
7345  * used to match the HTML to the slot. The element selector should be in normalized form (e.g. `myElement`)
7346  * and will match the standard element variants (e.g. `my-element`, `my:element`, `data-my-element`, etc).
7348  * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7350  * If the element selector is prefixed with a `?` then that slot is optional.
7352  * For example, the transclude object `{ slotA: '?myCustomElement' }` maps `<my-custom-element>` elements to
7353  * the `slotA` slot, which can be accessed via the `$transclude` function or via the {@link ngTransclude} directive.
7355  * Slots that are not marked as optional (`?`) will trigger a compile time error if there are no matching elements
7356  * in the transclude content. If you wish to know if an optional slot was filled with content, then you can call
7357  * `$transclude.isSlotFilled(slotName)` on the transclude function passed to the directive's link function and
7358  * injectable into the directive's controller.
7361  * #### Transclusion Functions
7363  * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
7364  * function** to the directive's `link` function and `controller`. This transclusion function is a special
7365  * **linking function** that will return the compiled contents linked to a new transclusion scope.
7367  * <div class="alert alert-info">
7368  * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
7369  * ngTransclude will deal with it for us.
7370  * </div>
7372  * If you want to manually control the insertion and removal of the transcluded content in your directive
7373  * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
7374  * object that contains the compiled DOM, which is linked to the correct transclusion scope.
7376  * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
7377  * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
7378  * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
7380  * <div class="alert alert-info">
7381  * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a transclude function
7382  * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
7383  * </div>
7385  * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
7386  * attach function**:
7388  * ```js
7389  * var transcludedContent, transclusionScope;
7391  * $transclude(function(clone, scope) {
7392  *   element.append(clone);
7393  *   transcludedContent = clone;
7394  *   transclusionScope = scope;
7395  * });
7396  * ```
7398  * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
7399  * associated transclusion scope:
7401  * ```js
7402  * transcludedContent.remove();
7403  * transclusionScope.$destroy();
7404  * ```
7406  * <div class="alert alert-info">
7407  * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
7408  * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
7409  * then you are also responsible for calling `$destroy` on the transclusion scope.
7410  * </div>
7412  * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
7413  * automatically destroy their transcluded clones as necessary so you do not need to worry about this if
7414  * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
7417  * #### Transclusion Scopes
7419  * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
7420  * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
7421  * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
7422  * was taken.
7424  * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
7425  * like this:
7427  * ```html
7428  * <div ng-app>
7429  *   <div isolate>
7430  *     <div transclusion>
7431  *     </div>
7432  *   </div>
7433  * </div>
7434  * ```
7436  * The `$parent` scope hierarchy will look like this:
7438    ```
7439    - $rootScope
7440      - isolate
7441        - transclusion
7442    ```
7444  * but the scopes will inherit prototypically from different scopes to their `$parent`.
7446    ```
7447    - $rootScope
7448      - transclusion
7449    - isolate
7450    ```
7453  * ### Attributes
7455  * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
7456  * `link()` or `compile()` functions. It has a variety of uses.
7458  * * *Accessing normalized attribute names:* Directives like 'ngBind' can be expressed in many ways:
7459  *   'ng:bind', `data-ng-bind`, or 'x-ng-bind'. The attributes object allows for normalized access
7460  *   to the attributes.
7462  * * *Directive inter-communication:* All directives share the same instance of the attributes
7463  *   object which allows the directives to use the attributes object as inter directive
7464  *   communication.
7466  * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
7467  *   allowing other directives to read the interpolated value.
7469  * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
7470  *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
7471  *   the only way to easily get the actual value because during the linking phase the interpolation
7472  *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
7474  * ```js
7475  * function linkingFn(scope, elm, attrs, ctrl) {
7476  *   // get the attribute value
7477  *   console.log(attrs.ngModel);
7479  *   // change the attribute
7480  *   attrs.$set('ngModel', 'new value');
7482  *   // observe changes to interpolated attribute
7483  *   attrs.$observe('ngModel', function(value) {
7484  *     console.log('ngModel has changed value to ' + value);
7485  *   });
7486  * }
7487  * ```
7489  * ## Example
7491  * <div class="alert alert-warning">
7492  * **Note**: Typically directives are registered with `module.directive`. The example below is
7493  * to illustrate how `$compile` works.
7494  * </div>
7496  <example module="compileExample">
7497    <file name="index.html">
7498     <script>
7499       angular.module('compileExample', [], function($compileProvider) {
7500         // configure new 'compile' directive by passing a directive
7501         // factory function. The factory function injects the '$compile'
7502         $compileProvider.directive('compile', function($compile) {
7503           // directive factory creates a link function
7504           return function(scope, element, attrs) {
7505             scope.$watch(
7506               function(scope) {
7507                  // watch the 'compile' expression for changes
7508                 return scope.$eval(attrs.compile);
7509               },
7510               function(value) {
7511                 // when the 'compile' expression changes
7512                 // assign it into the current DOM
7513                 element.html(value);
7515                 // compile the new DOM and link it to the current
7516                 // scope.
7517                 // NOTE: we only compile .childNodes so that
7518                 // we don't get into infinite loop compiling ourselves
7519                 $compile(element.contents())(scope);
7520               }
7521             );
7522           };
7523         });
7524       })
7525       .controller('GreeterController', ['$scope', function($scope) {
7526         $scope.name = 'Angular';
7527         $scope.html = 'Hello {{name}}';
7528       }]);
7529     </script>
7530     <div ng-controller="GreeterController">
7531       <input ng-model="name"> <br/>
7532       <textarea ng-model="html"></textarea> <br/>
7533       <div compile="html"></div>
7534     </div>
7535    </file>
7536    <file name="protractor.js" type="protractor">
7537      it('should auto compile', function() {
7538        var textarea = $('textarea');
7539        var output = $('div[compile]');
7540        // The initial state reads 'Hello Angular'.
7541        expect(output.getText()).toBe('Hello Angular');
7542        textarea.clear();
7543        textarea.sendKeys('{{name}}!');
7544        expect(output.getText()).toBe('Angular!');
7545      });
7546    </file>
7547  </example>
7551  * @param {string|DOMElement} element Element or HTML string to compile into a template function.
7552  * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
7554  * <div class="alert alert-danger">
7555  * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
7556  *   e.g. will not use the right outer scope. Please pass the transclude function as a
7557  *   `parentBoundTranscludeFn` to the link function instead.
7558  * </div>
7560  * @param {number} maxPriority only apply directives lower than given priority (Only effects the
7561  *                 root element(s), not their children)
7562  * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
7563  * (a DOM element/tree) to a scope. Where:
7565  *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
7566  *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
7567  *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
7568  *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
7569  *  called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
7571  *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
7572  *      * `scope` - is the current scope with which the linking function is working with.
7574  *  * `options` - An optional object hash with linking options. If `options` is provided, then the following
7575  *  keys may be used to control linking behavior:
7577  *      * `parentBoundTranscludeFn` - the transclude function made available to
7578  *        directives; if given, it will be passed through to the link functions of
7579  *        directives found in `element` during compilation.
7580  *      * `transcludeControllers` - an object hash with keys that map controller names
7581  *        to a hash with the key `instance`, which maps to the controller instance;
7582  *        if given, it will make the controllers available to directives on the compileNode:
7583  *        ```
7584  *        {
7585  *          parent: {
7586  *            instance: parentControllerInstance
7587  *          }
7588  *        }
7589  *        ```
7590  *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
7591  *        the cloned elements; only needed for transcludes that are allowed to contain non html
7592  *        elements (e.g. SVG elements). See also the directive.controller property.
7594  * Calling the linking function returns the element of the template. It is either the original
7595  * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
7597  * After linking the view is not updated until after a call to $digest which typically is done by
7598  * Angular automatically.
7600  * If you need access to the bound view, there are two ways to do it:
7602  * - If you are not asking the linking function to clone the template, create the DOM element(s)
7603  *   before you send them to the compiler and keep this reference around.
7604  *   ```js
7605  *     var element = $compile('<p>{{total}}</p>')(scope);
7606  *   ```
7608  * - if on the other hand, you need the element to be cloned, the view reference from the original
7609  *   example would not point to the clone, but rather to the original template that was cloned. In
7610  *   this case, you can access the clone via the cloneAttachFn:
7611  *   ```js
7612  *     var templateElement = angular.element('<p>{{total}}</p>'),
7613  *         scope = ....;
7615  *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
7616  *       //attach the clone to DOM document at the right place
7617  *     });
7619  *     //now we have reference to the cloned DOM via `clonedElement`
7620  *   ```
7623  * For information on how the compiler works, see the
7624  * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
7625  */
7627 var $compileMinErr = minErr('$compile');
7629 function UNINITIALIZED_VALUE() {}
7630 var _UNINITIALIZED_VALUE = new UNINITIALIZED_VALUE();
7633  * @ngdoc provider
7634  * @name $compileProvider
7636  * @description
7637  */
7638 $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
7639 function $CompileProvider($provide, $$sanitizeUriProvider) {
7640   var hasDirectives = {},
7641       Suffix = 'Directive',
7642       COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
7643       CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
7644       ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
7645       REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
7647   // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
7648   // The assumption is that future DOM event attribute names will begin with
7649   // 'on' and be composed of only English letters.
7650   var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
7651   var bindingCache = createMap();
7653   function parseIsolateBindings(scope, directiveName, isController) {
7654     var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/;
7656     var bindings = createMap();
7658     forEach(scope, function(definition, scopeName) {
7659       if (definition in bindingCache) {
7660         bindings[scopeName] = bindingCache[definition];
7661         return;
7662       }
7663       var match = definition.match(LOCAL_REGEXP);
7665       if (!match) {
7666         throw $compileMinErr('iscp',
7667             "Invalid {3} for directive '{0}'." +
7668             " Definition: {... {1}: '{2}' ...}",
7669             directiveName, scopeName, definition,
7670             (isController ? "controller bindings definition" :
7671             "isolate scope definition"));
7672       }
7674       bindings[scopeName] = {
7675         mode: match[1][0],
7676         collection: match[2] === '*',
7677         optional: match[3] === '?',
7678         attrName: match[4] || scopeName
7679       };
7680       if (match[4]) {
7681         bindingCache[definition] = bindings[scopeName];
7682       }
7683     });
7685     return bindings;
7686   }
7688   function parseDirectiveBindings(directive, directiveName) {
7689     var bindings = {
7690       isolateScope: null,
7691       bindToController: null
7692     };
7693     if (isObject(directive.scope)) {
7694       if (directive.bindToController === true) {
7695         bindings.bindToController = parseIsolateBindings(directive.scope,
7696                                                          directiveName, true);
7697         bindings.isolateScope = {};
7698       } else {
7699         bindings.isolateScope = parseIsolateBindings(directive.scope,
7700                                                      directiveName, false);
7701       }
7702     }
7703     if (isObject(directive.bindToController)) {
7704       bindings.bindToController =
7705           parseIsolateBindings(directive.bindToController, directiveName, true);
7706     }
7707     if (isObject(bindings.bindToController)) {
7708       var controller = directive.controller;
7709       var controllerAs = directive.controllerAs;
7710       if (!controller) {
7711         // There is no controller, there may or may not be a controllerAs property
7712         throw $compileMinErr('noctrl',
7713               "Cannot bind to controller without directive '{0}'s controller.",
7714               directiveName);
7715       } else if (!identifierForController(controller, controllerAs)) {
7716         // There is a controller, but no identifier or controllerAs property
7717         throw $compileMinErr('noident',
7718               "Cannot bind to controller without identifier for directive '{0}'.",
7719               directiveName);
7720       }
7721     }
7722     return bindings;
7723   }
7725   function assertValidDirectiveName(name) {
7726     var letter = name.charAt(0);
7727     if (!letter || letter !== lowercase(letter)) {
7728       throw $compileMinErr('baddir', "Directive/Component name '{0}' is invalid. The first character must be a lowercase letter", name);
7729     }
7730     if (name !== name.trim()) {
7731       throw $compileMinErr('baddir',
7732             "Directive/Component name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
7733             name);
7734     }
7735   }
7737   function getDirectiveRequire(directive) {
7738     var require = directive.require || (directive.controller && directive.name);
7740     if (!isArray(require) && isObject(require)) {
7741       forEach(require, function(value, key) {
7742         var match = value.match(REQUIRE_PREFIX_REGEXP);
7743         var name = value.substring(match[0].length);
7744         if (!name) require[key] = match[0] + key;
7745       });
7746     }
7748     return require;
7749   }
7751   /**
7752    * @ngdoc method
7753    * @name $compileProvider#directive
7754    * @kind function
7755    *
7756    * @description
7757    * Register a new directive with the compiler.
7758    *
7759    * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
7760    *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
7761    *    names and the values are the factories.
7762    * @param {Function|Array} directiveFactory An injectable directive factory function. See the
7763    *    {@link guide/directive directive guide} and the {@link $compile compile API} for more info.
7764    * @returns {ng.$compileProvider} Self for chaining.
7765    */
7766   this.directive = function registerDirective(name, directiveFactory) {
7767     assertNotHasOwnProperty(name, 'directive');
7768     if (isString(name)) {
7769       assertValidDirectiveName(name);
7770       assertArg(directiveFactory, 'directiveFactory');
7771       if (!hasDirectives.hasOwnProperty(name)) {
7772         hasDirectives[name] = [];
7773         $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
7774           function($injector, $exceptionHandler) {
7775             var directives = [];
7776             forEach(hasDirectives[name], function(directiveFactory, index) {
7777               try {
7778                 var directive = $injector.invoke(directiveFactory);
7779                 if (isFunction(directive)) {
7780                   directive = { compile: valueFn(directive) };
7781                 } else if (!directive.compile && directive.link) {
7782                   directive.compile = valueFn(directive.link);
7783                 }
7784                 directive.priority = directive.priority || 0;
7785                 directive.index = index;
7786                 directive.name = directive.name || name;
7787                 directive.require = getDirectiveRequire(directive);
7788                 directive.restrict = directive.restrict || 'EA';
7789                 directive.$$moduleName = directiveFactory.$$moduleName;
7790                 directives.push(directive);
7791               } catch (e) {
7792                 $exceptionHandler(e);
7793               }
7794             });
7795             return directives;
7796           }]);
7797       }
7798       hasDirectives[name].push(directiveFactory);
7799     } else {
7800       forEach(name, reverseParams(registerDirective));
7801     }
7802     return this;
7803   };
7805   /**
7806    * @ngdoc method
7807    * @name $compileProvider#component
7808    * @module ng
7809    * @param {string} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`)
7810    * @param {Object} options Component definition object (a simplified
7811    *    {@link ng.$compile#directive-definition-object directive definition object}),
7812    *    with the following properties (all optional):
7813    *
7814    *    - `controller` â€“ `{(string|function()=}` â€“ controller constructor function that should be
7815    *      associated with newly created scope or the name of a {@link ng.$compile#-controller-
7816    *      registered controller} if passed as a string. An empty `noop` function by default.
7817    *    - `controllerAs` â€“ `{string=}` â€“ identifier name for to reference the controller in the component's scope.
7818    *      If present, the controller will be published to scope under the `controllerAs` name.
7819    *      If not present, this will default to be `$ctrl`.
7820    *    - `template` â€“ `{string=|function()=}` â€“ html template as a string or a function that
7821    *      returns an html template as a string which should be used as the contents of this component.
7822    *      Empty string by default.
7823    *
7824    *      If `template` is a function, then it is {@link auto.$injector#invoke injected} with
7825    *      the following locals:
7826    *
7827    *      - `$element` - Current element
7828    *      - `$attrs` - Current attributes object for the element
7829    *
7830    *    - `templateUrl` â€“ `{string=|function()=}` â€“ path or function that returns a path to an html
7831    *      template that should be used  as the contents of this component.
7832    *
7833    *      If `templateUrl` is a function, then it is {@link auto.$injector#invoke injected} with
7834    *      the following locals:
7835    *
7836    *      - `$element` - Current element
7837    *      - `$attrs` - Current attributes object for the element
7838    *
7839    *    - `bindings` â€“ `{object=}` â€“ defines bindings between DOM attributes and component properties.
7840    *      Component properties are always bound to the component controller and not to the scope.
7841    *      See {@link ng.$compile#-bindtocontroller- `bindToController`}.
7842    *    - `transclude` â€“ `{boolean=}` â€“ whether {@link $compile#transclusion content transclusion} is enabled.
7843    *      Disabled by default.
7844    *    - `require` - `{Object<string, string>=}` - requires the controllers of other directives and binds them to
7845    *      this component's controller. The object keys specify the property names under which the required
7846    *      controllers (object values) will be bound. See {@link ng.$compile#-require- `require`}.
7847    *    - `$...` â€“ additional properties to attach to the directive factory function and the controller
7848    *      constructor function. (This is used by the component router to annotate)
7849    *
7850    * @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls.
7851    * @description
7852    * Register a **component definition** with the compiler. This is a shorthand for registering a special
7853    * type of directive, which represents a self-contained UI component in your application. Such components
7854    * are always isolated (i.e. `scope: {}`) and are always restricted to elements (i.e. `restrict: 'E'`).
7855    *
7856    * Component definitions are very simple and do not require as much configuration as defining general
7857    * directives. Component definitions usually consist only of a template and a controller backing it.
7858    *
7859    * In order to make the definition easier, components enforce best practices like use of `controllerAs`,
7860    * `bindToController`. They always have **isolate scope** and are restricted to elements.
7861    *
7862    * Here are a few examples of how you would usually define components:
7863    *
7864    * ```js
7865    *   var myMod = angular.module(...);
7866    *   myMod.component('myComp', {
7867    *     template: '<div>My name is {{$ctrl.name}}</div>',
7868    *     controller: function() {
7869    *       this.name = 'shahar';
7870    *     }
7871    *   });
7872    *
7873    *   myMod.component('myComp', {
7874    *     template: '<div>My name is {{$ctrl.name}}</div>',
7875    *     bindings: {name: '@'}
7876    *   });
7877    *
7878    *   myMod.component('myComp', {
7879    *     templateUrl: 'views/my-comp.html',
7880    *     controller: 'MyCtrl',
7881    *     controllerAs: 'ctrl',
7882    *     bindings: {name: '@'}
7883    *   });
7884    *
7885    * ```
7886    * For more examples, and an in-depth guide, see the {@link guide/component component guide}.
7887    *
7888    * <br />
7889    * See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
7890    */
7891   this.component = function registerComponent(name, options) {
7892     var controller = options.controller || function() {};
7894     function factory($injector) {
7895       function makeInjectable(fn) {
7896         if (isFunction(fn) || isArray(fn)) {
7897           return function(tElement, tAttrs) {
7898             return $injector.invoke(fn, this, {$element: tElement, $attrs: tAttrs});
7899           };
7900         } else {
7901           return fn;
7902         }
7903       }
7905       var template = (!options.template && !options.templateUrl ? '' : options.template);
7906       var ddo = {
7907         controller: controller,
7908         controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
7909         template: makeInjectable(template),
7910         templateUrl: makeInjectable(options.templateUrl),
7911         transclude: options.transclude,
7912         scope: {},
7913         bindToController: options.bindings || {},
7914         restrict: 'E',
7915         require: options.require
7916       };
7918       // Copy annotations (starting with $) over to the DDO
7919       forEach(options, function(val, key) {
7920         if (key.charAt(0) === '$') ddo[key] = val;
7921       });
7923       return ddo;
7924     }
7926     // TODO(pete) remove the following `forEach` before we release 1.6.0
7927     // The component-router@0.2.0 looks for the annotations on the controller constructor
7928     // Nothing in Angular looks for annotations on the factory function but we can't remove
7929     // it from 1.5.x yet.
7931     // Copy any annotation properties (starting with $) over to the factory and controller constructor functions
7932     // These could be used by libraries such as the new component router
7933     forEach(options, function(val, key) {
7934       if (key.charAt(0) === '$') {
7935         factory[key] = val;
7936         // Don't try to copy over annotations to named controller
7937         if (isFunction(controller)) controller[key] = val;
7938       }
7939     });
7941     factory.$inject = ['$injector'];
7943     return this.directive(name, factory);
7944   };
7947   /**
7948    * @ngdoc method
7949    * @name $compileProvider#aHrefSanitizationWhitelist
7950    * @kind function
7951    *
7952    * @description
7953    * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7954    * urls during a[href] sanitization.
7955    *
7956    * The sanitization is a security measure aimed at preventing XSS attacks via html links.
7957    *
7958    * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
7959    * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
7960    * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7961    * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7962    *
7963    * @param {RegExp=} regexp New regexp to whitelist urls with.
7964    * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7965    *    chaining otherwise.
7966    */
7967   this.aHrefSanitizationWhitelist = function(regexp) {
7968     if (isDefined(regexp)) {
7969       $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
7970       return this;
7971     } else {
7972       return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
7973     }
7974   };
7977   /**
7978    * @ngdoc method
7979    * @name $compileProvider#imgSrcSanitizationWhitelist
7980    * @kind function
7981    *
7982    * @description
7983    * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7984    * urls during img[src] sanitization.
7985    *
7986    * The sanitization is a security measure aimed at prevent XSS attacks via html links.
7987    *
7988    * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
7989    * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
7990    * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7991    * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7992    *
7993    * @param {RegExp=} regexp New regexp to whitelist urls with.
7994    * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7995    *    chaining otherwise.
7996    */
7997   this.imgSrcSanitizationWhitelist = function(regexp) {
7998     if (isDefined(regexp)) {
7999       $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
8000       return this;
8001     } else {
8002       return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
8003     }
8004   };
8006   /**
8007    * @ngdoc method
8008    * @name  $compileProvider#debugInfoEnabled
8009    *
8010    * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
8011    * current debugInfoEnabled state
8012    * @returns {*} current value if used as getter or itself (chaining) if used as setter
8013    *
8014    * @kind function
8015    *
8016    * @description
8017    * Call this method to enable/disable various debug runtime information in the compiler such as adding
8018    * binding information and a reference to the current scope on to DOM elements.
8019    * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
8020    * * `ng-binding` CSS class
8021    * * `$binding` data property containing an array of the binding expressions
8022    *
8023    * You may want to disable this in production for a significant performance boost. See
8024    * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
8025    *
8026    * The default value is true.
8027    */
8028   var debugInfoEnabled = true;
8029   this.debugInfoEnabled = function(enabled) {
8030     if (isDefined(enabled)) {
8031       debugInfoEnabled = enabled;
8032       return this;
8033     }
8034     return debugInfoEnabled;
8035   };
8038   var TTL = 10;
8039   /**
8040    * @ngdoc method
8041    * @name $compileProvider#onChangesTtl
8042    * @description
8043    *
8044    * Sets the number of times `$onChanges` hooks can trigger new changes before giving up and
8045    * assuming that the model is unstable.
8046    *
8047    * The current default is 10 iterations.
8048    *
8049    * In complex applications it's possible that dependencies between `$onChanges` hooks and bindings will result
8050    * in several iterations of calls to these hooks. However if an application needs more than the default 10
8051    * iterations to stabilize then you should investigate what is causing the model to continuously change during
8052    * the `$onChanges` hook execution.
8053    *
8054    * Increasing the TTL could have performance implications, so you should not change it without proper justification.
8055    *
8056    * @param {number} limit The number of `$onChanges` hook iterations.
8057    * @returns {number|object} the current limit (or `this` if called as a setter for chaining)
8058    */
8059   this.onChangesTtl = function(value) {
8060     if (arguments.length) {
8061       TTL = value;
8062       return this;
8063     }
8064     return TTL;
8065   };
8067   this.$get = [
8068             '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
8069             '$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri',
8070     function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
8071              $controller,   $rootScope,   $sce,   $animate,   $$sanitizeUri) {
8073     var SIMPLE_ATTR_NAME = /^\w/;
8074     var specialAttrHolder = window.document.createElement('div');
8078     var onChangesTtl = TTL;
8079     // The onChanges hooks should all be run together in a single digest
8080     // When changes occur, the call to trigger their hooks will be added to this queue
8081     var onChangesQueue;
8083     // This function is called in a $$postDigest to trigger all the onChanges hooks in a single digest
8084     function flushOnChangesQueue() {
8085       try {
8086         if (!(--onChangesTtl)) {
8087           // We have hit the TTL limit so reset everything
8088           onChangesQueue = undefined;
8089           throw $compileMinErr('infchng', '{0} $onChanges() iterations reached. Aborting!\n', TTL);
8090         }
8091         // We must run this hook in an apply since the $$postDigest runs outside apply
8092         $rootScope.$apply(function() {
8093           var errors = [];
8094           for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) {
8095             try {
8096               onChangesQueue[i]();
8097             } catch (e) {
8098               errors.push(e);
8099             }
8100           }
8101           // Reset the queue to trigger a new schedule next time there is a change
8102           onChangesQueue = undefined;
8103           if (errors.length) {
8104             throw errors;
8105           }
8106         });
8107       } finally {
8108         onChangesTtl++;
8109       }
8110     }
8113     function Attributes(element, attributesToCopy) {
8114       if (attributesToCopy) {
8115         var keys = Object.keys(attributesToCopy);
8116         var i, l, key;
8118         for (i = 0, l = keys.length; i < l; i++) {
8119           key = keys[i];
8120           this[key] = attributesToCopy[key];
8121         }
8122       } else {
8123         this.$attr = {};
8124       }
8126       this.$$element = element;
8127     }
8129     Attributes.prototype = {
8130       /**
8131        * @ngdoc method
8132        * @name $compile.directive.Attributes#$normalize
8133        * @kind function
8134        *
8135        * @description
8136        * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
8137        * `data-`) to its normalized, camelCase form.
8138        *
8139        * Also there is special case for Moz prefix starting with upper case letter.
8140        *
8141        * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
8142        *
8143        * @param {string} name Name to normalize
8144        */
8145       $normalize: directiveNormalize,
8148       /**
8149        * @ngdoc method
8150        * @name $compile.directive.Attributes#$addClass
8151        * @kind function
8152        *
8153        * @description
8154        * Adds the CSS class value specified by the classVal parameter to the element. If animations
8155        * are enabled then an animation will be triggered for the class addition.
8156        *
8157        * @param {string} classVal The className value that will be added to the element
8158        */
8159       $addClass: function(classVal) {
8160         if (classVal && classVal.length > 0) {
8161           $animate.addClass(this.$$element, classVal);
8162         }
8163       },
8165       /**
8166        * @ngdoc method
8167        * @name $compile.directive.Attributes#$removeClass
8168        * @kind function
8169        *
8170        * @description
8171        * Removes the CSS class value specified by the classVal parameter from the element. If
8172        * animations are enabled then an animation will be triggered for the class removal.
8173        *
8174        * @param {string} classVal The className value that will be removed from the element
8175        */
8176       $removeClass: function(classVal) {
8177         if (classVal && classVal.length > 0) {
8178           $animate.removeClass(this.$$element, classVal);
8179         }
8180       },
8182       /**
8183        * @ngdoc method
8184        * @name $compile.directive.Attributes#$updateClass
8185        * @kind function
8186        *
8187        * @description
8188        * Adds and removes the appropriate CSS class values to the element based on the difference
8189        * between the new and old CSS class values (specified as newClasses and oldClasses).
8190        *
8191        * @param {string} newClasses The current CSS className value
8192        * @param {string} oldClasses The former CSS className value
8193        */
8194       $updateClass: function(newClasses, oldClasses) {
8195         var toAdd = tokenDifference(newClasses, oldClasses);
8196         if (toAdd && toAdd.length) {
8197           $animate.addClass(this.$$element, toAdd);
8198         }
8200         var toRemove = tokenDifference(oldClasses, newClasses);
8201         if (toRemove && toRemove.length) {
8202           $animate.removeClass(this.$$element, toRemove);
8203         }
8204       },
8206       /**
8207        * Set a normalized attribute on the element in a way such that all directives
8208        * can share the attribute. This function properly handles boolean attributes.
8209        * @param {string} key Normalized key. (ie ngAttribute)
8210        * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
8211        * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
8212        *     Defaults to true.
8213        * @param {string=} attrName Optional none normalized name. Defaults to key.
8214        */
8215       $set: function(key, value, writeAttr, attrName) {
8216         // TODO: decide whether or not to throw an error if "class"
8217         //is set through this function since it may cause $updateClass to
8218         //become unstable.
8220         var node = this.$$element[0],
8221             booleanKey = getBooleanAttrName(node, key),
8222             aliasedKey = getAliasedAttrName(key),
8223             observer = key,
8224             nodeName;
8226         if (booleanKey) {
8227           this.$$element.prop(key, value);
8228           attrName = booleanKey;
8229         } else if (aliasedKey) {
8230           this[aliasedKey] = value;
8231           observer = aliasedKey;
8232         }
8234         this[key] = value;
8236         // translate normalized key to actual key
8237         if (attrName) {
8238           this.$attr[key] = attrName;
8239         } else {
8240           attrName = this.$attr[key];
8241           if (!attrName) {
8242             this.$attr[key] = attrName = snake_case(key, '-');
8243           }
8244         }
8246         nodeName = nodeName_(this.$$element);
8248         if ((nodeName === 'a' && (key === 'href' || key === 'xlinkHref')) ||
8249             (nodeName === 'img' && key === 'src')) {
8250           // sanitize a[href] and img[src] values
8251           this[key] = value = $$sanitizeUri(value, key === 'src');
8252         } else if (nodeName === 'img' && key === 'srcset' && isDefined(value)) {
8253           // sanitize img[srcset] values
8254           var result = "";
8256           // first check if there are spaces because it's not the same pattern
8257           var trimmedSrcset = trim(value);
8258           //                (   999x   ,|   999w   ,|   ,|,   )
8259           var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
8260           var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
8262           // split srcset into tuple of uri and descriptor except for the last item
8263           var rawUris = trimmedSrcset.split(pattern);
8265           // for each tuples
8266           var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
8267           for (var i = 0; i < nbrUrisWith2parts; i++) {
8268             var innerIdx = i * 2;
8269             // sanitize the uri
8270             result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
8271             // add the descriptor
8272             result += (" " + trim(rawUris[innerIdx + 1]));
8273           }
8275           // split the last item into uri and descriptor
8276           var lastTuple = trim(rawUris[i * 2]).split(/\s/);
8278           // sanitize the last uri
8279           result += $$sanitizeUri(trim(lastTuple[0]), true);
8281           // and add the last descriptor if any
8282           if (lastTuple.length === 2) {
8283             result += (" " + trim(lastTuple[1]));
8284           }
8285           this[key] = value = result;
8286         }
8288         if (writeAttr !== false) {
8289           if (value === null || isUndefined(value)) {
8290             this.$$element.removeAttr(attrName);
8291           } else {
8292             if (SIMPLE_ATTR_NAME.test(attrName)) {
8293               this.$$element.attr(attrName, value);
8294             } else {
8295               setSpecialAttr(this.$$element[0], attrName, value);
8296             }
8297           }
8298         }
8300         // fire observers
8301         var $$observers = this.$$observers;
8302         $$observers && forEach($$observers[observer], function(fn) {
8303           try {
8304             fn(value);
8305           } catch (e) {
8306             $exceptionHandler(e);
8307           }
8308         });
8309       },
8312       /**
8313        * @ngdoc method
8314        * @name $compile.directive.Attributes#$observe
8315        * @kind function
8316        *
8317        * @description
8318        * Observes an interpolated attribute.
8319        *
8320        * The observer function will be invoked once during the next `$digest` following
8321        * compilation. The observer is then invoked whenever the interpolated value
8322        * changes.
8323        *
8324        * @param {string} key Normalized key. (ie ngAttribute) .
8325        * @param {function(interpolatedValue)} fn Function that will be called whenever
8326                 the interpolated value of the attribute changes.
8327        *        See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation
8328        *        guide} for more info.
8329        * @returns {function()} Returns a deregistration function for this observer.
8330        */
8331       $observe: function(key, fn) {
8332         var attrs = this,
8333             $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
8334             listeners = ($$observers[key] || ($$observers[key] = []));
8336         listeners.push(fn);
8337         $rootScope.$evalAsync(function() {
8338           if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
8339             // no one registered attribute interpolation function, so lets call it manually
8340             fn(attrs[key]);
8341           }
8342         });
8344         return function() {
8345           arrayRemove(listeners, fn);
8346         };
8347       }
8348     };
8350     function setSpecialAttr(element, attrName, value) {
8351       // Attributes names that do not start with letters (such as `(click)`) cannot be set using `setAttribute`
8352       // so we have to jump through some hoops to get such an attribute
8353       // https://github.com/angular/angular.js/pull/13318
8354       specialAttrHolder.innerHTML = "<span " + attrName + ">";
8355       var attributes = specialAttrHolder.firstChild.attributes;
8356       var attribute = attributes[0];
8357       // We have to remove the attribute from its container element before we can add it to the destination element
8358       attributes.removeNamedItem(attribute.name);
8359       attribute.value = value;
8360       element.attributes.setNamedItem(attribute);
8361     }
8363     function safeAddClass($element, className) {
8364       try {
8365         $element.addClass(className);
8366       } catch (e) {
8367         // ignore, since it means that we are trying to set class on
8368         // SVG element, where class name is read-only.
8369       }
8370     }
8373     var startSymbol = $interpolate.startSymbol(),
8374         endSymbol = $interpolate.endSymbol(),
8375         denormalizeTemplate = (startSymbol == '{{' && endSymbol  == '}}')
8376             ? identity
8377             : function denormalizeTemplate(template) {
8378               return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
8379         },
8380         NG_ATTR_BINDING = /^ngAttr[A-Z]/;
8381     var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
8383     compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
8384       var bindings = $element.data('$binding') || [];
8386       if (isArray(binding)) {
8387         bindings = bindings.concat(binding);
8388       } else {
8389         bindings.push(binding);
8390       }
8392       $element.data('$binding', bindings);
8393     } : noop;
8395     compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
8396       safeAddClass($element, 'ng-binding');
8397     } : noop;
8399     compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
8400       var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
8401       $element.data(dataName, scope);
8402     } : noop;
8404     compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
8405       safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
8406     } : noop;
8408     compile.$$createComment = function(directiveName, comment) {
8409       var content = '';
8410       if (debugInfoEnabled) {
8411         content = ' ' + (directiveName || '') + ': ';
8412         if (comment) content += comment + ' ';
8413       }
8414       return window.document.createComment(content);
8415     };
8417     return compile;
8419     //================================
8421     function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
8422                         previousCompileContext) {
8423       if (!($compileNodes instanceof jqLite)) {
8424         // jquery always rewraps, whereas we need to preserve the original selector so that we can
8425         // modify it.
8426         $compileNodes = jqLite($compileNodes);
8427       }
8429       var NOT_EMPTY = /\S+/;
8431       // We can not compile top level text elements since text nodes can be merged and we will
8432       // not be able to attach scope data to them, so we will wrap them in <span>
8433       for (var i = 0, len = $compileNodes.length; i < len; i++) {
8434         var domNode = $compileNodes[i];
8436         if (domNode.nodeType === NODE_TYPE_TEXT && domNode.nodeValue.match(NOT_EMPTY) /* non-empty */) {
8437           jqLiteWrapNode(domNode, $compileNodes[i] = window.document.createElement('span'));
8438         }
8439       }
8441       var compositeLinkFn =
8442               compileNodes($compileNodes, transcludeFn, $compileNodes,
8443                            maxPriority, ignoreDirective, previousCompileContext);
8444       compile.$$addScopeClass($compileNodes);
8445       var namespace = null;
8446       return function publicLinkFn(scope, cloneConnectFn, options) {
8447         assertArg(scope, 'scope');
8449         if (previousCompileContext && previousCompileContext.needsNewScope) {
8450           // A parent directive did a replace and a directive on this element asked
8451           // for transclusion, which caused us to lose a layer of element on which
8452           // we could hold the new transclusion scope, so we will create it manually
8453           // here.
8454           scope = scope.$parent.$new();
8455         }
8457         options = options || {};
8458         var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
8459           transcludeControllers = options.transcludeControllers,
8460           futureParentElement = options.futureParentElement;
8462         // When `parentBoundTranscludeFn` is passed, it is a
8463         // `controllersBoundTransclude` function (it was previously passed
8464         // as `transclude` to directive.link) so we must unwrap it to get
8465         // its `boundTranscludeFn`
8466         if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
8467           parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
8468         }
8470         if (!namespace) {
8471           namespace = detectNamespaceForChildElements(futureParentElement);
8472         }
8473         var $linkNode;
8474         if (namespace !== 'html') {
8475           // When using a directive with replace:true and templateUrl the $compileNodes
8476           // (or a child element inside of them)
8477           // might change, so we need to recreate the namespace adapted compileNodes
8478           // for call to the link function.
8479           // Note: This will already clone the nodes...
8480           $linkNode = jqLite(
8481             wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
8482           );
8483         } else if (cloneConnectFn) {
8484           // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
8485           // and sometimes changes the structure of the DOM.
8486           $linkNode = JQLitePrototype.clone.call($compileNodes);
8487         } else {
8488           $linkNode = $compileNodes;
8489         }
8491         if (transcludeControllers) {
8492           for (var controllerName in transcludeControllers) {
8493             $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
8494           }
8495         }
8497         compile.$$addScopeInfo($linkNode, scope);
8499         if (cloneConnectFn) cloneConnectFn($linkNode, scope);
8500         if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
8501         return $linkNode;
8502       };
8503     }
8505     function detectNamespaceForChildElements(parentElement) {
8506       // TODO: Make this detect MathML as well...
8507       var node = parentElement && parentElement[0];
8508       if (!node) {
8509         return 'html';
8510       } else {
8511         return nodeName_(node) !== 'foreignobject' && toString.call(node).match(/SVG/) ? 'svg' : 'html';
8512       }
8513     }
8515     /**
8516      * Compile function matches each node in nodeList against the directives. Once all directives
8517      * for a particular node are collected their compile functions are executed. The compile
8518      * functions return values - the linking functions - are combined into a composite linking
8519      * function, which is the a linking function for the node.
8520      *
8521      * @param {NodeList} nodeList an array of nodes or NodeList to compile
8522      * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
8523      *        scope argument is auto-generated to the new child of the transcluded parent scope.
8524      * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
8525      *        the rootElement must be set the jqLite collection of the compile root. This is
8526      *        needed so that the jqLite collection items can be replaced with widgets.
8527      * @param {number=} maxPriority Max directive priority.
8528      * @returns {Function} A composite linking function of all of the matched directives or null.
8529      */
8530     function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
8531                             previousCompileContext) {
8532       var linkFns = [],
8533           attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
8535       for (var i = 0; i < nodeList.length; i++) {
8536         attrs = new Attributes();
8538         // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
8539         directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
8540                                         ignoreDirective);
8542         nodeLinkFn = (directives.length)
8543             ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
8544                                       null, [], [], previousCompileContext)
8545             : null;
8547         if (nodeLinkFn && nodeLinkFn.scope) {
8548           compile.$$addScopeClass(attrs.$$element);
8549         }
8551         childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
8552                       !(childNodes = nodeList[i].childNodes) ||
8553                       !childNodes.length)
8554             ? null
8555             : compileNodes(childNodes,
8556                  nodeLinkFn ? (
8557                   (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
8558                      && nodeLinkFn.transclude) : transcludeFn);
8560         if (nodeLinkFn || childLinkFn) {
8561           linkFns.push(i, nodeLinkFn, childLinkFn);
8562           linkFnFound = true;
8563           nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
8564         }
8566         //use the previous context only for the first element in the virtual group
8567         previousCompileContext = null;
8568       }
8570       // return a linking function if we have found anything, null otherwise
8571       return linkFnFound ? compositeLinkFn : null;
8573       function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
8574         var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
8575         var stableNodeList;
8578         if (nodeLinkFnFound) {
8579           // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
8580           // offsets don't get screwed up
8581           var nodeListLength = nodeList.length;
8582           stableNodeList = new Array(nodeListLength);
8584           // create a sparse array by only copying the elements which have a linkFn
8585           for (i = 0; i < linkFns.length; i+=3) {
8586             idx = linkFns[i];
8587             stableNodeList[idx] = nodeList[idx];
8588           }
8589         } else {
8590           stableNodeList = nodeList;
8591         }
8593         for (i = 0, ii = linkFns.length; i < ii;) {
8594           node = stableNodeList[linkFns[i++]];
8595           nodeLinkFn = linkFns[i++];
8596           childLinkFn = linkFns[i++];
8598           if (nodeLinkFn) {
8599             if (nodeLinkFn.scope) {
8600               childScope = scope.$new();
8601               compile.$$addScopeInfo(jqLite(node), childScope);
8602             } else {
8603               childScope = scope;
8604             }
8606             if (nodeLinkFn.transcludeOnThisElement) {
8607               childBoundTranscludeFn = createBoundTranscludeFn(
8608                   scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
8610             } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
8611               childBoundTranscludeFn = parentBoundTranscludeFn;
8613             } else if (!parentBoundTranscludeFn && transcludeFn) {
8614               childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
8616             } else {
8617               childBoundTranscludeFn = null;
8618             }
8620             nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
8622           } else if (childLinkFn) {
8623             childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
8624           }
8625         }
8626       }
8627     }
8629     function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
8630       function boundTranscludeFn(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
8632         if (!transcludedScope) {
8633           transcludedScope = scope.$new(false, containingScope);
8634           transcludedScope.$$transcluded = true;
8635         }
8637         return transcludeFn(transcludedScope, cloneFn, {
8638           parentBoundTranscludeFn: previousBoundTranscludeFn,
8639           transcludeControllers: controllers,
8640           futureParentElement: futureParentElement
8641         });
8642       }
8644       // We need  to attach the transclusion slots onto the `boundTranscludeFn`
8645       // so that they are available inside the `controllersBoundTransclude` function
8646       var boundSlots = boundTranscludeFn.$$slots = createMap();
8647       for (var slotName in transcludeFn.$$slots) {
8648         if (transcludeFn.$$slots[slotName]) {
8649           boundSlots[slotName] = createBoundTranscludeFn(scope, transcludeFn.$$slots[slotName], previousBoundTranscludeFn);
8650         } else {
8651           boundSlots[slotName] = null;
8652         }
8653       }
8655       return boundTranscludeFn;
8656     }
8658     /**
8659      * Looks for directives on the given node and adds them to the directive collection which is
8660      * sorted.
8661      *
8662      * @param node Node to search.
8663      * @param directives An array to which the directives are added to. This array is sorted before
8664      *        the function returns.
8665      * @param attrs The shared attrs object which is used to populate the normalized attributes.
8666      * @param {number=} maxPriority Max directive priority.
8667      */
8668     function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
8669       var nodeType = node.nodeType,
8670           attrsMap = attrs.$attr,
8671           match,
8672           className;
8674       switch (nodeType) {
8675         case NODE_TYPE_ELEMENT: /* Element */
8676           // use the node name: <directive>
8677           addDirective(directives,
8678               directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
8680           // iterate over the attributes
8681           for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
8682                    j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
8683             var attrStartName = false;
8684             var attrEndName = false;
8686             attr = nAttrs[j];
8687             name = attr.name;
8688             value = trim(attr.value);
8690             // support ngAttr attribute binding
8691             ngAttrName = directiveNormalize(name);
8692             if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
8693               name = name.replace(PREFIX_REGEXP, '')
8694                 .substr(8).replace(/_(.)/g, function(match, letter) {
8695                   return letter.toUpperCase();
8696                 });
8697             }
8699             var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
8700             if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
8701               attrStartName = name;
8702               attrEndName = name.substr(0, name.length - 5) + 'end';
8703               name = name.substr(0, name.length - 6);
8704             }
8706             nName = directiveNormalize(name.toLowerCase());
8707             attrsMap[nName] = name;
8708             if (isNgAttr || !attrs.hasOwnProperty(nName)) {
8709                 attrs[nName] = value;
8710                 if (getBooleanAttrName(node, nName)) {
8711                   attrs[nName] = true; // presence means true
8712                 }
8713             }
8714             addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
8715             addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
8716                           attrEndName);
8717           }
8719           // use class as directive
8720           className = node.className;
8721           if (isObject(className)) {
8722               // Maybe SVGAnimatedString
8723               className = className.animVal;
8724           }
8725           if (isString(className) && className !== '') {
8726             while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
8727               nName = directiveNormalize(match[2]);
8728               if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
8729                 attrs[nName] = trim(match[3]);
8730               }
8731               className = className.substr(match.index + match[0].length);
8732             }
8733           }
8734           break;
8735         case NODE_TYPE_TEXT: /* Text Node */
8736           if (msie === 11) {
8737             // Workaround for #11781
8738             while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
8739               node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
8740               node.parentNode.removeChild(node.nextSibling);
8741             }
8742           }
8743           addTextInterpolateDirective(directives, node.nodeValue);
8744           break;
8745         case NODE_TYPE_COMMENT: /* Comment */
8746           collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective);
8747           break;
8748       }
8750       directives.sort(byPriority);
8751       return directives;
8752     }
8754     function collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
8755       // function created because of performance, try/catch disables
8756       // the optimization of the whole function #14848
8757       try {
8758         var match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
8759         if (match) {
8760           var nName = directiveNormalize(match[1]);
8761           if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
8762             attrs[nName] = trim(match[2]);
8763           }
8764         }
8765       } catch (e) {
8766         // turns out that under some circumstances IE9 throws errors when one attempts to read
8767         // comment's node value.
8768         // Just ignore it and continue. (Can't seem to reproduce in test case.)
8769       }
8770     }
8772     /**
8773      * Given a node with an directive-start it collects all of the siblings until it finds
8774      * directive-end.
8775      * @param node
8776      * @param attrStart
8777      * @param attrEnd
8778      * @returns {*}
8779      */
8780     function groupScan(node, attrStart, attrEnd) {
8781       var nodes = [];
8782       var depth = 0;
8783       if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
8784         do {
8785           if (!node) {
8786             throw $compileMinErr('uterdir',
8787                       "Unterminated attribute, found '{0}' but no matching '{1}' found.",
8788                       attrStart, attrEnd);
8789           }
8790           if (node.nodeType == NODE_TYPE_ELEMENT) {
8791             if (node.hasAttribute(attrStart)) depth++;
8792             if (node.hasAttribute(attrEnd)) depth--;
8793           }
8794           nodes.push(node);
8795           node = node.nextSibling;
8796         } while (depth > 0);
8797       } else {
8798         nodes.push(node);
8799       }
8801       return jqLite(nodes);
8802     }
8804     /**
8805      * Wrapper for linking function which converts normal linking function into a grouped
8806      * linking function.
8807      * @param linkFn
8808      * @param attrStart
8809      * @param attrEnd
8810      * @returns {Function}
8811      */
8812     function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
8813       return function groupedElementsLink(scope, element, attrs, controllers, transcludeFn) {
8814         element = groupScan(element[0], attrStart, attrEnd);
8815         return linkFn(scope, element, attrs, controllers, transcludeFn);
8816       };
8817     }
8819     /**
8820      * A function generator that is used to support both eager and lazy compilation
8821      * linking function.
8822      * @param eager
8823      * @param $compileNodes
8824      * @param transcludeFn
8825      * @param maxPriority
8826      * @param ignoreDirective
8827      * @param previousCompileContext
8828      * @returns {Function}
8829      */
8830     function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) {
8831       var compiled;
8833       if (eager) {
8834         return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
8835       }
8836       return function lazyCompilation() {
8837         if (!compiled) {
8838           compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
8840           // Null out all of these references in order to make them eligible for garbage collection
8841           // since this is a potentially long lived closure
8842           $compileNodes = transcludeFn = previousCompileContext = null;
8843         }
8844         return compiled.apply(this, arguments);
8845       };
8846     }
8848     /**
8849      * Once the directives have been collected, their compile functions are executed. This method
8850      * is responsible for inlining directive templates as well as terminating the application
8851      * of the directives if the terminal directive has been reached.
8852      *
8853      * @param {Array} directives Array of collected directives to execute their compile function.
8854      *        this needs to be pre-sorted by priority order.
8855      * @param {Node} compileNode The raw DOM node to apply the compile functions to
8856      * @param {Object} templateAttrs The shared attribute function
8857      * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
8858      *                                                  scope argument is auto-generated to the new
8859      *                                                  child of the transcluded parent scope.
8860      * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
8861      *                              argument has the root jqLite array so that we can replace nodes
8862      *                              on it.
8863      * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
8864      *                                           compiling the transclusion.
8865      * @param {Array.<Function>} preLinkFns
8866      * @param {Array.<Function>} postLinkFns
8867      * @param {Object} previousCompileContext Context used for previous compilation of the current
8868      *                                        node
8869      * @returns {Function} linkFn
8870      */
8871     function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
8872                                    jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
8873                                    previousCompileContext) {
8874       previousCompileContext = previousCompileContext || {};
8876       var terminalPriority = -Number.MAX_VALUE,
8877           newScopeDirective = previousCompileContext.newScopeDirective,
8878           controllerDirectives = previousCompileContext.controllerDirectives,
8879           newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
8880           templateDirective = previousCompileContext.templateDirective,
8881           nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
8882           hasTranscludeDirective = false,
8883           hasTemplate = false,
8884           hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
8885           $compileNode = templateAttrs.$$element = jqLite(compileNode),
8886           directive,
8887           directiveName,
8888           $template,
8889           replaceDirective = originalReplaceDirective,
8890           childTranscludeFn = transcludeFn,
8891           linkFn,
8892           didScanForMultipleTransclusion = false,
8893           mightHaveMultipleTransclusionError = false,
8894           directiveValue;
8896       // executes all directives on the current element
8897       for (var i = 0, ii = directives.length; i < ii; i++) {
8898         directive = directives[i];
8899         var attrStart = directive.$$start;
8900         var attrEnd = directive.$$end;
8902         // collect multiblock sections
8903         if (attrStart) {
8904           $compileNode = groupScan(compileNode, attrStart, attrEnd);
8905         }
8906         $template = undefined;
8908         if (terminalPriority > directive.priority) {
8909           break; // prevent further processing of directives
8910         }
8912         if (directiveValue = directive.scope) {
8914           // skip the check for directives with async templates, we'll check the derived sync
8915           // directive when the template arrives
8916           if (!directive.templateUrl) {
8917             if (isObject(directiveValue)) {
8918               // This directive is trying to add an isolated scope.
8919               // Check that there is no scope of any kind already
8920               assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
8921                                 directive, $compileNode);
8922               newIsolateScopeDirective = directive;
8923             } else {
8924               // This directive is trying to add a child scope.
8925               // Check that there is no isolated scope already
8926               assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
8927                                 $compileNode);
8928             }
8929           }
8931           newScopeDirective = newScopeDirective || directive;
8932         }
8934         directiveName = directive.name;
8936         // If we encounter a condition that can result in transclusion on the directive,
8937         // then scan ahead in the remaining directives for others that may cause a multiple
8938         // transclusion error to be thrown during the compilation process.  If a matching directive
8939         // is found, then we know that when we encounter a transcluded directive, we need to eagerly
8940         // compile the `transclude` function rather than doing it lazily in order to throw
8941         // exceptions at the correct time
8942         if (!didScanForMultipleTransclusion && ((directive.replace && (directive.templateUrl || directive.template))
8943             || (directive.transclude && !directive.$$tlb))) {
8944                 var candidateDirective;
8946                 for (var scanningIndex = i + 1; candidateDirective = directives[scanningIndex++];) {
8947                     if ((candidateDirective.transclude && !candidateDirective.$$tlb)
8948                         || (candidateDirective.replace && (candidateDirective.templateUrl || candidateDirective.template))) {
8949                         mightHaveMultipleTransclusionError = true;
8950                         break;
8951                     }
8952                 }
8954                 didScanForMultipleTransclusion = true;
8955         }
8957         if (!directive.templateUrl && directive.controller) {
8958           directiveValue = directive.controller;
8959           controllerDirectives = controllerDirectives || createMap();
8960           assertNoDuplicate("'" + directiveName + "' controller",
8961               controllerDirectives[directiveName], directive, $compileNode);
8962           controllerDirectives[directiveName] = directive;
8963         }
8965         if (directiveValue = directive.transclude) {
8966           hasTranscludeDirective = true;
8968           // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
8969           // This option should only be used by directives that know how to safely handle element transclusion,
8970           // where the transcluded nodes are added or replaced after linking.
8971           if (!directive.$$tlb) {
8972             assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
8973             nonTlbTranscludeDirective = directive;
8974           }
8976           if (directiveValue == 'element') {
8977             hasElementTranscludeDirective = true;
8978             terminalPriority = directive.priority;
8979             $template = $compileNode;
8980             $compileNode = templateAttrs.$$element =
8981                 jqLite(compile.$$createComment(directiveName, templateAttrs[directiveName]));
8982             compileNode = $compileNode[0];
8983             replaceWith(jqCollection, sliceArgs($template), compileNode);
8985             // Support: Chrome < 50
8986             // https://github.com/angular/angular.js/issues/14041
8988             // In the versions of V8 prior to Chrome 50, the document fragment that is created
8989             // in the `replaceWith` function is improperly garbage collected despite still
8990             // being referenced by the `parentNode` property of all of the child nodes.  By adding
8991             // a reference to the fragment via a different property, we can avoid that incorrect
8992             // behavior.
8993             // TODO: remove this line after Chrome 50 has been released
8994             $template[0].$$parentNode = $template[0].parentNode;
8996             childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority,
8997                                         replaceDirective && replaceDirective.name, {
8998                                           // Don't pass in:
8999                                           // - controllerDirectives - otherwise we'll create duplicates controllers
9000                                           // - newIsolateScopeDirective or templateDirective - combining templates with
9001                                           //   element transclusion doesn't make sense.
9002                                           //
9003                                           // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
9004                                           // on the same element more than once.
9005                                           nonTlbTranscludeDirective: nonTlbTranscludeDirective
9006                                         });
9007           } else {
9009             var slots = createMap();
9011             $template = jqLite(jqLiteClone(compileNode)).contents();
9013             if (isObject(directiveValue)) {
9015               // We have transclusion slots,
9016               // collect them up, compile them and store their transclusion functions
9017               $template = [];
9019               var slotMap = createMap();
9020               var filledSlots = createMap();
9022               // Parse the element selectors
9023               forEach(directiveValue, function(elementSelector, slotName) {
9024                 // If an element selector starts with a ? then it is optional
9025                 var optional = (elementSelector.charAt(0) === '?');
9026                 elementSelector = optional ? elementSelector.substring(1) : elementSelector;
9028                 slotMap[elementSelector] = slotName;
9030                 // We explicitly assign `null` since this implies that a slot was defined but not filled.
9031                 // Later when calling boundTransclusion functions with a slot name we only error if the
9032                 // slot is `undefined`
9033                 slots[slotName] = null;
9035                 // filledSlots contains `true` for all slots that are either optional or have been
9036                 // filled. This is used to check that we have not missed any required slots
9037                 filledSlots[slotName] = optional;
9038               });
9040               // Add the matching elements into their slot
9041               forEach($compileNode.contents(), function(node) {
9042                 var slotName = slotMap[directiveNormalize(nodeName_(node))];
9043                 if (slotName) {
9044                   filledSlots[slotName] = true;
9045                   slots[slotName] = slots[slotName] || [];
9046                   slots[slotName].push(node);
9047                 } else {
9048                   $template.push(node);
9049                 }
9050               });
9052               // Check for required slots that were not filled
9053               forEach(filledSlots, function(filled, slotName) {
9054                 if (!filled) {
9055                   throw $compileMinErr('reqslot', 'Required transclusion slot `{0}` was not filled.', slotName);
9056                 }
9057               });
9059               for (var slotName in slots) {
9060                 if (slots[slotName]) {
9061                   // Only define a transclusion function if the slot was filled
9062                   slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName], transcludeFn);
9063                 }
9064               }
9065             }
9067             $compileNode.empty(); // clear contents
9068             childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, undefined,
9069                 undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
9070             childTranscludeFn.$$slots = slots;
9071           }
9072         }
9074         if (directive.template) {
9075           hasTemplate = true;
9076           assertNoDuplicate('template', templateDirective, directive, $compileNode);
9077           templateDirective = directive;
9079           directiveValue = (isFunction(directive.template))
9080               ? directive.template($compileNode, templateAttrs)
9081               : directive.template;
9083           directiveValue = denormalizeTemplate(directiveValue);
9085           if (directive.replace) {
9086             replaceDirective = directive;
9087             if (jqLiteIsTextNode(directiveValue)) {
9088               $template = [];
9089             } else {
9090               $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
9091             }
9092             compileNode = $template[0];
9094             if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
9095               throw $compileMinErr('tplrt',
9096                   "Template for directive '{0}' must have exactly one root element. {1}",
9097                   directiveName, '');
9098             }
9100             replaceWith(jqCollection, $compileNode, compileNode);
9102             var newTemplateAttrs = {$attr: {}};
9104             // combine directives from the original node and from the template:
9105             // - take the array of directives for this element
9106             // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
9107             // - collect directives from the template and sort them by priority
9108             // - combine directives as: processed + template + unprocessed
9109             var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
9110             var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
9112             if (newIsolateScopeDirective || newScopeDirective) {
9113               // The original directive caused the current element to be replaced but this element
9114               // also needs to have a new scope, so we need to tell the template directives
9115               // that they would need to get their scope from further up, if they require transclusion
9116               markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
9117             }
9118             directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
9119             mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
9121             ii = directives.length;
9122           } else {
9123             $compileNode.html(directiveValue);
9124           }
9125         }
9127         if (directive.templateUrl) {
9128           hasTemplate = true;
9129           assertNoDuplicate('template', templateDirective, directive, $compileNode);
9130           templateDirective = directive;
9132           if (directive.replace) {
9133             replaceDirective = directive;
9134           }
9136           /* jshint -W021 */
9137           nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
9138           /* jshint +W021 */
9139               templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
9140                 controllerDirectives: controllerDirectives,
9141                 newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
9142                 newIsolateScopeDirective: newIsolateScopeDirective,
9143                 templateDirective: templateDirective,
9144                 nonTlbTranscludeDirective: nonTlbTranscludeDirective
9145               });
9146           ii = directives.length;
9147         } else if (directive.compile) {
9148           try {
9149             linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
9150             var context = directive.$$originalDirective || directive;
9151             if (isFunction(linkFn)) {
9152               addLinkFns(null, bind(context, linkFn), attrStart, attrEnd);
9153             } else if (linkFn) {
9154               addLinkFns(bind(context, linkFn.pre), bind(context, linkFn.post), attrStart, attrEnd);
9155             }
9156           } catch (e) {
9157             $exceptionHandler(e, startingTag($compileNode));
9158           }
9159         }
9161         if (directive.terminal) {
9162           nodeLinkFn.terminal = true;
9163           terminalPriority = Math.max(terminalPriority, directive.priority);
9164         }
9166       }
9168       nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
9169       nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
9170       nodeLinkFn.templateOnThisElement = hasTemplate;
9171       nodeLinkFn.transclude = childTranscludeFn;
9173       previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
9175       // might be normal or delayed nodeLinkFn depending on if templateUrl is present
9176       return nodeLinkFn;
9178       ////////////////////
9180       function addLinkFns(pre, post, attrStart, attrEnd) {
9181         if (pre) {
9182           if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
9183           pre.require = directive.require;
9184           pre.directiveName = directiveName;
9185           if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
9186             pre = cloneAndAnnotateFn(pre, {isolateScope: true});
9187           }
9188           preLinkFns.push(pre);
9189         }
9190         if (post) {
9191           if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
9192           post.require = directive.require;
9193           post.directiveName = directiveName;
9194           if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
9195             post = cloneAndAnnotateFn(post, {isolateScope: true});
9196           }
9197           postLinkFns.push(post);
9198         }
9199       }
9201       function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
9202         var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
9203             attrs, scopeBindingInfo;
9205         if (compileNode === linkNode) {
9206           attrs = templateAttrs;
9207           $element = templateAttrs.$$element;
9208         } else {
9209           $element = jqLite(linkNode);
9210           attrs = new Attributes($element, templateAttrs);
9211         }
9213         controllerScope = scope;
9214         if (newIsolateScopeDirective) {
9215           isolateScope = scope.$new(true);
9216         } else if (newScopeDirective) {
9217           controllerScope = scope.$parent;
9218         }
9220         if (boundTranscludeFn) {
9221           // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
9222           // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
9223           transcludeFn = controllersBoundTransclude;
9224           transcludeFn.$$boundTransclude = boundTranscludeFn;
9225           // expose the slots on the `$transclude` function
9226           transcludeFn.isSlotFilled = function(slotName) {
9227             return !!boundTranscludeFn.$$slots[slotName];
9228           };
9229         }
9231         if (controllerDirectives) {
9232           elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective);
9233         }
9235         if (newIsolateScopeDirective) {
9236           // Initialize isolate scope bindings for new isolate scope directive.
9237           compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
9238               templateDirective === newIsolateScopeDirective.$$originalDirective)));
9239           compile.$$addScopeClass($element, true);
9240           isolateScope.$$isolateBindings =
9241               newIsolateScopeDirective.$$isolateBindings;
9242           scopeBindingInfo = initializeDirectiveBindings(scope, attrs, isolateScope,
9243                                         isolateScope.$$isolateBindings,
9244                                         newIsolateScopeDirective);
9245           if (scopeBindingInfo.removeWatches) {
9246             isolateScope.$on('$destroy', scopeBindingInfo.removeWatches);
9247           }
9248         }
9250         // Initialize bindToController bindings
9251         for (var name in elementControllers) {
9252           var controllerDirective = controllerDirectives[name];
9253           var controller = elementControllers[name];
9254           var bindings = controllerDirective.$$bindings.bindToController;
9256           if (controller.identifier && bindings) {
9257             controller.bindingInfo =
9258               initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
9259           } else {
9260             controller.bindingInfo = {};
9261           }
9263           var controllerResult = controller();
9264           if (controllerResult !== controller.instance) {
9265             // If the controller constructor has a return value, overwrite the instance
9266             // from setupControllers
9267             controller.instance = controllerResult;
9268             $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
9269             controller.bindingInfo.removeWatches && controller.bindingInfo.removeWatches();
9270             controller.bindingInfo =
9271               initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
9272           }
9273         }
9275         // Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy
9276         forEach(controllerDirectives, function(controllerDirective, name) {
9277           var require = controllerDirective.require;
9278           if (controllerDirective.bindToController && !isArray(require) && isObject(require)) {
9279             extend(elementControllers[name].instance, getControllers(name, require, $element, elementControllers));
9280           }
9281         });
9283         // Handle the init and destroy lifecycle hooks on all controllers that have them
9284         forEach(elementControllers, function(controller) {
9285           var controllerInstance = controller.instance;
9286           if (isFunction(controllerInstance.$onChanges)) {
9287             try {
9288               controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
9289             } catch (e) {
9290               $exceptionHandler(e);
9291             }
9292           }
9293           if (isFunction(controllerInstance.$onInit)) {
9294             try {
9295               controllerInstance.$onInit();
9296             } catch (e) {
9297               $exceptionHandler(e);
9298             }
9299           }
9300           if (isFunction(controllerInstance.$doCheck)) {
9301             controllerScope.$watch(function() { controllerInstance.$doCheck(); });
9302             controllerInstance.$doCheck();
9303           }
9304           if (isFunction(controllerInstance.$onDestroy)) {
9305             controllerScope.$on('$destroy', function callOnDestroyHook() {
9306               controllerInstance.$onDestroy();
9307             });
9308           }
9309         });
9311         // PRELINKING
9312         for (i = 0, ii = preLinkFns.length; i < ii; i++) {
9313           linkFn = preLinkFns[i];
9314           invokeLinkFn(linkFn,
9315               linkFn.isolateScope ? isolateScope : scope,
9316               $element,
9317               attrs,
9318               linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
9319               transcludeFn
9320           );
9321         }
9323         // RECURSION
9324         // We only pass the isolate scope, if the isolate directive has a template,
9325         // otherwise the child elements do not belong to the isolate directive.
9326         var scopeToChild = scope;
9327         if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
9328           scopeToChild = isolateScope;
9329         }
9330         childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
9332         // POSTLINKING
9333         for (i = postLinkFns.length - 1; i >= 0; i--) {
9334           linkFn = postLinkFns[i];
9335           invokeLinkFn(linkFn,
9336               linkFn.isolateScope ? isolateScope : scope,
9337               $element,
9338               attrs,
9339               linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
9340               transcludeFn
9341           );
9342         }
9344         // Trigger $postLink lifecycle hooks
9345         forEach(elementControllers, function(controller) {
9346           var controllerInstance = controller.instance;
9347           if (isFunction(controllerInstance.$postLink)) {
9348             controllerInstance.$postLink();
9349           }
9350         });
9352         // This is the function that is injected as `$transclude`.
9353         // Note: all arguments are optional!
9354         function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) {
9355           var transcludeControllers;
9356           // No scope passed in:
9357           if (!isScope(scope)) {
9358             slotName = futureParentElement;
9359             futureParentElement = cloneAttachFn;
9360             cloneAttachFn = scope;
9361             scope = undefined;
9362           }
9364           if (hasElementTranscludeDirective) {
9365             transcludeControllers = elementControllers;
9366           }
9367           if (!futureParentElement) {
9368             futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
9369           }
9370           if (slotName) {
9371             // slotTranscludeFn can be one of three things:
9372             //  * a transclude function - a filled slot
9373             //  * `null` - an optional slot that was not filled
9374             //  * `undefined` - a slot that was not declared (i.e. invalid)
9375             var slotTranscludeFn = boundTranscludeFn.$$slots[slotName];
9376             if (slotTranscludeFn) {
9377               return slotTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
9378             } else if (isUndefined(slotTranscludeFn)) {
9379               throw $compileMinErr('noslot',
9380                'No parent directive that requires a transclusion with slot name "{0}". ' +
9381                'Element: {1}',
9382                slotName, startingTag($element));
9383             }
9384           } else {
9385             return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
9386           }
9387         }
9388       }
9389     }
9391     function getControllers(directiveName, require, $element, elementControllers) {
9392       var value;
9394       if (isString(require)) {
9395         var match = require.match(REQUIRE_PREFIX_REGEXP);
9396         var name = require.substring(match[0].length);
9397         var inheritType = match[1] || match[3];
9398         var optional = match[2] === '?';
9400         //If only parents then start at the parent element
9401         if (inheritType === '^^') {
9402           $element = $element.parent();
9403         //Otherwise attempt getting the controller from elementControllers in case
9404         //the element is transcluded (and has no data) and to avoid .data if possible
9405         } else {
9406           value = elementControllers && elementControllers[name];
9407           value = value && value.instance;
9408         }
9410         if (!value) {
9411           var dataName = '$' + name + 'Controller';
9412           value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
9413         }
9415         if (!value && !optional) {
9416           throw $compileMinErr('ctreq',
9417               "Controller '{0}', required by directive '{1}', can't be found!",
9418               name, directiveName);
9419         }
9420       } else if (isArray(require)) {
9421         value = [];
9422         for (var i = 0, ii = require.length; i < ii; i++) {
9423           value[i] = getControllers(directiveName, require[i], $element, elementControllers);
9424         }
9425       } else if (isObject(require)) {
9426         value = {};
9427         forEach(require, function(controller, property) {
9428           value[property] = getControllers(directiveName, controller, $element, elementControllers);
9429         });
9430       }
9432       return value || null;
9433     }
9435     function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective) {
9436       var elementControllers = createMap();
9437       for (var controllerKey in controllerDirectives) {
9438         var directive = controllerDirectives[controllerKey];
9439         var locals = {
9440           $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
9441           $element: $element,
9442           $attrs: attrs,
9443           $transclude: transcludeFn
9444         };
9446         var controller = directive.controller;
9447         if (controller == '@') {
9448           controller = attrs[directive.name];
9449         }
9451         var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
9453         // For directives with element transclusion the element is a comment.
9454         // In this case .data will not attach any data.
9455         // Instead, we save the controllers for the element in a local hash and attach to .data
9456         // later, once we have the actual element.
9457         elementControllers[directive.name] = controllerInstance;
9458         $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
9459       }
9460       return elementControllers;
9461     }
9463     // Depending upon the context in which a directive finds itself it might need to have a new isolated
9464     // or child scope created. For instance:
9465     // * if the directive has been pulled into a template because another directive with a higher priority
9466     // asked for element transclusion
9467     // * if the directive itself asks for transclusion but it is at the root of a template and the original
9468     // element was replaced. See https://github.com/angular/angular.js/issues/12936
9469     function markDirectiveScope(directives, isolateScope, newScope) {
9470       for (var j = 0, jj = directives.length; j < jj; j++) {
9471         directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
9472       }
9473     }
9475     /**
9476      * looks up the directive and decorates it with exception handling and proper parameters. We
9477      * call this the boundDirective.
9478      *
9479      * @param {string} name name of the directive to look up.
9480      * @param {string} location The directive must be found in specific format.
9481      *   String containing any of theses characters:
9482      *
9483      *   * `E`: element name
9484      *   * `A': attribute
9485      *   * `C`: class
9486      *   * `M`: comment
9487      * @returns {boolean} true if directive was added.
9488      */
9489     function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
9490                           endAttrName) {
9491       if (name === ignoreDirective) return null;
9492       var match = null;
9493       if (hasDirectives.hasOwnProperty(name)) {
9494         for (var directive, directives = $injector.get(name + Suffix),
9495             i = 0, ii = directives.length; i < ii; i++) {
9496           try {
9497             directive = directives[i];
9498             if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
9499                  directive.restrict.indexOf(location) != -1) {
9500               if (startAttrName) {
9501                 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
9502               }
9503               if (!directive.$$bindings) {
9504                 var bindings = directive.$$bindings =
9505                     parseDirectiveBindings(directive, directive.name);
9506                 if (isObject(bindings.isolateScope)) {
9507                   directive.$$isolateBindings = bindings.isolateScope;
9508                 }
9509               }
9510               tDirectives.push(directive);
9511               match = directive;
9512             }
9513           } catch (e) { $exceptionHandler(e); }
9514         }
9515       }
9516       return match;
9517     }
9520     /**
9521      * looks up the directive and returns true if it is a multi-element directive,
9522      * and therefore requires DOM nodes between -start and -end markers to be grouped
9523      * together.
9524      *
9525      * @param {string} name name of the directive to look up.
9526      * @returns true if directive was registered as multi-element.
9527      */
9528     function directiveIsMultiElement(name) {
9529       if (hasDirectives.hasOwnProperty(name)) {
9530         for (var directive, directives = $injector.get(name + Suffix),
9531             i = 0, ii = directives.length; i < ii; i++) {
9532           directive = directives[i];
9533           if (directive.multiElement) {
9534             return true;
9535           }
9536         }
9537       }
9538       return false;
9539     }
9541     /**
9542      * When the element is replaced with HTML template then the new attributes
9543      * on the template need to be merged with the existing attributes in the DOM.
9544      * The desired effect is to have both of the attributes present.
9545      *
9546      * @param {object} dst destination attributes (original DOM)
9547      * @param {object} src source attributes (from the directive template)
9548      */
9549     function mergeTemplateAttributes(dst, src) {
9550       var srcAttr = src.$attr,
9551           dstAttr = dst.$attr,
9552           $element = dst.$$element;
9554       // reapply the old attributes to the new element
9555       forEach(dst, function(value, key) {
9556         if (key.charAt(0) != '$') {
9557           if (src[key] && src[key] !== value) {
9558             value += (key === 'style' ? ';' : ' ') + src[key];
9559           }
9560           dst.$set(key, value, true, srcAttr[key]);
9561         }
9562       });
9564       // copy the new attributes on the old attrs object
9565       forEach(src, function(value, key) {
9566         // Check if we already set this attribute in the loop above.
9567         // `dst` will never contain hasOwnProperty as DOM parser won't let it.
9568         // You will get an "InvalidCharacterError: DOM Exception 5" error if you
9569         // have an attribute like "has-own-property" or "data-has-own-property", etc.
9570         if (!dst.hasOwnProperty(key) && key.charAt(0) !== '$') {
9571           dst[key] = value;
9573           if (key !== 'class' && key !== 'style') {
9574             dstAttr[key] = srcAttr[key];
9575           }
9576         }
9577       });
9578     }
9581     function compileTemplateUrl(directives, $compileNode, tAttrs,
9582         $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
9583       var linkQueue = [],
9584           afterTemplateNodeLinkFn,
9585           afterTemplateChildLinkFn,
9586           beforeTemplateCompileNode = $compileNode[0],
9587           origAsyncDirective = directives.shift(),
9588           derivedSyncDirective = inherit(origAsyncDirective, {
9589             templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
9590           }),
9591           templateUrl = (isFunction(origAsyncDirective.templateUrl))
9592               ? origAsyncDirective.templateUrl($compileNode, tAttrs)
9593               : origAsyncDirective.templateUrl,
9594           templateNamespace = origAsyncDirective.templateNamespace;
9596       $compileNode.empty();
9598       $templateRequest(templateUrl)
9599         .then(function(content) {
9600           var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
9602           content = denormalizeTemplate(content);
9604           if (origAsyncDirective.replace) {
9605             if (jqLiteIsTextNode(content)) {
9606               $template = [];
9607             } else {
9608               $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
9609             }
9610             compileNode = $template[0];
9612             if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
9613               throw $compileMinErr('tplrt',
9614                   "Template for directive '{0}' must have exactly one root element. {1}",
9615                   origAsyncDirective.name, templateUrl);
9616             }
9618             tempTemplateAttrs = {$attr: {}};
9619             replaceWith($rootElement, $compileNode, compileNode);
9620             var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
9622             if (isObject(origAsyncDirective.scope)) {
9623               // the original directive that caused the template to be loaded async required
9624               // an isolate scope
9625               markDirectiveScope(templateDirectives, true);
9626             }
9627             directives = templateDirectives.concat(directives);
9628             mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
9629           } else {
9630             compileNode = beforeTemplateCompileNode;
9631             $compileNode.html(content);
9632           }
9634           directives.unshift(derivedSyncDirective);
9636           afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
9637               childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
9638               previousCompileContext);
9639           forEach($rootElement, function(node, i) {
9640             if (node == compileNode) {
9641               $rootElement[i] = $compileNode[0];
9642             }
9643           });
9644           afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
9646           while (linkQueue.length) {
9647             var scope = linkQueue.shift(),
9648                 beforeTemplateLinkNode = linkQueue.shift(),
9649                 linkRootElement = linkQueue.shift(),
9650                 boundTranscludeFn = linkQueue.shift(),
9651                 linkNode = $compileNode[0];
9653             if (scope.$$destroyed) continue;
9655             if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
9656               var oldClasses = beforeTemplateLinkNode.className;
9658               if (!(previousCompileContext.hasElementTranscludeDirective &&
9659                   origAsyncDirective.replace)) {
9660                 // it was cloned therefore we have to clone as well.
9661                 linkNode = jqLiteClone(compileNode);
9662               }
9663               replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
9665               // Copy in CSS classes from original node
9666               safeAddClass(jqLite(linkNode), oldClasses);
9667             }
9668             if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
9669               childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
9670             } else {
9671               childBoundTranscludeFn = boundTranscludeFn;
9672             }
9673             afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
9674               childBoundTranscludeFn);
9675           }
9676           linkQueue = null;
9677         });
9679       return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
9680         var childBoundTranscludeFn = boundTranscludeFn;
9681         if (scope.$$destroyed) return;
9682         if (linkQueue) {
9683           linkQueue.push(scope,
9684                          node,
9685                          rootElement,
9686                          childBoundTranscludeFn);
9687         } else {
9688           if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
9689             childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
9690           }
9691           afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
9692         }
9693       };
9694     }
9697     /**
9698      * Sorting function for bound directives.
9699      */
9700     function byPriority(a, b) {
9701       var diff = b.priority - a.priority;
9702       if (diff !== 0) return diff;
9703       if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
9704       return a.index - b.index;
9705     }
9707     function assertNoDuplicate(what, previousDirective, directive, element) {
9709       function wrapModuleNameIfDefined(moduleName) {
9710         return moduleName ?
9711           (' (module: ' + moduleName + ')') :
9712           '';
9713       }
9715       if (previousDirective) {
9716         throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
9717             previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
9718             directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
9719       }
9720     }
9723     function addTextInterpolateDirective(directives, text) {
9724       var interpolateFn = $interpolate(text, true);
9725       if (interpolateFn) {
9726         directives.push({
9727           priority: 0,
9728           compile: function textInterpolateCompileFn(templateNode) {
9729             var templateNodeParent = templateNode.parent(),
9730                 hasCompileParent = !!templateNodeParent.length;
9732             // When transcluding a template that has bindings in the root
9733             // we don't have a parent and thus need to add the class during linking fn.
9734             if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
9736             return function textInterpolateLinkFn(scope, node) {
9737               var parent = node.parent();
9738               if (!hasCompileParent) compile.$$addBindingClass(parent);
9739               compile.$$addBindingInfo(parent, interpolateFn.expressions);
9740               scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
9741                 node[0].nodeValue = value;
9742               });
9743             };
9744           }
9745         });
9746       }
9747     }
9750     function wrapTemplate(type, template) {
9751       type = lowercase(type || 'html');
9752       switch (type) {
9753       case 'svg':
9754       case 'math':
9755         var wrapper = window.document.createElement('div');
9756         wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
9757         return wrapper.childNodes[0].childNodes;
9758       default:
9759         return template;
9760       }
9761     }
9764     function getTrustedContext(node, attrNormalizedName) {
9765       if (attrNormalizedName == "srcdoc") {
9766         return $sce.HTML;
9767       }
9768       var tag = nodeName_(node);
9769       // maction[xlink:href] can source SVG.  It's not limited to <maction>.
9770       if (attrNormalizedName == "xlinkHref" ||
9771           (tag == "form" && attrNormalizedName == "action") ||
9772           (tag != "img" && (attrNormalizedName == "src" ||
9773                             attrNormalizedName == "ngSrc"))) {
9774         return $sce.RESOURCE_URL;
9775       }
9776     }
9779     function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
9780       var trustedContext = getTrustedContext(node, name);
9781       allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
9783       var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
9785       // no interpolation found -> ignore
9786       if (!interpolateFn) return;
9789       if (name === "multiple" && nodeName_(node) === "select") {
9790         throw $compileMinErr("selmulti",
9791             "Binding to the 'multiple' attribute is not supported. Element: {0}",
9792             startingTag(node));
9793       }
9795       directives.push({
9796         priority: 100,
9797         compile: function() {
9798             return {
9799               pre: function attrInterpolatePreLinkFn(scope, element, attr) {
9800                 var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
9802                 if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
9803                   throw $compileMinErr('nodomevents',
9804                       "Interpolations for HTML DOM event attributes are disallowed.  Please use the " +
9805                           "ng- versions (such as ng-click instead of onclick) instead.");
9806                 }
9808                 // If the attribute has changed since last $interpolate()ed
9809                 var newValue = attr[name];
9810                 if (newValue !== value) {
9811                   // we need to interpolate again since the attribute value has been updated
9812                   // (e.g. by another directive's compile function)
9813                   // ensure unset/empty values make interpolateFn falsy
9814                   interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
9815                   value = newValue;
9816                 }
9818                 // if attribute was updated so that there is no interpolation going on we don't want to
9819                 // register any observers
9820                 if (!interpolateFn) return;
9822                 // initialize attr object so that it's ready in case we need the value for isolate
9823                 // scope initialization, otherwise the value would not be available from isolate
9824                 // directive's linking fn during linking phase
9825                 attr[name] = interpolateFn(scope);
9827                 ($$observers[name] || ($$observers[name] = [])).$$inter = true;
9828                 (attr.$$observers && attr.$$observers[name].$$scope || scope).
9829                   $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
9830                     //special case for class attribute addition + removal
9831                     //so that class changes can tap into the animation
9832                     //hooks provided by the $animate service. Be sure to
9833                     //skip animations when the first digest occurs (when
9834                     //both the new and the old values are the same) since
9835                     //the CSS classes are the non-interpolated values
9836                     if (name === 'class' && newValue != oldValue) {
9837                       attr.$updateClass(newValue, oldValue);
9838                     } else {
9839                       attr.$set(name, newValue);
9840                     }
9841                   });
9842               }
9843             };
9844           }
9845       });
9846     }
9849     /**
9850      * This is a special jqLite.replaceWith, which can replace items which
9851      * have no parents, provided that the containing jqLite collection is provided.
9852      *
9853      * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
9854      *                               in the root of the tree.
9855      * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
9856      *                                  the shell, but replace its DOM node reference.
9857      * @param {Node} newNode The new DOM node.
9858      */
9859     function replaceWith($rootElement, elementsToRemove, newNode) {
9860       var firstElementToRemove = elementsToRemove[0],
9861           removeCount = elementsToRemove.length,
9862           parent = firstElementToRemove.parentNode,
9863           i, ii;
9865       if ($rootElement) {
9866         for (i = 0, ii = $rootElement.length; i < ii; i++) {
9867           if ($rootElement[i] == firstElementToRemove) {
9868             $rootElement[i++] = newNode;
9869             for (var j = i, j2 = j + removeCount - 1,
9870                      jj = $rootElement.length;
9871                  j < jj; j++, j2++) {
9872               if (j2 < jj) {
9873                 $rootElement[j] = $rootElement[j2];
9874               } else {
9875                 delete $rootElement[j];
9876               }
9877             }
9878             $rootElement.length -= removeCount - 1;
9880             // If the replaced element is also the jQuery .context then replace it
9881             // .context is a deprecated jQuery api, so we should set it only when jQuery set it
9882             // http://api.jquery.com/context/
9883             if ($rootElement.context === firstElementToRemove) {
9884               $rootElement.context = newNode;
9885             }
9886             break;
9887           }
9888         }
9889       }
9891       if (parent) {
9892         parent.replaceChild(newNode, firstElementToRemove);
9893       }
9895       // Append all the `elementsToRemove` to a fragment. This will...
9896       // - remove them from the DOM
9897       // - allow them to still be traversed with .nextSibling
9898       // - allow a single fragment.qSA to fetch all elements being removed
9899       var fragment = window.document.createDocumentFragment();
9900       for (i = 0; i < removeCount; i++) {
9901         fragment.appendChild(elementsToRemove[i]);
9902       }
9904       if (jqLite.hasData(firstElementToRemove)) {
9905         // Copy over user data (that includes Angular's $scope etc.). Don't copy private
9906         // data here because there's no public interface in jQuery to do that and copying over
9907         // event listeners (which is the main use of private data) wouldn't work anyway.
9908         jqLite.data(newNode, jqLite.data(firstElementToRemove));
9910         // Remove $destroy event listeners from `firstElementToRemove`
9911         jqLite(firstElementToRemove).off('$destroy');
9912       }
9914       // Cleanup any data/listeners on the elements and children.
9915       // This includes invoking the $destroy event on any elements with listeners.
9916       jqLite.cleanData(fragment.querySelectorAll('*'));
9918       // Update the jqLite collection to only contain the `newNode`
9919       for (i = 1; i < removeCount; i++) {
9920         delete elementsToRemove[i];
9921       }
9922       elementsToRemove[0] = newNode;
9923       elementsToRemove.length = 1;
9924     }
9927     function cloneAndAnnotateFn(fn, annotation) {
9928       return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
9929     }
9932     function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
9933       try {
9934         linkFn(scope, $element, attrs, controllers, transcludeFn);
9935       } catch (e) {
9936         $exceptionHandler(e, startingTag($element));
9937       }
9938     }
9941     // Set up $watches for isolate scope and controller bindings. This process
9942     // only occurs for isolate scopes and new scopes with controllerAs.
9943     function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
9944       var removeWatchCollection = [];
9945       var initialChanges = {};
9946       var changes;
9947       forEach(bindings, function initializeBinding(definition, scopeName) {
9948         var attrName = definition.attrName,
9949         optional = definition.optional,
9950         mode = definition.mode, // @, =, <, or &
9951         lastValue,
9952         parentGet, parentSet, compare, removeWatch;
9954         switch (mode) {
9956           case '@':
9957             if (!optional && !hasOwnProperty.call(attrs, attrName)) {
9958               destination[scopeName] = attrs[attrName] = void 0;
9959             }
9960             attrs.$observe(attrName, function(value) {
9961               if (isString(value) || isBoolean(value)) {
9962                 var oldValue = destination[scopeName];
9963                 recordChanges(scopeName, value, oldValue);
9964                 destination[scopeName] = value;
9965               }
9966             });
9967             attrs.$$observers[attrName].$$scope = scope;
9968             lastValue = attrs[attrName];
9969             if (isString(lastValue)) {
9970               // If the attribute has been provided then we trigger an interpolation to ensure
9971               // the value is there for use in the link fn
9972               destination[scopeName] = $interpolate(lastValue)(scope);
9973             } else if (isBoolean(lastValue)) {
9974               // If the attributes is one of the BOOLEAN_ATTR then Angular will have converted
9975               // the value to boolean rather than a string, so we special case this situation
9976               destination[scopeName] = lastValue;
9977             }
9978             initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
9979             break;
9981           case '=':
9982             if (!hasOwnProperty.call(attrs, attrName)) {
9983               if (optional) break;
9984               attrs[attrName] = void 0;
9985             }
9986             if (optional && !attrs[attrName]) break;
9988             parentGet = $parse(attrs[attrName]);
9989             if (parentGet.literal) {
9990               compare = equals;
9991             } else {
9992               compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); };
9993             }
9994             parentSet = parentGet.assign || function() {
9995               // reset the change, or we will throw this exception on every $digest
9996               lastValue = destination[scopeName] = parentGet(scope);
9997               throw $compileMinErr('nonassign',
9998                   "Expression '{0}' in attribute '{1}' used with directive '{2}' is non-assignable!",
9999                   attrs[attrName], attrName, directive.name);
10000             };
10001             lastValue = destination[scopeName] = parentGet(scope);
10002             var parentValueWatch = function parentValueWatch(parentValue) {
10003               if (!compare(parentValue, destination[scopeName])) {
10004                 // we are out of sync and need to copy
10005                 if (!compare(parentValue, lastValue)) {
10006                   // parent changed and it has precedence
10007                   destination[scopeName] = parentValue;
10008                 } else {
10009                   // if the parent can be assigned then do so
10010                   parentSet(scope, parentValue = destination[scopeName]);
10011                 }
10012               }
10013               return lastValue = parentValue;
10014             };
10015             parentValueWatch.$stateful = true;
10016             if (definition.collection) {
10017               removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
10018             } else {
10019               removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
10020             }
10021             removeWatchCollection.push(removeWatch);
10022             break;
10024           case '<':
10025             if (!hasOwnProperty.call(attrs, attrName)) {
10026               if (optional) break;
10027               attrs[attrName] = void 0;
10028             }
10029             if (optional && !attrs[attrName]) break;
10031             parentGet = $parse(attrs[attrName]);
10033             var initialValue = destination[scopeName] = parentGet(scope);
10034             initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
10036             removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newValue, oldValue) {
10037               if (oldValue === newValue) {
10038                 if (oldValue === initialValue) return;
10039                 oldValue = initialValue;
10040               }
10041               recordChanges(scopeName, newValue, oldValue);
10042               destination[scopeName] = newValue;
10043             }, parentGet.literal);
10045             removeWatchCollection.push(removeWatch);
10046             break;
10048           case '&':
10049             // Don't assign Object.prototype method to scope
10050             parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
10052             // Don't assign noop to destination if expression is not valid
10053             if (parentGet === noop && optional) break;
10055             destination[scopeName] = function(locals) {
10056               return parentGet(scope, locals);
10057             };
10058             break;
10059         }
10060       });
10062       function recordChanges(key, currentValue, previousValue) {
10063         if (isFunction(destination.$onChanges) && currentValue !== previousValue) {
10064           // If we have not already scheduled the top level onChangesQueue handler then do so now
10065           if (!onChangesQueue) {
10066             scope.$$postDigest(flushOnChangesQueue);
10067             onChangesQueue = [];
10068           }
10069           // If we have not already queued a trigger of onChanges for this controller then do so now
10070           if (!changes) {
10071             changes = {};
10072             onChangesQueue.push(triggerOnChangesHook);
10073           }
10074           // If the has been a change on this property already then we need to reuse the previous value
10075           if (changes[key]) {
10076             previousValue = changes[key].previousValue;
10077           }
10078           // Store this change
10079           changes[key] = new SimpleChange(previousValue, currentValue);
10080         }
10081       }
10083       function triggerOnChangesHook() {
10084         destination.$onChanges(changes);
10085         // Now clear the changes so that we schedule onChanges when more changes arrive
10086         changes = undefined;
10087       }
10089       return {
10090         initialChanges: initialChanges,
10091         removeWatches: removeWatchCollection.length && function removeWatches() {
10092           for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
10093             removeWatchCollection[i]();
10094           }
10095         }
10096       };
10097     }
10098   }];
10101 function SimpleChange(previous, current) {
10102   this.previousValue = previous;
10103   this.currentValue = current;
10105 SimpleChange.prototype.isFirstChange = function() { return this.previousValue === _UNINITIALIZED_VALUE; };
10108 var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
10110  * Converts all accepted directives format into proper directive name.
10111  * @param name Name to normalize
10112  */
10113 function directiveNormalize(name) {
10114   return camelCase(name.replace(PREFIX_REGEXP, ''));
10118  * @ngdoc type
10119  * @name $compile.directive.Attributes
10121  * @description
10122  * A shared object between directive compile / linking functions which contains normalized DOM
10123  * element attributes. The values reflect current binding state `{{ }}`. The normalization is
10124  * needed since all of these are treated as equivalent in Angular:
10126  * ```
10127  *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
10128  * ```
10129  */
10132  * @ngdoc property
10133  * @name $compile.directive.Attributes#$attr
10135  * @description
10136  * A map of DOM element attribute names to the normalized name. This is
10137  * needed to do reverse lookup from normalized name back to actual name.
10138  */
10142  * @ngdoc method
10143  * @name $compile.directive.Attributes#$set
10144  * @kind function
10146  * @description
10147  * Set DOM element attribute value.
10150  * @param {string} name Normalized element attribute name of the property to modify. The name is
10151  *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
10152  *          property to the original name.
10153  * @param {string} value Value to set the attribute to. The value can be an interpolated string.
10154  */
10159  * Closure compiler type information
10160  */
10162 function nodesetLinkingFn(
10163   /* angular.Scope */ scope,
10164   /* NodeList */ nodeList,
10165   /* Element */ rootElement,
10166   /* function(Function) */ boundTranscludeFn
10167 ) {}
10169 function directiveLinkingFn(
10170   /* nodesetLinkingFn */ nodesetLinkingFn,
10171   /* angular.Scope */ scope,
10172   /* Node */ node,
10173   /* Element */ rootElement,
10174   /* function(Function) */ boundTranscludeFn
10175 ) {}
10177 function tokenDifference(str1, str2) {
10178   var values = '',
10179       tokens1 = str1.split(/\s+/),
10180       tokens2 = str2.split(/\s+/);
10182   outer:
10183   for (var i = 0; i < tokens1.length; i++) {
10184     var token = tokens1[i];
10185     for (var j = 0; j < tokens2.length; j++) {
10186       if (token == tokens2[j]) continue outer;
10187     }
10188     values += (values.length > 0 ? ' ' : '') + token;
10189   }
10190   return values;
10193 function removeComments(jqNodes) {
10194   jqNodes = jqLite(jqNodes);
10195   var i = jqNodes.length;
10197   if (i <= 1) {
10198     return jqNodes;
10199   }
10201   while (i--) {
10202     var node = jqNodes[i];
10203     if (node.nodeType === NODE_TYPE_COMMENT) {
10204       splice.call(jqNodes, i, 1);
10205     }
10206   }
10207   return jqNodes;
10210 var $controllerMinErr = minErr('$controller');
10213 var CNTRL_REG = /^(\S+)(\s+as\s+([\w$]+))?$/;
10214 function identifierForController(controller, ident) {
10215   if (ident && isString(ident)) return ident;
10216   if (isString(controller)) {
10217     var match = CNTRL_REG.exec(controller);
10218     if (match) return match[3];
10219   }
10224  * @ngdoc provider
10225  * @name $controllerProvider
10226  * @description
10227  * The {@link ng.$controller $controller service} is used by Angular to create new
10228  * controllers.
10230  * This provider allows controller registration via the
10231  * {@link ng.$controllerProvider#register register} method.
10232  */
10233 function $ControllerProvider() {
10234   var controllers = {},
10235       globals = false;
10237   /**
10238    * @ngdoc method
10239    * @name $controllerProvider#has
10240    * @param {string} name Controller name to check.
10241    */
10242   this.has = function(name) {
10243     return controllers.hasOwnProperty(name);
10244   };
10246   /**
10247    * @ngdoc method
10248    * @name $controllerProvider#register
10249    * @param {string|Object} name Controller name, or an object map of controllers where the keys are
10250    *    the names and the values are the constructors.
10251    * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
10252    *    annotations in the array notation).
10253    */
10254   this.register = function(name, constructor) {
10255     assertNotHasOwnProperty(name, 'controller');
10256     if (isObject(name)) {
10257       extend(controllers, name);
10258     } else {
10259       controllers[name] = constructor;
10260     }
10261   };
10263   /**
10264    * @ngdoc method
10265    * @name $controllerProvider#allowGlobals
10266    * @description If called, allows `$controller` to find controller constructors on `window`
10267    */
10268   this.allowGlobals = function() {
10269     globals = true;
10270   };
10273   this.$get = ['$injector', '$window', function($injector, $window) {
10275     /**
10276      * @ngdoc service
10277      * @name $controller
10278      * @requires $injector
10279      *
10280      * @param {Function|string} constructor If called with a function then it's considered to be the
10281      *    controller constructor function. Otherwise it's considered to be a string which is used
10282      *    to retrieve the controller constructor using the following steps:
10283      *
10284      *    * check if a controller with given name is registered via `$controllerProvider`
10285      *    * check if evaluating the string on the current scope returns a constructor
10286      *    * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
10287      *      `window` object (not recommended)
10288      *
10289      *    The string can use the `controller as property` syntax, where the controller instance is published
10290      *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
10291      *    to work correctly.
10292      *
10293      * @param {Object} locals Injection locals for Controller.
10294      * @return {Object} Instance of given controller.
10295      *
10296      * @description
10297      * `$controller` service is responsible for instantiating controllers.
10298      *
10299      * It's just a simple call to {@link auto.$injector $injector}, but extracted into
10300      * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
10301      */
10302     return function $controller(expression, locals, later, ident) {
10303       // PRIVATE API:
10304       //   param `later` --- indicates that the controller's constructor is invoked at a later time.
10305       //                     If true, $controller will allocate the object with the correct
10306       //                     prototype chain, but will not invoke the controller until a returned
10307       //                     callback is invoked.
10308       //   param `ident` --- An optional label which overrides the label parsed from the controller
10309       //                     expression, if any.
10310       var instance, match, constructor, identifier;
10311       later = later === true;
10312       if (ident && isString(ident)) {
10313         identifier = ident;
10314       }
10316       if (isString(expression)) {
10317         match = expression.match(CNTRL_REG);
10318         if (!match) {
10319           throw $controllerMinErr('ctrlfmt',
10320             "Badly formed controller string '{0}'. " +
10321             "Must match `__name__ as __id__` or `__name__`.", expression);
10322         }
10323         constructor = match[1],
10324         identifier = identifier || match[3];
10325         expression = controllers.hasOwnProperty(constructor)
10326             ? controllers[constructor]
10327             : getter(locals.$scope, constructor, true) ||
10328                 (globals ? getter($window, constructor, true) : undefined);
10330         assertArgFn(expression, constructor, true);
10331       }
10333       if (later) {
10334         // Instantiate controller later:
10335         // This machinery is used to create an instance of the object before calling the
10336         // controller's constructor itself.
10337         //
10338         // This allows properties to be added to the controller before the constructor is
10339         // invoked. Primarily, this is used for isolate scope bindings in $compile.
10340         //
10341         // This feature is not intended for use by applications, and is thus not documented
10342         // publicly.
10343         // Object creation: http://jsperf.com/create-constructor/2
10344         var controllerPrototype = (isArray(expression) ?
10345           expression[expression.length - 1] : expression).prototype;
10346         instance = Object.create(controllerPrototype || null);
10348         if (identifier) {
10349           addIdentifier(locals, identifier, instance, constructor || expression.name);
10350         }
10352         var instantiate;
10353         return instantiate = extend(function $controllerInit() {
10354           var result = $injector.invoke(expression, instance, locals, constructor);
10355           if (result !== instance && (isObject(result) || isFunction(result))) {
10356             instance = result;
10357             if (identifier) {
10358               // If result changed, re-assign controllerAs value to scope.
10359               addIdentifier(locals, identifier, instance, constructor || expression.name);
10360             }
10361           }
10362           return instance;
10363         }, {
10364           instance: instance,
10365           identifier: identifier
10366         });
10367       }
10369       instance = $injector.instantiate(expression, locals, constructor);
10371       if (identifier) {
10372         addIdentifier(locals, identifier, instance, constructor || expression.name);
10373       }
10375       return instance;
10376     };
10378     function addIdentifier(locals, identifier, instance, name) {
10379       if (!(locals && isObject(locals.$scope))) {
10380         throw minErr('$controller')('noscp',
10381           "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
10382           name, identifier);
10383       }
10385       locals.$scope[identifier] = instance;
10386     }
10387   }];
10391  * @ngdoc service
10392  * @name $document
10393  * @requires $window
10395  * @description
10396  * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
10398  * @example
10399    <example module="documentExample">
10400      <file name="index.html">
10401        <div ng-controller="ExampleController">
10402          <p>$document title: <b ng-bind="title"></b></p>
10403          <p>window.document title: <b ng-bind="windowTitle"></b></p>
10404        </div>
10405      </file>
10406      <file name="script.js">
10407        angular.module('documentExample', [])
10408          .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
10409            $scope.title = $document[0].title;
10410            $scope.windowTitle = angular.element(window.document)[0].title;
10411          }]);
10412      </file>
10413    </example>
10414  */
10415 function $DocumentProvider() {
10416   this.$get = ['$window', function(window) {
10417     return jqLite(window.document);
10418   }];
10422  * @ngdoc service
10423  * @name $exceptionHandler
10424  * @requires ng.$log
10426  * @description
10427  * Any uncaught exception in angular expressions is delegated to this service.
10428  * The default implementation simply delegates to `$log.error` which logs it into
10429  * the browser console.
10431  * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
10432  * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
10434  * ## Example:
10436  * The example below will overwrite the default `$exceptionHandler` in order to (a) log uncaught
10437  * errors to the backend for later inspection by the developers and (b) to use `$log.warn()` instead
10438  * of `$log.error()`.
10440  * ```js
10441  *   angular.
10442  *     module('exceptionOverwrite', []).
10443  *     factory('$exceptionHandler', ['$log', 'logErrorsToBackend', function($log, logErrorsToBackend) {
10444  *       return function myExceptionHandler(exception, cause) {
10445  *         logErrorsToBackend(exception, cause);
10446  *         $log.warn(exception, cause);
10447  *       };
10448  *     }]);
10449  * ```
10451  * <hr />
10452  * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
10453  * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
10454  * (unless executed during a digest).
10456  * If you wish, you can manually delegate exceptions, e.g.
10457  * `try { ... } catch(e) { $exceptionHandler(e); }`
10459  * @param {Error} exception Exception associated with the error.
10460  * @param {string=} cause Optional information about the context in which
10461  *       the error was thrown.
10463  */
10464 function $ExceptionHandlerProvider() {
10465   this.$get = ['$log', function($log) {
10466     return function(exception, cause) {
10467       $log.error.apply($log, arguments);
10468     };
10469   }];
10472 var $$ForceReflowProvider = function() {
10473   this.$get = ['$document', function($document) {
10474     return function(domNode) {
10475       //the line below will force the browser to perform a repaint so
10476       //that all the animated elements within the animation frame will
10477       //be properly updated and drawn on screen. This is required to
10478       //ensure that the preparation animation is properly flushed so that
10479       //the active state picks up from there. DO NOT REMOVE THIS LINE.
10480       //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
10481       //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
10482       //WILL TAKE YEARS AWAY FROM YOUR LIFE.
10483       if (domNode) {
10484         if (!domNode.nodeType && domNode instanceof jqLite) {
10485           domNode = domNode[0];
10486         }
10487       } else {
10488         domNode = $document[0].body;
10489       }
10490       return domNode.offsetWidth + 1;
10491     };
10492   }];
10495 var APPLICATION_JSON = 'application/json';
10496 var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
10497 var JSON_START = /^\[|^\{(?!\{)/;
10498 var JSON_ENDS = {
10499   '[': /]$/,
10500   '{': /}$/
10502 var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
10503 var $httpMinErr = minErr('$http');
10504 var $httpMinErrLegacyFn = function(method) {
10505   return function() {
10506     throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
10507   };
10510 function serializeValue(v) {
10511   if (isObject(v)) {
10512     return isDate(v) ? v.toISOString() : toJson(v);
10513   }
10514   return v;
10518 function $HttpParamSerializerProvider() {
10519   /**
10520    * @ngdoc service
10521    * @name $httpParamSerializer
10522    * @description
10523    *
10524    * Default {@link $http `$http`} params serializer that converts objects to strings
10525    * according to the following rules:
10526    *
10527    * * `{'foo': 'bar'}` results in `foo=bar`
10528    * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
10529    * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
10530    * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D` (stringified and encoded representation of an object)
10531    *
10532    * Note that serializer will sort the request parameters alphabetically.
10533    * */
10535   this.$get = function() {
10536     return function ngParamSerializer(params) {
10537       if (!params) return '';
10538       var parts = [];
10539       forEachSorted(params, function(value, key) {
10540         if (value === null || isUndefined(value)) return;
10541         if (isArray(value)) {
10542           forEach(value, function(v) {
10543             parts.push(encodeUriQuery(key)  + '=' + encodeUriQuery(serializeValue(v)));
10544           });
10545         } else {
10546           parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
10547         }
10548       });
10550       return parts.join('&');
10551     };
10552   };
10555 function $HttpParamSerializerJQLikeProvider() {
10556   /**
10557    * @ngdoc service
10558    * @name $httpParamSerializerJQLike
10559    * @description
10560    *
10561    * Alternative {@link $http `$http`} params serializer that follows
10562    * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
10563    * The serializer will also sort the params alphabetically.
10564    *
10565    * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
10566    *
10567    * ```js
10568    * $http({
10569    *   url: myUrl,
10570    *   method: 'GET',
10571    *   params: myParams,
10572    *   paramSerializer: '$httpParamSerializerJQLike'
10573    * });
10574    * ```
10575    *
10576    * It is also possible to set it as the default `paramSerializer` in the
10577    * {@link $httpProvider#defaults `$httpProvider`}.
10578    *
10579    * Additionally, you can inject the serializer and use it explicitly, for example to serialize
10580    * form data for submission:
10581    *
10582    * ```js
10583    * .controller(function($http, $httpParamSerializerJQLike) {
10584    *   //...
10585    *
10586    *   $http({
10587    *     url: myUrl,
10588    *     method: 'POST',
10589    *     data: $httpParamSerializerJQLike(myData),
10590    *     headers: {
10591    *       'Content-Type': 'application/x-www-form-urlencoded'
10592    *     }
10593    *   });
10594    *
10595    * });
10596    * ```
10597    *
10598    * */
10599   this.$get = function() {
10600     return function jQueryLikeParamSerializer(params) {
10601       if (!params) return '';
10602       var parts = [];
10603       serialize(params, '', true);
10604       return parts.join('&');
10606       function serialize(toSerialize, prefix, topLevel) {
10607         if (toSerialize === null || isUndefined(toSerialize)) return;
10608         if (isArray(toSerialize)) {
10609           forEach(toSerialize, function(value, index) {
10610             serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
10611           });
10612         } else if (isObject(toSerialize) && !isDate(toSerialize)) {
10613           forEachSorted(toSerialize, function(value, key) {
10614             serialize(value, prefix +
10615                 (topLevel ? '' : '[') +
10616                 key +
10617                 (topLevel ? '' : ']'));
10618           });
10619         } else {
10620           parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
10621         }
10622       }
10623     };
10624   };
10627 function defaultHttpResponseTransform(data, headers) {
10628   if (isString(data)) {
10629     // Strip json vulnerability protection prefix and trim whitespace
10630     var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
10632     if (tempData) {
10633       var contentType = headers('Content-Type');
10634       if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
10635         data = fromJson(tempData);
10636       }
10637     }
10638   }
10640   return data;
10643 function isJsonLike(str) {
10644     var jsonStart = str.match(JSON_START);
10645     return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
10649  * Parse headers into key value object
10651  * @param {string} headers Raw headers as a string
10652  * @returns {Object} Parsed headers as key value object
10653  */
10654 function parseHeaders(headers) {
10655   var parsed = createMap(), i;
10657   function fillInParsed(key, val) {
10658     if (key) {
10659       parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
10660     }
10661   }
10663   if (isString(headers)) {
10664     forEach(headers.split('\n'), function(line) {
10665       i = line.indexOf(':');
10666       fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
10667     });
10668   } else if (isObject(headers)) {
10669     forEach(headers, function(headerVal, headerKey) {
10670       fillInParsed(lowercase(headerKey), trim(headerVal));
10671     });
10672   }
10674   return parsed;
10679  * Returns a function that provides access to parsed headers.
10681  * Headers are lazy parsed when first requested.
10682  * @see parseHeaders
10684  * @param {(string|Object)} headers Headers to provide access to.
10685  * @returns {function(string=)} Returns a getter function which if called with:
10687  *   - if called with single an argument returns a single header value or null
10688  *   - if called with no arguments returns an object containing all headers.
10689  */
10690 function headersGetter(headers) {
10691   var headersObj;
10693   return function(name) {
10694     if (!headersObj) headersObj =  parseHeaders(headers);
10696     if (name) {
10697       var value = headersObj[lowercase(name)];
10698       if (value === void 0) {
10699         value = null;
10700       }
10701       return value;
10702     }
10704     return headersObj;
10705   };
10710  * Chain all given functions
10712  * This function is used for both request and response transforming
10714  * @param {*} data Data to transform.
10715  * @param {function(string=)} headers HTTP headers getter fn.
10716  * @param {number} status HTTP status code of the response.
10717  * @param {(Function|Array.<Function>)} fns Function or an array of functions.
10718  * @returns {*} Transformed data.
10719  */
10720 function transformData(data, headers, status, fns) {
10721   if (isFunction(fns)) {
10722     return fns(data, headers, status);
10723   }
10725   forEach(fns, function(fn) {
10726     data = fn(data, headers, status);
10727   });
10729   return data;
10733 function isSuccess(status) {
10734   return 200 <= status && status < 300;
10739  * @ngdoc provider
10740  * @name $httpProvider
10741  * @description
10742  * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
10743  * */
10744 function $HttpProvider() {
10745   /**
10746    * @ngdoc property
10747    * @name $httpProvider#defaults
10748    * @description
10749    *
10750    * Object containing default values for all {@link ng.$http $http} requests.
10751    *
10752    * - **`defaults.cache`** - {boolean|Object} - A boolean value or object created with
10753    * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses
10754    * by default. See {@link $http#caching $http Caching} for more information.
10755    *
10756    * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
10757    * Defaults value is `'XSRF-TOKEN'`.
10758    *
10759    * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
10760    * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
10761    *
10762    * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
10763    * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
10764    * setting default headers.
10765    *     - **`defaults.headers.common`**
10766    *     - **`defaults.headers.post`**
10767    *     - **`defaults.headers.put`**
10768    *     - **`defaults.headers.patch`**
10769    *
10770    *
10771    * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
10772    *  used to the prepare string representation of request parameters (specified as an object).
10773    *  If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
10774    *  Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
10775    *
10776    **/
10777   var defaults = this.defaults = {
10778     // transform incoming response data
10779     transformResponse: [defaultHttpResponseTransform],
10781     // transform outgoing request data
10782     transformRequest: [function(d) {
10783       return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
10784     }],
10786     // default headers
10787     headers: {
10788       common: {
10789         'Accept': 'application/json, text/plain, */*'
10790       },
10791       post:   shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
10792       put:    shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
10793       patch:  shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
10794     },
10796     xsrfCookieName: 'XSRF-TOKEN',
10797     xsrfHeaderName: 'X-XSRF-TOKEN',
10799     paramSerializer: '$httpParamSerializer'
10800   };
10802   var useApplyAsync = false;
10803   /**
10804    * @ngdoc method
10805    * @name $httpProvider#useApplyAsync
10806    * @description
10807    *
10808    * Configure $http service to combine processing of multiple http responses received at around
10809    * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
10810    * significant performance improvement for bigger applications that make many HTTP requests
10811    * concurrently (common during application bootstrap).
10812    *
10813    * Defaults to false. If no value is specified, returns the current configured value.
10814    *
10815    * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
10816    *    "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
10817    *    to load and share the same digest cycle.
10818    *
10819    * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
10820    *    otherwise, returns the current configured value.
10821    **/
10822   this.useApplyAsync = function(value) {
10823     if (isDefined(value)) {
10824       useApplyAsync = !!value;
10825       return this;
10826     }
10827     return useApplyAsync;
10828   };
10830   var useLegacyPromise = true;
10831   /**
10832    * @ngdoc method
10833    * @name $httpProvider#useLegacyPromiseExtensions
10834    * @description
10835    *
10836    * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
10837    * This should be used to make sure that applications work without these methods.
10838    *
10839    * Defaults to true. If no value is specified, returns the current configured value.
10840    *
10841    * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
10842    *
10843    * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
10844    *    otherwise, returns the current configured value.
10845    **/
10846   this.useLegacyPromiseExtensions = function(value) {
10847     if (isDefined(value)) {
10848       useLegacyPromise = !!value;
10849       return this;
10850     }
10851     return useLegacyPromise;
10852   };
10854   /**
10855    * @ngdoc property
10856    * @name $httpProvider#interceptors
10857    * @description
10858    *
10859    * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
10860    * pre-processing of request or postprocessing of responses.
10861    *
10862    * These service factories are ordered by request, i.e. they are applied in the same order as the
10863    * array, on request, but reverse order, on response.
10864    *
10865    * {@link ng.$http#interceptors Interceptors detailed info}
10866    **/
10867   var interceptorFactories = this.interceptors = [];
10869   this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
10870       function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
10872     var defaultCache = $cacheFactory('$http');
10874     /**
10875      * Make sure that default param serializer is exposed as a function
10876      */
10877     defaults.paramSerializer = isString(defaults.paramSerializer) ?
10878       $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
10880     /**
10881      * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
10882      * The reversal is needed so that we can build up the interception chain around the
10883      * server request.
10884      */
10885     var reversedInterceptors = [];
10887     forEach(interceptorFactories, function(interceptorFactory) {
10888       reversedInterceptors.unshift(isString(interceptorFactory)
10889           ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
10890     });
10892     /**
10893      * @ngdoc service
10894      * @kind function
10895      * @name $http
10896      * @requires ng.$httpBackend
10897      * @requires $cacheFactory
10898      * @requires $rootScope
10899      * @requires $q
10900      * @requires $injector
10901      *
10902      * @description
10903      * The `$http` service is a core Angular service that facilitates communication with the remote
10904      * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
10905      * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
10906      *
10907      * For unit testing applications that use `$http` service, see
10908      * {@link ngMock.$httpBackend $httpBackend mock}.
10909      *
10910      * For a higher level of abstraction, please check out the {@link ngResource.$resource
10911      * $resource} service.
10912      *
10913      * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
10914      * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
10915      * it is important to familiarize yourself with these APIs and the guarantees they provide.
10916      *
10917      *
10918      * ## General usage
10919      * The `$http` service is a function which takes a single argument â€” a {@link $http#usage configuration object} â€”
10920      * that is used to generate an HTTP request and returns  a {@link ng.$q promise}.
10921      *
10922      * ```js
10923      *   // Simple GET request example:
10924      *   $http({
10925      *     method: 'GET',
10926      *     url: '/someUrl'
10927      *   }).then(function successCallback(response) {
10928      *       // this callback will be called asynchronously
10929      *       // when the response is available
10930      *     }, function errorCallback(response) {
10931      *       // called asynchronously if an error occurs
10932      *       // or server returns response with an error status.
10933      *     });
10934      * ```
10935      *
10936      * The response object has these properties:
10937      *
10938      *   - **data** â€“ `{string|Object}` â€“ The response body transformed with the transform
10939      *     functions.
10940      *   - **status** â€“ `{number}` â€“ HTTP status code of the response.
10941      *   - **headers** â€“ `{function([headerName])}` â€“ Header getter function.
10942      *   - **config** â€“ `{Object}` â€“ The configuration object that was used to generate the request.
10943      *   - **statusText** â€“ `{string}` â€“ HTTP status text of the response.
10944      *
10945      * A response status code between 200 and 299 is considered a success status and will result in
10946      * the success callback being called. Any response status code outside of that range is
10947      * considered an error status and will result in the error callback being called.
10948      * Also, status codes less than -1 are normalized to zero. -1 usually means the request was
10949      * aborted, e.g. using a `config.timeout`.
10950      * Note that if the response is a redirect, XMLHttpRequest will transparently follow it, meaning
10951      * that the outcome (success or error) will be determined by the final response status code.
10952      *
10953      *
10954      * ## Shortcut methods
10955      *
10956      * Shortcut methods are also available. All shortcut methods require passing in the URL, and
10957      * request data must be passed in for POST/PUT requests. An optional config can be passed as the
10958      * last argument.
10959      *
10960      * ```js
10961      *   $http.get('/someUrl', config).then(successCallback, errorCallback);
10962      *   $http.post('/someUrl', data, config).then(successCallback, errorCallback);
10963      * ```
10964      *
10965      * Complete list of shortcut methods:
10966      *
10967      * - {@link ng.$http#get $http.get}
10968      * - {@link ng.$http#head $http.head}
10969      * - {@link ng.$http#post $http.post}
10970      * - {@link ng.$http#put $http.put}
10971      * - {@link ng.$http#delete $http.delete}
10972      * - {@link ng.$http#jsonp $http.jsonp}
10973      * - {@link ng.$http#patch $http.patch}
10974      *
10975      *
10976      * ## Writing Unit Tests that use $http
10977      * When unit testing (using {@link ngMock ngMock}), it is necessary to call
10978      * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
10979      * request using trained responses.
10980      *
10981      * ```
10982      * $httpBackend.expectGET(...);
10983      * $http.get(...);
10984      * $httpBackend.flush();
10985      * ```
10986      *
10987      * ## Deprecation Notice
10988      * <div class="alert alert-danger">
10989      *   The `$http` legacy promise methods `success` and `error` have been deprecated.
10990      *   Use the standard `then` method instead.
10991      *   If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
10992      *   `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
10993      * </div>
10994      *
10995      * ## Setting HTTP Headers
10996      *
10997      * The $http service will automatically add certain HTTP headers to all requests. These defaults
10998      * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
10999      * object, which currently contains this default configuration:
11000      *
11001      * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
11002      *   - `Accept: application/json, text/plain, * / *`
11003      * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
11004      *   - `Content-Type: application/json`
11005      * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
11006      *   - `Content-Type: application/json`
11007      *
11008      * To add or overwrite these defaults, simply add or remove a property from these configuration
11009      * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
11010      * with the lowercased HTTP method name as the key, e.g.
11011      * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
11012      *
11013      * The defaults can also be set at runtime via the `$http.defaults` object in the same
11014      * fashion. For example:
11015      *
11016      * ```
11017      * module.run(function($http) {
11018      *   $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w';
11019      * });
11020      * ```
11021      *
11022      * In addition, you can supply a `headers` property in the config object passed when
11023      * calling `$http(config)`, which overrides the defaults without changing them globally.
11024      *
11025      * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
11026      * Use the `headers` property, setting the desired header to `undefined`. For example:
11027      *
11028      * ```js
11029      * var req = {
11030      *  method: 'POST',
11031      *  url: 'http://example.com',
11032      *  headers: {
11033      *    'Content-Type': undefined
11034      *  },
11035      *  data: { test: 'test' }
11036      * }
11037      *
11038      * $http(req).then(function(){...}, function(){...});
11039      * ```
11040      *
11041      * ## Transforming Requests and Responses
11042      *
11043      * Both requests and responses can be transformed using transformation functions: `transformRequest`
11044      * and `transformResponse`. These properties can be a single function that returns
11045      * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
11046      * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
11047      *
11048      * <div class="alert alert-warning">
11049      * **Note:** Angular does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline.
11050      * That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference).
11051      * For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest
11052      * function will be reflected on the scope and in any templates where the object is data-bound.
11053      * To prevent this, transform functions should have no side-effects.
11054      * If you need to modify properties, it is recommended to make a copy of the data, or create new object to return.
11055      * </div>
11056      *
11057      * ### Default Transformations
11058      *
11059      * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
11060      * `defaults.transformResponse` properties. If a request does not provide its own transformations
11061      * then these will be applied.
11062      *
11063      * You can augment or replace the default transformations by modifying these properties by adding to or
11064      * replacing the array.
11065      *
11066      * Angular provides the following default transformations:
11067      *
11068      * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
11069      *
11070      * - If the `data` property of the request configuration object contains an object, serialize it
11071      *   into JSON format.
11072      *
11073      * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
11074      *
11075      *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
11076      *  - If JSON response is detected, deserialize it using a JSON parser.
11077      *
11078      *
11079      * ### Overriding the Default Transformations Per Request
11080      *
11081      * If you wish to override the request/response transformations only for a single request then provide
11082      * `transformRequest` and/or `transformResponse` properties on the configuration object passed
11083      * into `$http`.
11084      *
11085      * Note that if you provide these properties on the config object the default transformations will be
11086      * overwritten. If you wish to augment the default transformations then you must include them in your
11087      * local transformation array.
11088      *
11089      * The following code demonstrates adding a new response transformation to be run after the default response
11090      * transformations have been run.
11091      *
11092      * ```js
11093      * function appendTransform(defaults, transform) {
11094      *
11095      *   // We can't guarantee that the default transformation is an array
11096      *   defaults = angular.isArray(defaults) ? defaults : [defaults];
11097      *
11098      *   // Append the new transformation to the defaults
11099      *   return defaults.concat(transform);
11100      * }
11101      *
11102      * $http({
11103      *   url: '...',
11104      *   method: 'GET',
11105      *   transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
11106      *     return doTransform(value);
11107      *   })
11108      * });
11109      * ```
11110      *
11111      *
11112      * ## Caching
11113      *
11114      * {@link ng.$http `$http`} responses are not cached by default. To enable caching, you must
11115      * set the config.cache value or the default cache value to TRUE or to a cache object (created
11116      * with {@link ng.$cacheFactory `$cacheFactory`}). If defined, the value of config.cache takes
11117      * precedence over the default cache value.
11118      *
11119      * In order to:
11120      *   * cache all responses - set the default cache value to TRUE or to a cache object
11121      *   * cache a specific response - set config.cache value to TRUE or to a cache object
11122      *
11123      * If caching is enabled, but neither the default cache nor config.cache are set to a cache object,
11124      * then the default `$cacheFactory("$http")` object is used.
11125      *
11126      * The default cache value can be set by updating the
11127      * {@link ng.$http#defaults `$http.defaults.cache`} property or the
11128      * {@link $httpProvider#defaults `$httpProvider.defaults.cache`} property.
11129      *
11130      * When caching is enabled, {@link ng.$http `$http`} stores the response from the server using
11131      * the relevant cache object. The next time the same request is made, the response is returned
11132      * from the cache without sending a request to the server.
11133      *
11134      * Take note that:
11135      *
11136      *   * Only GET and JSONP requests are cached.
11137      *   * The cache key is the request URL including search parameters; headers are not considered.
11138      *   * Cached responses are returned asynchronously, in the same way as responses from the server.
11139      *   * If multiple identical requests are made using the same cache, which is not yet populated,
11140      *     one request will be made to the server and remaining requests will return the same response.
11141      *   * A cache-control header on the response does not affect if or how responses are cached.
11142      *
11143      *
11144      * ## Interceptors
11145      *
11146      * Before you start creating interceptors, be sure to understand the
11147      * {@link ng.$q $q and deferred/promise APIs}.
11148      *
11149      * For purposes of global error handling, authentication, or any kind of synchronous or
11150      * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
11151      * able to intercept requests before they are handed to the server and
11152      * responses before they are handed over to the application code that
11153      * initiated these requests. The interceptors leverage the {@link ng.$q
11154      * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
11155      *
11156      * The interceptors are service factories that are registered with the `$httpProvider` by
11157      * adding them to the `$httpProvider.interceptors` array. The factory is called and
11158      * injected with dependencies (if specified) and returns the interceptor.
11159      *
11160      * There are two kinds of interceptors (and two kinds of rejection interceptors):
11161      *
11162      *   * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
11163      *     modify the `config` object or create a new one. The function needs to return the `config`
11164      *     object directly, or a promise containing the `config` or a new `config` object.
11165      *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
11166      *     resolved with a rejection.
11167      *   * `response`: interceptors get called with http `response` object. The function is free to
11168      *     modify the `response` object or create a new one. The function needs to return the `response`
11169      *     object directly, or as a promise containing the `response` or a new `response` object.
11170      *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
11171      *     resolved with a rejection.
11172      *
11173      *
11174      * ```js
11175      *   // register the interceptor as a service
11176      *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
11177      *     return {
11178      *       // optional method
11179      *       'request': function(config) {
11180      *         // do something on success
11181      *         return config;
11182      *       },
11183      *
11184      *       // optional method
11185      *      'requestError': function(rejection) {
11186      *         // do something on error
11187      *         if (canRecover(rejection)) {
11188      *           return responseOrNewPromise
11189      *         }
11190      *         return $q.reject(rejection);
11191      *       },
11192      *
11193      *
11194      *
11195      *       // optional method
11196      *       'response': function(response) {
11197      *         // do something on success
11198      *         return response;
11199      *       },
11200      *
11201      *       // optional method
11202      *      'responseError': function(rejection) {
11203      *         // do something on error
11204      *         if (canRecover(rejection)) {
11205      *           return responseOrNewPromise
11206      *         }
11207      *         return $q.reject(rejection);
11208      *       }
11209      *     };
11210      *   });
11211      *
11212      *   $httpProvider.interceptors.push('myHttpInterceptor');
11213      *
11214      *
11215      *   // alternatively, register the interceptor via an anonymous factory
11216      *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
11217      *     return {
11218      *      'request': function(config) {
11219      *          // same as above
11220      *       },
11221      *
11222      *       'response': function(response) {
11223      *          // same as above
11224      *       }
11225      *     };
11226      *   });
11227      * ```
11228      *
11229      * ## Security Considerations
11230      *
11231      * When designing web applications, consider security threats from:
11232      *
11233      * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
11234      * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
11235      *
11236      * Both server and the client must cooperate in order to eliminate these threats. Angular comes
11237      * pre-configured with strategies that address these issues, but for this to work backend server
11238      * cooperation is required.
11239      *
11240      * ### JSON Vulnerability Protection
11241      *
11242      * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
11243      * allows third party website to turn your JSON resource URL into
11244      * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
11245      * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
11246      * Angular will automatically strip the prefix before processing it as JSON.
11247      *
11248      * For example if your server needs to return:
11249      * ```js
11250      * ['one','two']
11251      * ```
11252      *
11253      * which is vulnerable to attack, your server can return:
11254      * ```js
11255      * )]}',
11256      * ['one','two']
11257      * ```
11258      *
11259      * Angular will strip the prefix, before processing the JSON.
11260      *
11261      *
11262      * ### Cross Site Request Forgery (XSRF) Protection
11263      *
11264      * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by
11265      * which the attacker can trick an authenticated user into unknowingly executing actions on your
11266      * website. Angular provides a mechanism to counter XSRF. When performing XHR requests, the
11267      * $http service reads a token from a cookie (by default, `XSRF-TOKEN`) and sets it as an HTTP
11268      * header (`X-XSRF-TOKEN`). Since only JavaScript that runs on your domain could read the
11269      * cookie, your server can be assured that the XHR came from JavaScript running on your domain.
11270      * The header will not be set for cross-domain requests.
11271      *
11272      * To take advantage of this, your server needs to set a token in a JavaScript readable session
11273      * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
11274      * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
11275      * that only JavaScript running on your domain could have sent the request. The token must be
11276      * unique for each user and must be verifiable by the server (to prevent the JavaScript from
11277      * making up its own tokens). We recommend that the token is a digest of your site's
11278      * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
11279      * for added security.
11280      *
11281      * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
11282      * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
11283      * or the per-request config object.
11284      *
11285      * In order to prevent collisions in environments where multiple Angular apps share the
11286      * same domain or subdomain, we recommend that each application uses unique cookie name.
11287      *
11288      * @param {object} config Object describing the request to be made and how it should be
11289      *    processed. The object has following properties:
11290      *
11291      *    - **method** â€“ `{string}` â€“ HTTP method (e.g. 'GET', 'POST', etc)
11292      *    - **url** â€“ `{string}` â€“ Absolute or relative URL of the resource that is being requested.
11293      *    - **params** â€“ `{Object.<string|Object>}` â€“ Map of strings or objects which will be serialized
11294      *      with the `paramSerializer` and appended as GET parameters.
11295      *    - **data** â€“ `{string|Object}` â€“ Data to be sent as the request message data.
11296      *    - **headers** â€“ `{Object}` â€“ Map of strings or functions which return strings representing
11297      *      HTTP headers to send to the server. If the return value of a function is null, the
11298      *      header will not be sent. Functions accept a config object as an argument.
11299      *    - **eventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest object.
11300      *      To bind events to the XMLHttpRequest upload object, use `uploadEventHandlers`.
11301      *      The handler will be called in the context of a `$apply` block.
11302      *    - **uploadEventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest upload
11303      *      object. To bind events to the XMLHttpRequest object, use `eventHandlers`.
11304      *      The handler will be called in the context of a `$apply` block.
11305      *    - **xsrfHeaderName** â€“ `{string}` â€“ Name of HTTP header to populate with the XSRF token.
11306      *    - **xsrfCookieName** â€“ `{string}` â€“ Name of cookie containing the XSRF token.
11307      *    - **transformRequest** â€“
11308      *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` â€“
11309      *      transform function or an array of such functions. The transform function takes the http
11310      *      request body and headers and returns its transformed (typically serialized) version.
11311      *      See {@link ng.$http#overriding-the-default-transformations-per-request
11312      *      Overriding the Default Transformations}
11313      *    - **transformResponse** â€“
11314      *      `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` â€“
11315      *      transform function or an array of such functions. The transform function takes the http
11316      *      response body, headers and status and returns its transformed (typically deserialized) version.
11317      *      See {@link ng.$http#overriding-the-default-transformations-per-request
11318      *      Overriding the Default Transformations}
11319      *    - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
11320      *      prepare the string representation of request parameters (specified as an object).
11321      *      If specified as string, it is interpreted as function registered with the
11322      *      {@link $injector $injector}, which means you can create your own serializer
11323      *      by registering it as a {@link auto.$provide#service service}.
11324      *      The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
11325      *      alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
11326      *    - **cache** â€“ `{boolean|Object}` â€“ A boolean value or object created with
11327      *      {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of the HTTP response.
11328      *      See {@link $http#caching $http Caching} for more information.
11329      *    - **timeout** â€“ `{number|Promise}` â€“ timeout in milliseconds, or {@link ng.$q promise}
11330      *      that should abort the request when resolved.
11331      *    - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
11332      *      XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
11333      *      for more information.
11334      *    - **responseType** - `{string}` - see
11335      *      [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
11336      *
11337      * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
11338      *                        when the request succeeds or fails.
11339      *
11340      *
11341      * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
11342      *   requests. This is primarily meant to be used for debugging purposes.
11343      *
11344      *
11345      * @example
11346 <example module="httpExample">
11347 <file name="index.html">
11348   <div ng-controller="FetchController">
11349     <select ng-model="method" aria-label="Request method">
11350       <option>GET</option>
11351       <option>JSONP</option>
11352     </select>
11353     <input type="text" ng-model="url" size="80" aria-label="URL" />
11354     <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
11355     <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
11356     <button id="samplejsonpbtn"
11357       ng-click="updateModel('JSONP',
11358                     'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
11359       Sample JSONP
11360     </button>
11361     <button id="invalidjsonpbtn"
11362       ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
11363         Invalid JSONP
11364       </button>
11365     <pre>http status code: {{status}}</pre>
11366     <pre>http response data: {{data}}</pre>
11367   </div>
11368 </file>
11369 <file name="script.js">
11370   angular.module('httpExample', [])
11371     .controller('FetchController', ['$scope', '$http', '$templateCache',
11372       function($scope, $http, $templateCache) {
11373         $scope.method = 'GET';
11374         $scope.url = 'http-hello.html';
11376         $scope.fetch = function() {
11377           $scope.code = null;
11378           $scope.response = null;
11380           $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
11381             then(function(response) {
11382               $scope.status = response.status;
11383               $scope.data = response.data;
11384             }, function(response) {
11385               $scope.data = response.data || "Request failed";
11386               $scope.status = response.status;
11387           });
11388         };
11390         $scope.updateModel = function(method, url) {
11391           $scope.method = method;
11392           $scope.url = url;
11393         };
11394       }]);
11395 </file>
11396 <file name="http-hello.html">
11397   Hello, $http!
11398 </file>
11399 <file name="protractor.js" type="protractor">
11400   var status = element(by.binding('status'));
11401   var data = element(by.binding('data'));
11402   var fetchBtn = element(by.id('fetchbtn'));
11403   var sampleGetBtn = element(by.id('samplegetbtn'));
11404   var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
11405   var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
11407   it('should make an xhr GET request', function() {
11408     sampleGetBtn.click();
11409     fetchBtn.click();
11410     expect(status.getText()).toMatch('200');
11411     expect(data.getText()).toMatch(/Hello, \$http!/);
11412   });
11414 // Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
11415 // it('should make a JSONP request to angularjs.org', function() {
11416 //   sampleJsonpBtn.click();
11417 //   fetchBtn.click();
11418 //   expect(status.getText()).toMatch('200');
11419 //   expect(data.getText()).toMatch(/Super Hero!/);
11420 // });
11422   it('should make JSONP request to invalid URL and invoke the error handler',
11423       function() {
11424     invalidJsonpBtn.click();
11425     fetchBtn.click();
11426     expect(status.getText()).toMatch('0');
11427     expect(data.getText()).toMatch('Request failed');
11428   });
11429 </file>
11430 </example>
11431      */
11432     function $http(requestConfig) {
11434       if (!isObject(requestConfig)) {
11435         throw minErr('$http')('badreq', 'Http request configuration must be an object.  Received: {0}', requestConfig);
11436       }
11438       if (!isString(requestConfig.url)) {
11439         throw minErr('$http')('badreq', 'Http request configuration url must be a string.  Received: {0}', requestConfig.url);
11440       }
11442       var config = extend({
11443         method: 'get',
11444         transformRequest: defaults.transformRequest,
11445         transformResponse: defaults.transformResponse,
11446         paramSerializer: defaults.paramSerializer
11447       }, requestConfig);
11449       config.headers = mergeHeaders(requestConfig);
11450       config.method = uppercase(config.method);
11451       config.paramSerializer = isString(config.paramSerializer) ?
11452           $injector.get(config.paramSerializer) : config.paramSerializer;
11454       var requestInterceptors = [];
11455       var responseInterceptors = [];
11456       var promise = $q.when(config);
11458       // apply interceptors
11459       forEach(reversedInterceptors, function(interceptor) {
11460         if (interceptor.request || interceptor.requestError) {
11461           requestInterceptors.unshift(interceptor.request, interceptor.requestError);
11462         }
11463         if (interceptor.response || interceptor.responseError) {
11464           responseInterceptors.push(interceptor.response, interceptor.responseError);
11465         }
11466       });
11468       promise = chainInterceptors(promise, requestInterceptors);
11469       promise = promise.then(serverRequest);
11470       promise = chainInterceptors(promise, responseInterceptors);
11472       if (useLegacyPromise) {
11473         promise.success = function(fn) {
11474           assertArgFn(fn, 'fn');
11476           promise.then(function(response) {
11477             fn(response.data, response.status, response.headers, config);
11478           });
11479           return promise;
11480         };
11482         promise.error = function(fn) {
11483           assertArgFn(fn, 'fn');
11485           promise.then(null, function(response) {
11486             fn(response.data, response.status, response.headers, config);
11487           });
11488           return promise;
11489         };
11490       } else {
11491         promise.success = $httpMinErrLegacyFn('success');
11492         promise.error = $httpMinErrLegacyFn('error');
11493       }
11495       return promise;
11498       function chainInterceptors(promise, interceptors) {
11499         for (var i = 0, ii = interceptors.length; i < ii;) {
11500           var thenFn = interceptors[i++];
11501           var rejectFn = interceptors[i++];
11503           promise = promise.then(thenFn, rejectFn);
11504         }
11506         interceptors.length = 0;
11508         return promise;
11509       }
11511       function executeHeaderFns(headers, config) {
11512         var headerContent, processedHeaders = {};
11514         forEach(headers, function(headerFn, header) {
11515           if (isFunction(headerFn)) {
11516             headerContent = headerFn(config);
11517             if (headerContent != null) {
11518               processedHeaders[header] = headerContent;
11519             }
11520           } else {
11521             processedHeaders[header] = headerFn;
11522           }
11523         });
11525         return processedHeaders;
11526       }
11528       function mergeHeaders(config) {
11529         var defHeaders = defaults.headers,
11530             reqHeaders = extend({}, config.headers),
11531             defHeaderName, lowercaseDefHeaderName, reqHeaderName;
11533         defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
11535         // using for-in instead of forEach to avoid unnecessary iteration after header has been found
11536         defaultHeadersIteration:
11537         for (defHeaderName in defHeaders) {
11538           lowercaseDefHeaderName = lowercase(defHeaderName);
11540           for (reqHeaderName in reqHeaders) {
11541             if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
11542               continue defaultHeadersIteration;
11543             }
11544           }
11546           reqHeaders[defHeaderName] = defHeaders[defHeaderName];
11547         }
11549         // execute if header value is a function for merged headers
11550         return executeHeaderFns(reqHeaders, shallowCopy(config));
11551       }
11553       function serverRequest(config) {
11554         var headers = config.headers;
11555         var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
11557         // strip content-type if data is undefined
11558         if (isUndefined(reqData)) {
11559           forEach(headers, function(value, header) {
11560             if (lowercase(header) === 'content-type') {
11561               delete headers[header];
11562             }
11563           });
11564         }
11566         if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
11567           config.withCredentials = defaults.withCredentials;
11568         }
11570         // send request
11571         return sendReq(config, reqData).then(transformResponse, transformResponse);
11572       }
11574       function transformResponse(response) {
11575         // make a copy since the response must be cacheable
11576         var resp = extend({}, response);
11577         resp.data = transformData(response.data, response.headers, response.status,
11578                                   config.transformResponse);
11579         return (isSuccess(response.status))
11580           ? resp
11581           : $q.reject(resp);
11582       }
11583     }
11585     $http.pendingRequests = [];
11587     /**
11588      * @ngdoc method
11589      * @name $http#get
11590      *
11591      * @description
11592      * Shortcut method to perform `GET` request.
11593      *
11594      * @param {string} url Relative or absolute URL specifying the destination of the request
11595      * @param {Object=} config Optional configuration object
11596      * @returns {HttpPromise} Future object
11597      */
11599     /**
11600      * @ngdoc method
11601      * @name $http#delete
11602      *
11603      * @description
11604      * Shortcut method to perform `DELETE` request.
11605      *
11606      * @param {string} url Relative or absolute URL specifying the destination of the request
11607      * @param {Object=} config Optional configuration object
11608      * @returns {HttpPromise} Future object
11609      */
11611     /**
11612      * @ngdoc method
11613      * @name $http#head
11614      *
11615      * @description
11616      * Shortcut method to perform `HEAD` request.
11617      *
11618      * @param {string} url Relative or absolute URL specifying the destination of the request
11619      * @param {Object=} config Optional configuration object
11620      * @returns {HttpPromise} Future object
11621      */
11623     /**
11624      * @ngdoc method
11625      * @name $http#jsonp
11626      *
11627      * @description
11628      * Shortcut method to perform `JSONP` request.
11629      * If you would like to customise where and how the callbacks are stored then try overriding
11630      * or decorating the {@link $jsonpCallbacks} service.
11631      *
11632      * @param {string} url Relative or absolute URL specifying the destination of the request.
11633      *                     The name of the callback should be the string `JSON_CALLBACK`.
11634      * @param {Object=} config Optional configuration object
11635      * @returns {HttpPromise} Future object
11636      */
11637     createShortMethods('get', 'delete', 'head', 'jsonp');
11639     /**
11640      * @ngdoc method
11641      * @name $http#post
11642      *
11643      * @description
11644      * Shortcut method to perform `POST` request.
11645      *
11646      * @param {string} url Relative or absolute URL specifying the destination of the request
11647      * @param {*} data Request content
11648      * @param {Object=} config Optional configuration object
11649      * @returns {HttpPromise} Future object
11650      */
11652     /**
11653      * @ngdoc method
11654      * @name $http#put
11655      *
11656      * @description
11657      * Shortcut method to perform `PUT` request.
11658      *
11659      * @param {string} url Relative or absolute URL specifying the destination of the request
11660      * @param {*} data Request content
11661      * @param {Object=} config Optional configuration object
11662      * @returns {HttpPromise} Future object
11663      */
11665      /**
11666       * @ngdoc method
11667       * @name $http#patch
11668       *
11669       * @description
11670       * Shortcut method to perform `PATCH` request.
11671       *
11672       * @param {string} url Relative or absolute URL specifying the destination of the request
11673       * @param {*} data Request content
11674       * @param {Object=} config Optional configuration object
11675       * @returns {HttpPromise} Future object
11676       */
11677     createShortMethodsWithData('post', 'put', 'patch');
11679         /**
11680          * @ngdoc property
11681          * @name $http#defaults
11682          *
11683          * @description
11684          * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
11685          * default headers, withCredentials as well as request and response transformations.
11686          *
11687          * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
11688          */
11689     $http.defaults = defaults;
11692     return $http;
11695     function createShortMethods(names) {
11696       forEach(arguments, function(name) {
11697         $http[name] = function(url, config) {
11698           return $http(extend({}, config || {}, {
11699             method: name,
11700             url: url
11701           }));
11702         };
11703       });
11704     }
11707     function createShortMethodsWithData(name) {
11708       forEach(arguments, function(name) {
11709         $http[name] = function(url, data, config) {
11710           return $http(extend({}, config || {}, {
11711             method: name,
11712             url: url,
11713             data: data
11714           }));
11715         };
11716       });
11717     }
11720     /**
11721      * Makes the request.
11722      *
11723      * !!! ACCESSES CLOSURE VARS:
11724      * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
11725      */
11726     function sendReq(config, reqData) {
11727       var deferred = $q.defer(),
11728           promise = deferred.promise,
11729           cache,
11730           cachedResp,
11731           reqHeaders = config.headers,
11732           url = buildUrl(config.url, config.paramSerializer(config.params));
11734       $http.pendingRequests.push(config);
11735       promise.then(removePendingReq, removePendingReq);
11738       if ((config.cache || defaults.cache) && config.cache !== false &&
11739           (config.method === 'GET' || config.method === 'JSONP')) {
11740         cache = isObject(config.cache) ? config.cache
11741               : isObject(defaults.cache) ? defaults.cache
11742               : defaultCache;
11743       }
11745       if (cache) {
11746         cachedResp = cache.get(url);
11747         if (isDefined(cachedResp)) {
11748           if (isPromiseLike(cachedResp)) {
11749             // cached request has already been sent, but there is no response yet
11750             cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
11751           } else {
11752             // serving from cache
11753             if (isArray(cachedResp)) {
11754               resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
11755             } else {
11756               resolvePromise(cachedResp, 200, {}, 'OK');
11757             }
11758           }
11759         } else {
11760           // put the promise for the non-transformed response into cache as a placeholder
11761           cache.put(url, promise);
11762         }
11763       }
11766       // if we won't have the response in cache, set the xsrf headers and
11767       // send the request to the backend
11768       if (isUndefined(cachedResp)) {
11769         var xsrfValue = urlIsSameOrigin(config.url)
11770             ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
11771             : undefined;
11772         if (xsrfValue) {
11773           reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
11774         }
11776         $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
11777             config.withCredentials, config.responseType,
11778             createApplyHandlers(config.eventHandlers),
11779             createApplyHandlers(config.uploadEventHandlers));
11780       }
11782       return promise;
11784       function createApplyHandlers(eventHandlers) {
11785         if (eventHandlers) {
11786           var applyHandlers = {};
11787           forEach(eventHandlers, function(eventHandler, key) {
11788             applyHandlers[key] = function(event) {
11789               if (useApplyAsync) {
11790                 $rootScope.$applyAsync(callEventHandler);
11791               } else if ($rootScope.$$phase) {
11792                 callEventHandler();
11793               } else {
11794                 $rootScope.$apply(callEventHandler);
11795               }
11797               function callEventHandler() {
11798                 eventHandler(event);
11799               }
11800             };
11801           });
11802           return applyHandlers;
11803         }
11804       }
11807       /**
11808        * Callback registered to $httpBackend():
11809        *  - caches the response if desired
11810        *  - resolves the raw $http promise
11811        *  - calls $apply
11812        */
11813       function done(status, response, headersString, statusText) {
11814         if (cache) {
11815           if (isSuccess(status)) {
11816             cache.put(url, [status, response, parseHeaders(headersString), statusText]);
11817           } else {
11818             // remove promise from the cache
11819             cache.remove(url);
11820           }
11821         }
11823         function resolveHttpPromise() {
11824           resolvePromise(response, status, headersString, statusText);
11825         }
11827         if (useApplyAsync) {
11828           $rootScope.$applyAsync(resolveHttpPromise);
11829         } else {
11830           resolveHttpPromise();
11831           if (!$rootScope.$$phase) $rootScope.$apply();
11832         }
11833       }
11836       /**
11837        * Resolves the raw $http promise.
11838        */
11839       function resolvePromise(response, status, headers, statusText) {
11840         //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
11841         status = status >= -1 ? status : 0;
11843         (isSuccess(status) ? deferred.resolve : deferred.reject)({
11844           data: response,
11845           status: status,
11846           headers: headersGetter(headers),
11847           config: config,
11848           statusText: statusText
11849         });
11850       }
11852       function resolvePromiseWithResult(result) {
11853         resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
11854       }
11856       function removePendingReq() {
11857         var idx = $http.pendingRequests.indexOf(config);
11858         if (idx !== -1) $http.pendingRequests.splice(idx, 1);
11859       }
11860     }
11863     function buildUrl(url, serializedParams) {
11864       if (serializedParams.length > 0) {
11865         url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
11866       }
11867       return url;
11868     }
11869   }];
11873  * @ngdoc service
11874  * @name $xhrFactory
11876  * @description
11877  * Factory function used to create XMLHttpRequest objects.
11879  * Replace or decorate this service to create your own custom XMLHttpRequest objects.
11881  * ```
11882  * angular.module('myApp', [])
11883  * .factory('$xhrFactory', function() {
11884  *   return function createXhr(method, url) {
11885  *     return new window.XMLHttpRequest({mozSystem: true});
11886  *   };
11887  * });
11888  * ```
11890  * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
11891  * @param {string} url URL of the request.
11892  */
11893 function $xhrFactoryProvider() {
11894   this.$get = function() {
11895     return function createXhr() {
11896       return new window.XMLHttpRequest();
11897     };
11898   };
11902  * @ngdoc service
11903  * @name $httpBackend
11904  * @requires $jsonpCallbacks
11905  * @requires $document
11906  * @requires $xhrFactory
11908  * @description
11909  * HTTP backend used by the {@link ng.$http service} that delegates to
11910  * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
11912  * You should never need to use this service directly, instead use the higher-level abstractions:
11913  * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
11915  * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
11916  * $httpBackend} which can be trained with responses.
11917  */
11918 function $HttpBackendProvider() {
11919   this.$get = ['$browser', '$jsonpCallbacks', '$document', '$xhrFactory', function($browser, $jsonpCallbacks, $document, $xhrFactory) {
11920     return createHttpBackend($browser, $xhrFactory, $browser.defer, $jsonpCallbacks, $document[0]);
11921   }];
11924 function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
11925   // TODO(vojta): fix the signature
11926   return function(method, url, post, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
11927     $browser.$$incOutstandingRequestCount();
11928     url = url || $browser.url();
11930     if (lowercase(method) === 'jsonp') {
11931       var callbackPath = callbacks.createCallback(url);
11932       var jsonpDone = jsonpReq(url, callbackPath, function(status, text) {
11933         // jsonpReq only ever sets status to 200 (OK), 404 (ERROR) or -1 (WAITING)
11934         var response = (status === 200) && callbacks.getResponse(callbackPath);
11935         completeRequest(callback, status, response, "", text);
11936         callbacks.removeCallback(callbackPath);
11937       });
11938     } else {
11940       var xhr = createXhr(method, url);
11942       xhr.open(method, url, true);
11943       forEach(headers, function(value, key) {
11944         if (isDefined(value)) {
11945             xhr.setRequestHeader(key, value);
11946         }
11947       });
11949       xhr.onload = function requestLoaded() {
11950         var statusText = xhr.statusText || '';
11952         // responseText is the old-school way of retrieving response (supported by IE9)
11953         // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
11954         var response = ('response' in xhr) ? xhr.response : xhr.responseText;
11956         // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
11957         var status = xhr.status === 1223 ? 204 : xhr.status;
11959         // fix status code when it is 0 (0 status is undocumented).
11960         // Occurs when accessing file resources or on Android 4.1 stock browser
11961         // while retrieving files from application cache.
11962         if (status === 0) {
11963           status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
11964         }
11966         completeRequest(callback,
11967             status,
11968             response,
11969             xhr.getAllResponseHeaders(),
11970             statusText);
11971       };
11973       var requestError = function() {
11974         // The response is always empty
11975         // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
11976         completeRequest(callback, -1, null, null, '');
11977       };
11979       xhr.onerror = requestError;
11980       xhr.onabort = requestError;
11982       forEach(eventHandlers, function(value, key) {
11983           xhr.addEventListener(key, value);
11984       });
11986       forEach(uploadEventHandlers, function(value, key) {
11987         xhr.upload.addEventListener(key, value);
11988       });
11990       if (withCredentials) {
11991         xhr.withCredentials = true;
11992       }
11994       if (responseType) {
11995         try {
11996           xhr.responseType = responseType;
11997         } catch (e) {
11998           // WebKit added support for the json responseType value on 09/03/2013
11999           // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
12000           // known to throw when setting the value "json" as the response type. Other older
12001           // browsers implementing the responseType
12002           //
12003           // The json response type can be ignored if not supported, because JSON payloads are
12004           // parsed on the client-side regardless.
12005           if (responseType !== 'json') {
12006             throw e;
12007           }
12008         }
12009       }
12011       xhr.send(isUndefined(post) ? null : post);
12012     }
12014     if (timeout > 0) {
12015       var timeoutId = $browserDefer(timeoutRequest, timeout);
12016     } else if (isPromiseLike(timeout)) {
12017       timeout.then(timeoutRequest);
12018     }
12021     function timeoutRequest() {
12022       jsonpDone && jsonpDone();
12023       xhr && xhr.abort();
12024     }
12026     function completeRequest(callback, status, response, headersString, statusText) {
12027       // cancel timeout and subsequent timeout promise resolution
12028       if (isDefined(timeoutId)) {
12029         $browserDefer.cancel(timeoutId);
12030       }
12031       jsonpDone = xhr = null;
12033       callback(status, response, headersString, statusText);
12034       $browser.$$completeOutstandingRequest(noop);
12035     }
12036   };
12038   function jsonpReq(url, callbackPath, done) {
12039     url = url.replace('JSON_CALLBACK', callbackPath);
12040     // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
12041     // - fetches local scripts via XHR and evals them
12042     // - adds and immediately removes script elements from the document
12043     var script = rawDocument.createElement('script'), callback = null;
12044     script.type = "text/javascript";
12045     script.src = url;
12046     script.async = true;
12048     callback = function(event) {
12049       removeEventListenerFn(script, "load", callback);
12050       removeEventListenerFn(script, "error", callback);
12051       rawDocument.body.removeChild(script);
12052       script = null;
12053       var status = -1;
12054       var text = "unknown";
12056       if (event) {
12057         if (event.type === "load" && !callbacks.wasCalled(callbackPath)) {
12058           event = { type: "error" };
12059         }
12060         text = event.type;
12061         status = event.type === "error" ? 404 : 200;
12062       }
12064       if (done) {
12065         done(status, text);
12066       }
12067     };
12069     addEventListenerFn(script, "load", callback);
12070     addEventListenerFn(script, "error", callback);
12071     rawDocument.body.appendChild(script);
12072     return callback;
12073   }
12076 var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
12077 $interpolateMinErr.throwNoconcat = function(text) {
12078   throw $interpolateMinErr('noconcat',
12079       "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
12080       "interpolations that concatenate multiple expressions when a trusted value is " +
12081       "required.  See http://docs.angularjs.org/api/ng.$sce", text);
12084 $interpolateMinErr.interr = function(text, err) {
12085   return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
12089  * @ngdoc provider
12090  * @name $interpolateProvider
12092  * @description
12094  * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
12096  * <div class="alert alert-danger">
12097  * This feature is sometimes used to mix different markup languages, e.g. to wrap an Angular
12098  * template within a Python Jinja template (or any other template language). Mixing templating
12099  * languages is **very dangerous**. The embedding template language will not safely escape Angular
12100  * expressions, so any user-controlled values in the template will cause Cross Site Scripting (XSS)
12101  * security bugs!
12102  * </div>
12104  * @example
12105 <example name="custom-interpolation-markup" module="customInterpolationApp">
12106 <file name="index.html">
12107 <script>
12108   var customInterpolationApp = angular.module('customInterpolationApp', []);
12110   customInterpolationApp.config(function($interpolateProvider) {
12111     $interpolateProvider.startSymbol('//');
12112     $interpolateProvider.endSymbol('//');
12113   });
12116   customInterpolationApp.controller('DemoController', function() {
12117       this.label = "This binding is brought you by // interpolation symbols.";
12118   });
12119 </script>
12120 <div ng-controller="DemoController as demo">
12121     //demo.label//
12122 </div>
12123 </file>
12124 <file name="protractor.js" type="protractor">
12125   it('should interpolate binding with custom symbols', function() {
12126     expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
12127   });
12128 </file>
12129 </example>
12130  */
12131 function $InterpolateProvider() {
12132   var startSymbol = '{{';
12133   var endSymbol = '}}';
12135   /**
12136    * @ngdoc method
12137    * @name $interpolateProvider#startSymbol
12138    * @description
12139    * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
12140    *
12141    * @param {string=} value new value to set the starting symbol to.
12142    * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
12143    */
12144   this.startSymbol = function(value) {
12145     if (value) {
12146       startSymbol = value;
12147       return this;
12148     } else {
12149       return startSymbol;
12150     }
12151   };
12153   /**
12154    * @ngdoc method
12155    * @name $interpolateProvider#endSymbol
12156    * @description
12157    * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
12158    *
12159    * @param {string=} value new value to set the ending symbol to.
12160    * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
12161    */
12162   this.endSymbol = function(value) {
12163     if (value) {
12164       endSymbol = value;
12165       return this;
12166     } else {
12167       return endSymbol;
12168     }
12169   };
12172   this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
12173     var startSymbolLength = startSymbol.length,
12174         endSymbolLength = endSymbol.length,
12175         escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
12176         escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
12178     function escape(ch) {
12179       return '\\\\\\' + ch;
12180     }
12182     function unescapeText(text) {
12183       return text.replace(escapedStartRegexp, startSymbol).
12184         replace(escapedEndRegexp, endSymbol);
12185     }
12187     function stringify(value) {
12188       if (value == null) { // null || undefined
12189         return '';
12190       }
12191       switch (typeof value) {
12192         case 'string':
12193           break;
12194         case 'number':
12195           value = '' + value;
12196           break;
12197         default:
12198           value = toJson(value);
12199       }
12201       return value;
12202     }
12204     //TODO: this is the same as the constantWatchDelegate in parse.js
12205     function constantWatchDelegate(scope, listener, objectEquality, constantInterp) {
12206       var unwatch;
12207       return unwatch = scope.$watch(function constantInterpolateWatch(scope) {
12208         unwatch();
12209         return constantInterp(scope);
12210       }, listener, objectEquality);
12211     }
12213     /**
12214      * @ngdoc service
12215      * @name $interpolate
12216      * @kind function
12217      *
12218      * @requires $parse
12219      * @requires $sce
12220      *
12221      * @description
12222      *
12223      * Compiles a string with markup into an interpolation function. This service is used by the
12224      * HTML {@link ng.$compile $compile} service for data binding. See
12225      * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
12226      * interpolation markup.
12227      *
12228      *
12229      * ```js
12230      *   var $interpolate = ...; // injected
12231      *   var exp = $interpolate('Hello {{name | uppercase}}!');
12232      *   expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
12233      * ```
12234      *
12235      * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
12236      * `true`, the interpolation function will return `undefined` unless all embedded expressions
12237      * evaluate to a value other than `undefined`.
12238      *
12239      * ```js
12240      *   var $interpolate = ...; // injected
12241      *   var context = {greeting: 'Hello', name: undefined };
12242      *
12243      *   // default "forgiving" mode
12244      *   var exp = $interpolate('{{greeting}} {{name}}!');
12245      *   expect(exp(context)).toEqual('Hello !');
12246      *
12247      *   // "allOrNothing" mode
12248      *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
12249      *   expect(exp(context)).toBeUndefined();
12250      *   context.name = 'Angular';
12251      *   expect(exp(context)).toEqual('Hello Angular!');
12252      * ```
12253      *
12254      * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
12255      *
12256      * #### Escaped Interpolation
12257      * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
12258      * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
12259      * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
12260      * or binding.
12261      *
12262      * This enables web-servers to prevent script injection attacks and defacing attacks, to some
12263      * degree, while also enabling code examples to work without relying on the
12264      * {@link ng.directive:ngNonBindable ngNonBindable} directive.
12265      *
12266      * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
12267      * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
12268      * interpolation start/end markers with their escaped counterparts.**
12269      *
12270      * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
12271      * output when the $interpolate service processes the text. So, for HTML elements interpolated
12272      * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
12273      * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
12274      * this is typically useful only when user-data is used in rendering a template from the server, or
12275      * when otherwise untrusted data is used by a directive.
12276      *
12277      * <example>
12278      *  <file name="index.html">
12279      *    <div ng-init="username='A user'">
12280      *      <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
12281      *        </p>
12282      *      <p><strong>{{username}}</strong> attempts to inject code which will deface the
12283      *        application, but fails to accomplish their task, because the server has correctly
12284      *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
12285      *        characters.</p>
12286      *      <p>Instead, the result of the attempted script injection is visible, and can be removed
12287      *        from the database by an administrator.</p>
12288      *    </div>
12289      *  </file>
12290      * </example>
12291      *
12292      * @knownIssue
12293      * It is currently not possible for an interpolated expression to contain the interpolation end
12294      * symbol. For example, `{{ '}}' }}` will be incorrectly interpreted as `{{ ' }}` + `' }}`, i.e.
12295      * an interpolated expression consisting of a single-quote (`'`) and the `' }}` string.
12296      *
12297      * @knownIssue
12298      * All directives and components must use the standard `{{` `}}` interpolation symbols
12299      * in their templates. If you change the application interpolation symbols the {@link $compile}
12300      * service will attempt to denormalize the standard symbols to the custom symbols.
12301      * The denormalization process is not clever enough to know not to replace instances of the standard
12302      * symbols where they would not normally be treated as interpolation symbols. For example in the following
12303      * code snippet the closing braces of the literal object will get incorrectly denormalized:
12304      *
12305      * ```
12306      * <div data-context='{"context":{"id":3,"type":"page"}}">
12307      * ```
12308      *
12309      * The workaround is to ensure that such instances are separated by whitespace:
12310      * ```
12311      * <div data-context='{"context":{"id":3,"type":"page"} }">
12312      * ```
12313      *
12314      * See https://github.com/angular/angular.js/pull/14610#issuecomment-219401099 for more information.
12315      *
12316      * @param {string} text The text with markup to interpolate.
12317      * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
12318      *    embedded expression in order to return an interpolation function. Strings with no
12319      *    embedded expression will return null for the interpolation function.
12320      * @param {string=} trustedContext when provided, the returned function passes the interpolated
12321      *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
12322      *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
12323      *    provides Strict Contextual Escaping for details.
12324      * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
12325      *    unless all embedded expressions evaluate to a value other than `undefined`.
12326      * @returns {function(context)} an interpolation function which is used to compute the
12327      *    interpolated string. The function has these parameters:
12328      *
12329      * - `context`: evaluation context for all expressions embedded in the interpolated text
12330      */
12331     function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
12332       // Provide a quick exit and simplified result function for text with no interpolation
12333       if (!text.length || text.indexOf(startSymbol) === -1) {
12334         var constantInterp;
12335         if (!mustHaveExpression) {
12336           var unescapedText = unescapeText(text);
12337           constantInterp = valueFn(unescapedText);
12338           constantInterp.exp = text;
12339           constantInterp.expressions = [];
12340           constantInterp.$$watchDelegate = constantWatchDelegate;
12341         }
12342         return constantInterp;
12343       }
12345       allOrNothing = !!allOrNothing;
12346       var startIndex,
12347           endIndex,
12348           index = 0,
12349           expressions = [],
12350           parseFns = [],
12351           textLength = text.length,
12352           exp,
12353           concat = [],
12354           expressionPositions = [];
12356       while (index < textLength) {
12357         if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
12358              ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
12359           if (index !== startIndex) {
12360             concat.push(unescapeText(text.substring(index, startIndex)));
12361           }
12362           exp = text.substring(startIndex + startSymbolLength, endIndex);
12363           expressions.push(exp);
12364           parseFns.push($parse(exp, parseStringifyInterceptor));
12365           index = endIndex + endSymbolLength;
12366           expressionPositions.push(concat.length);
12367           concat.push('');
12368         } else {
12369           // we did not find an interpolation, so we have to add the remainder to the separators array
12370           if (index !== textLength) {
12371             concat.push(unescapeText(text.substring(index)));
12372           }
12373           break;
12374         }
12375       }
12377       // Concatenating expressions makes it hard to reason about whether some combination of
12378       // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
12379       // single expression be used for iframe[src], object[src], etc., we ensure that the value
12380       // that's used is assigned or constructed by some JS code somewhere that is more testable or
12381       // make it obvious that you bound the value to some user controlled value.  This helps reduce
12382       // the load when auditing for XSS issues.
12383       if (trustedContext && concat.length > 1) {
12384           $interpolateMinErr.throwNoconcat(text);
12385       }
12387       if (!mustHaveExpression || expressions.length) {
12388         var compute = function(values) {
12389           for (var i = 0, ii = expressions.length; i < ii; i++) {
12390             if (allOrNothing && isUndefined(values[i])) return;
12391             concat[expressionPositions[i]] = values[i];
12392           }
12393           return concat.join('');
12394         };
12396         var getValue = function(value) {
12397           return trustedContext ?
12398             $sce.getTrusted(trustedContext, value) :
12399             $sce.valueOf(value);
12400         };
12402         return extend(function interpolationFn(context) {
12403             var i = 0;
12404             var ii = expressions.length;
12405             var values = new Array(ii);
12407             try {
12408               for (; i < ii; i++) {
12409                 values[i] = parseFns[i](context);
12410               }
12412               return compute(values);
12413             } catch (err) {
12414               $exceptionHandler($interpolateMinErr.interr(text, err));
12415             }
12417           }, {
12418           // all of these properties are undocumented for now
12419           exp: text, //just for compatibility with regular watchers created via $watch
12420           expressions: expressions,
12421           $$watchDelegate: function(scope, listener) {
12422             var lastValue;
12423             return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
12424               var currValue = compute(values);
12425               if (isFunction(listener)) {
12426                 listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
12427               }
12428               lastValue = currValue;
12429             });
12430           }
12431         });
12432       }
12434       function parseStringifyInterceptor(value) {
12435         try {
12436           value = getValue(value);
12437           return allOrNothing && !isDefined(value) ? value : stringify(value);
12438         } catch (err) {
12439           $exceptionHandler($interpolateMinErr.interr(text, err));
12440         }
12441       }
12442     }
12445     /**
12446      * @ngdoc method
12447      * @name $interpolate#startSymbol
12448      * @description
12449      * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
12450      *
12451      * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
12452      * the symbol.
12453      *
12454      * @returns {string} start symbol.
12455      */
12456     $interpolate.startSymbol = function() {
12457       return startSymbol;
12458     };
12461     /**
12462      * @ngdoc method
12463      * @name $interpolate#endSymbol
12464      * @description
12465      * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
12466      *
12467      * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
12468      * the symbol.
12469      *
12470      * @returns {string} end symbol.
12471      */
12472     $interpolate.endSymbol = function() {
12473       return endSymbol;
12474     };
12476     return $interpolate;
12477   }];
12480 function $IntervalProvider() {
12481   this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser',
12482        function($rootScope,   $window,   $q,   $$q,   $browser) {
12483     var intervals = {};
12486      /**
12487       * @ngdoc service
12488       * @name $interval
12489       *
12490       * @description
12491       * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
12492       * milliseconds.
12493       *
12494       * The return value of registering an interval function is a promise. This promise will be
12495       * notified upon each tick of the interval, and will be resolved after `count` iterations, or
12496       * run indefinitely if `count` is not defined. The value of the notification will be the
12497       * number of iterations that have run.
12498       * To cancel an interval, call `$interval.cancel(promise)`.
12499       *
12500       * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
12501       * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
12502       * time.
12503       *
12504       * <div class="alert alert-warning">
12505       * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
12506       * with them.  In particular they are not automatically destroyed when a controller's scope or a
12507       * directive's element are destroyed.
12508       * You should take this into consideration and make sure to always cancel the interval at the
12509       * appropriate moment.  See the example below for more details on how and when to do this.
12510       * </div>
12511       *
12512       * @param {function()} fn A function that should be called repeatedly.
12513       * @param {number} delay Number of milliseconds between each function call.
12514       * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
12515       *   indefinitely.
12516       * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
12517       *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
12518       * @param {...*=} Pass additional parameters to the executed function.
12519       * @returns {promise} A promise which will be notified on each iteration.
12520       *
12521       * @example
12522       * <example module="intervalExample">
12523       * <file name="index.html">
12524       *   <script>
12525       *     angular.module('intervalExample', [])
12526       *       .controller('ExampleController', ['$scope', '$interval',
12527       *         function($scope, $interval) {
12528       *           $scope.format = 'M/d/yy h:mm:ss a';
12529       *           $scope.blood_1 = 100;
12530       *           $scope.blood_2 = 120;
12531       *
12532       *           var stop;
12533       *           $scope.fight = function() {
12534       *             // Don't start a new fight if we are already fighting
12535       *             if ( angular.isDefined(stop) ) return;
12536       *
12537       *             stop = $interval(function() {
12538       *               if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
12539       *                 $scope.blood_1 = $scope.blood_1 - 3;
12540       *                 $scope.blood_2 = $scope.blood_2 - 4;
12541       *               } else {
12542       *                 $scope.stopFight();
12543       *               }
12544       *             }, 100);
12545       *           };
12546       *
12547       *           $scope.stopFight = function() {
12548       *             if (angular.isDefined(stop)) {
12549       *               $interval.cancel(stop);
12550       *               stop = undefined;
12551       *             }
12552       *           };
12553       *
12554       *           $scope.resetFight = function() {
12555       *             $scope.blood_1 = 100;
12556       *             $scope.blood_2 = 120;
12557       *           };
12558       *
12559       *           $scope.$on('$destroy', function() {
12560       *             // Make sure that the interval is destroyed too
12561       *             $scope.stopFight();
12562       *           });
12563       *         }])
12564       *       // Register the 'myCurrentTime' directive factory method.
12565       *       // We inject $interval and dateFilter service since the factory method is DI.
12566       *       .directive('myCurrentTime', ['$interval', 'dateFilter',
12567       *         function($interval, dateFilter) {
12568       *           // return the directive link function. (compile function not needed)
12569       *           return function(scope, element, attrs) {
12570       *             var format,  // date format
12571       *                 stopTime; // so that we can cancel the time updates
12572       *
12573       *             // used to update the UI
12574       *             function updateTime() {
12575       *               element.text(dateFilter(new Date(), format));
12576       *             }
12577       *
12578       *             // watch the expression, and update the UI on change.
12579       *             scope.$watch(attrs.myCurrentTime, function(value) {
12580       *               format = value;
12581       *               updateTime();
12582       *             });
12583       *
12584       *             stopTime = $interval(updateTime, 1000);
12585       *
12586       *             // listen on DOM destroy (removal) event, and cancel the next UI update
12587       *             // to prevent updating time after the DOM element was removed.
12588       *             element.on('$destroy', function() {
12589       *               $interval.cancel(stopTime);
12590       *             });
12591       *           }
12592       *         }]);
12593       *   </script>
12594       *
12595       *   <div>
12596       *     <div ng-controller="ExampleController">
12597       *       <label>Date format: <input ng-model="format"></label> <hr/>
12598       *       Current time is: <span my-current-time="format"></span>
12599       *       <hr/>
12600       *       Blood 1 : <font color='red'>{{blood_1}}</font>
12601       *       Blood 2 : <font color='red'>{{blood_2}}</font>
12602       *       <button type="button" data-ng-click="fight()">Fight</button>
12603       *       <button type="button" data-ng-click="stopFight()">StopFight</button>
12604       *       <button type="button" data-ng-click="resetFight()">resetFight</button>
12605       *     </div>
12606       *   </div>
12607       *
12608       * </file>
12609       * </example>
12610       */
12611     function interval(fn, delay, count, invokeApply) {
12612       var hasParams = arguments.length > 4,
12613           args = hasParams ? sliceArgs(arguments, 4) : [],
12614           setInterval = $window.setInterval,
12615           clearInterval = $window.clearInterval,
12616           iteration = 0,
12617           skipApply = (isDefined(invokeApply) && !invokeApply),
12618           deferred = (skipApply ? $$q : $q).defer(),
12619           promise = deferred.promise;
12621       count = isDefined(count) ? count : 0;
12623       promise.$$intervalId = setInterval(function tick() {
12624         if (skipApply) {
12625           $browser.defer(callback);
12626         } else {
12627           $rootScope.$evalAsync(callback);
12628         }
12629         deferred.notify(iteration++);
12631         if (count > 0 && iteration >= count) {
12632           deferred.resolve(iteration);
12633           clearInterval(promise.$$intervalId);
12634           delete intervals[promise.$$intervalId];
12635         }
12637         if (!skipApply) $rootScope.$apply();
12639       }, delay);
12641       intervals[promise.$$intervalId] = deferred;
12643       return promise;
12645       function callback() {
12646         if (!hasParams) {
12647           fn(iteration);
12648         } else {
12649           fn.apply(null, args);
12650         }
12651       }
12652     }
12655      /**
12656       * @ngdoc method
12657       * @name $interval#cancel
12658       *
12659       * @description
12660       * Cancels a task associated with the `promise`.
12661       *
12662       * @param {Promise=} promise returned by the `$interval` function.
12663       * @returns {boolean} Returns `true` if the task was successfully canceled.
12664       */
12665     interval.cancel = function(promise) {
12666       if (promise && promise.$$intervalId in intervals) {
12667         intervals[promise.$$intervalId].reject('canceled');
12668         $window.clearInterval(promise.$$intervalId);
12669         delete intervals[promise.$$intervalId];
12670         return true;
12671       }
12672       return false;
12673     };
12675     return interval;
12676   }];
12680  * @ngdoc service
12681  * @name $jsonpCallbacks
12682  * @requires $window
12683  * @description
12684  * This service handles the lifecycle of callbacks to handle JSONP requests.
12685  * Override this service if you wish to customise where the callbacks are stored and
12686  * how they vary compared to the requested url.
12687  */
12688 var $jsonpCallbacksProvider = function() {
12689   this.$get = ['$window', function($window) {
12690     var callbacks = $window.angular.callbacks;
12691     var callbackMap = {};
12693     function createCallback(callbackId) {
12694       var callback = function(data) {
12695         callback.data = data;
12696         callback.called = true;
12697       };
12698       callback.id = callbackId;
12699       return callback;
12700     }
12702     return {
12703       /**
12704        * @ngdoc method
12705        * @name $jsonpCallbacks#createCallback
12706        * @param {string} url the url of the JSONP request
12707        * @returns {string} the callback path to send to the server as part of the JSONP request
12708        * @description
12709        * {@link $httpBackend} calls this method to create a callback and get hold of the path to the callback
12710        * to pass to the server, which will be used to call the callback with its payload in the JSONP response.
12711        */
12712       createCallback: function(url) {
12713         var callbackId = '_' + (callbacks.$$counter++).toString(36);
12714         var callbackPath = 'angular.callbacks.' + callbackId;
12715         var callback = createCallback(callbackId);
12716         callbackMap[callbackPath] = callbacks[callbackId] = callback;
12717         return callbackPath;
12718       },
12719       /**
12720        * @ngdoc method
12721        * @name $jsonpCallbacks#wasCalled
12722        * @param {string} callbackPath the path to the callback that was sent in the JSONP request
12723        * @returns {boolean} whether the callback has been called, as a result of the JSONP response
12724        * @description
12725        * {@link $httpBackend} calls this method to find out whether the JSONP response actually called the
12726        * callback that was passed in the request.
12727        */
12728       wasCalled: function(callbackPath) {
12729         return callbackMap[callbackPath].called;
12730       },
12731       /**
12732        * @ngdoc method
12733        * @name $jsonpCallbacks#getResponse
12734        * @param {string} callbackPath the path to the callback that was sent in the JSONP request
12735        * @returns {*} the data received from the response via the registered callback
12736        * @description
12737        * {@link $httpBackend} calls this method to get hold of the data that was provided to the callback
12738        * in the JSONP response.
12739        */
12740       getResponse: function(callbackPath) {
12741         return callbackMap[callbackPath].data;
12742       },
12743       /**
12744        * @ngdoc method
12745        * @name $jsonpCallbacks#removeCallback
12746        * @param {string} callbackPath the path to the callback that was sent in the JSONP request
12747        * @description
12748        * {@link $httpBackend} calls this method to remove the callback after the JSONP request has
12749        * completed or timed-out.
12750        */
12751       removeCallback: function(callbackPath) {
12752         var callback = callbackMap[callbackPath];
12753         delete callbacks[callback.id];
12754         delete callbackMap[callbackPath];
12755       }
12756     };
12757   }];
12761  * @ngdoc service
12762  * @name $locale
12764  * @description
12765  * $locale service provides localization rules for various Angular components. As of right now the
12766  * only public api is:
12768  * * `id` â€“ `{string}` â€“ locale id formatted as `languageId-countryId` (e.g. `en-us`)
12769  */
12771 var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
12772     DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
12773 var $locationMinErr = minErr('$location');
12777  * Encode path using encodeUriSegment, ignoring forward slashes
12779  * @param {string} path Path to encode
12780  * @returns {string}
12781  */
12782 function encodePath(path) {
12783   var segments = path.split('/'),
12784       i = segments.length;
12786   while (i--) {
12787     segments[i] = encodeUriSegment(segments[i]);
12788   }
12790   return segments.join('/');
12793 function parseAbsoluteUrl(absoluteUrl, locationObj) {
12794   var parsedUrl = urlResolve(absoluteUrl);
12796   locationObj.$$protocol = parsedUrl.protocol;
12797   locationObj.$$host = parsedUrl.hostname;
12798   locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
12802 function parseAppUrl(relativeUrl, locationObj) {
12803   var prefixed = (relativeUrl.charAt(0) !== '/');
12804   if (prefixed) {
12805     relativeUrl = '/' + relativeUrl;
12806   }
12807   var match = urlResolve(relativeUrl);
12808   locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
12809       match.pathname.substring(1) : match.pathname);
12810   locationObj.$$search = parseKeyValue(match.search);
12811   locationObj.$$hash = decodeURIComponent(match.hash);
12813   // make sure path starts with '/';
12814   if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
12815     locationObj.$$path = '/' + locationObj.$$path;
12816   }
12819 function startsWith(haystack, needle) {
12820   return haystack.lastIndexOf(needle, 0) === 0;
12825  * @param {string} base
12826  * @param {string} url
12827  * @returns {string} returns text from `url` after `base` or `undefined` if it does not begin with
12828  *                   the expected string.
12829  */
12830 function stripBaseUrl(base, url) {
12831   if (startsWith(url, base)) {
12832     return url.substr(base.length);
12833   }
12837 function stripHash(url) {
12838   var index = url.indexOf('#');
12839   return index == -1 ? url : url.substr(0, index);
12842 function trimEmptyHash(url) {
12843   return url.replace(/(#.+)|#$/, '$1');
12847 function stripFile(url) {
12848   return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
12851 /* return the server only (scheme://host:port) */
12852 function serverBase(url) {
12853   return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
12858  * LocationHtml5Url represents an url
12859  * This object is exposed as $location service when HTML5 mode is enabled and supported
12861  * @constructor
12862  * @param {string} appBase application base URL
12863  * @param {string} appBaseNoFile application base URL stripped of any filename
12864  * @param {string} basePrefix url path prefix
12865  */
12866 function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
12867   this.$$html5 = true;
12868   basePrefix = basePrefix || '';
12869   parseAbsoluteUrl(appBase, this);
12872   /**
12873    * Parse given html5 (regular) url string into properties
12874    * @param {string} url HTML5 url
12875    * @private
12876    */
12877   this.$$parse = function(url) {
12878     var pathUrl = stripBaseUrl(appBaseNoFile, url);
12879     if (!isString(pathUrl)) {
12880       throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
12881           appBaseNoFile);
12882     }
12884     parseAppUrl(pathUrl, this);
12886     if (!this.$$path) {
12887       this.$$path = '/';
12888     }
12890     this.$$compose();
12891   };
12893   /**
12894    * Compose url and update `absUrl` property
12895    * @private
12896    */
12897   this.$$compose = function() {
12898     var search = toKeyValue(this.$$search),
12899         hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
12901     this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
12902     this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
12903   };
12905   this.$$parseLinkUrl = function(url, relHref) {
12906     if (relHref && relHref[0] === '#') {
12907       // special case for links to hash fragments:
12908       // keep the old url and only replace the hash fragment
12909       this.hash(relHref.slice(1));
12910       return true;
12911     }
12912     var appUrl, prevAppUrl;
12913     var rewrittenUrl;
12915     if (isDefined(appUrl = stripBaseUrl(appBase, url))) {
12916       prevAppUrl = appUrl;
12917       if (isDefined(appUrl = stripBaseUrl(basePrefix, appUrl))) {
12918         rewrittenUrl = appBaseNoFile + (stripBaseUrl('/', appUrl) || appUrl);
12919       } else {
12920         rewrittenUrl = appBase + prevAppUrl;
12921       }
12922     } else if (isDefined(appUrl = stripBaseUrl(appBaseNoFile, url))) {
12923       rewrittenUrl = appBaseNoFile + appUrl;
12924     } else if (appBaseNoFile == url + '/') {
12925       rewrittenUrl = appBaseNoFile;
12926     }
12927     if (rewrittenUrl) {
12928       this.$$parse(rewrittenUrl);
12929     }
12930     return !!rewrittenUrl;
12931   };
12936  * LocationHashbangUrl represents url
12937  * This object is exposed as $location service when developer doesn't opt into html5 mode.
12938  * It also serves as the base class for html5 mode fallback on legacy browsers.
12940  * @constructor
12941  * @param {string} appBase application base URL
12942  * @param {string} appBaseNoFile application base URL stripped of any filename
12943  * @param {string} hashPrefix hashbang prefix
12944  */
12945 function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
12947   parseAbsoluteUrl(appBase, this);
12950   /**
12951    * Parse given hashbang url into properties
12952    * @param {string} url Hashbang url
12953    * @private
12954    */
12955   this.$$parse = function(url) {
12956     var withoutBaseUrl = stripBaseUrl(appBase, url) || stripBaseUrl(appBaseNoFile, url);
12957     var withoutHashUrl;
12959     if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
12961       // The rest of the url starts with a hash so we have
12962       // got either a hashbang path or a plain hash fragment
12963       withoutHashUrl = stripBaseUrl(hashPrefix, withoutBaseUrl);
12964       if (isUndefined(withoutHashUrl)) {
12965         // There was no hashbang prefix so we just have a hash fragment
12966         withoutHashUrl = withoutBaseUrl;
12967       }
12969     } else {
12970       // There was no hashbang path nor hash fragment:
12971       // If we are in HTML5 mode we use what is left as the path;
12972       // Otherwise we ignore what is left
12973       if (this.$$html5) {
12974         withoutHashUrl = withoutBaseUrl;
12975       } else {
12976         withoutHashUrl = '';
12977         if (isUndefined(withoutBaseUrl)) {
12978           appBase = url;
12979           this.replace();
12980         }
12981       }
12982     }
12984     parseAppUrl(withoutHashUrl, this);
12986     this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
12988     this.$$compose();
12990     /*
12991      * In Windows, on an anchor node on documents loaded from
12992      * the filesystem, the browser will return a pathname
12993      * prefixed with the drive name ('/C:/path') when a
12994      * pathname without a drive is set:
12995      *  * a.setAttribute('href', '/foo')
12996      *   * a.pathname === '/C:/foo' //true
12997      *
12998      * Inside of Angular, we're always using pathnames that
12999      * do not include drive names for routing.
13000      */
13001     function removeWindowsDriveName(path, url, base) {
13002       /*
13003       Matches paths for file protocol on windows,
13004       such as /C:/foo/bar, and captures only /foo/bar.
13005       */
13006       var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
13008       var firstPathSegmentMatch;
13010       //Get the relative path from the input URL.
13011       if (startsWith(url, base)) {
13012         url = url.replace(base, '');
13013       }
13015       // The input URL intentionally contains a first path segment that ends with a colon.
13016       if (windowsFilePathExp.exec(url)) {
13017         return path;
13018       }
13020       firstPathSegmentMatch = windowsFilePathExp.exec(path);
13021       return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
13022     }
13023   };
13025   /**
13026    * Compose hashbang url and update `absUrl` property
13027    * @private
13028    */
13029   this.$$compose = function() {
13030     var search = toKeyValue(this.$$search),
13031         hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
13033     this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
13034     this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
13035   };
13037   this.$$parseLinkUrl = function(url, relHref) {
13038     if (stripHash(appBase) == stripHash(url)) {
13039       this.$$parse(url);
13040       return true;
13041     }
13042     return false;
13043   };
13048  * LocationHashbangUrl represents url
13049  * This object is exposed as $location service when html5 history api is enabled but the browser
13050  * does not support it.
13052  * @constructor
13053  * @param {string} appBase application base URL
13054  * @param {string} appBaseNoFile application base URL stripped of any filename
13055  * @param {string} hashPrefix hashbang prefix
13056  */
13057 function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
13058   this.$$html5 = true;
13059   LocationHashbangUrl.apply(this, arguments);
13061   this.$$parseLinkUrl = function(url, relHref) {
13062     if (relHref && relHref[0] === '#') {
13063       // special case for links to hash fragments:
13064       // keep the old url and only replace the hash fragment
13065       this.hash(relHref.slice(1));
13066       return true;
13067     }
13069     var rewrittenUrl;
13070     var appUrl;
13072     if (appBase == stripHash(url)) {
13073       rewrittenUrl = url;
13074     } else if ((appUrl = stripBaseUrl(appBaseNoFile, url))) {
13075       rewrittenUrl = appBase + hashPrefix + appUrl;
13076     } else if (appBaseNoFile === url + '/') {
13077       rewrittenUrl = appBaseNoFile;
13078     }
13079     if (rewrittenUrl) {
13080       this.$$parse(rewrittenUrl);
13081     }
13082     return !!rewrittenUrl;
13083   };
13085   this.$$compose = function() {
13086     var search = toKeyValue(this.$$search),
13087         hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
13089     this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
13090     // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
13091     this.$$absUrl = appBase + hashPrefix + this.$$url;
13092   };
13097 var locationPrototype = {
13099   /**
13100    * Ensure absolute url is initialized.
13101    * @private
13102    */
13103   $$absUrl:'',
13105   /**
13106    * Are we in html5 mode?
13107    * @private
13108    */
13109   $$html5: false,
13111   /**
13112    * Has any change been replacing?
13113    * @private
13114    */
13115   $$replace: false,
13117   /**
13118    * @ngdoc method
13119    * @name $location#absUrl
13120    *
13121    * @description
13122    * This method is getter only.
13123    *
13124    * Return full url representation with all segments encoded according to rules specified in
13125    * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
13126    *
13127    *
13128    * ```js
13129    * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
13130    * var absUrl = $location.absUrl();
13131    * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
13132    * ```
13133    *
13134    * @return {string} full url
13135    */
13136   absUrl: locationGetter('$$absUrl'),
13138   /**
13139    * @ngdoc method
13140    * @name $location#url
13141    *
13142    * @description
13143    * This method is getter / setter.
13144    *
13145    * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
13146    *
13147    * Change path, search and hash, when called with parameter and return `$location`.
13148    *
13149    *
13150    * ```js
13151    * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
13152    * var url = $location.url();
13153    * // => "/some/path?foo=bar&baz=xoxo"
13154    * ```
13155    *
13156    * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
13157    * @return {string} url
13158    */
13159   url: function(url) {
13160     if (isUndefined(url)) {
13161       return this.$$url;
13162     }
13164     var match = PATH_MATCH.exec(url);
13165     if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
13166     if (match[2] || match[1] || url === '') this.search(match[3] || '');
13167     this.hash(match[5] || '');
13169     return this;
13170   },
13172   /**
13173    * @ngdoc method
13174    * @name $location#protocol
13175    *
13176    * @description
13177    * This method is getter only.
13178    *
13179    * Return protocol of current url.
13180    *
13181    *
13182    * ```js
13183    * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
13184    * var protocol = $location.protocol();
13185    * // => "http"
13186    * ```
13187    *
13188    * @return {string} protocol of current url
13189    */
13190   protocol: locationGetter('$$protocol'),
13192   /**
13193    * @ngdoc method
13194    * @name $location#host
13195    *
13196    * @description
13197    * This method is getter only.
13198    *
13199    * Return host of current url.
13200    *
13201    * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
13202    *
13203    *
13204    * ```js
13205    * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
13206    * var host = $location.host();
13207    * // => "example.com"
13208    *
13209    * // given url http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo
13210    * host = $location.host();
13211    * // => "example.com"
13212    * host = location.host;
13213    * // => "example.com:8080"
13214    * ```
13215    *
13216    * @return {string} host of current url.
13217    */
13218   host: locationGetter('$$host'),
13220   /**
13221    * @ngdoc method
13222    * @name $location#port
13223    *
13224    * @description
13225    * This method is getter only.
13226    *
13227    * Return port of current url.
13228    *
13229    *
13230    * ```js
13231    * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
13232    * var port = $location.port();
13233    * // => 80
13234    * ```
13235    *
13236    * @return {Number} port
13237    */
13238   port: locationGetter('$$port'),
13240   /**
13241    * @ngdoc method
13242    * @name $location#path
13243    *
13244    * @description
13245    * This method is getter / setter.
13246    *
13247    * Return path of current url when called without any parameter.
13248    *
13249    * Change path when called with parameter and return `$location`.
13250    *
13251    * Note: Path should always begin with forward slash (/), this method will add the forward slash
13252    * if it is missing.
13253    *
13254    *
13255    * ```js
13256    * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
13257    * var path = $location.path();
13258    * // => "/some/path"
13259    * ```
13260    *
13261    * @param {(string|number)=} path New path
13262    * @return {(string|object)} path if called with no parameters, or `$location` if called with a parameter
13263    */
13264   path: locationGetterSetter('$$path', function(path) {
13265     path = path !== null ? path.toString() : '';
13266     return path.charAt(0) == '/' ? path : '/' + path;
13267   }),
13269   /**
13270    * @ngdoc method
13271    * @name $location#search
13272    *
13273    * @description
13274    * This method is getter / setter.
13275    *
13276    * Return search part (as object) of current url when called without any parameter.
13277    *
13278    * Change search part when called with parameter and return `$location`.
13279    *
13280    *
13281    * ```js
13282    * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
13283    * var searchObject = $location.search();
13284    * // => {foo: 'bar', baz: 'xoxo'}
13285    *
13286    * // set foo to 'yipee'
13287    * $location.search('foo', 'yipee');
13288    * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
13289    * ```
13290    *
13291    * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
13292    * hash object.
13293    *
13294    * When called with a single argument the method acts as a setter, setting the `search` component
13295    * of `$location` to the specified value.
13296    *
13297    * If the argument is a hash object containing an array of values, these values will be encoded
13298    * as duplicate search parameters in the url.
13299    *
13300    * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
13301    * will override only a single search property.
13302    *
13303    * If `paramValue` is an array, it will override the property of the `search` component of
13304    * `$location` specified via the first argument.
13305    *
13306    * If `paramValue` is `null`, the property specified via the first argument will be deleted.
13307    *
13308    * If `paramValue` is `true`, the property specified via the first argument will be added with no
13309    * value nor trailing equal sign.
13310    *
13311    * @return {Object} If called with no arguments returns the parsed `search` object. If called with
13312    * one or more arguments returns `$location` object itself.
13313    */
13314   search: function(search, paramValue) {
13315     switch (arguments.length) {
13316       case 0:
13317         return this.$$search;
13318       case 1:
13319         if (isString(search) || isNumber(search)) {
13320           search = search.toString();
13321           this.$$search = parseKeyValue(search);
13322         } else if (isObject(search)) {
13323           search = copy(search, {});
13324           // remove object undefined or null properties
13325           forEach(search, function(value, key) {
13326             if (value == null) delete search[key];
13327           });
13329           this.$$search = search;
13330         } else {
13331           throw $locationMinErr('isrcharg',
13332               'The first argument of the `$location#search()` call must be a string or an object.');
13333         }
13334         break;
13335       default:
13336         if (isUndefined(paramValue) || paramValue === null) {
13337           delete this.$$search[search];
13338         } else {
13339           this.$$search[search] = paramValue;
13340         }
13341     }
13343     this.$$compose();
13344     return this;
13345   },
13347   /**
13348    * @ngdoc method
13349    * @name $location#hash
13350    *
13351    * @description
13352    * This method is getter / setter.
13353    *
13354    * Returns the hash fragment when called without any parameters.
13355    *
13356    * Changes the hash fragment when called with a parameter and returns `$location`.
13357    *
13358    *
13359    * ```js
13360    * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
13361    * var hash = $location.hash();
13362    * // => "hashValue"
13363    * ```
13364    *
13365    * @param {(string|number)=} hash New hash fragment
13366    * @return {string} hash
13367    */
13368   hash: locationGetterSetter('$$hash', function(hash) {
13369     return hash !== null ? hash.toString() : '';
13370   }),
13372   /**
13373    * @ngdoc method
13374    * @name $location#replace
13375    *
13376    * @description
13377    * If called, all changes to $location during the current `$digest` will replace the current history
13378    * record, instead of adding a new one.
13379    */
13380   replace: function() {
13381     this.$$replace = true;
13382     return this;
13383   }
13386 forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
13387   Location.prototype = Object.create(locationPrototype);
13389   /**
13390    * @ngdoc method
13391    * @name $location#state
13392    *
13393    * @description
13394    * This method is getter / setter.
13395    *
13396    * Return the history state object when called without any parameter.
13397    *
13398    * Change the history state object when called with one parameter and return `$location`.
13399    * The state object is later passed to `pushState` or `replaceState`.
13400    *
13401    * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
13402    * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
13403    * older browsers (like IE9 or Android < 4.0), don't use this method.
13404    *
13405    * @param {object=} state State object for pushState or replaceState
13406    * @return {object} state
13407    */
13408   Location.prototype.state = function(state) {
13409     if (!arguments.length) {
13410       return this.$$state;
13411     }
13413     if (Location !== LocationHtml5Url || !this.$$html5) {
13414       throw $locationMinErr('nostate', 'History API state support is available only ' +
13415         'in HTML5 mode and only in browsers supporting HTML5 History API');
13416     }
13417     // The user might modify `stateObject` after invoking `$location.state(stateObject)`
13418     // but we're changing the $$state reference to $browser.state() during the $digest
13419     // so the modification window is narrow.
13420     this.$$state = isUndefined(state) ? null : state;
13422     return this;
13423   };
13427 function locationGetter(property) {
13428   return function() {
13429     return this[property];
13430   };
13434 function locationGetterSetter(property, preprocess) {
13435   return function(value) {
13436     if (isUndefined(value)) {
13437       return this[property];
13438     }
13440     this[property] = preprocess(value);
13441     this.$$compose();
13443     return this;
13444   };
13449  * @ngdoc service
13450  * @name $location
13452  * @requires $rootElement
13454  * @description
13455  * The $location service parses the URL in the browser address bar (based on the
13456  * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
13457  * available to your application. Changes to the URL in the address bar are reflected into
13458  * $location service and changes to $location are reflected into the browser address bar.
13460  * **The $location service:**
13462  * - Exposes the current URL in the browser address bar, so you can
13463  *   - Watch and observe the URL.
13464  *   - Change the URL.
13465  * - Synchronizes the URL with the browser when the user
13466  *   - Changes the address bar.
13467  *   - Clicks the back or forward button (or clicks a History link).
13468  *   - Clicks on a link.
13469  * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
13471  * For more information see {@link guide/$location Developer Guide: Using $location}
13472  */
13475  * @ngdoc provider
13476  * @name $locationProvider
13477  * @description
13478  * Use the `$locationProvider` to configure how the application deep linking paths are stored.
13479  */
13480 function $LocationProvider() {
13481   var hashPrefix = '',
13482       html5Mode = {
13483         enabled: false,
13484         requireBase: true,
13485         rewriteLinks: true
13486       };
13488   /**
13489    * @ngdoc method
13490    * @name $locationProvider#hashPrefix
13491    * @description
13492    * @param {string=} prefix Prefix for hash part (containing path and search)
13493    * @returns {*} current value if used as getter or itself (chaining) if used as setter
13494    */
13495   this.hashPrefix = function(prefix) {
13496     if (isDefined(prefix)) {
13497       hashPrefix = prefix;
13498       return this;
13499     } else {
13500       return hashPrefix;
13501     }
13502   };
13504   /**
13505    * @ngdoc method
13506    * @name $locationProvider#html5Mode
13507    * @description
13508    * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
13509    *   If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
13510    *   properties:
13511    *   - **enabled** â€“ `{boolean}` â€“ (default: false) If true, will rely on `history.pushState` to
13512    *     change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
13513    *     support `pushState`.
13514    *   - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
13515    *     whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
13516    *     true, and a base tag is not present, an error will be thrown when `$location` is injected.
13517    *     See the {@link guide/$location $location guide for more information}
13518    *   - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
13519    *     enables/disables url rewriting for relative links.
13520    *
13521    * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
13522    */
13523   this.html5Mode = function(mode) {
13524     if (isBoolean(mode)) {
13525       html5Mode.enabled = mode;
13526       return this;
13527     } else if (isObject(mode)) {
13529       if (isBoolean(mode.enabled)) {
13530         html5Mode.enabled = mode.enabled;
13531       }
13533       if (isBoolean(mode.requireBase)) {
13534         html5Mode.requireBase = mode.requireBase;
13535       }
13537       if (isBoolean(mode.rewriteLinks)) {
13538         html5Mode.rewriteLinks = mode.rewriteLinks;
13539       }
13541       return this;
13542     } else {
13543       return html5Mode;
13544     }
13545   };
13547   /**
13548    * @ngdoc event
13549    * @name $location#$locationChangeStart
13550    * @eventType broadcast on root scope
13551    * @description
13552    * Broadcasted before a URL will change.
13553    *
13554    * This change can be prevented by calling
13555    * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
13556    * details about event object. Upon successful change
13557    * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
13558    *
13559    * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
13560    * the browser supports the HTML5 History API.
13561    *
13562    * @param {Object} angularEvent Synthetic event object.
13563    * @param {string} newUrl New URL
13564    * @param {string=} oldUrl URL that was before it was changed.
13565    * @param {string=} newState New history state object
13566    * @param {string=} oldState History state object that was before it was changed.
13567    */
13569   /**
13570    * @ngdoc event
13571    * @name $location#$locationChangeSuccess
13572    * @eventType broadcast on root scope
13573    * @description
13574    * Broadcasted after a URL was changed.
13575    *
13576    * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
13577    * the browser supports the HTML5 History API.
13578    *
13579    * @param {Object} angularEvent Synthetic event object.
13580    * @param {string} newUrl New URL
13581    * @param {string=} oldUrl URL that was before it was changed.
13582    * @param {string=} newState New history state object
13583    * @param {string=} oldState History state object that was before it was changed.
13584    */
13586   this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
13587       function($rootScope, $browser, $sniffer, $rootElement, $window) {
13588     var $location,
13589         LocationMode,
13590         baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
13591         initialUrl = $browser.url(),
13592         appBase;
13594     if (html5Mode.enabled) {
13595       if (!baseHref && html5Mode.requireBase) {
13596         throw $locationMinErr('nobase',
13597           "$location in HTML5 mode requires a <base> tag to be present!");
13598       }
13599       appBase = serverBase(initialUrl) + (baseHref || '/');
13600       LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
13601     } else {
13602       appBase = stripHash(initialUrl);
13603       LocationMode = LocationHashbangUrl;
13604     }
13605     var appBaseNoFile = stripFile(appBase);
13607     $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
13608     $location.$$parseLinkUrl(initialUrl, initialUrl);
13610     $location.$$state = $browser.state();
13612     var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
13614     function setBrowserUrlWithFallback(url, replace, state) {
13615       var oldUrl = $location.url();
13616       var oldState = $location.$$state;
13617       try {
13618         $browser.url(url, replace, state);
13620         // Make sure $location.state() returns referentially identical (not just deeply equal)
13621         // state object; this makes possible quick checking if the state changed in the digest
13622         // loop. Checking deep equality would be too expensive.
13623         $location.$$state = $browser.state();
13624       } catch (e) {
13625         // Restore old values if pushState fails
13626         $location.url(oldUrl);
13627         $location.$$state = oldState;
13629         throw e;
13630       }
13631     }
13633     $rootElement.on('click', function(event) {
13634       // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
13635       // currently we open nice url link and redirect then
13637       if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
13639       var elm = jqLite(event.target);
13641       // traverse the DOM up to find first A tag
13642       while (nodeName_(elm[0]) !== 'a') {
13643         // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
13644         if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
13645       }
13647       var absHref = elm.prop('href');
13648       // get the actual href attribute - see
13649       // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
13650       var relHref = elm.attr('href') || elm.attr('xlink:href');
13652       if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
13653         // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
13654         // an animation.
13655         absHref = urlResolve(absHref.animVal).href;
13656       }
13658       // Ignore when url is started with javascript: or mailto:
13659       if (IGNORE_URI_REGEXP.test(absHref)) return;
13661       if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
13662         if ($location.$$parseLinkUrl(absHref, relHref)) {
13663           // We do a preventDefault for all urls that are part of the angular application,
13664           // in html5mode and also without, so that we are able to abort navigation without
13665           // getting double entries in the location history.
13666           event.preventDefault();
13667           // update location manually
13668           if ($location.absUrl() != $browser.url()) {
13669             $rootScope.$apply();
13670             // hack to work around FF6 bug 684208 when scenario runner clicks on links
13671             $window.angular['ff-684208-preventDefault'] = true;
13672           }
13673         }
13674       }
13675     });
13678     // rewrite hashbang url <> html5 url
13679     if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
13680       $browser.url($location.absUrl(), true);
13681     }
13683     var initializing = true;
13685     // update $location when $browser url changes
13686     $browser.onUrlChange(function(newUrl, newState) {
13688       if (isUndefined(stripBaseUrl(appBaseNoFile, newUrl))) {
13689         // If we are navigating outside of the app then force a reload
13690         $window.location.href = newUrl;
13691         return;
13692       }
13694       $rootScope.$evalAsync(function() {
13695         var oldUrl = $location.absUrl();
13696         var oldState = $location.$$state;
13697         var defaultPrevented;
13698         newUrl = trimEmptyHash(newUrl);
13699         $location.$$parse(newUrl);
13700         $location.$$state = newState;
13702         defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
13703             newState, oldState).defaultPrevented;
13705         // if the location was changed by a `$locationChangeStart` handler then stop
13706         // processing this location change
13707         if ($location.absUrl() !== newUrl) return;
13709         if (defaultPrevented) {
13710           $location.$$parse(oldUrl);
13711           $location.$$state = oldState;
13712           setBrowserUrlWithFallback(oldUrl, false, oldState);
13713         } else {
13714           initializing = false;
13715           afterLocationChange(oldUrl, oldState);
13716         }
13717       });
13718       if (!$rootScope.$$phase) $rootScope.$digest();
13719     });
13721     // update browser
13722     $rootScope.$watch(function $locationWatch() {
13723       var oldUrl = trimEmptyHash($browser.url());
13724       var newUrl = trimEmptyHash($location.absUrl());
13725       var oldState = $browser.state();
13726       var currentReplace = $location.$$replace;
13727       var urlOrStateChanged = oldUrl !== newUrl ||
13728         ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
13730       if (initializing || urlOrStateChanged) {
13731         initializing = false;
13733         $rootScope.$evalAsync(function() {
13734           var newUrl = $location.absUrl();
13735           var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
13736               $location.$$state, oldState).defaultPrevented;
13738           // if the location was changed by a `$locationChangeStart` handler then stop
13739           // processing this location change
13740           if ($location.absUrl() !== newUrl) return;
13742           if (defaultPrevented) {
13743             $location.$$parse(oldUrl);
13744             $location.$$state = oldState;
13745           } else {
13746             if (urlOrStateChanged) {
13747               setBrowserUrlWithFallback(newUrl, currentReplace,
13748                                         oldState === $location.$$state ? null : $location.$$state);
13749             }
13750             afterLocationChange(oldUrl, oldState);
13751           }
13752         });
13753       }
13755       $location.$$replace = false;
13757       // we don't need to return anything because $evalAsync will make the digest loop dirty when
13758       // there is a change
13759     });
13761     return $location;
13763     function afterLocationChange(oldUrl, oldState) {
13764       $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
13765         $location.$$state, oldState);
13766     }
13771  * @ngdoc service
13772  * @name $log
13773  * @requires $window
13775  * @description
13776  * Simple service for logging. Default implementation safely writes the message
13777  * into the browser's console (if present).
13779  * The main purpose of this service is to simplify debugging and troubleshooting.
13781  * The default is to log `debug` messages. You can use
13782  * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
13784  * @example
13785    <example module="logExample">
13786      <file name="script.js">
13787        angular.module('logExample', [])
13788          .controller('LogController', ['$scope', '$log', function($scope, $log) {
13789            $scope.$log = $log;
13790            $scope.message = 'Hello World!';
13791          }]);
13792      </file>
13793      <file name="index.html">
13794        <div ng-controller="LogController">
13795          <p>Reload this page with open console, enter text and hit the log button...</p>
13796          <label>Message:
13797          <input type="text" ng-model="message" /></label>
13798          <button ng-click="$log.log(message)">log</button>
13799          <button ng-click="$log.warn(message)">warn</button>
13800          <button ng-click="$log.info(message)">info</button>
13801          <button ng-click="$log.error(message)">error</button>
13802          <button ng-click="$log.debug(message)">debug</button>
13803        </div>
13804      </file>
13805    </example>
13806  */
13809  * @ngdoc provider
13810  * @name $logProvider
13811  * @description
13812  * Use the `$logProvider` to configure how the application logs messages
13813  */
13814 function $LogProvider() {
13815   var debug = true,
13816       self = this;
13818   /**
13819    * @ngdoc method
13820    * @name $logProvider#debugEnabled
13821    * @description
13822    * @param {boolean=} flag enable or disable debug level messages
13823    * @returns {*} current value if used as getter or itself (chaining) if used as setter
13824    */
13825   this.debugEnabled = function(flag) {
13826     if (isDefined(flag)) {
13827       debug = flag;
13828     return this;
13829     } else {
13830       return debug;
13831     }
13832   };
13834   this.$get = ['$window', function($window) {
13835     return {
13836       /**
13837        * @ngdoc method
13838        * @name $log#log
13839        *
13840        * @description
13841        * Write a log message
13842        */
13843       log: consoleLog('log'),
13845       /**
13846        * @ngdoc method
13847        * @name $log#info
13848        *
13849        * @description
13850        * Write an information message
13851        */
13852       info: consoleLog('info'),
13854       /**
13855        * @ngdoc method
13856        * @name $log#warn
13857        *
13858        * @description
13859        * Write a warning message
13860        */
13861       warn: consoleLog('warn'),
13863       /**
13864        * @ngdoc method
13865        * @name $log#error
13866        *
13867        * @description
13868        * Write an error message
13869        */
13870       error: consoleLog('error'),
13872       /**
13873        * @ngdoc method
13874        * @name $log#debug
13875        *
13876        * @description
13877        * Write a debug message
13878        */
13879       debug: (function() {
13880         var fn = consoleLog('debug');
13882         return function() {
13883           if (debug) {
13884             fn.apply(self, arguments);
13885           }
13886         };
13887       }())
13888     };
13890     function formatError(arg) {
13891       if (arg instanceof Error) {
13892         if (arg.stack) {
13893           arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
13894               ? 'Error: ' + arg.message + '\n' + arg.stack
13895               : arg.stack;
13896         } else if (arg.sourceURL) {
13897           arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
13898         }
13899       }
13900       return arg;
13901     }
13903     function consoleLog(type) {
13904       var console = $window.console || {},
13905           logFn = console[type] || console.log || noop,
13906           hasApply = false;
13908       // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
13909       // The reason behind this is that console.log has type "object" in IE8...
13910       try {
13911         hasApply = !!logFn.apply;
13912       } catch (e) {}
13914       if (hasApply) {
13915         return function() {
13916           var args = [];
13917           forEach(arguments, function(arg) {
13918             args.push(formatError(arg));
13919           });
13920           return logFn.apply(console, args);
13921         };
13922       }
13924       // we are IE which either doesn't have window.console => this is noop and we do nothing,
13925       // or we are IE where console.log doesn't have apply so we log at least first 2 args
13926       return function(arg1, arg2) {
13927         logFn(arg1, arg2 == null ? '' : arg2);
13928       };
13929     }
13930   }];
13933 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
13934  *     Any commits to this file should be reviewed with security in mind.  *
13935  *   Changes to this file can potentially create security vulnerabilities. *
13936  *          An approval from 2 Core members with history of modifying      *
13937  *                         this file is required.                          *
13938  *                                                                         *
13939  *  Does the change somehow allow for arbitrary javascript to be executed? *
13940  *    Or allows for someone to change the prototype of built-in objects?   *
13941  *     Or gives undesired access to variables likes document or window?    *
13942  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
13944 var $parseMinErr = minErr('$parse');
13946 // Sandboxing Angular Expressions
13947 // ------------------------------
13948 // Angular expressions are generally considered safe because these expressions only have direct
13949 // access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
13950 // obtaining a reference to native JS functions such as the Function constructor.
13952 // As an example, consider the following Angular expression:
13954 //   {}.toString.constructor('alert("evil JS code")')
13956 // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
13957 // against the expression language, but not to prevent exploits that were enabled by exposing
13958 // sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
13959 // practice and therefore we are not even trying to protect against interaction with an object
13960 // explicitly exposed in this way.
13962 // In general, it is not possible to access a Window object from an angular expression unless a
13963 // window or some DOM object that has a reference to window is published onto a Scope.
13964 // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
13965 // native objects.
13967 // See https://docs.angularjs.org/guide/security
13970 function ensureSafeMemberName(name, fullExpression) {
13971   if (name === "__defineGetter__" || name === "__defineSetter__"
13972       || name === "__lookupGetter__" || name === "__lookupSetter__"
13973       || name === "__proto__") {
13974     throw $parseMinErr('isecfld',
13975         'Attempting to access a disallowed field in Angular expressions! '
13976         + 'Expression: {0}', fullExpression);
13977   }
13978   return name;
13981 function getStringValue(name) {
13982   // Property names must be strings. This means that non-string objects cannot be used
13983   // as keys in an object. Any non-string object, including a number, is typecasted
13984   // into a string via the toString method.
13985   // -- MDN, https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names
13986   //
13987   // So, to ensure that we are checking the same `name` that JavaScript would use, we cast it
13988   // to a string. It's not always possible. If `name` is an object and its `toString` method is
13989   // 'broken' (doesn't return a string, isn't a function, etc.), an error will be thrown:
13990   //
13991   // TypeError: Cannot convert object to primitive value
13992   //
13993   // For performance reasons, we don't catch this error here and allow it to propagate up the call
13994   // stack. Note that you'll get the same error in JavaScript if you try to access a property using
13995   // such a 'broken' object as a key.
13996   return name + '';
13999 function ensureSafeObject(obj, fullExpression) {
14000   // nifty check if obj is Function that is fast and works across iframes and other contexts
14001   if (obj) {
14002     if (obj.constructor === obj) {
14003       throw $parseMinErr('isecfn',
14004           'Referencing Function in Angular expressions is disallowed! Expression: {0}',
14005           fullExpression);
14006     } else if (// isWindow(obj)
14007         obj.window === obj) {
14008       throw $parseMinErr('isecwindow',
14009           'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
14010           fullExpression);
14011     } else if (// isElement(obj)
14012         obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
14013       throw $parseMinErr('isecdom',
14014           'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
14015           fullExpression);
14016     } else if (// block Object so that we can't get hold of dangerous Object.* methods
14017         obj === Object) {
14018       throw $parseMinErr('isecobj',
14019           'Referencing Object in Angular expressions is disallowed! Expression: {0}',
14020           fullExpression);
14021     }
14022   }
14023   return obj;
14026 var CALL = Function.prototype.call;
14027 var APPLY = Function.prototype.apply;
14028 var BIND = Function.prototype.bind;
14030 function ensureSafeFunction(obj, fullExpression) {
14031   if (obj) {
14032     if (obj.constructor === obj) {
14033       throw $parseMinErr('isecfn',
14034         'Referencing Function in Angular expressions is disallowed! Expression: {0}',
14035         fullExpression);
14036     } else if (obj === CALL || obj === APPLY || obj === BIND) {
14037       throw $parseMinErr('isecff',
14038         'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
14039         fullExpression);
14040     }
14041   }
14044 function ensureSafeAssignContext(obj, fullExpression) {
14045   if (obj) {
14046     if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
14047         obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
14048       throw $parseMinErr('isecaf',
14049         'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
14050     }
14051   }
14054 var OPERATORS = createMap();
14055 forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
14056 var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
14059 /////////////////////////////////////////
14063  * @constructor
14064  */
14065 var Lexer = function(options) {
14066   this.options = options;
14069 Lexer.prototype = {
14070   constructor: Lexer,
14072   lex: function(text) {
14073     this.text = text;
14074     this.index = 0;
14075     this.tokens = [];
14077     while (this.index < this.text.length) {
14078       var ch = this.text.charAt(this.index);
14079       if (ch === '"' || ch === "'") {
14080         this.readString(ch);
14081       } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
14082         this.readNumber();
14083       } else if (this.isIdentifierStart(this.peekMultichar())) {
14084         this.readIdent();
14085       } else if (this.is(ch, '(){}[].,;:?')) {
14086         this.tokens.push({index: this.index, text: ch});
14087         this.index++;
14088       } else if (this.isWhitespace(ch)) {
14089         this.index++;
14090       } else {
14091         var ch2 = ch + this.peek();
14092         var ch3 = ch2 + this.peek(2);
14093         var op1 = OPERATORS[ch];
14094         var op2 = OPERATORS[ch2];
14095         var op3 = OPERATORS[ch3];
14096         if (op1 || op2 || op3) {
14097           var token = op3 ? ch3 : (op2 ? ch2 : ch);
14098           this.tokens.push({index: this.index, text: token, operator: true});
14099           this.index += token.length;
14100         } else {
14101           this.throwError('Unexpected next character ', this.index, this.index + 1);
14102         }
14103       }
14104     }
14105     return this.tokens;
14106   },
14108   is: function(ch, chars) {
14109     return chars.indexOf(ch) !== -1;
14110   },
14112   peek: function(i) {
14113     var num = i || 1;
14114     return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
14115   },
14117   isNumber: function(ch) {
14118     return ('0' <= ch && ch <= '9') && typeof ch === "string";
14119   },
14121   isWhitespace: function(ch) {
14122     // IE treats non-breaking space as \u00A0
14123     return (ch === ' ' || ch === '\r' || ch === '\t' ||
14124             ch === '\n' || ch === '\v' || ch === '\u00A0');
14125   },
14127   isIdentifierStart: function(ch) {
14128     return this.options.isIdentifierStart ?
14129         this.options.isIdentifierStart(ch, this.codePointAt(ch)) :
14130         this.isValidIdentifierStart(ch);
14131   },
14133   isValidIdentifierStart: function(ch) {
14134     return ('a' <= ch && ch <= 'z' ||
14135             'A' <= ch && ch <= 'Z' ||
14136             '_' === ch || ch === '$');
14137   },
14139   isIdentifierContinue: function(ch) {
14140     return this.options.isIdentifierContinue ?
14141         this.options.isIdentifierContinue(ch, this.codePointAt(ch)) :
14142         this.isValidIdentifierContinue(ch);
14143   },
14145   isValidIdentifierContinue: function(ch, cp) {
14146     return this.isValidIdentifierStart(ch, cp) || this.isNumber(ch);
14147   },
14149   codePointAt: function(ch) {
14150     if (ch.length === 1) return ch.charCodeAt(0);
14151     /*jshint bitwise: false*/
14152     return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35FDC00;
14153     /*jshint bitwise: true*/
14154   },
14156   peekMultichar: function() {
14157     var ch = this.text.charAt(this.index);
14158     var peek = this.peek();
14159     if (!peek) {
14160       return ch;
14161     }
14162     var cp1 = ch.charCodeAt(0);
14163     var cp2 = peek.charCodeAt(0);
14164     if (cp1 >= 0xD800 && cp1 <= 0xDBFF && cp2 >= 0xDC00 && cp2 <= 0xDFFF) {
14165       return ch + peek;
14166     }
14167     return ch;
14168   },
14170   isExpOperator: function(ch) {
14171     return (ch === '-' || ch === '+' || this.isNumber(ch));
14172   },
14174   throwError: function(error, start, end) {
14175     end = end || this.index;
14176     var colStr = (isDefined(start)
14177             ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
14178             : ' ' + end);
14179     throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
14180         error, colStr, this.text);
14181   },
14183   readNumber: function() {
14184     var number = '';
14185     var start = this.index;
14186     while (this.index < this.text.length) {
14187       var ch = lowercase(this.text.charAt(this.index));
14188       if (ch == '.' || this.isNumber(ch)) {
14189         number += ch;
14190       } else {
14191         var peekCh = this.peek();
14192         if (ch == 'e' && this.isExpOperator(peekCh)) {
14193           number += ch;
14194         } else if (this.isExpOperator(ch) &&
14195             peekCh && this.isNumber(peekCh) &&
14196             number.charAt(number.length - 1) == 'e') {
14197           number += ch;
14198         } else if (this.isExpOperator(ch) &&
14199             (!peekCh || !this.isNumber(peekCh)) &&
14200             number.charAt(number.length - 1) == 'e') {
14201           this.throwError('Invalid exponent');
14202         } else {
14203           break;
14204         }
14205       }
14206       this.index++;
14207     }
14208     this.tokens.push({
14209       index: start,
14210       text: number,
14211       constant: true,
14212       value: Number(number)
14213     });
14214   },
14216   readIdent: function() {
14217     var start = this.index;
14218     this.index += this.peekMultichar().length;
14219     while (this.index < this.text.length) {
14220       var ch = this.peekMultichar();
14221       if (!this.isIdentifierContinue(ch)) {
14222         break;
14223       }
14224       this.index += ch.length;
14225     }
14226     this.tokens.push({
14227       index: start,
14228       text: this.text.slice(start, this.index),
14229       identifier: true
14230     });
14231   },
14233   readString: function(quote) {
14234     var start = this.index;
14235     this.index++;
14236     var string = '';
14237     var rawString = quote;
14238     var escape = false;
14239     while (this.index < this.text.length) {
14240       var ch = this.text.charAt(this.index);
14241       rawString += ch;
14242       if (escape) {
14243         if (ch === 'u') {
14244           var hex = this.text.substring(this.index + 1, this.index + 5);
14245           if (!hex.match(/[\da-f]{4}/i)) {
14246             this.throwError('Invalid unicode escape [\\u' + hex + ']');
14247           }
14248           this.index += 4;
14249           string += String.fromCharCode(parseInt(hex, 16));
14250         } else {
14251           var rep = ESCAPE[ch];
14252           string = string + (rep || ch);
14253         }
14254         escape = false;
14255       } else if (ch === '\\') {
14256         escape = true;
14257       } else if (ch === quote) {
14258         this.index++;
14259         this.tokens.push({
14260           index: start,
14261           text: rawString,
14262           constant: true,
14263           value: string
14264         });
14265         return;
14266       } else {
14267         string += ch;
14268       }
14269       this.index++;
14270     }
14271     this.throwError('Unterminated quote', start);
14272   }
14275 var AST = function(lexer, options) {
14276   this.lexer = lexer;
14277   this.options = options;
14280 AST.Program = 'Program';
14281 AST.ExpressionStatement = 'ExpressionStatement';
14282 AST.AssignmentExpression = 'AssignmentExpression';
14283 AST.ConditionalExpression = 'ConditionalExpression';
14284 AST.LogicalExpression = 'LogicalExpression';
14285 AST.BinaryExpression = 'BinaryExpression';
14286 AST.UnaryExpression = 'UnaryExpression';
14287 AST.CallExpression = 'CallExpression';
14288 AST.MemberExpression = 'MemberExpression';
14289 AST.Identifier = 'Identifier';
14290 AST.Literal = 'Literal';
14291 AST.ArrayExpression = 'ArrayExpression';
14292 AST.Property = 'Property';
14293 AST.ObjectExpression = 'ObjectExpression';
14294 AST.ThisExpression = 'ThisExpression';
14295 AST.LocalsExpression = 'LocalsExpression';
14297 // Internal use only
14298 AST.NGValueParameter = 'NGValueParameter';
14300 AST.prototype = {
14301   ast: function(text) {
14302     this.text = text;
14303     this.tokens = this.lexer.lex(text);
14305     var value = this.program();
14307     if (this.tokens.length !== 0) {
14308       this.throwError('is an unexpected token', this.tokens[0]);
14309     }
14311     return value;
14312   },
14314   program: function() {
14315     var body = [];
14316     while (true) {
14317       if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
14318         body.push(this.expressionStatement());
14319       if (!this.expect(';')) {
14320         return { type: AST.Program, body: body};
14321       }
14322     }
14323   },
14325   expressionStatement: function() {
14326     return { type: AST.ExpressionStatement, expression: this.filterChain() };
14327   },
14329   filterChain: function() {
14330     var left = this.expression();
14331     var token;
14332     while ((token = this.expect('|'))) {
14333       left = this.filter(left);
14334     }
14335     return left;
14336   },
14338   expression: function() {
14339     return this.assignment();
14340   },
14342   assignment: function() {
14343     var result = this.ternary();
14344     if (this.expect('=')) {
14345       result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
14346     }
14347     return result;
14348   },
14350   ternary: function() {
14351     var test = this.logicalOR();
14352     var alternate;
14353     var consequent;
14354     if (this.expect('?')) {
14355       alternate = this.expression();
14356       if (this.consume(':')) {
14357         consequent = this.expression();
14358         return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
14359       }
14360     }
14361     return test;
14362   },
14364   logicalOR: function() {
14365     var left = this.logicalAND();
14366     while (this.expect('||')) {
14367       left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
14368     }
14369     return left;
14370   },
14372   logicalAND: function() {
14373     var left = this.equality();
14374     while (this.expect('&&')) {
14375       left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
14376     }
14377     return left;
14378   },
14380   equality: function() {
14381     var left = this.relational();
14382     var token;
14383     while ((token = this.expect('==','!=','===','!=='))) {
14384       left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
14385     }
14386     return left;
14387   },
14389   relational: function() {
14390     var left = this.additive();
14391     var token;
14392     while ((token = this.expect('<', '>', '<=', '>='))) {
14393       left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
14394     }
14395     return left;
14396   },
14398   additive: function() {
14399     var left = this.multiplicative();
14400     var token;
14401     while ((token = this.expect('+','-'))) {
14402       left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
14403     }
14404     return left;
14405   },
14407   multiplicative: function() {
14408     var left = this.unary();
14409     var token;
14410     while ((token = this.expect('*','/','%'))) {
14411       left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
14412     }
14413     return left;
14414   },
14416   unary: function() {
14417     var token;
14418     if ((token = this.expect('+', '-', '!'))) {
14419       return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
14420     } else {
14421       return this.primary();
14422     }
14423   },
14425   primary: function() {
14426     var primary;
14427     if (this.expect('(')) {
14428       primary = this.filterChain();
14429       this.consume(')');
14430     } else if (this.expect('[')) {
14431       primary = this.arrayDeclaration();
14432     } else if (this.expect('{')) {
14433       primary = this.object();
14434     } else if (this.selfReferential.hasOwnProperty(this.peek().text)) {
14435       primary = copy(this.selfReferential[this.consume().text]);
14436     } else if (this.options.literals.hasOwnProperty(this.peek().text)) {
14437       primary = { type: AST.Literal, value: this.options.literals[this.consume().text]};
14438     } else if (this.peek().identifier) {
14439       primary = this.identifier();
14440     } else if (this.peek().constant) {
14441       primary = this.constant();
14442     } else {
14443       this.throwError('not a primary expression', this.peek());
14444     }
14446     var next;
14447     while ((next = this.expect('(', '[', '.'))) {
14448       if (next.text === '(') {
14449         primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
14450         this.consume(')');
14451       } else if (next.text === '[') {
14452         primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
14453         this.consume(']');
14454       } else if (next.text === '.') {
14455         primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
14456       } else {
14457         this.throwError('IMPOSSIBLE');
14458       }
14459     }
14460     return primary;
14461   },
14463   filter: function(baseExpression) {
14464     var args = [baseExpression];
14465     var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
14467     while (this.expect(':')) {
14468       args.push(this.expression());
14469     }
14471     return result;
14472   },
14474   parseArguments: function() {
14475     var args = [];
14476     if (this.peekToken().text !== ')') {
14477       do {
14478         args.push(this.filterChain());
14479       } while (this.expect(','));
14480     }
14481     return args;
14482   },
14484   identifier: function() {
14485     var token = this.consume();
14486     if (!token.identifier) {
14487       this.throwError('is not a valid identifier', token);
14488     }
14489     return { type: AST.Identifier, name: token.text };
14490   },
14492   constant: function() {
14493     // TODO check that it is a constant
14494     return { type: AST.Literal, value: this.consume().value };
14495   },
14497   arrayDeclaration: function() {
14498     var elements = [];
14499     if (this.peekToken().text !== ']') {
14500       do {
14501         if (this.peek(']')) {
14502           // Support trailing commas per ES5.1.
14503           break;
14504         }
14505         elements.push(this.expression());
14506       } while (this.expect(','));
14507     }
14508     this.consume(']');
14510     return { type: AST.ArrayExpression, elements: elements };
14511   },
14513   object: function() {
14514     var properties = [], property;
14515     if (this.peekToken().text !== '}') {
14516       do {
14517         if (this.peek('}')) {
14518           // Support trailing commas per ES5.1.
14519           break;
14520         }
14521         property = {type: AST.Property, kind: 'init'};
14522         if (this.peek().constant) {
14523           property.key = this.constant();
14524           property.computed = false;
14525           this.consume(':');
14526           property.value = this.expression();
14527         } else if (this.peek().identifier) {
14528           property.key = this.identifier();
14529           property.computed = false;
14530           if (this.peek(':')) {
14531             this.consume(':');
14532             property.value = this.expression();
14533           } else {
14534             property.value = property.key;
14535           }
14536         } else if (this.peek('[')) {
14537           this.consume('[');
14538           property.key = this.expression();
14539           this.consume(']');
14540           property.computed = true;
14541           this.consume(':');
14542           property.value = this.expression();
14543         } else {
14544           this.throwError("invalid key", this.peek());
14545         }
14546         properties.push(property);
14547       } while (this.expect(','));
14548     }
14549     this.consume('}');
14551     return {type: AST.ObjectExpression, properties: properties };
14552   },
14554   throwError: function(msg, token) {
14555     throw $parseMinErr('syntax',
14556         'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
14557           token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
14558   },
14560   consume: function(e1) {
14561     if (this.tokens.length === 0) {
14562       throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
14563     }
14565     var token = this.expect(e1);
14566     if (!token) {
14567       this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
14568     }
14569     return token;
14570   },
14572   peekToken: function() {
14573     if (this.tokens.length === 0) {
14574       throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
14575     }
14576     return this.tokens[0];
14577   },
14579   peek: function(e1, e2, e3, e4) {
14580     return this.peekAhead(0, e1, e2, e3, e4);
14581   },
14583   peekAhead: function(i, e1, e2, e3, e4) {
14584     if (this.tokens.length > i) {
14585       var token = this.tokens[i];
14586       var t = token.text;
14587       if (t === e1 || t === e2 || t === e3 || t === e4 ||
14588           (!e1 && !e2 && !e3 && !e4)) {
14589         return token;
14590       }
14591     }
14592     return false;
14593   },
14595   expect: function(e1, e2, e3, e4) {
14596     var token = this.peek(e1, e2, e3, e4);
14597     if (token) {
14598       this.tokens.shift();
14599       return token;
14600     }
14601     return false;
14602   },
14604   selfReferential: {
14605     'this': {type: AST.ThisExpression },
14606     '$locals': {type: AST.LocalsExpression }
14607   }
14610 function ifDefined(v, d) {
14611   return typeof v !== 'undefined' ? v : d;
14614 function plusFn(l, r) {
14615   if (typeof l === 'undefined') return r;
14616   if (typeof r === 'undefined') return l;
14617   return l + r;
14620 function isStateless($filter, filterName) {
14621   var fn = $filter(filterName);
14622   return !fn.$stateful;
14625 function findConstantAndWatchExpressions(ast, $filter) {
14626   var allConstants;
14627   var argsToWatch;
14628   switch (ast.type) {
14629   case AST.Program:
14630     allConstants = true;
14631     forEach(ast.body, function(expr) {
14632       findConstantAndWatchExpressions(expr.expression, $filter);
14633       allConstants = allConstants && expr.expression.constant;
14634     });
14635     ast.constant = allConstants;
14636     break;
14637   case AST.Literal:
14638     ast.constant = true;
14639     ast.toWatch = [];
14640     break;
14641   case AST.UnaryExpression:
14642     findConstantAndWatchExpressions(ast.argument, $filter);
14643     ast.constant = ast.argument.constant;
14644     ast.toWatch = ast.argument.toWatch;
14645     break;
14646   case AST.BinaryExpression:
14647     findConstantAndWatchExpressions(ast.left, $filter);
14648     findConstantAndWatchExpressions(ast.right, $filter);
14649     ast.constant = ast.left.constant && ast.right.constant;
14650     ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
14651     break;
14652   case AST.LogicalExpression:
14653     findConstantAndWatchExpressions(ast.left, $filter);
14654     findConstantAndWatchExpressions(ast.right, $filter);
14655     ast.constant = ast.left.constant && ast.right.constant;
14656     ast.toWatch = ast.constant ? [] : [ast];
14657     break;
14658   case AST.ConditionalExpression:
14659     findConstantAndWatchExpressions(ast.test, $filter);
14660     findConstantAndWatchExpressions(ast.alternate, $filter);
14661     findConstantAndWatchExpressions(ast.consequent, $filter);
14662     ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
14663     ast.toWatch = ast.constant ? [] : [ast];
14664     break;
14665   case AST.Identifier:
14666     ast.constant = false;
14667     ast.toWatch = [ast];
14668     break;
14669   case AST.MemberExpression:
14670     findConstantAndWatchExpressions(ast.object, $filter);
14671     if (ast.computed) {
14672       findConstantAndWatchExpressions(ast.property, $filter);
14673     }
14674     ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
14675     ast.toWatch = [ast];
14676     break;
14677   case AST.CallExpression:
14678     allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
14679     argsToWatch = [];
14680     forEach(ast.arguments, function(expr) {
14681       findConstantAndWatchExpressions(expr, $filter);
14682       allConstants = allConstants && expr.constant;
14683       if (!expr.constant) {
14684         argsToWatch.push.apply(argsToWatch, expr.toWatch);
14685       }
14686     });
14687     ast.constant = allConstants;
14688     ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
14689     break;
14690   case AST.AssignmentExpression:
14691     findConstantAndWatchExpressions(ast.left, $filter);
14692     findConstantAndWatchExpressions(ast.right, $filter);
14693     ast.constant = ast.left.constant && ast.right.constant;
14694     ast.toWatch = [ast];
14695     break;
14696   case AST.ArrayExpression:
14697     allConstants = true;
14698     argsToWatch = [];
14699     forEach(ast.elements, function(expr) {
14700       findConstantAndWatchExpressions(expr, $filter);
14701       allConstants = allConstants && expr.constant;
14702       if (!expr.constant) {
14703         argsToWatch.push.apply(argsToWatch, expr.toWatch);
14704       }
14705     });
14706     ast.constant = allConstants;
14707     ast.toWatch = argsToWatch;
14708     break;
14709   case AST.ObjectExpression:
14710     allConstants = true;
14711     argsToWatch = [];
14712     forEach(ast.properties, function(property) {
14713       findConstantAndWatchExpressions(property.value, $filter);
14714       allConstants = allConstants && property.value.constant && !property.computed;
14715       if (!property.value.constant) {
14716         argsToWatch.push.apply(argsToWatch, property.value.toWatch);
14717       }
14718     });
14719     ast.constant = allConstants;
14720     ast.toWatch = argsToWatch;
14721     break;
14722   case AST.ThisExpression:
14723     ast.constant = false;
14724     ast.toWatch = [];
14725     break;
14726   case AST.LocalsExpression:
14727     ast.constant = false;
14728     ast.toWatch = [];
14729     break;
14730   }
14733 function getInputs(body) {
14734   if (body.length != 1) return;
14735   var lastExpression = body[0].expression;
14736   var candidate = lastExpression.toWatch;
14737   if (candidate.length !== 1) return candidate;
14738   return candidate[0] !== lastExpression ? candidate : undefined;
14741 function isAssignable(ast) {
14742   return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
14745 function assignableAST(ast) {
14746   if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
14747     return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
14748   }
14751 function isLiteral(ast) {
14752   return ast.body.length === 0 ||
14753       ast.body.length === 1 && (
14754       ast.body[0].expression.type === AST.Literal ||
14755       ast.body[0].expression.type === AST.ArrayExpression ||
14756       ast.body[0].expression.type === AST.ObjectExpression);
14759 function isConstant(ast) {
14760   return ast.constant;
14763 function ASTCompiler(astBuilder, $filter) {
14764   this.astBuilder = astBuilder;
14765   this.$filter = $filter;
14768 ASTCompiler.prototype = {
14769   compile: function(expression, expensiveChecks) {
14770     var self = this;
14771     var ast = this.astBuilder.ast(expression);
14772     this.state = {
14773       nextId: 0,
14774       filters: {},
14775       expensiveChecks: expensiveChecks,
14776       fn: {vars: [], body: [], own: {}},
14777       assign: {vars: [], body: [], own: {}},
14778       inputs: []
14779     };
14780     findConstantAndWatchExpressions(ast, self.$filter);
14781     var extra = '';
14782     var assignable;
14783     this.stage = 'assign';
14784     if ((assignable = assignableAST(ast))) {
14785       this.state.computing = 'assign';
14786       var result = this.nextId();
14787       this.recurse(assignable, result);
14788       this.return_(result);
14789       extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
14790     }
14791     var toWatch = getInputs(ast.body);
14792     self.stage = 'inputs';
14793     forEach(toWatch, function(watch, key) {
14794       var fnKey = 'fn' + key;
14795       self.state[fnKey] = {vars: [], body: [], own: {}};
14796       self.state.computing = fnKey;
14797       var intoId = self.nextId();
14798       self.recurse(watch, intoId);
14799       self.return_(intoId);
14800       self.state.inputs.push(fnKey);
14801       watch.watchId = key;
14802     });
14803     this.state.computing = 'fn';
14804     this.stage = 'main';
14805     this.recurse(ast);
14806     var fnString =
14807       // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
14808       // This is a workaround for this until we do a better job at only removing the prefix only when we should.
14809       '"' + this.USE + ' ' + this.STRICT + '";\n' +
14810       this.filterPrefix() +
14811       'var fn=' + this.generateFunction('fn', 's,l,a,i') +
14812       extra +
14813       this.watchFns() +
14814       'return fn;';
14816     /* jshint -W054 */
14817     var fn = (new Function('$filter',
14818         'ensureSafeMemberName',
14819         'ensureSafeObject',
14820         'ensureSafeFunction',
14821         'getStringValue',
14822         'ensureSafeAssignContext',
14823         'ifDefined',
14824         'plus',
14825         'text',
14826         fnString))(
14827           this.$filter,
14828           ensureSafeMemberName,
14829           ensureSafeObject,
14830           ensureSafeFunction,
14831           getStringValue,
14832           ensureSafeAssignContext,
14833           ifDefined,
14834           plusFn,
14835           expression);
14836     /* jshint +W054 */
14837     this.state = this.stage = undefined;
14838     fn.literal = isLiteral(ast);
14839     fn.constant = isConstant(ast);
14840     return fn;
14841   },
14843   USE: 'use',
14845   STRICT: 'strict',
14847   watchFns: function() {
14848     var result = [];
14849     var fns = this.state.inputs;
14850     var self = this;
14851     forEach(fns, function(name) {
14852       result.push('var ' + name + '=' + self.generateFunction(name, 's'));
14853     });
14854     if (fns.length) {
14855       result.push('fn.inputs=[' + fns.join(',') + '];');
14856     }
14857     return result.join('');
14858   },
14860   generateFunction: function(name, params) {
14861     return 'function(' + params + '){' +
14862         this.varsPrefix(name) +
14863         this.body(name) +
14864         '};';
14865   },
14867   filterPrefix: function() {
14868     var parts = [];
14869     var self = this;
14870     forEach(this.state.filters, function(id, filter) {
14871       parts.push(id + '=$filter(' + self.escape(filter) + ')');
14872     });
14873     if (parts.length) return 'var ' + parts.join(',') + ';';
14874     return '';
14875   },
14877   varsPrefix: function(section) {
14878     return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
14879   },
14881   body: function(section) {
14882     return this.state[section].body.join('');
14883   },
14885   recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
14886     var left, right, self = this, args, expression, computed;
14887     recursionFn = recursionFn || noop;
14888     if (!skipWatchIdCheck && isDefined(ast.watchId)) {
14889       intoId = intoId || this.nextId();
14890       this.if_('i',
14891         this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
14892         this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
14893       );
14894       return;
14895     }
14896     switch (ast.type) {
14897     case AST.Program:
14898       forEach(ast.body, function(expression, pos) {
14899         self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
14900         if (pos !== ast.body.length - 1) {
14901           self.current().body.push(right, ';');
14902         } else {
14903           self.return_(right);
14904         }
14905       });
14906       break;
14907     case AST.Literal:
14908       expression = this.escape(ast.value);
14909       this.assign(intoId, expression);
14910       recursionFn(expression);
14911       break;
14912     case AST.UnaryExpression:
14913       this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
14914       expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
14915       this.assign(intoId, expression);
14916       recursionFn(expression);
14917       break;
14918     case AST.BinaryExpression:
14919       this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
14920       this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
14921       if (ast.operator === '+') {
14922         expression = this.plus(left, right);
14923       } else if (ast.operator === '-') {
14924         expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
14925       } else {
14926         expression = '(' + left + ')' + ast.operator + '(' + right + ')';
14927       }
14928       this.assign(intoId, expression);
14929       recursionFn(expression);
14930       break;
14931     case AST.LogicalExpression:
14932       intoId = intoId || this.nextId();
14933       self.recurse(ast.left, intoId);
14934       self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
14935       recursionFn(intoId);
14936       break;
14937     case AST.ConditionalExpression:
14938       intoId = intoId || this.nextId();
14939       self.recurse(ast.test, intoId);
14940       self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
14941       recursionFn(intoId);
14942       break;
14943     case AST.Identifier:
14944       intoId = intoId || this.nextId();
14945       if (nameId) {
14946         nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
14947         nameId.computed = false;
14948         nameId.name = ast.name;
14949       }
14950       ensureSafeMemberName(ast.name);
14951       self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
14952         function() {
14953           self.if_(self.stage === 'inputs' || 's', function() {
14954             if (create && create !== 1) {
14955               self.if_(
14956                 self.not(self.nonComputedMember('s', ast.name)),
14957                 self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
14958             }
14959             self.assign(intoId, self.nonComputedMember('s', ast.name));
14960           });
14961         }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
14962         );
14963       if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
14964         self.addEnsureSafeObject(intoId);
14965       }
14966       recursionFn(intoId);
14967       break;
14968     case AST.MemberExpression:
14969       left = nameId && (nameId.context = this.nextId()) || this.nextId();
14970       intoId = intoId || this.nextId();
14971       self.recurse(ast.object, left, undefined, function() {
14972         self.if_(self.notNull(left), function() {
14973           if (create && create !== 1) {
14974             self.addEnsureSafeAssignContext(left);
14975           }
14976           if (ast.computed) {
14977             right = self.nextId();
14978             self.recurse(ast.property, right);
14979             self.getStringValue(right);
14980             self.addEnsureSafeMemberName(right);
14981             if (create && create !== 1) {
14982               self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
14983             }
14984             expression = self.ensureSafeObject(self.computedMember(left, right));
14985             self.assign(intoId, expression);
14986             if (nameId) {
14987               nameId.computed = true;
14988               nameId.name = right;
14989             }
14990           } else {
14991             ensureSafeMemberName(ast.property.name);
14992             if (create && create !== 1) {
14993               self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
14994             }
14995             expression = self.nonComputedMember(left, ast.property.name);
14996             if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
14997               expression = self.ensureSafeObject(expression);
14998             }
14999             self.assign(intoId, expression);
15000             if (nameId) {
15001               nameId.computed = false;
15002               nameId.name = ast.property.name;
15003             }
15004           }
15005         }, function() {
15006           self.assign(intoId, 'undefined');
15007         });
15008         recursionFn(intoId);
15009       }, !!create);
15010       break;
15011     case AST.CallExpression:
15012       intoId = intoId || this.nextId();
15013       if (ast.filter) {
15014         right = self.filter(ast.callee.name);
15015         args = [];
15016         forEach(ast.arguments, function(expr) {
15017           var argument = self.nextId();
15018           self.recurse(expr, argument);
15019           args.push(argument);
15020         });
15021         expression = right + '(' + args.join(',') + ')';
15022         self.assign(intoId, expression);
15023         recursionFn(intoId);
15024       } else {
15025         right = self.nextId();
15026         left = {};
15027         args = [];
15028         self.recurse(ast.callee, right, left, function() {
15029           self.if_(self.notNull(right), function() {
15030             self.addEnsureSafeFunction(right);
15031             forEach(ast.arguments, function(expr) {
15032               self.recurse(expr, self.nextId(), undefined, function(argument) {
15033                 args.push(self.ensureSafeObject(argument));
15034               });
15035             });
15036             if (left.name) {
15037               if (!self.state.expensiveChecks) {
15038                 self.addEnsureSafeObject(left.context);
15039               }
15040               expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
15041             } else {
15042               expression = right + '(' + args.join(',') + ')';
15043             }
15044             expression = self.ensureSafeObject(expression);
15045             self.assign(intoId, expression);
15046           }, function() {
15047             self.assign(intoId, 'undefined');
15048           });
15049           recursionFn(intoId);
15050         });
15051       }
15052       break;
15053     case AST.AssignmentExpression:
15054       right = this.nextId();
15055       left = {};
15056       if (!isAssignable(ast.left)) {
15057         throw $parseMinErr('lval', 'Trying to assign a value to a non l-value');
15058       }
15059       this.recurse(ast.left, undefined, left, function() {
15060         self.if_(self.notNull(left.context), function() {
15061           self.recurse(ast.right, right);
15062           self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
15063           self.addEnsureSafeAssignContext(left.context);
15064           expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
15065           self.assign(intoId, expression);
15066           recursionFn(intoId || expression);
15067         });
15068       }, 1);
15069       break;
15070     case AST.ArrayExpression:
15071       args = [];
15072       forEach(ast.elements, function(expr) {
15073         self.recurse(expr, self.nextId(), undefined, function(argument) {
15074           args.push(argument);
15075         });
15076       });
15077       expression = '[' + args.join(',') + ']';
15078       this.assign(intoId, expression);
15079       recursionFn(expression);
15080       break;
15081     case AST.ObjectExpression:
15082       args = [];
15083       computed = false;
15084       forEach(ast.properties, function(property) {
15085         if (property.computed) {
15086           computed = true;
15087         }
15088       });
15089       if (computed) {
15090         intoId = intoId || this.nextId();
15091         this.assign(intoId, '{}');
15092         forEach(ast.properties, function(property) {
15093           if (property.computed) {
15094             left = self.nextId();
15095             self.recurse(property.key, left);
15096           } else {
15097             left = property.key.type === AST.Identifier ?
15098                        property.key.name :
15099                        ('' + property.key.value);
15100           }
15101           right = self.nextId();
15102           self.recurse(property.value, right);
15103           self.assign(self.member(intoId, left, property.computed), right);
15104         });
15105       } else {
15106         forEach(ast.properties, function(property) {
15107           self.recurse(property.value, ast.constant ? undefined : self.nextId(), undefined, function(expr) {
15108             args.push(self.escape(
15109                 property.key.type === AST.Identifier ? property.key.name :
15110                   ('' + property.key.value)) +
15111                 ':' + expr);
15112           });
15113         });
15114         expression = '{' + args.join(',') + '}';
15115         this.assign(intoId, expression);
15116       }
15117       recursionFn(intoId || expression);
15118       break;
15119     case AST.ThisExpression:
15120       this.assign(intoId, 's');
15121       recursionFn('s');
15122       break;
15123     case AST.LocalsExpression:
15124       this.assign(intoId, 'l');
15125       recursionFn('l');
15126       break;
15127     case AST.NGValueParameter:
15128       this.assign(intoId, 'v');
15129       recursionFn('v');
15130       break;
15131     }
15132   },
15134   getHasOwnProperty: function(element, property) {
15135     var key = element + '.' + property;
15136     var own = this.current().own;
15137     if (!own.hasOwnProperty(key)) {
15138       own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
15139     }
15140     return own[key];
15141   },
15143   assign: function(id, value) {
15144     if (!id) return;
15145     this.current().body.push(id, '=', value, ';');
15146     return id;
15147   },
15149   filter: function(filterName) {
15150     if (!this.state.filters.hasOwnProperty(filterName)) {
15151       this.state.filters[filterName] = this.nextId(true);
15152     }
15153     return this.state.filters[filterName];
15154   },
15156   ifDefined: function(id, defaultValue) {
15157     return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
15158   },
15160   plus: function(left, right) {
15161     return 'plus(' + left + ',' + right + ')';
15162   },
15164   return_: function(id) {
15165     this.current().body.push('return ', id, ';');
15166   },
15168   if_: function(test, alternate, consequent) {
15169     if (test === true) {
15170       alternate();
15171     } else {
15172       var body = this.current().body;
15173       body.push('if(', test, '){');
15174       alternate();
15175       body.push('}');
15176       if (consequent) {
15177         body.push('else{');
15178         consequent();
15179         body.push('}');
15180       }
15181     }
15182   },
15184   not: function(expression) {
15185     return '!(' + expression + ')';
15186   },
15188   notNull: function(expression) {
15189     return expression + '!=null';
15190   },
15192   nonComputedMember: function(left, right) {
15193     var SAFE_IDENTIFIER = /[$_a-zA-Z][$_a-zA-Z0-9]*/;
15194     var UNSAFE_CHARACTERS = /[^$_a-zA-Z0-9]/g;
15195     if (SAFE_IDENTIFIER.test(right)) {
15196       return left + '.' + right;
15197     } else {
15198       return left  + '["' + right.replace(UNSAFE_CHARACTERS, this.stringEscapeFn) + '"]';
15199     }
15200   },
15202   computedMember: function(left, right) {
15203     return left + '[' + right + ']';
15204   },
15206   member: function(left, right, computed) {
15207     if (computed) return this.computedMember(left, right);
15208     return this.nonComputedMember(left, right);
15209   },
15211   addEnsureSafeObject: function(item) {
15212     this.current().body.push(this.ensureSafeObject(item), ';');
15213   },
15215   addEnsureSafeMemberName: function(item) {
15216     this.current().body.push(this.ensureSafeMemberName(item), ';');
15217   },
15219   addEnsureSafeFunction: function(item) {
15220     this.current().body.push(this.ensureSafeFunction(item), ';');
15221   },
15223   addEnsureSafeAssignContext: function(item) {
15224     this.current().body.push(this.ensureSafeAssignContext(item), ';');
15225   },
15227   ensureSafeObject: function(item) {
15228     return 'ensureSafeObject(' + item + ',text)';
15229   },
15231   ensureSafeMemberName: function(item) {
15232     return 'ensureSafeMemberName(' + item + ',text)';
15233   },
15235   ensureSafeFunction: function(item) {
15236     return 'ensureSafeFunction(' + item + ',text)';
15237   },
15239   getStringValue: function(item) {
15240     this.assign(item, 'getStringValue(' + item + ')');
15241   },
15243   ensureSafeAssignContext: function(item) {
15244     return 'ensureSafeAssignContext(' + item + ',text)';
15245   },
15247   lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
15248     var self = this;
15249     return function() {
15250       self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
15251     };
15252   },
15254   lazyAssign: function(id, value) {
15255     var self = this;
15256     return function() {
15257       self.assign(id, value);
15258     };
15259   },
15261   stringEscapeRegex: /[^ a-zA-Z0-9]/g,
15263   stringEscapeFn: function(c) {
15264     return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
15265   },
15267   escape: function(value) {
15268     if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
15269     if (isNumber(value)) return value.toString();
15270     if (value === true) return 'true';
15271     if (value === false) return 'false';
15272     if (value === null) return 'null';
15273     if (typeof value === 'undefined') return 'undefined';
15275     throw $parseMinErr('esc', 'IMPOSSIBLE');
15276   },
15278   nextId: function(skip, init) {
15279     var id = 'v' + (this.state.nextId++);
15280     if (!skip) {
15281       this.current().vars.push(id + (init ? '=' + init : ''));
15282     }
15283     return id;
15284   },
15286   current: function() {
15287     return this.state[this.state.computing];
15288   }
15292 function ASTInterpreter(astBuilder, $filter) {
15293   this.astBuilder = astBuilder;
15294   this.$filter = $filter;
15297 ASTInterpreter.prototype = {
15298   compile: function(expression, expensiveChecks) {
15299     var self = this;
15300     var ast = this.astBuilder.ast(expression);
15301     this.expression = expression;
15302     this.expensiveChecks = expensiveChecks;
15303     findConstantAndWatchExpressions(ast, self.$filter);
15304     var assignable;
15305     var assign;
15306     if ((assignable = assignableAST(ast))) {
15307       assign = this.recurse(assignable);
15308     }
15309     var toWatch = getInputs(ast.body);
15310     var inputs;
15311     if (toWatch) {
15312       inputs = [];
15313       forEach(toWatch, function(watch, key) {
15314         var input = self.recurse(watch);
15315         watch.input = input;
15316         inputs.push(input);
15317         watch.watchId = key;
15318       });
15319     }
15320     var expressions = [];
15321     forEach(ast.body, function(expression) {
15322       expressions.push(self.recurse(expression.expression));
15323     });
15324     var fn = ast.body.length === 0 ? noop :
15325              ast.body.length === 1 ? expressions[0] :
15326              function(scope, locals) {
15327                var lastValue;
15328                forEach(expressions, function(exp) {
15329                  lastValue = exp(scope, locals);
15330                });
15331                return lastValue;
15332              };
15333     if (assign) {
15334       fn.assign = function(scope, value, locals) {
15335         return assign(scope, locals, value);
15336       };
15337     }
15338     if (inputs) {
15339       fn.inputs = inputs;
15340     }
15341     fn.literal = isLiteral(ast);
15342     fn.constant = isConstant(ast);
15343     return fn;
15344   },
15346   recurse: function(ast, context, create) {
15347     var left, right, self = this, args, expression;
15348     if (ast.input) {
15349       return this.inputs(ast.input, ast.watchId);
15350     }
15351     switch (ast.type) {
15352     case AST.Literal:
15353       return this.value(ast.value, context);
15354     case AST.UnaryExpression:
15355       right = this.recurse(ast.argument);
15356       return this['unary' + ast.operator](right, context);
15357     case AST.BinaryExpression:
15358       left = this.recurse(ast.left);
15359       right = this.recurse(ast.right);
15360       return this['binary' + ast.operator](left, right, context);
15361     case AST.LogicalExpression:
15362       left = this.recurse(ast.left);
15363       right = this.recurse(ast.right);
15364       return this['binary' + ast.operator](left, right, context);
15365     case AST.ConditionalExpression:
15366       return this['ternary?:'](
15367         this.recurse(ast.test),
15368         this.recurse(ast.alternate),
15369         this.recurse(ast.consequent),
15370         context
15371       );
15372     case AST.Identifier:
15373       ensureSafeMemberName(ast.name, self.expression);
15374       return self.identifier(ast.name,
15375                              self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
15376                              context, create, self.expression);
15377     case AST.MemberExpression:
15378       left = this.recurse(ast.object, false, !!create);
15379       if (!ast.computed) {
15380         ensureSafeMemberName(ast.property.name, self.expression);
15381         right = ast.property.name;
15382       }
15383       if (ast.computed) right = this.recurse(ast.property);
15384       return ast.computed ?
15385         this.computedMember(left, right, context, create, self.expression) :
15386         this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
15387     case AST.CallExpression:
15388       args = [];
15389       forEach(ast.arguments, function(expr) {
15390         args.push(self.recurse(expr));
15391       });
15392       if (ast.filter) right = this.$filter(ast.callee.name);
15393       if (!ast.filter) right = this.recurse(ast.callee, true);
15394       return ast.filter ?
15395         function(scope, locals, assign, inputs) {
15396           var values = [];
15397           for (var i = 0; i < args.length; ++i) {
15398             values.push(args[i](scope, locals, assign, inputs));
15399           }
15400           var value = right.apply(undefined, values, inputs);
15401           return context ? {context: undefined, name: undefined, value: value} : value;
15402         } :
15403         function(scope, locals, assign, inputs) {
15404           var rhs = right(scope, locals, assign, inputs);
15405           var value;
15406           if (rhs.value != null) {
15407             ensureSafeObject(rhs.context, self.expression);
15408             ensureSafeFunction(rhs.value, self.expression);
15409             var values = [];
15410             for (var i = 0; i < args.length; ++i) {
15411               values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
15412             }
15413             value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
15414           }
15415           return context ? {value: value} : value;
15416         };
15417     case AST.AssignmentExpression:
15418       left = this.recurse(ast.left, true, 1);
15419       right = this.recurse(ast.right);
15420       return function(scope, locals, assign, inputs) {
15421         var lhs = left(scope, locals, assign, inputs);
15422         var rhs = right(scope, locals, assign, inputs);
15423         ensureSafeObject(lhs.value, self.expression);
15424         ensureSafeAssignContext(lhs.context);
15425         lhs.context[lhs.name] = rhs;
15426         return context ? {value: rhs} : rhs;
15427       };
15428     case AST.ArrayExpression:
15429       args = [];
15430       forEach(ast.elements, function(expr) {
15431         args.push(self.recurse(expr));
15432       });
15433       return function(scope, locals, assign, inputs) {
15434         var value = [];
15435         for (var i = 0; i < args.length; ++i) {
15436           value.push(args[i](scope, locals, assign, inputs));
15437         }
15438         return context ? {value: value} : value;
15439       };
15440     case AST.ObjectExpression:
15441       args = [];
15442       forEach(ast.properties, function(property) {
15443         if (property.computed) {
15444           args.push({key: self.recurse(property.key),
15445                      computed: true,
15446                      value: self.recurse(property.value)
15447           });
15448         } else {
15449           args.push({key: property.key.type === AST.Identifier ?
15450                           property.key.name :
15451                           ('' + property.key.value),
15452                      computed: false,
15453                      value: self.recurse(property.value)
15454           });
15455         }
15456       });
15457       return function(scope, locals, assign, inputs) {
15458         var value = {};
15459         for (var i = 0; i < args.length; ++i) {
15460           if (args[i].computed) {
15461             value[args[i].key(scope, locals, assign, inputs)] = args[i].value(scope, locals, assign, inputs);
15462           } else {
15463             value[args[i].key] = args[i].value(scope, locals, assign, inputs);
15464           }
15465         }
15466         return context ? {value: value} : value;
15467       };
15468     case AST.ThisExpression:
15469       return function(scope) {
15470         return context ? {value: scope} : scope;
15471       };
15472     case AST.LocalsExpression:
15473       return function(scope, locals) {
15474         return context ? {value: locals} : locals;
15475       };
15476     case AST.NGValueParameter:
15477       return function(scope, locals, assign) {
15478         return context ? {value: assign} : assign;
15479       };
15480     }
15481   },
15483   'unary+': function(argument, context) {
15484     return function(scope, locals, assign, inputs) {
15485       var arg = argument(scope, locals, assign, inputs);
15486       if (isDefined(arg)) {
15487         arg = +arg;
15488       } else {
15489         arg = 0;
15490       }
15491       return context ? {value: arg} : arg;
15492     };
15493   },
15494   'unary-': function(argument, context) {
15495     return function(scope, locals, assign, inputs) {
15496       var arg = argument(scope, locals, assign, inputs);
15497       if (isDefined(arg)) {
15498         arg = -arg;
15499       } else {
15500         arg = 0;
15501       }
15502       return context ? {value: arg} : arg;
15503     };
15504   },
15505   'unary!': function(argument, context) {
15506     return function(scope, locals, assign, inputs) {
15507       var arg = !argument(scope, locals, assign, inputs);
15508       return context ? {value: arg} : arg;
15509     };
15510   },
15511   'binary+': function(left, right, context) {
15512     return function(scope, locals, assign, inputs) {
15513       var lhs = left(scope, locals, assign, inputs);
15514       var rhs = right(scope, locals, assign, inputs);
15515       var arg = plusFn(lhs, rhs);
15516       return context ? {value: arg} : arg;
15517     };
15518   },
15519   'binary-': function(left, right, context) {
15520     return function(scope, locals, assign, inputs) {
15521       var lhs = left(scope, locals, assign, inputs);
15522       var rhs = right(scope, locals, assign, inputs);
15523       var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
15524       return context ? {value: arg} : arg;
15525     };
15526   },
15527   'binary*': function(left, right, context) {
15528     return function(scope, locals, assign, inputs) {
15529       var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
15530       return context ? {value: arg} : arg;
15531     };
15532   },
15533   'binary/': function(left, right, context) {
15534     return function(scope, locals, assign, inputs) {
15535       var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
15536       return context ? {value: arg} : arg;
15537     };
15538   },
15539   'binary%': function(left, right, context) {
15540     return function(scope, locals, assign, inputs) {
15541       var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
15542       return context ? {value: arg} : arg;
15543     };
15544   },
15545   'binary===': function(left, right, context) {
15546     return function(scope, locals, assign, inputs) {
15547       var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
15548       return context ? {value: arg} : arg;
15549     };
15550   },
15551   'binary!==': function(left, right, context) {
15552     return function(scope, locals, assign, inputs) {
15553       var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
15554       return context ? {value: arg} : arg;
15555     };
15556   },
15557   'binary==': function(left, right, context) {
15558     return function(scope, locals, assign, inputs) {
15559       var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
15560       return context ? {value: arg} : arg;
15561     };
15562   },
15563   'binary!=': function(left, right, context) {
15564     return function(scope, locals, assign, inputs) {
15565       var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
15566       return context ? {value: arg} : arg;
15567     };
15568   },
15569   'binary<': function(left, right, context) {
15570     return function(scope, locals, assign, inputs) {
15571       var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
15572       return context ? {value: arg} : arg;
15573     };
15574   },
15575   'binary>': function(left, right, context) {
15576     return function(scope, locals, assign, inputs) {
15577       var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
15578       return context ? {value: arg} : arg;
15579     };
15580   },
15581   'binary<=': function(left, right, context) {
15582     return function(scope, locals, assign, inputs) {
15583       var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
15584       return context ? {value: arg} : arg;
15585     };
15586   },
15587   'binary>=': function(left, right, context) {
15588     return function(scope, locals, assign, inputs) {
15589       var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
15590       return context ? {value: arg} : arg;
15591     };
15592   },
15593   'binary&&': function(left, right, context) {
15594     return function(scope, locals, assign, inputs) {
15595       var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
15596       return context ? {value: arg} : arg;
15597     };
15598   },
15599   'binary||': function(left, right, context) {
15600     return function(scope, locals, assign, inputs) {
15601       var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
15602       return context ? {value: arg} : arg;
15603     };
15604   },
15605   'ternary?:': function(test, alternate, consequent, context) {
15606     return function(scope, locals, assign, inputs) {
15607       var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
15608       return context ? {value: arg} : arg;
15609     };
15610   },
15611   value: function(value, context) {
15612     return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
15613   },
15614   identifier: function(name, expensiveChecks, context, create, expression) {
15615     return function(scope, locals, assign, inputs) {
15616       var base = locals && (name in locals) ? locals : scope;
15617       if (create && create !== 1 && base && !(base[name])) {
15618         base[name] = {};
15619       }
15620       var value = base ? base[name] : undefined;
15621       if (expensiveChecks) {
15622         ensureSafeObject(value, expression);
15623       }
15624       if (context) {
15625         return {context: base, name: name, value: value};
15626       } else {
15627         return value;
15628       }
15629     };
15630   },
15631   computedMember: function(left, right, context, create, expression) {
15632     return function(scope, locals, assign, inputs) {
15633       var lhs = left(scope, locals, assign, inputs);
15634       var rhs;
15635       var value;
15636       if (lhs != null) {
15637         rhs = right(scope, locals, assign, inputs);
15638         rhs = getStringValue(rhs);
15639         ensureSafeMemberName(rhs, expression);
15640         if (create && create !== 1) {
15641           ensureSafeAssignContext(lhs);
15642           if (lhs && !(lhs[rhs])) {
15643             lhs[rhs] = {};
15644           }
15645         }
15646         value = lhs[rhs];
15647         ensureSafeObject(value, expression);
15648       }
15649       if (context) {
15650         return {context: lhs, name: rhs, value: value};
15651       } else {
15652         return value;
15653       }
15654     };
15655   },
15656   nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
15657     return function(scope, locals, assign, inputs) {
15658       var lhs = left(scope, locals, assign, inputs);
15659       if (create && create !== 1) {
15660         ensureSafeAssignContext(lhs);
15661         if (lhs && !(lhs[right])) {
15662           lhs[right] = {};
15663         }
15664       }
15665       var value = lhs != null ? lhs[right] : undefined;
15666       if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
15667         ensureSafeObject(value, expression);
15668       }
15669       if (context) {
15670         return {context: lhs, name: right, value: value};
15671       } else {
15672         return value;
15673       }
15674     };
15675   },
15676   inputs: function(input, watchId) {
15677     return function(scope, value, locals, inputs) {
15678       if (inputs) return inputs[watchId];
15679       return input(scope, value, locals);
15680     };
15681   }
15685  * @constructor
15686  */
15687 var Parser = function(lexer, $filter, options) {
15688   this.lexer = lexer;
15689   this.$filter = $filter;
15690   this.options = options;
15691   this.ast = new AST(lexer, options);
15692   this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
15693                                    new ASTCompiler(this.ast, $filter);
15696 Parser.prototype = {
15697   constructor: Parser,
15699   parse: function(text) {
15700     return this.astCompiler.compile(text, this.options.expensiveChecks);
15701   }
15704 function isPossiblyDangerousMemberName(name) {
15705   return name == 'constructor';
15708 var objectValueOf = Object.prototype.valueOf;
15710 function getValueOf(value) {
15711   return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
15714 ///////////////////////////////////
15717  * @ngdoc service
15718  * @name $parse
15719  * @kind function
15721  * @description
15723  * Converts Angular {@link guide/expression expression} into a function.
15725  * ```js
15726  *   var getter = $parse('user.name');
15727  *   var setter = getter.assign;
15728  *   var context = {user:{name:'angular'}};
15729  *   var locals = {user:{name:'local'}};
15731  *   expect(getter(context)).toEqual('angular');
15732  *   setter(context, 'newValue');
15733  *   expect(context.user.name).toEqual('newValue');
15734  *   expect(getter(context, locals)).toEqual('local');
15735  * ```
15738  * @param {string} expression String expression to compile.
15739  * @returns {function(context, locals)} a function which represents the compiled expression:
15741  *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings
15742  *      are evaluated against (typically a scope object).
15743  *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in
15744  *      `context`.
15746  *    The returned function also has the following properties:
15747  *      * `literal` â€“ `{boolean}` â€“ whether the expression's top-level node is a JavaScript
15748  *        literal.
15749  *      * `constant` â€“ `{boolean}` â€“ whether the expression is made entirely of JavaScript
15750  *        constant literals.
15751  *      * `assign` â€“ `{?function(context, value)}` â€“ if the expression is assignable, this will be
15752  *        set to a function to change its value on the given context.
15754  */
15758  * @ngdoc provider
15759  * @name $parseProvider
15761  * @description
15762  * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
15763  *  service.
15764  */
15765 function $ParseProvider() {
15766   var cacheDefault = createMap();
15767   var cacheExpensive = createMap();
15768   var literals = {
15769     'true': true,
15770     'false': false,
15771     'null': null,
15772     'undefined': undefined
15773   };
15774   var identStart, identContinue;
15776   /**
15777    * @ngdoc method
15778    * @name $parseProvider#addLiteral
15779    * @description
15780    *
15781    * Configure $parse service to add literal values that will be present as literal at expressions.
15782    *
15783    * @param {string} literalName Token for the literal value. The literal name value must be a valid literal name.
15784    * @param {*} literalValue Value for this literal. All literal values must be primitives or `undefined`.
15785    *
15786    **/
15787   this.addLiteral = function(literalName, literalValue) {
15788     literals[literalName] = literalValue;
15789   };
15791  /**
15792   * @ngdoc method
15793   * @name $parseProvider#setIdentifierFns
15794   * @description
15795   *
15796   * Allows defining the set of characters that are allowed in Angular expressions. The function
15797   * `identifierStart` will get called to know if a given character is a valid character to be the
15798   * first character for an identifier. The function `identifierContinue` will get called to know if
15799   * a given character is a valid character to be a follow-up identifier character. The functions
15800   * `identifierStart` and `identifierContinue` will receive as arguments the single character to be
15801   * identifier and the character code point. These arguments will be `string` and `numeric`. Keep in
15802   * mind that the `string` parameter can be two characters long depending on the character
15803   * representation. It is expected for the function to return `true` or `false`, whether that
15804   * character is allowed or not.
15805   *
15806   * Since this function will be called extensivelly, keep the implementation of these functions fast,
15807   * as the performance of these functions have a direct impact on the expressions parsing speed.
15808   *
15809   * @param {function=} identifierStart The function that will decide whether the given character is
15810   *   a valid identifier start character.
15811   * @param {function=} identifierContinue The function that will decide whether the given character is
15812   *   a valid identifier continue character.
15813   */
15814   this.setIdentifierFns = function(identifierStart, identifierContinue) {
15815     identStart = identifierStart;
15816     identContinue = identifierContinue;
15817     return this;
15818   };
15820   this.$get = ['$filter', function($filter) {
15821     var noUnsafeEval = csp().noUnsafeEval;
15822     var $parseOptions = {
15823           csp: noUnsafeEval,
15824           expensiveChecks: false,
15825           literals: copy(literals),
15826           isIdentifierStart: isFunction(identStart) && identStart,
15827           isIdentifierContinue: isFunction(identContinue) && identContinue
15828         },
15829         $parseOptionsExpensive = {
15830           csp: noUnsafeEval,
15831           expensiveChecks: true,
15832           literals: copy(literals),
15833           isIdentifierStart: isFunction(identStart) && identStart,
15834           isIdentifierContinue: isFunction(identContinue) && identContinue
15835         };
15836     var runningChecksEnabled = false;
15838     $parse.$$runningExpensiveChecks = function() {
15839       return runningChecksEnabled;
15840     };
15842     return $parse;
15844     function $parse(exp, interceptorFn, expensiveChecks) {
15845       var parsedExpression, oneTime, cacheKey;
15847       expensiveChecks = expensiveChecks || runningChecksEnabled;
15849       switch (typeof exp) {
15850         case 'string':
15851           exp = exp.trim();
15852           cacheKey = exp;
15854           var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
15855           parsedExpression = cache[cacheKey];
15857           if (!parsedExpression) {
15858             if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
15859               oneTime = true;
15860               exp = exp.substring(2);
15861             }
15862             var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
15863             var lexer = new Lexer(parseOptions);
15864             var parser = new Parser(lexer, $filter, parseOptions);
15865             parsedExpression = parser.parse(exp);
15866             if (parsedExpression.constant) {
15867               parsedExpression.$$watchDelegate = constantWatchDelegate;
15868             } else if (oneTime) {
15869               parsedExpression.$$watchDelegate = parsedExpression.literal ?
15870                   oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
15871             } else if (parsedExpression.inputs) {
15872               parsedExpression.$$watchDelegate = inputsWatchDelegate;
15873             }
15874             if (expensiveChecks) {
15875               parsedExpression = expensiveChecksInterceptor(parsedExpression);
15876             }
15877             cache[cacheKey] = parsedExpression;
15878           }
15879           return addInterceptor(parsedExpression, interceptorFn);
15881         case 'function':
15882           return addInterceptor(exp, interceptorFn);
15884         default:
15885           return addInterceptor(noop, interceptorFn);
15886       }
15887     }
15889     function expensiveChecksInterceptor(fn) {
15890       if (!fn) return fn;
15891       expensiveCheckFn.$$watchDelegate = fn.$$watchDelegate;
15892       expensiveCheckFn.assign = expensiveChecksInterceptor(fn.assign);
15893       expensiveCheckFn.constant = fn.constant;
15894       expensiveCheckFn.literal = fn.literal;
15895       for (var i = 0; fn.inputs && i < fn.inputs.length; ++i) {
15896         fn.inputs[i] = expensiveChecksInterceptor(fn.inputs[i]);
15897       }
15898       expensiveCheckFn.inputs = fn.inputs;
15900       return expensiveCheckFn;
15902       function expensiveCheckFn(scope, locals, assign, inputs) {
15903         var expensiveCheckOldValue = runningChecksEnabled;
15904         runningChecksEnabled = true;
15905         try {
15906           return fn(scope, locals, assign, inputs);
15907         } finally {
15908           runningChecksEnabled = expensiveCheckOldValue;
15909         }
15910       }
15911     }
15913     function expressionInputDirtyCheck(newValue, oldValueOfValue) {
15915       if (newValue == null || oldValueOfValue == null) { // null/undefined
15916         return newValue === oldValueOfValue;
15917       }
15919       if (typeof newValue === 'object') {
15921         // attempt to convert the value to a primitive type
15922         // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
15923         //             be cheaply dirty-checked
15924         newValue = getValueOf(newValue);
15926         if (typeof newValue === 'object') {
15927           // objects/arrays are not supported - deep-watching them would be too expensive
15928           return false;
15929         }
15931         // fall-through to the primitive equality check
15932       }
15934       //Primitive or NaN
15935       return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
15936     }
15938     function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
15939       var inputExpressions = parsedExpression.inputs;
15940       var lastResult;
15942       if (inputExpressions.length === 1) {
15943         var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
15944         inputExpressions = inputExpressions[0];
15945         return scope.$watch(function expressionInputWatch(scope) {
15946           var newInputValue = inputExpressions(scope);
15947           if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
15948             lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
15949             oldInputValueOf = newInputValue && getValueOf(newInputValue);
15950           }
15951           return lastResult;
15952         }, listener, objectEquality, prettyPrintExpression);
15953       }
15955       var oldInputValueOfValues = [];
15956       var oldInputValues = [];
15957       for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
15958         oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
15959         oldInputValues[i] = null;
15960       }
15962       return scope.$watch(function expressionInputsWatch(scope) {
15963         var changed = false;
15965         for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
15966           var newInputValue = inputExpressions[i](scope);
15967           if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
15968             oldInputValues[i] = newInputValue;
15969             oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
15970           }
15971         }
15973         if (changed) {
15974           lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
15975         }
15977         return lastResult;
15978       }, listener, objectEquality, prettyPrintExpression);
15979     }
15981     function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
15982       var unwatch, lastValue;
15983       return unwatch = scope.$watch(function oneTimeWatch(scope) {
15984         return parsedExpression(scope);
15985       }, function oneTimeListener(value, old, scope) {
15986         lastValue = value;
15987         if (isFunction(listener)) {
15988           listener.apply(this, arguments);
15989         }
15990         if (isDefined(value)) {
15991           scope.$$postDigest(function() {
15992             if (isDefined(lastValue)) {
15993               unwatch();
15994             }
15995           });
15996         }
15997       }, objectEquality);
15998     }
16000     function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
16001       var unwatch, lastValue;
16002       return unwatch = scope.$watch(function oneTimeWatch(scope) {
16003         return parsedExpression(scope);
16004       }, function oneTimeListener(value, old, scope) {
16005         lastValue = value;
16006         if (isFunction(listener)) {
16007           listener.call(this, value, old, scope);
16008         }
16009         if (isAllDefined(value)) {
16010           scope.$$postDigest(function() {
16011             if (isAllDefined(lastValue)) unwatch();
16012           });
16013         }
16014       }, objectEquality);
16016       function isAllDefined(value) {
16017         var allDefined = true;
16018         forEach(value, function(val) {
16019           if (!isDefined(val)) allDefined = false;
16020         });
16021         return allDefined;
16022       }
16023     }
16025     function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
16026       var unwatch;
16027       return unwatch = scope.$watch(function constantWatch(scope) {
16028         unwatch();
16029         return parsedExpression(scope);
16030       }, listener, objectEquality);
16031     }
16033     function addInterceptor(parsedExpression, interceptorFn) {
16034       if (!interceptorFn) return parsedExpression;
16035       var watchDelegate = parsedExpression.$$watchDelegate;
16036       var useInputs = false;
16038       var regularWatch =
16039           watchDelegate !== oneTimeLiteralWatchDelegate &&
16040           watchDelegate !== oneTimeWatchDelegate;
16042       var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
16043         var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
16044         return interceptorFn(value, scope, locals);
16045       } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
16046         var value = parsedExpression(scope, locals, assign, inputs);
16047         var result = interceptorFn(value, scope, locals);
16048         // we only return the interceptor's result if the
16049         // initial value is defined (for bind-once)
16050         return isDefined(value) ? result : value;
16051       };
16053       // Propagate $$watchDelegates other then inputsWatchDelegate
16054       if (parsedExpression.$$watchDelegate &&
16055           parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
16056         fn.$$watchDelegate = parsedExpression.$$watchDelegate;
16057       } else if (!interceptorFn.$stateful) {
16058         // If there is an interceptor, but no watchDelegate then treat the interceptor like
16059         // we treat filters - it is assumed to be a pure function unless flagged with $stateful
16060         fn.$$watchDelegate = inputsWatchDelegate;
16061         useInputs = !parsedExpression.inputs;
16062         fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
16063       }
16065       return fn;
16066     }
16067   }];
16071  * @ngdoc service
16072  * @name $q
16073  * @requires $rootScope
16075  * @description
16076  * A service that helps you run functions asynchronously, and use their return values (or exceptions)
16077  * when they are done processing.
16079  * This is an implementation of promises/deferred objects inspired by
16080  * [Kris Kowal's Q](https://github.com/kriskowal/q).
16082  * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
16083  * implementations, and the other which resembles ES6 (ES2015) promises to some degree.
16085  * # $q constructor
16087  * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
16088  * function as the first argument. This is similar to the native Promise implementation from ES6,
16089  * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
16091  * While the constructor-style use is supported, not all of the supporting methods from ES6 promises are
16092  * available yet.
16094  * It can be used like so:
16096  * ```js
16097  *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
16098  *   // are available in the current lexical scope (they could have been injected or passed in).
16100  *   function asyncGreet(name) {
16101  *     // perform some asynchronous operation, resolve or reject the promise when appropriate.
16102  *     return $q(function(resolve, reject) {
16103  *       setTimeout(function() {
16104  *         if (okToGreet(name)) {
16105  *           resolve('Hello, ' + name + '!');
16106  *         } else {
16107  *           reject('Greeting ' + name + ' is not allowed.');
16108  *         }
16109  *       }, 1000);
16110  *     });
16111  *   }
16113  *   var promise = asyncGreet('Robin Hood');
16114  *   promise.then(function(greeting) {
16115  *     alert('Success: ' + greeting);
16116  *   }, function(reason) {
16117  *     alert('Failed: ' + reason);
16118  *   });
16119  * ```
16121  * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
16123  * Note: unlike ES6 behavior, an exception thrown in the constructor function will NOT implicitly reject the promise.
16125  * However, the more traditional CommonJS-style usage is still available, and documented below.
16127  * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
16128  * interface for interacting with an object that represents the result of an action that is
16129  * performed asynchronously, and may or may not be finished at any given point in time.
16131  * From the perspective of dealing with error handling, deferred and promise APIs are to
16132  * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
16134  * ```js
16135  *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
16136  *   // are available in the current lexical scope (they could have been injected or passed in).
16138  *   function asyncGreet(name) {
16139  *     var deferred = $q.defer();
16141  *     setTimeout(function() {
16142  *       deferred.notify('About to greet ' + name + '.');
16144  *       if (okToGreet(name)) {
16145  *         deferred.resolve('Hello, ' + name + '!');
16146  *       } else {
16147  *         deferred.reject('Greeting ' + name + ' is not allowed.');
16148  *       }
16149  *     }, 1000);
16151  *     return deferred.promise;
16152  *   }
16154  *   var promise = asyncGreet('Robin Hood');
16155  *   promise.then(function(greeting) {
16156  *     alert('Success: ' + greeting);
16157  *   }, function(reason) {
16158  *     alert('Failed: ' + reason);
16159  *   }, function(update) {
16160  *     alert('Got notification: ' + update);
16161  *   });
16162  * ```
16164  * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
16165  * comes in the way of guarantees that promise and deferred APIs make, see
16166  * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
16168  * Additionally the promise api allows for composition that is very hard to do with the
16169  * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
16170  * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
16171  * section on serial or parallel joining of promises.
16173  * # The Deferred API
16175  * A new instance of deferred is constructed by calling `$q.defer()`.
16177  * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
16178  * that can be used for signaling the successful or unsuccessful completion, as well as the status
16179  * of the task.
16181  * **Methods**
16183  * - `resolve(value)` â€“ resolves the derived promise with the `value`. If the value is a rejection
16184  *   constructed via `$q.reject`, the promise will be rejected instead.
16185  * - `reject(reason)` â€“ rejects the derived promise with the `reason`. This is equivalent to
16186  *   resolving it with a rejection constructed via `$q.reject`.
16187  * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
16188  *   multiple times before the promise is either resolved or rejected.
16190  * **Properties**
16192  * - promise â€“ `{Promise}` â€“ promise object associated with this deferred.
16195  * # The Promise API
16197  * A new promise instance is created when a deferred instance is created and can be retrieved by
16198  * calling `deferred.promise`.
16200  * The purpose of the promise object is to allow for interested parties to get access to the result
16201  * of the deferred task when it completes.
16203  * **Methods**
16205  * - `then(successCallback, [errorCallback], [notifyCallback])` â€“ regardless of when the promise was or
16206  *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
16207  *   as soon as the result is available. The callbacks are called with a single argument: the result
16208  *   or rejection reason. Additionally, the notify callback may be called zero or more times to
16209  *   provide a progress indication, before the promise is resolved or rejected.
16211  *   This method *returns a new promise* which is resolved or rejected via the return value of the
16212  *   `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
16213  *   with the value which is resolved in that promise using
16214  *   [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
16215  *   It also notifies via the return value of the `notifyCallback` method. The promise cannot be
16216  *   resolved or rejected from the notifyCallback method. The errorCallback and notifyCallback
16217  *   arguments are optional.
16219  * - `catch(errorCallback)` â€“ shorthand for `promise.then(null, errorCallback)`
16221  * - `finally(callback, notifyCallback)` â€“ allows you to observe either the fulfillment or rejection of a promise,
16222  *   but to do so without modifying the final value. This is useful to release resources or do some
16223  *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
16224  *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
16225  *   more information.
16227  * # Chaining promises
16229  * Because calling the `then` method of a promise returns a new derived promise, it is easily
16230  * possible to create a chain of promises:
16232  * ```js
16233  *   promiseB = promiseA.then(function(result) {
16234  *     return result + 1;
16235  *   });
16237  *   // promiseB will be resolved immediately after promiseA is resolved and its value
16238  *   // will be the result of promiseA incremented by 1
16239  * ```
16241  * It is possible to create chains of any length and since a promise can be resolved with another
16242  * promise (which will defer its resolution further), it is possible to pause/defer resolution of
16243  * the promises at any point in the chain. This makes it possible to implement powerful APIs like
16244  * $http's response interceptors.
16247  * # Differences between Kris Kowal's Q and $q
16249  *  There are two main differences:
16251  * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
16252  *   mechanism in angular, which means faster propagation of resolution or rejection into your
16253  *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
16254  * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
16255  *   all the important functionality needed for common async tasks.
16257  * # Testing
16259  *  ```js
16260  *    it('should simulate promise', inject(function($q, $rootScope) {
16261  *      var deferred = $q.defer();
16262  *      var promise = deferred.promise;
16263  *      var resolvedValue;
16265  *      promise.then(function(value) { resolvedValue = value; });
16266  *      expect(resolvedValue).toBeUndefined();
16268  *      // Simulate resolving of promise
16269  *      deferred.resolve(123);
16270  *      // Note that the 'then' function does not get called synchronously.
16271  *      // This is because we want the promise API to always be async, whether or not
16272  *      // it got called synchronously or asynchronously.
16273  *      expect(resolvedValue).toBeUndefined();
16275  *      // Propagate promise resolution to 'then' functions using $apply().
16276  *      $rootScope.$apply();
16277  *      expect(resolvedValue).toEqual(123);
16278  *    }));
16279  *  ```
16281  * @param {function(function, function)} resolver Function which is responsible for resolving or
16282  *   rejecting the newly created promise. The first parameter is a function which resolves the
16283  *   promise, the second parameter is a function which rejects the promise.
16285  * @returns {Promise} The newly created promise.
16286  */
16287 function $QProvider() {
16289   this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
16290     return qFactory(function(callback) {
16291       $rootScope.$evalAsync(callback);
16292     }, $exceptionHandler);
16293   }];
16296 function $$QProvider() {
16297   this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
16298     return qFactory(function(callback) {
16299       $browser.defer(callback);
16300     }, $exceptionHandler);
16301   }];
16305  * Constructs a promise manager.
16307  * @param {function(function)} nextTick Function for executing functions in the next turn.
16308  * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
16309  *     debugging purposes.
16310  * @returns {object} Promise manager.
16311  */
16312 function qFactory(nextTick, exceptionHandler) {
16313   var $qMinErr = minErr('$q', TypeError);
16315   /**
16316    * @ngdoc method
16317    * @name ng.$q#defer
16318    * @kind function
16319    *
16320    * @description
16321    * Creates a `Deferred` object which represents a task which will finish in the future.
16322    *
16323    * @returns {Deferred} Returns a new instance of deferred.
16324    */
16325   var defer = function() {
16326     var d = new Deferred();
16327     //Necessary to support unbound execution :/
16328     d.resolve = simpleBind(d, d.resolve);
16329     d.reject = simpleBind(d, d.reject);
16330     d.notify = simpleBind(d, d.notify);
16331     return d;
16332   };
16334   function Promise() {
16335     this.$$state = { status: 0 };
16336   }
16338   extend(Promise.prototype, {
16339     then: function(onFulfilled, onRejected, progressBack) {
16340       if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
16341         return this;
16342       }
16343       var result = new Deferred();
16345       this.$$state.pending = this.$$state.pending || [];
16346       this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
16347       if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
16349       return result.promise;
16350     },
16352     "catch": function(callback) {
16353       return this.then(null, callback);
16354     },
16356     "finally": function(callback, progressBack) {
16357       return this.then(function(value) {
16358         return handleCallback(value, true, callback);
16359       }, function(error) {
16360         return handleCallback(error, false, callback);
16361       }, progressBack);
16362     }
16363   });
16365   //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
16366   function simpleBind(context, fn) {
16367     return function(value) {
16368       fn.call(context, value);
16369     };
16370   }
16372   function processQueue(state) {
16373     var fn, deferred, pending;
16375     pending = state.pending;
16376     state.processScheduled = false;
16377     state.pending = undefined;
16378     for (var i = 0, ii = pending.length; i < ii; ++i) {
16379       deferred = pending[i][0];
16380       fn = pending[i][state.status];
16381       try {
16382         if (isFunction(fn)) {
16383           deferred.resolve(fn(state.value));
16384         } else if (state.status === 1) {
16385           deferred.resolve(state.value);
16386         } else {
16387           deferred.reject(state.value);
16388         }
16389       } catch (e) {
16390         deferred.reject(e);
16391         exceptionHandler(e);
16392       }
16393     }
16394   }
16396   function scheduleProcessQueue(state) {
16397     if (state.processScheduled || !state.pending) return;
16398     state.processScheduled = true;
16399     nextTick(function() { processQueue(state); });
16400   }
16402   function Deferred() {
16403     this.promise = new Promise();
16404   }
16406   extend(Deferred.prototype, {
16407     resolve: function(val) {
16408       if (this.promise.$$state.status) return;
16409       if (val === this.promise) {
16410         this.$$reject($qMinErr(
16411           'qcycle',
16412           "Expected promise to be resolved with value other than itself '{0}'",
16413           val));
16414       } else {
16415         this.$$resolve(val);
16416       }
16418     },
16420     $$resolve: function(val) {
16421       var then;
16422       var that = this;
16423       var done = false;
16424       try {
16425         if ((isObject(val) || isFunction(val))) then = val && val.then;
16426         if (isFunction(then)) {
16427           this.promise.$$state.status = -1;
16428           then.call(val, resolvePromise, rejectPromise, simpleBind(this, this.notify));
16429         } else {
16430           this.promise.$$state.value = val;
16431           this.promise.$$state.status = 1;
16432           scheduleProcessQueue(this.promise.$$state);
16433         }
16434       } catch (e) {
16435         rejectPromise(e);
16436         exceptionHandler(e);
16437       }
16439       function resolvePromise(val) {
16440         if (done) return;
16441         done = true;
16442         that.$$resolve(val);
16443       }
16444       function rejectPromise(val) {
16445         if (done) return;
16446         done = true;
16447         that.$$reject(val);
16448       }
16449     },
16451     reject: function(reason) {
16452       if (this.promise.$$state.status) return;
16453       this.$$reject(reason);
16454     },
16456     $$reject: function(reason) {
16457       this.promise.$$state.value = reason;
16458       this.promise.$$state.status = 2;
16459       scheduleProcessQueue(this.promise.$$state);
16460     },
16462     notify: function(progress) {
16463       var callbacks = this.promise.$$state.pending;
16465       if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
16466         nextTick(function() {
16467           var callback, result;
16468           for (var i = 0, ii = callbacks.length; i < ii; i++) {
16469             result = callbacks[i][0];
16470             callback = callbacks[i][3];
16471             try {
16472               result.notify(isFunction(callback) ? callback(progress) : progress);
16473             } catch (e) {
16474               exceptionHandler(e);
16475             }
16476           }
16477         });
16478       }
16479     }
16480   });
16482   /**
16483    * @ngdoc method
16484    * @name $q#reject
16485    * @kind function
16486    *
16487    * @description
16488    * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
16489    * used to forward rejection in a chain of promises. If you are dealing with the last promise in
16490    * a promise chain, you don't need to worry about it.
16491    *
16492    * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
16493    * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
16494    * a promise error callback and you want to forward the error to the promise derived from the
16495    * current promise, you have to "rethrow" the error by returning a rejection constructed via
16496    * `reject`.
16497    *
16498    * ```js
16499    *   promiseB = promiseA.then(function(result) {
16500    *     // success: do something and resolve promiseB
16501    *     //          with the old or a new result
16502    *     return result;
16503    *   }, function(reason) {
16504    *     // error: handle the error if possible and
16505    *     //        resolve promiseB with newPromiseOrValue,
16506    *     //        otherwise forward the rejection to promiseB
16507    *     if (canHandle(reason)) {
16508    *      // handle the error and recover
16509    *      return newPromiseOrValue;
16510    *     }
16511    *     return $q.reject(reason);
16512    *   });
16513    * ```
16514    *
16515    * @param {*} reason Constant, message, exception or an object representing the rejection reason.
16516    * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
16517    */
16518   var reject = function(reason) {
16519     var result = new Deferred();
16520     result.reject(reason);
16521     return result.promise;
16522   };
16524   var makePromise = function makePromise(value, resolved) {
16525     var result = new Deferred();
16526     if (resolved) {
16527       result.resolve(value);
16528     } else {
16529       result.reject(value);
16530     }
16531     return result.promise;
16532   };
16534   var handleCallback = function handleCallback(value, isResolved, callback) {
16535     var callbackOutput = null;
16536     try {
16537       if (isFunction(callback)) callbackOutput = callback();
16538     } catch (e) {
16539       return makePromise(e, false);
16540     }
16541     if (isPromiseLike(callbackOutput)) {
16542       return callbackOutput.then(function() {
16543         return makePromise(value, isResolved);
16544       }, function(error) {
16545         return makePromise(error, false);
16546       });
16547     } else {
16548       return makePromise(value, isResolved);
16549     }
16550   };
16552   /**
16553    * @ngdoc method
16554    * @name $q#when
16555    * @kind function
16556    *
16557    * @description
16558    * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
16559    * This is useful when you are dealing with an object that might or might not be a promise, or if
16560    * the promise comes from a source that can't be trusted.
16561    *
16562    * @param {*} value Value or a promise
16563    * @param {Function=} successCallback
16564    * @param {Function=} errorCallback
16565    * @param {Function=} progressCallback
16566    * @returns {Promise} Returns a promise of the passed value or promise
16567    */
16570   var when = function(value, callback, errback, progressBack) {
16571     var result = new Deferred();
16572     result.resolve(value);
16573     return result.promise.then(callback, errback, progressBack);
16574   };
16576   /**
16577    * @ngdoc method
16578    * @name $q#resolve
16579    * @kind function
16580    *
16581    * @description
16582    * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
16583    *
16584    * @param {*} value Value or a promise
16585    * @param {Function=} successCallback
16586    * @param {Function=} errorCallback
16587    * @param {Function=} progressCallback
16588    * @returns {Promise} Returns a promise of the passed value or promise
16589    */
16590   var resolve = when;
16592   /**
16593    * @ngdoc method
16594    * @name $q#all
16595    * @kind function
16596    *
16597    * @description
16598    * Combines multiple promises into a single promise that is resolved when all of the input
16599    * promises are resolved.
16600    *
16601    * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
16602    * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
16603    *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
16604    *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
16605    *   with the same rejection value.
16606    */
16608   function all(promises) {
16609     var deferred = new Deferred(),
16610         counter = 0,
16611         results = isArray(promises) ? [] : {};
16613     forEach(promises, function(promise, key) {
16614       counter++;
16615       when(promise).then(function(value) {
16616         if (results.hasOwnProperty(key)) return;
16617         results[key] = value;
16618         if (!(--counter)) deferred.resolve(results);
16619       }, function(reason) {
16620         if (results.hasOwnProperty(key)) return;
16621         deferred.reject(reason);
16622       });
16623     });
16625     if (counter === 0) {
16626       deferred.resolve(results);
16627     }
16629     return deferred.promise;
16630   }
16632   /**
16633    * @ngdoc method
16634    * @name $q#race
16635    * @kind function
16636    *
16637    * @description
16638    * Returns a promise that resolves or rejects as soon as one of those promises
16639    * resolves or rejects, with the value or reason from that promise.
16640    *
16641    * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
16642    * @returns {Promise} a promise that resolves or rejects as soon as one of the `promises`
16643    * resolves or rejects, with the value or reason from that promise.
16644    */
16646   function race(promises) {
16647     var deferred = defer();
16649     forEach(promises, function(promise) {
16650       when(promise).then(deferred.resolve, deferred.reject);
16651     });
16653     return deferred.promise;
16654   }
16656   var $Q = function Q(resolver) {
16657     if (!isFunction(resolver)) {
16658       throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
16659     }
16661     var deferred = new Deferred();
16663     function resolveFn(value) {
16664       deferred.resolve(value);
16665     }
16667     function rejectFn(reason) {
16668       deferred.reject(reason);
16669     }
16671     resolver(resolveFn, rejectFn);
16673     return deferred.promise;
16674   };
16676   // Let's make the instanceof operator work for promises, so that
16677   // `new $q(fn) instanceof $q` would evaluate to true.
16678   $Q.prototype = Promise.prototype;
16680   $Q.defer = defer;
16681   $Q.reject = reject;
16682   $Q.when = when;
16683   $Q.resolve = resolve;
16684   $Q.all = all;
16685   $Q.race = race;
16687   return $Q;
16690 function $$RAFProvider() { //rAF
16691   this.$get = ['$window', '$timeout', function($window, $timeout) {
16692     var requestAnimationFrame = $window.requestAnimationFrame ||
16693                                 $window.webkitRequestAnimationFrame;
16695     var cancelAnimationFrame = $window.cancelAnimationFrame ||
16696                                $window.webkitCancelAnimationFrame ||
16697                                $window.webkitCancelRequestAnimationFrame;
16699     var rafSupported = !!requestAnimationFrame;
16700     var raf = rafSupported
16701       ? function(fn) {
16702           var id = requestAnimationFrame(fn);
16703           return function() {
16704             cancelAnimationFrame(id);
16705           };
16706         }
16707       : function(fn) {
16708           var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
16709           return function() {
16710             $timeout.cancel(timer);
16711           };
16712         };
16714     raf.supported = rafSupported;
16716     return raf;
16717   }];
16721  * DESIGN NOTES
16723  * The design decisions behind the scope are heavily favored for speed and memory consumption.
16725  * The typical use of scope is to watch the expressions, which most of the time return the same
16726  * value as last time so we optimize the operation.
16728  * Closures construction is expensive in terms of speed as well as memory:
16729  *   - No closures, instead use prototypical inheritance for API
16730  *   - Internal state needs to be stored on scope directly, which means that private state is
16731  *     exposed as $$____ properties
16733  * Loop operations are optimized by using while(count--) { ... }
16734  *   - This means that in order to keep the same order of execution as addition we have to add
16735  *     items to the array at the beginning (unshift) instead of at the end (push)
16737  * Child scopes are created and removed often
16738  *   - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
16740  * There are fewer watches than observers. This is why you don't want the observer to be implemented
16741  * in the same way as watch. Watch requires return of the initialization function which is expensive
16742  * to construct.
16743  */
16747  * @ngdoc provider
16748  * @name $rootScopeProvider
16749  * @description
16751  * Provider for the $rootScope service.
16752  */
16755  * @ngdoc method
16756  * @name $rootScopeProvider#digestTtl
16757  * @description
16759  * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
16760  * assuming that the model is unstable.
16762  * The current default is 10 iterations.
16764  * In complex applications it's possible that the dependencies between `$watch`s will result in
16765  * several digest iterations. However if an application needs more than the default 10 digest
16766  * iterations for its model to stabilize then you should investigate what is causing the model to
16767  * continuously change during the digest.
16769  * Increasing the TTL could have performance implications, so you should not change it without
16770  * proper justification.
16772  * @param {number} limit The number of digest iterations.
16773  */
16777  * @ngdoc service
16778  * @name $rootScope
16779  * @description
16781  * Every application has a single root {@link ng.$rootScope.Scope scope}.
16782  * All other scopes are descendant scopes of the root scope. Scopes provide separation
16783  * between the model and the view, via a mechanism for watching the model for changes.
16784  * They also provide event emission/broadcast and subscription facility. See the
16785  * {@link guide/scope developer guide on scopes}.
16786  */
16787 function $RootScopeProvider() {
16788   var TTL = 10;
16789   var $rootScopeMinErr = minErr('$rootScope');
16790   var lastDirtyWatch = null;
16791   var applyAsyncId = null;
16793   this.digestTtl = function(value) {
16794     if (arguments.length) {
16795       TTL = value;
16796     }
16797     return TTL;
16798   };
16800   function createChildScopeClass(parent) {
16801     function ChildScope() {
16802       this.$$watchers = this.$$nextSibling =
16803           this.$$childHead = this.$$childTail = null;
16804       this.$$listeners = {};
16805       this.$$listenerCount = {};
16806       this.$$watchersCount = 0;
16807       this.$id = nextUid();
16808       this.$$ChildScope = null;
16809     }
16810     ChildScope.prototype = parent;
16811     return ChildScope;
16812   }
16814   this.$get = ['$exceptionHandler', '$parse', '$browser',
16815       function($exceptionHandler, $parse, $browser) {
16817     function destroyChildScope($event) {
16818         $event.currentScope.$$destroyed = true;
16819     }
16821     function cleanUpScope($scope) {
16823       if (msie === 9) {
16824         // There is a memory leak in IE9 if all child scopes are not disconnected
16825         // completely when a scope is destroyed. So this code will recurse up through
16826         // all this scopes children
16827         //
16828         // See issue https://github.com/angular/angular.js/issues/10706
16829         $scope.$$childHead && cleanUpScope($scope.$$childHead);
16830         $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
16831       }
16833       // The code below works around IE9 and V8's memory leaks
16834       //
16835       // See:
16836       // - https://code.google.com/p/v8/issues/detail?id=2073#c26
16837       // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
16838       // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
16840       $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
16841           $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
16842     }
16844     /**
16845      * @ngdoc type
16846      * @name $rootScope.Scope
16847      *
16848      * @description
16849      * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
16850      * {@link auto.$injector $injector}. Child scopes are created using the
16851      * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
16852      * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
16853      * an in-depth introduction and usage examples.
16854      *
16855      *
16856      * # Inheritance
16857      * A scope can inherit from a parent scope, as in this example:
16858      * ```js
16859          var parent = $rootScope;
16860          var child = parent.$new();
16862          parent.salutation = "Hello";
16863          expect(child.salutation).toEqual('Hello');
16865          child.salutation = "Welcome";
16866          expect(child.salutation).toEqual('Welcome');
16867          expect(parent.salutation).toEqual('Hello');
16868      * ```
16869      *
16870      * When interacting with `Scope` in tests, additional helper methods are available on the
16871      * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
16872      * details.
16873      *
16874      *
16875      * @param {Object.<string, function()>=} providers Map of service factory which need to be
16876      *                                       provided for the current scope. Defaults to {@link ng}.
16877      * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
16878      *                              append/override services provided by `providers`. This is handy
16879      *                              when unit-testing and having the need to override a default
16880      *                              service.
16881      * @returns {Object} Newly created scope.
16882      *
16883      */
16884     function Scope() {
16885       this.$id = nextUid();
16886       this.$$phase = this.$parent = this.$$watchers =
16887                      this.$$nextSibling = this.$$prevSibling =
16888                      this.$$childHead = this.$$childTail = null;
16889       this.$root = this;
16890       this.$$destroyed = false;
16891       this.$$listeners = {};
16892       this.$$listenerCount = {};
16893       this.$$watchersCount = 0;
16894       this.$$isolateBindings = null;
16895     }
16897     /**
16898      * @ngdoc property
16899      * @name $rootScope.Scope#$id
16900      *
16901      * @description
16902      * Unique scope ID (monotonically increasing) useful for debugging.
16903      */
16905      /**
16906       * @ngdoc property
16907       * @name $rootScope.Scope#$parent
16908       *
16909       * @description
16910       * Reference to the parent scope.
16911       */
16913       /**
16914        * @ngdoc property
16915        * @name $rootScope.Scope#$root
16916        *
16917        * @description
16918        * Reference to the root scope.
16919        */
16921     Scope.prototype = {
16922       constructor: Scope,
16923       /**
16924        * @ngdoc method
16925        * @name $rootScope.Scope#$new
16926        * @kind function
16927        *
16928        * @description
16929        * Creates a new child {@link ng.$rootScope.Scope scope}.
16930        *
16931        * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
16932        * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
16933        *
16934        * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
16935        * desired for the scope and its child scopes to be permanently detached from the parent and
16936        * thus stop participating in model change detection and listener notification by invoking.
16937        *
16938        * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
16939        *         parent scope. The scope is isolated, as it can not see parent scope properties.
16940        *         When creating widgets, it is useful for the widget to not accidentally read parent
16941        *         state.
16942        *
16943        * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
16944        *                              of the newly created scope. Defaults to `this` scope if not provided.
16945        *                              This is used when creating a transclude scope to correctly place it
16946        *                              in the scope hierarchy while maintaining the correct prototypical
16947        *                              inheritance.
16948        *
16949        * @returns {Object} The newly created child scope.
16950        *
16951        */
16952       $new: function(isolate, parent) {
16953         var child;
16955         parent = parent || this;
16957         if (isolate) {
16958           child = new Scope();
16959           child.$root = this.$root;
16960         } else {
16961           // Only create a child scope class if somebody asks for one,
16962           // but cache it to allow the VM to optimize lookups.
16963           if (!this.$$ChildScope) {
16964             this.$$ChildScope = createChildScopeClass(this);
16965           }
16966           child = new this.$$ChildScope();
16967         }
16968         child.$parent = parent;
16969         child.$$prevSibling = parent.$$childTail;
16970         if (parent.$$childHead) {
16971           parent.$$childTail.$$nextSibling = child;
16972           parent.$$childTail = child;
16973         } else {
16974           parent.$$childHead = parent.$$childTail = child;
16975         }
16977         // When the new scope is not isolated or we inherit from `this`, and
16978         // the parent scope is destroyed, the property `$$destroyed` is inherited
16979         // prototypically. In all other cases, this property needs to be set
16980         // when the parent scope is destroyed.
16981         // The listener needs to be added after the parent is set
16982         if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
16984         return child;
16985       },
16987       /**
16988        * @ngdoc method
16989        * @name $rootScope.Scope#$watch
16990        * @kind function
16991        *
16992        * @description
16993        * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
16994        *
16995        * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
16996        *   $digest()} and should return the value that will be watched. (`watchExpression` should not change
16997        *   its value when executed multiple times with the same input because it may be executed multiple
16998        *   times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
16999        *   [idempotent](http://en.wikipedia.org/wiki/Idempotence).
17000        * - The `listener` is called only when the value from the current `watchExpression` and the
17001        *   previous call to `watchExpression` are not equal (with the exception of the initial run,
17002        *   see below). Inequality is determined according to reference inequality,
17003        *   [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
17004        *    via the `!==` Javascript operator, unless `objectEquality == true`
17005        *   (see next point)
17006        * - When `objectEquality == true`, inequality of the `watchExpression` is determined
17007        *   according to the {@link angular.equals} function. To save the value of the object for
17008        *   later comparison, the {@link angular.copy} function is used. This therefore means that
17009        *   watching complex objects will have adverse memory and performance implications.
17010        * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
17011        *   This is achieved by rerunning the watchers until no changes are detected. The rerun
17012        *   iteration limit is 10 to prevent an infinite loop deadlock.
17013        *
17014        *
17015        * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
17016        * you can register a `watchExpression` function with no `listener`. (Be prepared for
17017        * multiple calls to your `watchExpression` because it will execute multiple times in a
17018        * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
17019        *
17020        * After a watcher is registered with the scope, the `listener` fn is called asynchronously
17021        * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
17022        * watcher. In rare cases, this is undesirable because the listener is called when the result
17023        * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
17024        * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
17025        * listener was called due to initialization.
17026        *
17027        *
17028        *
17029        * # Example
17030        * ```js
17031            // let's assume that scope was dependency injected as the $rootScope
17032            var scope = $rootScope;
17033            scope.name = 'misko';
17034            scope.counter = 0;
17036            expect(scope.counter).toEqual(0);
17037            scope.$watch('name', function(newValue, oldValue) {
17038              scope.counter = scope.counter + 1;
17039            });
17040            expect(scope.counter).toEqual(0);
17042            scope.$digest();
17043            // the listener is always called during the first $digest loop after it was registered
17044            expect(scope.counter).toEqual(1);
17046            scope.$digest();
17047            // but now it will not be called unless the value changes
17048            expect(scope.counter).toEqual(1);
17050            scope.name = 'adam';
17051            scope.$digest();
17052            expect(scope.counter).toEqual(2);
17056            // Using a function as a watchExpression
17057            var food;
17058            scope.foodCounter = 0;
17059            expect(scope.foodCounter).toEqual(0);
17060            scope.$watch(
17061              // This function returns the value being watched. It is called for each turn of the $digest loop
17062              function() { return food; },
17063              // This is the change listener, called when the value returned from the above function changes
17064              function(newValue, oldValue) {
17065                if ( newValue !== oldValue ) {
17066                  // Only increment the counter if the value changed
17067                  scope.foodCounter = scope.foodCounter + 1;
17068                }
17069              }
17070            );
17071            // No digest has been run so the counter will be zero
17072            expect(scope.foodCounter).toEqual(0);
17074            // Run the digest but since food has not changed count will still be zero
17075            scope.$digest();
17076            expect(scope.foodCounter).toEqual(0);
17078            // Update food and run digest.  Now the counter will increment
17079            food = 'cheeseburger';
17080            scope.$digest();
17081            expect(scope.foodCounter).toEqual(1);
17083        * ```
17084        *
17085        *
17086        *
17087        * @param {(function()|string)} watchExpression Expression that is evaluated on each
17088        *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
17089        *    a call to the `listener`.
17090        *
17091        *    - `string`: Evaluated as {@link guide/expression expression}
17092        *    - `function(scope)`: called with current `scope` as a parameter.
17093        * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
17094        *    of `watchExpression` changes.
17095        *
17096        *    - `newVal` contains the current value of the `watchExpression`
17097        *    - `oldVal` contains the previous value of the `watchExpression`
17098        *    - `scope` refers to the current scope
17099        * @param {boolean=} [objectEquality=false] Compare for object equality using {@link angular.equals} instead of
17100        *     comparing for reference equality.
17101        * @returns {function()} Returns a deregistration function for this listener.
17102        */
17103       $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
17104         var get = $parse(watchExp);
17106         if (get.$$watchDelegate) {
17107           return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
17108         }
17109         var scope = this,
17110             array = scope.$$watchers,
17111             watcher = {
17112               fn: listener,
17113               last: initWatchVal,
17114               get: get,
17115               exp: prettyPrintExpression || watchExp,
17116               eq: !!objectEquality
17117             };
17119         lastDirtyWatch = null;
17121         if (!isFunction(listener)) {
17122           watcher.fn = noop;
17123         }
17125         if (!array) {
17126           array = scope.$$watchers = [];
17127         }
17128         // we use unshift since we use a while loop in $digest for speed.
17129         // the while loop reads in reverse order.
17130         array.unshift(watcher);
17131         incrementWatchersCount(this, 1);
17133         return function deregisterWatch() {
17134           if (arrayRemove(array, watcher) >= 0) {
17135             incrementWatchersCount(scope, -1);
17136           }
17137           lastDirtyWatch = null;
17138         };
17139       },
17141       /**
17142        * @ngdoc method
17143        * @name $rootScope.Scope#$watchGroup
17144        * @kind function
17145        *
17146        * @description
17147        * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
17148        * If any one expression in the collection changes the `listener` is executed.
17149        *
17150        * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
17151        *   call to $digest() to see if any items changes.
17152        * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
17153        *
17154        * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
17155        * watched using {@link ng.$rootScope.Scope#$watch $watch()}
17156        *
17157        * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
17158        *    expression in `watchExpressions` changes
17159        *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
17160        *    those of `watchExpression`
17161        *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
17162        *    those of `watchExpression`
17163        *    The `scope` refers to the current scope.
17164        * @returns {function()} Returns a de-registration function for all listeners.
17165        */
17166       $watchGroup: function(watchExpressions, listener) {
17167         var oldValues = new Array(watchExpressions.length);
17168         var newValues = new Array(watchExpressions.length);
17169         var deregisterFns = [];
17170         var self = this;
17171         var changeReactionScheduled = false;
17172         var firstRun = true;
17174         if (!watchExpressions.length) {
17175           // No expressions means we call the listener ASAP
17176           var shouldCall = true;
17177           self.$evalAsync(function() {
17178             if (shouldCall) listener(newValues, newValues, self);
17179           });
17180           return function deregisterWatchGroup() {
17181             shouldCall = false;
17182           };
17183         }
17185         if (watchExpressions.length === 1) {
17186           // Special case size of one
17187           return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
17188             newValues[0] = value;
17189             oldValues[0] = oldValue;
17190             listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
17191           });
17192         }
17194         forEach(watchExpressions, function(expr, i) {
17195           var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
17196             newValues[i] = value;
17197             oldValues[i] = oldValue;
17198             if (!changeReactionScheduled) {
17199               changeReactionScheduled = true;
17200               self.$evalAsync(watchGroupAction);
17201             }
17202           });
17203           deregisterFns.push(unwatchFn);
17204         });
17206         function watchGroupAction() {
17207           changeReactionScheduled = false;
17209           if (firstRun) {
17210             firstRun = false;
17211             listener(newValues, newValues, self);
17212           } else {
17213             listener(newValues, oldValues, self);
17214           }
17215         }
17217         return function deregisterWatchGroup() {
17218           while (deregisterFns.length) {
17219             deregisterFns.shift()();
17220           }
17221         };
17222       },
17225       /**
17226        * @ngdoc method
17227        * @name $rootScope.Scope#$watchCollection
17228        * @kind function
17229        *
17230        * @description
17231        * Shallow watches the properties of an object and fires whenever any of the properties change
17232        * (for arrays, this implies watching the array items; for object maps, this implies watching
17233        * the properties). If a change is detected, the `listener` callback is fired.
17234        *
17235        * - The `obj` collection is observed via standard $watch operation and is examined on every
17236        *   call to $digest() to see if any items have been added, removed, or moved.
17237        * - The `listener` is called whenever anything within the `obj` has changed. Examples include
17238        *   adding, removing, and moving items belonging to an object or array.
17239        *
17240        *
17241        * # Example
17242        * ```js
17243           $scope.names = ['igor', 'matias', 'misko', 'james'];
17244           $scope.dataCount = 4;
17246           $scope.$watchCollection('names', function(newNames, oldNames) {
17247             $scope.dataCount = newNames.length;
17248           });
17250           expect($scope.dataCount).toEqual(4);
17251           $scope.$digest();
17253           //still at 4 ... no changes
17254           expect($scope.dataCount).toEqual(4);
17256           $scope.names.pop();
17257           $scope.$digest();
17259           //now there's been a change
17260           expect($scope.dataCount).toEqual(3);
17261        * ```
17262        *
17263        *
17264        * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
17265        *    expression value should evaluate to an object or an array which is observed on each
17266        *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
17267        *    collection will trigger a call to the `listener`.
17268        *
17269        * @param {function(newCollection, oldCollection, scope)} listener a callback function called
17270        *    when a change is detected.
17271        *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
17272        *    - The `oldCollection` object is a copy of the former collection data.
17273        *      Due to performance considerations, the`oldCollection` value is computed only if the
17274        *      `listener` function declares two or more arguments.
17275        *    - The `scope` argument refers to the current scope.
17276        *
17277        * @returns {function()} Returns a de-registration function for this listener. When the
17278        *    de-registration function is executed, the internal watch operation is terminated.
17279        */
17280       $watchCollection: function(obj, listener) {
17281         $watchCollectionInterceptor.$stateful = true;
17283         var self = this;
17284         // the current value, updated on each dirty-check run
17285         var newValue;
17286         // a shallow copy of the newValue from the last dirty-check run,
17287         // updated to match newValue during dirty-check run
17288         var oldValue;
17289         // a shallow copy of the newValue from when the last change happened
17290         var veryOldValue;
17291         // only track veryOldValue if the listener is asking for it
17292         var trackVeryOldValue = (listener.length > 1);
17293         var changeDetected = 0;
17294         var changeDetector = $parse(obj, $watchCollectionInterceptor);
17295         var internalArray = [];
17296         var internalObject = {};
17297         var initRun = true;
17298         var oldLength = 0;
17300         function $watchCollectionInterceptor(_value) {
17301           newValue = _value;
17302           var newLength, key, bothNaN, newItem, oldItem;
17304           // If the new value is undefined, then return undefined as the watch may be a one-time watch
17305           if (isUndefined(newValue)) return;
17307           if (!isObject(newValue)) { // if primitive
17308             if (oldValue !== newValue) {
17309               oldValue = newValue;
17310               changeDetected++;
17311             }
17312           } else if (isArrayLike(newValue)) {
17313             if (oldValue !== internalArray) {
17314               // we are transitioning from something which was not an array into array.
17315               oldValue = internalArray;
17316               oldLength = oldValue.length = 0;
17317               changeDetected++;
17318             }
17320             newLength = newValue.length;
17322             if (oldLength !== newLength) {
17323               // if lengths do not match we need to trigger change notification
17324               changeDetected++;
17325               oldValue.length = oldLength = newLength;
17326             }
17327             // copy the items to oldValue and look for changes.
17328             for (var i = 0; i < newLength; i++) {
17329               oldItem = oldValue[i];
17330               newItem = newValue[i];
17332               bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
17333               if (!bothNaN && (oldItem !== newItem)) {
17334                 changeDetected++;
17335                 oldValue[i] = newItem;
17336               }
17337             }
17338           } else {
17339             if (oldValue !== internalObject) {
17340               // we are transitioning from something which was not an object into object.
17341               oldValue = internalObject = {};
17342               oldLength = 0;
17343               changeDetected++;
17344             }
17345             // copy the items to oldValue and look for changes.
17346             newLength = 0;
17347             for (key in newValue) {
17348               if (hasOwnProperty.call(newValue, key)) {
17349                 newLength++;
17350                 newItem = newValue[key];
17351                 oldItem = oldValue[key];
17353                 if (key in oldValue) {
17354                   bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
17355                   if (!bothNaN && (oldItem !== newItem)) {
17356                     changeDetected++;
17357                     oldValue[key] = newItem;
17358                   }
17359                 } else {
17360                   oldLength++;
17361                   oldValue[key] = newItem;
17362                   changeDetected++;
17363                 }
17364               }
17365             }
17366             if (oldLength > newLength) {
17367               // we used to have more keys, need to find them and destroy them.
17368               changeDetected++;
17369               for (key in oldValue) {
17370                 if (!hasOwnProperty.call(newValue, key)) {
17371                   oldLength--;
17372                   delete oldValue[key];
17373                 }
17374               }
17375             }
17376           }
17377           return changeDetected;
17378         }
17380         function $watchCollectionAction() {
17381           if (initRun) {
17382             initRun = false;
17383             listener(newValue, newValue, self);
17384           } else {
17385             listener(newValue, veryOldValue, self);
17386           }
17388           // make a copy for the next time a collection is changed
17389           if (trackVeryOldValue) {
17390             if (!isObject(newValue)) {
17391               //primitive
17392               veryOldValue = newValue;
17393             } else if (isArrayLike(newValue)) {
17394               veryOldValue = new Array(newValue.length);
17395               for (var i = 0; i < newValue.length; i++) {
17396                 veryOldValue[i] = newValue[i];
17397               }
17398             } else { // if object
17399               veryOldValue = {};
17400               for (var key in newValue) {
17401                 if (hasOwnProperty.call(newValue, key)) {
17402                   veryOldValue[key] = newValue[key];
17403                 }
17404               }
17405             }
17406           }
17407         }
17409         return this.$watch(changeDetector, $watchCollectionAction);
17410       },
17412       /**
17413        * @ngdoc method
17414        * @name $rootScope.Scope#$digest
17415        * @kind function
17416        *
17417        * @description
17418        * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
17419        * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
17420        * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
17421        * until no more listeners are firing. This means that it is possible to get into an infinite
17422        * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
17423        * iterations exceeds 10.
17424        *
17425        * Usually, you don't call `$digest()` directly in
17426        * {@link ng.directive:ngController controllers} or in
17427        * {@link ng.$compileProvider#directive directives}.
17428        * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
17429        * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
17430        *
17431        * If you want to be notified whenever `$digest()` is called,
17432        * you can register a `watchExpression` function with
17433        * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
17434        *
17435        * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
17436        *
17437        * # Example
17438        * ```js
17439            var scope = ...;
17440            scope.name = 'misko';
17441            scope.counter = 0;
17443            expect(scope.counter).toEqual(0);
17444            scope.$watch('name', function(newValue, oldValue) {
17445              scope.counter = scope.counter + 1;
17446            });
17447            expect(scope.counter).toEqual(0);
17449            scope.$digest();
17450            // the listener is always called during the first $digest loop after it was registered
17451            expect(scope.counter).toEqual(1);
17453            scope.$digest();
17454            // but now it will not be called unless the value changes
17455            expect(scope.counter).toEqual(1);
17457            scope.name = 'adam';
17458            scope.$digest();
17459            expect(scope.counter).toEqual(2);
17460        * ```
17461        *
17462        */
17463       $digest: function() {
17464         var watch, value, last, fn, get,
17465             watchers,
17466             length,
17467             dirty, ttl = TTL,
17468             next, current, target = this,
17469             watchLog = [],
17470             logIdx, asyncTask;
17472         beginPhase('$digest');
17473         // Check for changes to browser url that happened in sync before the call to $digest
17474         $browser.$$checkUrlChange();
17476         if (this === $rootScope && applyAsyncId !== null) {
17477           // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
17478           // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
17479           $browser.defer.cancel(applyAsyncId);
17480           flushApplyAsync();
17481         }
17483         lastDirtyWatch = null;
17485         do { // "while dirty" loop
17486           dirty = false;
17487           current = target;
17489           // It's safe for asyncQueuePosition to be a local variable here because this loop can't
17490           // be reentered recursively. Calling $digest from a function passed to $applyAsync would
17491           // lead to a '$digest already in progress' error.
17492           for (var asyncQueuePosition = 0; asyncQueuePosition < asyncQueue.length; asyncQueuePosition++) {
17493             try {
17494               asyncTask = asyncQueue[asyncQueuePosition];
17495               asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
17496             } catch (e) {
17497               $exceptionHandler(e);
17498             }
17499             lastDirtyWatch = null;
17500           }
17501           asyncQueue.length = 0;
17503           traverseScopesLoop:
17504           do { // "traverse the scopes" loop
17505             if ((watchers = current.$$watchers)) {
17506               // process our watches
17507               length = watchers.length;
17508               while (length--) {
17509                 try {
17510                   watch = watchers[length];
17511                   // Most common watches are on primitives, in which case we can short
17512                   // circuit it with === operator, only when === fails do we use .equals
17513                   if (watch) {
17514                     get = watch.get;
17515                     if ((value = get(current)) !== (last = watch.last) &&
17516                         !(watch.eq
17517                             ? equals(value, last)
17518                             : (typeof value === 'number' && typeof last === 'number'
17519                                && isNaN(value) && isNaN(last)))) {
17520                       dirty = true;
17521                       lastDirtyWatch = watch;
17522                       watch.last = watch.eq ? copy(value, null) : value;
17523                       fn = watch.fn;
17524                       fn(value, ((last === initWatchVal) ? value : last), current);
17525                       if (ttl < 5) {
17526                         logIdx = 4 - ttl;
17527                         if (!watchLog[logIdx]) watchLog[logIdx] = [];
17528                         watchLog[logIdx].push({
17529                           msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
17530                           newVal: value,
17531                           oldVal: last
17532                         });
17533                       }
17534                     } else if (watch === lastDirtyWatch) {
17535                       // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
17536                       // have already been tested.
17537                       dirty = false;
17538                       break traverseScopesLoop;
17539                     }
17540                   }
17541                 } catch (e) {
17542                   $exceptionHandler(e);
17543                 }
17544               }
17545             }
17547             // Insanity Warning: scope depth-first traversal
17548             // yes, this code is a bit crazy, but it works and we have tests to prove it!
17549             // this piece should be kept in sync with the traversal in $broadcast
17550             if (!(next = ((current.$$watchersCount && current.$$childHead) ||
17551                 (current !== target && current.$$nextSibling)))) {
17552               while (current !== target && !(next = current.$$nextSibling)) {
17553                 current = current.$parent;
17554               }
17555             }
17556           } while ((current = next));
17558           // `break traverseScopesLoop;` takes us to here
17560           if ((dirty || asyncQueue.length) && !(ttl--)) {
17561             clearPhase();
17562             throw $rootScopeMinErr('infdig',
17563                 '{0} $digest() iterations reached. Aborting!\n' +
17564                 'Watchers fired in the last 5 iterations: {1}',
17565                 TTL, watchLog);
17566           }
17568         } while (dirty || asyncQueue.length);
17570         clearPhase();
17572         // postDigestQueuePosition isn't local here because this loop can be reentered recursively.
17573         while (postDigestQueuePosition < postDigestQueue.length) {
17574           try {
17575             postDigestQueue[postDigestQueuePosition++]();
17576           } catch (e) {
17577             $exceptionHandler(e);
17578           }
17579         }
17580         postDigestQueue.length = postDigestQueuePosition = 0;
17581       },
17584       /**
17585        * @ngdoc event
17586        * @name $rootScope.Scope#$destroy
17587        * @eventType broadcast on scope being destroyed
17588        *
17589        * @description
17590        * Broadcasted when a scope and its children are being destroyed.
17591        *
17592        * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
17593        * clean up DOM bindings before an element is removed from the DOM.
17594        */
17596       /**
17597        * @ngdoc method
17598        * @name $rootScope.Scope#$destroy
17599        * @kind function
17600        *
17601        * @description
17602        * Removes the current scope (and all of its children) from the parent scope. Removal implies
17603        * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
17604        * propagate to the current scope and its children. Removal also implies that the current
17605        * scope is eligible for garbage collection.
17606        *
17607        * The `$destroy()` is usually used by directives such as
17608        * {@link ng.directive:ngRepeat ngRepeat} for managing the
17609        * unrolling of the loop.
17610        *
17611        * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
17612        * Application code can register a `$destroy` event handler that will give it a chance to
17613        * perform any necessary cleanup.
17614        *
17615        * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
17616        * clean up DOM bindings before an element is removed from the DOM.
17617        */
17618       $destroy: function() {
17619         // We can't destroy a scope that has been already destroyed.
17620         if (this.$$destroyed) return;
17621         var parent = this.$parent;
17623         this.$broadcast('$destroy');
17624         this.$$destroyed = true;
17626         if (this === $rootScope) {
17627           //Remove handlers attached to window when $rootScope is removed
17628           $browser.$$applicationDestroyed();
17629         }
17631         incrementWatchersCount(this, -this.$$watchersCount);
17632         for (var eventName in this.$$listenerCount) {
17633           decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
17634         }
17636         // sever all the references to parent scopes (after this cleanup, the current scope should
17637         // not be retained by any of our references and should be eligible for garbage collection)
17638         if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
17639         if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
17640         if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
17641         if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
17643         // Disable listeners, watchers and apply/digest methods
17644         this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
17645         this.$on = this.$watch = this.$watchGroup = function() { return noop; };
17646         this.$$listeners = {};
17648         // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
17649         this.$$nextSibling = null;
17650         cleanUpScope(this);
17651       },
17653       /**
17654        * @ngdoc method
17655        * @name $rootScope.Scope#$eval
17656        * @kind function
17657        *
17658        * @description
17659        * Executes the `expression` on the current scope and returns the result. Any exceptions in
17660        * the expression are propagated (uncaught). This is useful when evaluating Angular
17661        * expressions.
17662        *
17663        * # Example
17664        * ```js
17665            var scope = ng.$rootScope.Scope();
17666            scope.a = 1;
17667            scope.b = 2;
17669            expect(scope.$eval('a+b')).toEqual(3);
17670            expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
17671        * ```
17672        *
17673        * @param {(string|function())=} expression An angular expression to be executed.
17674        *
17675        *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
17676        *    - `function(scope)`: execute the function with the current `scope` parameter.
17677        *
17678        * @param {(object)=} locals Local variables object, useful for overriding values in scope.
17679        * @returns {*} The result of evaluating the expression.
17680        */
17681       $eval: function(expr, locals) {
17682         return $parse(expr)(this, locals);
17683       },
17685       /**
17686        * @ngdoc method
17687        * @name $rootScope.Scope#$evalAsync
17688        * @kind function
17689        *
17690        * @description
17691        * Executes the expression on the current scope at a later point in time.
17692        *
17693        * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
17694        * that:
17695        *
17696        *   - it will execute after the function that scheduled the evaluation (preferably before DOM
17697        *     rendering).
17698        *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
17699        *     `expression` execution.
17700        *
17701        * Any exceptions from the execution of the expression are forwarded to the
17702        * {@link ng.$exceptionHandler $exceptionHandler} service.
17703        *
17704        * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
17705        * will be scheduled. However, it is encouraged to always call code that changes the model
17706        * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
17707        *
17708        * @param {(string|function())=} expression An angular expression to be executed.
17709        *
17710        *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
17711        *    - `function(scope)`: execute the function with the current `scope` parameter.
17712        *
17713        * @param {(object)=} locals Local variables object, useful for overriding values in scope.
17714        */
17715       $evalAsync: function(expr, locals) {
17716         // if we are outside of an $digest loop and this is the first time we are scheduling async
17717         // task also schedule async auto-flush
17718         if (!$rootScope.$$phase && !asyncQueue.length) {
17719           $browser.defer(function() {
17720             if (asyncQueue.length) {
17721               $rootScope.$digest();
17722             }
17723           });
17724         }
17726         asyncQueue.push({scope: this, expression: $parse(expr), locals: locals});
17727       },
17729       $$postDigest: function(fn) {
17730         postDigestQueue.push(fn);
17731       },
17733       /**
17734        * @ngdoc method
17735        * @name $rootScope.Scope#$apply
17736        * @kind function
17737        *
17738        * @description
17739        * `$apply()` is used to execute an expression in angular from outside of the angular
17740        * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
17741        * Because we are calling into the angular framework we need to perform proper scope life
17742        * cycle of {@link ng.$exceptionHandler exception handling},
17743        * {@link ng.$rootScope.Scope#$digest executing watches}.
17744        *
17745        * ## Life cycle
17746        *
17747        * # Pseudo-Code of `$apply()`
17748        * ```js
17749            function $apply(expr) {
17750              try {
17751                return $eval(expr);
17752              } catch (e) {
17753                $exceptionHandler(e);
17754              } finally {
17755                $root.$digest();
17756              }
17757            }
17758        * ```
17759        *
17760        *
17761        * Scope's `$apply()` method transitions through the following stages:
17762        *
17763        * 1. The {@link guide/expression expression} is executed using the
17764        *    {@link ng.$rootScope.Scope#$eval $eval()} method.
17765        * 2. Any exceptions from the execution of the expression are forwarded to the
17766        *    {@link ng.$exceptionHandler $exceptionHandler} service.
17767        * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
17768        *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
17769        *
17770        *
17771        * @param {(string|function())=} exp An angular expression to be executed.
17772        *
17773        *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
17774        *    - `function(scope)`: execute the function with current `scope` parameter.
17775        *
17776        * @returns {*} The result of evaluating the expression.
17777        */
17778       $apply: function(expr) {
17779         try {
17780           beginPhase('$apply');
17781           try {
17782             return this.$eval(expr);
17783           } finally {
17784             clearPhase();
17785           }
17786         } catch (e) {
17787           $exceptionHandler(e);
17788         } finally {
17789           try {
17790             $rootScope.$digest();
17791           } catch (e) {
17792             $exceptionHandler(e);
17793             throw e;
17794           }
17795         }
17796       },
17798       /**
17799        * @ngdoc method
17800        * @name $rootScope.Scope#$applyAsync
17801        * @kind function
17802        *
17803        * @description
17804        * Schedule the invocation of $apply to occur at a later time. The actual time difference
17805        * varies across browsers, but is typically around ~10 milliseconds.
17806        *
17807        * This can be used to queue up multiple expressions which need to be evaluated in the same
17808        * digest.
17809        *
17810        * @param {(string|function())=} exp An angular expression to be executed.
17811        *
17812        *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
17813        *    - `function(scope)`: execute the function with current `scope` parameter.
17814        */
17815       $applyAsync: function(expr) {
17816         var scope = this;
17817         expr && applyAsyncQueue.push($applyAsyncExpression);
17818         expr = $parse(expr);
17819         scheduleApplyAsync();
17821         function $applyAsyncExpression() {
17822           scope.$eval(expr);
17823         }
17824       },
17826       /**
17827        * @ngdoc method
17828        * @name $rootScope.Scope#$on
17829        * @kind function
17830        *
17831        * @description
17832        * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
17833        * discussion of event life cycle.
17834        *
17835        * The event listener function format is: `function(event, args...)`. The `event` object
17836        * passed into the listener has the following attributes:
17837        *
17838        *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
17839        *     `$broadcast`-ed.
17840        *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
17841        *     event propagates through the scope hierarchy, this property is set to null.
17842        *   - `name` - `{string}`: name of the event.
17843        *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
17844        *     further event propagation (available only for events that were `$emit`-ed).
17845        *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
17846        *     to true.
17847        *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
17848        *
17849        * @param {string} name Event name to listen on.
17850        * @param {function(event, ...args)} listener Function to call when the event is emitted.
17851        * @returns {function()} Returns a deregistration function for this listener.
17852        */
17853       $on: function(name, listener) {
17854         var namedListeners = this.$$listeners[name];
17855         if (!namedListeners) {
17856           this.$$listeners[name] = namedListeners = [];
17857         }
17858         namedListeners.push(listener);
17860         var current = this;
17861         do {
17862           if (!current.$$listenerCount[name]) {
17863             current.$$listenerCount[name] = 0;
17864           }
17865           current.$$listenerCount[name]++;
17866         } while ((current = current.$parent));
17868         var self = this;
17869         return function() {
17870           var indexOfListener = namedListeners.indexOf(listener);
17871           if (indexOfListener !== -1) {
17872             namedListeners[indexOfListener] = null;
17873             decrementListenerCount(self, 1, name);
17874           }
17875         };
17876       },
17879       /**
17880        * @ngdoc method
17881        * @name $rootScope.Scope#$emit
17882        * @kind function
17883        *
17884        * @description
17885        * Dispatches an event `name` upwards through the scope hierarchy notifying the
17886        * registered {@link ng.$rootScope.Scope#$on} listeners.
17887        *
17888        * The event life cycle starts at the scope on which `$emit` was called. All
17889        * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
17890        * notified. Afterwards, the event traverses upwards toward the root scope and calls all
17891        * registered listeners along the way. The event will stop propagating if one of the listeners
17892        * cancels it.
17893        *
17894        * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
17895        * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
17896        *
17897        * @param {string} name Event name to emit.
17898        * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
17899        * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
17900        */
17901       $emit: function(name, args) {
17902         var empty = [],
17903             namedListeners,
17904             scope = this,
17905             stopPropagation = false,
17906             event = {
17907               name: name,
17908               targetScope: scope,
17909               stopPropagation: function() {stopPropagation = true;},
17910               preventDefault: function() {
17911                 event.defaultPrevented = true;
17912               },
17913               defaultPrevented: false
17914             },
17915             listenerArgs = concat([event], arguments, 1),
17916             i, length;
17918         do {
17919           namedListeners = scope.$$listeners[name] || empty;
17920           event.currentScope = scope;
17921           for (i = 0, length = namedListeners.length; i < length; i++) {
17923             // if listeners were deregistered, defragment the array
17924             if (!namedListeners[i]) {
17925               namedListeners.splice(i, 1);
17926               i--;
17927               length--;
17928               continue;
17929             }
17930             try {
17931               //allow all listeners attached to the current scope to run
17932               namedListeners[i].apply(null, listenerArgs);
17933             } catch (e) {
17934               $exceptionHandler(e);
17935             }
17936           }
17937           //if any listener on the current scope stops propagation, prevent bubbling
17938           if (stopPropagation) {
17939             event.currentScope = null;
17940             return event;
17941           }
17942           //traverse upwards
17943           scope = scope.$parent;
17944         } while (scope);
17946         event.currentScope = null;
17948         return event;
17949       },
17952       /**
17953        * @ngdoc method
17954        * @name $rootScope.Scope#$broadcast
17955        * @kind function
17956        *
17957        * @description
17958        * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
17959        * registered {@link ng.$rootScope.Scope#$on} listeners.
17960        *
17961        * The event life cycle starts at the scope on which `$broadcast` was called. All
17962        * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
17963        * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
17964        * scope and calls all registered listeners along the way. The event cannot be canceled.
17965        *
17966        * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
17967        * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
17968        *
17969        * @param {string} name Event name to broadcast.
17970        * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
17971        * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
17972        */
17973       $broadcast: function(name, args) {
17974         var target = this,
17975             current = target,
17976             next = target,
17977             event = {
17978               name: name,
17979               targetScope: target,
17980               preventDefault: function() {
17981                 event.defaultPrevented = true;
17982               },
17983               defaultPrevented: false
17984             };
17986         if (!target.$$listenerCount[name]) return event;
17988         var listenerArgs = concat([event], arguments, 1),
17989             listeners, i, length;
17991         //down while you can, then up and next sibling or up and next sibling until back at root
17992         while ((current = next)) {
17993           event.currentScope = current;
17994           listeners = current.$$listeners[name] || [];
17995           for (i = 0, length = listeners.length; i < length; i++) {
17996             // if listeners were deregistered, defragment the array
17997             if (!listeners[i]) {
17998               listeners.splice(i, 1);
17999               i--;
18000               length--;
18001               continue;
18002             }
18004             try {
18005               listeners[i].apply(null, listenerArgs);
18006             } catch (e) {
18007               $exceptionHandler(e);
18008             }
18009           }
18011           // Insanity Warning: scope depth-first traversal
18012           // yes, this code is a bit crazy, but it works and we have tests to prove it!
18013           // this piece should be kept in sync with the traversal in $digest
18014           // (though it differs due to having the extra check for $$listenerCount)
18015           if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
18016               (current !== target && current.$$nextSibling)))) {
18017             while (current !== target && !(next = current.$$nextSibling)) {
18018               current = current.$parent;
18019             }
18020           }
18021         }
18023         event.currentScope = null;
18024         return event;
18025       }
18026     };
18028     var $rootScope = new Scope();
18030     //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
18031     var asyncQueue = $rootScope.$$asyncQueue = [];
18032     var postDigestQueue = $rootScope.$$postDigestQueue = [];
18033     var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
18035     var postDigestQueuePosition = 0;
18037     return $rootScope;
18040     function beginPhase(phase) {
18041       if ($rootScope.$$phase) {
18042         throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
18043       }
18045       $rootScope.$$phase = phase;
18046     }
18048     function clearPhase() {
18049       $rootScope.$$phase = null;
18050     }
18052     function incrementWatchersCount(current, count) {
18053       do {
18054         current.$$watchersCount += count;
18055       } while ((current = current.$parent));
18056     }
18058     function decrementListenerCount(current, count, name) {
18059       do {
18060         current.$$listenerCount[name] -= count;
18062         if (current.$$listenerCount[name] === 0) {
18063           delete current.$$listenerCount[name];
18064         }
18065       } while ((current = current.$parent));
18066     }
18068     /**
18069      * function used as an initial value for watchers.
18070      * because it's unique we can easily tell it apart from other values
18071      */
18072     function initWatchVal() {}
18074     function flushApplyAsync() {
18075       while (applyAsyncQueue.length) {
18076         try {
18077           applyAsyncQueue.shift()();
18078         } catch (e) {
18079           $exceptionHandler(e);
18080         }
18081       }
18082       applyAsyncId = null;
18083     }
18085     function scheduleApplyAsync() {
18086       if (applyAsyncId === null) {
18087         applyAsyncId = $browser.defer(function() {
18088           $rootScope.$apply(flushApplyAsync);
18089         });
18090       }
18091     }
18092   }];
18096  * @ngdoc service
18097  * @name $rootElement
18099  * @description
18100  * The root element of Angular application. This is either the element where {@link
18101  * ng.directive:ngApp ngApp} was declared or the element passed into
18102  * {@link angular.bootstrap}. The element represents the root element of application. It is also the
18103  * location where the application's {@link auto.$injector $injector} service gets
18104  * published, and can be retrieved using `$rootElement.injector()`.
18105  */
18108 // the implementation is in angular.bootstrap
18111  * @description
18112  * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
18113  */
18114 function $$SanitizeUriProvider() {
18115   var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
18116     imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
18118   /**
18119    * @description
18120    * Retrieves or overrides the default regular expression that is used for whitelisting of safe
18121    * urls during a[href] sanitization.
18122    *
18123    * The sanitization is a security measure aimed at prevent XSS attacks via html links.
18124    *
18125    * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
18126    * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
18127    * regular expression. If a match is found, the original url is written into the dom. Otherwise,
18128    * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
18129    *
18130    * @param {RegExp=} regexp New regexp to whitelist urls with.
18131    * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
18132    *    chaining otherwise.
18133    */
18134   this.aHrefSanitizationWhitelist = function(regexp) {
18135     if (isDefined(regexp)) {
18136       aHrefSanitizationWhitelist = regexp;
18137       return this;
18138     }
18139     return aHrefSanitizationWhitelist;
18140   };
18143   /**
18144    * @description
18145    * Retrieves or overrides the default regular expression that is used for whitelisting of safe
18146    * urls during img[src] sanitization.
18147    *
18148    * The sanitization is a security measure aimed at prevent XSS attacks via html links.
18149    *
18150    * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
18151    * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
18152    * regular expression. If a match is found, the original url is written into the dom. Otherwise,
18153    * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
18154    *
18155    * @param {RegExp=} regexp New regexp to whitelist urls with.
18156    * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
18157    *    chaining otherwise.
18158    */
18159   this.imgSrcSanitizationWhitelist = function(regexp) {
18160     if (isDefined(regexp)) {
18161       imgSrcSanitizationWhitelist = regexp;
18162       return this;
18163     }
18164     return imgSrcSanitizationWhitelist;
18165   };
18167   this.$get = function() {
18168     return function sanitizeUri(uri, isImage) {
18169       var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
18170       var normalizedVal;
18171       normalizedVal = urlResolve(uri).href;
18172       if (normalizedVal !== '' && !normalizedVal.match(regex)) {
18173         return 'unsafe:' + normalizedVal;
18174       }
18175       return uri;
18176     };
18177   };
18180 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18181  *     Any commits to this file should be reviewed with security in mind.  *
18182  *   Changes to this file can potentially create security vulnerabilities. *
18183  *          An approval from 2 Core members with history of modifying      *
18184  *                         this file is required.                          *
18185  *                                                                         *
18186  *  Does the change somehow allow for arbitrary javascript to be executed? *
18187  *    Or allows for someone to change the prototype of built-in objects?   *
18188  *     Or gives undesired access to variables likes document or window?    *
18189  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
18191 var $sceMinErr = minErr('$sce');
18193 var SCE_CONTEXTS = {
18194   HTML: 'html',
18195   CSS: 'css',
18196   URL: 'url',
18197   // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
18198   // url.  (e.g. ng-include, script src, templateUrl)
18199   RESOURCE_URL: 'resourceUrl',
18200   JS: 'js'
18203 // Helper functions follow.
18205 function adjustMatcher(matcher) {
18206   if (matcher === 'self') {
18207     return matcher;
18208   } else if (isString(matcher)) {
18209     // Strings match exactly except for 2 wildcards - '*' and '**'.
18210     // '*' matches any character except those from the set ':/.?&'.
18211     // '**' matches any character (like .* in a RegExp).
18212     // More than 2 *'s raises an error as it's ill defined.
18213     if (matcher.indexOf('***') > -1) {
18214       throw $sceMinErr('iwcard',
18215           'Illegal sequence *** in string matcher.  String: {0}', matcher);
18216     }
18217     matcher = escapeForRegexp(matcher).
18218                   replace('\\*\\*', '.*').
18219                   replace('\\*', '[^:/.?&;]*');
18220     return new RegExp('^' + matcher + '$');
18221   } else if (isRegExp(matcher)) {
18222     // The only other type of matcher allowed is a Regexp.
18223     // Match entire URL / disallow partial matches.
18224     // Flags are reset (i.e. no global, ignoreCase or multiline)
18225     return new RegExp('^' + matcher.source + '$');
18226   } else {
18227     throw $sceMinErr('imatcher',
18228         'Matchers may only be "self", string patterns or RegExp objects');
18229   }
18233 function adjustMatchers(matchers) {
18234   var adjustedMatchers = [];
18235   if (isDefined(matchers)) {
18236     forEach(matchers, function(matcher) {
18237       adjustedMatchers.push(adjustMatcher(matcher));
18238     });
18239   }
18240   return adjustedMatchers;
18245  * @ngdoc service
18246  * @name $sceDelegate
18247  * @kind function
18249  * @description
18251  * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
18252  * Contextual Escaping (SCE)} services to AngularJS.
18254  * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
18255  * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
18256  * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
18257  * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
18258  * work because `$sce` delegates to `$sceDelegate` for these operations.
18260  * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
18262  * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
18263  * can override it completely to change the behavior of `$sce`, the common case would
18264  * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
18265  * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
18266  * templates.  Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
18267  * $sceDelegateProvider.resourceUrlWhitelist} and {@link
18268  * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
18269  */
18272  * @ngdoc provider
18273  * @name $sceDelegateProvider
18274  * @description
18276  * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
18277  * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure
18278  * that the URLs used for sourcing Angular templates are safe.  Refer {@link
18279  * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
18280  * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
18282  * For the general details about this service in Angular, read the main page for {@link ng.$sce
18283  * Strict Contextual Escaping (SCE)}.
18285  * **Example**:  Consider the following case. <a name="example"></a>
18287  * - your app is hosted at url `http://myapp.example.com/`
18288  * - but some of your templates are hosted on other domains you control such as
18289  *   `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc.
18290  * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
18292  * Here is what a secure configuration for this scenario might look like:
18294  * ```
18295  *  angular.module('myApp', []).config(function($sceDelegateProvider) {
18296  *    $sceDelegateProvider.resourceUrlWhitelist([
18297  *      // Allow same origin resource loads.
18298  *      'self',
18299  *      // Allow loading from our assets domain.  Notice the difference between * and **.
18300  *      'http://srv*.assets.example.com/**'
18301  *    ]);
18303  *    // The blacklist overrides the whitelist so the open redirect here is blocked.
18304  *    $sceDelegateProvider.resourceUrlBlacklist([
18305  *      'http://myapp.example.com/clickThru**'
18306  *    ]);
18307  *  });
18308  * ```
18309  */
18311 function $SceDelegateProvider() {
18312   this.SCE_CONTEXTS = SCE_CONTEXTS;
18314   // Resource URLs can also be trusted by policy.
18315   var resourceUrlWhitelist = ['self'],
18316       resourceUrlBlacklist = [];
18318   /**
18319    * @ngdoc method
18320    * @name $sceDelegateProvider#resourceUrlWhitelist
18321    * @kind function
18322    *
18323    * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
18324    *    provided.  This must be an array or null.  A snapshot of this array is used so further
18325    *    changes to the array are ignored.
18326    *
18327    *    Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
18328    *    allowed in this array.
18329    *
18330    *    <div class="alert alert-warning">
18331    *    **Note:** an empty whitelist array will block all URLs!
18332    *    </div>
18333    *
18334    * @return {Array} the currently set whitelist array.
18335    *
18336    * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
18337    * same origin resource requests.
18338    *
18339    * @description
18340    * Sets/Gets the whitelist of trusted resource URLs.
18341    */
18342   this.resourceUrlWhitelist = function(value) {
18343     if (arguments.length) {
18344       resourceUrlWhitelist = adjustMatchers(value);
18345     }
18346     return resourceUrlWhitelist;
18347   };
18349   /**
18350    * @ngdoc method
18351    * @name $sceDelegateProvider#resourceUrlBlacklist
18352    * @kind function
18353    *
18354    * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
18355    *    provided.  This must be an array or null.  A snapshot of this array is used so further
18356    *    changes to the array are ignored.
18357    *
18358    *    Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
18359    *    allowed in this array.
18360    *
18361    *    The typical usage for the blacklist is to **block
18362    *    [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
18363    *    these would otherwise be trusted but actually return content from the redirected domain.
18364    *
18365    *    Finally, **the blacklist overrides the whitelist** and has the final say.
18366    *
18367    * @return {Array} the currently set blacklist array.
18368    *
18369    * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
18370    * is no blacklist.)
18371    *
18372    * @description
18373    * Sets/Gets the blacklist of trusted resource URLs.
18374    */
18376   this.resourceUrlBlacklist = function(value) {
18377     if (arguments.length) {
18378       resourceUrlBlacklist = adjustMatchers(value);
18379     }
18380     return resourceUrlBlacklist;
18381   };
18383   this.$get = ['$injector', function($injector) {
18385     var htmlSanitizer = function htmlSanitizer(html) {
18386       throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
18387     };
18389     if ($injector.has('$sanitize')) {
18390       htmlSanitizer = $injector.get('$sanitize');
18391     }
18394     function matchUrl(matcher, parsedUrl) {
18395       if (matcher === 'self') {
18396         return urlIsSameOrigin(parsedUrl);
18397       } else {
18398         // definitely a regex.  See adjustMatchers()
18399         return !!matcher.exec(parsedUrl.href);
18400       }
18401     }
18403     function isResourceUrlAllowedByPolicy(url) {
18404       var parsedUrl = urlResolve(url.toString());
18405       var i, n, allowed = false;
18406       // Ensure that at least one item from the whitelist allows this url.
18407       for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
18408         if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
18409           allowed = true;
18410           break;
18411         }
18412       }
18413       if (allowed) {
18414         // Ensure that no item from the blacklist blocked this url.
18415         for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
18416           if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
18417             allowed = false;
18418             break;
18419           }
18420         }
18421       }
18422       return allowed;
18423     }
18425     function generateHolderType(Base) {
18426       var holderType = function TrustedValueHolderType(trustedValue) {
18427         this.$$unwrapTrustedValue = function() {
18428           return trustedValue;
18429         };
18430       };
18431       if (Base) {
18432         holderType.prototype = new Base();
18433       }
18434       holderType.prototype.valueOf = function sceValueOf() {
18435         return this.$$unwrapTrustedValue();
18436       };
18437       holderType.prototype.toString = function sceToString() {
18438         return this.$$unwrapTrustedValue().toString();
18439       };
18440       return holderType;
18441     }
18443     var trustedValueHolderBase = generateHolderType(),
18444         byType = {};
18446     byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
18447     byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
18448     byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
18449     byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
18450     byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
18452     /**
18453      * @ngdoc method
18454      * @name $sceDelegate#trustAs
18455      *
18456      * @description
18457      * Returns an object that is trusted by angular for use in specified strict
18458      * contextual escaping contexts (such as ng-bind-html, ng-include, any src
18459      * attribute interpolation, any dom event binding attribute interpolation
18460      * such as for onclick,  etc.) that uses the provided value.
18461      * See {@link ng.$sce $sce} for enabling strict contextual escaping.
18462      *
18463      * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
18464      *   resourceUrl, html, js and css.
18465      * @param {*} value The value that that should be considered trusted/safe.
18466      * @returns {*} A value that can be used to stand in for the provided `value` in places
18467      * where Angular expects a $sce.trustAs() return value.
18468      */
18469     function trustAs(type, trustedValue) {
18470       var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
18471       if (!Constructor) {
18472         throw $sceMinErr('icontext',
18473             'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
18474             type, trustedValue);
18475       }
18476       if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
18477         return trustedValue;
18478       }
18479       // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
18480       // mutable objects, we ensure here that the value passed in is actually a string.
18481       if (typeof trustedValue !== 'string') {
18482         throw $sceMinErr('itype',
18483             'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
18484             type);
18485       }
18486       return new Constructor(trustedValue);
18487     }
18489     /**
18490      * @ngdoc method
18491      * @name $sceDelegate#valueOf
18492      *
18493      * @description
18494      * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
18495      * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
18496      * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
18497      *
18498      * If the passed parameter is not a value that had been returned by {@link
18499      * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
18500      *
18501      * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
18502      *      call or anything else.
18503      * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
18504      *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
18505      *     `value` unchanged.
18506      */
18507     function valueOf(maybeTrusted) {
18508       if (maybeTrusted instanceof trustedValueHolderBase) {
18509         return maybeTrusted.$$unwrapTrustedValue();
18510       } else {
18511         return maybeTrusted;
18512       }
18513     }
18515     /**
18516      * @ngdoc method
18517      * @name $sceDelegate#getTrusted
18518      *
18519      * @description
18520      * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
18521      * returns the originally supplied value if the queried context type is a supertype of the
18522      * created type.  If this condition isn't satisfied, throws an exception.
18523      *
18524      * <div class="alert alert-danger">
18525      * Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting
18526      * (XSS) vulnerability in your application.
18527      * </div>
18528      *
18529      * @param {string} type The kind of context in which this value is to be used.
18530      * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
18531      *     `$sceDelegate.trustAs`} call.
18532      * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
18533      *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.
18534      */
18535     function getTrusted(type, maybeTrusted) {
18536       if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
18537         return maybeTrusted;
18538       }
18539       var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
18540       if (constructor && maybeTrusted instanceof constructor) {
18541         return maybeTrusted.$$unwrapTrustedValue();
18542       }
18543       // If we get here, then we may only take one of two actions.
18544       // 1. sanitize the value for the requested type, or
18545       // 2. throw an exception.
18546       if (type === SCE_CONTEXTS.RESOURCE_URL) {
18547         if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
18548           return maybeTrusted;
18549         } else {
18550           throw $sceMinErr('insecurl',
18551               'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
18552               maybeTrusted.toString());
18553         }
18554       } else if (type === SCE_CONTEXTS.HTML) {
18555         return htmlSanitizer(maybeTrusted);
18556       }
18557       throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
18558     }
18560     return { trustAs: trustAs,
18561              getTrusted: getTrusted,
18562              valueOf: valueOf };
18563   }];
18568  * @ngdoc provider
18569  * @name $sceProvider
18570  * @description
18572  * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
18573  * -   enable/disable Strict Contextual Escaping (SCE) in a module
18574  * -   override the default implementation with a custom delegate
18576  * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
18577  */
18579 /* jshint maxlen: false*/
18582  * @ngdoc service
18583  * @name $sce
18584  * @kind function
18586  * @description
18588  * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
18590  * # Strict Contextual Escaping
18592  * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
18593  * contexts to result in a value that is marked as safe to use for that context.  One example of
18594  * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer
18595  * to these contexts as privileged or SCE contexts.
18597  * As of version 1.2, Angular ships with SCE enabled by default.
18599  * Note:  When enabled (the default), IE<11 in quirks mode is not supported.  In this mode, IE<11 allow
18600  * one to execute arbitrary javascript by the use of the expression() syntax.  Refer
18601  * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
18602  * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
18603  * to the top of your HTML document.
18605  * SCE assists in writing code in a way that (a) is secure by default and (b) makes auditing for
18606  * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
18608  * Here's an example of a binding in a privileged context:
18610  * ```
18611  * <input ng-model="userHtml" aria-label="User input">
18612  * <div ng-bind-html="userHtml"></div>
18613  * ```
18615  * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
18616  * disabled, this application allows the user to render arbitrary HTML into the DIV.
18617  * In a more realistic example, one may be rendering user comments, blog articles, etc. via
18618  * bindings.  (HTML is just one example of a context where rendering user controlled input creates
18619  * security vulnerabilities.)
18621  * For the case of HTML, you might use a library, either on the client side, or on the server side,
18622  * to sanitize unsafe HTML before binding to the value and rendering it in the document.
18624  * How would you ensure that every place that used these types of bindings was bound to a value that
18625  * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
18626  * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
18627  * properties/fields and forgot to update the binding to the sanitized value?
18629  * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
18630  * determine that something explicitly says it's safe to use a value for binding in that
18631  * context.  You can then audit your code (a simple grep would do) to ensure that this is only done
18632  * for those values that you can easily tell are safe - because they were received from your server,
18633  * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps
18634  * allowing only the files in a specific directory to do this.  Ensuring that the internal API
18635  * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
18637  * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
18638  * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
18639  * obtain values that will be accepted by SCE / privileged contexts.
18642  * ## How does it work?
18644  * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
18645  * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link
18646  * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
18647  * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
18649  * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
18650  * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
18651  * simplified):
18653  * ```
18654  * var ngBindHtmlDirective = ['$sce', function($sce) {
18655  *   return function(scope, element, attr) {
18656  *     scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
18657  *       element.html(value || '');
18658  *     });
18659  *   };
18660  * }];
18661  * ```
18663  * ## Impact on loading templates
18665  * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
18666  * `templateUrl`'s specified by {@link guide/directive directives}.
18668  * By default, Angular only loads templates from the same domain and protocol as the application
18669  * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl
18670  * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
18671  * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
18672  * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
18674  * *Please note*:
18675  * The browser's
18676  * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
18677  * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
18678  * policy apply in addition to this and may further restrict whether the template is successfully
18679  * loaded.  This means that without the right CORS policy, loading templates from a different domain
18680  * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
18681  * browsers.
18683  * ## This feels like too much overhead
18685  * It's important to remember that SCE only applies to interpolation expressions.
18687  * If your expressions are constant literals, they're automatically trusted and you don't need to
18688  * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
18689  * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
18691  * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
18692  * through {@link ng.$sce#getTrusted $sce.getTrusted}.  SCE doesn't play a role here.
18694  * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
18695  * templates in `ng-include` from your application's domain without having to even know about SCE.
18696  * It blocks loading templates from other domains or loading templates over http from an https
18697  * served document.  You can change these by setting your own custom {@link
18698  * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
18699  * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
18701  * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
18702  * application that's secure and can be audited to verify that with much more ease than bolting
18703  * security onto an application later.
18705  * <a name="contexts"></a>
18706  * ## What trusted context types are supported?
18708  * | Context             | Notes          |
18709  * |---------------------|----------------|
18710  * | `$sce.HTML`         | For HTML that's safe to source into the application.  The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. |
18711  * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
18712  * | `$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. |
18713  * | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents 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. |
18714  * | `$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. |
18716  * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
18718  *  Each element in these arrays must be one of the following:
18720  *  - **'self'**
18721  *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
18722  *      domain** as the application document using the **same protocol**.
18723  *  - **String** (except the special value `'self'`)
18724  *    - The string is matched against the full *normalized / absolute URL* of the resource
18725  *      being tested (substring matches are not good enough.)
18726  *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
18727  *      match themselves.
18728  *    - `*`: matches zero or more occurrences of any character other than one of the following 6
18729  *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'.  It's a useful wildcard for use
18730  *      in a whitelist.
18731  *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not
18732  *      appropriate for use in a scheme, domain, etc. as it would match too much.  (e.g.
18733  *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
18734  *      not have been the intention.)  Its usage at the very end of the path is ok.  (e.g.
18735  *      http://foo.example.com/templates/**).
18736  *  - **RegExp** (*see caveat below*)
18737  *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
18738  *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
18739  *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
18740  *      have good test coverage).  For instance, the use of `.` in the regex is correct only in a
18741  *      small number of cases.  A `.` character in the regex used when matching the scheme or a
18742  *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
18743  *      is highly recommended to use the string patterns and only fall back to regular expressions
18744  *      as a last resort.
18745  *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
18746  *      matched against the **entire** *normalized / absolute URL* of the resource being tested
18747  *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
18748  *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
18749  *    - If you are generating your JavaScript from some other templating engine (not
18750  *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
18751  *      remember to escape your regular expression (and be aware that you might need more than
18752  *      one level of escaping depending on your templating engine and the way you interpolated
18753  *      the value.)  Do make use of your platform's escaping mechanism as it might be good
18754  *      enough before coding your own.  E.g. Ruby has
18755  *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
18756  *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
18757  *      Javascript lacks a similar built in function for escaping.  Take a look at Google
18758  *      Closure library's [goog.string.regExpEscape(s)](
18759  *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
18761  * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
18763  * ## Show me an example using SCE.
18765  * <example module="mySceApp" deps="angular-sanitize.js">
18766  * <file name="index.html">
18767  *   <div ng-controller="AppController as myCtrl">
18768  *     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
18769  *     <b>User comments</b><br>
18770  *     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
18771  *     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
18772  *     exploit.
18773  *     <div class="well">
18774  *       <div ng-repeat="userComment in myCtrl.userComments">
18775  *         <b>{{userComment.name}}</b>:
18776  *         <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
18777  *         <br>
18778  *       </div>
18779  *     </div>
18780  *   </div>
18781  * </file>
18783  * <file name="script.js">
18784  *   angular.module('mySceApp', ['ngSanitize'])
18785  *     .controller('AppController', ['$http', '$templateCache', '$sce',
18786  *       function($http, $templateCache, $sce) {
18787  *         var self = this;
18788  *         $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
18789  *           self.userComments = userComments;
18790  *         });
18791  *         self.explicitlyTrustedHtml = $sce.trustAsHtml(
18792  *             '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
18793  *             'sanitization.&quot;">Hover over this text.</span>');
18794  *       }]);
18795  * </file>
18797  * <file name="test_data.json">
18798  * [
18799  *   { "name": "Alice",
18800  *     "htmlComment":
18801  *         "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
18802  *   },
18803  *   { "name": "Bob",
18804  *     "htmlComment": "<i>Yes!</i>  Am I the only other one?"
18805  *   }
18806  * ]
18807  * </file>
18809  * <file name="protractor.js" type="protractor">
18810  *   describe('SCE doc demo', function() {
18811  *     it('should sanitize untrusted values', function() {
18812  *       expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
18813  *           .toBe('<span>Is <i>anyone</i> reading this?</span>');
18814  *     });
18816  *     it('should NOT sanitize explicitly trusted values', function() {
18817  *       expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
18818  *           '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
18819  *           'sanitization.&quot;">Hover over this text.</span>');
18820  *     });
18821  *   });
18822  * </file>
18823  * </example>
18827  * ## Can I disable SCE completely?
18829  * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
18830  * for little coding overhead.  It will be much harder to take an SCE disabled application and
18831  * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
18832  * for cases where you have a lot of existing code that was written before SCE was introduced and
18833  * you're migrating them a module at a time.
18835  * That said, here's how you can completely disable SCE:
18837  * ```
18838  * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
18839  *   // Completely disable SCE.  For demonstration purposes only!
18840  *   // Do not use in new projects.
18841  *   $sceProvider.enabled(false);
18842  * });
18843  * ```
18845  */
18846 /* jshint maxlen: 100 */
18848 function $SceProvider() {
18849   var enabled = true;
18851   /**
18852    * @ngdoc method
18853    * @name $sceProvider#enabled
18854    * @kind function
18855    *
18856    * @param {boolean=} value If provided, then enables/disables SCE.
18857    * @return {boolean} true if SCE is enabled, false otherwise.
18858    *
18859    * @description
18860    * Enables/disables SCE and returns the current value.
18861    */
18862   this.enabled = function(value) {
18863     if (arguments.length) {
18864       enabled = !!value;
18865     }
18866     return enabled;
18867   };
18870   /* Design notes on the default implementation for SCE.
18871    *
18872    * The API contract for the SCE delegate
18873    * -------------------------------------
18874    * The SCE delegate object must provide the following 3 methods:
18875    *
18876    * - trustAs(contextEnum, value)
18877    *     This method is used to tell the SCE service that the provided value is OK to use in the
18878    *     contexts specified by contextEnum.  It must return an object that will be accepted by
18879    *     getTrusted() for a compatible contextEnum and return this value.
18880    *
18881    * - valueOf(value)
18882    *     For values that were not produced by trustAs(), return them as is.  For values that were
18883    *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
18884    *     trustAs is wrapping the given values into some type, this operation unwraps it when given
18885    *     such a value.
18886    *
18887    * - getTrusted(contextEnum, value)
18888    *     This function should return the a value that is safe to use in the context specified by
18889    *     contextEnum or throw and exception otherwise.
18890    *
18891    * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
18892    * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
18893    * instance, an implementation could maintain a registry of all trusted objects by context.  In
18894    * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
18895    * return the same object passed in if it was found in the registry under a compatible context or
18896    * throw an exception otherwise.  An implementation might only wrap values some of the time based
18897    * on some criteria.  getTrusted() might return a value and not throw an exception for special
18898    * constants or objects even if not wrapped.  All such implementations fulfill this contract.
18899    *
18900    *
18901    * A note on the inheritance model for SCE contexts
18902    * ------------------------------------------------
18903    * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
18904    * is purely an implementation details.
18905    *
18906    * The contract is simply this:
18907    *
18908    *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
18909    *     will also succeed.
18910    *
18911    * Inheritance happens to capture this in a natural way.  In some future, we
18912    * may not use inheritance anymore.  That is OK because no code outside of
18913    * sce.js and sceSpecs.js would need to be aware of this detail.
18914    */
18916   this.$get = ['$parse', '$sceDelegate', function(
18917                 $parse,   $sceDelegate) {
18918     // Prereq: Ensure that we're not running in IE<11 quirks mode.  In that mode, IE < 11 allow
18919     // the "expression(javascript expression)" syntax which is insecure.
18920     if (enabled && msie < 8) {
18921       throw $sceMinErr('iequirks',
18922         'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
18923         'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
18924         'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
18925     }
18927     var sce = shallowCopy(SCE_CONTEXTS);
18929     /**
18930      * @ngdoc method
18931      * @name $sce#isEnabled
18932      * @kind function
18933      *
18934      * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you
18935      * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
18936      *
18937      * @description
18938      * Returns a boolean indicating if SCE is enabled.
18939      */
18940     sce.isEnabled = function() {
18941       return enabled;
18942     };
18943     sce.trustAs = $sceDelegate.trustAs;
18944     sce.getTrusted = $sceDelegate.getTrusted;
18945     sce.valueOf = $sceDelegate.valueOf;
18947     if (!enabled) {
18948       sce.trustAs = sce.getTrusted = function(type, value) { return value; };
18949       sce.valueOf = identity;
18950     }
18952     /**
18953      * @ngdoc method
18954      * @name $sce#parseAs
18955      *
18956      * @description
18957      * Converts Angular {@link guide/expression expression} into a function.  This is like {@link
18958      * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
18959      * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
18960      * *result*)}
18961      *
18962      * @param {string} type The kind of SCE context in which this result will be used.
18963      * @param {string} expression String expression to compile.
18964      * @returns {function(context, locals)} a function which represents the compiled expression:
18965      *
18966      *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings
18967      *      are evaluated against (typically a scope object).
18968      *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in
18969      *      `context`.
18970      */
18971     sce.parseAs = function sceParseAs(type, expr) {
18972       var parsed = $parse(expr);
18973       if (parsed.literal && parsed.constant) {
18974         return parsed;
18975       } else {
18976         return $parse(expr, function(value) {
18977           return sce.getTrusted(type, value);
18978         });
18979       }
18980     };
18982     /**
18983      * @ngdoc method
18984      * @name $sce#trustAs
18985      *
18986      * @description
18987      * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.  As such,
18988      * returns an object that is trusted by angular for use in specified strict contextual
18989      * escaping contexts (such as ng-bind-html, ng-include, any src attribute
18990      * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)
18991      * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual
18992      * escaping.
18993      *
18994      * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
18995      *   resourceUrl, html, js and css.
18996      * @param {*} value The value that that should be considered trusted/safe.
18997      * @returns {*} A value that can be used to stand in for the provided `value` in places
18998      * where Angular expects a $sce.trustAs() return value.
18999      */
19001     /**
19002      * @ngdoc method
19003      * @name $sce#trustAsHtml
19004      *
19005      * @description
19006      * Shorthand method.  `$sce.trustAsHtml(value)` â†’
19007      *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
19008      *
19009      * @param {*} value The value to trustAs.
19010      * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
19011      *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives
19012      *     only accept expressions that are either literal constants or are the
19013      *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
19014      */
19016     /**
19017      * @ngdoc method
19018      * @name $sce#trustAsUrl
19019      *
19020      * @description
19021      * Shorthand method.  `$sce.trustAsUrl(value)` â†’
19022      *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
19023      *
19024      * @param {*} value The value to trustAs.
19025      * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
19026      *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives
19027      *     only accept expressions that are either literal constants or are the
19028      *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
19029      */
19031     /**
19032      * @ngdoc method
19033      * @name $sce#trustAsResourceUrl
19034      *
19035      * @description
19036      * Shorthand method.  `$sce.trustAsResourceUrl(value)` â†’
19037      *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
19038      *
19039      * @param {*} value The value to trustAs.
19040      * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
19041      *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives
19042      *     only accept expressions that are either literal constants or are the return
19043      *     value of {@link ng.$sce#trustAs $sce.trustAs}.)
19044      */
19046     /**
19047      * @ngdoc method
19048      * @name $sce#trustAsJs
19049      *
19050      * @description
19051      * Shorthand method.  `$sce.trustAsJs(value)` â†’
19052      *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
19053      *
19054      * @param {*} value The value to trustAs.
19055      * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
19056      *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives
19057      *     only accept expressions that are either literal constants or are the
19058      *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
19059      */
19061     /**
19062      * @ngdoc method
19063      * @name $sce#getTrusted
19064      *
19065      * @description
19066      * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,
19067      * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
19068      * originally supplied value if the queried context type is a supertype of the created type.
19069      * If this condition isn't satisfied, throws an exception.
19070      *
19071      * @param {string} type The kind of context in which this value is to be used.
19072      * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
19073      *                         call.
19074      * @returns {*} The value the was originally provided to
19075      *              {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
19076      *              Otherwise, throws an exception.
19077      */
19079     /**
19080      * @ngdoc method
19081      * @name $sce#getTrustedHtml
19082      *
19083      * @description
19084      * Shorthand method.  `$sce.getTrustedHtml(value)` â†’
19085      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
19086      *
19087      * @param {*} value The value to pass to `$sce.getTrusted`.
19088      * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
19089      */
19091     /**
19092      * @ngdoc method
19093      * @name $sce#getTrustedCss
19094      *
19095      * @description
19096      * Shorthand method.  `$sce.getTrustedCss(value)` â†’
19097      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
19098      *
19099      * @param {*} value The value to pass to `$sce.getTrusted`.
19100      * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
19101      */
19103     /**
19104      * @ngdoc method
19105      * @name $sce#getTrustedUrl
19106      *
19107      * @description
19108      * Shorthand method.  `$sce.getTrustedUrl(value)` â†’
19109      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
19110      *
19111      * @param {*} value The value to pass to `$sce.getTrusted`.
19112      * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
19113      */
19115     /**
19116      * @ngdoc method
19117      * @name $sce#getTrustedResourceUrl
19118      *
19119      * @description
19120      * Shorthand method.  `$sce.getTrustedResourceUrl(value)` â†’
19121      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
19122      *
19123      * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
19124      * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
19125      */
19127     /**
19128      * @ngdoc method
19129      * @name $sce#getTrustedJs
19130      *
19131      * @description
19132      * Shorthand method.  `$sce.getTrustedJs(value)` â†’
19133      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
19134      *
19135      * @param {*} value The value to pass to `$sce.getTrusted`.
19136      * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
19137      */
19139     /**
19140      * @ngdoc method
19141      * @name $sce#parseAsHtml
19142      *
19143      * @description
19144      * Shorthand method.  `$sce.parseAsHtml(expression string)` â†’
19145      *     {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
19146      *
19147      * @param {string} expression String expression to compile.
19148      * @returns {function(context, locals)} a function which represents the compiled expression:
19149      *
19150      *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings
19151      *      are evaluated against (typically a scope object).
19152      *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in
19153      *      `context`.
19154      */
19156     /**
19157      * @ngdoc method
19158      * @name $sce#parseAsCss
19159      *
19160      * @description
19161      * Shorthand method.  `$sce.parseAsCss(value)` â†’
19162      *     {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
19163      *
19164      * @param {string} expression String expression to compile.
19165      * @returns {function(context, locals)} a function which represents the compiled expression:
19166      *
19167      *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings
19168      *      are evaluated against (typically a scope object).
19169      *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in
19170      *      `context`.
19171      */
19173     /**
19174      * @ngdoc method
19175      * @name $sce#parseAsUrl
19176      *
19177      * @description
19178      * Shorthand method.  `$sce.parseAsUrl(value)` â†’
19179      *     {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
19180      *
19181      * @param {string} expression String expression to compile.
19182      * @returns {function(context, locals)} a function which represents the compiled expression:
19183      *
19184      *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings
19185      *      are evaluated against (typically a scope object).
19186      *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in
19187      *      `context`.
19188      */
19190     /**
19191      * @ngdoc method
19192      * @name $sce#parseAsResourceUrl
19193      *
19194      * @description
19195      * Shorthand method.  `$sce.parseAsResourceUrl(value)` â†’
19196      *     {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
19197      *
19198      * @param {string} expression String expression to compile.
19199      * @returns {function(context, locals)} a function which represents the compiled expression:
19200      *
19201      *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings
19202      *      are evaluated against (typically a scope object).
19203      *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in
19204      *      `context`.
19205      */
19207     /**
19208      * @ngdoc method
19209      * @name $sce#parseAsJs
19210      *
19211      * @description
19212      * Shorthand method.  `$sce.parseAsJs(value)` â†’
19213      *     {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
19214      *
19215      * @param {string} expression String expression to compile.
19216      * @returns {function(context, locals)} a function which represents the compiled expression:
19217      *
19218      *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings
19219      *      are evaluated against (typically a scope object).
19220      *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in
19221      *      `context`.
19222      */
19224     // Shorthand delegations.
19225     var parse = sce.parseAs,
19226         getTrusted = sce.getTrusted,
19227         trustAs = sce.trustAs;
19229     forEach(SCE_CONTEXTS, function(enumValue, name) {
19230       var lName = lowercase(name);
19231       sce[camelCase("parse_as_" + lName)] = function(expr) {
19232         return parse(enumValue, expr);
19233       };
19234       sce[camelCase("get_trusted_" + lName)] = function(value) {
19235         return getTrusted(enumValue, value);
19236       };
19237       sce[camelCase("trust_as_" + lName)] = function(value) {
19238         return trustAs(enumValue, value);
19239       };
19240     });
19242     return sce;
19243   }];
19247  * !!! This is an undocumented "private" service !!!
19249  * @name $sniffer
19250  * @requires $window
19251  * @requires $document
19253  * @property {boolean} history Does the browser support html5 history api ?
19254  * @property {boolean} transitions Does the browser support CSS transition events ?
19255  * @property {boolean} animations Does the browser support CSS animation events ?
19257  * @description
19258  * This is very simple implementation of testing browser's features.
19259  */
19260 function $SnifferProvider() {
19261   this.$get = ['$window', '$document', function($window, $document) {
19262     var eventSupport = {},
19263         // Chrome Packaged Apps are not allowed to access `history.pushState`. They can be detected by
19264         // the presence of `chrome.app.runtime` (see https://developer.chrome.com/apps/api_index)
19265         isChromePackagedApp = $window.chrome && $window.chrome.app && $window.chrome.app.runtime,
19266         hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState,
19267         android =
19268           toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
19269         boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
19270         document = $document[0] || {},
19271         vendorPrefix,
19272         vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
19273         bodyStyle = document.body && document.body.style,
19274         transitions = false,
19275         animations = false,
19276         match;
19278     if (bodyStyle) {
19279       for (var prop in bodyStyle) {
19280         if (match = vendorRegex.exec(prop)) {
19281           vendorPrefix = match[0];
19282           vendorPrefix = vendorPrefix[0].toUpperCase() + vendorPrefix.substr(1);
19283           break;
19284         }
19285       }
19287       if (!vendorPrefix) {
19288         vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
19289       }
19291       transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
19292       animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
19294       if (android && (!transitions ||  !animations)) {
19295         transitions = isString(bodyStyle.webkitTransition);
19296         animations = isString(bodyStyle.webkitAnimation);
19297       }
19298     }
19301     return {
19302       // Android has history.pushState, but it does not update location correctly
19303       // so let's not use the history API at all.
19304       // http://code.google.com/p/android/issues/detail?id=17471
19305       // https://github.com/angular/angular.js/issues/904
19307       // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
19308       // so let's not use the history API also
19309       // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
19310       // jshint -W018
19311       history: !!(hasHistoryPushState && !(android < 4) && !boxee),
19312       // jshint +W018
19313       hasEvent: function(event) {
19314         // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
19315         // it. In particular the event is not fired when backspace or delete key are pressed or
19316         // when cut operation is performed.
19317         // IE10+ implements 'input' event but it erroneously fires under various situations,
19318         // e.g. when placeholder changes, or a form is focused.
19319         if (event === 'input' && msie <= 11) return false;
19321         if (isUndefined(eventSupport[event])) {
19322           var divElm = document.createElement('div');
19323           eventSupport[event] = 'on' + event in divElm;
19324         }
19326         return eventSupport[event];
19327       },
19328       csp: csp(),
19329       vendorPrefix: vendorPrefix,
19330       transitions: transitions,
19331       animations: animations,
19332       android: android
19333     };
19334   }];
19337 var $templateRequestMinErr = minErr('$compile');
19340  * @ngdoc provider
19341  * @name $templateRequestProvider
19342  * @description
19343  * Used to configure the options passed to the {@link $http} service when making a template request.
19345  * For example, it can be used for specifying the "Accept" header that is sent to the server, when
19346  * requesting a template.
19347  */
19348 function $TemplateRequestProvider() {
19350   var httpOptions;
19352   /**
19353    * @ngdoc method
19354    * @name $templateRequestProvider#httpOptions
19355    * @description
19356    * The options to be passed to the {@link $http} service when making the request.
19357    * You can use this to override options such as the "Accept" header for template requests.
19358    *
19359    * The {@link $templateRequest} will set the `cache` and the `transformResponse` properties of the
19360    * options if not overridden here.
19361    *
19362    * @param {string=} value new value for the {@link $http} options.
19363    * @returns {string|self} Returns the {@link $http} options when used as getter and self if used as setter.
19364    */
19365   this.httpOptions = function(val) {
19366     if (val) {
19367       httpOptions = val;
19368       return this;
19369     }
19370     return httpOptions;
19371   };
19373   /**
19374    * @ngdoc service
19375    * @name $templateRequest
19376    *
19377    * @description
19378    * The `$templateRequest` service runs security checks then downloads the provided template using
19379    * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
19380    * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
19381    * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
19382    * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
19383    * when `tpl` is of type string and `$templateCache` has the matching entry.
19384    *
19385    * If you want to pass custom options to the `$http` service, such as setting the Accept header you
19386    * can configure this via {@link $templateRequestProvider#httpOptions}.
19387    *
19388    * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
19389    * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
19390    *
19391    * @return {Promise} a promise for the HTTP response data of the given URL.
19392    *
19393    * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
19394    */
19395   this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
19397     function handleRequestFn(tpl, ignoreRequestError) {
19398       handleRequestFn.totalPendingRequests++;
19400       // We consider the template cache holds only trusted templates, so
19401       // there's no need to go through whitelisting again for keys that already
19402       // are included in there. This also makes Angular accept any script
19403       // directive, no matter its name. However, we still need to unwrap trusted
19404       // types.
19405       if (!isString(tpl) || isUndefined($templateCache.get(tpl))) {
19406         tpl = $sce.getTrustedResourceUrl(tpl);
19407       }
19409       var transformResponse = $http.defaults && $http.defaults.transformResponse;
19411       if (isArray(transformResponse)) {
19412         transformResponse = transformResponse.filter(function(transformer) {
19413           return transformer !== defaultHttpResponseTransform;
19414         });
19415       } else if (transformResponse === defaultHttpResponseTransform) {
19416         transformResponse = null;
19417       }
19419       return $http.get(tpl, extend({
19420           cache: $templateCache,
19421           transformResponse: transformResponse
19422         }, httpOptions))
19423         ['finally'](function() {
19424           handleRequestFn.totalPendingRequests--;
19425         })
19426         .then(function(response) {
19427           $templateCache.put(tpl, response.data);
19428           return response.data;
19429         }, handleError);
19431       function handleError(resp) {
19432         if (!ignoreRequestError) {
19433           throw $templateRequestMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
19434             tpl, resp.status, resp.statusText);
19435         }
19436         return $q.reject(resp);
19437       }
19438     }
19440     handleRequestFn.totalPendingRequests = 0;
19442     return handleRequestFn;
19443   }];
19446 function $$TestabilityProvider() {
19447   this.$get = ['$rootScope', '$browser', '$location',
19448        function($rootScope,   $browser,   $location) {
19450     /**
19451      * @name $testability
19452      *
19453      * @description
19454      * The private $$testability service provides a collection of methods for use when debugging
19455      * or by automated test and debugging tools.
19456      */
19457     var testability = {};
19459     /**
19460      * @name $$testability#findBindings
19461      *
19462      * @description
19463      * Returns an array of elements that are bound (via ng-bind or {{}})
19464      * to expressions matching the input.
19465      *
19466      * @param {Element} element The element root to search from.
19467      * @param {string} expression The binding expression to match.
19468      * @param {boolean} opt_exactMatch If true, only returns exact matches
19469      *     for the expression. Filters and whitespace are ignored.
19470      */
19471     testability.findBindings = function(element, expression, opt_exactMatch) {
19472       var bindings = element.getElementsByClassName('ng-binding');
19473       var matches = [];
19474       forEach(bindings, function(binding) {
19475         var dataBinding = angular.element(binding).data('$binding');
19476         if (dataBinding) {
19477           forEach(dataBinding, function(bindingName) {
19478             if (opt_exactMatch) {
19479               var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
19480               if (matcher.test(bindingName)) {
19481                 matches.push(binding);
19482               }
19483             } else {
19484               if (bindingName.indexOf(expression) != -1) {
19485                 matches.push(binding);
19486               }
19487             }
19488           });
19489         }
19490       });
19491       return matches;
19492     };
19494     /**
19495      * @name $$testability#findModels
19496      *
19497      * @description
19498      * Returns an array of elements that are two-way found via ng-model to
19499      * expressions matching the input.
19500      *
19501      * @param {Element} element The element root to search from.
19502      * @param {string} expression The model expression to match.
19503      * @param {boolean} opt_exactMatch If true, only returns exact matches
19504      *     for the expression.
19505      */
19506     testability.findModels = function(element, expression, opt_exactMatch) {
19507       var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
19508       for (var p = 0; p < prefixes.length; ++p) {
19509         var attributeEquals = opt_exactMatch ? '=' : '*=';
19510         var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
19511         var elements = element.querySelectorAll(selector);
19512         if (elements.length) {
19513           return elements;
19514         }
19515       }
19516     };
19518     /**
19519      * @name $$testability#getLocation
19520      *
19521      * @description
19522      * Shortcut for getting the location in a browser agnostic way. Returns
19523      *     the path, search, and hash. (e.g. /path?a=b#hash)
19524      */
19525     testability.getLocation = function() {
19526       return $location.url();
19527     };
19529     /**
19530      * @name $$testability#setLocation
19531      *
19532      * @description
19533      * Shortcut for navigating to a location without doing a full page reload.
19534      *
19535      * @param {string} url The location url (path, search and hash,
19536      *     e.g. /path?a=b#hash) to go to.
19537      */
19538     testability.setLocation = function(url) {
19539       if (url !== $location.url()) {
19540         $location.url(url);
19541         $rootScope.$digest();
19542       }
19543     };
19545     /**
19546      * @name $$testability#whenStable
19547      *
19548      * @description
19549      * Calls the callback when $timeout and $http requests are completed.
19550      *
19551      * @param {function} callback
19552      */
19553     testability.whenStable = function(callback) {
19554       $browser.notifyWhenNoOutstandingRequests(callback);
19555     };
19557     return testability;
19558   }];
19561 function $TimeoutProvider() {
19562   this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
19563        function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {
19565     var deferreds = {};
19568      /**
19569       * @ngdoc service
19570       * @name $timeout
19571       *
19572       * @description
19573       * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
19574       * block and delegates any exceptions to
19575       * {@link ng.$exceptionHandler $exceptionHandler} service.
19576       *
19577       * The return value of calling `$timeout` is a promise, which will be resolved when
19578       * the delay has passed and the timeout function, if provided, is executed.
19579       *
19580       * To cancel a timeout request, call `$timeout.cancel(promise)`.
19581       *
19582       * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
19583       * synchronously flush the queue of deferred functions.
19584       *
19585       * If you only want a promise that will be resolved after some specified delay
19586       * then you can call `$timeout` without the `fn` function.
19587       *
19588       * @param {function()=} fn A function, whose execution should be delayed.
19589       * @param {number=} [delay=0] Delay in milliseconds.
19590       * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
19591       *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
19592       * @param {...*=} Pass additional parameters to the executed function.
19593       * @returns {Promise} Promise that will be resolved when the timeout is reached. The promise
19594       *   will be resolved with the return value of the `fn` function.
19595       *
19596       */
19597     function timeout(fn, delay, invokeApply) {
19598       if (!isFunction(fn)) {
19599         invokeApply = delay;
19600         delay = fn;
19601         fn = noop;
19602       }
19604       var args = sliceArgs(arguments, 3),
19605           skipApply = (isDefined(invokeApply) && !invokeApply),
19606           deferred = (skipApply ? $$q : $q).defer(),
19607           promise = deferred.promise,
19608           timeoutId;
19610       timeoutId = $browser.defer(function() {
19611         try {
19612           deferred.resolve(fn.apply(null, args));
19613         } catch (e) {
19614           deferred.reject(e);
19615           $exceptionHandler(e);
19616         }
19617         finally {
19618           delete deferreds[promise.$$timeoutId];
19619         }
19621         if (!skipApply) $rootScope.$apply();
19622       }, delay);
19624       promise.$$timeoutId = timeoutId;
19625       deferreds[timeoutId] = deferred;
19627       return promise;
19628     }
19631      /**
19632       * @ngdoc method
19633       * @name $timeout#cancel
19634       *
19635       * @description
19636       * Cancels a task associated with the `promise`. As a result of this, the promise will be
19637       * resolved with a rejection.
19638       *
19639       * @param {Promise=} promise Promise returned by the `$timeout` function.
19640       * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
19641       *   canceled.
19642       */
19643     timeout.cancel = function(promise) {
19644       if (promise && promise.$$timeoutId in deferreds) {
19645         deferreds[promise.$$timeoutId].reject('canceled');
19646         delete deferreds[promise.$$timeoutId];
19647         return $browser.defer.cancel(promise.$$timeoutId);
19648       }
19649       return false;
19650     };
19652     return timeout;
19653   }];
19656 // NOTE:  The usage of window and document instead of $window and $document here is
19657 // deliberate.  This service depends on the specific behavior of anchor nodes created by the
19658 // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
19659 // cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
19660 // doesn't know about mocked locations and resolves URLs to the real document - which is
19661 // exactly the behavior needed here.  There is little value is mocking these out for this
19662 // service.
19663 var urlParsingNode = window.document.createElement("a");
19664 var originUrl = urlResolve(window.location.href);
19669  * Implementation Notes for non-IE browsers
19670  * ----------------------------------------
19671  * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
19672  * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
19673  * URL will be resolved into an absolute URL in the context of the application document.
19674  * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
19675  * properties are all populated to reflect the normalized URL.  This approach has wide
19676  * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc.  See
19677  * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
19679  * Implementation Notes for IE
19680  * ---------------------------
19681  * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
19682  * browsers.  However, the parsed components will not be set if the URL assigned did not specify
19683  * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
19684  * work around that by performing the parsing in a 2nd step by taking a previously normalized
19685  * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
19686  * properties such as protocol, hostname, port, etc.
19688  * References:
19689  *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
19690  *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
19691  *   http://url.spec.whatwg.org/#urlutils
19692  *   https://github.com/angular/angular.js/pull/2902
19693  *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
19695  * @kind function
19696  * @param {string} url The URL to be parsed.
19697  * @description Normalizes and parses a URL.
19698  * @returns {object} Returns the normalized URL as a dictionary.
19700  *   | member name   | Description    |
19701  *   |---------------|----------------|
19702  *   | href          | A normalized version of the provided URL if it was not an absolute URL |
19703  *   | protocol      | The protocol including the trailing colon                              |
19704  *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
19705  *   | search        | The search params, minus the question mark                             |
19706  *   | hash          | The hash string, minus the hash symbol
19707  *   | hostname      | The hostname
19708  *   | port          | The port, without ":"
19709  *   | pathname      | The pathname, beginning with "/"
19711  */
19712 function urlResolve(url) {
19713   var href = url;
19715   if (msie) {
19716     // Normalize before parse.  Refer Implementation Notes on why this is
19717     // done in two steps on IE.
19718     urlParsingNode.setAttribute("href", href);
19719     href = urlParsingNode.href;
19720   }
19722   urlParsingNode.setAttribute('href', href);
19724   // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
19725   return {
19726     href: urlParsingNode.href,
19727     protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
19728     host: urlParsingNode.host,
19729     search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
19730     hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
19731     hostname: urlParsingNode.hostname,
19732     port: urlParsingNode.port,
19733     pathname: (urlParsingNode.pathname.charAt(0) === '/')
19734       ? urlParsingNode.pathname
19735       : '/' + urlParsingNode.pathname
19736   };
19740  * Parse a request URL and determine whether this is a same-origin request as the application document.
19742  * @param {string|object} requestUrl The url of the request as a string that will be resolved
19743  * or a parsed URL object.
19744  * @returns {boolean} Whether the request is for the same origin as the application document.
19745  */
19746 function urlIsSameOrigin(requestUrl) {
19747   var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
19748   return (parsed.protocol === originUrl.protocol &&
19749           parsed.host === originUrl.host);
19753  * @ngdoc service
19754  * @name $window
19756  * @description
19757  * A reference to the browser's `window` object. While `window`
19758  * is globally available in JavaScript, it causes testability problems, because
19759  * it is a global variable. In angular we always refer to it through the
19760  * `$window` service, so it may be overridden, removed or mocked for testing.
19762  * Expressions, like the one defined for the `ngClick` directive in the example
19763  * below, are evaluated with respect to the current scope.  Therefore, there is
19764  * no risk of inadvertently coding in a dependency on a global value in such an
19765  * expression.
19767  * @example
19768    <example module="windowExample">
19769      <file name="index.html">
19770        <script>
19771          angular.module('windowExample', [])
19772            .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
19773              $scope.greeting = 'Hello, World!';
19774              $scope.doGreeting = function(greeting) {
19775                $window.alert(greeting);
19776              };
19777            }]);
19778        </script>
19779        <div ng-controller="ExampleController">
19780          <input type="text" ng-model="greeting" aria-label="greeting" />
19781          <button ng-click="doGreeting(greeting)">ALERT</button>
19782        </div>
19783      </file>
19784      <file name="protractor.js" type="protractor">
19785       it('should display the greeting in the input box', function() {
19786        element(by.model('greeting')).sendKeys('Hello, E2E Tests');
19787        // If we click the button it will block the test runner
19788        // element(':button').click();
19789       });
19790      </file>
19791    </example>
19792  */
19793 function $WindowProvider() {
19794   this.$get = valueFn(window);
19798  * @name $$cookieReader
19799  * @requires $document
19801  * @description
19802  * This is a private service for reading cookies used by $http and ngCookies
19804  * @return {Object} a key/value map of the current cookies
19805  */
19806 function $$CookieReader($document) {
19807   var rawDocument = $document[0] || {};
19808   var lastCookies = {};
19809   var lastCookieString = '';
19811   function safeDecodeURIComponent(str) {
19812     try {
19813       return decodeURIComponent(str);
19814     } catch (e) {
19815       return str;
19816     }
19817   }
19819   return function() {
19820     var cookieArray, cookie, i, index, name;
19821     var currentCookieString = rawDocument.cookie || '';
19823     if (currentCookieString !== lastCookieString) {
19824       lastCookieString = currentCookieString;
19825       cookieArray = lastCookieString.split('; ');
19826       lastCookies = {};
19828       for (i = 0; i < cookieArray.length; i++) {
19829         cookie = cookieArray[i];
19830         index = cookie.indexOf('=');
19831         if (index > 0) { //ignore nameless cookies
19832           name = safeDecodeURIComponent(cookie.substring(0, index));
19833           // the first value that is seen for a cookie is the most
19834           // specific one.  values for the same cookie name that
19835           // follow are for less specific paths.
19836           if (isUndefined(lastCookies[name])) {
19837             lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
19838           }
19839         }
19840       }
19841     }
19842     return lastCookies;
19843   };
19846 $$CookieReader.$inject = ['$document'];
19848 function $$CookieReaderProvider() {
19849   this.$get = $$CookieReader;
19852 /* global currencyFilter: true,
19853  dateFilter: true,
19854  filterFilter: true,
19855  jsonFilter: true,
19856  limitToFilter: true,
19857  lowercaseFilter: true,
19858  numberFilter: true,
19859  orderByFilter: true,
19860  uppercaseFilter: true,
19861  */
19864  * @ngdoc provider
19865  * @name $filterProvider
19866  * @description
19868  * Filters are just functions which transform input to an output. However filters need to be
19869  * Dependency Injected. To achieve this a filter definition consists of a factory function which is
19870  * annotated with dependencies and is responsible for creating a filter function.
19872  * <div class="alert alert-warning">
19873  * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
19874  * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
19875  * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
19876  * (`myapp_subsection_filterx`).
19877  * </div>
19879  * ```js
19880  *   // Filter registration
19881  *   function MyModule($provide, $filterProvider) {
19882  *     // create a service to demonstrate injection (not always needed)
19883  *     $provide.value('greet', function(name){
19884  *       return 'Hello ' + name + '!';
19885  *     });
19887  *     // register a filter factory which uses the
19888  *     // greet service to demonstrate DI.
19889  *     $filterProvider.register('greet', function(greet){
19890  *       // return the filter function which uses the greet service
19891  *       // to generate salutation
19892  *       return function(text) {
19893  *         // filters need to be forgiving so check input validity
19894  *         return text && greet(text) || text;
19895  *       };
19896  *     });
19897  *   }
19898  * ```
19900  * The filter function is registered with the `$injector` under the filter name suffix with
19901  * `Filter`.
19903  * ```js
19904  *   it('should be the same instance', inject(
19905  *     function($filterProvider) {
19906  *       $filterProvider.register('reverse', function(){
19907  *         return ...;
19908  *       });
19909  *     },
19910  *     function($filter, reverseFilter) {
19911  *       expect($filter('reverse')).toBe(reverseFilter);
19912  *     });
19913  * ```
19916  * For more information about how angular filters work, and how to create your own filters, see
19917  * {@link guide/filter Filters} in the Angular Developer Guide.
19918  */
19921  * @ngdoc service
19922  * @name $filter
19923  * @kind function
19924  * @description
19925  * Filters are used for formatting data displayed to the user.
19927  * The general syntax in templates is as follows:
19929  *         {{ expression [| filter_name[:parameter_value] ... ] }}
19931  * @param {String} name Name of the filter function to retrieve
19932  * @return {Function} the filter function
19933  * @example
19934    <example name="$filter" module="filterExample">
19935      <file name="index.html">
19936        <div ng-controller="MainCtrl">
19937         <h3>{{ originalText }}</h3>
19938         <h3>{{ filteredText }}</h3>
19939        </div>
19940      </file>
19942      <file name="script.js">
19943       angular.module('filterExample', [])
19944       .controller('MainCtrl', function($scope, $filter) {
19945         $scope.originalText = 'hello';
19946         $scope.filteredText = $filter('uppercase')($scope.originalText);
19947       });
19948      </file>
19949    </example>
19950   */
19951 $FilterProvider.$inject = ['$provide'];
19952 function $FilterProvider($provide) {
19953   var suffix = 'Filter';
19955   /**
19956    * @ngdoc method
19957    * @name $filterProvider#register
19958    * @param {string|Object} name Name of the filter function, or an object map of filters where
19959    *    the keys are the filter names and the values are the filter factories.
19960    *
19961    *    <div class="alert alert-warning">
19962    *    **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
19963    *    Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
19964    *    your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
19965    *    (`myapp_subsection_filterx`).
19966    *    </div>
19967     * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
19968    * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
19969    *    of the registered filter instances.
19970    */
19971   function register(name, factory) {
19972     if (isObject(name)) {
19973       var filters = {};
19974       forEach(name, function(filter, key) {
19975         filters[key] = register(key, filter);
19976       });
19977       return filters;
19978     } else {
19979       return $provide.factory(name + suffix, factory);
19980     }
19981   }
19982   this.register = register;
19984   this.$get = ['$injector', function($injector) {
19985     return function(name) {
19986       return $injector.get(name + suffix);
19987     };
19988   }];
19990   ////////////////////////////////////////
19992   /* global
19993     currencyFilter: false,
19994     dateFilter: false,
19995     filterFilter: false,
19996     jsonFilter: false,
19997     limitToFilter: false,
19998     lowercaseFilter: false,
19999     numberFilter: false,
20000     orderByFilter: false,
20001     uppercaseFilter: false,
20002   */
20004   register('currency', currencyFilter);
20005   register('date', dateFilter);
20006   register('filter', filterFilter);
20007   register('json', jsonFilter);
20008   register('limitTo', limitToFilter);
20009   register('lowercase', lowercaseFilter);
20010   register('number', numberFilter);
20011   register('orderBy', orderByFilter);
20012   register('uppercase', uppercaseFilter);
20016  * @ngdoc filter
20017  * @name filter
20018  * @kind function
20020  * @description
20021  * Selects a subset of items from `array` and returns it as a new array.
20023  * @param {Array} array The source array.
20024  * @param {string|Object|function()} expression The predicate to be used for selecting items from
20025  *   `array`.
20027  *   Can be one of:
20029  *   - `string`: The string is used for matching against the contents of the `array`. All strings or
20030  *     objects with string properties in `array` that match this string will be returned. This also
20031  *     applies to nested object properties.
20032  *     The predicate can be negated by prefixing the string with `!`.
20034  *   - `Object`: A pattern object can be used to filter specific properties on objects contained
20035  *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
20036  *     which have property `name` containing "M" and property `phone` containing "1". A special
20037  *     property name (`$` by default) can be used (e.g. as in `{$: "text"}`) to accept a match
20038  *     against any property of the object or its nested object properties. That's equivalent to the
20039  *     simple substring match with a `string` as described above. The special property name can be
20040  *     overwritten, using the `anyPropertyKey` parameter.
20041  *     The predicate can be negated by prefixing the string with `!`.
20042  *     For example `{name: "!M"}` predicate will return an array of items which have property `name`
20043  *     not containing "M".
20045  *     Note that a named property will match properties on the same level only, while the special
20046  *     `$` property will match properties on the same level or deeper. E.g. an array item like
20047  *     `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
20048  *     **will** be matched by `{$: 'John'}`.
20050  *   - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
20051  *     The function is called for each element of the array, with the element, its index, and
20052  *     the entire array itself as arguments.
20054  *     The final result is an array of those elements that the predicate returned true for.
20056  * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
20057  *     determining if the expected value (from the filter expression) and actual value (from
20058  *     the object in the array) should be considered a match.
20060  *   Can be one of:
20062  *   - `function(actual, expected)`:
20063  *     The function will be given the object value and the predicate value to compare and
20064  *     should return true if both values should be considered equal.
20066  *   - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
20067  *     This is essentially strict comparison of expected and actual.
20069  *   - `false|undefined`: A short hand for a function which will look for a substring match in case
20070  *     insensitive way.
20072  *     Primitive values are converted to strings. Objects are not compared against primitives,
20073  *     unless they have a custom `toString` method (e.g. `Date` objects).
20075  * @param {string=} anyPropertyKey The special property name that matches against any property.
20076  *     By default `$`.
20078  * @example
20079    <example>
20080      <file name="index.html">
20081        <div ng-init="friends = [{name:'John', phone:'555-1276'},
20082                                 {name:'Mary', phone:'800-BIG-MARY'},
20083                                 {name:'Mike', phone:'555-4321'},
20084                                 {name:'Adam', phone:'555-5678'},
20085                                 {name:'Julie', phone:'555-8765'},
20086                                 {name:'Juliette', phone:'555-5678'}]"></div>
20088        <label>Search: <input ng-model="searchText"></label>
20089        <table id="searchTextResults">
20090          <tr><th>Name</th><th>Phone</th></tr>
20091          <tr ng-repeat="friend in friends | filter:searchText">
20092            <td>{{friend.name}}</td>
20093            <td>{{friend.phone}}</td>
20094          </tr>
20095        </table>
20096        <hr>
20097        <label>Any: <input ng-model="search.$"></label> <br>
20098        <label>Name only <input ng-model="search.name"></label><br>
20099        <label>Phone only <input ng-model="search.phone"></label><br>
20100        <label>Equality <input type="checkbox" ng-model="strict"></label><br>
20101        <table id="searchObjResults">
20102          <tr><th>Name</th><th>Phone</th></tr>
20103          <tr ng-repeat="friendObj in friends | filter:search:strict">
20104            <td>{{friendObj.name}}</td>
20105            <td>{{friendObj.phone}}</td>
20106          </tr>
20107        </table>
20108      </file>
20109      <file name="protractor.js" type="protractor">
20110        var expectFriendNames = function(expectedNames, key) {
20111          element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
20112            arr.forEach(function(wd, i) {
20113              expect(wd.getText()).toMatch(expectedNames[i]);
20114            });
20115          });
20116        };
20118        it('should search across all fields when filtering with a string', function() {
20119          var searchText = element(by.model('searchText'));
20120          searchText.clear();
20121          searchText.sendKeys('m');
20122          expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
20124          searchText.clear();
20125          searchText.sendKeys('76');
20126          expectFriendNames(['John', 'Julie'], 'friend');
20127        });
20129        it('should search in specific fields when filtering with a predicate object', function() {
20130          var searchAny = element(by.model('search.$'));
20131          searchAny.clear();
20132          searchAny.sendKeys('i');
20133          expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
20134        });
20135        it('should use a equal comparison when comparator is true', function() {
20136          var searchName = element(by.model('search.name'));
20137          var strict = element(by.model('strict'));
20138          searchName.clear();
20139          searchName.sendKeys('Julie');
20140          strict.click();
20141          expectFriendNames(['Julie'], 'friendObj');
20142        });
20143      </file>
20144    </example>
20145  */
20147 function filterFilter() {
20148   return function(array, expression, comparator, anyPropertyKey) {
20149     if (!isArrayLike(array)) {
20150       if (array == null) {
20151         return array;
20152       } else {
20153         throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
20154       }
20155     }
20157     anyPropertyKey = anyPropertyKey || '$';
20158     var expressionType = getTypeForFilter(expression);
20159     var predicateFn;
20160     var matchAgainstAnyProp;
20162     switch (expressionType) {
20163       case 'function':
20164         predicateFn = expression;
20165         break;
20166       case 'boolean':
20167       case 'null':
20168       case 'number':
20169       case 'string':
20170         matchAgainstAnyProp = true;
20171         //jshint -W086
20172       case 'object':
20173         //jshint +W086
20174         predicateFn = createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp);
20175         break;
20176       default:
20177         return array;
20178     }
20180     return Array.prototype.filter.call(array, predicateFn);
20181   };
20184 // Helper functions for `filterFilter`
20185 function createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp) {
20186   var shouldMatchPrimitives = isObject(expression) && (anyPropertyKey in expression);
20187   var predicateFn;
20189   if (comparator === true) {
20190     comparator = equals;
20191   } else if (!isFunction(comparator)) {
20192     comparator = function(actual, expected) {
20193       if (isUndefined(actual)) {
20194         // No substring matching against `undefined`
20195         return false;
20196       }
20197       if ((actual === null) || (expected === null)) {
20198         // No substring matching against `null`; only match against `null`
20199         return actual === expected;
20200       }
20201       if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
20202         // Should not compare primitives against objects, unless they have custom `toString` method
20203         return false;
20204       }
20206       actual = lowercase('' + actual);
20207       expected = lowercase('' + expected);
20208       return actual.indexOf(expected) !== -1;
20209     };
20210   }
20212   predicateFn = function(item) {
20213     if (shouldMatchPrimitives && !isObject(item)) {
20214       return deepCompare(item, expression[anyPropertyKey], comparator, anyPropertyKey, false);
20215     }
20216     return deepCompare(item, expression, comparator, anyPropertyKey, matchAgainstAnyProp);
20217   };
20219   return predicateFn;
20222 function deepCompare(actual, expected, comparator, anyPropertyKey, matchAgainstAnyProp, dontMatchWholeObject) {
20223   var actualType = getTypeForFilter(actual);
20224   var expectedType = getTypeForFilter(expected);
20226   if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
20227     return !deepCompare(actual, expected.substring(1), comparator, anyPropertyKey, matchAgainstAnyProp);
20228   } else if (isArray(actual)) {
20229     // In case `actual` is an array, consider it a match
20230     // if ANY of it's items matches `expected`
20231     return actual.some(function(item) {
20232       return deepCompare(item, expected, comparator, anyPropertyKey, matchAgainstAnyProp);
20233     });
20234   }
20236   switch (actualType) {
20237     case 'object':
20238       var key;
20239       if (matchAgainstAnyProp) {
20240         for (key in actual) {
20241           if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) {
20242             return true;
20243           }
20244         }
20245         return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, anyPropertyKey, false);
20246       } else if (expectedType === 'object') {
20247         for (key in expected) {
20248           var expectedVal = expected[key];
20249           if (isFunction(expectedVal) || isUndefined(expectedVal)) {
20250             continue;
20251           }
20253           var matchAnyProperty = key === anyPropertyKey;
20254           var actualVal = matchAnyProperty ? actual : actual[key];
20255           if (!deepCompare(actualVal, expectedVal, comparator, anyPropertyKey, matchAnyProperty, matchAnyProperty)) {
20256             return false;
20257           }
20258         }
20259         return true;
20260       } else {
20261         return comparator(actual, expected);
20262       }
20263       break;
20264     case 'function':
20265       return false;
20266     default:
20267       return comparator(actual, expected);
20268   }
20271 // Used for easily differentiating between `null` and actual `object`
20272 function getTypeForFilter(val) {
20273   return (val === null) ? 'null' : typeof val;
20276 var MAX_DIGITS = 22;
20277 var DECIMAL_SEP = '.';
20278 var ZERO_CHAR = '0';
20281  * @ngdoc filter
20282  * @name currency
20283  * @kind function
20285  * @description
20286  * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
20287  * symbol for current locale is used.
20289  * @param {number} amount Input to filter.
20290  * @param {string=} symbol Currency symbol or identifier to be displayed.
20291  * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
20292  * @returns {string} Formatted number.
20295  * @example
20296    <example module="currencyExample">
20297      <file name="index.html">
20298        <script>
20299          angular.module('currencyExample', [])
20300            .controller('ExampleController', ['$scope', function($scope) {
20301              $scope.amount = 1234.56;
20302            }]);
20303        </script>
20304        <div ng-controller="ExampleController">
20305          <input type="number" ng-model="amount" aria-label="amount"> <br>
20306          default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
20307          custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
20308          no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
20309        </div>
20310      </file>
20311      <file name="protractor.js" type="protractor">
20312        it('should init with 1234.56', function() {
20313          expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
20314          expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
20315          expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
20316        });
20317        it('should update', function() {
20318          if (browser.params.browser == 'safari') {
20319            // Safari does not understand the minus key. See
20320            // https://github.com/angular/protractor/issues/481
20321            return;
20322          }
20323          element(by.model('amount')).clear();
20324          element(by.model('amount')).sendKeys('-1234');
20325          expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
20326          expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
20327          expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
20328        });
20329      </file>
20330    </example>
20331  */
20332 currencyFilter.$inject = ['$locale'];
20333 function currencyFilter($locale) {
20334   var formats = $locale.NUMBER_FORMATS;
20335   return function(amount, currencySymbol, fractionSize) {
20336     if (isUndefined(currencySymbol)) {
20337       currencySymbol = formats.CURRENCY_SYM;
20338     }
20340     if (isUndefined(fractionSize)) {
20341       fractionSize = formats.PATTERNS[1].maxFrac;
20342     }
20344     // if null or undefined pass it through
20345     return (amount == null)
20346         ? amount
20347         : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
20348             replace(/\u00A4/g, currencySymbol);
20349   };
20353  * @ngdoc filter
20354  * @name number
20355  * @kind function
20357  * @description
20358  * Formats a number as text.
20360  * If the input is null or undefined, it will just be returned.
20361  * If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively.
20362  * If the input is not a number an empty string is returned.
20365  * @param {number|string} number Number to format.
20366  * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
20367  * If this is not provided then the fraction size is computed from the current locale's number
20368  * formatting pattern. In the case of the default locale, it will be 3.
20369  * @returns {string} Number rounded to `fractionSize` appropriately formatted based on the current
20370  *                   locale (e.g., in the en_US locale it will have "." as the decimal separator and
20371  *                   include "," group separators after each third digit).
20373  * @example
20374    <example module="numberFilterExample">
20375      <file name="index.html">
20376        <script>
20377          angular.module('numberFilterExample', [])
20378            .controller('ExampleController', ['$scope', function($scope) {
20379              $scope.val = 1234.56789;
20380            }]);
20381        </script>
20382        <div ng-controller="ExampleController">
20383          <label>Enter number: <input ng-model='val'></label><br>
20384          Default formatting: <span id='number-default'>{{val | number}}</span><br>
20385          No fractions: <span>{{val | number:0}}</span><br>
20386          Negative number: <span>{{-val | number:4}}</span>
20387        </div>
20388      </file>
20389      <file name="protractor.js" type="protractor">
20390        it('should format numbers', function() {
20391          expect(element(by.id('number-default')).getText()).toBe('1,234.568');
20392          expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
20393          expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
20394        });
20396        it('should update', function() {
20397          element(by.model('val')).clear();
20398          element(by.model('val')).sendKeys('3374.333');
20399          expect(element(by.id('number-default')).getText()).toBe('3,374.333');
20400          expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
20401          expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
20402       });
20403      </file>
20404    </example>
20405  */
20406 numberFilter.$inject = ['$locale'];
20407 function numberFilter($locale) {
20408   var formats = $locale.NUMBER_FORMATS;
20409   return function(number, fractionSize) {
20411     // if null or undefined pass it through
20412     return (number == null)
20413         ? number
20414         : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
20415                        fractionSize);
20416   };
20420  * Parse a number (as a string) into three components that can be used
20421  * for formatting the number.
20423  * (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/)
20425  * @param  {string} numStr The number to parse
20426  * @return {object} An object describing this number, containing the following keys:
20427  *  - d : an array of digits containing leading zeros as necessary
20428  *  - i : the number of the digits in `d` that are to the left of the decimal point
20429  *  - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d`
20431  */
20432 function parse(numStr) {
20433   var exponent = 0, digits, numberOfIntegerDigits;
20434   var i, j, zeros;
20436   // Decimal point?
20437   if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) {
20438     numStr = numStr.replace(DECIMAL_SEP, '');
20439   }
20441   // Exponential form?
20442   if ((i = numStr.search(/e/i)) > 0) {
20443     // Work out the exponent.
20444     if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i;
20445     numberOfIntegerDigits += +numStr.slice(i + 1);
20446     numStr = numStr.substring(0, i);
20447   } else if (numberOfIntegerDigits < 0) {
20448     // There was no decimal point or exponent so it is an integer.
20449     numberOfIntegerDigits = numStr.length;
20450   }
20452   // Count the number of leading zeros.
20453   for (i = 0; numStr.charAt(i) == ZERO_CHAR; i++) {/* jshint noempty: false */}
20455   if (i == (zeros = numStr.length)) {
20456     // The digits are all zero.
20457     digits = [0];
20458     numberOfIntegerDigits = 1;
20459   } else {
20460     // Count the number of trailing zeros
20461     zeros--;
20462     while (numStr.charAt(zeros) == ZERO_CHAR) zeros--;
20464     // Trailing zeros are insignificant so ignore them
20465     numberOfIntegerDigits -= i;
20466     digits = [];
20467     // Convert string to array of digits without leading/trailing zeros.
20468     for (j = 0; i <= zeros; i++, j++) {
20469       digits[j] = +numStr.charAt(i);
20470     }
20471   }
20473   // If the number overflows the maximum allowed digits then use an exponent.
20474   if (numberOfIntegerDigits > MAX_DIGITS) {
20475     digits = digits.splice(0, MAX_DIGITS - 1);
20476     exponent = numberOfIntegerDigits - 1;
20477     numberOfIntegerDigits = 1;
20478   }
20480   return { d: digits, e: exponent, i: numberOfIntegerDigits };
20484  * Round the parsed number to the specified number of decimal places
20485  * This function changed the parsedNumber in-place
20486  */
20487 function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
20488     var digits = parsedNumber.d;
20489     var fractionLen = digits.length - parsedNumber.i;
20491     // determine fractionSize if it is not specified; `+fractionSize` converts it to a number
20492     fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize;
20494     // The index of the digit to where rounding is to occur
20495     var roundAt = fractionSize + parsedNumber.i;
20496     var digit = digits[roundAt];
20498     if (roundAt > 0) {
20499       // Drop fractional digits beyond `roundAt`
20500       digits.splice(Math.max(parsedNumber.i, roundAt));
20502       // Set non-fractional digits beyond `roundAt` to 0
20503       for (var j = roundAt; j < digits.length; j++) {
20504         digits[j] = 0;
20505       }
20506     } else {
20507       // We rounded to zero so reset the parsedNumber
20508       fractionLen = Math.max(0, fractionLen);
20509       parsedNumber.i = 1;
20510       digits.length = Math.max(1, roundAt = fractionSize + 1);
20511       digits[0] = 0;
20512       for (var i = 1; i < roundAt; i++) digits[i] = 0;
20513     }
20515     if (digit >= 5) {
20516       if (roundAt - 1 < 0) {
20517         for (var k = 0; k > roundAt; k--) {
20518           digits.unshift(0);
20519           parsedNumber.i++;
20520         }
20521         digits.unshift(1);
20522         parsedNumber.i++;
20523       } else {
20524         digits[roundAt - 1]++;
20525       }
20526     }
20528     // Pad out with zeros to get the required fraction length
20529     for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0);
20532     // Do any carrying, e.g. a digit was rounded up to 10
20533     var carry = digits.reduceRight(function(carry, d, i, digits) {
20534       d = d + carry;
20535       digits[i] = d % 10;
20536       return Math.floor(d / 10);
20537     }, 0);
20538     if (carry) {
20539       digits.unshift(carry);
20540       parsedNumber.i++;
20541     }
20545  * Format a number into a string
20546  * @param  {number} number       The number to format
20547  * @param  {{
20548  *           minFrac, // the minimum number of digits required in the fraction part of the number
20549  *           maxFrac, // the maximum number of digits required in the fraction part of the number
20550  *           gSize,   // number of digits in each group of separated digits
20551  *           lgSize,  // number of digits in the last group of digits before the decimal separator
20552  *           negPre,  // the string to go in front of a negative number (e.g. `-` or `(`))
20553  *           posPre,  // the string to go in front of a positive number
20554  *           negSuf,  // the string to go after a negative number (e.g. `)`)
20555  *           posSuf   // the string to go after a positive number
20556  *         }} pattern
20557  * @param  {string} groupSep     The string to separate groups of number (e.g. `,`)
20558  * @param  {string} decimalSep   The string to act as the decimal separator (e.g. `.`)
20559  * @param  {[type]} fractionSize The size of the fractional part of the number
20560  * @return {string}              The number formatted as a string
20561  */
20562 function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
20564   if (!(isString(number) || isNumber(number)) || isNaN(number)) return '';
20566   var isInfinity = !isFinite(number);
20567   var isZero = false;
20568   var numStr = Math.abs(number) + '',
20569       formattedText = '',
20570       parsedNumber;
20572   if (isInfinity) {
20573     formattedText = '\u221e';
20574   } else {
20575     parsedNumber = parse(numStr);
20577     roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac);
20579     var digits = parsedNumber.d;
20580     var integerLen = parsedNumber.i;
20581     var exponent = parsedNumber.e;
20582     var decimals = [];
20583     isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true);
20585     // pad zeros for small numbers
20586     while (integerLen < 0) {
20587       digits.unshift(0);
20588       integerLen++;
20589     }
20591     // extract decimals digits
20592     if (integerLen > 0) {
20593       decimals = digits.splice(integerLen, digits.length);
20594     } else {
20595       decimals = digits;
20596       digits = [0];
20597     }
20599     // format the integer digits with grouping separators
20600     var groups = [];
20601     if (digits.length >= pattern.lgSize) {
20602       groups.unshift(digits.splice(-pattern.lgSize, digits.length).join(''));
20603     }
20604     while (digits.length > pattern.gSize) {
20605       groups.unshift(digits.splice(-pattern.gSize, digits.length).join(''));
20606     }
20607     if (digits.length) {
20608       groups.unshift(digits.join(''));
20609     }
20610     formattedText = groups.join(groupSep);
20612     // append the decimal digits
20613     if (decimals.length) {
20614       formattedText += decimalSep + decimals.join('');
20615     }
20617     if (exponent) {
20618       formattedText += 'e+' + exponent;
20619     }
20620   }
20621   if (number < 0 && !isZero) {
20622     return pattern.negPre + formattedText + pattern.negSuf;
20623   } else {
20624     return pattern.posPre + formattedText + pattern.posSuf;
20625   }
20628 function padNumber(num, digits, trim, negWrap) {
20629   var neg = '';
20630   if (num < 0 || (negWrap && num <= 0)) {
20631     if (negWrap) {
20632       num = -num + 1;
20633     } else {
20634       num = -num;
20635       neg = '-';
20636     }
20637   }
20638   num = '' + num;
20639   while (num.length < digits) num = ZERO_CHAR + num;
20640   if (trim) {
20641     num = num.substr(num.length - digits);
20642   }
20643   return neg + num;
20647 function dateGetter(name, size, offset, trim, negWrap) {
20648   offset = offset || 0;
20649   return function(date) {
20650     var value = date['get' + name]();
20651     if (offset > 0 || value > -offset) {
20652       value += offset;
20653     }
20654     if (value === 0 && offset == -12) value = 12;
20655     return padNumber(value, size, trim, negWrap);
20656   };
20659 function dateStrGetter(name, shortForm, standAlone) {
20660   return function(date, formats) {
20661     var value = date['get' + name]();
20662     var propPrefix = (standAlone ? 'STANDALONE' : '') + (shortForm ? 'SHORT' : '');
20663     var get = uppercase(propPrefix + name);
20665     return formats[get][value];
20666   };
20669 function timeZoneGetter(date, formats, offset) {
20670   var zone = -1 * offset;
20671   var paddedZone = (zone >= 0) ? "+" : "";
20673   paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
20674                 padNumber(Math.abs(zone % 60), 2);
20676   return paddedZone;
20679 function getFirstThursdayOfYear(year) {
20680     // 0 = index of January
20681     var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
20682     // 4 = index of Thursday (+1 to account for 1st = 5)
20683     // 11 = index of *next* Thursday (+1 account for 1st = 12)
20684     return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
20687 function getThursdayThisWeek(datetime) {
20688     return new Date(datetime.getFullYear(), datetime.getMonth(),
20689       // 4 = index of Thursday
20690       datetime.getDate() + (4 - datetime.getDay()));
20693 function weekGetter(size) {
20694    return function(date) {
20695       var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
20696          thisThurs = getThursdayThisWeek(date);
20698       var diff = +thisThurs - +firstThurs,
20699          result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
20701       return padNumber(result, size);
20702    };
20705 function ampmGetter(date, formats) {
20706   return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
20709 function eraGetter(date, formats) {
20710   return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
20713 function longEraGetter(date, formats) {
20714   return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
20717 var DATE_FORMATS = {
20718   yyyy: dateGetter('FullYear', 4, 0, false, true),
20719     yy: dateGetter('FullYear', 2, 0, true, true),
20720      y: dateGetter('FullYear', 1, 0, false, true),
20721   MMMM: dateStrGetter('Month'),
20722    MMM: dateStrGetter('Month', true),
20723     MM: dateGetter('Month', 2, 1),
20724      M: dateGetter('Month', 1, 1),
20725   LLLL: dateStrGetter('Month', false, true),
20726     dd: dateGetter('Date', 2),
20727      d: dateGetter('Date', 1),
20728     HH: dateGetter('Hours', 2),
20729      H: dateGetter('Hours', 1),
20730     hh: dateGetter('Hours', 2, -12),
20731      h: dateGetter('Hours', 1, -12),
20732     mm: dateGetter('Minutes', 2),
20733      m: dateGetter('Minutes', 1),
20734     ss: dateGetter('Seconds', 2),
20735      s: dateGetter('Seconds', 1),
20736      // while ISO 8601 requires fractions to be prefixed with `.` or `,`
20737      // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
20738    sss: dateGetter('Milliseconds', 3),
20739   EEEE: dateStrGetter('Day'),
20740    EEE: dateStrGetter('Day', true),
20741      a: ampmGetter,
20742      Z: timeZoneGetter,
20743     ww: weekGetter(2),
20744      w: weekGetter(1),
20745      G: eraGetter,
20746      GG: eraGetter,
20747      GGG: eraGetter,
20748      GGGG: longEraGetter
20751 var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
20752     NUMBER_STRING = /^\-?\d+$/;
20755  * @ngdoc filter
20756  * @name date
20757  * @kind function
20759  * @description
20760  *   Formats `date` to a string based on the requested `format`.
20762  *   `format` string can be composed of the following elements:
20764  *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
20765  *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
20766  *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
20767  *   * `'MMMM'`: Month in year (January-December)
20768  *   * `'MMM'`: Month in year (Jan-Dec)
20769  *   * `'MM'`: Month in year, padded (01-12)
20770  *   * `'M'`: Month in year (1-12)
20771  *   * `'LLLL'`: Stand-alone month in year (January-December)
20772  *   * `'dd'`: Day in month, padded (01-31)
20773  *   * `'d'`: Day in month (1-31)
20774  *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
20775  *   * `'EEE'`: Day in Week, (Sun-Sat)
20776  *   * `'HH'`: Hour in day, padded (00-23)
20777  *   * `'H'`: Hour in day (0-23)
20778  *   * `'hh'`: Hour in AM/PM, padded (01-12)
20779  *   * `'h'`: Hour in AM/PM, (1-12)
20780  *   * `'mm'`: Minute in hour, padded (00-59)
20781  *   * `'m'`: Minute in hour (0-59)
20782  *   * `'ss'`: Second in minute, padded (00-59)
20783  *   * `'s'`: Second in minute (0-59)
20784  *   * `'sss'`: Millisecond in second, padded (000-999)
20785  *   * `'a'`: AM/PM marker
20786  *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
20787  *   * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
20788  *   * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
20789  *   * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
20790  *   * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
20792  *   `format` string can also be one of the following predefined
20793  *   {@link guide/i18n localizable formats}:
20795  *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
20796  *     (e.g. Sep 3, 2010 12:05:08 PM)
20797  *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)
20798  *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
20799  *     (e.g. Friday, September 3, 2010)
20800  *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
20801  *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
20802  *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
20803  *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
20804  *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
20806  *   `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
20807  *   `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
20808  *   (e.g. `"h 'o''clock'"`).
20810  * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
20811  *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
20812  *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
20813  *    specified in the string input, the time is considered to be in the local timezone.
20814  * @param {string=} format Formatting rules (see Description). If not specified,
20815  *    `mediumDate` is used.
20816  * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
20817  *    continental US time zone abbreviations, but for general use, use a time zone offset, for
20818  *    example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
20819  *    If not specified, the timezone of the browser will be used.
20820  * @returns {string} Formatted string or the input if input is not recognized as date/millis.
20822  * @example
20823    <example>
20824      <file name="index.html">
20825        <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
20826            <span>{{1288323623006 | date:'medium'}}</span><br>
20827        <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
20828           <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
20829        <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
20830           <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
20831        <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
20832           <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
20833      </file>
20834      <file name="protractor.js" type="protractor">
20835        it('should format date', function() {
20836          expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
20837             toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
20838          expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
20839             toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
20840          expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
20841             toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
20842          expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
20843             toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
20844        });
20845      </file>
20846    </example>
20847  */
20848 dateFilter.$inject = ['$locale'];
20849 function dateFilter($locale) {
20852   var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
20853                      // 1        2       3         4          5          6          7          8  9     10      11
20854   function jsonStringToDate(string) {
20855     var match;
20856     if (match = string.match(R_ISO8601_STR)) {
20857       var date = new Date(0),
20858           tzHour = 0,
20859           tzMin  = 0,
20860           dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
20861           timeSetter = match[8] ? date.setUTCHours : date.setHours;
20863       if (match[9]) {
20864         tzHour = toInt(match[9] + match[10]);
20865         tzMin = toInt(match[9] + match[11]);
20866       }
20867       dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
20868       var h = toInt(match[4] || 0) - tzHour;
20869       var m = toInt(match[5] || 0) - tzMin;
20870       var s = toInt(match[6] || 0);
20871       var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
20872       timeSetter.call(date, h, m, s, ms);
20873       return date;
20874     }
20875     return string;
20876   }
20879   return function(date, format, timezone) {
20880     var text = '',
20881         parts = [],
20882         fn, match;
20884     format = format || 'mediumDate';
20885     format = $locale.DATETIME_FORMATS[format] || format;
20886     if (isString(date)) {
20887       date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
20888     }
20890     if (isNumber(date)) {
20891       date = new Date(date);
20892     }
20894     if (!isDate(date) || !isFinite(date.getTime())) {
20895       return date;
20896     }
20898     while (format) {
20899       match = DATE_FORMATS_SPLIT.exec(format);
20900       if (match) {
20901         parts = concat(parts, match, 1);
20902         format = parts.pop();
20903       } else {
20904         parts.push(format);
20905         format = null;
20906       }
20907     }
20909     var dateTimezoneOffset = date.getTimezoneOffset();
20910     if (timezone) {
20911       dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
20912       date = convertTimezoneToLocal(date, timezone, true);
20913     }
20914     forEach(parts, function(value) {
20915       fn = DATE_FORMATS[value];
20916       text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
20917                  : value === "''" ? "'" : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
20918     });
20920     return text;
20921   };
20926  * @ngdoc filter
20927  * @name json
20928  * @kind function
20930  * @description
20931  *   Allows you to convert a JavaScript object into JSON string.
20933  *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
20934  *   the binding is automatically converted to JSON.
20936  * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
20937  * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
20938  * @returns {string} JSON string.
20941  * @example
20942    <example>
20943      <file name="index.html">
20944        <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
20945        <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
20946      </file>
20947      <file name="protractor.js" type="protractor">
20948        it('should jsonify filtered objects', function() {
20949          expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n  "name": ?"value"\n}/);
20950          expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n    "name": ?"value"\n}/);
20951        });
20952      </file>
20953    </example>
20955  */
20956 function jsonFilter() {
20957   return function(object, spacing) {
20958     if (isUndefined(spacing)) {
20959         spacing = 2;
20960     }
20961     return toJson(object, spacing);
20962   };
20967  * @ngdoc filter
20968  * @name lowercase
20969  * @kind function
20970  * @description
20971  * Converts string to lowercase.
20972  * @see angular.lowercase
20973  */
20974 var lowercaseFilter = valueFn(lowercase);
20978  * @ngdoc filter
20979  * @name uppercase
20980  * @kind function
20981  * @description
20982  * Converts string to uppercase.
20983  * @see angular.uppercase
20984  */
20985 var uppercaseFilter = valueFn(uppercase);
20988  * @ngdoc filter
20989  * @name limitTo
20990  * @kind function
20992  * @description
20993  * Creates a new array or string containing only a specified number of elements. The elements are
20994  * taken from either the beginning or the end of the source array, string or number, as specified by
20995  * the value and sign (positive or negative) of `limit`. Other array-like objects are also supported
20996  * (e.g. array subclasses, NodeLists, jqLite/jQuery collections etc). If a number is used as input,
20997  * it is converted to a string.
20999  * @param {Array|ArrayLike|string|number} input - Array/array-like, string or number to be limited.
21000  * @param {string|number} limit - The length of the returned array or string. If the `limit` number
21001  *     is positive, `limit` number of items from the beginning of the source array/string are copied.
21002  *     If the number is negative, `limit` number  of items from the end of the source array/string
21003  *     are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
21004  *     the input will be returned unchanged.
21005  * @param {(string|number)=} begin - Index at which to begin limitation. As a negative index,
21006  *     `begin` indicates an offset from the end of `input`. Defaults to `0`.
21007  * @returns {Array|string} A new sub-array or substring of length `limit` or less if the input had
21008  *     less than `limit` elements.
21010  * @example
21011    <example module="limitToExample">
21012      <file name="index.html">
21013        <script>
21014          angular.module('limitToExample', [])
21015            .controller('ExampleController', ['$scope', function($scope) {
21016              $scope.numbers = [1,2,3,4,5,6,7,8,9];
21017              $scope.letters = "abcdefghi";
21018              $scope.longNumber = 2345432342;
21019              $scope.numLimit = 3;
21020              $scope.letterLimit = 3;
21021              $scope.longNumberLimit = 3;
21022            }]);
21023        </script>
21024        <div ng-controller="ExampleController">
21025          <label>
21026             Limit {{numbers}} to:
21027             <input type="number" step="1" ng-model="numLimit">
21028          </label>
21029          <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
21030          <label>
21031             Limit {{letters}} to:
21032             <input type="number" step="1" ng-model="letterLimit">
21033          </label>
21034          <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
21035          <label>
21036             Limit {{longNumber}} to:
21037             <input type="number" step="1" ng-model="longNumberLimit">
21038          </label>
21039          <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
21040        </div>
21041      </file>
21042      <file name="protractor.js" type="protractor">
21043        var numLimitInput = element(by.model('numLimit'));
21044        var letterLimitInput = element(by.model('letterLimit'));
21045        var longNumberLimitInput = element(by.model('longNumberLimit'));
21046        var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
21047        var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
21048        var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
21050        it('should limit the number array to first three items', function() {
21051          expect(numLimitInput.getAttribute('value')).toBe('3');
21052          expect(letterLimitInput.getAttribute('value')).toBe('3');
21053          expect(longNumberLimitInput.getAttribute('value')).toBe('3');
21054          expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
21055          expect(limitedLetters.getText()).toEqual('Output letters: abc');
21056          expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
21057        });
21059        // There is a bug in safari and protractor that doesn't like the minus key
21060        // it('should update the output when -3 is entered', function() {
21061        //   numLimitInput.clear();
21062        //   numLimitInput.sendKeys('-3');
21063        //   letterLimitInput.clear();
21064        //   letterLimitInput.sendKeys('-3');
21065        //   longNumberLimitInput.clear();
21066        //   longNumberLimitInput.sendKeys('-3');
21067        //   expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
21068        //   expect(limitedLetters.getText()).toEqual('Output letters: ghi');
21069        //   expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
21070        // });
21072        it('should not exceed the maximum size of input array', function() {
21073          numLimitInput.clear();
21074          numLimitInput.sendKeys('100');
21075          letterLimitInput.clear();
21076          letterLimitInput.sendKeys('100');
21077          longNumberLimitInput.clear();
21078          longNumberLimitInput.sendKeys('100');
21079          expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
21080          expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
21081          expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
21082        });
21083      </file>
21084    </example>
21086 function limitToFilter() {
21087   return function(input, limit, begin) {
21088     if (Math.abs(Number(limit)) === Infinity) {
21089       limit = Number(limit);
21090     } else {
21091       limit = toInt(limit);
21092     }
21093     if (isNaN(limit)) return input;
21095     if (isNumber(input)) input = input.toString();
21096     if (!isArrayLike(input)) return input;
21098     begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
21099     begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
21101     if (limit >= 0) {
21102       return sliceFn(input, begin, begin + limit);
21103     } else {
21104       if (begin === 0) {
21105         return sliceFn(input, limit, input.length);
21106       } else {
21107         return sliceFn(input, Math.max(0, begin + limit), begin);
21108       }
21109     }
21110   };
21113 function sliceFn(input, begin, end) {
21114   if (isString(input)) return input.slice(begin, end);
21116   return slice.call(input, begin, end);
21120  * @ngdoc filter
21121  * @name orderBy
21122  * @kind function
21124  * @description
21125  * Returns an array containing the items from the specified `collection`, ordered by a `comparator`
21126  * function based on the values computed using the `expression` predicate.
21128  * For example, `[{id: 'foo'}, {id: 'bar'}] | orderBy:'id'` would result in
21129  * `[{id: 'bar'}, {id: 'foo'}]`.
21131  * The `collection` can be an Array or array-like object (e.g. NodeList, jQuery object, TypedArray,
21132  * String, etc).
21134  * The `expression` can be a single predicate, or a list of predicates each serving as a tie-breaker
21135  * for the preceeding one. The `expression` is evaluated against each item and the output is used
21136  * for comparing with other items.
21138  * You can change the sorting order by setting `reverse` to `true`. By default, items are sorted in
21139  * ascending order.
21141  * The comparison is done using the `comparator` function. If none is specified, a default, built-in
21142  * comparator is used (see below for details - in a nutshell, it compares numbers numerically and
21143  * strings alphabetically).
21145  * ### Under the hood
21147  * Ordering the specified `collection` happens in two phases:
21149  * 1. All items are passed through the predicate (or predicates), and the returned values are saved
21150  *    along with their type (`string`, `number` etc). For example, an item `{label: 'foo'}`, passed
21151  *    through a predicate that extracts the value of the `label` property, would be transformed to:
21152  *    ```
21153  *    {
21154  *      value: 'foo',
21155  *      type: 'string',
21156  *      index: ...
21157  *    }
21158  *    ```
21159  * 2. The comparator function is used to sort the items, based on the derived values, types and
21160  *    indices.
21162  * If you use a custom comparator, it will be called with pairs of objects of the form
21163  * `{value: ..., type: '...', index: ...}` and is expected to return `0` if the objects are equal
21164  * (as far as the comparator is concerned), `-1` if the 1st one should be ranked higher than the
21165  * second, or `1` otherwise.
21167  * In order to ensure that the sorting will be deterministic across platforms, if none of the
21168  * specified predicates can distinguish between two items, `orderBy` will automatically introduce a
21169  * dummy predicate that returns the item's index as `value`.
21170  * (If you are using a custom comparator, make sure it can handle this predicate as well.)
21172  * Finally, in an attempt to simplify things, if a predicate returns an object as the extracted
21173  * value for an item, `orderBy` will try to convert that object to a primitive value, before passing
21174  * it to the comparator. The following rules govern the conversion:
21176  * 1. If the object has a `valueOf()` method that returns a primitive, its return value will be
21177  *    used instead.<br />
21178  *    (If the object has a `valueOf()` method that returns another object, then the returned object
21179  *    will be used in subsequent steps.)
21180  * 2. If the object has a custom `toString()` method (i.e. not the one inherited from `Object`) that
21181  *    returns a primitive, its return value will be used instead.<br />
21182  *    (If the object has a `toString()` method that returns another object, then the returned object
21183  *    will be used in subsequent steps.)
21184  * 3. No conversion; the object itself is used.
21186  * ### The default comparator
21188  * The default, built-in comparator should be sufficient for most usecases. In short, it compares
21189  * numbers numerically, strings alphabetically (and case-insensitively), for objects falls back to
21190  * using their index in the original collection, and sorts values of different types by type.
21192  * More specifically, it follows these steps to determine the relative order of items:
21194  * 1. If the compared values are of different types, compare the types themselves alphabetically.
21195  * 2. If both values are of type `string`, compare them alphabetically in a case- and
21196  *    locale-insensitive way.
21197  * 3. If both values are objects, compare their indices instead.
21198  * 4. Otherwise, return:
21199  *    -  `0`, if the values are equal (by strict equality comparison, i.e. using `===`).
21200  *    - `-1`, if the 1st value is "less than" the 2nd value (compared using the `<` operator).
21201  *    -  `1`, otherwise.
21203  * **Note:** If you notice numbers not being sorted as expected, make sure they are actually being
21204  *           saved as numbers and not strings.
21206  * @param {Array|ArrayLike} collection - The collection (array or array-like object) to sort.
21207  * @param {(Function|string|Array.<Function|string>)=} expression - A predicate (or list of
21208  *    predicates) to be used by the comparator to determine the order of elements.
21210  *    Can be one of:
21212  *    - `Function`: A getter function. This function will be called with each item as argument and
21213  *      the return value will be used for sorting.
21214  *    - `string`: An Angular expression. This expression will be evaluated against each item and the
21215  *      result will be used for sorting. For example, use `'label'` to sort by a property called
21216  *      `label` or `'label.substring(0, 3)'` to sort by the first 3 characters of the `label`
21217  *      property.<br />
21218  *      (The result of a constant expression is interpreted as a property name to be used for
21219  *      comparison. For example, use `'"special name"'` (note the extra pair of quotes) to sort by a
21220  *      property called `special name`.)<br />
21221  *      An expression can be optionally prefixed with `+` or `-` to control the sorting direction,
21222  *      ascending or descending. For example, `'+label'` or `'-label'`. If no property is provided,
21223  *      (e.g. `'+'` or `'-'`), the collection element itself is used in comparisons.
21224  *    - `Array`: An array of function and/or string predicates. If a predicate cannot determine the
21225  *      relative order of two items, the next predicate is used as a tie-breaker.
21227  * **Note:** If the predicate is missing or empty then it defaults to `'+'`.
21229  * @param {boolean=} reverse - If `true`, reverse the sorting order.
21230  * @param {(Function)=} comparator - The comparator function used to determine the relative order of
21231  *    value pairs. If omitted, the built-in comparator will be used.
21233  * @returns {Array} - The sorted array.
21236  * @example
21237  * ### Ordering a table with `ngRepeat`
21239  * The example below demonstrates a simple {@link ngRepeat ngRepeat}, where the data is sorted by
21240  * age in descending order (expression is set to `'-age'`). The `comparator` is not set, which means
21241  * it defaults to the built-in comparator.
21243    <example name="orderBy-static" module="orderByExample1">
21244      <file name="index.html">
21245        <div ng-controller="ExampleController">
21246          <table class="friends">
21247            <tr>
21248              <th>Name</th>
21249              <th>Phone Number</th>
21250              <th>Age</th>
21251            </tr>
21252            <tr ng-repeat="friend in friends | orderBy:'-age'">
21253              <td>{{friend.name}}</td>
21254              <td>{{friend.phone}}</td>
21255              <td>{{friend.age}}</td>
21256            </tr>
21257          </table>
21258        </div>
21259      </file>
21260      <file name="script.js">
21261        angular.module('orderByExample1', [])
21262          .controller('ExampleController', ['$scope', function($scope) {
21263            $scope.friends = [
21264              {name: 'John',   phone: '555-1212',  age: 10},
21265              {name: 'Mary',   phone: '555-9876',  age: 19},
21266              {name: 'Mike',   phone: '555-4321',  age: 21},
21267              {name: 'Adam',   phone: '555-5678',  age: 35},
21268              {name: 'Julie',  phone: '555-8765',  age: 29}
21269            ];
21270          }]);
21271      </file>
21272      <file name="style.css">
21273        .friends {
21274          border-collapse: collapse;
21275        }
21277        .friends th {
21278          border-bottom: 1px solid;
21279        }
21280        .friends td, .friends th {
21281          border-left: 1px solid;
21282          padding: 5px 10px;
21283        }
21284        .friends td:first-child, .friends th:first-child {
21285          border-left: none;
21286        }
21287      </file>
21288      <file name="protractor.js" type="protractor">
21289        // Element locators
21290        var names = element.all(by.repeater('friends').column('friend.name'));
21292        it('should sort friends by age in reverse order', function() {
21293          expect(names.get(0).getText()).toBe('Adam');
21294          expect(names.get(1).getText()).toBe('Julie');
21295          expect(names.get(2).getText()).toBe('Mike');
21296          expect(names.get(3).getText()).toBe('Mary');
21297          expect(names.get(4).getText()).toBe('John');
21298        });
21299      </file>
21300    </example>
21301  * <hr />
21303  * @example
21304  * ### Changing parameters dynamically
21306  * All parameters can be changed dynamically. The next example shows how you can make the columns of
21307  * a table sortable, by binding the `expression` and `reverse` parameters to scope properties.
21309    <example name="orderBy-dynamic" module="orderByExample2">
21310      <file name="index.html">
21311        <div ng-controller="ExampleController">
21312          <pre>Sort by = {{propertyName}}; reverse = {{reverse}}</pre>
21313          <hr/>
21314          <button ng-click="propertyName = null; reverse = false">Set to unsorted</button>
21315          <hr/>
21316          <table class="friends">
21317            <tr>
21318              <th>
21319                <button ng-click="sortBy('name')">Name</button>
21320                <span class="sortorder" ng-show="propertyName === 'name'" ng-class="{reverse: reverse}"></span>
21321              </th>
21322              <th>
21323                <button ng-click="sortBy('phone')">Phone Number</button>
21324                <span class="sortorder" ng-show="propertyName === 'phone'" ng-class="{reverse: reverse}"></span>
21325              </th>
21326              <th>
21327                <button ng-click="sortBy('age')">Age</button>
21328                <span class="sortorder" ng-show="propertyName === 'age'" ng-class="{reverse: reverse}"></span>
21329              </th>
21330            </tr>
21331            <tr ng-repeat="friend in friends | orderBy:propertyName:reverse">
21332              <td>{{friend.name}}</td>
21333              <td>{{friend.phone}}</td>
21334              <td>{{friend.age}}</td>
21335            </tr>
21336          </table>
21337        </div>
21338      </file>
21339      <file name="script.js">
21340        angular.module('orderByExample2', [])
21341          .controller('ExampleController', ['$scope', function($scope) {
21342            var friends = [
21343              {name: 'John',   phone: '555-1212',  age: 10},
21344              {name: 'Mary',   phone: '555-9876',  age: 19},
21345              {name: 'Mike',   phone: '555-4321',  age: 21},
21346              {name: 'Adam',   phone: '555-5678',  age: 35},
21347              {name: 'Julie',  phone: '555-8765',  age: 29}
21348            ];
21350            $scope.propertyName = 'age';
21351            $scope.reverse = true;
21352            $scope.friends = friends;
21354            $scope.sortBy = function(propertyName) {
21355              $scope.reverse = ($scope.propertyName === propertyName) ? !$scope.reverse : false;
21356              $scope.propertyName = propertyName;
21357            };
21358          }]);
21359      </file>
21360      <file name="style.css">
21361        .friends {
21362          border-collapse: collapse;
21363        }
21365        .friends th {
21366          border-bottom: 1px solid;
21367        }
21368        .friends td, .friends th {
21369          border-left: 1px solid;
21370          padding: 5px 10px;
21371        }
21372        .friends td:first-child, .friends th:first-child {
21373          border-left: none;
21374        }
21376        .sortorder:after {
21377          content: '\25b2';   // BLACK UP-POINTING TRIANGLE
21378        }
21379        .sortorder.reverse:after {
21380          content: '\25bc';   // BLACK DOWN-POINTING TRIANGLE
21381        }
21382      </file>
21383      <file name="protractor.js" type="protractor">
21384        // Element locators
21385        var unsortButton = element(by.partialButtonText('unsorted'));
21386        var nameHeader = element(by.partialButtonText('Name'));
21387        var phoneHeader = element(by.partialButtonText('Phone'));
21388        var ageHeader = element(by.partialButtonText('Age'));
21389        var firstName = element(by.repeater('friends').column('friend.name').row(0));
21390        var lastName = element(by.repeater('friends').column('friend.name').row(4));
21392        it('should sort friends by some property, when clicking on the column header', function() {
21393          expect(firstName.getText()).toBe('Adam');
21394          expect(lastName.getText()).toBe('John');
21396          phoneHeader.click();
21397          expect(firstName.getText()).toBe('John');
21398          expect(lastName.getText()).toBe('Mary');
21400          nameHeader.click();
21401          expect(firstName.getText()).toBe('Adam');
21402          expect(lastName.getText()).toBe('Mike');
21404          ageHeader.click();
21405          expect(firstName.getText()).toBe('John');
21406          expect(lastName.getText()).toBe('Adam');
21407        });
21409        it('should sort friends in reverse order, when clicking on the same column', function() {
21410          expect(firstName.getText()).toBe('Adam');
21411          expect(lastName.getText()).toBe('John');
21413          ageHeader.click();
21414          expect(firstName.getText()).toBe('John');
21415          expect(lastName.getText()).toBe('Adam');
21417          ageHeader.click();
21418          expect(firstName.getText()).toBe('Adam');
21419          expect(lastName.getText()).toBe('John');
21420        });
21422        it('should restore the original order, when clicking "Set to unsorted"', function() {
21423          expect(firstName.getText()).toBe('Adam');
21424          expect(lastName.getText()).toBe('John');
21426          unsortButton.click();
21427          expect(firstName.getText()).toBe('John');
21428          expect(lastName.getText()).toBe('Julie');
21429        });
21430      </file>
21431    </example>
21432  * <hr />
21434  * @example
21435  * ### Using `orderBy` inside a controller
21437  * It is also possible to call the `orderBy` filter manually, by injecting `orderByFilter`, and
21438  * calling it with the desired parameters. (Alternatively, you could inject the `$filter` factory
21439  * and retrieve the `orderBy` filter with `$filter('orderBy')`.)
21441    <example name="orderBy-call-manually" module="orderByExample3">
21442      <file name="index.html">
21443        <div ng-controller="ExampleController">
21444          <pre>Sort by = {{propertyName}}; reverse = {{reverse}}</pre>
21445          <hr/>
21446          <button ng-click="sortBy(null)">Set to unsorted</button>
21447          <hr/>
21448          <table class="friends">
21449            <tr>
21450              <th>
21451                <button ng-click="sortBy('name')">Name</button>
21452                <span class="sortorder" ng-show="propertyName === 'name'" ng-class="{reverse: reverse}"></span>
21453              </th>
21454              <th>
21455                <button ng-click="sortBy('phone')">Phone Number</button>
21456                <span class="sortorder" ng-show="propertyName === 'phone'" ng-class="{reverse: reverse}"></span>
21457              </th>
21458              <th>
21459                <button ng-click="sortBy('age')">Age</button>
21460                <span class="sortorder" ng-show="propertyName === 'age'" ng-class="{reverse: reverse}"></span>
21461              </th>
21462            </tr>
21463            <tr ng-repeat="friend in friends">
21464              <td>{{friend.name}}</td>
21465              <td>{{friend.phone}}</td>
21466              <td>{{friend.age}}</td>
21467            </tr>
21468          </table>
21469        </div>
21470      </file>
21471      <file name="script.js">
21472        angular.module('orderByExample3', [])
21473          .controller('ExampleController', ['$scope', 'orderByFilter', function($scope, orderBy) {
21474            var friends = [
21475              {name: 'John',   phone: '555-1212',  age: 10},
21476              {name: 'Mary',   phone: '555-9876',  age: 19},
21477              {name: 'Mike',   phone: '555-4321',  age: 21},
21478              {name: 'Adam',   phone: '555-5678',  age: 35},
21479              {name: 'Julie',  phone: '555-8765',  age: 29}
21480            ];
21482            $scope.propertyName = 'age';
21483            $scope.reverse = true;
21484            $scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse);
21486            $scope.sortBy = function(propertyName) {
21487              $scope.reverse = (propertyName !== null && $scope.propertyName === propertyName)
21488                  ? !$scope.reverse : false;
21489              $scope.propertyName = propertyName;
21490              $scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse);
21491            };
21492          }]);
21493      </file>
21494      <file name="style.css">
21495        .friends {
21496          border-collapse: collapse;
21497        }
21499        .friends th {
21500          border-bottom: 1px solid;
21501        }
21502        .friends td, .friends th {
21503          border-left: 1px solid;
21504          padding: 5px 10px;
21505        }
21506        .friends td:first-child, .friends th:first-child {
21507          border-left: none;
21508        }
21510        .sortorder:after {
21511          content: '\25b2';   // BLACK UP-POINTING TRIANGLE
21512        }
21513        .sortorder.reverse:after {
21514          content: '\25bc';   // BLACK DOWN-POINTING TRIANGLE
21515        }
21516      </file>
21517      <file name="protractor.js" type="protractor">
21518        // Element locators
21519        var unsortButton = element(by.partialButtonText('unsorted'));
21520        var nameHeader = element(by.partialButtonText('Name'));
21521        var phoneHeader = element(by.partialButtonText('Phone'));
21522        var ageHeader = element(by.partialButtonText('Age'));
21523        var firstName = element(by.repeater('friends').column('friend.name').row(0));
21524        var lastName = element(by.repeater('friends').column('friend.name').row(4));
21526        it('should sort friends by some property, when clicking on the column header', function() {
21527          expect(firstName.getText()).toBe('Adam');
21528          expect(lastName.getText()).toBe('John');
21530          phoneHeader.click();
21531          expect(firstName.getText()).toBe('John');
21532          expect(lastName.getText()).toBe('Mary');
21534          nameHeader.click();
21535          expect(firstName.getText()).toBe('Adam');
21536          expect(lastName.getText()).toBe('Mike');
21538          ageHeader.click();
21539          expect(firstName.getText()).toBe('John');
21540          expect(lastName.getText()).toBe('Adam');
21541        });
21543        it('should sort friends in reverse order, when clicking on the same column', function() {
21544          expect(firstName.getText()).toBe('Adam');
21545          expect(lastName.getText()).toBe('John');
21547          ageHeader.click();
21548          expect(firstName.getText()).toBe('John');
21549          expect(lastName.getText()).toBe('Adam');
21551          ageHeader.click();
21552          expect(firstName.getText()).toBe('Adam');
21553          expect(lastName.getText()).toBe('John');
21554        });
21556        it('should restore the original order, when clicking "Set to unsorted"', function() {
21557          expect(firstName.getText()).toBe('Adam');
21558          expect(lastName.getText()).toBe('John');
21560          unsortButton.click();
21561          expect(firstName.getText()).toBe('John');
21562          expect(lastName.getText()).toBe('Julie');
21563        });
21564      </file>
21565    </example>
21566  * <hr />
21568  * @example
21569  * ### Using a custom comparator
21571  * If you have very specific requirements about the way items are sorted, you can pass your own
21572  * comparator function. For example, you might need to compare some strings in a locale-sensitive
21573  * way. (When specifying a custom comparator, you also need to pass a value for the `reverse`
21574  * argument - passing `false` retains the default sorting order, i.e. ascending.)
21576    <example name="orderBy-custom-comparator" module="orderByExample4">
21577      <file name="index.html">
21578        <div ng-controller="ExampleController">
21579          <div class="friends-container custom-comparator">
21580            <h3>Locale-sensitive Comparator</h3>
21581            <table class="friends">
21582              <tr>
21583                <th>Name</th>
21584                <th>Favorite Letter</th>
21585              </tr>
21586              <tr ng-repeat="friend in friends | orderBy:'favoriteLetter':false:localeSensitiveComparator">
21587                <td>{{friend.name}}</td>
21588                <td>{{friend.favoriteLetter}}</td>
21589              </tr>
21590            </table>
21591          </div>
21592          <div class="friends-container default-comparator">
21593            <h3>Default Comparator</h3>
21594            <table class="friends">
21595              <tr>
21596                <th>Name</th>
21597                <th>Favorite Letter</th>
21598              </tr>
21599              <tr ng-repeat="friend in friends | orderBy:'favoriteLetter'">
21600                <td>{{friend.name}}</td>
21601                <td>{{friend.favoriteLetter}}</td>
21602              </tr>
21603            </table>
21604          </div>
21605        </div>
21606      </file>
21607      <file name="script.js">
21608        angular.module('orderByExample4', [])
21609          .controller('ExampleController', ['$scope', function($scope) {
21610            $scope.friends = [
21611              {name: 'John',   favoriteLetter: 'Ä'},
21612              {name: 'Mary',   favoriteLetter: 'Ãœ'},
21613              {name: 'Mike',   favoriteLetter: 'Ö'},
21614              {name: 'Adam',   favoriteLetter: 'H'},
21615              {name: 'Julie',  favoriteLetter: 'Z'}
21616            ];
21618            $scope.localeSensitiveComparator = function(v1, v2) {
21619              // If we don't get strings, just compare by index
21620              if (v1.type !== 'string' || v2.type !== 'string') {
21621                return (v1.index < v2.index) ? -1 : 1;
21622              }
21624              // Compare strings alphabetically, taking locale into account
21625              return v1.value.localeCompare(v2.value);
21626            };
21627          }]);
21628      </file>
21629      <file name="style.css">
21630        .friends-container {
21631          display: inline-block;
21632          margin: 0 30px;
21633        }
21635        .friends {
21636          border-collapse: collapse;
21637        }
21639        .friends th {
21640          border-bottom: 1px solid;
21641        }
21642        .friends td, .friends th {
21643          border-left: 1px solid;
21644          padding: 5px 10px;
21645        }
21646        .friends td:first-child, .friends th:first-child {
21647          border-left: none;
21648        }
21649      </file>
21650      <file name="protractor.js" type="protractor">
21651        // Element locators
21652        var container = element(by.css('.custom-comparator'));
21653        var names = container.all(by.repeater('friends').column('friend.name'));
21655        it('should sort friends by favorite letter (in correct alphabetical order)', function() {
21656          expect(names.get(0).getText()).toBe('John');
21657          expect(names.get(1).getText()).toBe('Adam');
21658          expect(names.get(2).getText()).toBe('Mike');
21659          expect(names.get(3).getText()).toBe('Mary');
21660          expect(names.get(4).getText()).toBe('Julie');
21661        });
21662      </file>
21663    </example>
21665  */
21666 orderByFilter.$inject = ['$parse'];
21667 function orderByFilter($parse) {
21668   return function(array, sortPredicate, reverseOrder, compareFn) {
21670     if (array == null) return array;
21671     if (!isArrayLike(array)) {
21672       throw minErr('orderBy')('notarray', 'Expected array but received: {0}', array);
21673     }
21675     if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
21676     if (sortPredicate.length === 0) { sortPredicate = ['+']; }
21678     var predicates = processPredicates(sortPredicate);
21680     var descending = reverseOrder ? -1 : 1;
21682     // Define the `compare()` function. Use a default comparator if none is specified.
21683     var compare = isFunction(compareFn) ? compareFn : defaultCompare;
21685     // The next three lines are a version of a Swartzian Transform idiom from Perl
21686     // (sometimes called the Decorate-Sort-Undecorate idiom)
21687     // See https://en.wikipedia.org/wiki/Schwartzian_transform
21688     var compareValues = Array.prototype.map.call(array, getComparisonObject);
21689     compareValues.sort(doComparison);
21690     array = compareValues.map(function(item) { return item.value; });
21692     return array;
21694     function getComparisonObject(value, index) {
21695       // NOTE: We are adding an extra `tieBreaker` value based on the element's index.
21696       // This will be used to keep the sort stable when none of the input predicates can
21697       // distinguish between two elements.
21698       return {
21699         value: value,
21700         tieBreaker: {value: index, type: 'number', index: index},
21701         predicateValues: predicates.map(function(predicate) {
21702           return getPredicateValue(predicate.get(value), index);
21703         })
21704       };
21705     }
21707     function doComparison(v1, v2) {
21708       for (var i = 0, ii = predicates.length; i < ii; i++) {
21709         var result = compare(v1.predicateValues[i], v2.predicateValues[i]);
21710         if (result) {
21711           return result * predicates[i].descending * descending;
21712         }
21713       }
21715       return compare(v1.tieBreaker, v2.tieBreaker) * descending;
21716     }
21717   };
21719   function processPredicates(sortPredicates) {
21720     return sortPredicates.map(function(predicate) {
21721       var descending = 1, get = identity;
21723       if (isFunction(predicate)) {
21724         get = predicate;
21725       } else if (isString(predicate)) {
21726         if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
21727           descending = predicate.charAt(0) == '-' ? -1 : 1;
21728           predicate = predicate.substring(1);
21729         }
21730         if (predicate !== '') {
21731           get = $parse(predicate);
21732           if (get.constant) {
21733             var key = get();
21734             get = function(value) { return value[key]; };
21735           }
21736         }
21737       }
21738       return {get: get, descending: descending};
21739     });
21740   }
21742   function isPrimitive(value) {
21743     switch (typeof value) {
21744       case 'number': /* falls through */
21745       case 'boolean': /* falls through */
21746       case 'string':
21747         return true;
21748       default:
21749         return false;
21750     }
21751   }
21753   function objectValue(value) {
21754     // If `valueOf` is a valid function use that
21755     if (isFunction(value.valueOf)) {
21756       value = value.valueOf();
21757       if (isPrimitive(value)) return value;
21758     }
21759     // If `toString` is a valid function and not the one from `Object.prototype` use that
21760     if (hasCustomToString(value)) {
21761       value = value.toString();
21762       if (isPrimitive(value)) return value;
21763     }
21765     return value;
21766   }
21768   function getPredicateValue(value, index) {
21769     var type = typeof value;
21770     if (value === null) {
21771       type = 'string';
21772       value = 'null';
21773     } else if (type === 'object') {
21774       value = objectValue(value);
21775     }
21776     return {value: value, type: type, index: index};
21777   }
21779   function defaultCompare(v1, v2) {
21780     var result = 0;
21781     var type1 = v1.type;
21782     var type2 = v2.type;
21784     if (type1 === type2) {
21785       var value1 = v1.value;
21786       var value2 = v2.value;
21788       if (type1 === 'string') {
21789         // Compare strings case-insensitively
21790         value1 = value1.toLowerCase();
21791         value2 = value2.toLowerCase();
21792       } else if (type1 === 'object') {
21793         // For basic objects, use the position of the object
21794         // in the collection instead of the value
21795         if (isObject(value1)) value1 = v1.index;
21796         if (isObject(value2)) value2 = v2.index;
21797       }
21799       if (value1 !== value2) {
21800         result = value1 < value2 ? -1 : 1;
21801       }
21802     } else {
21803       result = type1 < type2 ? -1 : 1;
21804     }
21806     return result;
21807   }
21810 function ngDirective(directive) {
21811   if (isFunction(directive)) {
21812     directive = {
21813       link: directive
21814     };
21815   }
21816   directive.restrict = directive.restrict || 'AC';
21817   return valueFn(directive);
21821  * @ngdoc directive
21822  * @name a
21823  * @restrict E
21825  * @description
21826  * Modifies the default behavior of the html A tag so that the default action is prevented when
21827  * the href attribute is empty.
21829  * This change permits the easy creation of action links with the `ngClick` directive
21830  * without changing the location or causing page reloads, e.g.:
21831  * `<a href="" ng-click="list.addItem()">Add Item</a>`
21832  */
21833 var htmlAnchorDirective = valueFn({
21834   restrict: 'E',
21835   compile: function(element, attr) {
21836     if (!attr.href && !attr.xlinkHref) {
21837       return function(scope, element) {
21838         // If the linked element is not an anchor tag anymore, do nothing
21839         if (element[0].nodeName.toLowerCase() !== 'a') return;
21841         // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
21842         var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
21843                    'xlink:href' : 'href';
21844         element.on('click', function(event) {
21845           // if we have no href url, then don't navigate anywhere.
21846           if (!element.attr(href)) {
21847             event.preventDefault();
21848           }
21849         });
21850       };
21851     }
21852   }
21856  * @ngdoc directive
21857  * @name ngHref
21858  * @restrict A
21859  * @priority 99
21861  * @description
21862  * Using Angular markup like `{{hash}}` in an href attribute will
21863  * make the link go to the wrong URL if the user clicks it before
21864  * Angular has a chance to replace the `{{hash}}` markup with its
21865  * value. Until Angular replaces the markup the link will be broken
21866  * and will most likely return a 404 error. The `ngHref` directive
21867  * solves this problem.
21869  * The wrong way to write it:
21870  * ```html
21871  * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
21872  * ```
21874  * The correct way to write it:
21875  * ```html
21876  * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
21877  * ```
21879  * @element A
21880  * @param {template} ngHref any string which can contain `{{}}` markup.
21882  * @example
21883  * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
21884  * in links and their different behaviors:
21885     <example>
21886       <file name="index.html">
21887         <input ng-model="value" /><br />
21888         <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
21889         <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
21890         <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
21891         <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
21892         <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
21893         <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
21894       </file>
21895       <file name="protractor.js" type="protractor">
21896         it('should execute ng-click but not reload when href without value', function() {
21897           element(by.id('link-1')).click();
21898           expect(element(by.model('value')).getAttribute('value')).toEqual('1');
21899           expect(element(by.id('link-1')).getAttribute('href')).toBe('');
21900         });
21902         it('should execute ng-click but not reload when href empty string', function() {
21903           element(by.id('link-2')).click();
21904           expect(element(by.model('value')).getAttribute('value')).toEqual('2');
21905           expect(element(by.id('link-2')).getAttribute('href')).toBe('');
21906         });
21908         it('should execute ng-click and change url when ng-href specified', function() {
21909           expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
21911           element(by.id('link-3')).click();
21913           // At this point, we navigate away from an Angular page, so we need
21914           // to use browser.driver to get the base webdriver.
21916           browser.wait(function() {
21917             return browser.driver.getCurrentUrl().then(function(url) {
21918               return url.match(/\/123$/);
21919             });
21920           }, 5000, 'page should navigate to /123');
21921         });
21923         it('should execute ng-click but not reload when href empty string and name specified', function() {
21924           element(by.id('link-4')).click();
21925           expect(element(by.model('value')).getAttribute('value')).toEqual('4');
21926           expect(element(by.id('link-4')).getAttribute('href')).toBe('');
21927         });
21929         it('should execute ng-click but not reload when no href but name specified', function() {
21930           element(by.id('link-5')).click();
21931           expect(element(by.model('value')).getAttribute('value')).toEqual('5');
21932           expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
21933         });
21935         it('should only change url when only ng-href', function() {
21936           element(by.model('value')).clear();
21937           element(by.model('value')).sendKeys('6');
21938           expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
21940           element(by.id('link-6')).click();
21942           // At this point, we navigate away from an Angular page, so we need
21943           // to use browser.driver to get the base webdriver.
21944           browser.wait(function() {
21945             return browser.driver.getCurrentUrl().then(function(url) {
21946               return url.match(/\/6$/);
21947             });
21948           }, 5000, 'page should navigate to /6');
21949         });
21950       </file>
21951     </example>
21952  */
21955  * @ngdoc directive
21956  * @name ngSrc
21957  * @restrict A
21958  * @priority 99
21960  * @description
21961  * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
21962  * work right: The browser will fetch from the URL with the literal
21963  * text `{{hash}}` until Angular replaces the expression inside
21964  * `{{hash}}`. The `ngSrc` directive solves this problem.
21966  * The buggy way to write it:
21967  * ```html
21968  * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
21969  * ```
21971  * The correct way to write it:
21972  * ```html
21973  * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
21974  * ```
21976  * @element IMG
21977  * @param {template} ngSrc any string which can contain `{{}}` markup.
21978  */
21981  * @ngdoc directive
21982  * @name ngSrcset
21983  * @restrict A
21984  * @priority 99
21986  * @description
21987  * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
21988  * work right: The browser will fetch from the URL with the literal
21989  * text `{{hash}}` until Angular replaces the expression inside
21990  * `{{hash}}`. The `ngSrcset` directive solves this problem.
21992  * The buggy way to write it:
21993  * ```html
21994  * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
21995  * ```
21997  * The correct way to write it:
21998  * ```html
21999  * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
22000  * ```
22002  * @element IMG
22003  * @param {template} ngSrcset any string which can contain `{{}}` markup.
22004  */
22007  * @ngdoc directive
22008  * @name ngDisabled
22009  * @restrict A
22010  * @priority 100
22012  * @description
22014  * This directive sets the `disabled` attribute on the element if the
22015  * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
22017  * A special directive is necessary because we cannot use interpolation inside the `disabled`
22018  * attribute. See the {@link guide/interpolation interpolation guide} for more info.
22020  * @example
22021     <example>
22022       <file name="index.html">
22023         <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
22024         <button ng-model="button" ng-disabled="checked">Button</button>
22025       </file>
22026       <file name="protractor.js" type="protractor">
22027         it('should toggle button', function() {
22028           expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
22029           element(by.model('checked')).click();
22030           expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
22031         });
22032       </file>
22033     </example>
22035  * @element INPUT
22036  * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
22037  *     then the `disabled` attribute will be set on the element
22038  */
22042  * @ngdoc directive
22043  * @name ngChecked
22044  * @restrict A
22045  * @priority 100
22047  * @description
22048  * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
22050  * Note that this directive should not be used together with {@link ngModel `ngModel`},
22051  * as this can lead to unexpected behavior.
22053  * A special directive is necessary because we cannot use interpolation inside the `checked`
22054  * attribute. See the {@link guide/interpolation interpolation guide} for more info.
22056  * @example
22057     <example>
22058       <file name="index.html">
22059         <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
22060         <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
22061       </file>
22062       <file name="protractor.js" type="protractor">
22063         it('should check both checkBoxes', function() {
22064           expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
22065           element(by.model('master')).click();
22066           expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
22067         });
22068       </file>
22069     </example>
22071  * @element INPUT
22072  * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
22073  *     then the `checked` attribute will be set on the element
22074  */
22078  * @ngdoc directive
22079  * @name ngReadonly
22080  * @restrict A
22081  * @priority 100
22083  * @description
22085  * Sets the `readonly` attribute on the element, if the expression inside `ngReadonly` is truthy.
22086  * Note that `readonly` applies only to `input` elements with specific types. [See the input docs on
22087  * MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-readonly) for more information.
22089  * A special directive is necessary because we cannot use interpolation inside the `readonly`
22090  * attribute. See the {@link guide/interpolation interpolation guide} for more info.
22092  * @example
22093     <example>
22094       <file name="index.html">
22095         <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
22096         <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
22097       </file>
22098       <file name="protractor.js" type="protractor">
22099         it('should toggle readonly attr', function() {
22100           expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
22101           element(by.model('checked')).click();
22102           expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
22103         });
22104       </file>
22105     </example>
22107  * @element INPUT
22108  * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
22109  *     then special attribute "readonly" will be set on the element
22110  */
22114  * @ngdoc directive
22115  * @name ngSelected
22116  * @restrict A
22117  * @priority 100
22119  * @description
22121  * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy.
22123  * A special directive is necessary because we cannot use interpolation inside the `selected`
22124  * attribute. See the {@link guide/interpolation interpolation guide} for more info.
22126  * <div class="alert alert-warning">
22127  *   **Note:** `ngSelected` does not interact with the `select` and `ngModel` directives, it only
22128  *   sets the `selected` attribute on the element. If you are using `ngModel` on the select, you
22129  *   should not use `ngSelected` on the options, as `ngModel` will set the select value and
22130  *   selected options.
22131  * </div>
22133  * @example
22134     <example>
22135       <file name="index.html">
22136         <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
22137         <select aria-label="ngSelected demo">
22138           <option>Hello!</option>
22139           <option id="greet" ng-selected="selected">Greetings!</option>
22140         </select>
22141       </file>
22142       <file name="protractor.js" type="protractor">
22143         it('should select Greetings!', function() {
22144           expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
22145           element(by.model('selected')).click();
22146           expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
22147         });
22148       </file>
22149     </example>
22151  * @element OPTION
22152  * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
22153  *     then special attribute "selected" will be set on the element
22154  */
22157  * @ngdoc directive
22158  * @name ngOpen
22159  * @restrict A
22160  * @priority 100
22162  * @description
22164  * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy.
22166  * A special directive is necessary because we cannot use interpolation inside the `open`
22167  * attribute. See the {@link guide/interpolation interpolation guide} for more info.
22169  * ## A note about browser compatibility
22171  * Edge, Firefox, and Internet Explorer do not support the `details` element, it is
22172  * recommended to use {@link ng.ngShow} and {@link ng.ngHide} instead.
22174  * @example
22175      <example>
22176        <file name="index.html">
22177          <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
22178          <details id="details" ng-open="open">
22179             <summary>Show/Hide me</summary>
22180          </details>
22181        </file>
22182        <file name="protractor.js" type="protractor">
22183          it('should toggle open', function() {
22184            expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
22185            element(by.model('open')).click();
22186            expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
22187          });
22188        </file>
22189      </example>
22191  * @element DETAILS
22192  * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
22193  *     then special attribute "open" will be set on the element
22194  */
22196 var ngAttributeAliasDirectives = {};
22198 // boolean attrs are evaluated
22199 forEach(BOOLEAN_ATTR, function(propName, attrName) {
22200   // binding to multiple is not supported
22201   if (propName == "multiple") return;
22203   function defaultLinkFn(scope, element, attr) {
22204     scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
22205       attr.$set(attrName, !!value);
22206     });
22207   }
22209   var normalized = directiveNormalize('ng-' + attrName);
22210   var linkFn = defaultLinkFn;
22212   if (propName === 'checked') {
22213     linkFn = function(scope, element, attr) {
22214       // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
22215       if (attr.ngModel !== attr[normalized]) {
22216         defaultLinkFn(scope, element, attr);
22217       }
22218     };
22219   }
22221   ngAttributeAliasDirectives[normalized] = function() {
22222     return {
22223       restrict: 'A',
22224       priority: 100,
22225       link: linkFn
22226     };
22227   };
22230 // aliased input attrs are evaluated
22231 forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
22232   ngAttributeAliasDirectives[ngAttr] = function() {
22233     return {
22234       priority: 100,
22235       link: function(scope, element, attr) {
22236         //special case ngPattern when a literal regular expression value
22237         //is used as the expression (this way we don't have to watch anything).
22238         if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
22239           var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
22240           if (match) {
22241             attr.$set("ngPattern", new RegExp(match[1], match[2]));
22242             return;
22243           }
22244         }
22246         scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
22247           attr.$set(ngAttr, value);
22248         });
22249       }
22250     };
22251   };
22254 // ng-src, ng-srcset, ng-href are interpolated
22255 forEach(['src', 'srcset', 'href'], function(attrName) {
22256   var normalized = directiveNormalize('ng-' + attrName);
22257   ngAttributeAliasDirectives[normalized] = function() {
22258     return {
22259       priority: 99, // it needs to run after the attributes are interpolated
22260       link: function(scope, element, attr) {
22261         var propName = attrName,
22262             name = attrName;
22264         if (attrName === 'href' &&
22265             toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
22266           name = 'xlinkHref';
22267           attr.$attr[name] = 'xlink:href';
22268           propName = null;
22269         }
22271         attr.$observe(normalized, function(value) {
22272           if (!value) {
22273             if (attrName === 'href') {
22274               attr.$set(name, null);
22275             }
22276             return;
22277           }
22279           attr.$set(name, value);
22281           // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
22282           // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
22283           // to set the property as well to achieve the desired effect.
22284           // we use attr[attrName] value since $set can sanitize the url.
22285           if (msie && propName) element.prop(propName, attr[name]);
22286         });
22287       }
22288     };
22289   };
22292 /* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
22293  */
22294 var nullFormCtrl = {
22295   $addControl: noop,
22296   $$renameControl: nullFormRenameControl,
22297   $removeControl: noop,
22298   $setValidity: noop,
22299   $setDirty: noop,
22300   $setPristine: noop,
22301   $setSubmitted: noop
22303 SUBMITTED_CLASS = 'ng-submitted';
22305 function nullFormRenameControl(control, name) {
22306   control.$name = name;
22310  * @ngdoc type
22311  * @name form.FormController
22313  * @property {boolean} $pristine True if user has not interacted with the form yet.
22314  * @property {boolean} $dirty True if user has already interacted with the form.
22315  * @property {boolean} $valid True if all of the containing forms and controls are valid.
22316  * @property {boolean} $invalid True if at least one containing control or form is invalid.
22317  * @property {boolean} $pending True if at least one containing control or form is pending.
22318  * @property {boolean} $submitted True if user has submitted the form even if its invalid.
22320  * @property {Object} $error Is an object hash, containing references to controls or
22321  *  forms with failing validators, where:
22323  *  - keys are validation tokens (error names),
22324  *  - values are arrays of controls or forms that have a failing validator for given error name.
22326  *  Built-in validation tokens:
22328  *  - `email`
22329  *  - `max`
22330  *  - `maxlength`
22331  *  - `min`
22332  *  - `minlength`
22333  *  - `number`
22334  *  - `pattern`
22335  *  - `required`
22336  *  - `url`
22337  *  - `date`
22338  *  - `datetimelocal`
22339  *  - `time`
22340  *  - `week`
22341  *  - `month`
22343  * @description
22344  * `FormController` keeps track of all its controls and nested forms as well as the state of them,
22345  * such as being valid/invalid or dirty/pristine.
22347  * Each {@link ng.directive:form form} directive creates an instance
22348  * of `FormController`.
22350  */
22351 //asks for $scope to fool the BC controller module
22352 FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
22353 function FormController(element, attrs, $scope, $animate, $interpolate) {
22354   var form = this,
22355       controls = [];
22357   // init state
22358   form.$error = {};
22359   form.$$success = {};
22360   form.$pending = undefined;
22361   form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
22362   form.$dirty = false;
22363   form.$pristine = true;
22364   form.$valid = true;
22365   form.$invalid = false;
22366   form.$submitted = false;
22367   form.$$parentForm = nullFormCtrl;
22369   /**
22370    * @ngdoc method
22371    * @name form.FormController#$rollbackViewValue
22372    *
22373    * @description
22374    * Rollback all form controls pending updates to the `$modelValue`.
22375    *
22376    * Updates may be pending by a debounced event or because the input is waiting for a some future
22377    * event defined in `ng-model-options`. This method is typically needed by the reset button of
22378    * a form that uses `ng-model-options` to pend updates.
22379    */
22380   form.$rollbackViewValue = function() {
22381     forEach(controls, function(control) {
22382       control.$rollbackViewValue();
22383     });
22384   };
22386   /**
22387    * @ngdoc method
22388    * @name form.FormController#$commitViewValue
22389    *
22390    * @description
22391    * Commit all form controls pending updates to the `$modelValue`.
22392    *
22393    * Updates may be pending by a debounced event or because the input is waiting for a some future
22394    * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
22395    * usually handles calling this in response to input events.
22396    */
22397   form.$commitViewValue = function() {
22398     forEach(controls, function(control) {
22399       control.$commitViewValue();
22400     });
22401   };
22403   /**
22404    * @ngdoc method
22405    * @name form.FormController#$addControl
22406    * @param {object} control control object, either a {@link form.FormController} or an
22407    * {@link ngModel.NgModelController}
22408    *
22409    * @description
22410    * Register a control with the form. Input elements using ngModelController do this automatically
22411    * when they are linked.
22412    *
22413    * Note that the current state of the control will not be reflected on the new parent form. This
22414    * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
22415    * state.
22416    *
22417    * However, if the method is used programmatically, for example by adding dynamically created controls,
22418    * or controls that have been previously removed without destroying their corresponding DOM element,
22419    * it's the developers responsibility to make sure the current state propagates to the parent form.
22420    *
22421    * For example, if an input control is added that is already `$dirty` and has `$error` properties,
22422    * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
22423    */
22424   form.$addControl = function(control) {
22425     // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
22426     // and not added to the scope.  Now we throw an error.
22427     assertNotHasOwnProperty(control.$name, 'input');
22428     controls.push(control);
22430     if (control.$name) {
22431       form[control.$name] = control;
22432     }
22434     control.$$parentForm = form;
22435   };
22437   // Private API: rename a form control
22438   form.$$renameControl = function(control, newName) {
22439     var oldName = control.$name;
22441     if (form[oldName] === control) {
22442       delete form[oldName];
22443     }
22444     form[newName] = control;
22445     control.$name = newName;
22446   };
22448   /**
22449    * @ngdoc method
22450    * @name form.FormController#$removeControl
22451    * @param {object} control control object, either a {@link form.FormController} or an
22452    * {@link ngModel.NgModelController}
22453    *
22454    * @description
22455    * Deregister a control from the form.
22456    *
22457    * Input elements using ngModelController do this automatically when they are destroyed.
22458    *
22459    * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
22460    * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
22461    * different from case to case. For example, removing the only `$dirty` control from a form may or
22462    * may not mean that the form is still `$dirty`.
22463    */
22464   form.$removeControl = function(control) {
22465     if (control.$name && form[control.$name] === control) {
22466       delete form[control.$name];
22467     }
22468     forEach(form.$pending, function(value, name) {
22469       form.$setValidity(name, null, control);
22470     });
22471     forEach(form.$error, function(value, name) {
22472       form.$setValidity(name, null, control);
22473     });
22474     forEach(form.$$success, function(value, name) {
22475       form.$setValidity(name, null, control);
22476     });
22478     arrayRemove(controls, control);
22479     control.$$parentForm = nullFormCtrl;
22480   };
22483   /**
22484    * @ngdoc method
22485    * @name form.FormController#$setValidity
22486    *
22487    * @description
22488    * Sets the validity of a form control.
22489    *
22490    * This method will also propagate to parent forms.
22491    */
22492   addSetValidityMethod({
22493     ctrl: this,
22494     $element: element,
22495     set: function(object, property, controller) {
22496       var list = object[property];
22497       if (!list) {
22498         object[property] = [controller];
22499       } else {
22500         var index = list.indexOf(controller);
22501         if (index === -1) {
22502           list.push(controller);
22503         }
22504       }
22505     },
22506     unset: function(object, property, controller) {
22507       var list = object[property];
22508       if (!list) {
22509         return;
22510       }
22511       arrayRemove(list, controller);
22512       if (list.length === 0) {
22513         delete object[property];
22514       }
22515     },
22516     $animate: $animate
22517   });
22519   /**
22520    * @ngdoc method
22521    * @name form.FormController#$setDirty
22522    *
22523    * @description
22524    * Sets the form to a dirty state.
22525    *
22526    * This method can be called to add the 'ng-dirty' class and set the form to a dirty
22527    * state (ng-dirty class). This method will also propagate to parent forms.
22528    */
22529   form.$setDirty = function() {
22530     $animate.removeClass(element, PRISTINE_CLASS);
22531     $animate.addClass(element, DIRTY_CLASS);
22532     form.$dirty = true;
22533     form.$pristine = false;
22534     form.$$parentForm.$setDirty();
22535   };
22537   /**
22538    * @ngdoc method
22539    * @name form.FormController#$setPristine
22540    *
22541    * @description
22542    * Sets the form to its pristine state.
22543    *
22544    * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
22545    * state (ng-pristine class). This method will also propagate to all the controls contained
22546    * in this form.
22547    *
22548    * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
22549    * saving or resetting it.
22550    */
22551   form.$setPristine = function() {
22552     $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
22553     form.$dirty = false;
22554     form.$pristine = true;
22555     form.$submitted = false;
22556     forEach(controls, function(control) {
22557       control.$setPristine();
22558     });
22559   };
22561   /**
22562    * @ngdoc method
22563    * @name form.FormController#$setUntouched
22564    *
22565    * @description
22566    * Sets the form to its untouched state.
22567    *
22568    * This method can be called to remove the 'ng-touched' class and set the form controls to their
22569    * untouched state (ng-untouched class).
22570    *
22571    * Setting a form controls back to their untouched state is often useful when setting the form
22572    * back to its pristine state.
22573    */
22574   form.$setUntouched = function() {
22575     forEach(controls, function(control) {
22576       control.$setUntouched();
22577     });
22578   };
22580   /**
22581    * @ngdoc method
22582    * @name form.FormController#$setSubmitted
22583    *
22584    * @description
22585    * Sets the form to its submitted state.
22586    */
22587   form.$setSubmitted = function() {
22588     $animate.addClass(element, SUBMITTED_CLASS);
22589     form.$submitted = true;
22590     form.$$parentForm.$setSubmitted();
22591   };
22595  * @ngdoc directive
22596  * @name ngForm
22597  * @restrict EAC
22599  * @description
22600  * Nestable alias of {@link ng.directive:form `form`} directive. HTML
22601  * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
22602  * sub-group of controls needs to be determined.
22604  * Note: the purpose of `ngForm` is to group controls,
22605  * but not to be a replacement for the `<form>` tag with all of its capabilities
22606  * (e.g. posting to the server, ...).
22608  * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
22609  *                       related scope, under this name.
22611  */
22613  /**
22614  * @ngdoc directive
22615  * @name form
22616  * @restrict E
22618  * @description
22619  * Directive that instantiates
22620  * {@link form.FormController FormController}.
22622  * If the `name` attribute is specified, the form controller is published onto the current scope under
22623  * this name.
22625  * # Alias: {@link ng.directive:ngForm `ngForm`}
22627  * In Angular, forms can be nested. This means that the outer form is valid when all of the child
22628  * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
22629  * Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
22630  * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
22631  * of controls needs to be determined.
22633  * # CSS classes
22634  *  - `ng-valid` is set if the form is valid.
22635  *  - `ng-invalid` is set if the form is invalid.
22636  *  - `ng-pending` is set if the form is pending.
22637  *  - `ng-pristine` is set if the form is pristine.
22638  *  - `ng-dirty` is set if the form is dirty.
22639  *  - `ng-submitted` is set if the form was submitted.
22641  * Keep in mind that ngAnimate can detect each of these classes when added and removed.
22644  * # Submitting a form and preventing the default action
22646  * Since the role of forms in client-side Angular applications is different than in classical
22647  * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
22648  * page reload that sends the data to the server. Instead some javascript logic should be triggered
22649  * to handle the form submission in an application-specific way.
22651  * For this reason, Angular prevents the default action (form submission to the server) unless the
22652  * `<form>` element has an `action` attribute specified.
22654  * You can use one of the following two ways to specify what javascript method should be called when
22655  * a form is submitted:
22657  * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
22658  * - {@link ng.directive:ngClick ngClick} directive on the first
22659   *  button or input field of type submit (input[type=submit])
22661  * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
22662  * or {@link ng.directive:ngClick ngClick} directives.
22663  * This is because of the following form submission rules in the HTML specification:
22665  * - If a form has only one input field then hitting enter in this field triggers form submit
22666  * (`ngSubmit`)
22667  * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
22668  * doesn't trigger submit
22669  * - if a form has one or more input fields and one or more buttons or input[type=submit] then
22670  * hitting enter in any of the input fields will trigger the click handler on the *first* button or
22671  * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
22673  * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
22674  * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
22675  * to have access to the updated model.
22677  * ## Animation Hooks
22679  * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
22680  * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
22681  * other validations that are performed within the form. Animations in ngForm are similar to how
22682  * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
22683  * as JS animations.
22685  * The following example shows a simple way to utilize CSS transitions to style a form element
22686  * that has been rendered as invalid after it has been validated:
22688  * <pre>
22689  * //be sure to include ngAnimate as a module to hook into more
22690  * //advanced animations
22691  * .my-form {
22692  *   transition:0.5s linear all;
22693  *   background: white;
22694  * }
22695  * .my-form.ng-invalid {
22696  *   background: red;
22697  *   color:white;
22698  * }
22699  * </pre>
22701  * @example
22702     <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
22703       <file name="index.html">
22704        <script>
22705          angular.module('formExample', [])
22706            .controller('FormController', ['$scope', function($scope) {
22707              $scope.userType = 'guest';
22708            }]);
22709        </script>
22710        <style>
22711         .my-form {
22712           transition:all linear 0.5s;
22713           background: transparent;
22714         }
22715         .my-form.ng-invalid {
22716           background: red;
22717         }
22718        </style>
22719        <form name="myForm" ng-controller="FormController" class="my-form">
22720          userType: <input name="input" ng-model="userType" required>
22721          <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
22722          <code>userType = {{userType}}</code><br>
22723          <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
22724          <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
22725          <code>myForm.$valid = {{myForm.$valid}}</code><br>
22726          <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
22727         </form>
22728       </file>
22729       <file name="protractor.js" type="protractor">
22730         it('should initialize to model', function() {
22731           var userType = element(by.binding('userType'));
22732           var valid = element(by.binding('myForm.input.$valid'));
22734           expect(userType.getText()).toContain('guest');
22735           expect(valid.getText()).toContain('true');
22736         });
22738         it('should be invalid if empty', function() {
22739           var userType = element(by.binding('userType'));
22740           var valid = element(by.binding('myForm.input.$valid'));
22741           var userInput = element(by.model('userType'));
22743           userInput.clear();
22744           userInput.sendKeys('');
22746           expect(userType.getText()).toEqual('userType =');
22747           expect(valid.getText()).toContain('false');
22748         });
22749       </file>
22750     </example>
22752  * @param {string=} name Name of the form. If specified, the form controller will be published into
22753  *                       related scope, under this name.
22754  */
22755 var formDirectiveFactory = function(isNgForm) {
22756   return ['$timeout', '$parse', function($timeout, $parse) {
22757     var formDirective = {
22758       name: 'form',
22759       restrict: isNgForm ? 'EAC' : 'E',
22760       require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
22761       controller: FormController,
22762       compile: function ngFormCompile(formElement, attr) {
22763         // Setup initial state of the control
22764         formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
22766         var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
22768         return {
22769           pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
22770             var controller = ctrls[0];
22772             // if `action` attr is not present on the form, prevent the default action (submission)
22773             if (!('action' in attr)) {
22774               // we can't use jq events because if a form is destroyed during submission the default
22775               // action is not prevented. see #1238
22776               //
22777               // IE 9 is not affected because it doesn't fire a submit event and try to do a full
22778               // page reload if the form was destroyed by submission of the form via a click handler
22779               // on a button in the form. Looks like an IE9 specific bug.
22780               var handleFormSubmission = function(event) {
22781                 scope.$apply(function() {
22782                   controller.$commitViewValue();
22783                   controller.$setSubmitted();
22784                 });
22786                 event.preventDefault();
22787               };
22789               addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
22791               // unregister the preventDefault listener so that we don't not leak memory but in a
22792               // way that will achieve the prevention of the default action.
22793               formElement.on('$destroy', function() {
22794                 $timeout(function() {
22795                   removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
22796                 }, 0, false);
22797               });
22798             }
22800             var parentFormCtrl = ctrls[1] || controller.$$parentForm;
22801             parentFormCtrl.$addControl(controller);
22803             var setter = nameAttr ? getSetter(controller.$name) : noop;
22805             if (nameAttr) {
22806               setter(scope, controller);
22807               attr.$observe(nameAttr, function(newValue) {
22808                 if (controller.$name === newValue) return;
22809                 setter(scope, undefined);
22810                 controller.$$parentForm.$$renameControl(controller, newValue);
22811                 setter = getSetter(controller.$name);
22812                 setter(scope, controller);
22813               });
22814             }
22815             formElement.on('$destroy', function() {
22816               controller.$$parentForm.$removeControl(controller);
22817               setter(scope, undefined);
22818               extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
22819             });
22820           }
22821         };
22822       }
22823     };
22825     return formDirective;
22827     function getSetter(expression) {
22828       if (expression === '') {
22829         //create an assignable expression, so forms with an empty name can be renamed later
22830         return $parse('this[""]').assign;
22831       }
22832       return $parse(expression).assign || noop;
22833     }
22834   }];
22837 var formDirective = formDirectiveFactory();
22838 var ngFormDirective = formDirectiveFactory(true);
22840 /* global VALID_CLASS: false,
22841   INVALID_CLASS: false,
22842   PRISTINE_CLASS: false,
22843   DIRTY_CLASS: false,
22844   UNTOUCHED_CLASS: false,
22845   TOUCHED_CLASS: false,
22846   ngModelMinErr: false,
22849 // Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
22850 var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/;
22851 // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
22852 // Note: We are being more lenient, because browsers are too.
22853 //   1. Scheme
22854 //   2. Slashes
22855 //   3. Username
22856 //   4. Password
22857 //   5. Hostname
22858 //   6. Port
22859 //   7. Path
22860 //   8. Query
22861 //   9. Fragment
22862 //                 1111111111111111 222   333333    44444        555555555555555555555555    666     77777777     8888888     999
22863 var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
22864 /* jshint maxlen:220 */
22865 var EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+\/0-9=?A-Z^_`a-z{|}~]+(\.[-!#$%&'*+\/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/;
22866 /* jshint maxlen:200 */
22867 var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
22868 var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/;
22869 var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
22870 var WEEK_REGEXP = /^(\d{4,})-W(\d\d)$/;
22871 var MONTH_REGEXP = /^(\d{4,})-(\d\d)$/;
22872 var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
22874 var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown';
22875 var PARTIAL_VALIDATION_TYPES = createMap();
22876 forEach('date,datetime-local,month,time,week'.split(','), function(type) {
22877   PARTIAL_VALIDATION_TYPES[type] = true;
22880 var inputType = {
22882   /**
22883    * @ngdoc input
22884    * @name input[text]
22885    *
22886    * @description
22887    * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
22888    *
22889    *
22890    * @param {string} ngModel Assignable angular expression to data-bind to.
22891    * @param {string=} name Property name of the form under which the control is published.
22892    * @param {string=} required Adds `required` validation error key if the value is not entered.
22893    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22894    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22895    *    `required` when you want to data-bind to the `required` attribute.
22896    * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22897    *    minlength.
22898    * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22899    *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
22900    *    any length.
22901    * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
22902    *    that contains the regular expression body that will be converted to a regular expression
22903    *    as in the ngPattern directive.
22904    * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
22905    *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
22906    *    If the expression evaluates to a RegExp object, then this is used directly.
22907    *    If the expression evaluates to a string, then it will be converted to a RegExp
22908    *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22909    *    `new RegExp('^abc$')`.<br />
22910    *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22911    *    start at the index of the last search's match, thus not taking the whole input value into
22912    *    account.
22913    * @param {string=} ngChange Angular expression to be executed when input changes due to user
22914    *    interaction with the input element.
22915    * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22916    *    This parameter is ignored for input[type=password] controls, which will never trim the
22917    *    input.
22918    *
22919    * @example
22920       <example name="text-input-directive" module="textInputExample">
22921         <file name="index.html">
22922          <script>
22923            angular.module('textInputExample', [])
22924              .controller('ExampleController', ['$scope', function($scope) {
22925                $scope.example = {
22926                  text: 'guest',
22927                  word: /^\s*\w*\s*$/
22928                };
22929              }]);
22930          </script>
22931          <form name="myForm" ng-controller="ExampleController">
22932            <label>Single word:
22933              <input type="text" name="input" ng-model="example.text"
22934                     ng-pattern="example.word" required ng-trim="false">
22935            </label>
22936            <div role="alert">
22937              <span class="error" ng-show="myForm.input.$error.required">
22938                Required!</span>
22939              <span class="error" ng-show="myForm.input.$error.pattern">
22940                Single word only!</span>
22941            </div>
22942            <code>text = {{example.text}}</code><br/>
22943            <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br/>
22944            <code>myForm.input.$error = {{myForm.input.$error}}</code><br/>
22945            <code>myForm.$valid = {{myForm.$valid}}</code><br/>
22946            <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br/>
22947           </form>
22948         </file>
22949         <file name="protractor.js" type="protractor">
22950           var text = element(by.binding('example.text'));
22951           var valid = element(by.binding('myForm.input.$valid'));
22952           var input = element(by.model('example.text'));
22954           it('should initialize to model', function() {
22955             expect(text.getText()).toContain('guest');
22956             expect(valid.getText()).toContain('true');
22957           });
22959           it('should be invalid if empty', function() {
22960             input.clear();
22961             input.sendKeys('');
22963             expect(text.getText()).toEqual('text =');
22964             expect(valid.getText()).toContain('false');
22965           });
22967           it('should be invalid if multi word', function() {
22968             input.clear();
22969             input.sendKeys('hello world');
22971             expect(valid.getText()).toContain('false');
22972           });
22973         </file>
22974       </example>
22975    */
22976   'text': textInputType,
22978     /**
22979      * @ngdoc input
22980      * @name input[date]
22981      *
22982      * @description
22983      * Input with date validation and transformation. In browsers that do not yet support
22984      * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
22985      * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
22986      * modern browsers do not yet support this input type, it is important to provide cues to users on the
22987      * expected input format via a placeholder or label.
22988      *
22989      * The model must always be a Date object, otherwise Angular will throw an error.
22990      * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
22991      *
22992      * The timezone to be used to read/write the `Date` instance in the model can be defined using
22993      * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
22994      *
22995      * @param {string} ngModel Assignable angular expression to data-bind to.
22996      * @param {string=} name Property name of the form under which the control is published.
22997      * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
22998      *   valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
22999      *   (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
23000      *   constraint validation.
23001      * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
23002      *   a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
23003      *   (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
23004      *   constraint validation.
23005      * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
23006      *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
23007      * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
23008      *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
23009      * @param {string=} required Sets `required` validation error key if the value is not entered.
23010      * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23011      *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23012      *    `required` when you want to data-bind to the `required` attribute.
23013      * @param {string=} ngChange Angular expression to be executed when input changes due to user
23014      *    interaction with the input element.
23015      *
23016      * @example
23017      <example name="date-input-directive" module="dateInputExample">
23018      <file name="index.html">
23019        <script>
23020           angular.module('dateInputExample', [])
23021             .controller('DateController', ['$scope', function($scope) {
23022               $scope.example = {
23023                 value: new Date(2013, 9, 22)
23024               };
23025             }]);
23026        </script>
23027        <form name="myForm" ng-controller="DateController as dateCtrl">
23028           <label for="exampleInput">Pick a date in 2013:</label>
23029           <input type="date" id="exampleInput" name="input" ng-model="example.value"
23030               placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
23031           <div role="alert">
23032             <span class="error" ng-show="myForm.input.$error.required">
23033                 Required!</span>
23034             <span class="error" ng-show="myForm.input.$error.date">
23035                 Not a valid date!</span>
23036            </div>
23037            <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
23038            <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
23039            <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
23040            <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
23041            <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
23042        </form>
23043      </file>
23044      <file name="protractor.js" type="protractor">
23045         var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
23046         var valid = element(by.binding('myForm.input.$valid'));
23047         var input = element(by.model('example.value'));
23049         // currently protractor/webdriver does not support
23050         // sending keys to all known HTML5 input controls
23051         // for various browsers (see https://github.com/angular/protractor/issues/562).
23052         function setInput(val) {
23053           // set the value of the element and force validation.
23054           var scr = "var ipt = document.getElementById('exampleInput'); " +
23055           "ipt.value = '" + val + "';" +
23056           "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
23057           browser.executeScript(scr);
23058         }
23060         it('should initialize to model', function() {
23061           expect(value.getText()).toContain('2013-10-22');
23062           expect(valid.getText()).toContain('myForm.input.$valid = true');
23063         });
23065         it('should be invalid if empty', function() {
23066           setInput('');
23067           expect(value.getText()).toEqual('value =');
23068           expect(valid.getText()).toContain('myForm.input.$valid = false');
23069         });
23071         it('should be invalid if over max', function() {
23072           setInput('2015-01-01');
23073           expect(value.getText()).toContain('');
23074           expect(valid.getText()).toContain('myForm.input.$valid = false');
23075         });
23076      </file>
23077      </example>
23078      */
23079   'date': createDateInputType('date', DATE_REGEXP,
23080          createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
23081          'yyyy-MM-dd'),
23083    /**
23084     * @ngdoc input
23085     * @name input[datetime-local]
23086     *
23087     * @description
23088     * Input with datetime validation and transformation. In browsers that do not yet support
23089     * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
23090     * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
23091     *
23092     * The model must always be a Date object, otherwise Angular will throw an error.
23093     * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
23094     *
23095     * The timezone to be used to read/write the `Date` instance in the model can be defined using
23096     * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
23097     *
23098     * @param {string} ngModel Assignable angular expression to data-bind to.
23099     * @param {string=} name Property name of the form under which the control is published.
23100     * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
23101     *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
23102     *   inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
23103     *   Note that `min` will also add native HTML5 constraint validation.
23104     * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
23105     *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
23106     *   inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
23107     *   Note that `max` will also add native HTML5 constraint validation.
23108     * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
23109     *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
23110     * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
23111     *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
23112     * @param {string=} required Sets `required` validation error key if the value is not entered.
23113     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23114     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23115     *    `required` when you want to data-bind to the `required` attribute.
23116     * @param {string=} ngChange Angular expression to be executed when input changes due to user
23117     *    interaction with the input element.
23118     *
23119     * @example
23120     <example name="datetimelocal-input-directive" module="dateExample">
23121     <file name="index.html">
23122       <script>
23123         angular.module('dateExample', [])
23124           .controller('DateController', ['$scope', function($scope) {
23125             $scope.example = {
23126               value: new Date(2010, 11, 28, 14, 57)
23127             };
23128           }]);
23129       </script>
23130       <form name="myForm" ng-controller="DateController as dateCtrl">
23131         <label for="exampleInput">Pick a date between in 2013:</label>
23132         <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
23133             placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
23134         <div role="alert">
23135           <span class="error" ng-show="myForm.input.$error.required">
23136               Required!</span>
23137           <span class="error" ng-show="myForm.input.$error.datetimelocal">
23138               Not a valid date!</span>
23139         </div>
23140         <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
23141         <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
23142         <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
23143         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
23144         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
23145       </form>
23146     </file>
23147     <file name="protractor.js" type="protractor">
23148       var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
23149       var valid = element(by.binding('myForm.input.$valid'));
23150       var input = element(by.model('example.value'));
23152       // currently protractor/webdriver does not support
23153       // sending keys to all known HTML5 input controls
23154       // for various browsers (https://github.com/angular/protractor/issues/562).
23155       function setInput(val) {
23156         // set the value of the element and force validation.
23157         var scr = "var ipt = document.getElementById('exampleInput'); " +
23158         "ipt.value = '" + val + "';" +
23159         "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
23160         browser.executeScript(scr);
23161       }
23163       it('should initialize to model', function() {
23164         expect(value.getText()).toContain('2010-12-28T14:57:00');
23165         expect(valid.getText()).toContain('myForm.input.$valid = true');
23166       });
23168       it('should be invalid if empty', function() {
23169         setInput('');
23170         expect(value.getText()).toEqual('value =');
23171         expect(valid.getText()).toContain('myForm.input.$valid = false');
23172       });
23174       it('should be invalid if over max', function() {
23175         setInput('2015-01-01T23:59:00');
23176         expect(value.getText()).toContain('');
23177         expect(valid.getText()).toContain('myForm.input.$valid = false');
23178       });
23179     </file>
23180     </example>
23181     */
23182   'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
23183       createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
23184       'yyyy-MM-ddTHH:mm:ss.sss'),
23186   /**
23187    * @ngdoc input
23188    * @name input[time]
23189    *
23190    * @description
23191    * Input with time validation and transformation. In browsers that do not yet support
23192    * the HTML5 time input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
23193    * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
23194    * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
23195    *
23196    * The model must always be a Date object, otherwise Angular will throw an error.
23197    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
23198    *
23199    * The timezone to be used to read/write the `Date` instance in the model can be defined using
23200    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
23201    *
23202    * @param {string} ngModel Assignable angular expression to data-bind to.
23203    * @param {string=} name Property name of the form under which the control is published.
23204    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
23205    *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
23206    *   attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
23207    *   native HTML5 constraint validation.
23208    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
23209    *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
23210    *   attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
23211    *   native HTML5 constraint validation.
23212    * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
23213    *   `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
23214    * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
23215    *   `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
23216    * @param {string=} required Sets `required` validation error key if the value is not entered.
23217    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23218    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23219    *    `required` when you want to data-bind to the `required` attribute.
23220    * @param {string=} ngChange Angular expression to be executed when input changes due to user
23221    *    interaction with the input element.
23222    *
23223    * @example
23224    <example name="time-input-directive" module="timeExample">
23225    <file name="index.html">
23226      <script>
23227       angular.module('timeExample', [])
23228         .controller('DateController', ['$scope', function($scope) {
23229           $scope.example = {
23230             value: new Date(1970, 0, 1, 14, 57, 0)
23231           };
23232         }]);
23233      </script>
23234      <form name="myForm" ng-controller="DateController as dateCtrl">
23235         <label for="exampleInput">Pick a time between 8am and 5pm:</label>
23236         <input type="time" id="exampleInput" name="input" ng-model="example.value"
23237             placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
23238         <div role="alert">
23239           <span class="error" ng-show="myForm.input.$error.required">
23240               Required!</span>
23241           <span class="error" ng-show="myForm.input.$error.time">
23242               Not a valid date!</span>
23243         </div>
23244         <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
23245         <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
23246         <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
23247         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
23248         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
23249      </form>
23250    </file>
23251    <file name="protractor.js" type="protractor">
23252       var value = element(by.binding('example.value | date: "HH:mm:ss"'));
23253       var valid = element(by.binding('myForm.input.$valid'));
23254       var input = element(by.model('example.value'));
23256       // currently protractor/webdriver does not support
23257       // sending keys to all known HTML5 input controls
23258       // for various browsers (https://github.com/angular/protractor/issues/562).
23259       function setInput(val) {
23260         // set the value of the element and force validation.
23261         var scr = "var ipt = document.getElementById('exampleInput'); " +
23262         "ipt.value = '" + val + "';" +
23263         "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
23264         browser.executeScript(scr);
23265       }
23267       it('should initialize to model', function() {
23268         expect(value.getText()).toContain('14:57:00');
23269         expect(valid.getText()).toContain('myForm.input.$valid = true');
23270       });
23272       it('should be invalid if empty', function() {
23273         setInput('');
23274         expect(value.getText()).toEqual('value =');
23275         expect(valid.getText()).toContain('myForm.input.$valid = false');
23276       });
23278       it('should be invalid if over max', function() {
23279         setInput('23:59:00');
23280         expect(value.getText()).toContain('');
23281         expect(valid.getText()).toContain('myForm.input.$valid = false');
23282       });
23283    </file>
23284    </example>
23285    */
23286   'time': createDateInputType('time', TIME_REGEXP,
23287       createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
23288      'HH:mm:ss.sss'),
23290    /**
23291     * @ngdoc input
23292     * @name input[week]
23293     *
23294     * @description
23295     * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
23296     * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
23297     * week format (yyyy-W##), for example: `2013-W02`.
23298     *
23299     * The model must always be a Date object, otherwise Angular will throw an error.
23300     * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
23301     *
23302     * The timezone to be used to read/write the `Date` instance in the model can be defined using
23303     * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
23304     *
23305     * @param {string} ngModel Assignable angular expression to data-bind to.
23306     * @param {string=} name Property name of the form under which the control is published.
23307     * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
23308     *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
23309     *   attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
23310     *   native HTML5 constraint validation.
23311     * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
23312     *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
23313     *   attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
23314     *   native HTML5 constraint validation.
23315     * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
23316     *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
23317     * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
23318     *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
23319     * @param {string=} required Sets `required` validation error key if the value is not entered.
23320     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23321     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23322     *    `required` when you want to data-bind to the `required` attribute.
23323     * @param {string=} ngChange Angular expression to be executed when input changes due to user
23324     *    interaction with the input element.
23325     *
23326     * @example
23327     <example name="week-input-directive" module="weekExample">
23328     <file name="index.html">
23329       <script>
23330       angular.module('weekExample', [])
23331         .controller('DateController', ['$scope', function($scope) {
23332           $scope.example = {
23333             value: new Date(2013, 0, 3)
23334           };
23335         }]);
23336       </script>
23337       <form name="myForm" ng-controller="DateController as dateCtrl">
23338         <label>Pick a date between in 2013:
23339           <input id="exampleInput" type="week" name="input" ng-model="example.value"
23340                  placeholder="YYYY-W##" min="2012-W32"
23341                  max="2013-W52" required />
23342         </label>
23343         <div role="alert">
23344           <span class="error" ng-show="myForm.input.$error.required">
23345               Required!</span>
23346           <span class="error" ng-show="myForm.input.$error.week">
23347               Not a valid date!</span>
23348         </div>
23349         <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
23350         <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
23351         <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
23352         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
23353         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
23354       </form>
23355     </file>
23356     <file name="protractor.js" type="protractor">
23357       var value = element(by.binding('example.value | date: "yyyy-Www"'));
23358       var valid = element(by.binding('myForm.input.$valid'));
23359       var input = element(by.model('example.value'));
23361       // currently protractor/webdriver does not support
23362       // sending keys to all known HTML5 input controls
23363       // for various browsers (https://github.com/angular/protractor/issues/562).
23364       function setInput(val) {
23365         // set the value of the element and force validation.
23366         var scr = "var ipt = document.getElementById('exampleInput'); " +
23367         "ipt.value = '" + val + "';" +
23368         "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
23369         browser.executeScript(scr);
23370       }
23372       it('should initialize to model', function() {
23373         expect(value.getText()).toContain('2013-W01');
23374         expect(valid.getText()).toContain('myForm.input.$valid = true');
23375       });
23377       it('should be invalid if empty', function() {
23378         setInput('');
23379         expect(value.getText()).toEqual('value =');
23380         expect(valid.getText()).toContain('myForm.input.$valid = false');
23381       });
23383       it('should be invalid if over max', function() {
23384         setInput('2015-W01');
23385         expect(value.getText()).toContain('');
23386         expect(valid.getText()).toContain('myForm.input.$valid = false');
23387       });
23388     </file>
23389     </example>
23390     */
23391   'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
23393   /**
23394    * @ngdoc input
23395    * @name input[month]
23396    *
23397    * @description
23398    * Input with month validation and transformation. In browsers that do not yet support
23399    * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
23400    * month format (yyyy-MM), for example: `2009-01`.
23401    *
23402    * The model must always be a Date object, otherwise Angular will throw an error.
23403    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
23404    * If the model is not set to the first of the month, the next view to model update will set it
23405    * to the first of the month.
23406    *
23407    * The timezone to be used to read/write the `Date` instance in the model can be defined using
23408    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
23409    *
23410    * @param {string} ngModel Assignable angular expression to data-bind to.
23411    * @param {string=} name Property name of the form under which the control is published.
23412    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
23413    *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
23414    *   attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
23415    *   native HTML5 constraint validation.
23416    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
23417    *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
23418    *   attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
23419    *   native HTML5 constraint validation.
23420    * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
23421    *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
23422    * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
23423    *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
23425    * @param {string=} required Sets `required` validation error key if the value is not entered.
23426    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23427    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23428    *    `required` when you want to data-bind to the `required` attribute.
23429    * @param {string=} ngChange Angular expression to be executed when input changes due to user
23430    *    interaction with the input element.
23431    *
23432    * @example
23433    <example name="month-input-directive" module="monthExample">
23434    <file name="index.html">
23435      <script>
23436       angular.module('monthExample', [])
23437         .controller('DateController', ['$scope', function($scope) {
23438           $scope.example = {
23439             value: new Date(2013, 9, 1)
23440           };
23441         }]);
23442      </script>
23443      <form name="myForm" ng-controller="DateController as dateCtrl">
23444        <label for="exampleInput">Pick a month in 2013:</label>
23445        <input id="exampleInput" type="month" name="input" ng-model="example.value"
23446           placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
23447        <div role="alert">
23448          <span class="error" ng-show="myForm.input.$error.required">
23449             Required!</span>
23450          <span class="error" ng-show="myForm.input.$error.month">
23451             Not a valid month!</span>
23452        </div>
23453        <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
23454        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
23455        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
23456        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
23457        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
23458      </form>
23459    </file>
23460    <file name="protractor.js" type="protractor">
23461       var value = element(by.binding('example.value | date: "yyyy-MM"'));
23462       var valid = element(by.binding('myForm.input.$valid'));
23463       var input = element(by.model('example.value'));
23465       // currently protractor/webdriver does not support
23466       // sending keys to all known HTML5 input controls
23467       // for various browsers (https://github.com/angular/protractor/issues/562).
23468       function setInput(val) {
23469         // set the value of the element and force validation.
23470         var scr = "var ipt = document.getElementById('exampleInput'); " +
23471         "ipt.value = '" + val + "';" +
23472         "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
23473         browser.executeScript(scr);
23474       }
23476       it('should initialize to model', function() {
23477         expect(value.getText()).toContain('2013-10');
23478         expect(valid.getText()).toContain('myForm.input.$valid = true');
23479       });
23481       it('should be invalid if empty', function() {
23482         setInput('');
23483         expect(value.getText()).toEqual('value =');
23484         expect(valid.getText()).toContain('myForm.input.$valid = false');
23485       });
23487       it('should be invalid if over max', function() {
23488         setInput('2015-01');
23489         expect(value.getText()).toContain('');
23490         expect(valid.getText()).toContain('myForm.input.$valid = false');
23491       });
23492    </file>
23493    </example>
23494    */
23495   'month': createDateInputType('month', MONTH_REGEXP,
23496      createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
23497      'yyyy-MM'),
23499   /**
23500    * @ngdoc input
23501    * @name input[number]
23502    *
23503    * @description
23504    * Text input with number validation and transformation. Sets the `number` validation
23505    * error if not a valid number.
23506    *
23507    * <div class="alert alert-warning">
23508    * The model must always be of type `number` otherwise Angular will throw an error.
23509    * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
23510    * error docs for more information and an example of how to convert your model if necessary.
23511    * </div>
23512    *
23513    * ## Issues with HTML5 constraint validation
23514    *
23515    * In browsers that follow the
23516    * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
23517    * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
23518    * If a non-number is entered in the input, the browser will report the value as an empty string,
23519    * which means the view / model values in `ngModel` and subsequently the scope value
23520    * will also be an empty string.
23521    *
23522    *
23523    * @param {string} ngModel Assignable angular expression to data-bind to.
23524    * @param {string=} name Property name of the form under which the control is published.
23525    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
23526    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
23527    * @param {string=} required Sets `required` validation error key if the value is not entered.
23528    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23529    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23530    *    `required` when you want to data-bind to the `required` attribute.
23531    * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
23532    *    minlength.
23533    * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
23534    *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
23535    *    any length.
23536    * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
23537    *    that contains the regular expression body that will be converted to a regular expression
23538    *    as in the ngPattern directive.
23539    * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
23540    *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
23541    *    If the expression evaluates to a RegExp object, then this is used directly.
23542    *    If the expression evaluates to a string, then it will be converted to a RegExp
23543    *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
23544    *    `new RegExp('^abc$')`.<br />
23545    *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
23546    *    start at the index of the last search's match, thus not taking the whole input value into
23547    *    account.
23548    * @param {string=} ngChange Angular expression to be executed when input changes due to user
23549    *    interaction with the input element.
23550    *
23551    * @example
23552       <example name="number-input-directive" module="numberExample">
23553         <file name="index.html">
23554          <script>
23555            angular.module('numberExample', [])
23556              .controller('ExampleController', ['$scope', function($scope) {
23557                $scope.example = {
23558                  value: 12
23559                };
23560              }]);
23561          </script>
23562          <form name="myForm" ng-controller="ExampleController">
23563            <label>Number:
23564              <input type="number" name="input" ng-model="example.value"
23565                     min="0" max="99" required>
23566           </label>
23567            <div role="alert">
23568              <span class="error" ng-show="myForm.input.$error.required">
23569                Required!</span>
23570              <span class="error" ng-show="myForm.input.$error.number">
23571                Not valid number!</span>
23572            </div>
23573            <tt>value = {{example.value}}</tt><br/>
23574            <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
23575            <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
23576            <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
23577            <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
23578           </form>
23579         </file>
23580         <file name="protractor.js" type="protractor">
23581           var value = element(by.binding('example.value'));
23582           var valid = element(by.binding('myForm.input.$valid'));
23583           var input = element(by.model('example.value'));
23585           it('should initialize to model', function() {
23586             expect(value.getText()).toContain('12');
23587             expect(valid.getText()).toContain('true');
23588           });
23590           it('should be invalid if empty', function() {
23591             input.clear();
23592             input.sendKeys('');
23593             expect(value.getText()).toEqual('value =');
23594             expect(valid.getText()).toContain('false');
23595           });
23597           it('should be invalid if over max', function() {
23598             input.clear();
23599             input.sendKeys('123');
23600             expect(value.getText()).toEqual('value =');
23601             expect(valid.getText()).toContain('false');
23602           });
23603         </file>
23604       </example>
23605    */
23606   'number': numberInputType,
23609   /**
23610    * @ngdoc input
23611    * @name input[url]
23612    *
23613    * @description
23614    * Text input with URL validation. Sets the `url` validation error key if the content is not a
23615    * valid URL.
23616    *
23617    * <div class="alert alert-warning">
23618    * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
23619    * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
23620    * the built-in validators (see the {@link guide/forms Forms guide})
23621    * </div>
23622    *
23623    * @param {string} ngModel Assignable angular expression to data-bind to.
23624    * @param {string=} name Property name of the form under which the control is published.
23625    * @param {string=} required Sets `required` validation error key if the value is not entered.
23626    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23627    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23628    *    `required` when you want to data-bind to the `required` attribute.
23629    * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
23630    *    minlength.
23631    * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
23632    *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
23633    *    any length.
23634    * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
23635    *    that contains the regular expression body that will be converted to a regular expression
23636    *    as in the ngPattern directive.
23637    * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
23638    *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
23639    *    If the expression evaluates to a RegExp object, then this is used directly.
23640    *    If the expression evaluates to a string, then it will be converted to a RegExp
23641    *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
23642    *    `new RegExp('^abc$')`.<br />
23643    *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
23644    *    start at the index of the last search's match, thus not taking the whole input value into
23645    *    account.
23646    * @param {string=} ngChange Angular expression to be executed when input changes due to user
23647    *    interaction with the input element.
23648    *
23649    * @example
23650       <example name="url-input-directive" module="urlExample">
23651         <file name="index.html">
23652          <script>
23653            angular.module('urlExample', [])
23654              .controller('ExampleController', ['$scope', function($scope) {
23655                $scope.url = {
23656                  text: 'http://google.com'
23657                };
23658              }]);
23659          </script>
23660          <form name="myForm" ng-controller="ExampleController">
23661            <label>URL:
23662              <input type="url" name="input" ng-model="url.text" required>
23663            <label>
23664            <div role="alert">
23665              <span class="error" ng-show="myForm.input.$error.required">
23666                Required!</span>
23667              <span class="error" ng-show="myForm.input.$error.url">
23668                Not valid url!</span>
23669            </div>
23670            <tt>text = {{url.text}}</tt><br/>
23671            <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
23672            <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
23673            <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
23674            <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
23675            <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
23676           </form>
23677         </file>
23678         <file name="protractor.js" type="protractor">
23679           var text = element(by.binding('url.text'));
23680           var valid = element(by.binding('myForm.input.$valid'));
23681           var input = element(by.model('url.text'));
23683           it('should initialize to model', function() {
23684             expect(text.getText()).toContain('http://google.com');
23685             expect(valid.getText()).toContain('true');
23686           });
23688           it('should be invalid if empty', function() {
23689             input.clear();
23690             input.sendKeys('');
23692             expect(text.getText()).toEqual('text =');
23693             expect(valid.getText()).toContain('false');
23694           });
23696           it('should be invalid if not url', function() {
23697             input.clear();
23698             input.sendKeys('box');
23700             expect(valid.getText()).toContain('false');
23701           });
23702         </file>
23703       </example>
23704    */
23705   'url': urlInputType,
23708   /**
23709    * @ngdoc input
23710    * @name input[email]
23711    *
23712    * @description
23713    * Text input with email validation. Sets the `email` validation error key if not a valid email
23714    * address.
23715    *
23716    * <div class="alert alert-warning">
23717    * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
23718    * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
23719    * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
23720    * </div>
23721    *
23722    * @param {string} ngModel Assignable angular expression to data-bind to.
23723    * @param {string=} name Property name of the form under which the control is published.
23724    * @param {string=} required Sets `required` validation error key if the value is not entered.
23725    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23726    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23727    *    `required` when you want to data-bind to the `required` attribute.
23728    * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
23729    *    minlength.
23730    * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
23731    *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
23732    *    any length.
23733    * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
23734    *    that contains the regular expression body that will be converted to a regular expression
23735    *    as in the ngPattern directive.
23736    * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
23737    *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
23738    *    If the expression evaluates to a RegExp object, then this is used directly.
23739    *    If the expression evaluates to a string, then it will be converted to a RegExp
23740    *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
23741    *    `new RegExp('^abc$')`.<br />
23742    *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
23743    *    start at the index of the last search's match, thus not taking the whole input value into
23744    *    account.
23745    * @param {string=} ngChange Angular expression to be executed when input changes due to user
23746    *    interaction with the input element.
23747    *
23748    * @example
23749       <example name="email-input-directive" module="emailExample">
23750         <file name="index.html">
23751          <script>
23752            angular.module('emailExample', [])
23753              .controller('ExampleController', ['$scope', function($scope) {
23754                $scope.email = {
23755                  text: 'me@example.com'
23756                };
23757              }]);
23758          </script>
23759            <form name="myForm" ng-controller="ExampleController">
23760              <label>Email:
23761                <input type="email" name="input" ng-model="email.text" required>
23762              </label>
23763              <div role="alert">
23764                <span class="error" ng-show="myForm.input.$error.required">
23765                  Required!</span>
23766                <span class="error" ng-show="myForm.input.$error.email">
23767                  Not valid email!</span>
23768              </div>
23769              <tt>text = {{email.text}}</tt><br/>
23770              <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
23771              <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
23772              <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
23773              <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
23774              <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
23775            </form>
23776          </file>
23777         <file name="protractor.js" type="protractor">
23778           var text = element(by.binding('email.text'));
23779           var valid = element(by.binding('myForm.input.$valid'));
23780           var input = element(by.model('email.text'));
23782           it('should initialize to model', function() {
23783             expect(text.getText()).toContain('me@example.com');
23784             expect(valid.getText()).toContain('true');
23785           });
23787           it('should be invalid if empty', function() {
23788             input.clear();
23789             input.sendKeys('');
23790             expect(text.getText()).toEqual('text =');
23791             expect(valid.getText()).toContain('false');
23792           });
23794           it('should be invalid if not email', function() {
23795             input.clear();
23796             input.sendKeys('xxx');
23798             expect(valid.getText()).toContain('false');
23799           });
23800         </file>
23801       </example>
23802    */
23803   'email': emailInputType,
23806   /**
23807    * @ngdoc input
23808    * @name input[radio]
23809    *
23810    * @description
23811    * HTML radio button.
23812    *
23813    * @param {string} ngModel Assignable angular expression to data-bind to.
23814    * @param {string} value The value to which the `ngModel` expression should be set when selected.
23815    *    Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
23816    *    too. Use `ngValue` if you need complex models (`number`, `object`, ...).
23817    * @param {string=} name Property name of the form under which the control is published.
23818    * @param {string=} ngChange Angular expression to be executed when input changes due to user
23819    *    interaction with the input element.
23820    * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
23821    *    is selected. Should be used instead of the `value` attribute if you need
23822    *    a non-string `ngModel` (`boolean`, `array`, ...).
23823    *
23824    * @example
23825       <example name="radio-input-directive" module="radioExample">
23826         <file name="index.html">
23827          <script>
23828            angular.module('radioExample', [])
23829              .controller('ExampleController', ['$scope', function($scope) {
23830                $scope.color = {
23831                  name: 'blue'
23832                };
23833                $scope.specialValue = {
23834                  "id": "12345",
23835                  "value": "green"
23836                };
23837              }]);
23838          </script>
23839          <form name="myForm" ng-controller="ExampleController">
23840            <label>
23841              <input type="radio" ng-model="color.name" value="red">
23842              Red
23843            </label><br/>
23844            <label>
23845              <input type="radio" ng-model="color.name" ng-value="specialValue">
23846              Green
23847            </label><br/>
23848            <label>
23849              <input type="radio" ng-model="color.name" value="blue">
23850              Blue
23851            </label><br/>
23852            <tt>color = {{color.name | json}}</tt><br/>
23853           </form>
23854           Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
23855         </file>
23856         <file name="protractor.js" type="protractor">
23857           it('should change state', function() {
23858             var color = element(by.binding('color.name'));
23860             expect(color.getText()).toContain('blue');
23862             element.all(by.model('color.name')).get(0).click();
23864             expect(color.getText()).toContain('red');
23865           });
23866         </file>
23867       </example>
23868    */
23869   'radio': radioInputType,
23872   /**
23873    * @ngdoc input
23874    * @name input[checkbox]
23875    *
23876    * @description
23877    * HTML checkbox.
23878    *
23879    * @param {string} ngModel Assignable angular expression to data-bind to.
23880    * @param {string=} name Property name of the form under which the control is published.
23881    * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
23882    * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
23883    * @param {string=} ngChange Angular expression to be executed when input changes due to user
23884    *    interaction with the input element.
23885    *
23886    * @example
23887       <example name="checkbox-input-directive" module="checkboxExample">
23888         <file name="index.html">
23889          <script>
23890            angular.module('checkboxExample', [])
23891              .controller('ExampleController', ['$scope', function($scope) {
23892                $scope.checkboxModel = {
23893                 value1 : true,
23894                 value2 : 'YES'
23895               };
23896              }]);
23897          </script>
23898          <form name="myForm" ng-controller="ExampleController">
23899            <label>Value1:
23900              <input type="checkbox" ng-model="checkboxModel.value1">
23901            </label><br/>
23902            <label>Value2:
23903              <input type="checkbox" ng-model="checkboxModel.value2"
23904                     ng-true-value="'YES'" ng-false-value="'NO'">
23905             </label><br/>
23906            <tt>value1 = {{checkboxModel.value1}}</tt><br/>
23907            <tt>value2 = {{checkboxModel.value2}}</tt><br/>
23908           </form>
23909         </file>
23910         <file name="protractor.js" type="protractor">
23911           it('should change state', function() {
23912             var value1 = element(by.binding('checkboxModel.value1'));
23913             var value2 = element(by.binding('checkboxModel.value2'));
23915             expect(value1.getText()).toContain('true');
23916             expect(value2.getText()).toContain('YES');
23918             element(by.model('checkboxModel.value1')).click();
23919             element(by.model('checkboxModel.value2')).click();
23921             expect(value1.getText()).toContain('false');
23922             expect(value2.getText()).toContain('NO');
23923           });
23924         </file>
23925       </example>
23926    */
23927   'checkbox': checkboxInputType,
23929   'hidden': noop,
23930   'button': noop,
23931   'submit': noop,
23932   'reset': noop,
23933   'file': noop
23936 function stringBasedInputType(ctrl) {
23937   ctrl.$formatters.push(function(value) {
23938     return ctrl.$isEmpty(value) ? value : value.toString();
23939   });
23942 function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
23943   baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
23944   stringBasedInputType(ctrl);
23947 function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
23948   var type = lowercase(element[0].type);
23950   // In composition mode, users are still inputing intermediate text buffer,
23951   // hold the listener until composition is done.
23952   // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
23953   if (!$sniffer.android) {
23954     var composing = false;
23956     element.on('compositionstart', function() {
23957       composing = true;
23958     });
23960     element.on('compositionend', function() {
23961       composing = false;
23962       listener();
23963     });
23964   }
23966   var timeout;
23968   var listener = function(ev) {
23969     if (timeout) {
23970       $browser.defer.cancel(timeout);
23971       timeout = null;
23972     }
23973     if (composing) return;
23974     var value = element.val(),
23975         event = ev && ev.type;
23977     // By default we will trim the value
23978     // If the attribute ng-trim exists we will avoid trimming
23979     // If input type is 'password', the value is never trimmed
23980     if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
23981       value = trim(value);
23982     }
23984     // If a control is suffering from bad input (due to native validators), browsers discard its
23985     // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
23986     // control's value is the same empty value twice in a row.
23987     if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
23988       ctrl.$setViewValue(value, event);
23989     }
23990   };
23992   // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
23993   // input event on backspace, delete or cut
23994   if ($sniffer.hasEvent('input')) {
23995     element.on('input', listener);
23996   } else {
23997     var deferListener = function(ev, input, origValue) {
23998       if (!timeout) {
23999         timeout = $browser.defer(function() {
24000           timeout = null;
24001           if (!input || input.value !== origValue) {
24002             listener(ev);
24003           }
24004         });
24005       }
24006     };
24008     element.on('keydown', function(event) {
24009       var key = event.keyCode;
24011       // ignore
24012       //    command            modifiers                   arrows
24013       if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
24015       deferListener(event, this, this.value);
24016     });
24018     // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
24019     if ($sniffer.hasEvent('paste')) {
24020       element.on('paste cut', deferListener);
24021     }
24022   }
24024   // if user paste into input using mouse on older browser
24025   // or form autocomplete on newer browser, we need "change" event to catch it
24026   element.on('change', listener);
24028   // Some native input types (date-family) have the ability to change validity without
24029   // firing any input/change events.
24030   // For these event types, when native validators are present and the browser supports the type,
24031   // check for validity changes on various DOM events.
24032   if (PARTIAL_VALIDATION_TYPES[type] && ctrl.$$hasNativeValidators && type === attr.type) {
24033     element.on(PARTIAL_VALIDATION_EVENTS, function(ev) {
24034       if (!timeout) {
24035         var validity = this[VALIDITY_STATE_PROPERTY];
24036         var origBadInput = validity.badInput;
24037         var origTypeMismatch = validity.typeMismatch;
24038         timeout = $browser.defer(function() {
24039           timeout = null;
24040           if (validity.badInput !== origBadInput || validity.typeMismatch !== origTypeMismatch) {
24041             listener(ev);
24042           }
24043         });
24044       }
24045     });
24046   }
24048   ctrl.$render = function() {
24049     // Workaround for Firefox validation #12102.
24050     var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
24051     if (element.val() !== value) {
24052       element.val(value);
24053     }
24054   };
24057 function weekParser(isoWeek, existingDate) {
24058   if (isDate(isoWeek)) {
24059     return isoWeek;
24060   }
24062   if (isString(isoWeek)) {
24063     WEEK_REGEXP.lastIndex = 0;
24064     var parts = WEEK_REGEXP.exec(isoWeek);
24065     if (parts) {
24066       var year = +parts[1],
24067           week = +parts[2],
24068           hours = 0,
24069           minutes = 0,
24070           seconds = 0,
24071           milliseconds = 0,
24072           firstThurs = getFirstThursdayOfYear(year),
24073           addDays = (week - 1) * 7;
24075       if (existingDate) {
24076         hours = existingDate.getHours();
24077         minutes = existingDate.getMinutes();
24078         seconds = existingDate.getSeconds();
24079         milliseconds = existingDate.getMilliseconds();
24080       }
24082       return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
24083     }
24084   }
24086   return NaN;
24089 function createDateParser(regexp, mapping) {
24090   return function(iso, date) {
24091     var parts, map;
24093     if (isDate(iso)) {
24094       return iso;
24095     }
24097     if (isString(iso)) {
24098       // When a date is JSON'ified to wraps itself inside of an extra
24099       // set of double quotes. This makes the date parsing code unable
24100       // to match the date string and parse it as a date.
24101       if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
24102         iso = iso.substring(1, iso.length - 1);
24103       }
24104       if (ISO_DATE_REGEXP.test(iso)) {
24105         return new Date(iso);
24106       }
24107       regexp.lastIndex = 0;
24108       parts = regexp.exec(iso);
24110       if (parts) {
24111         parts.shift();
24112         if (date) {
24113           map = {
24114             yyyy: date.getFullYear(),
24115             MM: date.getMonth() + 1,
24116             dd: date.getDate(),
24117             HH: date.getHours(),
24118             mm: date.getMinutes(),
24119             ss: date.getSeconds(),
24120             sss: date.getMilliseconds() / 1000
24121           };
24122         } else {
24123           map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
24124         }
24126         forEach(parts, function(part, index) {
24127           if (index < mapping.length) {
24128             map[mapping[index]] = +part;
24129           }
24130         });
24131         return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
24132       }
24133     }
24135     return NaN;
24136   };
24139 function createDateInputType(type, regexp, parseDate, format) {
24140   return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
24141     badInputChecker(scope, element, attr, ctrl);
24142     baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
24143     var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
24144     var previousDate;
24146     ctrl.$$parserName = type;
24147     ctrl.$parsers.push(function(value) {
24148       if (ctrl.$isEmpty(value)) return null;
24149       if (regexp.test(value)) {
24150         // Note: We cannot read ctrl.$modelValue, as there might be a different
24151         // parser/formatter in the processing chain so that the model
24152         // contains some different data format!
24153         var parsedDate = parseDate(value, previousDate);
24154         if (timezone) {
24155           parsedDate = convertTimezoneToLocal(parsedDate, timezone);
24156         }
24157         return parsedDate;
24158       }
24159       return undefined;
24160     });
24162     ctrl.$formatters.push(function(value) {
24163       if (value && !isDate(value)) {
24164         throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
24165       }
24166       if (isValidDate(value)) {
24167         previousDate = value;
24168         if (previousDate && timezone) {
24169           previousDate = convertTimezoneToLocal(previousDate, timezone, true);
24170         }
24171         return $filter('date')(value, format, timezone);
24172       } else {
24173         previousDate = null;
24174         return '';
24175       }
24176     });
24178     if (isDefined(attr.min) || attr.ngMin) {
24179       var minVal;
24180       ctrl.$validators.min = function(value) {
24181         return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
24182       };
24183       attr.$observe('min', function(val) {
24184         minVal = parseObservedDateValue(val);
24185         ctrl.$validate();
24186       });
24187     }
24189     if (isDefined(attr.max) || attr.ngMax) {
24190       var maxVal;
24191       ctrl.$validators.max = function(value) {
24192         return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
24193       };
24194       attr.$observe('max', function(val) {
24195         maxVal = parseObservedDateValue(val);
24196         ctrl.$validate();
24197       });
24198     }
24200     function isValidDate(value) {
24201       // Invalid Date: getTime() returns NaN
24202       return value && !(value.getTime && value.getTime() !== value.getTime());
24203     }
24205     function parseObservedDateValue(val) {
24206       return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
24207     }
24208   };
24211 function badInputChecker(scope, element, attr, ctrl) {
24212   var node = element[0];
24213   var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
24214   if (nativeValidation) {
24215     ctrl.$parsers.push(function(value) {
24216       var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
24217       return validity.badInput || validity.typeMismatch ? undefined : value;
24218     });
24219   }
24222 function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
24223   badInputChecker(scope, element, attr, ctrl);
24224   baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
24226   ctrl.$$parserName = 'number';
24227   ctrl.$parsers.push(function(value) {
24228     if (ctrl.$isEmpty(value))      return null;
24229     if (NUMBER_REGEXP.test(value)) return parseFloat(value);
24230     return undefined;
24231   });
24233   ctrl.$formatters.push(function(value) {
24234     if (!ctrl.$isEmpty(value)) {
24235       if (!isNumber(value)) {
24236         throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
24237       }
24238       value = value.toString();
24239     }
24240     return value;
24241   });
24243   if (isDefined(attr.min) || attr.ngMin) {
24244     var minVal;
24245     ctrl.$validators.min = function(value) {
24246       return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
24247     };
24249     attr.$observe('min', function(val) {
24250       if (isDefined(val) && !isNumber(val)) {
24251         val = parseFloat(val);
24252       }
24253       minVal = isNumber(val) && !isNaN(val) ? val : undefined;
24254       // TODO(matsko): implement validateLater to reduce number of validations
24255       ctrl.$validate();
24256     });
24257   }
24259   if (isDefined(attr.max) || attr.ngMax) {
24260     var maxVal;
24261     ctrl.$validators.max = function(value) {
24262       return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
24263     };
24265     attr.$observe('max', function(val) {
24266       if (isDefined(val) && !isNumber(val)) {
24267         val = parseFloat(val);
24268       }
24269       maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
24270       // TODO(matsko): implement validateLater to reduce number of validations
24271       ctrl.$validate();
24272     });
24273   }
24276 function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
24277   // Note: no badInputChecker here by purpose as `url` is only a validation
24278   // in browsers, i.e. we can always read out input.value even if it is not valid!
24279   baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
24280   stringBasedInputType(ctrl);
24282   ctrl.$$parserName = 'url';
24283   ctrl.$validators.url = function(modelValue, viewValue) {
24284     var value = modelValue || viewValue;
24285     return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
24286   };
24289 function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
24290   // Note: no badInputChecker here by purpose as `url` is only a validation
24291   // in browsers, i.e. we can always read out input.value even if it is not valid!
24292   baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
24293   stringBasedInputType(ctrl);
24295   ctrl.$$parserName = 'email';
24296   ctrl.$validators.email = function(modelValue, viewValue) {
24297     var value = modelValue || viewValue;
24298     return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
24299   };
24302 function radioInputType(scope, element, attr, ctrl) {
24303   // make the name unique, if not defined
24304   if (isUndefined(attr.name)) {
24305     element.attr('name', nextUid());
24306   }
24308   var listener = function(ev) {
24309     if (element[0].checked) {
24310       ctrl.$setViewValue(attr.value, ev && ev.type);
24311     }
24312   };
24314   element.on('click', listener);
24316   ctrl.$render = function() {
24317     var value = attr.value;
24318     element[0].checked = (value == ctrl.$viewValue);
24319   };
24321   attr.$observe('value', ctrl.$render);
24324 function parseConstantExpr($parse, context, name, expression, fallback) {
24325   var parseFn;
24326   if (isDefined(expression)) {
24327     parseFn = $parse(expression);
24328     if (!parseFn.constant) {
24329       throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
24330                                    '`{1}`.', name, expression);
24331     }
24332     return parseFn(context);
24333   }
24334   return fallback;
24337 function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
24338   var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
24339   var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
24341   var listener = function(ev) {
24342     ctrl.$setViewValue(element[0].checked, ev && ev.type);
24343   };
24345   element.on('click', listener);
24347   ctrl.$render = function() {
24348     element[0].checked = ctrl.$viewValue;
24349   };
24351   // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
24352   // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
24353   // it to a boolean.
24354   ctrl.$isEmpty = function(value) {
24355     return value === false;
24356   };
24358   ctrl.$formatters.push(function(value) {
24359     return equals(value, trueValue);
24360   });
24362   ctrl.$parsers.push(function(value) {
24363     return value ? trueValue : falseValue;
24364   });
24369  * @ngdoc directive
24370  * @name textarea
24371  * @restrict E
24373  * @description
24374  * HTML textarea element control with angular data-binding. The data-binding and validation
24375  * properties of this element are exactly the same as those of the
24376  * {@link ng.directive:input input element}.
24378  * @param {string} ngModel Assignable angular expression to data-bind to.
24379  * @param {string=} name Property name of the form under which the control is published.
24380  * @param {string=} required Sets `required` validation error key if the value is not entered.
24381  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
24382  *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
24383  *    `required` when you want to data-bind to the `required` attribute.
24384  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
24385  *    minlength.
24386  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
24387  *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
24388  *    length.
24389  * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
24390  *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
24391  *    If the expression evaluates to a RegExp object, then this is used directly.
24392  *    If the expression evaluates to a string, then it will be converted to a RegExp
24393  *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
24394  *    `new RegExp('^abc$')`.<br />
24395  *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
24396  *    start at the index of the last search's match, thus not taking the whole input value into
24397  *    account.
24398  * @param {string=} ngChange Angular expression to be executed when input changes due to user
24399  *    interaction with the input element.
24400  * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
24401  */
24405  * @ngdoc directive
24406  * @name input
24407  * @restrict E
24409  * @description
24410  * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
24411  * input state control, and validation.
24412  * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
24414  * <div class="alert alert-warning">
24415  * **Note:** Not every feature offered is available for all input types.
24416  * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
24417  * </div>
24419  * @param {string} ngModel Assignable angular expression to data-bind to.
24420  * @param {string=} name Property name of the form under which the control is published.
24421  * @param {string=} required Sets `required` validation error key if the value is not entered.
24422  * @param {boolean=} ngRequired Sets `required` attribute if set to true
24423  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
24424  *    minlength.
24425  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
24426  *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
24427  *    length.
24428  * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
24429  *    value does not match a RegExp found by evaluating the Angular expression given in the attribute value.
24430  *    If the expression evaluates to a RegExp object, then this is used directly.
24431  *    If the expression evaluates to a string, then it will be converted to a RegExp
24432  *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
24433  *    `new RegExp('^abc$')`.<br />
24434  *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
24435  *    start at the index of the last search's match, thus not taking the whole input value into
24436  *    account.
24437  * @param {string=} ngChange Angular expression to be executed when input changes due to user
24438  *    interaction with the input element.
24439  * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
24440  *    This parameter is ignored for input[type=password] controls, which will never trim the
24441  *    input.
24443  * @example
24444     <example name="input-directive" module="inputExample">
24445       <file name="index.html">
24446        <script>
24447           angular.module('inputExample', [])
24448             .controller('ExampleController', ['$scope', function($scope) {
24449               $scope.user = {name: 'guest', last: 'visitor'};
24450             }]);
24451        </script>
24452        <div ng-controller="ExampleController">
24453          <form name="myForm">
24454            <label>
24455               User name:
24456               <input type="text" name="userName" ng-model="user.name" required>
24457            </label>
24458            <div role="alert">
24459              <span class="error" ng-show="myForm.userName.$error.required">
24460               Required!</span>
24461            </div>
24462            <label>
24463               Last name:
24464               <input type="text" name="lastName" ng-model="user.last"
24465               ng-minlength="3" ng-maxlength="10">
24466            </label>
24467            <div role="alert">
24468              <span class="error" ng-show="myForm.lastName.$error.minlength">
24469                Too short!</span>
24470              <span class="error" ng-show="myForm.lastName.$error.maxlength">
24471                Too long!</span>
24472            </div>
24473          </form>
24474          <hr>
24475          <tt>user = {{user}}</tt><br/>
24476          <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
24477          <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
24478          <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
24479          <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
24480          <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24481          <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24482          <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
24483          <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
24484        </div>
24485       </file>
24486       <file name="protractor.js" type="protractor">
24487         var user = element(by.exactBinding('user'));
24488         var userNameValid = element(by.binding('myForm.userName.$valid'));
24489         var lastNameValid = element(by.binding('myForm.lastName.$valid'));
24490         var lastNameError = element(by.binding('myForm.lastName.$error'));
24491         var formValid = element(by.binding('myForm.$valid'));
24492         var userNameInput = element(by.model('user.name'));
24493         var userLastInput = element(by.model('user.last'));
24495         it('should initialize to model', function() {
24496           expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
24497           expect(userNameValid.getText()).toContain('true');
24498           expect(formValid.getText()).toContain('true');
24499         });
24501         it('should be invalid if empty when required', function() {
24502           userNameInput.clear();
24503           userNameInput.sendKeys('');
24505           expect(user.getText()).toContain('{"last":"visitor"}');
24506           expect(userNameValid.getText()).toContain('false');
24507           expect(formValid.getText()).toContain('false');
24508         });
24510         it('should be valid if empty when min length is set', function() {
24511           userLastInput.clear();
24512           userLastInput.sendKeys('');
24514           expect(user.getText()).toContain('{"name":"guest","last":""}');
24515           expect(lastNameValid.getText()).toContain('true');
24516           expect(formValid.getText()).toContain('true');
24517         });
24519         it('should be invalid if less than required min length', function() {
24520           userLastInput.clear();
24521           userLastInput.sendKeys('xx');
24523           expect(user.getText()).toContain('{"name":"guest"}');
24524           expect(lastNameValid.getText()).toContain('false');
24525           expect(lastNameError.getText()).toContain('minlength');
24526           expect(formValid.getText()).toContain('false');
24527         });
24529         it('should be invalid if longer than max length', function() {
24530           userLastInput.clear();
24531           userLastInput.sendKeys('some ridiculously long name');
24533           expect(user.getText()).toContain('{"name":"guest"}');
24534           expect(lastNameValid.getText()).toContain('false');
24535           expect(lastNameError.getText()).toContain('maxlength');
24536           expect(formValid.getText()).toContain('false');
24537         });
24538       </file>
24539     </example>
24540  */
24541 var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
24542     function($browser, $sniffer, $filter, $parse) {
24543   return {
24544     restrict: 'E',
24545     require: ['?ngModel'],
24546     link: {
24547       pre: function(scope, element, attr, ctrls) {
24548         if (ctrls[0]) {
24549           (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
24550                                                               $browser, $filter, $parse);
24551         }
24552       }
24553     }
24554   };
24559 var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
24561  * @ngdoc directive
24562  * @name ngValue
24564  * @description
24565  * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
24566  * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
24567  * the bound value.
24569  * `ngValue` is useful when dynamically generating lists of radio buttons using
24570  * {@link ngRepeat `ngRepeat`}, as shown below.
24572  * Likewise, `ngValue` can be used to generate `<option>` elements for
24573  * the {@link select `select`} element. In that case however, only strings are supported
24574  * for the `value `attribute, so the resulting `ngModel` will always be a string.
24575  * Support for `select` models with non-string values is available via `ngOptions`.
24577  * @element input
24578  * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
24579  *   of the `input` element
24581  * @example
24582     <example name="ngValue-directive" module="valueExample">
24583       <file name="index.html">
24584        <script>
24585           angular.module('valueExample', [])
24586             .controller('ExampleController', ['$scope', function($scope) {
24587               $scope.names = ['pizza', 'unicorns', 'robots'];
24588               $scope.my = { favorite: 'unicorns' };
24589             }]);
24590        </script>
24591         <form ng-controller="ExampleController">
24592           <h2>Which is your favorite?</h2>
24593             <label ng-repeat="name in names" for="{{name}}">
24594               {{name}}
24595               <input type="radio"
24596                      ng-model="my.favorite"
24597                      ng-value="name"
24598                      id="{{name}}"
24599                      name="favorite">
24600             </label>
24601           <div>You chose {{my.favorite}}</div>
24602         </form>
24603       </file>
24604       <file name="protractor.js" type="protractor">
24605         var favorite = element(by.binding('my.favorite'));
24607         it('should initialize to model', function() {
24608           expect(favorite.getText()).toContain('unicorns');
24609         });
24610         it('should bind the values to the inputs', function() {
24611           element.all(by.model('my.favorite')).get(0).click();
24612           expect(favorite.getText()).toContain('pizza');
24613         });
24614       </file>
24615     </example>
24616  */
24617 var ngValueDirective = function() {
24618   return {
24619     restrict: 'A',
24620     priority: 100,
24621     compile: function(tpl, tplAttr) {
24622       if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
24623         return function ngValueConstantLink(scope, elm, attr) {
24624           attr.$set('value', scope.$eval(attr.ngValue));
24625         };
24626       } else {
24627         return function ngValueLink(scope, elm, attr) {
24628           scope.$watch(attr.ngValue, function valueWatchAction(value) {
24629             attr.$set('value', value);
24630           });
24631         };
24632       }
24633     }
24634   };
24638  * @ngdoc directive
24639  * @name ngBind
24640  * @restrict AC
24642  * @description
24643  * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
24644  * with the value of a given expression, and to update the text content when the value of that
24645  * expression changes.
24647  * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
24648  * `{{ expression }}` which is similar but less verbose.
24650  * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
24651  * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
24652  * element attribute, it makes the bindings invisible to the user while the page is loading.
24654  * An alternative solution to this problem would be using the
24655  * {@link ng.directive:ngCloak ngCloak} directive.
24658  * @element ANY
24659  * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
24661  * @example
24662  * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
24663    <example module="bindExample">
24664      <file name="index.html">
24665        <script>
24666          angular.module('bindExample', [])
24667            .controller('ExampleController', ['$scope', function($scope) {
24668              $scope.name = 'Whirled';
24669            }]);
24670        </script>
24671        <div ng-controller="ExampleController">
24672          <label>Enter name: <input type="text" ng-model="name"></label><br>
24673          Hello <span ng-bind="name"></span>!
24674        </div>
24675      </file>
24676      <file name="protractor.js" type="protractor">
24677        it('should check ng-bind', function() {
24678          var nameInput = element(by.model('name'));
24680          expect(element(by.binding('name')).getText()).toBe('Whirled');
24681          nameInput.clear();
24682          nameInput.sendKeys('world');
24683          expect(element(by.binding('name')).getText()).toBe('world');
24684        });
24685      </file>
24686    </example>
24687  */
24688 var ngBindDirective = ['$compile', function($compile) {
24689   return {
24690     restrict: 'AC',
24691     compile: function ngBindCompile(templateElement) {
24692       $compile.$$addBindingClass(templateElement);
24693       return function ngBindLink(scope, element, attr) {
24694         $compile.$$addBindingInfo(element, attr.ngBind);
24695         element = element[0];
24696         scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
24697           element.textContent = isUndefined(value) ? '' : value;
24698         });
24699       };
24700     }
24701   };
24706  * @ngdoc directive
24707  * @name ngBindTemplate
24709  * @description
24710  * The `ngBindTemplate` directive specifies that the element
24711  * text content should be replaced with the interpolation of the template
24712  * in the `ngBindTemplate` attribute.
24713  * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
24714  * expressions. This directive is needed since some HTML elements
24715  * (such as TITLE and OPTION) cannot contain SPAN elements.
24717  * @element ANY
24718  * @param {string} ngBindTemplate template of form
24719  *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
24721  * @example
24722  * Try it here: enter text in text box and watch the greeting change.
24723    <example module="bindExample">
24724      <file name="index.html">
24725        <script>
24726          angular.module('bindExample', [])
24727            .controller('ExampleController', ['$scope', function($scope) {
24728              $scope.salutation = 'Hello';
24729              $scope.name = 'World';
24730            }]);
24731        </script>
24732        <div ng-controller="ExampleController">
24733         <label>Salutation: <input type="text" ng-model="salutation"></label><br>
24734         <label>Name: <input type="text" ng-model="name"></label><br>
24735         <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
24736        </div>
24737      </file>
24738      <file name="protractor.js" type="protractor">
24739        it('should check ng-bind', function() {
24740          var salutationElem = element(by.binding('salutation'));
24741          var salutationInput = element(by.model('salutation'));
24742          var nameInput = element(by.model('name'));
24744          expect(salutationElem.getText()).toBe('Hello World!');
24746          salutationInput.clear();
24747          salutationInput.sendKeys('Greetings');
24748          nameInput.clear();
24749          nameInput.sendKeys('user');
24751          expect(salutationElem.getText()).toBe('Greetings user!');
24752        });
24753      </file>
24754    </example>
24755  */
24756 var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
24757   return {
24758     compile: function ngBindTemplateCompile(templateElement) {
24759       $compile.$$addBindingClass(templateElement);
24760       return function ngBindTemplateLink(scope, element, attr) {
24761         var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
24762         $compile.$$addBindingInfo(element, interpolateFn.expressions);
24763         element = element[0];
24764         attr.$observe('ngBindTemplate', function(value) {
24765           element.textContent = isUndefined(value) ? '' : value;
24766         });
24767       };
24768     }
24769   };
24774  * @ngdoc directive
24775  * @name ngBindHtml
24777  * @description
24778  * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
24779  * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
24780  * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
24781  * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
24782  * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
24784  * You may also bypass sanitization for values you know are safe. To do so, bind to
24785  * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example
24786  * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
24788  * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
24789  * will have an exception (instead of an exploit.)
24791  * @element ANY
24792  * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
24794  * @example
24796    <example module="bindHtmlExample" deps="angular-sanitize.js">
24797      <file name="index.html">
24798        <div ng-controller="ExampleController">
24799         <p ng-bind-html="myHTML"></p>
24800        </div>
24801      </file>
24803      <file name="script.js">
24804        angular.module('bindHtmlExample', ['ngSanitize'])
24805          .controller('ExampleController', ['$scope', function($scope) {
24806            $scope.myHTML =
24807               'I am an <code>HTML</code>string with ' +
24808               '<a href="#">links!</a> and other <em>stuff</em>';
24809          }]);
24810      </file>
24812      <file name="protractor.js" type="protractor">
24813        it('should check ng-bind-html', function() {
24814          expect(element(by.binding('myHTML')).getText()).toBe(
24815              'I am an HTMLstring with links! and other stuff');
24816        });
24817      </file>
24818    </example>
24819  */
24820 var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
24821   return {
24822     restrict: 'A',
24823     compile: function ngBindHtmlCompile(tElement, tAttrs) {
24824       var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
24825       var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function sceValueOf(val) {
24826         // Unwrap the value to compare the actual inner safe value, not the wrapper object.
24827         return $sce.valueOf(val);
24828       });
24829       $compile.$$addBindingClass(tElement);
24831       return function ngBindHtmlLink(scope, element, attr) {
24832         $compile.$$addBindingInfo(element, attr.ngBindHtml);
24834         scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
24835           // The watched value is the unwrapped value. To avoid re-escaping, use the direct getter.
24836           var value = ngBindHtmlGetter(scope);
24837           element.html($sce.getTrustedHtml(value) || '');
24838         });
24839       };
24840     }
24841   };
24845  * @ngdoc directive
24846  * @name ngChange
24848  * @description
24849  * Evaluate the given expression when the user changes the input.
24850  * The expression is evaluated immediately, unlike the JavaScript onchange event
24851  * which only triggers at the end of a change (usually, when the user leaves the
24852  * form element or presses the return key).
24854  * The `ngChange` expression is only evaluated when a change in the input value causes
24855  * a new value to be committed to the model.
24857  * It will not be evaluated:
24858  * * if the value returned from the `$parsers` transformation pipeline has not changed
24859  * * if the input has continued to be invalid since the model will stay `null`
24860  * * if the model is changed programmatically and not by a change to the input value
24863  * Note, this directive requires `ngModel` to be present.
24865  * @element input
24866  * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
24867  * in input value.
24869  * @example
24870  * <example name="ngChange-directive" module="changeExample">
24871  *   <file name="index.html">
24872  *     <script>
24873  *       angular.module('changeExample', [])
24874  *         .controller('ExampleController', ['$scope', function($scope) {
24875  *           $scope.counter = 0;
24876  *           $scope.change = function() {
24877  *             $scope.counter++;
24878  *           };
24879  *         }]);
24880  *     </script>
24881  *     <div ng-controller="ExampleController">
24882  *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
24883  *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
24884  *       <label for="ng-change-example2">Confirmed</label><br />
24885  *       <tt>debug = {{confirmed}}</tt><br/>
24886  *       <tt>counter = {{counter}}</tt><br/>
24887  *     </div>
24888  *   </file>
24889  *   <file name="protractor.js" type="protractor">
24890  *     var counter = element(by.binding('counter'));
24891  *     var debug = element(by.binding('confirmed'));
24893  *     it('should evaluate the expression if changing from view', function() {
24894  *       expect(counter.getText()).toContain('0');
24896  *       element(by.id('ng-change-example1')).click();
24898  *       expect(counter.getText()).toContain('1');
24899  *       expect(debug.getText()).toContain('true');
24900  *     });
24902  *     it('should not evaluate the expression if changing from model', function() {
24903  *       element(by.id('ng-change-example2')).click();
24905  *       expect(counter.getText()).toContain('0');
24906  *       expect(debug.getText()).toContain('true');
24907  *     });
24908  *   </file>
24909  * </example>
24910  */
24911 var ngChangeDirective = valueFn({
24912   restrict: 'A',
24913   require: 'ngModel',
24914   link: function(scope, element, attr, ctrl) {
24915     ctrl.$viewChangeListeners.push(function() {
24916       scope.$eval(attr.ngChange);
24917     });
24918   }
24921 function classDirective(name, selector) {
24922   name = 'ngClass' + name;
24923   return ['$animate', function($animate) {
24924     return {
24925       restrict: 'AC',
24926       link: function(scope, element, attr) {
24927         var oldVal;
24929         scope.$watch(attr[name], ngClassWatchAction, true);
24931         attr.$observe('class', function(value) {
24932           ngClassWatchAction(scope.$eval(attr[name]));
24933         });
24936         if (name !== 'ngClass') {
24937           scope.$watch('$index', function($index, old$index) {
24938             // jshint bitwise: false
24939             var mod = $index & 1;
24940             if (mod !== (old$index & 1)) {
24941               var classes = arrayClasses(scope.$eval(attr[name]));
24942               mod === selector ?
24943                 addClasses(classes) :
24944                 removeClasses(classes);
24945             }
24946           });
24947         }
24949         function addClasses(classes) {
24950           var newClasses = digestClassCounts(classes, 1);
24951           attr.$addClass(newClasses);
24952         }
24954         function removeClasses(classes) {
24955           var newClasses = digestClassCounts(classes, -1);
24956           attr.$removeClass(newClasses);
24957         }
24959         function digestClassCounts(classes, count) {
24960           // Use createMap() to prevent class assumptions involving property
24961           // names in Object.prototype
24962           var classCounts = element.data('$classCounts') || createMap();
24963           var classesToUpdate = [];
24964           forEach(classes, function(className) {
24965             if (count > 0 || classCounts[className]) {
24966               classCounts[className] = (classCounts[className] || 0) + count;
24967               if (classCounts[className] === +(count > 0)) {
24968                 classesToUpdate.push(className);
24969               }
24970             }
24971           });
24972           element.data('$classCounts', classCounts);
24973           return classesToUpdate.join(' ');
24974         }
24976         function updateClasses(oldClasses, newClasses) {
24977           var toAdd = arrayDifference(newClasses, oldClasses);
24978           var toRemove = arrayDifference(oldClasses, newClasses);
24979           toAdd = digestClassCounts(toAdd, 1);
24980           toRemove = digestClassCounts(toRemove, -1);
24981           if (toAdd && toAdd.length) {
24982             $animate.addClass(element, toAdd);
24983           }
24984           if (toRemove && toRemove.length) {
24985             $animate.removeClass(element, toRemove);
24986           }
24987         }
24989         function ngClassWatchAction(newVal) {
24990           // jshint bitwise: false
24991           if (selector === true || (scope.$index & 1) === selector) {
24992           // jshint bitwise: true
24993             var newClasses = arrayClasses(newVal || []);
24994             if (!oldVal) {
24995               addClasses(newClasses);
24996             } else if (!equals(newVal,oldVal)) {
24997               var oldClasses = arrayClasses(oldVal);
24998               updateClasses(oldClasses, newClasses);
24999             }
25000           }
25001           if (isArray(newVal)) {
25002             oldVal = newVal.map(function(v) { return shallowCopy(v); });
25003           } else {
25004             oldVal = shallowCopy(newVal);
25005           }
25006         }
25007       }
25008     };
25010     function arrayDifference(tokens1, tokens2) {
25011       var values = [];
25013       outer:
25014       for (var i = 0; i < tokens1.length; i++) {
25015         var token = tokens1[i];
25016         for (var j = 0; j < tokens2.length; j++) {
25017           if (token == tokens2[j]) continue outer;
25018         }
25019         values.push(token);
25020       }
25021       return values;
25022     }
25024     function arrayClasses(classVal) {
25025       var classes = [];
25026       if (isArray(classVal)) {
25027         forEach(classVal, function(v) {
25028           classes = classes.concat(arrayClasses(v));
25029         });
25030         return classes;
25031       } else if (isString(classVal)) {
25032         return classVal.split(' ');
25033       } else if (isObject(classVal)) {
25034         forEach(classVal, function(v, k) {
25035           if (v) {
25036             classes = classes.concat(k.split(' '));
25037           }
25038         });
25039         return classes;
25040       }
25041       return classVal;
25042     }
25043   }];
25047  * @ngdoc directive
25048  * @name ngClass
25049  * @restrict AC
25051  * @description
25052  * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
25053  * an expression that represents all classes to be added.
25055  * The directive operates in three different ways, depending on which of three types the expression
25056  * evaluates to:
25058  * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
25059  * names.
25061  * 2. If the expression evaluates to an object, then for each key-value pair of the
25062  * object with a truthy value the corresponding key is used as a class name.
25064  * 3. If the expression evaluates to an array, each element of the array should either be a string as in
25065  * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
25066  * to give you more control over what CSS classes appear. See the code below for an example of this.
25069  * The directive won't add duplicate classes if a particular class was already set.
25071  * When the expression changes, the previously added classes are removed and only then are the
25072  * new classes added.
25074  * @knownIssue
25075  * You should not use {@link guide/interpolation interpolation} in the value of the `class`
25076  * attribute, when using the `ngClass` directive on the same element.
25077  * See {@link guide/interpolation#known-issues here} for more info.
25079  * @animations
25080  * | Animation                        | Occurs                              |
25081  * |----------------------------------|-------------------------------------|
25082  * | {@link ng.$animate#addClass addClass}       | just before the class is applied to the element   |
25083  * | {@link ng.$animate#removeClass removeClass} | just before the class is removed from the element |
25085  * @element ANY
25086  * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
25087  *   of the evaluation can be a string representing space delimited class
25088  *   names, an array, or a map of class names to boolean values. In the case of a map, the
25089  *   names of the properties whose values are truthy will be added as css classes to the
25090  *   element.
25092  * @example Example that demonstrates basic bindings via ngClass directive.
25093    <example>
25094      <file name="index.html">
25095        <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
25096        <label>
25097           <input type="checkbox" ng-model="deleted">
25098           deleted (apply "strike" class)
25099        </label><br>
25100        <label>
25101           <input type="checkbox" ng-model="important">
25102           important (apply "bold" class)
25103        </label><br>
25104        <label>
25105           <input type="checkbox" ng-model="error">
25106           error (apply "has-error" class)
25107        </label>
25108        <hr>
25109        <p ng-class="style">Using String Syntax</p>
25110        <input type="text" ng-model="style"
25111               placeholder="Type: bold strike red" aria-label="Type: bold strike red">
25112        <hr>
25113        <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
25114        <input ng-model="style1"
25115               placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
25116        <input ng-model="style2"
25117               placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
25118        <input ng-model="style3"
25119               placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
25120        <hr>
25121        <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
25122        <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
25123        <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
25124      </file>
25125      <file name="style.css">
25126        .strike {
25127            text-decoration: line-through;
25128        }
25129        .bold {
25130            font-weight: bold;
25131        }
25132        .red {
25133            color: red;
25134        }
25135        .has-error {
25136            color: red;
25137            background-color: yellow;
25138        }
25139        .orange {
25140            color: orange;
25141        }
25142      </file>
25143      <file name="protractor.js" type="protractor">
25144        var ps = element.all(by.css('p'));
25146        it('should let you toggle the class', function() {
25148          expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
25149          expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
25151          element(by.model('important')).click();
25152          expect(ps.first().getAttribute('class')).toMatch(/bold/);
25154          element(by.model('error')).click();
25155          expect(ps.first().getAttribute('class')).toMatch(/has-error/);
25156        });
25158        it('should let you toggle string example', function() {
25159          expect(ps.get(1).getAttribute('class')).toBe('');
25160          element(by.model('style')).clear();
25161          element(by.model('style')).sendKeys('red');
25162          expect(ps.get(1).getAttribute('class')).toBe('red');
25163        });
25165        it('array example should have 3 classes', function() {
25166          expect(ps.get(2).getAttribute('class')).toBe('');
25167          element(by.model('style1')).sendKeys('bold');
25168          element(by.model('style2')).sendKeys('strike');
25169          element(by.model('style3')).sendKeys('red');
25170          expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
25171        });
25173        it('array with map example should have 2 classes', function() {
25174          expect(ps.last().getAttribute('class')).toBe('');
25175          element(by.model('style4')).sendKeys('bold');
25176          element(by.model('warning')).click();
25177          expect(ps.last().getAttribute('class')).toBe('bold orange');
25178        });
25179      </file>
25180    </example>
25182    ## Animations
25184    The example below demonstrates how to perform animations using ngClass.
25186    <example module="ngAnimate" deps="angular-animate.js" animations="true">
25187      <file name="index.html">
25188       <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
25189       <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
25190       <br>
25191       <span class="base-class" ng-class="myVar">Sample Text</span>
25192      </file>
25193      <file name="style.css">
25194        .base-class {
25195          transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
25196        }
25198        .base-class.my-class {
25199          color: red;
25200          font-size:3em;
25201        }
25202      </file>
25203      <file name="protractor.js" type="protractor">
25204        it('should check ng-class', function() {
25205          expect(element(by.css('.base-class')).getAttribute('class')).not.
25206            toMatch(/my-class/);
25208          element(by.id('setbtn')).click();
25210          expect(element(by.css('.base-class')).getAttribute('class')).
25211            toMatch(/my-class/);
25213          element(by.id('clearbtn')).click();
25215          expect(element(by.css('.base-class')).getAttribute('class')).not.
25216            toMatch(/my-class/);
25217        });
25218      </file>
25219    </example>
25222    ## ngClass and pre-existing CSS3 Transitions/Animations
25223    The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
25224    Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
25225    any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
25226    to view the step by step details of {@link $animate#addClass $animate.addClass} and
25227    {@link $animate#removeClass $animate.removeClass}.
25228  */
25229 var ngClassDirective = classDirective('', true);
25232  * @ngdoc directive
25233  * @name ngClassOdd
25234  * @restrict AC
25236  * @description
25237  * The `ngClassOdd` and `ngClassEven` directives work exactly as
25238  * {@link ng.directive:ngClass ngClass}, except they work in
25239  * conjunction with `ngRepeat` and take effect only on odd (even) rows.
25241  * This directive can be applied only within the scope of an
25242  * {@link ng.directive:ngRepeat ngRepeat}.
25244  * @element ANY
25245  * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
25246  *   of the evaluation can be a string representing space delimited class names or an array.
25248  * @example
25249    <example>
25250      <file name="index.html">
25251         <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
25252           <li ng-repeat="name in names">
25253            <span ng-class-odd="'odd'" ng-class-even="'even'">
25254              {{name}}
25255            </span>
25256           </li>
25257         </ol>
25258      </file>
25259      <file name="style.css">
25260        .odd {
25261          color: red;
25262        }
25263        .even {
25264          color: blue;
25265        }
25266      </file>
25267      <file name="protractor.js" type="protractor">
25268        it('should check ng-class-odd and ng-class-even', function() {
25269          expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
25270            toMatch(/odd/);
25271          expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
25272            toMatch(/even/);
25273        });
25274      </file>
25275    </example>
25276  */
25277 var ngClassOddDirective = classDirective('Odd', 0);
25280  * @ngdoc directive
25281  * @name ngClassEven
25282  * @restrict AC
25284  * @description
25285  * The `ngClassOdd` and `ngClassEven` directives work exactly as
25286  * {@link ng.directive:ngClass ngClass}, except they work in
25287  * conjunction with `ngRepeat` and take effect only on odd (even) rows.
25289  * This directive can be applied only within the scope of an
25290  * {@link ng.directive:ngRepeat ngRepeat}.
25292  * @element ANY
25293  * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
25294  *   result of the evaluation can be a string representing space delimited class names or an array.
25296  * @example
25297    <example>
25298      <file name="index.html">
25299         <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
25300           <li ng-repeat="name in names">
25301            <span ng-class-odd="'odd'" ng-class-even="'even'">
25302              {{name}} &nbsp; &nbsp; &nbsp;
25303            </span>
25304           </li>
25305         </ol>
25306      </file>
25307      <file name="style.css">
25308        .odd {
25309          color: red;
25310        }
25311        .even {
25312          color: blue;
25313        }
25314      </file>
25315      <file name="protractor.js" type="protractor">
25316        it('should check ng-class-odd and ng-class-even', function() {
25317          expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
25318            toMatch(/odd/);
25319          expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
25320            toMatch(/even/);
25321        });
25322      </file>
25323    </example>
25324  */
25325 var ngClassEvenDirective = classDirective('Even', 1);
25328  * @ngdoc directive
25329  * @name ngCloak
25330  * @restrict AC
25332  * @description
25333  * The `ngCloak` directive is used to prevent the Angular html template from being briefly
25334  * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
25335  * directive to avoid the undesirable flicker effect caused by the html template display.
25337  * The directive can be applied to the `<body>` element, but the preferred usage is to apply
25338  * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
25339  * of the browser view.
25341  * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
25342  * `angular.min.js`.
25343  * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
25345  * ```css
25346  * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
25347  *   display: none !important;
25348  * }
25349  * ```
25351  * When this css rule is loaded by the browser, all html elements (including their children) that
25352  * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
25353  * during the compilation of the template it deletes the `ngCloak` element attribute, making
25354  * the compiled element visible.
25356  * For the best result, the `angular.js` script must be loaded in the head section of the html
25357  * document; alternatively, the css rule above must be included in the external stylesheet of the
25358  * application.
25360  * @element ANY
25362  * @example
25363    <example>
25364      <file name="index.html">
25365         <div id="template1" ng-cloak>{{ 'hello' }}</div>
25366         <div id="template2" class="ng-cloak">{{ 'world' }}</div>
25367      </file>
25368      <file name="protractor.js" type="protractor">
25369        it('should remove the template directive and css class', function() {
25370          expect($('#template1').getAttribute('ng-cloak')).
25371            toBeNull();
25372          expect($('#template2').getAttribute('ng-cloak')).
25373            toBeNull();
25374        });
25375      </file>
25376    </example>
25378  */
25379 var ngCloakDirective = ngDirective({
25380   compile: function(element, attr) {
25381     attr.$set('ngCloak', undefined);
25382     element.removeClass('ng-cloak');
25383   }
25387  * @ngdoc directive
25388  * @name ngController
25390  * @description
25391  * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
25392  * supports the principles behind the Model-View-Controller design pattern.
25394  * MVC components in angular:
25396  * * Model â€” Models are the properties of a scope; scopes are attached to the DOM where scope properties
25397  *   are accessed through bindings.
25398  * * View â€” The template (HTML with data bindings) that is rendered into the View.
25399  * * Controller â€” The `ngController` directive specifies a Controller class; the class contains business
25400  *   logic behind the application to decorate the scope with functions and values
25402  * Note that you can also attach controllers to the DOM by declaring it in a route definition
25403  * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
25404  * again using `ng-controller` in the template itself.  This will cause the controller to be attached
25405  * and executed twice.
25407  * @element ANY
25408  * @scope
25409  * @priority 500
25410  * @param {expression} ngController Name of a constructor function registered with the current
25411  * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
25412  * that on the current scope evaluates to a constructor function.
25414  * The controller instance can be published into a scope property by specifying
25415  * `ng-controller="as propertyName"`.
25417  * If the current `$controllerProvider` is configured to use globals (via
25418  * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
25419  * also be the name of a globally accessible constructor function (not recommended).
25421  * @example
25422  * Here is a simple form for editing user contact information. Adding, removing, clearing, and
25423  * greeting are methods declared on the controller (see source tab). These methods can
25424  * easily be called from the angular markup. Any changes to the data are automatically reflected
25425  * in the View without the need for a manual update.
25427  * Two different declaration styles are included below:
25429  * * one binds methods and properties directly onto the controller using `this`:
25430  * `ng-controller="SettingsController1 as settings"`
25431  * * one injects `$scope` into the controller:
25432  * `ng-controller="SettingsController2"`
25434  * The second option is more common in the Angular community, and is generally used in boilerplates
25435  * and in this guide. However, there are advantages to binding properties directly to the controller
25436  * and avoiding scope.
25438  * * Using `controller as` makes it obvious which controller you are accessing in the template when
25439  * multiple controllers apply to an element.
25440  * * If you are writing your controllers as classes you have easier access to the properties and
25441  * methods, which will appear on the scope, from inside the controller code.
25442  * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
25443  * inheritance masking primitives.
25445  * This example demonstrates the `controller as` syntax.
25447  * <example name="ngControllerAs" module="controllerAsExample">
25448  *   <file name="index.html">
25449  *    <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
25450  *      <label>Name: <input type="text" ng-model="settings.name"/></label>
25451  *      <button ng-click="settings.greet()">greet</button><br/>
25452  *      Contact:
25453  *      <ul>
25454  *        <li ng-repeat="contact in settings.contacts">
25455  *          <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
25456  *             <option>phone</option>
25457  *             <option>email</option>
25458  *          </select>
25459  *          <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
25460  *          <button ng-click="settings.clearContact(contact)">clear</button>
25461  *          <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
25462  *        </li>
25463  *        <li><button ng-click="settings.addContact()">add</button></li>
25464  *     </ul>
25465  *    </div>
25466  *   </file>
25467  *   <file name="app.js">
25468  *    angular.module('controllerAsExample', [])
25469  *      .controller('SettingsController1', SettingsController1);
25471  *    function SettingsController1() {
25472  *      this.name = "John Smith";
25473  *      this.contacts = [
25474  *        {type: 'phone', value: '408 555 1212'},
25475  *        {type: 'email', value: 'john.smith@example.org'} ];
25476  *    }
25478  *    SettingsController1.prototype.greet = function() {
25479  *      alert(this.name);
25480  *    };
25482  *    SettingsController1.prototype.addContact = function() {
25483  *      this.contacts.push({type: 'email', value: 'yourname@example.org'});
25484  *    };
25486  *    SettingsController1.prototype.removeContact = function(contactToRemove) {
25487  *     var index = this.contacts.indexOf(contactToRemove);
25488  *      this.contacts.splice(index, 1);
25489  *    };
25491  *    SettingsController1.prototype.clearContact = function(contact) {
25492  *      contact.type = 'phone';
25493  *      contact.value = '';
25494  *    };
25495  *   </file>
25496  *   <file name="protractor.js" type="protractor">
25497  *     it('should check controller as', function() {
25498  *       var container = element(by.id('ctrl-as-exmpl'));
25499  *         expect(container.element(by.model('settings.name'))
25500  *           .getAttribute('value')).toBe('John Smith');
25502  *       var firstRepeat =
25503  *           container.element(by.repeater('contact in settings.contacts').row(0));
25504  *       var secondRepeat =
25505  *           container.element(by.repeater('contact in settings.contacts').row(1));
25507  *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
25508  *           .toBe('408 555 1212');
25510  *       expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
25511  *           .toBe('john.smith@example.org');
25513  *       firstRepeat.element(by.buttonText('clear')).click();
25515  *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
25516  *           .toBe('');
25518  *       container.element(by.buttonText('add')).click();
25520  *       expect(container.element(by.repeater('contact in settings.contacts').row(2))
25521  *           .element(by.model('contact.value'))
25522  *           .getAttribute('value'))
25523  *           .toBe('yourname@example.org');
25524  *     });
25525  *   </file>
25526  * </example>
25528  * This example demonstrates the "attach to `$scope`" style of controller.
25530  * <example name="ngController" module="controllerExample">
25531  *  <file name="index.html">
25532  *   <div id="ctrl-exmpl" ng-controller="SettingsController2">
25533  *     <label>Name: <input type="text" ng-model="name"/></label>
25534  *     <button ng-click="greet()">greet</button><br/>
25535  *     Contact:
25536  *     <ul>
25537  *       <li ng-repeat="contact in contacts">
25538  *         <select ng-model="contact.type" id="select_{{$index}}">
25539  *            <option>phone</option>
25540  *            <option>email</option>
25541  *         </select>
25542  *         <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
25543  *         <button ng-click="clearContact(contact)">clear</button>
25544  *         <button ng-click="removeContact(contact)">X</button>
25545  *       </li>
25546  *       <li>[ <button ng-click="addContact()">add</button> ]</li>
25547  *    </ul>
25548  *   </div>
25549  *  </file>
25550  *  <file name="app.js">
25551  *   angular.module('controllerExample', [])
25552  *     .controller('SettingsController2', ['$scope', SettingsController2]);
25554  *   function SettingsController2($scope) {
25555  *     $scope.name = "John Smith";
25556  *     $scope.contacts = [
25557  *       {type:'phone', value:'408 555 1212'},
25558  *       {type:'email', value:'john.smith@example.org'} ];
25560  *     $scope.greet = function() {
25561  *       alert($scope.name);
25562  *     };
25564  *     $scope.addContact = function() {
25565  *       $scope.contacts.push({type:'email', value:'yourname@example.org'});
25566  *     };
25568  *     $scope.removeContact = function(contactToRemove) {
25569  *       var index = $scope.contacts.indexOf(contactToRemove);
25570  *       $scope.contacts.splice(index, 1);
25571  *     };
25573  *     $scope.clearContact = function(contact) {
25574  *       contact.type = 'phone';
25575  *       contact.value = '';
25576  *     };
25577  *   }
25578  *  </file>
25579  *  <file name="protractor.js" type="protractor">
25580  *    it('should check controller', function() {
25581  *      var container = element(by.id('ctrl-exmpl'));
25583  *      expect(container.element(by.model('name'))
25584  *          .getAttribute('value')).toBe('John Smith');
25586  *      var firstRepeat =
25587  *          container.element(by.repeater('contact in contacts').row(0));
25588  *      var secondRepeat =
25589  *          container.element(by.repeater('contact in contacts').row(1));
25591  *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
25592  *          .toBe('408 555 1212');
25593  *      expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
25594  *          .toBe('john.smith@example.org');
25596  *      firstRepeat.element(by.buttonText('clear')).click();
25598  *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
25599  *          .toBe('');
25601  *      container.element(by.buttonText('add')).click();
25603  *      expect(container.element(by.repeater('contact in contacts').row(2))
25604  *          .element(by.model('contact.value'))
25605  *          .getAttribute('value'))
25606  *          .toBe('yourname@example.org');
25607  *    });
25608  *  </file>
25609  *</example>
25611  */
25612 var ngControllerDirective = [function() {
25613   return {
25614     restrict: 'A',
25615     scope: true,
25616     controller: '@',
25617     priority: 500
25618   };
25622  * @ngdoc directive
25623  * @name ngCsp
25625  * @element html
25626  * @description
25628  * Angular has some features that can break certain
25629  * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
25631  * If you intend to implement these rules then you must tell Angular not to use these features.
25633  * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
25636  * The following rules affect Angular:
25638  * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
25639  * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
25640  * increase in the speed of evaluating Angular expressions.
25642  * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
25643  * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
25644  * To make these directives work when a CSP rule is blocking inline styles, you must link to the
25645  * `angular-csp.css` in your HTML manually.
25647  * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
25648  * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
25649  * however, triggers a CSP error to be logged in the console:
25651  * ```
25652  * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
25653  * script in the following Content Security Policy directive: "default-src 'self'". Note that
25654  * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
25655  * ```
25657  * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
25658  * directive on an element of the HTML document that appears before the `<script>` tag that loads
25659  * the `angular.js` file.
25661  * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
25663  * You can specify which of the CSP related Angular features should be deactivated by providing
25664  * a value for the `ng-csp` attribute. The options are as follows:
25666  * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
25668  * * no-unsafe-eval: this stops Angular from optimizing $parse with unsafe eval of strings
25670  * You can use these values in the following combinations:
25673  * * No declaration means that Angular will assume that you can do inline styles, but it will do
25674  * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
25675  * of Angular.
25677  * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
25678  * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
25679  * of Angular.
25681  * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
25682  * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
25684  * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
25685  * run eval - no automatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
25687  * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
25688  * styles nor use eval, which is the same as an empty: ng-csp.
25689  * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
25691  * @example
25692  * This example shows how to apply the `ngCsp` directive to the `html` tag.
25693    ```html
25694      <!doctype html>
25695      <html ng-app ng-csp>
25696      ...
25697      ...
25698      </html>
25699    ```
25700   * @example
25701       // Note: the suffix `.csp` in the example name triggers
25702       // csp mode in our http server!
25703       <example name="example.csp" module="cspExample" ng-csp="true">
25704         <file name="index.html">
25705           <div ng-controller="MainController as ctrl">
25706             <div>
25707               <button ng-click="ctrl.inc()" id="inc">Increment</button>
25708               <span id="counter">
25709                 {{ctrl.counter}}
25710               </span>
25711             </div>
25713             <div>
25714               <button ng-click="ctrl.evil()" id="evil">Evil</button>
25715               <span id="evilError">
25716                 {{ctrl.evilError}}
25717               </span>
25718             </div>
25719           </div>
25720         </file>
25721         <file name="script.js">
25722            angular.module('cspExample', [])
25723              .controller('MainController', function() {
25724                 this.counter = 0;
25725                 this.inc = function() {
25726                   this.counter++;
25727                 };
25728                 this.evil = function() {
25729                   // jshint evil:true
25730                   try {
25731                     eval('1+2');
25732                   } catch (e) {
25733                     this.evilError = e.message;
25734                   }
25735                 };
25736               });
25737         </file>
25738         <file name="protractor.js" type="protractor">
25739           var util, webdriver;
25741           var incBtn = element(by.id('inc'));
25742           var counter = element(by.id('counter'));
25743           var evilBtn = element(by.id('evil'));
25744           var evilError = element(by.id('evilError'));
25746           function getAndClearSevereErrors() {
25747             return browser.manage().logs().get('browser').then(function(browserLog) {
25748               return browserLog.filter(function(logEntry) {
25749                 return logEntry.level.value > webdriver.logging.Level.WARNING.value;
25750               });
25751             });
25752           }
25754           function clearErrors() {
25755             getAndClearSevereErrors();
25756           }
25758           function expectNoErrors() {
25759             getAndClearSevereErrors().then(function(filteredLog) {
25760               expect(filteredLog.length).toEqual(0);
25761               if (filteredLog.length) {
25762                 console.log('browser console errors: ' + util.inspect(filteredLog));
25763               }
25764             });
25765           }
25767           function expectError(regex) {
25768             getAndClearSevereErrors().then(function(filteredLog) {
25769               var found = false;
25770               filteredLog.forEach(function(log) {
25771                 if (log.message.match(regex)) {
25772                   found = true;
25773                 }
25774               });
25775               if (!found) {
25776                 throw new Error('expected an error that matches ' + regex);
25777               }
25778             });
25779           }
25781           beforeEach(function() {
25782             util = require('util');
25783             webdriver = require('protractor/node_modules/selenium-webdriver');
25784           });
25786           // For now, we only test on Chrome,
25787           // as Safari does not load the page with Protractor's injected scripts,
25788           // and Firefox webdriver always disables content security policy (#6358)
25789           if (browser.params.browser !== 'chrome') {
25790             return;
25791           }
25793           it('should not report errors when the page is loaded', function() {
25794             // clear errors so we are not dependent on previous tests
25795             clearErrors();
25796             // Need to reload the page as the page is already loaded when
25797             // we come here
25798             browser.driver.getCurrentUrl().then(function(url) {
25799               browser.get(url);
25800             });
25801             expectNoErrors();
25802           });
25804           it('should evaluate expressions', function() {
25805             expect(counter.getText()).toEqual('0');
25806             incBtn.click();
25807             expect(counter.getText()).toEqual('1');
25808             expectNoErrors();
25809           });
25811           it('should throw and report an error when using "eval"', function() {
25812             evilBtn.click();
25813             expect(evilError.getText()).toMatch(/Content Security Policy/);
25814             expectError(/Content Security Policy/);
25815           });
25816         </file>
25817       </example>
25818   */
25820 // ngCsp is not implemented as a proper directive any more, because we need it be processed while we
25821 // bootstrap the system (before $parse is instantiated), for this reason we just have
25822 // the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
25825  * @ngdoc directive
25826  * @name ngClick
25828  * @description
25829  * The ngClick directive allows you to specify custom behavior when
25830  * an element is clicked.
25832  * @element ANY
25833  * @priority 0
25834  * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
25835  * click. ({@link guide/expression#-event- Event object is available as `$event`})
25837  * @example
25838    <example>
25839      <file name="index.html">
25840       <button ng-click="count = count + 1" ng-init="count=0">
25841         Increment
25842       </button>
25843       <span>
25844         count: {{count}}
25845       </span>
25846      </file>
25847      <file name="protractor.js" type="protractor">
25848        it('should check ng-click', function() {
25849          expect(element(by.binding('count')).getText()).toMatch('0');
25850          element(by.css('button')).click();
25851          expect(element(by.binding('count')).getText()).toMatch('1');
25852        });
25853      </file>
25854    </example>
25855  */
25857  * A collection of directives that allows creation of custom event handlers that are defined as
25858  * angular expressions and are compiled and executed within the current scope.
25859  */
25860 var ngEventDirectives = {};
25862 // For events that might fire synchronously during DOM manipulation
25863 // we need to execute their event handlers asynchronously using $evalAsync,
25864 // so that they are not executed in an inconsistent state.
25865 var forceAsyncEvents = {
25866   'blur': true,
25867   'focus': true
25869 forEach(
25870   'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
25871   function(eventName) {
25872     var directiveName = directiveNormalize('ng-' + eventName);
25873     ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
25874       return {
25875         restrict: 'A',
25876         compile: function($element, attr) {
25877           // We expose the powerful $event object on the scope that provides access to the Window,
25878           // etc. that isn't protected by the fast paths in $parse.  We explicitly request better
25879           // checks at the cost of speed since event handler expressions are not executed as
25880           // frequently as regular change detection.
25881           var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
25882           return function ngEventHandler(scope, element) {
25883             element.on(eventName, function(event) {
25884               var callback = function() {
25885                 fn(scope, {$event:event});
25886               };
25887               if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
25888                 scope.$evalAsync(callback);
25889               } else {
25890                 scope.$apply(callback);
25891               }
25892             });
25893           };
25894         }
25895       };
25896     }];
25897   }
25901  * @ngdoc directive
25902  * @name ngDblclick
25904  * @description
25905  * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
25907  * @element ANY
25908  * @priority 0
25909  * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
25910  * a dblclick. (The Event object is available as `$event`)
25912  * @example
25913    <example>
25914      <file name="index.html">
25915       <button ng-dblclick="count = count + 1" ng-init="count=0">
25916         Increment (on double click)
25917       </button>
25918       count: {{count}}
25919      </file>
25920    </example>
25921  */
25925  * @ngdoc directive
25926  * @name ngMousedown
25928  * @description
25929  * The ngMousedown directive allows you to specify custom behavior on mousedown event.
25931  * @element ANY
25932  * @priority 0
25933  * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
25934  * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
25936  * @example
25937    <example>
25938      <file name="index.html">
25939       <button ng-mousedown="count = count + 1" ng-init="count=0">
25940         Increment (on mouse down)
25941       </button>
25942       count: {{count}}
25943      </file>
25944    </example>
25945  */
25949  * @ngdoc directive
25950  * @name ngMouseup
25952  * @description
25953  * Specify custom behavior on mouseup event.
25955  * @element ANY
25956  * @priority 0
25957  * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
25958  * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
25960  * @example
25961    <example>
25962      <file name="index.html">
25963       <button ng-mouseup="count = count + 1" ng-init="count=0">
25964         Increment (on mouse up)
25965       </button>
25966       count: {{count}}
25967      </file>
25968    </example>
25969  */
25972  * @ngdoc directive
25973  * @name ngMouseover
25975  * @description
25976  * Specify custom behavior on mouseover event.
25978  * @element ANY
25979  * @priority 0
25980  * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
25981  * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
25983  * @example
25984    <example>
25985      <file name="index.html">
25986       <button ng-mouseover="count = count + 1" ng-init="count=0">
25987         Increment (when mouse is over)
25988       </button>
25989       count: {{count}}
25990      </file>
25991    </example>
25992  */
25996  * @ngdoc directive
25997  * @name ngMouseenter
25999  * @description
26000  * Specify custom behavior on mouseenter event.
26002  * @element ANY
26003  * @priority 0
26004  * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
26005  * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
26007  * @example
26008    <example>
26009      <file name="index.html">
26010       <button ng-mouseenter="count = count + 1" ng-init="count=0">
26011         Increment (when mouse enters)
26012       </button>
26013       count: {{count}}
26014      </file>
26015    </example>
26016  */
26020  * @ngdoc directive
26021  * @name ngMouseleave
26023  * @description
26024  * Specify custom behavior on mouseleave event.
26026  * @element ANY
26027  * @priority 0
26028  * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
26029  * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
26031  * @example
26032    <example>
26033      <file name="index.html">
26034       <button ng-mouseleave="count = count + 1" ng-init="count=0">
26035         Increment (when mouse leaves)
26036       </button>
26037       count: {{count}}
26038      </file>
26039    </example>
26040  */
26044  * @ngdoc directive
26045  * @name ngMousemove
26047  * @description
26048  * Specify custom behavior on mousemove event.
26050  * @element ANY
26051  * @priority 0
26052  * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
26053  * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
26055  * @example
26056    <example>
26057      <file name="index.html">
26058       <button ng-mousemove="count = count + 1" ng-init="count=0">
26059         Increment (when mouse moves)
26060       </button>
26061       count: {{count}}
26062      </file>
26063    </example>
26064  */
26068  * @ngdoc directive
26069  * @name ngKeydown
26071  * @description
26072  * Specify custom behavior on keydown event.
26074  * @element ANY
26075  * @priority 0
26076  * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
26077  * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
26079  * @example
26080    <example>
26081      <file name="index.html">
26082       <input ng-keydown="count = count + 1" ng-init="count=0">
26083       key down count: {{count}}
26084      </file>
26085    </example>
26086  */
26090  * @ngdoc directive
26091  * @name ngKeyup
26093  * @description
26094  * Specify custom behavior on keyup event.
26096  * @element ANY
26097  * @priority 0
26098  * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
26099  * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
26101  * @example
26102    <example>
26103      <file name="index.html">
26104        <p>Typing in the input box below updates the key count</p>
26105        <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
26107        <p>Typing in the input box below updates the keycode</p>
26108        <input ng-keyup="event=$event">
26109        <p>event keyCode: {{ event.keyCode }}</p>
26110        <p>event altKey: {{ event.altKey }}</p>
26111      </file>
26112    </example>
26113  */
26117  * @ngdoc directive
26118  * @name ngKeypress
26120  * @description
26121  * Specify custom behavior on keypress event.
26123  * @element ANY
26124  * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
26125  * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
26126  * and can be interrogated for keyCode, altKey, etc.)
26128  * @example
26129    <example>
26130      <file name="index.html">
26131       <input ng-keypress="count = count + 1" ng-init="count=0">
26132       key press count: {{count}}
26133      </file>
26134    </example>
26135  */
26139  * @ngdoc directive
26140  * @name ngSubmit
26142  * @description
26143  * Enables binding angular expressions to onsubmit events.
26145  * Additionally it prevents the default action (which for form means sending the request to the
26146  * server and reloading the current page), but only if the form does not contain `action`,
26147  * `data-action`, or `x-action` attributes.
26149  * <div class="alert alert-warning">
26150  * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
26151  * `ngSubmit` handlers together. See the
26152  * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
26153  * for a detailed discussion of when `ngSubmit` may be triggered.
26154  * </div>
26156  * @element form
26157  * @priority 0
26158  * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
26159  * ({@link guide/expression#-event- Event object is available as `$event`})
26161  * @example
26162    <example module="submitExample">
26163      <file name="index.html">
26164       <script>
26165         angular.module('submitExample', [])
26166           .controller('ExampleController', ['$scope', function($scope) {
26167             $scope.list = [];
26168             $scope.text = 'hello';
26169             $scope.submit = function() {
26170               if ($scope.text) {
26171                 $scope.list.push(this.text);
26172                 $scope.text = '';
26173               }
26174             };
26175           }]);
26176       </script>
26177       <form ng-submit="submit()" ng-controller="ExampleController">
26178         Enter text and hit enter:
26179         <input type="text" ng-model="text" name="text" />
26180         <input type="submit" id="submit" value="Submit" />
26181         <pre>list={{list}}</pre>
26182       </form>
26183      </file>
26184      <file name="protractor.js" type="protractor">
26185        it('should check ng-submit', function() {
26186          expect(element(by.binding('list')).getText()).toBe('list=[]');
26187          element(by.css('#submit')).click();
26188          expect(element(by.binding('list')).getText()).toContain('hello');
26189          expect(element(by.model('text')).getAttribute('value')).toBe('');
26190        });
26191        it('should ignore empty strings', function() {
26192          expect(element(by.binding('list')).getText()).toBe('list=[]');
26193          element(by.css('#submit')).click();
26194          element(by.css('#submit')).click();
26195          expect(element(by.binding('list')).getText()).toContain('hello');
26196         });
26197      </file>
26198    </example>
26199  */
26202  * @ngdoc directive
26203  * @name ngFocus
26205  * @description
26206  * Specify custom behavior on focus event.
26208  * Note: As the `focus` event is executed synchronously when calling `input.focus()`
26209  * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
26210  * during an `$apply` to ensure a consistent state.
26212  * @element window, input, select, textarea, a
26213  * @priority 0
26214  * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
26215  * focus. ({@link guide/expression#-event- Event object is available as `$event`})
26217  * @example
26218  * See {@link ng.directive:ngClick ngClick}
26219  */
26222  * @ngdoc directive
26223  * @name ngBlur
26225  * @description
26226  * Specify custom behavior on blur event.
26228  * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
26229  * an element has lost focus.
26231  * Note: As the `blur` event is executed synchronously also during DOM manipulations
26232  * (e.g. removing a focussed input),
26233  * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
26234  * during an `$apply` to ensure a consistent state.
26236  * @element window, input, select, textarea, a
26237  * @priority 0
26238  * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
26239  * blur. ({@link guide/expression#-event- Event object is available as `$event`})
26241  * @example
26242  * See {@link ng.directive:ngClick ngClick}
26243  */
26246  * @ngdoc directive
26247  * @name ngCopy
26249  * @description
26250  * Specify custom behavior on copy event.
26252  * @element window, input, select, textarea, a
26253  * @priority 0
26254  * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
26255  * copy. ({@link guide/expression#-event- Event object is available as `$event`})
26257  * @example
26258    <example>
26259      <file name="index.html">
26260       <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
26261       copied: {{copied}}
26262      </file>
26263    </example>
26264  */
26267  * @ngdoc directive
26268  * @name ngCut
26270  * @description
26271  * Specify custom behavior on cut event.
26273  * @element window, input, select, textarea, a
26274  * @priority 0
26275  * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
26276  * cut. ({@link guide/expression#-event- Event object is available as `$event`})
26278  * @example
26279    <example>
26280      <file name="index.html">
26281       <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
26282       cut: {{cut}}
26283      </file>
26284    </example>
26285  */
26288  * @ngdoc directive
26289  * @name ngPaste
26291  * @description
26292  * Specify custom behavior on paste event.
26294  * @element window, input, select, textarea, a
26295  * @priority 0
26296  * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
26297  * paste. ({@link guide/expression#-event- Event object is available as `$event`})
26299  * @example
26300    <example>
26301      <file name="index.html">
26302       <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
26303       pasted: {{paste}}
26304      </file>
26305    </example>
26306  */
26309  * @ngdoc directive
26310  * @name ngIf
26311  * @restrict A
26312  * @multiElement
26314  * @description
26315  * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
26316  * {expression}. If the expression assigned to `ngIf` evaluates to a false
26317  * value then the element is removed from the DOM, otherwise a clone of the
26318  * element is reinserted into the DOM.
26320  * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
26321  * element in the DOM rather than changing its visibility via the `display` css property.  A common
26322  * case when this difference is significant is when using css selectors that rely on an element's
26323  * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
26325  * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
26326  * is created when the element is restored.  The scope created within `ngIf` inherits from
26327  * its parent scope using
26328  * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
26329  * An important implication of this is if `ngModel` is used within `ngIf` to bind to
26330  * a javascript primitive defined in the parent scope. In this case any modifications made to the
26331  * variable within the child scope will override (hide) the value in the parent scope.
26333  * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
26334  * is if an element's class attribute is directly modified after it's compiled, using something like
26335  * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
26336  * the added class will be lost because the original compiled state is used to regenerate the element.
26338  * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
26339  * and `leave` effects.
26341  * @animations
26342  * | Animation                        | Occurs                               |
26343  * |----------------------------------|-------------------------------------|
26344  * | {@link ng.$animate#enter enter}  | just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container |
26345  * | {@link ng.$animate#leave leave}  | just before the `ngIf` contents are removed from the DOM |
26347  * @element ANY
26348  * @scope
26349  * @priority 600
26350  * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
26351  *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
26352  *     element is added to the DOM tree.
26354  * @example
26355   <example module="ngAnimate" deps="angular-animate.js" animations="true">
26356     <file name="index.html">
26357       <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
26358       Show when checked:
26359       <span ng-if="checked" class="animate-if">
26360         This is removed when the checkbox is unchecked.
26361       </span>
26362     </file>
26363     <file name="animations.css">
26364       .animate-if {
26365         background:white;
26366         border:1px solid black;
26367         padding:10px;
26368       }
26370       .animate-if.ng-enter, .animate-if.ng-leave {
26371         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
26372       }
26374       .animate-if.ng-enter,
26375       .animate-if.ng-leave.ng-leave-active {
26376         opacity:0;
26377       }
26379       .animate-if.ng-leave,
26380       .animate-if.ng-enter.ng-enter-active {
26381         opacity:1;
26382       }
26383     </file>
26384   </example>
26385  */
26386 var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
26387   return {
26388     multiElement: true,
26389     transclude: 'element',
26390     priority: 600,
26391     terminal: true,
26392     restrict: 'A',
26393     $$tlb: true,
26394     link: function($scope, $element, $attr, ctrl, $transclude) {
26395         var block, childScope, previousElements;
26396         $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
26398           if (value) {
26399             if (!childScope) {
26400               $transclude(function(clone, newScope) {
26401                 childScope = newScope;
26402                 clone[clone.length++] = $compile.$$createComment('end ngIf', $attr.ngIf);
26403                 // Note: We only need the first/last node of the cloned nodes.
26404                 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
26405                 // by a directive with templateUrl when its template arrives.
26406                 block = {
26407                   clone: clone
26408                 };
26409                 $animate.enter(clone, $element.parent(), $element);
26410               });
26411             }
26412           } else {
26413             if (previousElements) {
26414               previousElements.remove();
26415               previousElements = null;
26416             }
26417             if (childScope) {
26418               childScope.$destroy();
26419               childScope = null;
26420             }
26421             if (block) {
26422               previousElements = getBlockNodes(block.clone);
26423               $animate.leave(previousElements).then(function() {
26424                 previousElements = null;
26425               });
26426               block = null;
26427             }
26428           }
26429         });
26430     }
26431   };
26435  * @ngdoc directive
26436  * @name ngInclude
26437  * @restrict ECA
26439  * @description
26440  * Fetches, compiles and includes an external HTML fragment.
26442  * By default, the template URL is restricted to the same domain and protocol as the
26443  * application document. This is done by calling {@link $sce#getTrustedResourceUrl
26444  * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
26445  * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
26446  * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
26447  * ng.$sce Strict Contextual Escaping}.
26449  * In addition, the browser's
26450  * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
26451  * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
26452  * policy may further restrict whether the template is successfully loaded.
26453  * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
26454  * access on some browsers.
26456  * @animations
26457  * | Animation                        | Occurs                              |
26458  * |----------------------------------|-------------------------------------|
26459  * | {@link ng.$animate#enter enter}  | when the expression changes, on the new include |
26460  * | {@link ng.$animate#leave leave}  | when the expression changes, on the old include |
26462  * The enter and leave animation occur concurrently.
26464  * @scope
26465  * @priority 400
26467  * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
26468  *                 make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
26469  * @param {string=} onload Expression to evaluate when a new partial is loaded.
26470  *                  <div class="alert alert-warning">
26471  *                  **Note:** When using onload on SVG elements in IE11, the browser will try to call
26472  *                  a function with the name on the window element, which will usually throw a
26473  *                  "function is undefined" error. To fix this, you can instead use `data-onload` or a
26474  *                  different form that {@link guide/directive#normalization matches} `onload`.
26475  *                  </div>
26476    *
26477  * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
26478  *                  $anchorScroll} to scroll the viewport after the content is loaded.
26480  *                  - If the attribute is not set, disable scrolling.
26481  *                  - If the attribute is set without value, enable scrolling.
26482  *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
26484  * @example
26485   <example module="includeExample" deps="angular-animate.js" animations="true">
26486     <file name="index.html">
26487      <div ng-controller="ExampleController">
26488        <select ng-model="template" ng-options="t.name for t in templates">
26489         <option value="">(blank)</option>
26490        </select>
26491        url of the template: <code>{{template.url}}</code>
26492        <hr/>
26493        <div class="slide-animate-container">
26494          <div class="slide-animate" ng-include="template.url"></div>
26495        </div>
26496      </div>
26497     </file>
26498     <file name="script.js">
26499       angular.module('includeExample', ['ngAnimate'])
26500         .controller('ExampleController', ['$scope', function($scope) {
26501           $scope.templates =
26502             [ { name: 'template1.html', url: 'template1.html'},
26503               { name: 'template2.html', url: 'template2.html'} ];
26504           $scope.template = $scope.templates[0];
26505         }]);
26506      </file>
26507     <file name="template1.html">
26508       Content of template1.html
26509     </file>
26510     <file name="template2.html">
26511       Content of template2.html
26512     </file>
26513     <file name="animations.css">
26514       .slide-animate-container {
26515         position:relative;
26516         background:white;
26517         border:1px solid black;
26518         height:40px;
26519         overflow:hidden;
26520       }
26522       .slide-animate {
26523         padding:10px;
26524       }
26526       .slide-animate.ng-enter, .slide-animate.ng-leave {
26527         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
26529         position:absolute;
26530         top:0;
26531         left:0;
26532         right:0;
26533         bottom:0;
26534         display:block;
26535         padding:10px;
26536       }
26538       .slide-animate.ng-enter {
26539         top:-50px;
26540       }
26541       .slide-animate.ng-enter.ng-enter-active {
26542         top:0;
26543       }
26545       .slide-animate.ng-leave {
26546         top:0;
26547       }
26548       .slide-animate.ng-leave.ng-leave-active {
26549         top:50px;
26550       }
26551     </file>
26552     <file name="protractor.js" type="protractor">
26553       var templateSelect = element(by.model('template'));
26554       var includeElem = element(by.css('[ng-include]'));
26556       it('should load template1.html', function() {
26557         expect(includeElem.getText()).toMatch(/Content of template1.html/);
26558       });
26560       it('should load template2.html', function() {
26561         if (browser.params.browser == 'firefox') {
26562           // Firefox can't handle using selects
26563           // See https://github.com/angular/protractor/issues/480
26564           return;
26565         }
26566         templateSelect.click();
26567         templateSelect.all(by.css('option')).get(2).click();
26568         expect(includeElem.getText()).toMatch(/Content of template2.html/);
26569       });
26571       it('should change to blank', function() {
26572         if (browser.params.browser == 'firefox') {
26573           // Firefox can't handle using selects
26574           return;
26575         }
26576         templateSelect.click();
26577         templateSelect.all(by.css('option')).get(0).click();
26578         expect(includeElem.isPresent()).toBe(false);
26579       });
26580     </file>
26581   </example>
26582  */
26586  * @ngdoc event
26587  * @name ngInclude#$includeContentRequested
26588  * @eventType emit on the scope ngInclude was declared in
26589  * @description
26590  * Emitted every time the ngInclude content is requested.
26592  * @param {Object} angularEvent Synthetic event object.
26593  * @param {String} src URL of content to load.
26594  */
26598  * @ngdoc event
26599  * @name ngInclude#$includeContentLoaded
26600  * @eventType emit on the current ngInclude scope
26601  * @description
26602  * Emitted every time the ngInclude content is reloaded.
26604  * @param {Object} angularEvent Synthetic event object.
26605  * @param {String} src URL of content to load.
26606  */
26610  * @ngdoc event
26611  * @name ngInclude#$includeContentError
26612  * @eventType emit on the scope ngInclude was declared in
26613  * @description
26614  * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
26616  * @param {Object} angularEvent Synthetic event object.
26617  * @param {String} src URL of content to load.
26618  */
26619 var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
26620                   function($templateRequest,   $anchorScroll,   $animate) {
26621   return {
26622     restrict: 'ECA',
26623     priority: 400,
26624     terminal: true,
26625     transclude: 'element',
26626     controller: angular.noop,
26627     compile: function(element, attr) {
26628       var srcExp = attr.ngInclude || attr.src,
26629           onloadExp = attr.onload || '',
26630           autoScrollExp = attr.autoscroll;
26632       return function(scope, $element, $attr, ctrl, $transclude) {
26633         var changeCounter = 0,
26634             currentScope,
26635             previousElement,
26636             currentElement;
26638         var cleanupLastIncludeContent = function() {
26639           if (previousElement) {
26640             previousElement.remove();
26641             previousElement = null;
26642           }
26643           if (currentScope) {
26644             currentScope.$destroy();
26645             currentScope = null;
26646           }
26647           if (currentElement) {
26648             $animate.leave(currentElement).then(function() {
26649               previousElement = null;
26650             });
26651             previousElement = currentElement;
26652             currentElement = null;
26653           }
26654         };
26656         scope.$watch(srcExp, function ngIncludeWatchAction(src) {
26657           var afterAnimation = function() {
26658             if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
26659               $anchorScroll();
26660             }
26661           };
26662           var thisChangeId = ++changeCounter;
26664           if (src) {
26665             //set the 2nd param to true to ignore the template request error so that the inner
26666             //contents and scope can be cleaned up.
26667             $templateRequest(src, true).then(function(response) {
26668               if (scope.$$destroyed) return;
26670               if (thisChangeId !== changeCounter) return;
26671               var newScope = scope.$new();
26672               ctrl.template = response;
26674               // Note: This will also link all children of ng-include that were contained in the original
26675               // html. If that content contains controllers, ... they could pollute/change the scope.
26676               // However, using ng-include on an element with additional content does not make sense...
26677               // Note: We can't remove them in the cloneAttchFn of $transclude as that
26678               // function is called before linking the content, which would apply child
26679               // directives to non existing elements.
26680               var clone = $transclude(newScope, function(clone) {
26681                 cleanupLastIncludeContent();
26682                 $animate.enter(clone, null, $element).then(afterAnimation);
26683               });
26685               currentScope = newScope;
26686               currentElement = clone;
26688               currentScope.$emit('$includeContentLoaded', src);
26689               scope.$eval(onloadExp);
26690             }, function() {
26691               if (scope.$$destroyed) return;
26693               if (thisChangeId === changeCounter) {
26694                 cleanupLastIncludeContent();
26695                 scope.$emit('$includeContentError', src);
26696               }
26697             });
26698             scope.$emit('$includeContentRequested', src);
26699           } else {
26700             cleanupLastIncludeContent();
26701             ctrl.template = null;
26702           }
26703         });
26704       };
26705     }
26706   };
26709 // This directive is called during the $transclude call of the first `ngInclude` directive.
26710 // It will replace and compile the content of the element with the loaded template.
26711 // We need this directive so that the element content is already filled when
26712 // the link function of another directive on the same element as ngInclude
26713 // is called.
26714 var ngIncludeFillContentDirective = ['$compile',
26715   function($compile) {
26716     return {
26717       restrict: 'ECA',
26718       priority: -400,
26719       require: 'ngInclude',
26720       link: function(scope, $element, $attr, ctrl) {
26721         if (toString.call($element[0]).match(/SVG/)) {
26722           // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
26723           // support innerHTML, so detect this here and try to generate the contents
26724           // specially.
26725           $element.empty();
26726           $compile(jqLiteBuildFragment(ctrl.template, window.document).childNodes)(scope,
26727               function namespaceAdaptedClone(clone) {
26728             $element.append(clone);
26729           }, {futureParentElement: $element});
26730           return;
26731         }
26733         $element.html(ctrl.template);
26734         $compile($element.contents())(scope);
26735       }
26736     };
26737   }];
26740  * @ngdoc directive
26741  * @name ngInit
26742  * @restrict AC
26744  * @description
26745  * The `ngInit` directive allows you to evaluate an expression in the
26746  * current scope.
26748  * <div class="alert alert-danger">
26749  * This directive can be abused to add unnecessary amounts of logic into your templates.
26750  * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
26751  * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
26752  * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
26753  * rather than `ngInit` to initialize values on a scope.
26754  * </div>
26756  * <div class="alert alert-warning">
26757  * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
26758  * sure you have parentheses to ensure correct operator precedence:
26759  * <pre class="prettyprint">
26760  * `<div ng-init="test1 = ($index | toString)"></div>`
26761  * </pre>
26762  * </div>
26764  * @priority 450
26766  * @element ANY
26767  * @param {expression} ngInit {@link guide/expression Expression} to eval.
26769  * @example
26770    <example module="initExample">
26771      <file name="index.html">
26772    <script>
26773      angular.module('initExample', [])
26774        .controller('ExampleController', ['$scope', function($scope) {
26775          $scope.list = [['a', 'b'], ['c', 'd']];
26776        }]);
26777    </script>
26778    <div ng-controller="ExampleController">
26779      <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
26780        <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
26781           <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
26782        </div>
26783      </div>
26784    </div>
26785      </file>
26786      <file name="protractor.js" type="protractor">
26787        it('should alias index positions', function() {
26788          var elements = element.all(by.css('.example-init'));
26789          expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
26790          expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
26791          expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
26792          expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
26793        });
26794      </file>
26795    </example>
26796  */
26797 var ngInitDirective = ngDirective({
26798   priority: 450,
26799   compile: function() {
26800     return {
26801       pre: function(scope, element, attrs) {
26802         scope.$eval(attrs.ngInit);
26803       }
26804     };
26805   }
26809  * @ngdoc directive
26810  * @name ngList
26812  * @description
26813  * Text input that converts between a delimited string and an array of strings. The default
26814  * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
26815  * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
26817  * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
26818  * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
26819  *   list item is respected. This implies that the user of the directive is responsible for
26820  *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
26821  *   tab or newline character.
26822  * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
26823  *   when joining the list items back together) and whitespace around each list item is stripped
26824  *   before it is added to the model.
26826  * ### Example with Validation
26828  * <example name="ngList-directive" module="listExample">
26829  *   <file name="app.js">
26830  *      angular.module('listExample', [])
26831  *        .controller('ExampleController', ['$scope', function($scope) {
26832  *          $scope.names = ['morpheus', 'neo', 'trinity'];
26833  *        }]);
26834  *   </file>
26835  *   <file name="index.html">
26836  *    <form name="myForm" ng-controller="ExampleController">
26837  *      <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
26838  *      <span role="alert">
26839  *        <span class="error" ng-show="myForm.namesInput.$error.required">
26840  *        Required!</span>
26841  *      </span>
26842  *      <br>
26843  *      <tt>names = {{names}}</tt><br/>
26844  *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
26845  *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
26846  *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
26847  *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
26848  *     </form>
26849  *   </file>
26850  *   <file name="protractor.js" type="protractor">
26851  *     var listInput = element(by.model('names'));
26852  *     var names = element(by.exactBinding('names'));
26853  *     var valid = element(by.binding('myForm.namesInput.$valid'));
26854  *     var error = element(by.css('span.error'));
26856  *     it('should initialize to model', function() {
26857  *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
26858  *       expect(valid.getText()).toContain('true');
26859  *       expect(error.getCssValue('display')).toBe('none');
26860  *     });
26862  *     it('should be invalid if empty', function() {
26863  *       listInput.clear();
26864  *       listInput.sendKeys('');
26866  *       expect(names.getText()).toContain('');
26867  *       expect(valid.getText()).toContain('false');
26868  *       expect(error.getCssValue('display')).not.toBe('none');
26869  *     });
26870  *   </file>
26871  * </example>
26873  * ### Example - splitting on newline
26874  * <example name="ngList-directive-newlines">
26875  *   <file name="index.html">
26876  *    <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
26877  *    <pre>{{ list | json }}</pre>
26878  *   </file>
26879  *   <file name="protractor.js" type="protractor">
26880  *     it("should split the text by newlines", function() {
26881  *       var listInput = element(by.model('list'));
26882  *       var output = element(by.binding('list | json'));
26883  *       listInput.sendKeys('abc\ndef\nghi');
26884  *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
26885  *     });
26886  *   </file>
26887  * </example>
26889  * @element input
26890  * @param {string=} ngList optional delimiter that should be used to split the value.
26891  */
26892 var ngListDirective = function() {
26893   return {
26894     restrict: 'A',
26895     priority: 100,
26896     require: 'ngModel',
26897     link: function(scope, element, attr, ctrl) {
26898       // We want to control whitespace trimming so we use this convoluted approach
26899       // to access the ngList attribute, which doesn't pre-trim the attribute
26900       var ngList = element.attr(attr.$attr.ngList) || ', ';
26901       var trimValues = attr.ngTrim !== 'false';
26902       var separator = trimValues ? trim(ngList) : ngList;
26904       var parse = function(viewValue) {
26905         // If the viewValue is invalid (say required but empty) it will be `undefined`
26906         if (isUndefined(viewValue)) return;
26908         var list = [];
26910         if (viewValue) {
26911           forEach(viewValue.split(separator), function(value) {
26912             if (value) list.push(trimValues ? trim(value) : value);
26913           });
26914         }
26916         return list;
26917       };
26919       ctrl.$parsers.push(parse);
26920       ctrl.$formatters.push(function(value) {
26921         if (isArray(value)) {
26922           return value.join(ngList);
26923         }
26925         return undefined;
26926       });
26928       // Override the standard $isEmpty because an empty array means the input is empty.
26929       ctrl.$isEmpty = function(value) {
26930         return !value || !value.length;
26931       };
26932     }
26933   };
26936 /* global VALID_CLASS: true,
26937   INVALID_CLASS: true,
26938   PRISTINE_CLASS: true,
26939   DIRTY_CLASS: true,
26940   UNTOUCHED_CLASS: true,
26941   TOUCHED_CLASS: true,
26944 var VALID_CLASS = 'ng-valid',
26945     INVALID_CLASS = 'ng-invalid',
26946     PRISTINE_CLASS = 'ng-pristine',
26947     DIRTY_CLASS = 'ng-dirty',
26948     UNTOUCHED_CLASS = 'ng-untouched',
26949     TOUCHED_CLASS = 'ng-touched',
26950     PENDING_CLASS = 'ng-pending',
26951     EMPTY_CLASS = 'ng-empty',
26952     NOT_EMPTY_CLASS = 'ng-not-empty';
26954 var ngModelMinErr = minErr('ngModel');
26957  * @ngdoc type
26958  * @name ngModel.NgModelController
26960  * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
26961  * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
26962  * is set.
26963  * @property {*} $modelValue The value in the model that the control is bound to.
26964  * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
26965        the control reads value from the DOM. The functions are called in array order, each passing
26966        its return value through to the next. The last return value is forwarded to the
26967        {@link ngModel.NgModelController#$validators `$validators`} collection.
26969 Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
26970 `$viewValue`}.
26972 Returning `undefined` from a parser means a parse error occurred. In that case,
26973 no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
26974 will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
26975 is set to `true`. The parse error is stored in `ngModel.$error.parse`.
26978  * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
26979        the model value changes. The functions are called in reverse array order, each passing the value through to the
26980        next. The last return value is used as the actual DOM value.
26981        Used to format / convert values for display in the control.
26982  * ```js
26983  * function formatter(value) {
26984  *   if (value) {
26985  *     return value.toUpperCase();
26986  *   }
26987  * }
26988  * ngModel.$formatters.push(formatter);
26989  * ```
26991  * @property {Object.<string, function>} $validators A collection of validators that are applied
26992  *      whenever the model value changes. The key value within the object refers to the name of the
26993  *      validator while the function refers to the validation operation. The validation operation is
26994  *      provided with the model value as an argument and must return a true or false value depending
26995  *      on the response of that validation.
26997  * ```js
26998  * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
26999  *   var value = modelValue || viewValue;
27000  *   return /[0-9]+/.test(value) &&
27001  *          /[a-z]+/.test(value) &&
27002  *          /[A-Z]+/.test(value) &&
27003  *          /\W+/.test(value);
27004  * };
27005  * ```
27007  * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
27008  *      perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
27009  *      is expected to return a promise when it is run during the model validation process. Once the promise
27010  *      is delivered then the validation status will be set to true when fulfilled and false when rejected.
27011  *      When the asynchronous validators are triggered, each of the validators will run in parallel and the model
27012  *      value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
27013  *      is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
27014  *      will only run once all synchronous validators have passed.
27016  * Please note that if $http is used then it is important that the server returns a success HTTP response code
27017  * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
27019  * ```js
27020  * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
27021  *   var value = modelValue || viewValue;
27023  *   // Lookup user by username
27024  *   return $http.get('/api/users/' + value).
27025  *      then(function resolved() {
27026  *        //username exists, this means validation fails
27027  *        return $q.reject('exists');
27028  *      }, function rejected() {
27029  *        //username does not exist, therefore this validation passes
27030  *        return true;
27031  *      });
27032  * };
27033  * ```
27035  * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
27036  *     view value has changed. It is called with no arguments, and its return value is ignored.
27037  *     This can be used in place of additional $watches against the model value.
27039  * @property {Object} $error An object hash with all failing validator ids as keys.
27040  * @property {Object} $pending An object hash with all pending validator ids as keys.
27042  * @property {boolean} $untouched True if control has not lost focus yet.
27043  * @property {boolean} $touched True if control has lost focus.
27044  * @property {boolean} $pristine True if user has not interacted with the control yet.
27045  * @property {boolean} $dirty True if user has already interacted with the control.
27046  * @property {boolean} $valid True if there is no error.
27047  * @property {boolean} $invalid True if at least one error on the control.
27048  * @property {string} $name The name attribute of the control.
27050  * @description
27052  * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
27053  * The controller contains services for data-binding, validation, CSS updates, and value formatting
27054  * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
27055  * listening to DOM events.
27056  * Such DOM related logic should be provided by other directives which make use of
27057  * `NgModelController` for data-binding to control elements.
27058  * Angular provides this DOM logic for most {@link input `input`} elements.
27059  * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
27060  * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
27062  * @example
27063  * ### Custom Control Example
27064  * This example shows how to use `NgModelController` with a custom control to achieve
27065  * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
27066  * collaborate together to achieve the desired result.
27068  * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
27069  * contents be edited in place by the user.
27071  * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
27072  * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
27073  * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
27074  * that content using the `$sce` service.
27076  * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
27077     <file name="style.css">
27078       [contenteditable] {
27079         border: 1px solid black;
27080         background-color: white;
27081         min-height: 20px;
27082       }
27084       .ng-invalid {
27085         border: 1px solid red;
27086       }
27088     </file>
27089     <file name="script.js">
27090       angular.module('customControl', ['ngSanitize']).
27091         directive('contenteditable', ['$sce', function($sce) {
27092           return {
27093             restrict: 'A', // only activate on element attribute
27094             require: '?ngModel', // get a hold of NgModelController
27095             link: function(scope, element, attrs, ngModel) {
27096               if (!ngModel) return; // do nothing if no ng-model
27098               // Specify how UI should be updated
27099               ngModel.$render = function() {
27100                 element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
27101               };
27103               // Listen for change events to enable binding
27104               element.on('blur keyup change', function() {
27105                 scope.$evalAsync(read);
27106               });
27107               read(); // initialize
27109               // Write data to the model
27110               function read() {
27111                 var html = element.html();
27112                 // When we clear the content editable the browser leaves a <br> behind
27113                 // If strip-br attribute is provided then we strip this out
27114                 if ( attrs.stripBr && html == '<br>' ) {
27115                   html = '';
27116                 }
27117                 ngModel.$setViewValue(html);
27118               }
27119             }
27120           };
27121         }]);
27122     </file>
27123     <file name="index.html">
27124       <form name="myForm">
27125        <div contenteditable
27126             name="myWidget" ng-model="userContent"
27127             strip-br="true"
27128             required>Change me!</div>
27129         <span ng-show="myForm.myWidget.$error.required">Required!</span>
27130        <hr>
27131        <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
27132       </form>
27133     </file>
27134     <file name="protractor.js" type="protractor">
27135     it('should data-bind and become invalid', function() {
27136       if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
27137         // SafariDriver can't handle contenteditable
27138         // and Firefox driver can't clear contenteditables very well
27139         return;
27140       }
27141       var contentEditable = element(by.css('[contenteditable]'));
27142       var content = 'Change me!';
27144       expect(contentEditable.getText()).toEqual(content);
27146       contentEditable.clear();
27147       contentEditable.sendKeys(protractor.Key.BACK_SPACE);
27148       expect(contentEditable.getText()).toEqual('');
27149       expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
27150     });
27151     </file>
27152  * </example>
27155  */
27156 var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
27157     function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
27158   this.$viewValue = Number.NaN;
27159   this.$modelValue = Number.NaN;
27160   this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
27161   this.$validators = {};
27162   this.$asyncValidators = {};
27163   this.$parsers = [];
27164   this.$formatters = [];
27165   this.$viewChangeListeners = [];
27166   this.$untouched = true;
27167   this.$touched = false;
27168   this.$pristine = true;
27169   this.$dirty = false;
27170   this.$valid = true;
27171   this.$invalid = false;
27172   this.$error = {}; // keep invalid keys here
27173   this.$$success = {}; // keep valid keys here
27174   this.$pending = undefined; // keep pending keys here
27175   this.$name = $interpolate($attr.name || '', false)($scope);
27176   this.$$parentForm = nullFormCtrl;
27178   var parsedNgModel = $parse($attr.ngModel),
27179       parsedNgModelAssign = parsedNgModel.assign,
27180       ngModelGet = parsedNgModel,
27181       ngModelSet = parsedNgModelAssign,
27182       pendingDebounce = null,
27183       parserValid,
27184       ctrl = this;
27186   this.$$setOptions = function(options) {
27187     ctrl.$options = options;
27188     if (options && options.getterSetter) {
27189       var invokeModelGetter = $parse($attr.ngModel + '()'),
27190           invokeModelSetter = $parse($attr.ngModel + '($$$p)');
27192       ngModelGet = function($scope) {
27193         var modelValue = parsedNgModel($scope);
27194         if (isFunction(modelValue)) {
27195           modelValue = invokeModelGetter($scope);
27196         }
27197         return modelValue;
27198       };
27199       ngModelSet = function($scope, newValue) {
27200         if (isFunction(parsedNgModel($scope))) {
27201           invokeModelSetter($scope, {$$$p: newValue});
27202         } else {
27203           parsedNgModelAssign($scope, newValue);
27204         }
27205       };
27206     } else if (!parsedNgModel.assign) {
27207       throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
27208           $attr.ngModel, startingTag($element));
27209     }
27210   };
27212   /**
27213    * @ngdoc method
27214    * @name ngModel.NgModelController#$render
27215    *
27216    * @description
27217    * Called when the view needs to be updated. It is expected that the user of the ng-model
27218    * directive will implement this method.
27219    *
27220    * The `$render()` method is invoked in the following situations:
27221    *
27222    * * `$rollbackViewValue()` is called.  If we are rolling back the view value to the last
27223    *   committed value then `$render()` is called to update the input control.
27224    * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
27225    *   the `$viewValue` are different from last time.
27226    *
27227    * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
27228    * `$modelValue` and `$viewValue` are actually different from their previous values. If `$modelValue`
27229    * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
27230    * invoked if you only change a property on the objects.
27231    */
27232   this.$render = noop;
27234   /**
27235    * @ngdoc method
27236    * @name ngModel.NgModelController#$isEmpty
27237    *
27238    * @description
27239    * This is called when we need to determine if the value of an input is empty.
27240    *
27241    * For instance, the required directive does this to work out if the input has data or not.
27242    *
27243    * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
27244    *
27245    * You can override this for input directives whose concept of being empty is different from the
27246    * default. The `checkboxInputType` directive does this because in its case a value of `false`
27247    * implies empty.
27248    *
27249    * @param {*} value The value of the input to check for emptiness.
27250    * @returns {boolean} True if `value` is "empty".
27251    */
27252   this.$isEmpty = function(value) {
27253     return isUndefined(value) || value === '' || value === null || value !== value;
27254   };
27256   this.$$updateEmptyClasses = function(value) {
27257     if (ctrl.$isEmpty(value)) {
27258       $animate.removeClass($element, NOT_EMPTY_CLASS);
27259       $animate.addClass($element, EMPTY_CLASS);
27260     } else {
27261       $animate.removeClass($element, EMPTY_CLASS);
27262       $animate.addClass($element, NOT_EMPTY_CLASS);
27263     }
27264   };
27267   var currentValidationRunId = 0;
27269   /**
27270    * @ngdoc method
27271    * @name ngModel.NgModelController#$setValidity
27272    *
27273    * @description
27274    * Change the validity state, and notify the form.
27275    *
27276    * This method can be called within $parsers/$formatters or a custom validation implementation.
27277    * However, in most cases it should be sufficient to use the `ngModel.$validators` and
27278    * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
27279    *
27280    * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
27281    *        to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
27282    *        (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
27283    *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
27284    *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
27285    *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
27286    * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
27287    *                          or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
27288    *                          Skipped is used by Angular when validators do not run because of parse errors and
27289    *                          when `$asyncValidators` do not run because any of the `$validators` failed.
27290    */
27291   addSetValidityMethod({
27292     ctrl: this,
27293     $element: $element,
27294     set: function(object, property) {
27295       object[property] = true;
27296     },
27297     unset: function(object, property) {
27298       delete object[property];
27299     },
27300     $animate: $animate
27301   });
27303   /**
27304    * @ngdoc method
27305    * @name ngModel.NgModelController#$setPristine
27306    *
27307    * @description
27308    * Sets the control to its pristine state.
27309    *
27310    * This method can be called to remove the `ng-dirty` class and set the control to its pristine
27311    * state (`ng-pristine` class). A model is considered to be pristine when the control
27312    * has not been changed from when first compiled.
27313    */
27314   this.$setPristine = function() {
27315     ctrl.$dirty = false;
27316     ctrl.$pristine = true;
27317     $animate.removeClass($element, DIRTY_CLASS);
27318     $animate.addClass($element, PRISTINE_CLASS);
27319   };
27321   /**
27322    * @ngdoc method
27323    * @name ngModel.NgModelController#$setDirty
27324    *
27325    * @description
27326    * Sets the control to its dirty state.
27327    *
27328    * This method can be called to remove the `ng-pristine` class and set the control to its dirty
27329    * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
27330    * from when first compiled.
27331    */
27332   this.$setDirty = function() {
27333     ctrl.$dirty = true;
27334     ctrl.$pristine = false;
27335     $animate.removeClass($element, PRISTINE_CLASS);
27336     $animate.addClass($element, DIRTY_CLASS);
27337     ctrl.$$parentForm.$setDirty();
27338   };
27340   /**
27341    * @ngdoc method
27342    * @name ngModel.NgModelController#$setUntouched
27343    *
27344    * @description
27345    * Sets the control to its untouched state.
27346    *
27347    * This method can be called to remove the `ng-touched` class and set the control to its
27348    * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
27349    * by default, however this function can be used to restore that state if the model has
27350    * already been touched by the user.
27351    */
27352   this.$setUntouched = function() {
27353     ctrl.$touched = false;
27354     ctrl.$untouched = true;
27355     $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
27356   };
27358   /**
27359    * @ngdoc method
27360    * @name ngModel.NgModelController#$setTouched
27361    *
27362    * @description
27363    * Sets the control to its touched state.
27364    *
27365    * This method can be called to remove the `ng-untouched` class and set the control to its
27366    * touched state (`ng-touched` class). A model is considered to be touched when the user has
27367    * first focused the control element and then shifted focus away from the control (blur event).
27368    */
27369   this.$setTouched = function() {
27370     ctrl.$touched = true;
27371     ctrl.$untouched = false;
27372     $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
27373   };
27375   /**
27376    * @ngdoc method
27377    * @name ngModel.NgModelController#$rollbackViewValue
27378    *
27379    * @description
27380    * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
27381    * which may be caused by a pending debounced event or because the input is waiting for a some
27382    * future event.
27383    *
27384    * If you have an input that uses `ng-model-options` to set up debounced updates or updates that
27385    * depend on special events such as blur, you can have a situation where there is a period when
27386    * the `$viewValue` is out of sync with the ngModel's `$modelValue`.
27387    *
27388    * In this case, you can use `$rollbackViewValue()` to manually cancel the debounced / future update
27389    * and reset the input to the last committed view value.
27390    *
27391    * It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue`
27392    * programmatically before these debounced/future events have resolved/occurred, because Angular's
27393    * dirty checking mechanism is not able to tell whether the model has actually changed or not.
27394    *
27395    * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
27396    * input which may have such events pending. This is important in order to make sure that the
27397    * input field will be updated with the new model value and any pending operations are cancelled.
27398    *
27399    * <example name="ng-model-cancel-update" module="cancel-update-example">
27400    *   <file name="app.js">
27401    *     angular.module('cancel-update-example', [])
27402    *
27403    *     .controller('CancelUpdateController', ['$scope', function($scope) {
27404    *       $scope.model = {};
27405    *
27406    *       $scope.setEmpty = function(e, value, rollback) {
27407    *         if (e.keyCode == 27) {
27408    *           e.preventDefault();
27409    *           if (rollback) {
27410    *             $scope.myForm[value].$rollbackViewValue();
27411    *           }
27412    *           $scope.model[value] = '';
27413    *         }
27414    *       };
27415    *     }]);
27416    *   </file>
27417    *   <file name="index.html">
27418    *     <div ng-controller="CancelUpdateController">
27419    *        <p>Both of these inputs are only updated if they are blurred. Hitting escape should
27420    *        empty them. Follow these steps and observe the difference:</p>
27421    *       <ol>
27422    *         <li>Type something in the input. You will see that the model is not yet updated</li>
27423    *         <li>Press the Escape key.
27424    *           <ol>
27425    *             <li> In the first example, nothing happens, because the model is already '', and no
27426    *             update is detected. If you blur the input, the model will be set to the current view.
27427    *             </li>
27428    *             <li> In the second example, the pending update is cancelled, and the input is set back
27429    *             to the last committed view value (''). Blurring the input does nothing.
27430    *             </li>
27431    *           </ol>
27432    *         </li>
27433    *       </ol>
27434    *
27435    *       <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
27436    *         <div>
27437    *        <p id="inputDescription1">Without $rollbackViewValue():</p>
27438    *         <input name="value1" aria-describedby="inputDescription1" ng-model="model.value1"
27439    *                ng-keydown="setEmpty($event, 'value1')">
27440    *         value1: "{{ model.value1 }}"
27441    *         </div>
27442    *
27443    *         <div>
27444    *        <p id="inputDescription2">With $rollbackViewValue():</p>
27445    *         <input name="value2" aria-describedby="inputDescription2" ng-model="model.value2"
27446    *                ng-keydown="setEmpty($event, 'value2', true)">
27447    *         value2: "{{ model.value2 }}"
27448    *         </div>
27449    *       </form>
27450    *     </div>
27451    *   </file>
27452        <file name="style.css">
27453           div {
27454             display: table-cell;
27455           }
27456           div:nth-child(1) {
27457             padding-right: 30px;
27458           }
27460         </file>
27461    * </example>
27462    */
27463   this.$rollbackViewValue = function() {
27464     $timeout.cancel(pendingDebounce);
27465     ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
27466     ctrl.$render();
27467   };
27469   /**
27470    * @ngdoc method
27471    * @name ngModel.NgModelController#$validate
27472    *
27473    * @description
27474    * Runs each of the registered validators (first synchronous validators and then
27475    * asynchronous validators).
27476    * If the validity changes to invalid, the model will be set to `undefined`,
27477    * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
27478    * If the validity changes to valid, it will set the model to the last available valid
27479    * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
27480    */
27481   this.$validate = function() {
27482     // ignore $validate before model is initialized
27483     if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
27484       return;
27485     }
27487     var viewValue = ctrl.$$lastCommittedViewValue;
27488     // Note: we use the $$rawModelValue as $modelValue might have been
27489     // set to undefined during a view -> model update that found validation
27490     // errors. We can't parse the view here, since that could change
27491     // the model although neither viewValue nor the model on the scope changed
27492     var modelValue = ctrl.$$rawModelValue;
27494     var prevValid = ctrl.$valid;
27495     var prevModelValue = ctrl.$modelValue;
27497     var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
27499     ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
27500       // If there was no change in validity, don't update the model
27501       // This prevents changing an invalid modelValue to undefined
27502       if (!allowInvalid && prevValid !== allValid) {
27503         // Note: Don't check ctrl.$valid here, as we could have
27504         // external validators (e.g. calculated on the server),
27505         // that just call $setValidity and need the model value
27506         // to calculate their validity.
27507         ctrl.$modelValue = allValid ? modelValue : undefined;
27509         if (ctrl.$modelValue !== prevModelValue) {
27510           ctrl.$$writeModelToScope();
27511         }
27512       }
27513     });
27515   };
27517   this.$$runValidators = function(modelValue, viewValue, doneCallback) {
27518     currentValidationRunId++;
27519     var localValidationRunId = currentValidationRunId;
27521     // check parser error
27522     if (!processParseErrors()) {
27523       validationDone(false);
27524       return;
27525     }
27526     if (!processSyncValidators()) {
27527       validationDone(false);
27528       return;
27529     }
27530     processAsyncValidators();
27532     function processParseErrors() {
27533       var errorKey = ctrl.$$parserName || 'parse';
27534       if (isUndefined(parserValid)) {
27535         setValidity(errorKey, null);
27536       } else {
27537         if (!parserValid) {
27538           forEach(ctrl.$validators, function(v, name) {
27539             setValidity(name, null);
27540           });
27541           forEach(ctrl.$asyncValidators, function(v, name) {
27542             setValidity(name, null);
27543           });
27544         }
27545         // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
27546         setValidity(errorKey, parserValid);
27547         return parserValid;
27548       }
27549       return true;
27550     }
27552     function processSyncValidators() {
27553       var syncValidatorsValid = true;
27554       forEach(ctrl.$validators, function(validator, name) {
27555         var result = validator(modelValue, viewValue);
27556         syncValidatorsValid = syncValidatorsValid && result;
27557         setValidity(name, result);
27558       });
27559       if (!syncValidatorsValid) {
27560         forEach(ctrl.$asyncValidators, function(v, name) {
27561           setValidity(name, null);
27562         });
27563         return false;
27564       }
27565       return true;
27566     }
27568     function processAsyncValidators() {
27569       var validatorPromises = [];
27570       var allValid = true;
27571       forEach(ctrl.$asyncValidators, function(validator, name) {
27572         var promise = validator(modelValue, viewValue);
27573         if (!isPromiseLike(promise)) {
27574           throw ngModelMinErr('nopromise',
27575             "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
27576         }
27577         setValidity(name, undefined);
27578         validatorPromises.push(promise.then(function() {
27579           setValidity(name, true);
27580         }, function() {
27581           allValid = false;
27582           setValidity(name, false);
27583         }));
27584       });
27585       if (!validatorPromises.length) {
27586         validationDone(true);
27587       } else {
27588         $q.all(validatorPromises).then(function() {
27589           validationDone(allValid);
27590         }, noop);
27591       }
27592     }
27594     function setValidity(name, isValid) {
27595       if (localValidationRunId === currentValidationRunId) {
27596         ctrl.$setValidity(name, isValid);
27597       }
27598     }
27600     function validationDone(allValid) {
27601       if (localValidationRunId === currentValidationRunId) {
27603         doneCallback(allValid);
27604       }
27605     }
27606   };
27608   /**
27609    * @ngdoc method
27610    * @name ngModel.NgModelController#$commitViewValue
27611    *
27612    * @description
27613    * Commit a pending update to the `$modelValue`.
27614    *
27615    * Updates may be pending by a debounced event or because the input is waiting for a some future
27616    * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
27617    * usually handles calling this in response to input events.
27618    */
27619   this.$commitViewValue = function() {
27620     var viewValue = ctrl.$viewValue;
27622     $timeout.cancel(pendingDebounce);
27624     // If the view value has not changed then we should just exit, except in the case where there is
27625     // a native validator on the element. In this case the validation state may have changed even though
27626     // the viewValue has stayed empty.
27627     if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
27628       return;
27629     }
27630     ctrl.$$updateEmptyClasses(viewValue);
27631     ctrl.$$lastCommittedViewValue = viewValue;
27633     // change to dirty
27634     if (ctrl.$pristine) {
27635       this.$setDirty();
27636     }
27637     this.$$parseAndValidate();
27638   };
27640   this.$$parseAndValidate = function() {
27641     var viewValue = ctrl.$$lastCommittedViewValue;
27642     var modelValue = viewValue;
27643     parserValid = isUndefined(modelValue) ? undefined : true;
27645     if (parserValid) {
27646       for (var i = 0; i < ctrl.$parsers.length; i++) {
27647         modelValue = ctrl.$parsers[i](modelValue);
27648         if (isUndefined(modelValue)) {
27649           parserValid = false;
27650           break;
27651         }
27652       }
27653     }
27654     if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
27655       // ctrl.$modelValue has not been touched yet...
27656       ctrl.$modelValue = ngModelGet($scope);
27657     }
27658     var prevModelValue = ctrl.$modelValue;
27659     var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
27660     ctrl.$$rawModelValue = modelValue;
27662     if (allowInvalid) {
27663       ctrl.$modelValue = modelValue;
27664       writeToModelIfNeeded();
27665     }
27667     // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
27668     // This can happen if e.g. $setViewValue is called from inside a parser
27669     ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
27670       if (!allowInvalid) {
27671         // Note: Don't check ctrl.$valid here, as we could have
27672         // external validators (e.g. calculated on the server),
27673         // that just call $setValidity and need the model value
27674         // to calculate their validity.
27675         ctrl.$modelValue = allValid ? modelValue : undefined;
27676         writeToModelIfNeeded();
27677       }
27678     });
27680     function writeToModelIfNeeded() {
27681       if (ctrl.$modelValue !== prevModelValue) {
27682         ctrl.$$writeModelToScope();
27683       }
27684     }
27685   };
27687   this.$$writeModelToScope = function() {
27688     ngModelSet($scope, ctrl.$modelValue);
27689     forEach(ctrl.$viewChangeListeners, function(listener) {
27690       try {
27691         listener();
27692       } catch (e) {
27693         $exceptionHandler(e);
27694       }
27695     });
27696   };
27698   /**
27699    * @ngdoc method
27700    * @name ngModel.NgModelController#$setViewValue
27701    *
27702    * @description
27703    * Update the view value.
27704    *
27705    * This method should be called when a control wants to change the view value; typically,
27706    * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
27707    * directive calls it when the value of the input changes and {@link ng.directive:select select}
27708    * calls it when an option is selected.
27709    *
27710    * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
27711    * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
27712    * value sent directly for processing, finally to be applied to `$modelValue` and then the
27713    * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
27714    * in the `$viewChangeListeners` list, are called.
27715    *
27716    * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
27717    * and the `default` trigger is not listed, all those actions will remain pending until one of the
27718    * `updateOn` events is triggered on the DOM element.
27719    * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
27720    * directive is used with a custom debounce for this particular event.
27721    * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
27722    * is specified, once the timer runs out.
27723    *
27724    * When used with standard inputs, the view value will always be a string (which is in some cases
27725    * parsed into another type, such as a `Date` object for `input[date]`.)
27726    * However, custom controls might also pass objects to this method. In this case, we should make
27727    * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
27728    * perform a deep watch of objects, it only looks for a change of identity. If you only change
27729    * the property of the object then ngModel will not realize that the object has changed and
27730    * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
27731    * not change properties of the copy once it has been passed to `$setViewValue`.
27732    * Otherwise you may cause the model value on the scope to change incorrectly.
27733    *
27734    * <div class="alert alert-info">
27735    * In any case, the value passed to the method should always reflect the current value
27736    * of the control. For example, if you are calling `$setViewValue` for an input element,
27737    * you should pass the input DOM value. Otherwise, the control and the scope model become
27738    * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
27739    * the control's DOM value in any way. If we want to change the control's DOM value
27740    * programmatically, we should update the `ngModel` scope expression. Its new value will be
27741    * picked up by the model controller, which will run it through the `$formatters`, `$render` it
27742    * to update the DOM, and finally call `$validate` on it.
27743    * </div>
27744    *
27745    * @param {*} value value from the view.
27746    * @param {string} trigger Event that triggered the update.
27747    */
27748   this.$setViewValue = function(value, trigger) {
27749     ctrl.$viewValue = value;
27750     if (!ctrl.$options || ctrl.$options.updateOnDefault) {
27751       ctrl.$$debounceViewValueCommit(trigger);
27752     }
27753   };
27755   this.$$debounceViewValueCommit = function(trigger) {
27756     var debounceDelay = 0,
27757         options = ctrl.$options,
27758         debounce;
27760     if (options && isDefined(options.debounce)) {
27761       debounce = options.debounce;
27762       if (isNumber(debounce)) {
27763         debounceDelay = debounce;
27764       } else if (isNumber(debounce[trigger])) {
27765         debounceDelay = debounce[trigger];
27766       } else if (isNumber(debounce['default'])) {
27767         debounceDelay = debounce['default'];
27768       }
27769     }
27771     $timeout.cancel(pendingDebounce);
27772     if (debounceDelay) {
27773       pendingDebounce = $timeout(function() {
27774         ctrl.$commitViewValue();
27775       }, debounceDelay);
27776     } else if ($rootScope.$$phase) {
27777       ctrl.$commitViewValue();
27778     } else {
27779       $scope.$apply(function() {
27780         ctrl.$commitViewValue();
27781       });
27782     }
27783   };
27785   // model -> value
27786   // Note: we cannot use a normal scope.$watch as we want to detect the following:
27787   // 1. scope value is 'a'
27788   // 2. user enters 'b'
27789   // 3. ng-change kicks in and reverts scope value to 'a'
27790   //    -> scope value did not change since the last digest as
27791   //       ng-change executes in apply phase
27792   // 4. view should be changed back to 'a'
27793   $scope.$watch(function ngModelWatch() {
27794     var modelValue = ngModelGet($scope);
27796     // if scope model value and ngModel value are out of sync
27797     // TODO(perf): why not move this to the action fn?
27798     if (modelValue !== ctrl.$modelValue &&
27799        // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
27800        (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
27801     ) {
27802       ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
27803       parserValid = undefined;
27805       var formatters = ctrl.$formatters,
27806           idx = formatters.length;
27808       var viewValue = modelValue;
27809       while (idx--) {
27810         viewValue = formatters[idx](viewValue);
27811       }
27812       if (ctrl.$viewValue !== viewValue) {
27813         ctrl.$$updateEmptyClasses(viewValue);
27814         ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
27815         ctrl.$render();
27817         ctrl.$$runValidators(modelValue, viewValue, noop);
27818       }
27819     }
27821     return modelValue;
27822   });
27827  * @ngdoc directive
27828  * @name ngModel
27830  * @element input
27831  * @priority 1
27833  * @description
27834  * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
27835  * property on the scope using {@link ngModel.NgModelController NgModelController},
27836  * which is created and exposed by this directive.
27838  * `ngModel` is responsible for:
27840  * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
27841  *   require.
27842  * - Providing validation behavior (i.e. required, number, email, url).
27843  * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
27844  * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`,
27845  *   `ng-untouched`, `ng-empty`, `ng-not-empty`) including animations.
27846  * - Registering the control with its parent {@link ng.directive:form form}.
27848  * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
27849  * current scope. If the property doesn't already exist on this scope, it will be created
27850  * implicitly and added to the scope.
27852  * For best practices on using `ngModel`, see:
27854  *  - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
27856  * For basic examples, how to use `ngModel`, see:
27858  *  - {@link ng.directive:input input}
27859  *    - {@link input[text] text}
27860  *    - {@link input[checkbox] checkbox}
27861  *    - {@link input[radio] radio}
27862  *    - {@link input[number] number}
27863  *    - {@link input[email] email}
27864  *    - {@link input[url] url}
27865  *    - {@link input[date] date}
27866  *    - {@link input[datetime-local] datetime-local}
27867  *    - {@link input[time] time}
27868  *    - {@link input[month] month}
27869  *    - {@link input[week] week}
27870  *  - {@link ng.directive:select select}
27871  *  - {@link ng.directive:textarea textarea}
27873  * # Complex Models (objects or collections)
27875  * By default, `ngModel` watches the model by reference, not value. This is important to know when
27876  * binding inputs to models that are objects (e.g. `Date`) or collections (e.g. arrays). If only properties of the
27877  * object or collection change, `ngModel` will not be notified and so the input will not be  re-rendered.
27879  * The model must be assigned an entirely new object or collection before a re-rendering will occur.
27881  * Some directives have options that will cause them to use a custom `$watchCollection` on the model expression
27882  * - for example, `ngOptions` will do so when a `track by` clause is included in the comprehension expression or
27883  * if the select is given the `multiple` attribute.
27885  * The `$watchCollection()` method only does a shallow comparison, meaning that changing properties deeper than the
27886  * first level of the object (or only changing the properties of an item in the collection if it's an array) will still
27887  * not trigger a re-rendering of the model.
27889  * # CSS classes
27890  * The following CSS classes are added and removed on the associated input/select/textarea element
27891  * depending on the validity of the model.
27893  *  - `ng-valid`: the model is valid
27894  *  - `ng-invalid`: the model is invalid
27895  *  - `ng-valid-[key]`: for each valid key added by `$setValidity`
27896  *  - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
27897  *  - `ng-pristine`: the control hasn't been interacted with yet
27898  *  - `ng-dirty`: the control has been interacted with
27899  *  - `ng-touched`: the control has been blurred
27900  *  - `ng-untouched`: the control hasn't been blurred
27901  *  - `ng-pending`: any `$asyncValidators` are unfulfilled
27902  *  - `ng-empty`: the view does not contain a value or the value is deemed "empty", as defined
27903  *     by the {@link ngModel.NgModelController#$isEmpty} method
27904  *  - `ng-not-empty`: the view contains a non-empty value
27906  * Keep in mind that ngAnimate can detect each of these classes when added and removed.
27908  * ## Animation Hooks
27910  * Animations within models are triggered when any of the associated CSS classes are added and removed
27911  * on the input element which is attached to the model. These classes include: `.ng-pristine`, `.ng-dirty`,
27912  * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
27913  * The animations that are triggered within ngModel are similar to how they work in ngClass and
27914  * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
27916  * The following example shows a simple way to utilize CSS transitions to style an input element
27917  * that has been rendered as invalid after it has been validated:
27919  * <pre>
27920  * //be sure to include ngAnimate as a module to hook into more
27921  * //advanced animations
27922  * .my-input {
27923  *   transition:0.5s linear all;
27924  *   background: white;
27925  * }
27926  * .my-input.ng-invalid {
27927  *   background: red;
27928  *   color:white;
27929  * }
27930  * </pre>
27932  * @example
27933  * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
27934      <file name="index.html">
27935        <script>
27936         angular.module('inputExample', [])
27937           .controller('ExampleController', ['$scope', function($scope) {
27938             $scope.val = '1';
27939           }]);
27940        </script>
27941        <style>
27942          .my-input {
27943            transition:all linear 0.5s;
27944            background: transparent;
27945          }
27946          .my-input.ng-invalid {
27947            color:white;
27948            background: red;
27949          }
27950        </style>
27951        <p id="inputDescription">
27952         Update input to see transitions when valid/invalid.
27953         Integer is a valid value.
27954        </p>
27955        <form name="testForm" ng-controller="ExampleController">
27956          <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
27957                 aria-describedby="inputDescription" />
27958        </form>
27959      </file>
27960  * </example>
27962  * ## Binding to a getter/setter
27964  * Sometimes it's helpful to bind `ngModel` to a getter/setter function.  A getter/setter is a
27965  * function that returns a representation of the model when called with zero arguments, and sets
27966  * the internal state of a model when called with an argument. It's sometimes useful to use this
27967  * for models that have an internal representation that's different from what the model exposes
27968  * to the view.
27970  * <div class="alert alert-success">
27971  * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
27972  * frequently than other parts of your code.
27973  * </div>
27975  * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
27976  * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
27977  * a `<form>`, which will enable this behavior for all `<input>`s within it. See
27978  * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
27980  * The following example shows how to use `ngModel` with a getter/setter:
27982  * @example
27983  * <example name="ngModel-getter-setter" module="getterSetterExample">
27984      <file name="index.html">
27985        <div ng-controller="ExampleController">
27986          <form name="userForm">
27987            <label>Name:
27988              <input type="text" name="userName"
27989                     ng-model="user.name"
27990                     ng-model-options="{ getterSetter: true }" />
27991            </label>
27992          </form>
27993          <pre>user.name = <span ng-bind="user.name()"></span></pre>
27994        </div>
27995      </file>
27996      <file name="app.js">
27997        angular.module('getterSetterExample', [])
27998          .controller('ExampleController', ['$scope', function($scope) {
27999            var _name = 'Brian';
28000            $scope.user = {
28001              name: function(newName) {
28002               // Note that newName can be undefined for two reasons:
28003               // 1. Because it is called as a getter and thus called with no arguments
28004               // 2. Because the property should actually be set to undefined. This happens e.g. if the
28005               //    input is invalid
28006               return arguments.length ? (_name = newName) : _name;
28007              }
28008            };
28009          }]);
28010      </file>
28011  * </example>
28012  */
28013 var ngModelDirective = ['$rootScope', function($rootScope) {
28014   return {
28015     restrict: 'A',
28016     require: ['ngModel', '^?form', '^?ngModelOptions'],
28017     controller: NgModelController,
28018     // Prelink needs to run before any input directive
28019     // so that we can set the NgModelOptions in NgModelController
28020     // before anyone else uses it.
28021     priority: 1,
28022     compile: function ngModelCompile(element) {
28023       // Setup initial state of the control
28024       element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
28026       return {
28027         pre: function ngModelPreLink(scope, element, attr, ctrls) {
28028           var modelCtrl = ctrls[0],
28029               formCtrl = ctrls[1] || modelCtrl.$$parentForm;
28031           modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
28033           // notify others, especially parent forms
28034           formCtrl.$addControl(modelCtrl);
28036           attr.$observe('name', function(newValue) {
28037             if (modelCtrl.$name !== newValue) {
28038               modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
28039             }
28040           });
28042           scope.$on('$destroy', function() {
28043             modelCtrl.$$parentForm.$removeControl(modelCtrl);
28044           });
28045         },
28046         post: function ngModelPostLink(scope, element, attr, ctrls) {
28047           var modelCtrl = ctrls[0];
28048           if (modelCtrl.$options && modelCtrl.$options.updateOn) {
28049             element.on(modelCtrl.$options.updateOn, function(ev) {
28050               modelCtrl.$$debounceViewValueCommit(ev && ev.type);
28051             });
28052           }
28054           element.on('blur', function() {
28055             if (modelCtrl.$touched) return;
28057             if ($rootScope.$$phase) {
28058               scope.$evalAsync(modelCtrl.$setTouched);
28059             } else {
28060               scope.$apply(modelCtrl.$setTouched);
28061             }
28062           });
28063         }
28064       };
28065     }
28066   };
28069 var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
28072  * @ngdoc directive
28073  * @name ngModelOptions
28075  * @description
28076  * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
28077  * events that will trigger a model update and/or a debouncing delay so that the actual update only
28078  * takes place when a timer expires; this timer will be reset after another change takes place.
28080  * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
28081  * be different from the value in the actual model. This means that if you update the model you
28082  * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
28083  * order to make sure it is synchronized with the model and that any debounced action is canceled.
28085  * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
28086  * method is by making sure the input is placed inside a form that has a `name` attribute. This is
28087  * important because `form` controllers are published to the related scope under the name in their
28088  * `name` attribute.
28090  * Any pending changes will take place immediately when an enclosing form is submitted via the
28091  * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
28092  * to have access to the updated model.
28094  * `ngModelOptions` has an effect on the element it's declared on and its descendants.
28096  * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
28097  *   - `updateOn`: string specifying which event should the input be bound to. You can set several
28098  *     events using an space delimited list. There is a special event called `default` that
28099  *     matches the default events belonging of the control.
28100  *   - `debounce`: integer value which contains the debounce model update value in milliseconds. A
28101  *     value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
28102  *     custom value for each event. For example:
28103  *     `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
28104  *   - `allowInvalid`: boolean value which indicates that the model can be set with values that did
28105  *     not validate correctly instead of the default behavior of setting the model to undefined.
28106  *   - `getterSetter`: boolean value which determines whether or not to treat functions bound to
28107        `ngModel` as getters/setters.
28108  *   - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
28109  *     `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
28110  *     continental US time zone abbreviations, but for general use, use a time zone offset, for
28111  *     example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
28112  *     If not specified, the timezone of the browser will be used.
28114  * @example
28116   The following example shows how to override immediate updates. Changes on the inputs within the
28117   form will update the model only when the control loses focus (blur event). If `escape` key is
28118   pressed while the input field is focused, the value is reset to the value in the current model.
28120   <example name="ngModelOptions-directive-blur" module="optionsExample">
28121     <file name="index.html">
28122       <div ng-controller="ExampleController">
28123         <form name="userForm">
28124           <label>Name:
28125             <input type="text" name="userName"
28126                    ng-model="user.name"
28127                    ng-model-options="{ updateOn: 'blur' }"
28128                    ng-keyup="cancel($event)" />
28129           </label><br />
28130           <label>Other data:
28131             <input type="text" ng-model="user.data" />
28132           </label><br />
28133         </form>
28134         <pre>user.name = <span ng-bind="user.name"></span></pre>
28135         <pre>user.data = <span ng-bind="user.data"></span></pre>
28136       </div>
28137     </file>
28138     <file name="app.js">
28139       angular.module('optionsExample', [])
28140         .controller('ExampleController', ['$scope', function($scope) {
28141           $scope.user = { name: 'John', data: '' };
28143           $scope.cancel = function(e) {
28144             if (e.keyCode == 27) {
28145               $scope.userForm.userName.$rollbackViewValue();
28146             }
28147           };
28148         }]);
28149     </file>
28150     <file name="protractor.js" type="protractor">
28151       var model = element(by.binding('user.name'));
28152       var input = element(by.model('user.name'));
28153       var other = element(by.model('user.data'));
28155       it('should allow custom events', function() {
28156         input.sendKeys(' Doe');
28157         input.click();
28158         expect(model.getText()).toEqual('John');
28159         other.click();
28160         expect(model.getText()).toEqual('John Doe');
28161       });
28163       it('should $rollbackViewValue when model changes', function() {
28164         input.sendKeys(' Doe');
28165         expect(input.getAttribute('value')).toEqual('John Doe');
28166         input.sendKeys(protractor.Key.ESCAPE);
28167         expect(input.getAttribute('value')).toEqual('John');
28168         other.click();
28169         expect(model.getText()).toEqual('John');
28170       });
28171     </file>
28172   </example>
28174   This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
28175   If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
28177   <example name="ngModelOptions-directive-debounce" module="optionsExample">
28178     <file name="index.html">
28179       <div ng-controller="ExampleController">
28180         <form name="userForm">
28181           <label>Name:
28182             <input type="text" name="userName"
28183                    ng-model="user.name"
28184                    ng-model-options="{ debounce: 1000 }" />
28185           </label>
28186           <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
28187           <br />
28188         </form>
28189         <pre>user.name = <span ng-bind="user.name"></span></pre>
28190       </div>
28191     </file>
28192     <file name="app.js">
28193       angular.module('optionsExample', [])
28194         .controller('ExampleController', ['$scope', function($scope) {
28195           $scope.user = { name: 'Igor' };
28196         }]);
28197     </file>
28198   </example>
28200   This one shows how to bind to getter/setters:
28202   <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
28203     <file name="index.html">
28204       <div ng-controller="ExampleController">
28205         <form name="userForm">
28206           <label>Name:
28207             <input type="text" name="userName"
28208                    ng-model="user.name"
28209                    ng-model-options="{ getterSetter: true }" />
28210           </label>
28211         </form>
28212         <pre>user.name = <span ng-bind="user.name()"></span></pre>
28213       </div>
28214     </file>
28215     <file name="app.js">
28216       angular.module('getterSetterExample', [])
28217         .controller('ExampleController', ['$scope', function($scope) {
28218           var _name = 'Brian';
28219           $scope.user = {
28220             name: function(newName) {
28221               // Note that newName can be undefined for two reasons:
28222               // 1. Because it is called as a getter and thus called with no arguments
28223               // 2. Because the property should actually be set to undefined. This happens e.g. if the
28224               //    input is invalid
28225               return arguments.length ? (_name = newName) : _name;
28226             }
28227           };
28228         }]);
28229     </file>
28230   </example>
28231  */
28232 var ngModelOptionsDirective = function() {
28233   return {
28234     restrict: 'A',
28235     controller: ['$scope', '$attrs', function($scope, $attrs) {
28236       var that = this;
28237       this.$options = copy($scope.$eval($attrs.ngModelOptions));
28238       // Allow adding/overriding bound events
28239       if (isDefined(this.$options.updateOn)) {
28240         this.$options.updateOnDefault = false;
28241         // extract "default" pseudo-event from list of events that can trigger a model update
28242         this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
28243           that.$options.updateOnDefault = true;
28244           return ' ';
28245         }));
28246       } else {
28247         this.$options.updateOnDefault = true;
28248       }
28249     }]
28250   };
28255 // helper methods
28256 function addSetValidityMethod(context) {
28257   var ctrl = context.ctrl,
28258       $element = context.$element,
28259       classCache = {},
28260       set = context.set,
28261       unset = context.unset,
28262       $animate = context.$animate;
28264   classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
28266   ctrl.$setValidity = setValidity;
28268   function setValidity(validationErrorKey, state, controller) {
28269     if (isUndefined(state)) {
28270       createAndSet('$pending', validationErrorKey, controller);
28271     } else {
28272       unsetAndCleanup('$pending', validationErrorKey, controller);
28273     }
28274     if (!isBoolean(state)) {
28275       unset(ctrl.$error, validationErrorKey, controller);
28276       unset(ctrl.$$success, validationErrorKey, controller);
28277     } else {
28278       if (state) {
28279         unset(ctrl.$error, validationErrorKey, controller);
28280         set(ctrl.$$success, validationErrorKey, controller);
28281       } else {
28282         set(ctrl.$error, validationErrorKey, controller);
28283         unset(ctrl.$$success, validationErrorKey, controller);
28284       }
28285     }
28286     if (ctrl.$pending) {
28287       cachedToggleClass(PENDING_CLASS, true);
28288       ctrl.$valid = ctrl.$invalid = undefined;
28289       toggleValidationCss('', null);
28290     } else {
28291       cachedToggleClass(PENDING_CLASS, false);
28292       ctrl.$valid = isObjectEmpty(ctrl.$error);
28293       ctrl.$invalid = !ctrl.$valid;
28294       toggleValidationCss('', ctrl.$valid);
28295     }
28297     // re-read the state as the set/unset methods could have
28298     // combined state in ctrl.$error[validationError] (used for forms),
28299     // where setting/unsetting only increments/decrements the value,
28300     // and does not replace it.
28301     var combinedState;
28302     if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
28303       combinedState = undefined;
28304     } else if (ctrl.$error[validationErrorKey]) {
28305       combinedState = false;
28306     } else if (ctrl.$$success[validationErrorKey]) {
28307       combinedState = true;
28308     } else {
28309       combinedState = null;
28310     }
28312     toggleValidationCss(validationErrorKey, combinedState);
28313     ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
28314   }
28316   function createAndSet(name, value, controller) {
28317     if (!ctrl[name]) {
28318       ctrl[name] = {};
28319     }
28320     set(ctrl[name], value, controller);
28321   }
28323   function unsetAndCleanup(name, value, controller) {
28324     if (ctrl[name]) {
28325       unset(ctrl[name], value, controller);
28326     }
28327     if (isObjectEmpty(ctrl[name])) {
28328       ctrl[name] = undefined;
28329     }
28330   }
28332   function cachedToggleClass(className, switchValue) {
28333     if (switchValue && !classCache[className]) {
28334       $animate.addClass($element, className);
28335       classCache[className] = true;
28336     } else if (!switchValue && classCache[className]) {
28337       $animate.removeClass($element, className);
28338       classCache[className] = false;
28339     }
28340   }
28342   function toggleValidationCss(validationErrorKey, isValid) {
28343     validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
28345     cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
28346     cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
28347   }
28350 function isObjectEmpty(obj) {
28351   if (obj) {
28352     for (var prop in obj) {
28353       if (obj.hasOwnProperty(prop)) {
28354         return false;
28355       }
28356     }
28357   }
28358   return true;
28362  * @ngdoc directive
28363  * @name ngNonBindable
28364  * @restrict AC
28365  * @priority 1000
28367  * @description
28368  * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
28369  * DOM element. This is useful if the element contains what appears to be Angular directives and
28370  * bindings but which should be ignored by Angular. This could be the case if you have a site that
28371  * displays snippets of code, for instance.
28373  * @element ANY
28375  * @example
28376  * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
28377  * but the one wrapped in `ngNonBindable` is left alone.
28379  * @example
28380     <example>
28381       <file name="index.html">
28382         <div>Normal: {{1 + 2}}</div>
28383         <div ng-non-bindable>Ignored: {{1 + 2}}</div>
28384       </file>
28385       <file name="protractor.js" type="protractor">
28386        it('should check ng-non-bindable', function() {
28387          expect(element(by.binding('1 + 2')).getText()).toContain('3');
28388          expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
28389        });
28390       </file>
28391     </example>
28392  */
28393 var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
28395 /* global jqLiteRemove */
28397 var ngOptionsMinErr = minErr('ngOptions');
28400  * @ngdoc directive
28401  * @name ngOptions
28402  * @restrict A
28404  * @description
28406  * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
28407  * elements for the `<select>` element using the array or object obtained by evaluating the
28408  * `ngOptions` comprehension expression.
28410  * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
28411  * similar result. However, `ngOptions` provides some benefits such as reducing memory and
28412  * increasing speed by not creating a new scope for each repeated instance, as well as providing
28413  * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
28414  * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
28415  *  to a non-string value. This is because an option element can only be bound to string values at
28416  * present.
28418  * When an item in the `<select>` menu is selected, the array element or object property
28419  * represented by the selected option will be bound to the model identified by the `ngModel`
28420  * directive.
28422  * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
28423  * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
28424  * option. See example below for demonstration.
28426  * ## Complex Models (objects or collections)
28428  * By default, `ngModel` watches the model by reference, not value. This is important to know when
28429  * binding the select to a model that is an object or a collection.
28431  * One issue occurs if you want to preselect an option. For example, if you set
28432  * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
28433  * because the objects are not identical. So by default, you should always reference the item in your collection
28434  * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
28436  * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
28437  * of the item not by reference, but by the result of the `track by` expression. For example, if your
28438  * collection items have an id property, you would `track by item.id`.
28440  * A different issue with objects or collections is that ngModel won't detect if an object property or
28441  * a collection item changes. For that reason, `ngOptions` additionally watches the model using
28442  * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
28443  * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
28444  * has not changed identity, but only a property on the object or an item in the collection changes.
28446  * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
28447  * if the model is an array). This means that changing a property deeper than the first level inside the
28448  * object/collection will not trigger a re-rendering.
28450  * ## `select` **`as`**
28452  * Using `select` **`as`** will bind the result of the `select` expression to the model, but
28453  * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
28454  * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
28455  * is used, the result of that expression will be set as the value of the `option` and `select` elements.
28458  * ### `select` **`as`** and **`track by`**
28460  * <div class="alert alert-warning">
28461  * Be careful when using `select` **`as`** and **`track by`** in the same expression.
28462  * </div>
28464  * Given this array of items on the $scope:
28466  * ```js
28467  * $scope.items = [{
28468  *   id: 1,
28469  *   label: 'aLabel',
28470  *   subItem: { name: 'aSubItem' }
28471  * }, {
28472  *   id: 2,
28473  *   label: 'bLabel',
28474  *   subItem: { name: 'bSubItem' }
28475  * }];
28476  * ```
28478  * This will work:
28480  * ```html
28481  * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
28482  * ```
28483  * ```js
28484  * $scope.selected = $scope.items[0];
28485  * ```
28487  * but this will not work:
28489  * ```html
28490  * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
28491  * ```
28492  * ```js
28493  * $scope.selected = $scope.items[0].subItem;
28494  * ```
28496  * In both examples, the **`track by`** expression is applied successfully to each `item` in the
28497  * `items` array. Because the selected option has been set programmatically in the controller, the
28498  * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
28499  * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
28500  * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
28501  * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
28502  * is not matched against any `<option>` and the `<select>` appears as having no selected value.
28505  * @param {string} ngModel Assignable angular expression to data-bind to.
28506  * @param {string=} name Property name of the form under which the control is published.
28507  * @param {string=} required The control is considered valid only if value is entered.
28508  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
28509  *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
28510  *    `required` when you want to data-bind to the `required` attribute.
28511  * @param {comprehension_expression=} ngOptions in one of the following forms:
28513  *   * for array data sources:
28514  *     * `label` **`for`** `value` **`in`** `array`
28515  *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
28516  *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
28517  *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
28518  *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
28519  *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
28520  *     * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
28521  *        (for including a filter with `track by`)
28522  *   * for object data sources:
28523  *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
28524  *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
28525  *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
28526  *     * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
28527  *     * `select` **`as`** `label` **`group by`** `group`
28528  *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
28529  *     * `select` **`as`** `label` **`disable when`** `disable`
28530  *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
28532  * Where:
28534  *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
28535  *   * `value`: local variable which will refer to each item in the `array` or each property value
28536  *      of `object` during iteration.
28537  *   * `key`: local variable which will refer to a property name in `object` during iteration.
28538  *   * `label`: The result of this expression will be the label for `<option>` element. The
28539  *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
28540  *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
28541  *      element. If not specified, `select` expression will default to `value`.
28542  *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
28543  *      DOM element.
28544  *   * `disable`: The result of this expression will be used to disable the rendered `<option>`
28545  *      element. Return `true` to disable.
28546  *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
28547  *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
28548  *     `value` variable (e.g. `value.propertyName`). With this the selection is preserved
28549  *      even when the options are recreated (e.g. reloaded from the server).
28551  * @example
28552     <example module="selectExample">
28553       <file name="index.html">
28554         <script>
28555         angular.module('selectExample', [])
28556           .controller('ExampleController', ['$scope', function($scope) {
28557             $scope.colors = [
28558               {name:'black', shade:'dark'},
28559               {name:'white', shade:'light', notAnOption: true},
28560               {name:'red', shade:'dark'},
28561               {name:'blue', shade:'dark', notAnOption: true},
28562               {name:'yellow', shade:'light', notAnOption: false}
28563             ];
28564             $scope.myColor = $scope.colors[2]; // red
28565           }]);
28566         </script>
28567         <div ng-controller="ExampleController">
28568           <ul>
28569             <li ng-repeat="color in colors">
28570               <label>Name: <input ng-model="color.name"></label>
28571               <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
28572               <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
28573             </li>
28574             <li>
28575               <button ng-click="colors.push({})">add</button>
28576             </li>
28577           </ul>
28578           <hr/>
28579           <label>Color (null not allowed):
28580             <select ng-model="myColor" ng-options="color.name for color in colors"></select>
28581           </label><br/>
28582           <label>Color (null allowed):
28583           <span  class="nullable">
28584             <select ng-model="myColor" ng-options="color.name for color in colors">
28585               <option value="">-- choose color --</option>
28586             </select>
28587           </span></label><br/>
28589           <label>Color grouped by shade:
28590             <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
28591             </select>
28592           </label><br/>
28594           <label>Color grouped by shade, with some disabled:
28595             <select ng-model="myColor"
28596                   ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
28597             </select>
28598           </label><br/>
28602           Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
28603           <br/>
28604           <hr/>
28605           Currently selected: {{ {selected_color:myColor} }}
28606           <div style="border:solid 1px black; height:20px"
28607                ng-style="{'background-color':myColor.name}">
28608           </div>
28609         </div>
28610       </file>
28611       <file name="protractor.js" type="protractor">
28612          it('should check ng-options', function() {
28613            expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
28614            element.all(by.model('myColor')).first().click();
28615            element.all(by.css('select[ng-model="myColor"] option')).first().click();
28616            expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
28617            element(by.css('.nullable select[ng-model="myColor"]')).click();
28618            element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
28619            expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
28620          });
28621       </file>
28622     </example>
28623  */
28625 // jshint maxlen: false
28626 //                     //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
28627 var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\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]+?))?$/;
28628                         // 1: value expression (valueFn)
28629                         // 2: label expression (displayFn)
28630                         // 3: group by expression (groupByFn)
28631                         // 4: disable when expression (disableWhenFn)
28632                         // 5: array item variable name
28633                         // 6: object item key variable name
28634                         // 7: object item value variable name
28635                         // 8: collection expression
28636                         // 9: track by expression
28637 // jshint maxlen: 100
28640 var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile, $document, $parse) {
28642   function parseOptionsExpression(optionsExp, selectElement, scope) {
28644     var match = optionsExp.match(NG_OPTIONS_REGEXP);
28645     if (!(match)) {
28646       throw ngOptionsMinErr('iexp',
28647         "Expected expression in form of " +
28648         "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
28649         " but got '{0}'. Element: {1}",
28650         optionsExp, startingTag(selectElement));
28651     }
28653     // Extract the parts from the ngOptions expression
28655     // The variable name for the value of the item in the collection
28656     var valueName = match[5] || match[7];
28657     // The variable name for the key of the item in the collection
28658     var keyName = match[6];
28660     // An expression that generates the viewValue for an option if there is a label expression
28661     var selectAs = / as /.test(match[0]) && match[1];
28662     // An expression that is used to track the id of each object in the options collection
28663     var trackBy = match[9];
28664     // An expression that generates the viewValue for an option if there is no label expression
28665     var valueFn = $parse(match[2] ? match[1] : valueName);
28666     var selectAsFn = selectAs && $parse(selectAs);
28667     var viewValueFn = selectAsFn || valueFn;
28668     var trackByFn = trackBy && $parse(trackBy);
28670     // Get the value by which we are going to track the option
28671     // if we have a trackFn then use that (passing scope and locals)
28672     // otherwise just hash the given viewValue
28673     var getTrackByValueFn = trackBy ?
28674                               function(value, locals) { return trackByFn(scope, locals); } :
28675                               function getHashOfValue(value) { return hashKey(value); };
28676     var getTrackByValue = function(value, key) {
28677       return getTrackByValueFn(value, getLocals(value, key));
28678     };
28680     var displayFn = $parse(match[2] || match[1]);
28681     var groupByFn = $parse(match[3] || '');
28682     var disableWhenFn = $parse(match[4] || '');
28683     var valuesFn = $parse(match[8]);
28685     var locals = {};
28686     var getLocals = keyName ? function(value, key) {
28687       locals[keyName] = key;
28688       locals[valueName] = value;
28689       return locals;
28690     } : function(value) {
28691       locals[valueName] = value;
28692       return locals;
28693     };
28696     function Option(selectValue, viewValue, label, group, disabled) {
28697       this.selectValue = selectValue;
28698       this.viewValue = viewValue;
28699       this.label = label;
28700       this.group = group;
28701       this.disabled = disabled;
28702     }
28704     function getOptionValuesKeys(optionValues) {
28705       var optionValuesKeys;
28707       if (!keyName && isArrayLike(optionValues)) {
28708         optionValuesKeys = optionValues;
28709       } else {
28710         // if object, extract keys, in enumeration order, unsorted
28711         optionValuesKeys = [];
28712         for (var itemKey in optionValues) {
28713           if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
28714             optionValuesKeys.push(itemKey);
28715           }
28716         }
28717       }
28718       return optionValuesKeys;
28719     }
28721     return {
28722       trackBy: trackBy,
28723       getTrackByValue: getTrackByValue,
28724       getWatchables: $parse(valuesFn, function(optionValues) {
28725         // Create a collection of things that we would like to watch (watchedArray)
28726         // so that they can all be watched using a single $watchCollection
28727         // that only runs the handler once if anything changes
28728         var watchedArray = [];
28729         optionValues = optionValues || [];
28731         var optionValuesKeys = getOptionValuesKeys(optionValues);
28732         var optionValuesLength = optionValuesKeys.length;
28733         for (var index = 0; index < optionValuesLength; index++) {
28734           var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
28735           var value = optionValues[key];
28737           var locals = getLocals(value, key);
28738           var selectValue = getTrackByValueFn(value, locals);
28739           watchedArray.push(selectValue);
28741           // Only need to watch the displayFn if there is a specific label expression
28742           if (match[2] || match[1]) {
28743             var label = displayFn(scope, locals);
28744             watchedArray.push(label);
28745           }
28747           // Only need to watch the disableWhenFn if there is a specific disable expression
28748           if (match[4]) {
28749             var disableWhen = disableWhenFn(scope, locals);
28750             watchedArray.push(disableWhen);
28751           }
28752         }
28753         return watchedArray;
28754       }),
28756       getOptions: function() {
28758         var optionItems = [];
28759         var selectValueMap = {};
28761         // The option values were already computed in the `getWatchables` fn,
28762         // which must have been called to trigger `getOptions`
28763         var optionValues = valuesFn(scope) || [];
28764         var optionValuesKeys = getOptionValuesKeys(optionValues);
28765         var optionValuesLength = optionValuesKeys.length;
28767         for (var index = 0; index < optionValuesLength; index++) {
28768           var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
28769           var value = optionValues[key];
28770           var locals = getLocals(value, key);
28771           var viewValue = viewValueFn(scope, locals);
28772           var selectValue = getTrackByValueFn(viewValue, locals);
28773           var label = displayFn(scope, locals);
28774           var group = groupByFn(scope, locals);
28775           var disabled = disableWhenFn(scope, locals);
28776           var optionItem = new Option(selectValue, viewValue, label, group, disabled);
28778           optionItems.push(optionItem);
28779           selectValueMap[selectValue] = optionItem;
28780         }
28782         return {
28783           items: optionItems,
28784           selectValueMap: selectValueMap,
28785           getOptionFromViewValue: function(value) {
28786             return selectValueMap[getTrackByValue(value)];
28787           },
28788           getViewValueFromOption: function(option) {
28789             // If the viewValue could be an object that may be mutated by the application,
28790             // we need to make a copy and not return the reference to the value on the option.
28791             return trackBy ? angular.copy(option.viewValue) : option.viewValue;
28792           }
28793         };
28794       }
28795     };
28796   }
28799   // we can't just jqLite('<option>') since jqLite is not smart enough
28800   // to create it in <select> and IE barfs otherwise.
28801   var optionTemplate = window.document.createElement('option'),
28802       optGroupTemplate = window.document.createElement('optgroup');
28804     function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
28806       var selectCtrl = ctrls[0];
28807       var ngModelCtrl = ctrls[1];
28808       var multiple = attr.multiple;
28810       // The emptyOption allows the application developer to provide their own custom "empty"
28811       // option when the viewValue does not match any of the option values.
28812       var emptyOption;
28813       for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
28814         if (children[i].value === '') {
28815           emptyOption = children.eq(i);
28816           break;
28817         }
28818       }
28820       var providedEmptyOption = !!emptyOption;
28822       var unknownOption = jqLite(optionTemplate.cloneNode(false));
28823       unknownOption.val('?');
28825       var options;
28826       var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
28827       // This stores the newly created options before they are appended to the select.
28828       // Since the contents are removed from the fragment when it is appended,
28829       // we only need to create it once.
28830       var listFragment = $document[0].createDocumentFragment();
28832       var renderEmptyOption = function() {
28833         if (!providedEmptyOption) {
28834           selectElement.prepend(emptyOption);
28835         }
28836         selectElement.val('');
28837         emptyOption.prop('selected', true); // needed for IE
28838         emptyOption.attr('selected', true);
28839       };
28841       var removeEmptyOption = function() {
28842         if (!providedEmptyOption) {
28843           emptyOption.remove();
28844         }
28845       };
28848       var renderUnknownOption = function() {
28849         selectElement.prepend(unknownOption);
28850         selectElement.val('?');
28851         unknownOption.prop('selected', true); // needed for IE
28852         unknownOption.attr('selected', true);
28853       };
28855       var removeUnknownOption = function() {
28856         unknownOption.remove();
28857       };
28859       // Update the controller methods for multiple selectable options
28860       if (!multiple) {
28862         selectCtrl.writeValue = function writeNgOptionsValue(value) {
28863           var option = options.getOptionFromViewValue(value);
28865           if (option) {
28866             // Don't update the option when it is already selected.
28867             // For example, the browser will select the first option by default. In that case,
28868             // most properties are set automatically - except the `selected` attribute, which we
28869             // set always
28871             if (selectElement[0].value !== option.selectValue) {
28872               removeUnknownOption();
28873               removeEmptyOption();
28875               selectElement[0].value = option.selectValue;
28876               option.element.selected = true;
28877             }
28879             option.element.setAttribute('selected', 'selected');
28880           } else {
28881             if (value === null || providedEmptyOption) {
28882               removeUnknownOption();
28883               renderEmptyOption();
28884             } else {
28885               removeEmptyOption();
28886               renderUnknownOption();
28887             }
28888           }
28889         };
28891         selectCtrl.readValue = function readNgOptionsValue() {
28893           var selectedOption = options.selectValueMap[selectElement.val()];
28895           if (selectedOption && !selectedOption.disabled) {
28896             removeEmptyOption();
28897             removeUnknownOption();
28898             return options.getViewValueFromOption(selectedOption);
28899           }
28900           return null;
28901         };
28903         // If we are using `track by` then we must watch the tracked value on the model
28904         // since ngModel only watches for object identity change
28905         if (ngOptions.trackBy) {
28906           scope.$watch(
28907             function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
28908             function() { ngModelCtrl.$render(); }
28909           );
28910         }
28912       } else {
28914         ngModelCtrl.$isEmpty = function(value) {
28915           return !value || value.length === 0;
28916         };
28919         selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
28920           options.items.forEach(function(option) {
28921             option.element.selected = false;
28922           });
28924           if (value) {
28925             value.forEach(function(item) {
28926               var option = options.getOptionFromViewValue(item);
28927               if (option) option.element.selected = true;
28928             });
28929           }
28930         };
28933         selectCtrl.readValue = function readNgOptionsMultiple() {
28934           var selectedValues = selectElement.val() || [],
28935               selections = [];
28937           forEach(selectedValues, function(value) {
28938             var option = options.selectValueMap[value];
28939             if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
28940           });
28942           return selections;
28943         };
28945         // If we are using `track by` then we must watch these tracked values on the model
28946         // since ngModel only watches for object identity change
28947         if (ngOptions.trackBy) {
28949           scope.$watchCollection(function() {
28950             if (isArray(ngModelCtrl.$viewValue)) {
28951               return ngModelCtrl.$viewValue.map(function(value) {
28952                 return ngOptions.getTrackByValue(value);
28953               });
28954             }
28955           }, function() {
28956             ngModelCtrl.$render();
28957           });
28959         }
28960       }
28963       if (providedEmptyOption) {
28965         // we need to remove it before calling selectElement.empty() because otherwise IE will
28966         // remove the label from the element. wtf?
28967         emptyOption.remove();
28969         // compile the element since there might be bindings in it
28970         $compile(emptyOption)(scope);
28972         // remove the class, which is added automatically because we recompile the element and it
28973         // becomes the compilation root
28974         emptyOption.removeClass('ng-scope');
28975       } else {
28976         emptyOption = jqLite(optionTemplate.cloneNode(false));
28977       }
28979       selectElement.empty();
28981       // We need to do this here to ensure that the options object is defined
28982       // when we first hit it in writeNgOptionsValue
28983       updateOptions();
28985       // We will re-render the option elements if the option values or labels change
28986       scope.$watchCollection(ngOptions.getWatchables, updateOptions);
28988       // ------------------------------------------------------------------ //
28990       function addOptionElement(option, parent) {
28991         var optionElement = optionTemplate.cloneNode(false);
28992         parent.appendChild(optionElement);
28993         updateOptionElement(option, optionElement);
28994       }
28997       function updateOptionElement(option, element) {
28998         option.element = element;
28999         element.disabled = option.disabled;
29000         // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
29001         // selects in certain circumstances when multiple selects are next to each other and display
29002         // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
29003         // See https://github.com/angular/angular.js/issues/11314 for more info.
29004         // This is unfortunately untestable with unit / e2e tests
29005         if (option.label !== element.label) {
29006           element.label = option.label;
29007           element.textContent = option.label;
29008         }
29009         if (option.value !== element.value) element.value = option.selectValue;
29010       }
29012       function updateOptions() {
29013         var previousValue = options && selectCtrl.readValue();
29015         // We must remove all current options, but cannot simply set innerHTML = null
29016         // since the providedEmptyOption might have an ngIf on it that inserts comments which we
29017         // must preserve.
29018         // Instead, iterate over the current option elements and remove them or their optgroup
29019         // parents
29020         if (options) {
29022           for (var i = options.items.length - 1; i >= 0; i--) {
29023             var option = options.items[i];
29024             if (isDefined(option.group)) {
29025               jqLiteRemove(option.element.parentNode);
29026             } else {
29027               jqLiteRemove(option.element);
29028             }
29029           }
29030         }
29032         options = ngOptions.getOptions();
29034         var groupElementMap = {};
29036         // Ensure that the empty option is always there if it was explicitly provided
29037         if (providedEmptyOption) {
29038           selectElement.prepend(emptyOption);
29039         }
29041         options.items.forEach(function addOption(option) {
29042           var groupElement;
29044           if (isDefined(option.group)) {
29046             // This option is to live in a group
29047             // See if we have already created this group
29048             groupElement = groupElementMap[option.group];
29050             if (!groupElement) {
29052               groupElement = optGroupTemplate.cloneNode(false);
29053               listFragment.appendChild(groupElement);
29055               // Update the label on the group element
29056               // "null" is special cased because of Safari
29057               groupElement.label = option.group === null ? 'null' : option.group;
29059               // Store it for use later
29060               groupElementMap[option.group] = groupElement;
29061             }
29063             addOptionElement(option, groupElement);
29065           } else {
29067             // This option is not in a group
29068             addOptionElement(option, listFragment);
29069           }
29070         });
29072         selectElement[0].appendChild(listFragment);
29074         ngModelCtrl.$render();
29076         // Check to see if the value has changed due to the update to the options
29077         if (!ngModelCtrl.$isEmpty(previousValue)) {
29078           var nextValue = selectCtrl.readValue();
29079           var isNotPrimitive = ngOptions.trackBy || multiple;
29080           if (isNotPrimitive ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
29081             ngModelCtrl.$setViewValue(nextValue);
29082             ngModelCtrl.$render();
29083           }
29084         }
29086       }
29087   }
29089   return {
29090     restrict: 'A',
29091     terminal: true,
29092     require: ['select', 'ngModel'],
29093     link: {
29094       pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
29095         // Deactivate the SelectController.register method to prevent
29096         // option directives from accidentally registering themselves
29097         // (and unwanted $destroy handlers etc.)
29098         ctrls[0].registerOption = noop;
29099       },
29100       post: ngOptionsPostLink
29101     }
29102   };
29106  * @ngdoc directive
29107  * @name ngPluralize
29108  * @restrict EA
29110  * @description
29111  * `ngPluralize` is a directive that displays messages according to en-US localization rules.
29112  * These rules are bundled with angular.js, but can be overridden
29113  * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
29114  * by specifying the mappings between
29115  * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
29116  * and the strings to be displayed.
29118  * # Plural categories and explicit number rules
29119  * There are two
29120  * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
29121  * in Angular's default en-US locale: "one" and "other".
29123  * While a plural category may match many numbers (for example, in en-US locale, "other" can match
29124  * any number that is not 1), an explicit number rule can only match one number. For example, the
29125  * explicit number rule for "3" matches the number 3. There are examples of plural categories
29126  * and explicit number rules throughout the rest of this documentation.
29128  * # Configuring ngPluralize
29129  * You configure ngPluralize by providing 2 attributes: `count` and `when`.
29130  * You can also provide an optional attribute, `offset`.
29132  * The value of the `count` attribute can be either a string or an {@link guide/expression
29133  * Angular expression}; these are evaluated on the current scope for its bound value.
29135  * The `when` attribute specifies the mappings between plural categories and the actual
29136  * string to be displayed. The value of the attribute should be a JSON object.
29138  * The following example shows how to configure ngPluralize:
29140  * ```html
29141  * <ng-pluralize count="personCount"
29142                  when="{'0': 'Nobody is viewing.',
29143  *                      'one': '1 person is viewing.',
29144  *                      'other': '{} people are viewing.'}">
29145  * </ng-pluralize>
29146  *```
29148  * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
29149  * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
29150  * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
29151  * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
29152  * show "a dozen people are viewing".
29154  * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
29155  * into pluralized strings. In the previous example, Angular will replace `{}` with
29156  * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
29157  * for <span ng-non-bindable>{{numberExpression}}</span>.
29159  * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
29160  * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
29162  * # Configuring ngPluralize with offset
29163  * The `offset` attribute allows further customization of pluralized text, which can result in
29164  * a better user experience. For example, instead of the message "4 people are viewing this document",
29165  * you might display "John, Kate and 2 others are viewing this document".
29166  * The offset attribute allows you to offset a number by any desired value.
29167  * Let's take a look at an example:
29169  * ```html
29170  * <ng-pluralize count="personCount" offset=2
29171  *               when="{'0': 'Nobody is viewing.',
29172  *                      '1': '{{person1}} is viewing.',
29173  *                      '2': '{{person1}} and {{person2}} are viewing.',
29174  *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
29175  *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
29176  * </ng-pluralize>
29177  * ```
29179  * Notice that we are still using two plural categories(one, other), but we added
29180  * three explicit number rules 0, 1 and 2.
29181  * When one person, perhaps John, views the document, "John is viewing" will be shown.
29182  * When three people view the document, no explicit number rule is found, so
29183  * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
29184  * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
29185  * is shown.
29187  * Note that when you specify offsets, you must provide explicit number rules for
29188  * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
29189  * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
29190  * plural categories "one" and "other".
29192  * @param {string|expression} count The variable to be bound to.
29193  * @param {string} when The mapping between plural category to its corresponding strings.
29194  * @param {number=} offset Offset to deduct from the total number.
29196  * @example
29197     <example module="pluralizeExample">
29198       <file name="index.html">
29199         <script>
29200           angular.module('pluralizeExample', [])
29201             .controller('ExampleController', ['$scope', function($scope) {
29202               $scope.person1 = 'Igor';
29203               $scope.person2 = 'Misko';
29204               $scope.personCount = 1;
29205             }]);
29206         </script>
29207         <div ng-controller="ExampleController">
29208           <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
29209           <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
29210           <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
29212           <!--- Example with simple pluralization rules for en locale --->
29213           Without Offset:
29214           <ng-pluralize count="personCount"
29215                         when="{'0': 'Nobody is viewing.',
29216                                'one': '1 person is viewing.',
29217                                'other': '{} people are viewing.'}">
29218           </ng-pluralize><br>
29220           <!--- Example with offset --->
29221           With Offset(2):
29222           <ng-pluralize count="personCount" offset=2
29223                         when="{'0': 'Nobody is viewing.',
29224                                '1': '{{person1}} is viewing.',
29225                                '2': '{{person1}} and {{person2}} are viewing.',
29226                                'one': '{{person1}}, {{person2}} and one other person are viewing.',
29227                                'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
29228           </ng-pluralize>
29229         </div>
29230       </file>
29231       <file name="protractor.js" type="protractor">
29232         it('should show correct pluralized string', function() {
29233           var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
29234           var withOffset = element.all(by.css('ng-pluralize')).get(1);
29235           var countInput = element(by.model('personCount'));
29237           expect(withoutOffset.getText()).toEqual('1 person is viewing.');
29238           expect(withOffset.getText()).toEqual('Igor is viewing.');
29240           countInput.clear();
29241           countInput.sendKeys('0');
29243           expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
29244           expect(withOffset.getText()).toEqual('Nobody is viewing.');
29246           countInput.clear();
29247           countInput.sendKeys('2');
29249           expect(withoutOffset.getText()).toEqual('2 people are viewing.');
29250           expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
29252           countInput.clear();
29253           countInput.sendKeys('3');
29255           expect(withoutOffset.getText()).toEqual('3 people are viewing.');
29256           expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
29258           countInput.clear();
29259           countInput.sendKeys('4');
29261           expect(withoutOffset.getText()).toEqual('4 people are viewing.');
29262           expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
29263         });
29264         it('should show data-bound names', function() {
29265           var withOffset = element.all(by.css('ng-pluralize')).get(1);
29266           var personCount = element(by.model('personCount'));
29267           var person1 = element(by.model('person1'));
29268           var person2 = element(by.model('person2'));
29269           personCount.clear();
29270           personCount.sendKeys('4');
29271           person1.clear();
29272           person1.sendKeys('Di');
29273           person2.clear();
29274           person2.sendKeys('Vojta');
29275           expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
29276         });
29277       </file>
29278     </example>
29279  */
29280 var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
29281   var BRACE = /{}/g,
29282       IS_WHEN = /^when(Minus)?(.+)$/;
29284   return {
29285     link: function(scope, element, attr) {
29286       var numberExp = attr.count,
29287           whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
29288           offset = attr.offset || 0,
29289           whens = scope.$eval(whenExp) || {},
29290           whensExpFns = {},
29291           startSymbol = $interpolate.startSymbol(),
29292           endSymbol = $interpolate.endSymbol(),
29293           braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
29294           watchRemover = angular.noop,
29295           lastCount;
29297       forEach(attr, function(expression, attributeName) {
29298         var tmpMatch = IS_WHEN.exec(attributeName);
29299         if (tmpMatch) {
29300           var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
29301           whens[whenKey] = element.attr(attr.$attr[attributeName]);
29302         }
29303       });
29304       forEach(whens, function(expression, key) {
29305         whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
29307       });
29309       scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
29310         var count = parseFloat(newVal);
29311         var countIsNaN = isNaN(count);
29313         if (!countIsNaN && !(count in whens)) {
29314           // If an explicit number rule such as 1, 2, 3... is defined, just use it.
29315           // Otherwise, check it against pluralization rules in $locale service.
29316           count = $locale.pluralCat(count - offset);
29317         }
29319         // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
29320         // In JS `NaN !== NaN`, so we have to explicitly check.
29321         if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
29322           watchRemover();
29323           var whenExpFn = whensExpFns[count];
29324           if (isUndefined(whenExpFn)) {
29325             if (newVal != null) {
29326               $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
29327             }
29328             watchRemover = noop;
29329             updateElementText();
29330           } else {
29331             watchRemover = scope.$watch(whenExpFn, updateElementText);
29332           }
29333           lastCount = count;
29334         }
29335       });
29337       function updateElementText(newText) {
29338         element.text(newText || '');
29339       }
29340     }
29341   };
29345  * @ngdoc directive
29346  * @name ngRepeat
29347  * @multiElement
29349  * @description
29350  * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
29351  * instance gets its own scope, where the given loop variable is set to the current collection item,
29352  * and `$index` is set to the item index or key.
29354  * Special properties are exposed on the local scope of each template instance, including:
29356  * | Variable  | Type            | Details                                                                     |
29357  * |-----------|-----------------|-----------------------------------------------------------------------------|
29358  * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
29359  * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
29360  * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
29361  * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
29362  * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
29363  * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
29365  * <div class="alert alert-info">
29366  *   Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
29367  *   This may be useful when, for instance, nesting ngRepeats.
29368  * </div>
29371  * # Iterating over object properties
29373  * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
29374  * syntax:
29376  * ```js
29377  * <div ng-repeat="(key, value) in myObj"> ... </div>
29378  * ```
29380  * However, there are a limitations compared to array iteration:
29382  * - The JavaScript specification does not define the order of keys
29383  *   returned for an object, so Angular relies on the order returned by the browser
29384  *   when running `for key in myObj`. Browsers generally follow the strategy of providing
29385  *   keys in the order in which they were defined, although there are exceptions when keys are deleted
29386  *   and reinstated. See the
29387  *   [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
29389  * - `ngRepeat` will silently *ignore* object keys starting with `$`, because
29390  *   it's a prefix used by Angular for public (`$`) and private (`$$`) properties.
29392  * - The built-in filters {@link ng.orderBy orderBy} and {@link ng.filter filter} do not work with
29393  *   objects, and will throw an error if used with one.
29395  * If you are hitting any of these limitations, the recommended workaround is to convert your object into an array
29396  * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
29397  * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
29398  * or implement a `$watch` on the object yourself.
29401  * # Tracking and Duplicates
29403  * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
29404  * the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
29406  * * When an item is added, a new instance of the template is added to the DOM.
29407  * * When an item is removed, its template instance is removed from the DOM.
29408  * * When items are reordered, their respective templates are reordered in the DOM.
29410  * To minimize creation of DOM elements, `ngRepeat` uses a function
29411  * to "keep track" of all items in the collection and their corresponding DOM elements.
29412  * For example, if an item is added to the collection, ngRepeat will know that all other items
29413  * already have DOM elements, and will not re-render them.
29415  * The default tracking function (which tracks items by their identity) does not allow
29416  * duplicate items in arrays. This is because when there are duplicates, it is not possible
29417  * to maintain a one-to-one mapping between collection items and DOM elements.
29419  * If you do need to repeat duplicate items, you can substitute the default tracking behavior
29420  * with your own using the `track by` expression.
29422  * For example, you may track items by the index of each item in the collection, using the
29423  * special scope property `$index`:
29424  * ```html
29425  *    <div ng-repeat="n in [42, 42, 43, 43] track by $index">
29426  *      {{n}}
29427  *    </div>
29428  * ```
29430  * You may also use arbitrary expressions in `track by`, including references to custom functions
29431  * on the scope:
29432  * ```html
29433  *    <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
29434  *      {{n}}
29435  *    </div>
29436  * ```
29438  * <div class="alert alert-success">
29439  * If you are working with objects that have an identifier property, you should track
29440  * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
29441  * will not have to rebuild the DOM elements for items it has already rendered, even if the
29442  * JavaScript objects in the collection have been substituted for new ones. For large collections,
29443  * this significantly improves rendering performance. If you don't have a unique identifier,
29444  * `track by $index` can also provide a performance boost.
29445  * </div>
29446  * ```html
29447  *    <div ng-repeat="model in collection track by model.id">
29448  *      {{model.name}}
29449  *    </div>
29450  * ```
29452  * When no `track by` expression is provided, it is equivalent to tracking by the built-in
29453  * `$id` function, which tracks items by their identity:
29454  * ```html
29455  *    <div ng-repeat="obj in collection track by $id(obj)">
29456  *      {{obj.prop}}
29457  *    </div>
29458  * ```
29460  * <div class="alert alert-warning">
29461  * **Note:** `track by` must always be the last expression:
29462  * </div>
29463  * ```
29464  * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
29465  *     {{model.name}}
29466  * </div>
29467  * ```
29469  * # Special repeat start and end points
29470  * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
29471  * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
29472  * 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)
29473  * up to and including the ending HTML tag where **ng-repeat-end** is placed.
29475  * The example below makes use of this feature:
29476  * ```html
29477  *   <header ng-repeat-start="item in items">
29478  *     Header {{ item }}
29479  *   </header>
29480  *   <div class="body">
29481  *     Body {{ item }}
29482  *   </div>
29483  *   <footer ng-repeat-end>
29484  *     Footer {{ item }}
29485  *   </footer>
29486  * ```
29488  * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
29489  * ```html
29490  *   <header>
29491  *     Header A
29492  *   </header>
29493  *   <div class="body">
29494  *     Body A
29495  *   </div>
29496  *   <footer>
29497  *     Footer A
29498  *   </footer>
29499  *   <header>
29500  *     Header B
29501  *   </header>
29502  *   <div class="body">
29503  *     Body B
29504  *   </div>
29505  *   <footer>
29506  *     Footer B
29507  *   </footer>
29508  * ```
29510  * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
29511  * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
29513  * @animations
29514  * | Animation                        | Occurs                              |
29515  * |----------------------------------|-------------------------------------|
29516  * | {@link ng.$animate#enter enter} | when a new item is added to the list or when an item is revealed after a filter |
29517  * | {@link ng.$animate#leave leave} | when an item is removed from the list or when an item is filtered out |
29518  * | {@link ng.$animate#move move } | when an adjacent item is filtered out causing a reorder or when the item contents are reordered |
29520  * See the example below for defining CSS animations with ngRepeat.
29522  * @element ANY
29523  * @scope
29524  * @priority 1000
29525  * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
29526  *   formats are currently supported:
29528  *   * `variable in expression` â€“ where variable is the user defined loop variable and `expression`
29529  *     is a scope expression giving the collection to enumerate.
29531  *     For example: `album in artist.albums`.
29533  *   * `(key, value) in expression` â€“ where `key` and `value` can be any user defined identifiers,
29534  *     and `expression` is the scope expression giving the collection to enumerate.
29536  *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
29538  *   * `variable in expression track by tracking_expression` â€“ You can also provide an optional tracking expression
29539  *     which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
29540  *     is specified, ng-repeat associates elements by identity. It is an error to have
29541  *     more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
29542  *     mapped to the same DOM element, which is not possible.)
29544  *     Note that the tracking expression must come last, after any filters, and the alias expression.
29546  *     For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
29547  *     will be associated by item identity in the array.
29549  *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
29550  *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
29551  *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM
29552  *     element in the same way in the DOM.
29554  *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
29555  *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`
29556  *     property is same.
29558  *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
29559  *     to items in conjunction with a tracking expression.
29561  *   * `variable in expression as alias_expression` â€“ You can also provide an optional alias expression which will then store the
29562  *     intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
29563  *     when a filter is active on the repeater, but the filtered result set is empty.
29565  *     For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
29566  *     the items have been processed through the filter.
29568  *     Please note that `as [variable name] is not an operator but rather a part of ngRepeat micro-syntax so it can be used only at the end
29569  *     (and not as operator, inside an expression).
29571  *     For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
29573  * @example
29574  * This example uses `ngRepeat` to display a list of people. A filter is used to restrict the displayed
29575  * results by name. New (entering) and removed (leaving) items are animated.
29576   <example module="ngRepeat" name="ngRepeat" deps="angular-animate.js" animations="true">
29577     <file name="index.html">
29578       <div ng-controller="repeatController">
29579         I have {{friends.length}} friends. They are:
29580         <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
29581         <ul class="example-animate-container">
29582           <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
29583             [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
29584           </li>
29585           <li class="animate-repeat" ng-if="results.length == 0">
29586             <strong>No results found...</strong>
29587           </li>
29588         </ul>
29589       </div>
29590     </file>
29591     <file name="script.js">
29592       angular.module('ngRepeat', ['ngAnimate']).controller('repeatController', function($scope) {
29593         $scope.friends = [
29594           {name:'John', age:25, gender:'boy'},
29595           {name:'Jessie', age:30, gender:'girl'},
29596           {name:'Johanna', age:28, gender:'girl'},
29597           {name:'Joy', age:15, gender:'girl'},
29598           {name:'Mary', age:28, gender:'girl'},
29599           {name:'Peter', age:95, gender:'boy'},
29600           {name:'Sebastian', age:50, gender:'boy'},
29601           {name:'Erika', age:27, gender:'girl'},
29602           {name:'Patrick', age:40, gender:'boy'},
29603           {name:'Samantha', age:60, gender:'girl'}
29604         ];
29605       });
29606     </file>
29607     <file name="animations.css">
29608       .example-animate-container {
29609         background:white;
29610         border:1px solid black;
29611         list-style:none;
29612         margin:0;
29613         padding:0 10px;
29614       }
29616       .animate-repeat {
29617         line-height:30px;
29618         list-style:none;
29619         box-sizing:border-box;
29620       }
29622       .animate-repeat.ng-move,
29623       .animate-repeat.ng-enter,
29624       .animate-repeat.ng-leave {
29625         transition:all linear 0.5s;
29626       }
29628       .animate-repeat.ng-leave.ng-leave-active,
29629       .animate-repeat.ng-move,
29630       .animate-repeat.ng-enter {
29631         opacity:0;
29632         max-height:0;
29633       }
29635       .animate-repeat.ng-leave,
29636       .animate-repeat.ng-move.ng-move-active,
29637       .animate-repeat.ng-enter.ng-enter-active {
29638         opacity:1;
29639         max-height:30px;
29640       }
29641     </file>
29642     <file name="protractor.js" type="protractor">
29643       var friends = element.all(by.repeater('friend in friends'));
29645       it('should render initial data set', function() {
29646         expect(friends.count()).toBe(10);
29647         expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
29648         expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
29649         expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
29650         expect(element(by.binding('friends.length')).getText())
29651             .toMatch("I have 10 friends. They are:");
29652       });
29654        it('should update repeater when filter predicate changes', function() {
29655          expect(friends.count()).toBe(10);
29657          element(by.model('q')).sendKeys('ma');
29659          expect(friends.count()).toBe(2);
29660          expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
29661          expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
29662        });
29663       </file>
29664     </example>
29665  */
29666 var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $animate, $compile) {
29667   var NG_REMOVED = '$$NG_REMOVED';
29668   var ngRepeatMinErr = minErr('ngRepeat');
29670   var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
29671     // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
29672     scope[valueIdentifier] = value;
29673     if (keyIdentifier) scope[keyIdentifier] = key;
29674     scope.$index = index;
29675     scope.$first = (index === 0);
29676     scope.$last = (index === (arrayLength - 1));
29677     scope.$middle = !(scope.$first || scope.$last);
29678     // jshint bitwise: false
29679     scope.$odd = !(scope.$even = (index&1) === 0);
29680     // jshint bitwise: true
29681   };
29683   var getBlockStart = function(block) {
29684     return block.clone[0];
29685   };
29687   var getBlockEnd = function(block) {
29688     return block.clone[block.clone.length - 1];
29689   };
29692   return {
29693     restrict: 'A',
29694     multiElement: true,
29695     transclude: 'element',
29696     priority: 1000,
29697     terminal: true,
29698     $$tlb: true,
29699     compile: function ngRepeatCompile($element, $attr) {
29700       var expression = $attr.ngRepeat;
29701       var ngRepeatEndComment = $compile.$$createComment('end ngRepeat', expression);
29703       var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
29705       if (!match) {
29706         throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
29707             expression);
29708       }
29710       var lhs = match[1];
29711       var rhs = match[2];
29712       var aliasAs = match[3];
29713       var trackByExp = match[4];
29715       match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
29717       if (!match) {
29718         throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
29719             lhs);
29720       }
29721       var valueIdentifier = match[3] || match[1];
29722       var keyIdentifier = match[2];
29724       if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
29725           /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
29726         throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
29727           aliasAs);
29728       }
29730       var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
29731       var hashFnLocals = {$id: hashKey};
29733       if (trackByExp) {
29734         trackByExpGetter = $parse(trackByExp);
29735       } else {
29736         trackByIdArrayFn = function(key, value) {
29737           return hashKey(value);
29738         };
29739         trackByIdObjFn = function(key) {
29740           return key;
29741         };
29742       }
29744       return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
29746         if (trackByExpGetter) {
29747           trackByIdExpFn = function(key, value, index) {
29748             // assign key, value, and $index to the locals so that they can be used in hash functions
29749             if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
29750             hashFnLocals[valueIdentifier] = value;
29751             hashFnLocals.$index = index;
29752             return trackByExpGetter($scope, hashFnLocals);
29753           };
29754         }
29756         // Store a list of elements from previous run. This is a hash where key is the item from the
29757         // iterator, and the value is objects with following properties.
29758         //   - scope: bound scope
29759         //   - element: previous element.
29760         //   - index: position
29761         //
29762         // We are using no-proto object so that we don't need to guard against inherited props via
29763         // hasOwnProperty.
29764         var lastBlockMap = createMap();
29766         //watch props
29767         $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
29768           var index, length,
29769               previousNode = $element[0],     // node that cloned nodes should be inserted after
29770                                               // initialized to the comment node anchor
29771               nextNode,
29772               // Same as lastBlockMap but it has the current state. It will become the
29773               // lastBlockMap on the next iteration.
29774               nextBlockMap = createMap(),
29775               collectionLength,
29776               key, value, // key/value of iteration
29777               trackById,
29778               trackByIdFn,
29779               collectionKeys,
29780               block,       // last object information {scope, element, id}
29781               nextBlockOrder,
29782               elementsToRemove;
29784           if (aliasAs) {
29785             $scope[aliasAs] = collection;
29786           }
29788           if (isArrayLike(collection)) {
29789             collectionKeys = collection;
29790             trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
29791           } else {
29792             trackByIdFn = trackByIdExpFn || trackByIdObjFn;
29793             // if object, extract keys, in enumeration order, unsorted
29794             collectionKeys = [];
29795             for (var itemKey in collection) {
29796               if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
29797                 collectionKeys.push(itemKey);
29798               }
29799             }
29800           }
29802           collectionLength = collectionKeys.length;
29803           nextBlockOrder = new Array(collectionLength);
29805           // locate existing items
29806           for (index = 0; index < collectionLength; index++) {
29807             key = (collection === collectionKeys) ? index : collectionKeys[index];
29808             value = collection[key];
29809             trackById = trackByIdFn(key, value, index);
29810             if (lastBlockMap[trackById]) {
29811               // found previously seen block
29812               block = lastBlockMap[trackById];
29813               delete lastBlockMap[trackById];
29814               nextBlockMap[trackById] = block;
29815               nextBlockOrder[index] = block;
29816             } else if (nextBlockMap[trackById]) {
29817               // if collision detected. restore lastBlockMap and throw an error
29818               forEach(nextBlockOrder, function(block) {
29819                 if (block && block.scope) lastBlockMap[block.id] = block;
29820               });
29821               throw ngRepeatMinErr('dupes',
29822                   "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
29823                   expression, trackById, value);
29824             } else {
29825               // new never before seen block
29826               nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
29827               nextBlockMap[trackById] = true;
29828             }
29829           }
29831           // remove leftover items
29832           for (var blockKey in lastBlockMap) {
29833             block = lastBlockMap[blockKey];
29834             elementsToRemove = getBlockNodes(block.clone);
29835             $animate.leave(elementsToRemove);
29836             if (elementsToRemove[0].parentNode) {
29837               // if the element was not removed yet because of pending animation, mark it as deleted
29838               // so that we can ignore it later
29839               for (index = 0, length = elementsToRemove.length; index < length; index++) {
29840                 elementsToRemove[index][NG_REMOVED] = true;
29841               }
29842             }
29843             block.scope.$destroy();
29844           }
29846           // we are not using forEach for perf reasons (trying to avoid #call)
29847           for (index = 0; index < collectionLength; index++) {
29848             key = (collection === collectionKeys) ? index : collectionKeys[index];
29849             value = collection[key];
29850             block = nextBlockOrder[index];
29852             if (block.scope) {
29853               // if we have already seen this object, then we need to reuse the
29854               // associated scope/element
29856               nextNode = previousNode;
29858               // skip nodes that are already pending removal via leave animation
29859               do {
29860                 nextNode = nextNode.nextSibling;
29861               } while (nextNode && nextNode[NG_REMOVED]);
29863               if (getBlockStart(block) != nextNode) {
29864                 // existing item which got moved
29865                 $animate.move(getBlockNodes(block.clone), null, previousNode);
29866               }
29867               previousNode = getBlockEnd(block);
29868               updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
29869             } else {
29870               // new item which we don't know about
29871               $transclude(function ngRepeatTransclude(clone, scope) {
29872                 block.scope = scope;
29873                 // http://jsperf.com/clone-vs-createcomment
29874                 var endNode = ngRepeatEndComment.cloneNode(false);
29875                 clone[clone.length++] = endNode;
29877                 $animate.enter(clone, null, previousNode);
29878                 previousNode = endNode;
29879                 // Note: We only need the first/last node of the cloned nodes.
29880                 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
29881                 // by a directive with templateUrl when its template arrives.
29882                 block.clone = clone;
29883                 nextBlockMap[block.id] = block;
29884                 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
29885               });
29886             }
29887           }
29888           lastBlockMap = nextBlockMap;
29889         });
29890       };
29891     }
29892   };
29895 var NG_HIDE_CLASS = 'ng-hide';
29896 var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
29898  * @ngdoc directive
29899  * @name ngShow
29900  * @multiElement
29902  * @description
29903  * The `ngShow` directive shows or hides the given HTML element based on the expression
29904  * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
29905  * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
29906  * in AngularJS and sets the display style to none (using an !important flag).
29907  * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
29909  * ```html
29910  * <!-- when $scope.myValue is truthy (element is visible) -->
29911  * <div ng-show="myValue"></div>
29913  * <!-- when $scope.myValue is falsy (element is hidden) -->
29914  * <div ng-show="myValue" class="ng-hide"></div>
29915  * ```
29917  * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
29918  * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
29919  * from the element causing the element not to appear hidden.
29921  * ## Why is !important used?
29923  * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
29924  * can be easily overridden by heavier selectors. For example, something as simple
29925  * as changing the display style on a HTML list item would make hidden elements appear visible.
29926  * This also becomes a bigger issue when dealing with CSS frameworks.
29928  * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
29929  * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
29930  * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
29932  * ### Overriding `.ng-hide`
29934  * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
29935  * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
29936  * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
29937  * with extra animation classes that can be added.
29939  * ```css
29940  * .ng-hide:not(.ng-hide-animate) {
29941  *   /&#42; this is just another form of hiding an element &#42;/
29942  *   display: block!important;
29943  *   position: absolute;
29944  *   top: -9999px;
29945  *   left: -9999px;
29946  * }
29947  * ```
29949  * By default you don't need to override in CSS anything and the animations will work around the display style.
29951  * ## A note about animations with `ngShow`
29953  * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
29954  * is true and false. This system works like the animation system present with ngClass except that
29955  * you must also include the !important flag to override the display property
29956  * so that you can perform an animation when the element is hidden during the time of the animation.
29958  * ```css
29959  * //
29960  * //a working example can be found at the bottom of this page
29961  * //
29962  * .my-element.ng-hide-add, .my-element.ng-hide-remove {
29963  *   /&#42; this is required as of 1.3x to properly
29964  *      apply all styling in a show/hide animation &#42;/
29965  *   transition: 0s linear all;
29966  * }
29968  * .my-element.ng-hide-add-active,
29969  * .my-element.ng-hide-remove-active {
29970  *   /&#42; the transition is defined in the active class &#42;/
29971  *   transition: 1s linear all;
29972  * }
29974  * .my-element.ng-hide-add { ... }
29975  * .my-element.ng-hide-add.ng-hide-add-active { ... }
29976  * .my-element.ng-hide-remove { ... }
29977  * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
29978  * ```
29980  * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
29981  * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
29983  * @animations
29984  * | Animation                        | Occurs                              |
29985  * |----------------------------------|-------------------------------------|
29986  * | {@link $animate#addClass addClass} `.ng-hide`  | after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden |
29987  * | {@link $animate#removeClass removeClass}  `.ng-hide`  | after the `ngShow` expression evaluates to a truthy value and just before contents are set to visible |
29989  * @element ANY
29990  * @param {expression} ngShow If the {@link guide/expression expression} is truthy
29991  *     then the element is shown or hidden respectively.
29993  * @example
29994   <example module="ngAnimate" deps="angular-animate.js" animations="true">
29995     <file name="index.html">
29996       Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
29997       <div>
29998         Show:
29999         <div class="check-element animate-show" ng-show="checked">
30000           <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
30001         </div>
30002       </div>
30003       <div>
30004         Hide:
30005         <div class="check-element animate-show" ng-hide="checked">
30006           <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
30007         </div>
30008       </div>
30009     </file>
30010     <file name="glyphicons.css">
30011       @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
30012     </file>
30013     <file name="animations.css">
30014       .animate-show {
30015         line-height: 20px;
30016         opacity: 1;
30017         padding: 10px;
30018         border: 1px solid black;
30019         background: white;
30020       }
30022       .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
30023         transition: all linear 0.5s;
30024       }
30026       .animate-show.ng-hide {
30027         line-height: 0;
30028         opacity: 0;
30029         padding: 0 10px;
30030       }
30032       .check-element {
30033         padding: 10px;
30034         border: 1px solid black;
30035         background: white;
30036       }
30037     </file>
30038     <file name="protractor.js" type="protractor">
30039       var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
30040       var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
30042       it('should check ng-show / ng-hide', function() {
30043         expect(thumbsUp.isDisplayed()).toBeFalsy();
30044         expect(thumbsDown.isDisplayed()).toBeTruthy();
30046         element(by.model('checked')).click();
30048         expect(thumbsUp.isDisplayed()).toBeTruthy();
30049         expect(thumbsDown.isDisplayed()).toBeFalsy();
30050       });
30051     </file>
30052   </example>
30053  */
30054 var ngShowDirective = ['$animate', function($animate) {
30055   return {
30056     restrict: 'A',
30057     multiElement: true,
30058     link: function(scope, element, attr) {
30059       scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
30060         // we're adding a temporary, animation-specific class for ng-hide since this way
30061         // we can control when the element is actually displayed on screen without having
30062         // to have a global/greedy CSS selector that breaks when other animations are run.
30063         // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
30064         $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
30065           tempClasses: NG_HIDE_IN_PROGRESS_CLASS
30066         });
30067       });
30068     }
30069   };
30074  * @ngdoc directive
30075  * @name ngHide
30076  * @multiElement
30078  * @description
30079  * The `ngHide` directive shows or hides the given HTML element based on the expression
30080  * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
30081  * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
30082  * in AngularJS and sets the display style to none (using an !important flag).
30083  * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
30085  * ```html
30086  * <!-- when $scope.myValue is truthy (element is hidden) -->
30087  * <div ng-hide="myValue" class="ng-hide"></div>
30089  * <!-- when $scope.myValue is falsy (element is visible) -->
30090  * <div ng-hide="myValue"></div>
30091  * ```
30093  * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
30094  * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
30095  * from the element causing the element not to appear hidden.
30097  * ## Why is !important used?
30099  * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
30100  * can be easily overridden by heavier selectors. For example, something as simple
30101  * as changing the display style on a HTML list item would make hidden elements appear visible.
30102  * This also becomes a bigger issue when dealing with CSS frameworks.
30104  * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
30105  * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
30106  * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
30108  * ### Overriding `.ng-hide`
30110  * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
30111  * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
30112  * class in CSS:
30114  * ```css
30115  * .ng-hide {
30116  *   /&#42; this is just another form of hiding an element &#42;/
30117  *   display: block!important;
30118  *   position: absolute;
30119  *   top: -9999px;
30120  *   left: -9999px;
30121  * }
30122  * ```
30124  * By default you don't need to override in CSS anything and the animations will work around the display style.
30126  * ## A note about animations with `ngHide`
30128  * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
30129  * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
30130  * CSS class is added and removed for you instead of your own CSS class.
30132  * ```css
30133  * //
30134  * //a working example can be found at the bottom of this page
30135  * //
30136  * .my-element.ng-hide-add, .my-element.ng-hide-remove {
30137  *   transition: 0.5s linear all;
30138  * }
30140  * .my-element.ng-hide-add { ... }
30141  * .my-element.ng-hide-add.ng-hide-add-active { ... }
30142  * .my-element.ng-hide-remove { ... }
30143  * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
30144  * ```
30146  * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
30147  * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
30149  * @animations
30150  * | Animation                        | Occurs                              |
30151  * |----------------------------------|-------------------------------------|
30152  * | {@link $animate#addClass addClass} `.ng-hide`  | after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden |
30153  * | {@link $animate#removeClass removeClass}  `.ng-hide`  | after the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible |
30156  * @element ANY
30157  * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
30158  *     the element is shown or hidden respectively.
30160  * @example
30161   <example module="ngAnimate" deps="angular-animate.js" animations="true">
30162     <file name="index.html">
30163       Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
30164       <div>
30165         Show:
30166         <div class="check-element animate-hide" ng-show="checked">
30167           <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
30168         </div>
30169       </div>
30170       <div>
30171         Hide:
30172         <div class="check-element animate-hide" ng-hide="checked">
30173           <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
30174         </div>
30175       </div>
30176     </file>
30177     <file name="glyphicons.css">
30178       @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
30179     </file>
30180     <file name="animations.css">
30181       .animate-hide {
30182         transition: all linear 0.5s;
30183         line-height: 20px;
30184         opacity: 1;
30185         padding: 10px;
30186         border: 1px solid black;
30187         background: white;
30188       }
30190       .animate-hide.ng-hide {
30191         line-height: 0;
30192         opacity: 0;
30193         padding: 0 10px;
30194       }
30196       .check-element {
30197         padding: 10px;
30198         border: 1px solid black;
30199         background: white;
30200       }
30201     </file>
30202     <file name="protractor.js" type="protractor">
30203       var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
30204       var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
30206       it('should check ng-show / ng-hide', function() {
30207         expect(thumbsUp.isDisplayed()).toBeFalsy();
30208         expect(thumbsDown.isDisplayed()).toBeTruthy();
30210         element(by.model('checked')).click();
30212         expect(thumbsUp.isDisplayed()).toBeTruthy();
30213         expect(thumbsDown.isDisplayed()).toBeFalsy();
30214       });
30215     </file>
30216   </example>
30217  */
30218 var ngHideDirective = ['$animate', function($animate) {
30219   return {
30220     restrict: 'A',
30221     multiElement: true,
30222     link: function(scope, element, attr) {
30223       scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
30224         // The comment inside of the ngShowDirective explains why we add and
30225         // remove a temporary class for the show/hide animation
30226         $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
30227           tempClasses: NG_HIDE_IN_PROGRESS_CLASS
30228         });
30229       });
30230     }
30231   };
30235  * @ngdoc directive
30236  * @name ngStyle
30237  * @restrict AC
30239  * @description
30240  * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
30242  * @knownIssue
30243  * You should not use {@link guide/interpolation interpolation} in the value of the `style`
30244  * attribute, when using the `ngStyle` directive on the same element.
30245  * See {@link guide/interpolation#known-issues here} for more info.
30247  * @element ANY
30248  * @param {expression} ngStyle
30250  * {@link guide/expression Expression} which evals to an
30251  * object whose keys are CSS style names and values are corresponding values for those CSS
30252  * keys.
30254  * Since some CSS style names are not valid keys for an object, they must be quoted.
30255  * See the 'background-color' style in the example below.
30257  * @example
30258    <example>
30259      <file name="index.html">
30260         <input type="button" value="set color" ng-click="myStyle={color:'red'}">
30261         <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
30262         <input type="button" value="clear" ng-click="myStyle={}">
30263         <br/>
30264         <span ng-style="myStyle">Sample Text</span>
30265         <pre>myStyle={{myStyle}}</pre>
30266      </file>
30267      <file name="style.css">
30268        span {
30269          color: black;
30270        }
30271      </file>
30272      <file name="protractor.js" type="protractor">
30273        var colorSpan = element(by.css('span'));
30275        it('should check ng-style', function() {
30276          expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
30277          element(by.css('input[value=\'set color\']')).click();
30278          expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
30279          element(by.css('input[value=clear]')).click();
30280          expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
30281        });
30282      </file>
30283    </example>
30284  */
30285 var ngStyleDirective = ngDirective(function(scope, element, attr) {
30286   scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
30287     if (oldStyles && (newStyles !== oldStyles)) {
30288       forEach(oldStyles, function(val, style) { element.css(style, '');});
30289     }
30290     if (newStyles) element.css(newStyles);
30291   }, true);
30295  * @ngdoc directive
30296  * @name ngSwitch
30297  * @restrict EA
30299  * @description
30300  * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
30301  * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
30302  * as specified in the template.
30304  * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
30305  * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
30306  * matches the value obtained from the evaluated expression. In other words, you define a container element
30307  * (where you place the directive), place an expression on the **`on="..."` attribute**
30308  * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
30309  * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
30310  * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
30311  * attribute is displayed.
30313  * <div class="alert alert-info">
30314  * Be aware that the attribute values to match against cannot be expressions. They are interpreted
30315  * as literal string values to match against.
30316  * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
30317  * value of the expression `$scope.someVal`.
30318  * </div>
30320  * @animations
30321  * | Animation                        | Occurs                              |
30322  * |----------------------------------|-------------------------------------|
30323  * | {@link ng.$animate#enter enter}  | after the ngSwitch contents change and the matched child element is placed inside the container |
30324  * | {@link ng.$animate#leave leave}  | after the ngSwitch contents change and just before the former contents are removed from the DOM |
30326  * @usage
30328  * ```
30329  * <ANY ng-switch="expression">
30330  *   <ANY ng-switch-when="matchValue1">...</ANY>
30331  *   <ANY ng-switch-when="matchValue2">...</ANY>
30332  *   <ANY ng-switch-default>...</ANY>
30333  * </ANY>
30334  * ```
30337  * @scope
30338  * @priority 1200
30339  * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
30340  * On child elements add:
30342  * * `ngSwitchWhen`: the case statement to match against. If match then this
30343  *   case will be displayed. If the same match appears multiple times, all the
30344  *   elements will be displayed.
30345  * * `ngSwitchDefault`: the default case when no other case match. If there
30346  *   are multiple default cases, all of them will be displayed when no other
30347  *   case match.
30350  * @example
30351   <example module="switchExample" deps="angular-animate.js" animations="true">
30352     <file name="index.html">
30353       <div ng-controller="ExampleController">
30354         <select ng-model="selection" ng-options="item for item in items">
30355         </select>
30356         <code>selection={{selection}}</code>
30357         <hr/>
30358         <div class="animate-switch-container"
30359           ng-switch on="selection">
30360             <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
30361             <div class="animate-switch" ng-switch-when="home">Home Span</div>
30362             <div class="animate-switch" ng-switch-default>default</div>
30363         </div>
30364       </div>
30365     </file>
30366     <file name="script.js">
30367       angular.module('switchExample', ['ngAnimate'])
30368         .controller('ExampleController', ['$scope', function($scope) {
30369           $scope.items = ['settings', 'home', 'other'];
30370           $scope.selection = $scope.items[0];
30371         }]);
30372     </file>
30373     <file name="animations.css">
30374       .animate-switch-container {
30375         position:relative;
30376         background:white;
30377         border:1px solid black;
30378         height:40px;
30379         overflow:hidden;
30380       }
30382       .animate-switch {
30383         padding:10px;
30384       }
30386       .animate-switch.ng-animate {
30387         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
30389         position:absolute;
30390         top:0;
30391         left:0;
30392         right:0;
30393         bottom:0;
30394       }
30396       .animate-switch.ng-leave.ng-leave-active,
30397       .animate-switch.ng-enter {
30398         top:-50px;
30399       }
30400       .animate-switch.ng-leave,
30401       .animate-switch.ng-enter.ng-enter-active {
30402         top:0;
30403       }
30404     </file>
30405     <file name="protractor.js" type="protractor">
30406       var switchElem = element(by.css('[ng-switch]'));
30407       var select = element(by.model('selection'));
30409       it('should start in settings', function() {
30410         expect(switchElem.getText()).toMatch(/Settings Div/);
30411       });
30412       it('should change to home', function() {
30413         select.all(by.css('option')).get(1).click();
30414         expect(switchElem.getText()).toMatch(/Home Span/);
30415       });
30416       it('should select default', function() {
30417         select.all(by.css('option')).get(2).click();
30418         expect(switchElem.getText()).toMatch(/default/);
30419       });
30420     </file>
30421   </example>
30422  */
30423 var ngSwitchDirective = ['$animate', '$compile', function($animate, $compile) {
30424   return {
30425     require: 'ngSwitch',
30427     // asks for $scope to fool the BC controller module
30428     controller: ['$scope', function ngSwitchController() {
30429      this.cases = {};
30430     }],
30431     link: function(scope, element, attr, ngSwitchController) {
30432       var watchExpr = attr.ngSwitch || attr.on,
30433           selectedTranscludes = [],
30434           selectedElements = [],
30435           previousLeaveAnimations = [],
30436           selectedScopes = [];
30438       var spliceFactory = function(array, index) {
30439           return function() { array.splice(index, 1); };
30440       };
30442       scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
30443         var i, ii;
30444         for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
30445           $animate.cancel(previousLeaveAnimations[i]);
30446         }
30447         previousLeaveAnimations.length = 0;
30449         for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
30450           var selected = getBlockNodes(selectedElements[i].clone);
30451           selectedScopes[i].$destroy();
30452           var promise = previousLeaveAnimations[i] = $animate.leave(selected);
30453           promise.then(spliceFactory(previousLeaveAnimations, i));
30454         }
30456         selectedElements.length = 0;
30457         selectedScopes.length = 0;
30459         if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
30460           forEach(selectedTranscludes, function(selectedTransclude) {
30461             selectedTransclude.transclude(function(caseElement, selectedScope) {
30462               selectedScopes.push(selectedScope);
30463               var anchor = selectedTransclude.element;
30464               caseElement[caseElement.length++] = $compile.$$createComment('end ngSwitchWhen');
30465               var block = { clone: caseElement };
30467               selectedElements.push(block);
30468               $animate.enter(caseElement, anchor.parent(), anchor);
30469             });
30470           });
30471         }
30472       });
30473     }
30474   };
30477 var ngSwitchWhenDirective = ngDirective({
30478   transclude: 'element',
30479   priority: 1200,
30480   require: '^ngSwitch',
30481   multiElement: true,
30482   link: function(scope, element, attrs, ctrl, $transclude) {
30483     ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
30484     ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
30485   }
30488 var ngSwitchDefaultDirective = ngDirective({
30489   transclude: 'element',
30490   priority: 1200,
30491   require: '^ngSwitch',
30492   multiElement: true,
30493   link: function(scope, element, attr, ctrl, $transclude) {
30494     ctrl.cases['?'] = (ctrl.cases['?'] || []);
30495     ctrl.cases['?'].push({ transclude: $transclude, element: element });
30496    }
30500  * @ngdoc directive
30501  * @name ngTransclude
30502  * @restrict EAC
30504  * @description
30505  * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
30507  * You can specify that you want to insert a named transclusion slot, instead of the default slot, by providing the slot name
30508  * as the value of the `ng-transclude` or `ng-transclude-slot` attribute.
30510  * If the transcluded content is not empty (i.e. contains one or more DOM nodes, including whitespace text nodes), any existing
30511  * content of this element will be removed before the transcluded content is inserted.
30512  * If the transcluded content is empty, the existing content is left intact. This lets you provide fallback content in the case
30513  * that no transcluded content is provided.
30515  * @element ANY
30517  * @param {string} ngTransclude|ngTranscludeSlot the name of the slot to insert at this point. If this is not provided, is empty
30518  *                                               or its value is the same as the name of the attribute then the default slot is used.
30520  * @example
30521  * ### Basic transclusion
30522  * This example demonstrates basic transclusion of content into a component directive.
30523  * <example name="simpleTranscludeExample" module="transcludeExample">
30524  *   <file name="index.html">
30525  *     <script>
30526  *       angular.module('transcludeExample', [])
30527  *        .directive('pane', function(){
30528  *           return {
30529  *             restrict: 'E',
30530  *             transclude: true,
30531  *             scope: { title:'@' },
30532  *             template: '<div style="border: 1px solid black;">' +
30533  *                         '<div style="background-color: gray">{{title}}</div>' +
30534  *                         '<ng-transclude></ng-transclude>' +
30535  *                       '</div>'
30536  *           };
30537  *       })
30538  *       .controller('ExampleController', ['$scope', function($scope) {
30539  *         $scope.title = 'Lorem Ipsum';
30540  *         $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
30541  *       }]);
30542  *     </script>
30543  *     <div ng-controller="ExampleController">
30544  *       <input ng-model="title" aria-label="title"> <br/>
30545  *       <textarea ng-model="text" aria-label="text"></textarea> <br/>
30546  *       <pane title="{{title}}">{{text}}</pane>
30547  *     </div>
30548  *   </file>
30549  *   <file name="protractor.js" type="protractor">
30550  *      it('should have transcluded', function() {
30551  *        var titleElement = element(by.model('title'));
30552  *        titleElement.clear();
30553  *        titleElement.sendKeys('TITLE');
30554  *        var textElement = element(by.model('text'));
30555  *        textElement.clear();
30556  *        textElement.sendKeys('TEXT');
30557  *        expect(element(by.binding('title')).getText()).toEqual('TITLE');
30558  *        expect(element(by.binding('text')).getText()).toEqual('TEXT');
30559  *      });
30560  *   </file>
30561  * </example>
30563  * @example
30564  * ### Transclude fallback content
30565  * This example shows how to use `NgTransclude` with fallback content, that
30566  * is displayed if no transcluded content is provided.
30568  * <example module="transcludeFallbackContentExample">
30569  * <file name="index.html">
30570  * <script>
30571  * angular.module('transcludeFallbackContentExample', [])
30572  * .directive('myButton', function(){
30573  *             return {
30574  *               restrict: 'E',
30575  *               transclude: true,
30576  *               scope: true,
30577  *               template: '<button style="cursor: pointer;">' +
30578  *                           '<ng-transclude>' +
30579  *                             '<b style="color: red;">Button1</b>' +
30580  *                           '</ng-transclude>' +
30581  *                         '</button>'
30582  *             };
30583  *         });
30584  * </script>
30585  * <!-- fallback button content -->
30586  * <my-button id="fallback"></my-button>
30587  * <!-- modified button content -->
30588  * <my-button id="modified">
30589  *   <i style="color: green;">Button2</i>
30590  * </my-button>
30591  * </file>
30592  * <file name="protractor.js" type="protractor">
30593  * it('should have different transclude element content', function() {
30594  *          expect(element(by.id('fallback')).getText()).toBe('Button1');
30595  *          expect(element(by.id('modified')).getText()).toBe('Button2');
30596  *        });
30597  * </file>
30598  * </example>
30600  * @example
30601  * ### Multi-slot transclusion
30602  * This example demonstrates using multi-slot transclusion in a component directive.
30603  * <example name="multiSlotTranscludeExample" module="multiSlotTranscludeExample">
30604  *   <file name="index.html">
30605  *    <style>
30606  *      .title, .footer {
30607  *        background-color: gray
30608  *      }
30609  *    </style>
30610  *    <div ng-controller="ExampleController">
30611  *      <input ng-model="title" aria-label="title"> <br/>
30612  *      <textarea ng-model="text" aria-label="text"></textarea> <br/>
30613  *      <pane>
30614  *        <pane-title><a ng-href="{{link}}">{{title}}</a></pane-title>
30615  *        <pane-body><p>{{text}}</p></pane-body>
30616  *      </pane>
30617  *    </div>
30618  *   </file>
30619  *   <file name="app.js">
30620  *    angular.module('multiSlotTranscludeExample', [])
30621  *     .directive('pane', function(){
30622  *        return {
30623  *          restrict: 'E',
30624  *          transclude: {
30625  *            'title': '?paneTitle',
30626  *            'body': 'paneBody',
30627  *            'footer': '?paneFooter'
30628  *          },
30629  *          template: '<div style="border: 1px solid black;">' +
30630  *                      '<div class="title" ng-transclude="title">Fallback Title</div>' +
30631  *                      '<div ng-transclude="body"></div>' +
30632  *                      '<div class="footer" ng-transclude="footer">Fallback Footer</div>' +
30633  *                    '</div>'
30634  *        };
30635  *    })
30636  *    .controller('ExampleController', ['$scope', function($scope) {
30637  *      $scope.title = 'Lorem Ipsum';
30638  *      $scope.link = "https://google.com";
30639  *      $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
30640  *    }]);
30641  *   </file>
30642  *   <file name="protractor.js" type="protractor">
30643  *      it('should have transcluded the title and the body', function() {
30644  *        var titleElement = element(by.model('title'));
30645  *        titleElement.clear();
30646  *        titleElement.sendKeys('TITLE');
30647  *        var textElement = element(by.model('text'));
30648  *        textElement.clear();
30649  *        textElement.sendKeys('TEXT');
30650  *        expect(element(by.css('.title')).getText()).toEqual('TITLE');
30651  *        expect(element(by.binding('text')).getText()).toEqual('TEXT');
30652  *        expect(element(by.css('.footer')).getText()).toEqual('Fallback Footer');
30653  *      });
30654  *   </file>
30655  * </example>
30656  */
30657 var ngTranscludeMinErr = minErr('ngTransclude');
30658 var ngTranscludeDirective = ['$compile', function($compile) {
30659   return {
30660     restrict: 'EAC',
30661     terminal: true,
30662     compile: function ngTranscludeCompile(tElement) {
30664       // Remove and cache any original content to act as a fallback
30665       var fallbackLinkFn = $compile(tElement.contents());
30666       tElement.empty();
30668       return function ngTranscludePostLink($scope, $element, $attrs, controller, $transclude) {
30670         if (!$transclude) {
30671           throw ngTranscludeMinErr('orphan',
30672           'Illegal use of ngTransclude directive in the template! ' +
30673           'No parent directive that requires a transclusion found. ' +
30674           'Element: {0}',
30675           startingTag($element));
30676         }
30679         // If the attribute is of the form: `ng-transclude="ng-transclude"` then treat it like the default
30680         if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
30681           $attrs.ngTransclude = '';
30682         }
30683         var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
30685         // If the slot is required and no transclusion content is provided then this call will throw an error
30686         $transclude(ngTranscludeCloneAttachFn, null, slotName);
30688         // If the slot is optional and no transclusion content is provided then use the fallback content
30689         if (slotName && !$transclude.isSlotFilled(slotName)) {
30690           useFallbackContent();
30691         }
30693         function ngTranscludeCloneAttachFn(clone, transcludedScope) {
30694           if (clone.length) {
30695             $element.append(clone);
30696           } else {
30697             useFallbackContent();
30698             // There is nothing linked against the transcluded scope since no content was available,
30699             // so it should be safe to clean up the generated scope.
30700             transcludedScope.$destroy();
30701           }
30702         }
30704         function useFallbackContent() {
30705           // Since this is the fallback content rather than the transcluded content,
30706           // we link against the scope of this directive rather than the transcluded scope
30707           fallbackLinkFn($scope, function(clone) {
30708             $element.append(clone);
30709           });
30710         }
30711       };
30712     }
30713   };
30717  * @ngdoc directive
30718  * @name script
30719  * @restrict E
30721  * @description
30722  * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
30723  * template can be used by {@link ng.directive:ngInclude `ngInclude`},
30724  * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
30725  * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
30726  * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
30728  * @param {string} type Must be set to `'text/ng-template'`.
30729  * @param {string} id Cache name of the template.
30731  * @example
30732   <example>
30733     <file name="index.html">
30734       <script type="text/ng-template" id="/tpl.html">
30735         Content of the template.
30736       </script>
30738       <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
30739       <div id="tpl-content" ng-include src="currentTpl"></div>
30740     </file>
30741     <file name="protractor.js" type="protractor">
30742       it('should load template defined inside script tag', function() {
30743         element(by.css('#tpl-link')).click();
30744         expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
30745       });
30746     </file>
30747   </example>
30748  */
30749 var scriptDirective = ['$templateCache', function($templateCache) {
30750   return {
30751     restrict: 'E',
30752     terminal: true,
30753     compile: function(element, attr) {
30754       if (attr.type == 'text/ng-template') {
30755         var templateUrl = attr.id,
30756             text = element[0].text;
30758         $templateCache.put(templateUrl, text);
30759       }
30760     }
30761   };
30764 var noopNgModelController = { $setViewValue: noop, $render: noop };
30766 function chromeHack(optionElement) {
30767   // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
30768   // Adding an <option selected="selected"> element to a <select required="required"> should
30769   // automatically select the new element
30770   if (optionElement[0].hasAttribute('selected')) {
30771     optionElement[0].selected = true;
30772   }
30776  * @ngdoc type
30777  * @name  select.SelectController
30778  * @description
30779  * The controller for the `<select>` directive. This provides support for reading
30780  * and writing the selected value(s) of the control and also coordinates dynamically
30781  * added `<option>` elements, perhaps by an `ngRepeat` directive.
30782  */
30783 var SelectController =
30784         ['$element', '$scope', function($element, $scope) {
30786   var self = this,
30787       optionsMap = new HashMap();
30789   // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
30790   self.ngModelCtrl = noopNgModelController;
30792   // The "unknown" option is one that is prepended to the list if the viewValue
30793   // does not match any of the options. When it is rendered the value of the unknown
30794   // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
30795   //
30796   // We can't just jqLite('<option>') since jqLite is not smart enough
30797   // to create it in <select> and IE barfs otherwise.
30798   self.unknownOption = jqLite(window.document.createElement('option'));
30799   self.renderUnknownOption = function(val) {
30800     var unknownVal = '? ' + hashKey(val) + ' ?';
30801     self.unknownOption.val(unknownVal);
30802     $element.prepend(self.unknownOption);
30803     $element.val(unknownVal);
30804   };
30806   $scope.$on('$destroy', function() {
30807     // disable unknown option so that we don't do work when the whole select is being destroyed
30808     self.renderUnknownOption = noop;
30809   });
30811   self.removeUnknownOption = function() {
30812     if (self.unknownOption.parent()) self.unknownOption.remove();
30813   };
30816   // Read the value of the select control, the implementation of this changes depending
30817   // upon whether the select can have multiple values and whether ngOptions is at work.
30818   self.readValue = function readSingleValue() {
30819     self.removeUnknownOption();
30820     return $element.val();
30821   };
30824   // Write the value to the select control, the implementation of this changes depending
30825   // upon whether the select can have multiple values and whether ngOptions is at work.
30826   self.writeValue = function writeSingleValue(value) {
30827     if (self.hasOption(value)) {
30828       self.removeUnknownOption();
30829       $element.val(value);
30830       if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
30831     } else {
30832       if (value == null && self.emptyOption) {
30833         self.removeUnknownOption();
30834         $element.val('');
30835       } else {
30836         self.renderUnknownOption(value);
30837       }
30838     }
30839   };
30842   // Tell the select control that an option, with the given value, has been added
30843   self.addOption = function(value, element) {
30844     // Skip comment nodes, as they only pollute the `optionsMap`
30845     if (element[0].nodeType === NODE_TYPE_COMMENT) return;
30847     assertNotHasOwnProperty(value, '"option value"');
30848     if (value === '') {
30849       self.emptyOption = element;
30850     }
30851     var count = optionsMap.get(value) || 0;
30852     optionsMap.put(value, count + 1);
30853     self.ngModelCtrl.$render();
30854     chromeHack(element);
30855   };
30857   // Tell the select control that an option, with the given value, has been removed
30858   self.removeOption = function(value) {
30859     var count = optionsMap.get(value);
30860     if (count) {
30861       if (count === 1) {
30862         optionsMap.remove(value);
30863         if (value === '') {
30864           self.emptyOption = undefined;
30865         }
30866       } else {
30867         optionsMap.put(value, count - 1);
30868       }
30869     }
30870   };
30872   // Check whether the select control has an option matching the given value
30873   self.hasOption = function(value) {
30874     return !!optionsMap.get(value);
30875   };
30878   self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
30880     if (interpolateValueFn) {
30881       // The value attribute is interpolated
30882       var oldVal;
30883       optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
30884         if (isDefined(oldVal)) {
30885           self.removeOption(oldVal);
30886         }
30887         oldVal = newVal;
30888         self.addOption(newVal, optionElement);
30889       });
30890     } else if (interpolateTextFn) {
30891       // The text content is interpolated
30892       optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
30893         optionAttrs.$set('value', newVal);
30894         if (oldVal !== newVal) {
30895           self.removeOption(oldVal);
30896         }
30897         self.addOption(newVal, optionElement);
30898       });
30899     } else {
30900       // The value attribute is static
30901       self.addOption(optionAttrs.value, optionElement);
30902     }
30904     optionElement.on('$destroy', function() {
30905       self.removeOption(optionAttrs.value);
30906       self.ngModelCtrl.$render();
30907     });
30908   };
30912  * @ngdoc directive
30913  * @name select
30914  * @restrict E
30916  * @description
30917  * HTML `SELECT` element with angular data-binding.
30919  * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
30920  * between the scope and the `<select>` control (including setting default values).
30921  * It also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
30922  * {@link ngOptions `ngOptions`} directives.
30924  * When an item in the `<select>` menu is selected, the value of the selected option will be bound
30925  * to the model identified by the `ngModel` directive. With static or repeated options, this is
30926  * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
30927  * If you want dynamic value attributes, you can use interpolation inside the value attribute.
30929  * <div class="alert alert-warning">
30930  * Note that the value of a `select` directive used without `ngOptions` is always a string.
30931  * When the model needs to be bound to a non-string value, you must either explicitly convert it
30932  * using a directive (see example below) or use `ngOptions` to specify the set of options.
30933  * This is because an option element can only be bound to string values at present.
30934  * </div>
30936  * If the viewValue of `ngModel` does not match any of the options, then the control
30937  * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
30939  * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
30940  * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
30941  * option. See example below for demonstration.
30943  * <div class="alert alert-info">
30944  * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
30945  * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as
30946  * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
30947  * comprehension expression, and additionally in reducing memory and increasing speed by not creating
30948  * a new scope for each repeated instance.
30949  * </div>
30952  * @param {string} ngModel Assignable angular expression to data-bind to.
30953  * @param {string=} name Property name of the form under which the control is published.
30954  * @param {string=} multiple Allows multiple options to be selected. The selected values will be
30955  *     bound to the model as an array.
30956  * @param {string=} required Sets `required` validation error key if the value is not entered.
30957  * @param {string=} ngRequired Adds required attribute and required validation constraint to
30958  * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
30959  * when you want to data-bind to the required attribute.
30960  * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
30961  *    interaction with the select element.
30962  * @param {string=} ngOptions sets the options that the select is populated with and defines what is
30963  * set on the model on selection. See {@link ngOptions `ngOptions`}.
30965  * @example
30966  * ### Simple `select` elements with static options
30968  * <example name="static-select" module="staticSelect">
30969  * <file name="index.html">
30970  * <div ng-controller="ExampleController">
30971  *   <form name="myForm">
30972  *     <label for="singleSelect"> Single select: </label><br>
30973  *     <select name="singleSelect" ng-model="data.singleSelect">
30974  *       <option value="option-1">Option 1</option>
30975  *       <option value="option-2">Option 2</option>
30976  *     </select><br>
30978  *     <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
30979  *     <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
30980  *       <option value="">---Please select---</option> <!-- not selected / blank option -->
30981  *       <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
30982  *       <option value="option-2">Option 2</option>
30983  *     </select><br>
30984  *     <button ng-click="forceUnknownOption()">Force unknown option</button><br>
30985  *     <tt>singleSelect = {{data.singleSelect}}</tt>
30987  *     <hr>
30988  *     <label for="multipleSelect"> Multiple select: </label><br>
30989  *     <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
30990  *       <option value="option-1">Option 1</option>
30991  *       <option value="option-2">Option 2</option>
30992  *       <option value="option-3">Option 3</option>
30993  *     </select><br>
30994  *     <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
30995  *   </form>
30996  * </div>
30997  * </file>
30998  * <file name="app.js">
30999  *  angular.module('staticSelect', [])
31000  *    .controller('ExampleController', ['$scope', function($scope) {
31001  *      $scope.data = {
31002  *       singleSelect: null,
31003  *       multipleSelect: [],
31004  *       option1: 'option-1',
31005  *      };
31007  *      $scope.forceUnknownOption = function() {
31008  *        $scope.data.singleSelect = 'nonsense';
31009  *      };
31010  *   }]);
31011  * </file>
31012  *</example>
31014  * ### Using `ngRepeat` to generate `select` options
31015  * <example name="ngrepeat-select" module="ngrepeatSelect">
31016  * <file name="index.html">
31017  * <div ng-controller="ExampleController">
31018  *   <form name="myForm">
31019  *     <label for="repeatSelect"> Repeat select: </label>
31020  *     <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
31021  *       <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
31022  *     </select>
31023  *   </form>
31024  *   <hr>
31025  *   <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
31026  * </div>
31027  * </file>
31028  * <file name="app.js">
31029  *  angular.module('ngrepeatSelect', [])
31030  *    .controller('ExampleController', ['$scope', function($scope) {
31031  *      $scope.data = {
31032  *       repeatSelect: null,
31033  *       availableOptions: [
31034  *         {id: '1', name: 'Option A'},
31035  *         {id: '2', name: 'Option B'},
31036  *         {id: '3', name: 'Option C'}
31037  *       ],
31038  *      };
31039  *   }]);
31040  * </file>
31041  *</example>
31044  * ### Using `select` with `ngOptions` and setting a default value
31045  * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
31047  * <example name="select-with-default-values" module="defaultValueSelect">
31048  * <file name="index.html">
31049  * <div ng-controller="ExampleController">
31050  *   <form name="myForm">
31051  *     <label for="mySelect">Make a choice:</label>
31052  *     <select name="mySelect" id="mySelect"
31053  *       ng-options="option.name for option in data.availableOptions track by option.id"
31054  *       ng-model="data.selectedOption"></select>
31055  *   </form>
31056  *   <hr>
31057  *   <tt>option = {{data.selectedOption}}</tt><br/>
31058  * </div>
31059  * </file>
31060  * <file name="app.js">
31061  *  angular.module('defaultValueSelect', [])
31062  *    .controller('ExampleController', ['$scope', function($scope) {
31063  *      $scope.data = {
31064  *       availableOptions: [
31065  *         {id: '1', name: 'Option A'},
31066  *         {id: '2', name: 'Option B'},
31067  *         {id: '3', name: 'Option C'}
31068  *       ],
31069  *       selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
31070  *       };
31071  *   }]);
31072  * </file>
31073  *</example>
31076  * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
31078  * <example name="select-with-non-string-options" module="nonStringSelect">
31079  *   <file name="index.html">
31080  *     <select ng-model="model.id" convert-to-number>
31081  *       <option value="0">Zero</option>
31082  *       <option value="1">One</option>
31083  *       <option value="2">Two</option>
31084  *     </select>
31085  *     {{ model }}
31086  *   </file>
31087  *   <file name="app.js">
31088  *     angular.module('nonStringSelect', [])
31089  *       .run(function($rootScope) {
31090  *         $rootScope.model = { id: 2 };
31091  *       })
31092  *       .directive('convertToNumber', function() {
31093  *         return {
31094  *           require: 'ngModel',
31095  *           link: function(scope, element, attrs, ngModel) {
31096  *             ngModel.$parsers.push(function(val) {
31097  *               return parseInt(val, 10);
31098  *             });
31099  *             ngModel.$formatters.push(function(val) {
31100  *               return '' + val;
31101  *             });
31102  *           }
31103  *         };
31104  *       });
31105  *   </file>
31106  *   <file name="protractor.js" type="protractor">
31107  *     it('should initialize to model', function() {
31108  *       var select = element(by.css('select'));
31109  *       expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
31110  *     });
31111  *   </file>
31112  * </example>
31114  */
31115 var selectDirective = function() {
31117   return {
31118     restrict: 'E',
31119     require: ['select', '?ngModel'],
31120     controller: SelectController,
31121     priority: 1,
31122     link: {
31123       pre: selectPreLink,
31124       post: selectPostLink
31125     }
31126   };
31128   function selectPreLink(scope, element, attr, ctrls) {
31130       // if ngModel is not defined, we don't need to do anything
31131       var ngModelCtrl = ctrls[1];
31132       if (!ngModelCtrl) return;
31134       var selectCtrl = ctrls[0];
31136       selectCtrl.ngModelCtrl = ngModelCtrl;
31138       // When the selected item(s) changes we delegate getting the value of the select control
31139       // to the `readValue` method, which can be changed if the select can have multiple
31140       // selected values or if the options are being generated by `ngOptions`
31141       element.on('change', function() {
31142         scope.$apply(function() {
31143           ngModelCtrl.$setViewValue(selectCtrl.readValue());
31144         });
31145       });
31147       // If the select allows multiple values then we need to modify how we read and write
31148       // values from and to the control; also what it means for the value to be empty and
31149       // we have to add an extra watch since ngModel doesn't work well with arrays - it
31150       // doesn't trigger rendering if only an item in the array changes.
31151       if (attr.multiple) {
31153         // Read value now needs to check each option to see if it is selected
31154         selectCtrl.readValue = function readMultipleValue() {
31155           var array = [];
31156           forEach(element.find('option'), function(option) {
31157             if (option.selected) {
31158               array.push(option.value);
31159             }
31160           });
31161           return array;
31162         };
31164         // Write value now needs to set the selected property of each matching option
31165         selectCtrl.writeValue = function writeMultipleValue(value) {
31166           var items = new HashMap(value);
31167           forEach(element.find('option'), function(option) {
31168             option.selected = isDefined(items.get(option.value));
31169           });
31170         };
31172         // we have to do it on each watch since ngModel watches reference, but
31173         // we need to work of an array, so we need to see if anything was inserted/removed
31174         var lastView, lastViewRef = NaN;
31175         scope.$watch(function selectMultipleWatch() {
31176           if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
31177             lastView = shallowCopy(ngModelCtrl.$viewValue);
31178             ngModelCtrl.$render();
31179           }
31180           lastViewRef = ngModelCtrl.$viewValue;
31181         });
31183         // If we are a multiple select then value is now a collection
31184         // so the meaning of $isEmpty changes
31185         ngModelCtrl.$isEmpty = function(value) {
31186           return !value || value.length === 0;
31187         };
31189       }
31190     }
31192     function selectPostLink(scope, element, attrs, ctrls) {
31193       // if ngModel is not defined, we don't need to do anything
31194       var ngModelCtrl = ctrls[1];
31195       if (!ngModelCtrl) return;
31197       var selectCtrl = ctrls[0];
31199       // We delegate rendering to the `writeValue` method, which can be changed
31200       // if the select can have multiple selected values or if the options are being
31201       // generated by `ngOptions`.
31202       // This must be done in the postLink fn to prevent $render to be called before
31203       // all nodes have been linked correctly.
31204       ngModelCtrl.$render = function() {
31205         selectCtrl.writeValue(ngModelCtrl.$viewValue);
31206       };
31207     }
31211 // The option directive is purely designed to communicate the existence (or lack of)
31212 // of dynamically created (and destroyed) option elements to their containing select
31213 // directive via its controller.
31214 var optionDirective = ['$interpolate', function($interpolate) {
31215   return {
31216     restrict: 'E',
31217     priority: 100,
31218     compile: function(element, attr) {
31219       if (isDefined(attr.value)) {
31220         // If the value attribute is defined, check if it contains an interpolation
31221         var interpolateValueFn = $interpolate(attr.value, true);
31222       } else {
31223         // If the value attribute is not defined then we fall back to the
31224         // text content of the option element, which may be interpolated
31225         var interpolateTextFn = $interpolate(element.text(), true);
31226         if (!interpolateTextFn) {
31227           attr.$set('value', element.text());
31228         }
31229       }
31231       return function(scope, element, attr) {
31232         // This is an optimization over using ^^ since we don't want to have to search
31233         // all the way to the root of the DOM for every single option element
31234         var selectCtrlName = '$selectController',
31235             parent = element.parent(),
31236             selectCtrl = parent.data(selectCtrlName) ||
31237               parent.parent().data(selectCtrlName); // in case we are in optgroup
31239         if (selectCtrl) {
31240           selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
31241         }
31242       };
31243     }
31244   };
31247 var styleDirective = valueFn({
31248   restrict: 'E',
31249   terminal: false
31253  * @ngdoc directive
31254  * @name ngRequired
31255  * @restrict A
31257  * @description
31259  * ngRequired adds the required {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
31260  * It is most often used for {@link input `input`} and {@link select `select`} controls, but can also be
31261  * applied to custom controls.
31263  * The directive sets the `required` attribute on the element if the Angular expression inside
31264  * `ngRequired` evaluates to true. A special directive for setting `required` is necessary because we
31265  * cannot use interpolation inside `required`. See the {@link guide/interpolation interpolation guide}
31266  * for more info.
31268  * The validator will set the `required` error key to true if the `required` attribute is set and
31269  * calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty`} with the
31270  * {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} returns `true`. For example, the
31271  * `$isEmpty()` implementation for `input[text]` checks the length of the `$viewValue`. When developing
31272  * custom controls, `$isEmpty()` can be overwritten to account for a $viewValue that is not string-based.
31274  * @example
31275  * <example name="ngRequiredDirective" module="ngRequiredExample">
31276  *   <file name="index.html">
31277  *     <script>
31278  *       angular.module('ngRequiredExample', [])
31279  *         .controller('ExampleController', ['$scope', function($scope) {
31280  *           $scope.required = true;
31281  *         }]);
31282  *     </script>
31283  *     <div ng-controller="ExampleController">
31284  *       <form name="form">
31285  *         <label for="required">Toggle required: </label>
31286  *         <input type="checkbox" ng-model="required" id="required" />
31287  *         <br>
31288  *         <label for="input">This input must be filled if `required` is true: </label>
31289  *         <input type="text" ng-model="model" id="input" name="input" ng-required="required" /><br>
31290  *         <hr>
31291  *         required error set? = <code>{{form.input.$error.required}}</code><br>
31292  *         model = <code>{{model}}</code>
31293  *       </form>
31294  *     </div>
31295  *   </file>
31296  *   <file name="protractor.js" type="protractor">
31297        var required = element(by.binding('form.input.$error.required'));
31298        var model = element(by.binding('model'));
31299        var input = element(by.id('input'));
31301        it('should set the required error', function() {
31302          expect(required.getText()).toContain('true');
31304          input.sendKeys('123');
31305          expect(required.getText()).not.toContain('true');
31306          expect(model.getText()).toContain('123');
31307        });
31308  *   </file>
31309  * </example>
31310  */
31311 var requiredDirective = function() {
31312   return {
31313     restrict: 'A',
31314     require: '?ngModel',
31315     link: function(scope, elm, attr, ctrl) {
31316       if (!ctrl) return;
31317       attr.required = true; // force truthy in case we are on non input element
31319       ctrl.$validators.required = function(modelValue, viewValue) {
31320         return !attr.required || !ctrl.$isEmpty(viewValue);
31321       };
31323       attr.$observe('required', function() {
31324         ctrl.$validate();
31325       });
31326     }
31327   };
31331  * @ngdoc directive
31332  * @name ngPattern
31334  * @description
31336  * ngPattern adds the pattern {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
31337  * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
31339  * The validator sets the `pattern` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
31340  * does not match a RegExp which is obtained by evaluating the Angular expression given in the
31341  * `ngPattern` attribute value:
31342  * * If the expression evaluates to a RegExp object, then this is used directly.
31343  * * If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it
31344  * in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
31346  * <div class="alert alert-info">
31347  * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
31348  * start at the index of the last search's match, thus not taking the whole input value into
31349  * account.
31350  * </div>
31352  * <div class="alert alert-info">
31353  * **Note:** This directive is also added when the plain `pattern` attribute is used, with two
31354  * differences:
31355  * <ol>
31356  *   <li>
31357  *     `ngPattern` does not set the `pattern` attribute and therefore HTML5 constraint validation is
31358  *     not available.
31359  *   </li>
31360  *   <li>
31361  *     The `ngPattern` attribute must be an expression, while the `pattern` value must be
31362  *     interpolated.
31363  *   </li>
31364  * </ol>
31365  * </div>
31367  * @example
31368  * <example name="ngPatternDirective" module="ngPatternExample">
31369  *   <file name="index.html">
31370  *     <script>
31371  *       angular.module('ngPatternExample', [])
31372  *         .controller('ExampleController', ['$scope', function($scope) {
31373  *           $scope.regex = '\\d+';
31374  *         }]);
31375  *     </script>
31376  *     <div ng-controller="ExampleController">
31377  *       <form name="form">
31378  *         <label for="regex">Set a pattern (regex string): </label>
31379  *         <input type="text" ng-model="regex" id="regex" />
31380  *         <br>
31381  *         <label for="input">This input is restricted by the current pattern: </label>
31382  *         <input type="text" ng-model="model" id="input" name="input" ng-pattern="regex" /><br>
31383  *         <hr>
31384  *         input valid? = <code>{{form.input.$valid}}</code><br>
31385  *         model = <code>{{model}}</code>
31386  *       </form>
31387  *     </div>
31388  *   </file>
31389  *   <file name="protractor.js" type="protractor">
31390        var model = element(by.binding('model'));
31391        var input = element(by.id('input'));
31393        it('should validate the input with the default pattern', function() {
31394          input.sendKeys('aaa');
31395          expect(model.getText()).not.toContain('aaa');
31397          input.clear().then(function() {
31398            input.sendKeys('123');
31399            expect(model.getText()).toContain('123');
31400          });
31401        });
31402  *   </file>
31403  * </example>
31404  */
31405 var patternDirective = function() {
31406   return {
31407     restrict: 'A',
31408     require: '?ngModel',
31409     link: function(scope, elm, attr, ctrl) {
31410       if (!ctrl) return;
31412       var regexp, patternExp = attr.ngPattern || attr.pattern;
31413       attr.$observe('pattern', function(regex) {
31414         if (isString(regex) && regex.length > 0) {
31415           regex = new RegExp('^' + regex + '$');
31416         }
31418         if (regex && !regex.test) {
31419           throw minErr('ngPattern')('noregexp',
31420             'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
31421             regex, startingTag(elm));
31422         }
31424         regexp = regex || undefined;
31425         ctrl.$validate();
31426       });
31428       ctrl.$validators.pattern = function(modelValue, viewValue) {
31429         // HTML5 pattern constraint validates the input value, so we validate the viewValue
31430         return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
31431       };
31432     }
31433   };
31437  * @ngdoc directive
31438  * @name ngMaxlength
31440  * @description
31442  * ngMaxlength adds the maxlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
31443  * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
31445  * The validator sets the `maxlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
31446  * is longer than the integer obtained by evaluating the Angular expression given in the
31447  * `ngMaxlength` attribute value.
31449  * <div class="alert alert-info">
31450  * **Note:** This directive is also added when the plain `maxlength` attribute is used, with two
31451  * differences:
31452  * <ol>
31453  *   <li>
31454  *     `ngMaxlength` does not set the `maxlength` attribute and therefore HTML5 constraint
31455  *     validation is not available.
31456  *   </li>
31457  *   <li>
31458  *     The `ngMaxlength` attribute must be an expression, while the `maxlength` value must be
31459  *     interpolated.
31460  *   </li>
31461  * </ol>
31462  * </div>
31464  * @example
31465  * <example name="ngMaxlengthDirective" module="ngMaxlengthExample">
31466  *   <file name="index.html">
31467  *     <script>
31468  *       angular.module('ngMaxlengthExample', [])
31469  *         .controller('ExampleController', ['$scope', function($scope) {
31470  *           $scope.maxlength = 5;
31471  *         }]);
31472  *     </script>
31473  *     <div ng-controller="ExampleController">
31474  *       <form name="form">
31475  *         <label for="maxlength">Set a maxlength: </label>
31476  *         <input type="number" ng-model="maxlength" id="maxlength" />
31477  *         <br>
31478  *         <label for="input">This input is restricted by the current maxlength: </label>
31479  *         <input type="text" ng-model="model" id="input" name="input" ng-maxlength="maxlength" /><br>
31480  *         <hr>
31481  *         input valid? = <code>{{form.input.$valid}}</code><br>
31482  *         model = <code>{{model}}</code>
31483  *       </form>
31484  *     </div>
31485  *   </file>
31486  *   <file name="protractor.js" type="protractor">
31487        var model = element(by.binding('model'));
31488        var input = element(by.id('input'));
31490        it('should validate the input with the default maxlength', function() {
31491          input.sendKeys('abcdef');
31492          expect(model.getText()).not.toContain('abcdef');
31494          input.clear().then(function() {
31495            input.sendKeys('abcde');
31496            expect(model.getText()).toContain('abcde');
31497          });
31498        });
31499  *   </file>
31500  * </example>
31501  */
31502 var maxlengthDirective = function() {
31503   return {
31504     restrict: 'A',
31505     require: '?ngModel',
31506     link: function(scope, elm, attr, ctrl) {
31507       if (!ctrl) return;
31509       var maxlength = -1;
31510       attr.$observe('maxlength', function(value) {
31511         var intVal = toInt(value);
31512         maxlength = isNaN(intVal) ? -1 : intVal;
31513         ctrl.$validate();
31514       });
31515       ctrl.$validators.maxlength = function(modelValue, viewValue) {
31516         return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
31517       };
31518     }
31519   };
31523  * @ngdoc directive
31524  * @name ngMinlength
31526  * @description
31528  * ngMinlength adds the minlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
31529  * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
31531  * The validator sets the `minlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
31532  * is shorter than the integer obtained by evaluating the Angular expression given in the
31533  * `ngMinlength` attribute value.
31535  * <div class="alert alert-info">
31536  * **Note:** This directive is also added when the plain `minlength` attribute is used, with two
31537  * differences:
31538  * <ol>
31539  *   <li>
31540  *     `ngMinlength` does not set the `minlength` attribute and therefore HTML5 constraint
31541  *     validation is not available.
31542  *   </li>
31543  *   <li>
31544  *     The `ngMinlength` value must be an expression, while the `minlength` value must be
31545  *     interpolated.
31546  *   </li>
31547  * </ol>
31548  * </div>
31550  * @example
31551  * <example name="ngMinlengthDirective" module="ngMinlengthExample">
31552  *   <file name="index.html">
31553  *     <script>
31554  *       angular.module('ngMinlengthExample', [])
31555  *         .controller('ExampleController', ['$scope', function($scope) {
31556  *           $scope.minlength = 3;
31557  *         }]);
31558  *     </script>
31559  *     <div ng-controller="ExampleController">
31560  *       <form name="form">
31561  *         <label for="minlength">Set a minlength: </label>
31562  *         <input type="number" ng-model="minlength" id="minlength" />
31563  *         <br>
31564  *         <label for="input">This input is restricted by the current minlength: </label>
31565  *         <input type="text" ng-model="model" id="input" name="input" ng-minlength="minlength" /><br>
31566  *         <hr>
31567  *         input valid? = <code>{{form.input.$valid}}</code><br>
31568  *         model = <code>{{model}}</code>
31569  *       </form>
31570  *     </div>
31571  *   </file>
31572  *   <file name="protractor.js" type="protractor">
31573        var model = element(by.binding('model'));
31574        var input = element(by.id('input'));
31576        it('should validate the input with the default minlength', function() {
31577          input.sendKeys('ab');
31578          expect(model.getText()).not.toContain('ab');
31580          input.sendKeys('abc');
31581          expect(model.getText()).toContain('abc');
31582        });
31583  *   </file>
31584  * </example>
31585  */
31586 var minlengthDirective = function() {
31587   return {
31588     restrict: 'A',
31589     require: '?ngModel',
31590     link: function(scope, elm, attr, ctrl) {
31591       if (!ctrl) return;
31593       var minlength = 0;
31594       attr.$observe('minlength', function(value) {
31595         minlength = toInt(value) || 0;
31596         ctrl.$validate();
31597       });
31598       ctrl.$validators.minlength = function(modelValue, viewValue) {
31599         return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
31600       };
31601     }
31602   };
31605 if (window.angular.bootstrap) {
31606   //AngularJS is already loaded, so we can return here...
31607   if (window.console) {
31608     console.log('WARNING: Tried to load angular more than once.');
31609   }
31610   return;
31613 //try to bind to jquery now so that one can write jqLite(document).ready()
31614 //but we will rebind on bootstrap again.
31615 bindJQuery();
31617 publishExternalAPI(angular);
31619 angular.module("ngLocale", [], ["$provide", function($provide) {
31620 var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
31621 function getDecimals(n) {
31622   n = n + '';
31623   var i = n.indexOf('.');
31624   return (i == -1) ? 0 : n.length - i - 1;
31627 function getVF(n, opt_precision) {
31628   var v = opt_precision;
31630   if (undefined === v) {
31631     v = Math.min(getDecimals(n), 3);
31632   }
31634   var base = Math.pow(10, v);
31635   var f = ((n * base) | 0) % base;
31636   return {v: v, f: f};
31639 $provide.value("$locale", {
31640   "DATETIME_FORMATS": {
31641     "AMPMS": [
31642       "AM",
31643       "PM"
31644     ],
31645     "DAY": [
31646       "Sunday",
31647       "Monday",
31648       "Tuesday",
31649       "Wednesday",
31650       "Thursday",
31651       "Friday",
31652       "Saturday"
31653     ],
31654     "ERANAMES": [
31655       "Before Christ",
31656       "Anno Domini"
31657     ],
31658     "ERAS": [
31659       "BC",
31660       "AD"
31661     ],
31662     "FIRSTDAYOFWEEK": 6,
31663     "MONTH": [
31664       "January",
31665       "February",
31666       "March",
31667       "April",
31668       "May",
31669       "June",
31670       "July",
31671       "August",
31672       "September",
31673       "October",
31674       "November",
31675       "December"
31676     ],
31677     "SHORTDAY": [
31678       "Sun",
31679       "Mon",
31680       "Tue",
31681       "Wed",
31682       "Thu",
31683       "Fri",
31684       "Sat"
31685     ],
31686     "SHORTMONTH": [
31687       "Jan",
31688       "Feb",
31689       "Mar",
31690       "Apr",
31691       "May",
31692       "Jun",
31693       "Jul",
31694       "Aug",
31695       "Sep",
31696       "Oct",
31697       "Nov",
31698       "Dec"
31699     ],
31700     "STANDALONEMONTH": [
31701       "January",
31702       "February",
31703       "March",
31704       "April",
31705       "May",
31706       "June",
31707       "July",
31708       "August",
31709       "September",
31710       "October",
31711       "November",
31712       "December"
31713     ],
31714     "WEEKENDRANGE": [
31715       5,
31716       6
31717     ],
31718     "fullDate": "EEEE, MMMM d, y",
31719     "longDate": "MMMM d, y",
31720     "medium": "MMM d, y h:mm:ss a",
31721     "mediumDate": "MMM d, y",
31722     "mediumTime": "h:mm:ss a",
31723     "short": "M/d/yy h:mm a",
31724     "shortDate": "M/d/yy",
31725     "shortTime": "h:mm a"
31726   },
31727   "NUMBER_FORMATS": {
31728     "CURRENCY_SYM": "$",
31729     "DECIMAL_SEP": ".",
31730     "GROUP_SEP": ",",
31731     "PATTERNS": [
31732       {
31733         "gSize": 3,
31734         "lgSize": 3,
31735         "maxFrac": 3,
31736         "minFrac": 0,
31737         "minInt": 1,
31738         "negPre": "-",
31739         "negSuf": "",
31740         "posPre": "",
31741         "posSuf": ""
31742       },
31743       {
31744         "gSize": 3,
31745         "lgSize": 3,
31746         "maxFrac": 2,
31747         "minFrac": 2,
31748         "minInt": 1,
31749         "negPre": "-\u00a4",
31750         "negSuf": "",
31751         "posPre": "\u00a4",
31752         "posSuf": ""
31753       }
31754     ]
31755   },
31756   "id": "en-us",
31757   "localeID": "en_US",
31758   "pluralCat": function(n, opt_precision) {  var i = n | 0;  var vf = getVF(n, opt_precision);  if (i == 1 && vf.v == 0) {    return PLURAL_CATEGORY.ONE;  }  return PLURAL_CATEGORY.OTHER;}
31760 }]);
31762   jqLite(window.document).ready(function() {
31763     angularInit(window.document, bootstrap);
31764   });
31766 })(window);
31768 !window.angular.$$csp().noInlineStyle && window.angular.element(document.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:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');